lib: reorder protocol functions to avoid forward declarations (misc)
No binary (build failed)
—
(log yok)
| @@ -65,41 +65,6 @@ | |
| #define DICT_DEFINE3 "/LOOKUP:" | |
| - | /* |
| - | * Forward declarations. |
| - | */ |
| - | |
| - | static CURLcode dict_do(struct Curl_easy *data, bool *done); |
| - | |
| - | /* |
| - | * DICT protocol handler. |
| - | */ |
| - | |
| - | const struct Curl_handler Curl_handler_dict = { |
| - | "dict", /* scheme */ |
| - | ZERO_NULL, /* setup_connection */ |
| - | dict_do, /* do_it */ |
| - | ZERO_NULL, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | ZERO_NULL, /* connect_it */ |
| - | ZERO_NULL, /* connecting */ |
| - | ZERO_NULL, /* doing */ |
| - | ZERO_NULL, /* proto_pollset */ |
| - | ZERO_NULL, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | ZERO_NULL, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | ZERO_NULL, /* write_resp */ |
| - | ZERO_NULL, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | ZERO_NULL, /* follow */ |
| - | PORT_DICT, /* defport */ |
| - | CURLPROTO_DICT, /* protocol */ |
| - | CURLPROTO_DICT, /* family */ |
| - | PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ |
| - | }; |
| - | |
| #define DYN_DICT_WORD 10000 | |
| static char *unescape_word(const char *input) | |
| { | |
| @@ -306,4 +271,33 @@ error: | |
| curlx_free(path); | |
| return result; | |
| } | |
| + | |
| + | /* |
| + | * DICT protocol handler. |
| + | */ |
| + | const struct Curl_handler Curl_handler_dict = { |
| + | "dict", /* scheme */ |
| + | ZERO_NULL, /* setup_connection */ |
| + | dict_do, /* do_it */ |
| + | ZERO_NULL, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | ZERO_NULL, /* connect_it */ |
| + | ZERO_NULL, /* connecting */ |
| + | ZERO_NULL, /* doing */ |
| + | ZERO_NULL, /* proto_pollset */ |
| + | ZERO_NULL, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | ZERO_NULL, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | ZERO_NULL, /* write_resp */ |
| + | ZERO_NULL, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | ZERO_NULL, /* follow */ |
| + | PORT_DICT, /* defport */ |
| + | CURLPROTO_DICT, /* protocol */ |
| + | CURLPROTO_DICT, /* family */ |
| + | PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ |
| + | }; |
| + | |
| #endif /* CURL_DISABLE_DICT */ | |
| @@ -82,49 +82,6 @@ struct FILEPROTO { | |
| int fd; /* open file descriptor to read from! */ | |
| }; | |
| - | /* |
| - | * Forward declarations. |
| - | */ |
| - | |
| - | static CURLcode file_do(struct Curl_easy *data, bool *done); |
| - | static CURLcode file_done(struct Curl_easy *data, |
| - | CURLcode status, bool premature); |
| - | static CURLcode file_connect(struct Curl_easy *data, bool *done); |
| - | static CURLcode file_disconnect(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | bool dead_connection); |
| - | static CURLcode file_setup_connection(struct Curl_easy *data, |
| - | struct connectdata *conn); |
| - | |
| - | /* |
| - | * FILE scheme handler. |
| - | */ |
| - | |
| - | const struct Curl_handler Curl_handler_file = { |
| - | "file", /* scheme */ |
| - | file_setup_connection, /* setup_connection */ |
| - | file_do, /* do_it */ |
| - | file_done, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | file_connect, /* connect_it */ |
| - | ZERO_NULL, /* connecting */ |
| - | ZERO_NULL, /* doing */ |
| - | ZERO_NULL, /* proto_pollset */ |
| - | ZERO_NULL, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | ZERO_NULL, /* perform_pollset */ |
| - | file_disconnect, /* disconnect */ |
| - | ZERO_NULL, /* write_resp */ |
| - | ZERO_NULL, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | ZERO_NULL, /* follow */ |
| - | 0, /* defport */ |
| - | CURLPROTO_FILE, /* protocol */ |
| - | CURLPROTO_FILE, /* family */ |
| - | PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */ |
| - | }; |
| - | |
| static void file_cleanup(struct FILEPROTO *file) | |
| { | |
| Curl_safefree(file->freepath); | |
| @@ -158,6 +115,19 @@ static CURLcode file_setup_connection(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| + | static CURLcode file_done(struct Curl_easy *data, |
| + | CURLcode status, bool premature) |
| + | { |
| + | struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); |
| + | (void)status; |
| + | (void)premature; |
| + | |
| + | if(file) |
| + | file_cleanup(file); |
| + | |
| + | return CURLE_OK; |
| + | } |
| + | |
| /* | |
| * file_connect() gets called from Curl_protocol_connect() to allow us to | |
| * do protocol-specific actions at connect-time. We emulate a | |
| @@ -276,19 +246,6 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) | |
| return CURLE_OK; | |
| } | |
| - | static CURLcode file_done(struct Curl_easy *data, |
| - | CURLcode status, bool premature) |
| - | { |
| - | struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY); |
| - | (void)status; |
| - | (void)premature; |
| - | |
| - | if(file) |
| - | file_cleanup(file); |
| - | |
| - | return CURLE_OK; |
| - | } |
| - | |
| static CURLcode file_disconnect(struct Curl_easy *data, | |
| struct connectdata *conn, | |
| bool dead_connection) | |
| @@ -648,4 +605,32 @@ out: | |
| return result; | |
| } | |
| + | /* |
| + | * FILE scheme handler. |
| + | */ |
| + | const struct Curl_handler Curl_handler_file = { |
| + | "file", /* scheme */ |
| + | file_setup_connection, /* setup_connection */ |
| + | file_do, /* do_it */ |
| + | file_done, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | file_connect, /* connect_it */ |
| + | ZERO_NULL, /* connecting */ |
| + | ZERO_NULL, /* doing */ |
| + | ZERO_NULL, /* proto_pollset */ |
| + | ZERO_NULL, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | ZERO_NULL, /* perform_pollset */ |
| + | file_disconnect, /* disconnect */ |
| + | ZERO_NULL, /* write_resp */ |
| + | ZERO_NULL, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | ZERO_NULL, /* follow */ |
| + | 0, /* defport */ |
| + | CURLPROTO_FILE, /* protocol */ |
| + | CURLPROTO_FILE, /* family */ |
| + | PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */ |
| + | }; |
| + | |
| #endif | |
| @@ -36,73 +36,7 @@ | |
| #include "url.h" | |
| #include "escape.h" | |
| - | /* |
| - | * Forward declarations. |
| - | */ |
| - | |
| - | static CURLcode gopher_do(struct Curl_easy *data, bool *done); |
| #ifdef USE_SSL | |
| - | static CURLcode gopher_connect(struct Curl_easy *data, bool *done); |
| - | static CURLcode gopher_connecting(struct Curl_easy *data, bool *done); |
| - | #endif |
| - | |
| - | /* |
| - | * Gopher protocol handler. |
| - | * This is also a nice simple template to build off for simple |
| - | * connect-command-download protocols. |
| - | */ |
| - | |
| - | const struct Curl_handler Curl_handler_gopher = { |
| - | "gopher", /* scheme */ |
| - | ZERO_NULL, /* setup_connection */ |
| - | gopher_do, /* do_it */ |
| - | ZERO_NULL, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | ZERO_NULL, /* connect_it */ |
| - | ZERO_NULL, /* connecting */ |
| - | ZERO_NULL, /* doing */ |
| - | ZERO_NULL, /* proto_pollset */ |
| - | ZERO_NULL, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | ZERO_NULL, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | ZERO_NULL, /* write_resp */ |
| - | ZERO_NULL, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | ZERO_NULL, /* follow */ |
| - | PORT_GOPHER, /* defport */ |
| - | CURLPROTO_GOPHER, /* protocol */ |
| - | CURLPROTO_GOPHER, /* family */ |
| - | PROTOPT_NONE /* flags */ |
| - | }; |
| - | |
| - | #ifdef USE_SSL |
| - | const struct Curl_handler Curl_handler_gophers = { |
| - | "gophers", /* scheme */ |
| - | ZERO_NULL, /* setup_connection */ |
| - | gopher_do, /* do_it */ |
| - | ZERO_NULL, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | gopher_connect, /* connect_it */ |
| - | gopher_connecting, /* connecting */ |
| - | ZERO_NULL, /* doing */ |
| - | ZERO_NULL, /* proto_pollset */ |
| - | ZERO_NULL, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | ZERO_NULL, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | ZERO_NULL, /* write_resp */ |
| - | ZERO_NULL, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | ZERO_NULL, /* follow */ |
| - | PORT_GOPHER, /* defport */ |
| - | CURLPROTO_GOPHERS, /* protocol */ |
| - | CURLPROTO_GOPHER, /* family */ |
| - | PROTOPT_SSL /* flags */ |
| - | }; |
| - | |
| static CURLcode gopher_connect(struct Curl_easy *data, bool *done) | |
| { | |
| (void)data; | |
| @@ -231,4 +165,63 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) | |
| Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); | |
| return CURLE_OK; | |
| } | |
| + | |
| + | /* |
| + | * Gopher protocol handler. |
| + | * This is also a nice simple template to build off for simple |
| + | * connect-command-download protocols. |
| + | */ |
| + | |
| + | const struct Curl_handler Curl_handler_gopher = { |
| + | "gopher", /* scheme */ |
| + | ZERO_NULL, /* setup_connection */ |
| + | gopher_do, /* do_it */ |
| + | ZERO_NULL, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | ZERO_NULL, /* connect_it */ |
| + | ZERO_NULL, /* connecting */ |
| + | ZERO_NULL, /* doing */ |
| + | ZERO_NULL, /* proto_pollset */ |
| + | ZERO_NULL, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | ZERO_NULL, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | ZERO_NULL, /* write_resp */ |
| + | ZERO_NULL, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | ZERO_NULL, /* follow */ |
| + | PORT_GOPHER, /* defport */ |
| + | CURLPROTO_GOPHER, /* protocol */ |
| + | CURLPROTO_GOPHER, /* family */ |
| + | PROTOPT_NONE /* flags */ |
| + | }; |
| + | |
| + | #ifdef USE_SSL |
| + | const struct Curl_handler Curl_handler_gophers = { |
| + | "gophers", /* scheme */ |
| + | ZERO_NULL, /* setup_connection */ |
| + | gopher_do, /* do_it */ |
| + | ZERO_NULL, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | gopher_connect, /* connect_it */ |
| + | gopher_connecting, /* connecting */ |
| + | ZERO_NULL, /* doing */ |
| + | ZERO_NULL, /* proto_pollset */ |
| + | ZERO_NULL, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | ZERO_NULL, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | ZERO_NULL, /* write_resp */ |
| + | ZERO_NULL, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | ZERO_NULL, /* follow */ |
| + | PORT_GOPHER, /* defport */ |
| + | CURLPROTO_GOPHERS, /* protocol */ |
| + | CURLPROTO_GOPHER, /* family */ |
| + | PROTOPT_SSL /* flags */ |
| + | }; |
| + | #endif |
| + | |
| #endif /* CURL_DISABLE_GOPHER */ | |
| @@ -85,94 +85,6 @@ | |
| #include "bufref.h" | |
| #include "curlx/strparse.h" | |
| - | /* |
| - | * Forward declarations. |
| - | */ |
| - | |
| - | static bool http_should_fail(struct Curl_easy *data, int httpcode); |
| - | static bool http_exp100_is_waiting(struct Curl_easy *data); |
| - | static CURLcode http_exp100_add_reader(struct Curl_easy *data); |
| - | static void http_exp100_send_anyway(struct Curl_easy *data); |
| - | static bool http_exp100_is_selected(struct Curl_easy *data); |
| - | static void http_exp100_got100(struct Curl_easy *data); |
| - | static CURLcode http_firstwrite(struct Curl_easy *data); |
| - | static CURLcode http_header(struct Curl_easy *data, |
| - | const char *hd, size_t hdlen); |
| - | static CURLcode http_range(struct Curl_easy *data, |
| - | Curl_HttpReq httpreq); |
| - | static CURLcode http_req_set_TE(struct Curl_easy *data, |
| - | struct dynbuf *req, |
| - | int httpversion); |
| - | static CURLcode http_size(struct Curl_easy *data); |
| - | static CURLcode http_statusline(struct Curl_easy *data, |
| - | struct connectdata *conn); |
| - | static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req); |
| - | static CURLcode http_useragent(struct Curl_easy *data); |
| - | static CURLcode http_write_header(struct Curl_easy *data, |
| - | const char *hd, size_t hdlen); |
| - | |
| - | /* |
| - | * HTTP handler interface. |
| - | */ |
| - | const struct Curl_handler Curl_handler_http = { |
| - | "http", /* scheme */ |
| - | Curl_http_setup_conn, /* setup_connection */ |
| - | Curl_http, /* do_it */ |
| - | Curl_http_done, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | ZERO_NULL, /* connect_it */ |
| - | ZERO_NULL, /* connecting */ |
| - | ZERO_NULL, /* doing */ |
| - | ZERO_NULL, /* proto_pollset */ |
| - | Curl_http_doing_pollset, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | Curl_http_perform_pollset, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | Curl_http_write_resp, /* write_resp */ |
| - | Curl_http_write_resp_hd, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | Curl_http_follow, /* follow */ |
| - | PORT_HTTP, /* defport */ |
| - | CURLPROTO_HTTP, /* protocol */ |
| - | CURLPROTO_HTTP, /* family */ |
| - | PROTOPT_CREDSPERREQUEST | /* flags */ |
| - | PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE |
| - | |
| - | }; |
| - | |
| - | #ifdef USE_SSL |
| - | /* |
| - | * HTTPS handler interface. |
| - | */ |
| - | const struct Curl_handler Curl_handler_https = { |
| - | "https", /* scheme */ |
| - | Curl_http_setup_conn, /* setup_connection */ |
| - | Curl_http, /* do_it */ |
| - | Curl_http_done, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | ZERO_NULL, /* connect_it */ |
| - | NULL, /* connecting */ |
| - | ZERO_NULL, /* doing */ |
| - | NULL, /* proto_pollset */ |
| - | Curl_http_doing_pollset, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | Curl_http_perform_pollset, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | Curl_http_write_resp, /* write_resp */ |
| - | Curl_http_write_resp_hd, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | Curl_http_follow, /* follow */ |
| - | PORT_HTTPS, /* defport */ |
| - | CURLPROTO_HTTPS, /* protocol */ |
| - | CURLPROTO_HTTP, /* family */ |
| - | PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ |
| - | PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE |
| - | }; |
| - | |
| - | #endif |
| - | |
| void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg) | |
| { | |
| memset(neg, 0, sizeof(*neg)); | |
| @@ -547,13 +459,83 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| + | /** |
| + | * http_should_fail() determines whether an HTTP response code has gotten us |
| + | * into an error state or not. |
| + | * |
| + | * @retval FALSE communications should continue |
| + | * |
| + | * @retval TRUE communications should not continue |
| + | */ |
| + | static bool http_should_fail(struct Curl_easy *data, int httpcode) |
| + | { |
| + | DEBUGASSERT(data); |
| + | DEBUGASSERT(data->conn); |
| + | |
| + | /* |
| + | ** If we have not been asked to fail on error, |
| + | ** do not fail. |
| + | */ |
| + | if(!data->set.http_fail_on_error) |
| + | return FALSE; |
| + | |
| + | /* |
| + | ** Any code < 400 is never terminal. |
| + | */ |
| + | if(httpcode < 400) |
| + | return FALSE; |
| + | |
| + | /* |
| + | ** A 416 response to a resume request is presumably because the file is |
| + | ** already completely downloaded and thus not actually a fail. |
| + | */ |
| + | if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && |
| + | httpcode == 416) |
| + | return FALSE; |
| + | |
| + | /* |
| + | ** Any code >= 400 that is not 401 or 407 is always |
| + | ** a terminal error |
| + | */ |
| + | if((httpcode != 401) && (httpcode != 407)) |
| + | return TRUE; |
| + | |
| + | /* |
| + | ** All we have left to deal with is 401 and 407 |
| + | */ |
| + | DEBUGASSERT((httpcode == 401) || (httpcode == 407)); |
| + | |
| + | /* |
| + | ** Examine the current authentication state to see if this is an error. The |
| + | ** idea is for this function to get called after processing all the headers |
| + | ** in a response message. So, if we have been to asked to authenticate a |
| + | ** particular stage, and we have done it, we are OK. If we are already |
| + | ** completely authenticated, it is not OK to get another 401 or 407. |
| + | ** |
| + | ** It is possible for authentication to go stale such that the client needs |
| + | ** to reauthenticate. Once that info is available, use it here. |
| + | */ |
| + | |
| + | /* |
| + | ** Either we are not authenticating, or we are supposed to be authenticating |
| + | ** something else. This is an error. |
| + | */ |
| + | if((httpcode == 401) && !data->state.aptr.user) |
| + | return TRUE; |
| + | #ifndef CURL_DISABLE_PROXY |
| + | if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) |
| + | return TRUE; |
| + | #endif |
| + | |
| + | return data->state.authproblem; |
| + | } |
| + | |
| /* | |
| * Curl_http_auth_act() gets called when all HTTP headers have been received | |
| * and it checks what authentication methods that are available and decides | |
| * which one (if any) to use. It will set 'newurl' if an auth method was | |
| * picked. | |
| */ | |
| - | |
| CURLcode Curl_http_auth_act(struct Curl_easy *data) | |
| { | |
| struct connectdata *conn = data->conn; | |
| @@ -1116,77 +1098,6 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, | |
| #endif | |
| } | |
| - | /** |
| - | * http_should_fail() determines whether an HTTP response code has gotten us |
| - | * into an error state or not. |
| - | * |
| - | * @retval FALSE communications should continue |
| - | * |
| - | * @retval TRUE communications should not continue |
| - | */ |
| - | static bool http_should_fail(struct Curl_easy *data, int httpcode) |
| - | { |
| - | DEBUGASSERT(data); |
| - | DEBUGASSERT(data->conn); |
| - | |
| - | /* |
| - | ** If we have not been asked to fail on error, |
| - | ** do not fail. |
| - | */ |
| - | if(!data->set.http_fail_on_error) |
| - | return FALSE; |
| - | |
| - | /* |
| - | ** Any code < 400 is never terminal. |
| - | */ |
| - | if(httpcode < 400) |
| - | return FALSE; |
| - | |
| - | /* |
| - | ** A 416 response to a resume request is presumably because the file is |
| - | ** already completely downloaded and thus not actually a fail. |
| - | */ |
| - | if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && |
| - | httpcode == 416) |
| - | return FALSE; |
| - | |
| - | /* |
| - | ** Any code >= 400 that is not 401 or 407 is always |
| - | ** a terminal error |
| - | */ |
| - | if((httpcode != 401) && (httpcode != 407)) |
| - | return TRUE; |
| - | |
| - | /* |
| - | ** All we have left to deal with is 401 and 407 |
| - | */ |
| - | DEBUGASSERT((httpcode == 401) || (httpcode == 407)); |
| - | |
| - | /* |
| - | ** Examine the current authentication state to see if this is an error. The |
| - | ** idea is for this function to get called after processing all the headers |
| - | ** in a response message. So, if we have been to asked to authenticate a |
| - | ** particular stage, and we have done it, we are OK. If we are already |
| - | ** completely authenticated, it is not OK to get another 401 or 407. |
| - | ** |
| - | ** It is possible for authentication to go stale such that the client needs |
| - | ** to reauthenticate. Once that info is available, use it here. |
| - | */ |
| - | |
| - | /* |
| - | ** Either we are not authenticating, or we are supposed to be authenticating |
| - | ** something else. This is an error. |
| - | */ |
| - | if((httpcode == 401) && !data->state.aptr.user) |
| - | return TRUE; |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) |
| - | return TRUE; |
| - | #endif |
| - | |
| - | return data->state.authproblem; |
| - | } |
| - | |
| static void http_switch_to_get(struct Curl_easy *data, int code) | |
| { | |
| const char *req = data->set.str[STRING_CUSTOMREQUEST]; | |
| @@ -1532,6 +1443,145 @@ bool Curl_compareheader(const char *headerline, /* line to check */ | |
| return FALSE; /* no match */ | |
| } | |
| + | struct cr_exp100_ctx { |
| + | struct Curl_creader super; |
| + | struct curltime start; /* time started waiting */ |
| + | enum expect100 state; |
| + | }; |
| + | |
| + | /* Expect: 100-continue client reader, blocking uploads */ |
| + | |
| + | static void http_exp100_continue(struct Curl_easy *data, |
| + | struct Curl_creader *reader) |
| + | { |
| + | struct cr_exp100_ctx *ctx = reader->ctx; |
| + | if(ctx->state > EXP100_SEND_DATA) { |
| + | ctx->state = EXP100_SEND_DATA; |
| + | Curl_expire_done(data, EXPIRE_100_TIMEOUT); |
| + | } |
| + | } |
| + | |
| + | static CURLcode cr_exp100_read(struct Curl_easy *data, |
| + | struct Curl_creader *reader, |
| + | char *buf, size_t blen, |
| + | size_t *nread, bool *eos) |
| + | { |
| + | struct cr_exp100_ctx *ctx = reader->ctx; |
| + | timediff_t ms; |
| + | |
| + | switch(ctx->state) { |
| + | case EXP100_SENDING_REQUEST: |
| + | if(!Curl_req_sendbuf_empty(data)) { |
| + | /* The initial request data has not been fully sent yet. Do |
| + | * not start the timer yet. */ |
| + | DEBUGF(infof(data, "cr_exp100_read, request not full sent yet")); |
| + | *nread = 0; |
| + | *eos = FALSE; |
| + | return CURLE_OK; |
| + | } |
| + | /* We are now waiting for a reply from the server or |
| + | * a timeout on our side IFF the request has been fully sent. */ |
| + | DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, " |
| + | "timeout %dms", data->set.expect_100_timeout)); |
| + | ctx->state = EXP100_AWAITING_CONTINUE; |
| + | ctx->start = *Curl_pgrs_now(data); |
| + | Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); |
| + | *nread = 0; |
| + | *eos = FALSE; |
| + | return CURLE_OK; |
| + | case EXP100_FAILED: |
| + | DEBUGF(infof(data, "cr_exp100_read, expectation failed, error")); |
| + | *nread = 0; |
| + | *eos = FALSE; |
| + | return CURLE_READ_ERROR; |
| + | case EXP100_AWAITING_CONTINUE: |
| + | ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start); |
| + | if(ms < data->set.expect_100_timeout) { |
| + | DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); |
| + | *nread = 0; |
| + | *eos = FALSE; |
| + | return CURLE_OK; |
| + | } |
| + | /* we have waited long enough, continue anyway */ |
| + | http_exp100_continue(data, reader); |
| + | infof(data, "Done waiting for 100-continue"); |
| + | FALLTHROUGH(); |
| + | default: |
| + | DEBUGF(infof(data, "cr_exp100_read, pass through")); |
| + | return Curl_creader_read(data, reader->next, buf, blen, nread, eos); |
| + | } |
| + | } |
| + | |
| + | static void cr_exp100_done(struct Curl_easy *data, |
| + | struct Curl_creader *reader, int premature) |
| + | { |
| + | struct cr_exp100_ctx *ctx = reader->ctx; |
| + | ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA; |
| + | Curl_expire_done(data, EXPIRE_100_TIMEOUT); |
| + | } |
| + | |
| + | static const struct Curl_crtype cr_exp100 = { |
| + | "cr-exp100", |
| + | Curl_creader_def_init, |
| + | cr_exp100_read, |
| + | Curl_creader_def_close, |
| + | Curl_creader_def_needs_rewind, |
| + | Curl_creader_def_total_length, |
| + | Curl_creader_def_resume_from, |
| + | Curl_creader_def_cntrl, |
| + | Curl_creader_def_is_paused, |
| + | cr_exp100_done, |
| + | sizeof(struct cr_exp100_ctx) |
| + | }; |
| + | |
| + | static CURLcode http_exp100_add_reader(struct Curl_easy *data) |
| + | { |
| + | struct Curl_creader *reader = NULL; |
| + | CURLcode result; |
| + | |
| + | result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL); |
| + | if(!result) |
| + | result = Curl_creader_add(data, reader); |
| + | if(!result) { |
| + | struct cr_exp100_ctx *ctx = reader->ctx; |
| + | ctx->state = EXP100_SENDING_REQUEST; |
| + | } |
| + | |
| + | if(result && reader) |
| + | Curl_creader_free(data, reader); |
| + | return result; |
| + | } |
| + | |
| + | static void http_exp100_got100(struct Curl_easy *data) |
| + | { |
| + | struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); |
| + | if(r) |
| + | http_exp100_continue(data, r); |
| + | } |
| + | |
| + | static bool http_exp100_is_waiting(struct Curl_easy *data) |
| + | { |
| + | struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); |
| + | if(r) { |
| + | struct cr_exp100_ctx *ctx = r->ctx; |
| + | return ctx->state == EXP100_AWAITING_CONTINUE; |
| + | } |
| + | return FALSE; |
| + | } |
| + | |
| + | static void http_exp100_send_anyway(struct Curl_easy *data) |
| + | { |
| + | struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); |
| + | if(r) |
| + | http_exp100_continue(data, r); |
| + | } |
| + | |
| + | static bool http_exp100_is_selected(struct Curl_easy *data) |
| + | { |
| + | struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); |
| + | return !!r; |
| + | } |
| + | |
| /* this returns the socket to wait for in the DO and DOING state for the multi | |
| interface and then we are always _sending_ a request and thus we wait for | |
| the single socket to become writable only */ | |
| @@ -1559,6 +1609,33 @@ CURLcode Curl_http_perform_pollset(struct Curl_easy *data, | |
| return result; | |
| } | |
| + | static CURLcode http_write_header(struct Curl_easy *data, |
| + | const char *hd, size_t hdlen) |
| + | { |
| + | CURLcode result; |
| + | int writetype; |
| + | |
| + | /* now, only output this if the header AND body are requested: |
| + | */ |
| + | Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen); |
| + | |
| + | writetype = CLIENTWRITE_HEADER | |
| + | ((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0); |
| + | |
| + | result = Curl_client_write(data, writetype, hd, hdlen); |
| + | if(result) |
| + | return result; |
| + | |
| + | result = Curl_bump_headersize(data, hdlen, FALSE); |
| + | if(result) |
| + | return result; |
| + | |
| + | data->req.deductheadercount = (100 <= data->req.httpcode && |
| + | 199 >= data->req.httpcode) ? |
| + | data->req.headerbytecount : 0; |
| + | return result; |
| + | } |
| + | |
| /* | |
| * Curl_http_done() gets called after a single HTTP request has been | |
| * performed. | |
| @@ -3800,33 +3877,6 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| - | static CURLcode http_write_header(struct Curl_easy *data, |
| - | const char *hd, size_t hdlen) |
| - | { |
| - | CURLcode result; |
| - | int writetype; |
| - | |
| - | /* now, only output this if the header AND body are requested: |
| - | */ |
| - | Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen); |
| - | |
| - | writetype = CLIENTWRITE_HEADER | |
| - | ((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0); |
| - | |
| - | result = Curl_client_write(data, writetype, hd, hdlen); |
| - | if(result) |
| - | return result; |
| - | |
| - | result = Curl_bump_headersize(data, hdlen, FALSE); |
| - | if(result) |
| - | return result; |
| - | |
| - | data->req.deductheadercount = (100 <= data->req.httpcode && |
| - | 199 >= data->req.httpcode) ? |
| - | data->req.headerbytecount : 0; |
| - | return result; |
| - | } |
| - | |
| static CURLcode http_on_response(struct Curl_easy *data, | |
| const char *last_hd, size_t last_hd_len, | |
| const char *buf, size_t blen, | |
| @@ -4931,143 +4981,65 @@ void Curl_http_resp_free(struct http_resp *resp) | |
| } | |
| } | |
| - | struct cr_exp100_ctx { |
| - | struct Curl_creader super; |
| - | struct curltime start; /* time started waiting */ |
| - | enum expect100 state; |
| - | }; |
| - | |
| - | /* Expect: 100-continue client reader, blocking uploads */ |
| - | |
| - | static void http_exp100_continue(struct Curl_easy *data, |
| - | struct Curl_creader *reader) |
| - | { |
| - | struct cr_exp100_ctx *ctx = reader->ctx; |
| - | if(ctx->state > EXP100_SEND_DATA) { |
| - | ctx->state = EXP100_SEND_DATA; |
| - | Curl_expire_done(data, EXPIRE_100_TIMEOUT); |
| - | } |
| - | } |
| - | |
| - | static CURLcode cr_exp100_read(struct Curl_easy *data, |
| - | struct Curl_creader *reader, |
| - | char *buf, size_t blen, |
| - | size_t *nread, bool *eos) |
| - | { |
| - | struct cr_exp100_ctx *ctx = reader->ctx; |
| - | timediff_t ms; |
| - | |
| - | switch(ctx->state) { |
| - | case EXP100_SENDING_REQUEST: |
| - | if(!Curl_req_sendbuf_empty(data)) { |
| - | /* The initial request data has not been fully sent yet. Do |
| - | * not start the timer yet. */ |
| - | DEBUGF(infof(data, "cr_exp100_read, request not full sent yet")); |
| - | *nread = 0; |
| - | *eos = FALSE; |
| - | return CURLE_OK; |
| - | } |
| - | /* We are now waiting for a reply from the server or |
| - | * a timeout on our side IFF the request has been fully sent. */ |
| - | DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, " |
| - | "timeout %dms", data->set.expect_100_timeout)); |
| - | ctx->state = EXP100_AWAITING_CONTINUE; |
| - | ctx->start = *Curl_pgrs_now(data); |
| - | Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); |
| - | *nread = 0; |
| - | *eos = FALSE; |
| - | return CURLE_OK; |
| - | case EXP100_FAILED: |
| - | DEBUGF(infof(data, "cr_exp100_read, expectation failed, error")); |
| - | *nread = 0; |
| - | *eos = FALSE; |
| - | return CURLE_READ_ERROR; |
| - | case EXP100_AWAITING_CONTINUE: |
| - | ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start); |
| - | if(ms < data->set.expect_100_timeout) { |
| - | DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); |
| - | *nread = 0; |
| - | *eos = FALSE; |
| - | return CURLE_OK; |
| - | } |
| - | /* we have waited long enough, continue anyway */ |
| - | http_exp100_continue(data, reader); |
| - | infof(data, "Done waiting for 100-continue"); |
| - | FALLTHROUGH(); |
| - | default: |
| - | DEBUGF(infof(data, "cr_exp100_read, pass through")); |
| - | return Curl_creader_read(data, reader->next, buf, blen, nread, eos); |
| - | } |
| - | } |
| - | |
| - | static void cr_exp100_done(struct Curl_easy *data, |
| - | struct Curl_creader *reader, int premature) |
| - | { |
| - | struct cr_exp100_ctx *ctx = reader->ctx; |
| - | ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA; |
| - | Curl_expire_done(data, EXPIRE_100_TIMEOUT); |
| - | } |
| + | /* |
| + | * HTTP handler interface. |
| + | */ |
| + | const struct Curl_handler Curl_handler_http = { |
| + | "http", /* scheme */ |
| + | Curl_http_setup_conn, /* setup_connection */ |
| + | Curl_http, /* do_it */ |
| + | Curl_http_done, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | ZERO_NULL, /* connect_it */ |
| + | ZERO_NULL, /* connecting */ |
| + | ZERO_NULL, /* doing */ |
| + | ZERO_NULL, /* proto_pollset */ |
| + | Curl_http_doing_pollset, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | Curl_http_perform_pollset, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | Curl_http_write_resp, /* write_resp */ |
| + | Curl_http_write_resp_hd, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | Curl_http_follow, /* follow */ |
| + | PORT_HTTP, /* defport */ |
| + | CURLPROTO_HTTP, /* protocol */ |
| + | CURLPROTO_HTTP, /* family */ |
| + | PROTOPT_CREDSPERREQUEST | /* flags */ |
| + | PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE |
| - | static const struct Curl_crtype cr_exp100 = { |
| - | "cr-exp100", |
| - | Curl_creader_def_init, |
| - | cr_exp100_read, |
| - | Curl_creader_def_close, |
| - | Curl_creader_def_needs_rewind, |
| - | Curl_creader_def_total_length, |
| - | Curl_creader_def_resume_from, |
| - | Curl_creader_def_cntrl, |
| - | Curl_creader_def_is_paused, |
| - | cr_exp100_done, |
| - | sizeof(struct cr_exp100_ctx) |
| }; | |
| - | static CURLcode http_exp100_add_reader(struct Curl_easy *data) |
| - | { |
| - | struct Curl_creader *reader = NULL; |
| - | CURLcode result; |
| - | |
| - | result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL); |
| - | if(!result) |
| - | result = Curl_creader_add(data, reader); |
| - | if(!result) { |
| - | struct cr_exp100_ctx *ctx = reader->ctx; |
| - | ctx->state = EXP100_SENDING_REQUEST; |
| - | } |
| - | |
| - | if(result && reader) |
| - | Curl_creader_free(data, reader); |
| - | return result; |
| - | } |
| - | |
| - | static void http_exp100_got100(struct Curl_easy *data) |
| - | { |
| - | struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); |
| - | if(r) |
| - | http_exp100_continue(data, r); |
| - | } |
| - | |
| - | static bool http_exp100_is_waiting(struct Curl_easy *data) |
| - | { |
| - | struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); |
| - | if(r) { |
| - | struct cr_exp100_ctx *ctx = r->ctx; |
| - | return ctx->state == EXP100_AWAITING_CONTINUE; |
| - | } |
| - | return FALSE; |
| - | } |
| - | |
| - | static void http_exp100_send_anyway(struct Curl_easy *data) |
| - | { |
| - | struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); |
| - | if(r) |
| - | http_exp100_continue(data, r); |
| - | } |
| - | |
| - | static bool http_exp100_is_selected(struct Curl_easy *data) |
| - | { |
| - | struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); |
| - | return !!r; |
| - | } |
| + | #ifdef USE_SSL |
| + | /* |
| + | * HTTPS handler interface. |
| + | */ |
| + | const struct Curl_handler Curl_handler_https = { |
| + | "https", /* scheme */ |
| + | Curl_http_setup_conn, /* setup_connection */ |
| + | Curl_http, /* do_it */ |
| + | Curl_http_done, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | ZERO_NULL, /* connect_it */ |
| + | NULL, /* connecting */ |
| + | ZERO_NULL, /* doing */ |
| + | NULL, /* proto_pollset */ |
| + | Curl_http_doing_pollset, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | Curl_http_perform_pollset, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | Curl_http_write_resp, /* write_resp */ |
| + | Curl_http_write_resp_hd, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | Curl_http_follow, /* follow */ |
| + | PORT_HTTPS, /* defport */ |
| + | CURLPROTO_HTTPS, /* protocol */ |
| + | CURLPROTO_HTTP, /* family */ |
| + | PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ |
| + | PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE |
| + | }; |
| + | #endif |
| #endif /* CURL_DISABLE_HTTP */ | |
| @@ -89,48 +89,6 @@ struct MQTT { | |
| BIT(pingsent); /* 1 while we wait for ping response */ | |
| }; | |
| - | /* |
| - | * Forward declarations. |
| - | */ |
| - | |
| - | static CURLcode mqtt_do(struct Curl_easy *data, bool *done); |
| - | static CURLcode mqtt_done(struct Curl_easy *data, |
| - | CURLcode status, bool premature); |
| - | static CURLcode mqtt_doing(struct Curl_easy *data, bool *done); |
| - | static CURLcode mqtt_pollset(struct Curl_easy *data, |
| - | struct easy_pollset *ps); |
| - | static CURLcode mqtt_setup_conn(struct Curl_easy *data, |
| - | struct connectdata *conn); |
| - | |
| - | /* |
| - | * MQTT protocol handler. |
| - | */ |
| - | |
| - | const struct Curl_handler Curl_handler_mqtt = { |
| - | "mqtt", /* scheme */ |
| - | mqtt_setup_conn, /* setup_connection */ |
| - | mqtt_do, /* do_it */ |
| - | mqtt_done, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | ZERO_NULL, /* connect_it */ |
| - | ZERO_NULL, /* connecting */ |
| - | mqtt_doing, /* doing */ |
| - | ZERO_NULL, /* proto_pollset */ |
| - | mqtt_pollset, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | ZERO_NULL, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | ZERO_NULL, /* write_resp */ |
| - | ZERO_NULL, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | ZERO_NULL, /* follow */ |
| - | PORT_MQTT, /* defport */ |
| - | CURLPROTO_MQTT, /* protocol */ |
| - | CURLPROTO_MQTT, /* family */ |
| - | PROTOPT_NONE /* flags */ |
| - | }; |
| - | |
| static void mqtt_easy_dtor(void *key, size_t klen, void *entry) | |
| { | |
| struct MQTT *mq = entry; | |
| @@ -976,4 +934,33 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) | |
| return result; | |
| } | |
| + | /* |
| + | * MQTT protocol handler. |
| + | */ |
| + | |
| + | const struct Curl_handler Curl_handler_mqtt = { |
| + | "mqtt", /* scheme */ |
| + | mqtt_setup_conn, /* setup_connection */ |
| + | mqtt_do, /* do_it */ |
| + | mqtt_done, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | ZERO_NULL, /* connect_it */ |
| + | ZERO_NULL, /* connecting */ |
| + | mqtt_doing, /* doing */ |
| + | ZERO_NULL, /* proto_pollset */ |
| + | mqtt_pollset, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | ZERO_NULL, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | ZERO_NULL, /* write_resp */ |
| + | ZERO_NULL, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | ZERO_NULL, /* follow */ |
| + | PORT_MQTT, /* defport */ |
| + | CURLPROTO_MQTT, /* protocol */ |
| + | CURLPROTO_MQTT, /* family */ |
| + | PROTOPT_NONE /* flags */ |
| + | }; |
| + | |
| #endif /* CURL_DISABLE_MQTT */ | |
| @@ -285,77 +285,6 @@ struct smb_tree_disconnect { | |
| # pragma pack(pop) | |
| #endif | |
| - | /* Local API functions */ |
| - | static CURLcode smb_setup_connection(struct Curl_easy *data, |
| - | struct connectdata *conn); |
| - | static CURLcode smb_connect(struct Curl_easy *data, bool *done); |
| - | static CURLcode smb_connection_state(struct Curl_easy *data, bool *done); |
| - | static CURLcode smb_do(struct Curl_easy *data, bool *done); |
| - | static CURLcode smb_request_state(struct Curl_easy *data, bool *done); |
| - | static CURLcode smb_pollset(struct Curl_easy *data, |
| - | struct easy_pollset *ps); |
| - | static CURLcode smb_parse_url_path(struct Curl_easy *data, |
| - | struct smb_conn *smbc, |
| - | struct smb_request *req); |
| - | |
| - | /* |
| - | * SMB handler interface |
| - | */ |
| - | const struct Curl_handler Curl_handler_smb = { |
| - | "smb", /* scheme */ |
| - | smb_setup_connection, /* setup_connection */ |
| - | smb_do, /* do_it */ |
| - | ZERO_NULL, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | smb_connect, /* connect_it */ |
| - | smb_connection_state, /* connecting */ |
| - | smb_request_state, /* doing */ |
| - | smb_pollset, /* proto_pollset */ |
| - | smb_pollset, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | ZERO_NULL, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | ZERO_NULL, /* write_resp */ |
| - | ZERO_NULL, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | ZERO_NULL, /* follow */ |
| - | PORT_SMB, /* defport */ |
| - | CURLPROTO_SMB, /* protocol */ |
| - | CURLPROTO_SMB, /* family */ |
| - | PROTOPT_CONN_REUSE /* flags */ |
| - | }; |
| - | |
| - | #ifdef USE_SSL |
| - | /* |
| - | * SMBS handler interface |
| - | */ |
| - | const struct Curl_handler Curl_handler_smbs = { |
| - | "smbs", /* scheme */ |
| - | smb_setup_connection, /* setup_connection */ |
| - | smb_do, /* do_it */ |
| - | ZERO_NULL, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | smb_connect, /* connect_it */ |
| - | smb_connection_state, /* connecting */ |
| - | smb_request_state, /* doing */ |
| - | smb_pollset, /* proto_pollset */ |
| - | smb_pollset, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | ZERO_NULL, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | ZERO_NULL, /* write_resp */ |
| - | ZERO_NULL, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | ZERO_NULL, /* follow */ |
| - | PORT_SMBS, /* defport */ |
| - | CURLPROTO_SMBS, /* protocol */ |
| - | CURLPROTO_SMB, /* family */ |
| - | PROTOPT_SSL | PROTOPT_CONN_REUSE /* flags */ |
| - | }; |
| - | #endif |
| - | |
| #define MAX_PAYLOAD_SIZE 0x8000 | |
| #define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000) | |
| #define CLIENTNAME "curl" | |
| @@ -460,6 +389,49 @@ static void smb_conn_dtor(void *key, size_t klen, void *entry) | |
| curlx_free(smbc); | |
| } | |
| + | static CURLcode smb_parse_url_path(struct Curl_easy *data, |
| + | struct smb_conn *smbc, |
| + | struct smb_request *req) |
| + | { |
| + | char *path; |
| + | char *slash; |
| + | CURLcode result; |
| + | |
| + | /* URL decode the path */ |
| + | result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); |
| + | if(result) |
| + | return result; |
| + | |
| + | /* Parse the path for the share */ |
| + | smbc->share = curlx_strdup((*path == '/' || *path == '\\') |
| + | ? path + 1 : path); |
| + | curlx_free(path); |
| + | if(!smbc->share) |
| + | return CURLE_OUT_OF_MEMORY; |
| + | |
| + | slash = strchr(smbc->share, '/'); |
| + | if(!slash) |
| + | slash = strchr(smbc->share, '\\'); |
| + | |
| + | /* The share must be present */ |
| + | if(!slash) { |
| + | Curl_safefree(smbc->share); |
| + | failf(data, "missing share in URL path for SMB"); |
| + | return CURLE_URL_MALFORMAT; |
| + | } |
| + | |
| + | /* Parse the path for the file path converting any forward slashes into |
| + | backslashes */ |
| + | *slash++ = 0; |
| + | req->path = slash; |
| + | |
| + | for(; *slash; slash++) { |
| + | if(*slash == '/') |
| + | *slash = '\\'; |
| + | } |
| + | return CURLE_OK; |
| + | } |
| + | |
| /* this should setup things in the connection, not in the easy | |
| handle */ | |
| static CURLcode smb_setup_connection(struct Curl_easy *data, | |
| @@ -1226,47 +1198,62 @@ static CURLcode smb_do(struct Curl_easy *data, bool *done) | |
| return CURLE_URL_MALFORMAT; | |
| } | |
| - | static CURLcode smb_parse_url_path(struct Curl_easy *data, |
| - | struct smb_conn *smbc, |
| - | struct smb_request *req) |
| - | { |
| - | char *path; |
| - | char *slash; |
| - | CURLcode result; |
| - | |
| - | /* URL decode the path */ |
| - | result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); |
| - | if(result) |
| - | return result; |
| - | |
| - | /* Parse the path for the share */ |
| - | smbc->share = curlx_strdup((*path == '/' || *path == '\\') |
| - | ? path + 1 : path); |
| - | curlx_free(path); |
| - | if(!smbc->share) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | |
| - | slash = strchr(smbc->share, '/'); |
| - | if(!slash) |
| - | slash = strchr(smbc->share, '\\'); |
| - | |
| - | /* The share must be present */ |
| - | if(!slash) { |
| - | Curl_safefree(smbc->share); |
| - | failf(data, "missing share in URL path for SMB"); |
| - | return CURLE_URL_MALFORMAT; |
| - | } |
| - | |
| - | /* Parse the path for the file path converting any forward slashes into |
| - | backslashes */ |
| - | *slash++ = 0; |
| - | req->path = slash; |
| + | /* |
| + | * SMB handler interface |
| + | */ |
| + | const struct Curl_handler Curl_handler_smb = { |
| + | "smb", /* scheme */ |
| + | smb_setup_connection, /* setup_connection */ |
| + | smb_do, /* do_it */ |
| + | ZERO_NULL, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | smb_connect, /* connect_it */ |
| + | smb_connection_state, /* connecting */ |
| + | smb_request_state, /* doing */ |
| + | smb_pollset, /* proto_pollset */ |
| + | smb_pollset, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | ZERO_NULL, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | ZERO_NULL, /* write_resp */ |
| + | ZERO_NULL, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | ZERO_NULL, /* follow */ |
| + | PORT_SMB, /* defport */ |
| + | CURLPROTO_SMB, /* protocol */ |
| + | CURLPROTO_SMB, /* family */ |
| + | PROTOPT_CONN_REUSE /* flags */ |
| + | }; |
| - | for(; *slash; slash++) { |
| - | if(*slash == '/') |
| - | *slash = '\\'; |
| - | } |
| - | return CURLE_OK; |
| - | } |
| + | #ifdef USE_SSL |
| + | /* |
| + | * SMBS handler interface |
| + | */ |
| + | const struct Curl_handler Curl_handler_smbs = { |
| + | "smbs", /* scheme */ |
| + | smb_setup_connection, /* setup_connection */ |
| + | smb_do, /* do_it */ |
| + | ZERO_NULL, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | smb_connect, /* connect_it */ |
| + | smb_connection_state, /* connecting */ |
| + | smb_request_state, /* doing */ |
| + | smb_pollset, /* proto_pollset */ |
| + | smb_pollset, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | ZERO_NULL, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | ZERO_NULL, /* write_resp */ |
| + | ZERO_NULL, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | ZERO_NULL, /* follow */ |
| + | PORT_SMBS, /* defport */ |
| + | CURLPROTO_SMBS, /* protocol */ |
| + | CURLPROTO_SMB, /* family */ |
| + | PROTOPT_SSL | PROTOPT_CONN_REUSE /* flags */ |
| + | }; |
| + | #endif |
| #endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ | |
| @@ -142,49 +142,6 @@ struct tftp_conn { | |
| BIT(remote_pinned); | |
| }; | |
| - | /* Forward declarations */ |
| - | static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event); |
| - | static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event); |
| - | static CURLcode tftp_connect(struct Curl_easy *data, bool *done); |
| - | static CURLcode tftp_do(struct Curl_easy *data, bool *done); |
| - | static CURLcode tftp_done(struct Curl_easy *data, |
| - | CURLcode, bool premature); |
| - | static CURLcode tftp_setup_connection(struct Curl_easy *data, |
| - | struct connectdata *conn); |
| - | static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done); |
| - | static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done); |
| - | static CURLcode tftp_pollset(struct Curl_easy *data, |
| - | struct easy_pollset *ps); |
| - | static CURLcode tftp_translate_code(tftp_error_t error); |
| - | |
| - | /* |
| - | * TFTP protocol handler. |
| - | */ |
| - | const struct Curl_handler Curl_handler_tftp = { |
| - | "tftp", /* scheme */ |
| - | tftp_setup_connection, /* setup_connection */ |
| - | tftp_do, /* do_it */ |
| - | tftp_done, /* done */ |
| - | ZERO_NULL, /* do_more */ |
| - | tftp_connect, /* connect_it */ |
| - | tftp_multi_statemach, /* connecting */ |
| - | tftp_doing, /* doing */ |
| - | tftp_pollset, /* proto_pollset */ |
| - | tftp_pollset, /* doing_pollset */ |
| - | ZERO_NULL, /* domore_pollset */ |
| - | ZERO_NULL, /* perform_pollset */ |
| - | ZERO_NULL, /* disconnect */ |
| - | ZERO_NULL, /* write_resp */ |
| - | ZERO_NULL, /* write_resp_hd */ |
| - | ZERO_NULL, /* connection_check */ |
| - | ZERO_NULL, /* attach connection */ |
| - | ZERO_NULL, /* follow */ |
| - | PORT_TFTP, /* defport */ |
| - | CURLPROTO_TFTP, /* protocol */ |
| - | CURLPROTO_TFTP, /* family */ |
| - | PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */ |
| - | }; |
| - | |
| /********************************************************** | |
| * | |
| * tftp_set_timeouts - | |
| @@ -383,185 +340,175 @@ static CURLcode tftp_option_add(struct tftp_conn *state, size_t *csize, | |
| return CURLE_OK; | |
| } | |
| - | static CURLcode tftp_connect_for_tx(struct tftp_conn *state, |
| - | tftp_event_t event) |
| - | { |
| - | CURLcode result; |
| - | #ifndef CURL_DISABLE_VERBOSE_STRINGS |
| - | struct Curl_easy *data = state->data; |
| - | |
| - | infof(data, "%s", "Connected for transmit"); |
| - | #endif |
| - | state->state = TFTP_STATE_TX; |
| - | result = tftp_set_timeouts(state); |
| - | if(result) |
| - | return result; |
| - | return tftp_tx(state, event); |
| - | } |
| - | |
| - | static CURLcode tftp_connect_for_rx(struct tftp_conn *state, |
| - | tftp_event_t event) |
| - | { |
| - | CURLcode result; |
| - | #ifndef CURL_DISABLE_VERBOSE_STRINGS |
| - | struct Curl_easy *data = state->data; |
| - | |
| - | infof(data, "%s", "Connected for receive"); |
| - | #endif |
| - | state->state = TFTP_STATE_RX; |
| - | result = tftp_set_timeouts(state); |
| - | if(result) |
| - | return result; |
| - | return tftp_rx(state, event); |
| - | } |
| + | /* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit |
| + | boundary */ |
| + | #define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff) |
| - | static CURLcode tftp_send_first(struct tftp_conn *state, |
| - | tftp_event_t event) |
| + | /********************************************************** |
| + | * |
| + | * tftp_tx |
| + | * |
| + | * Event handler for the TX state |
| + | * |
| + | **********************************************************/ |
| + | static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) |
| { | |
| - | size_t sbytes; |
| - | ssize_t senddata; |
| - | const char *mode = "octet"; |
| - | char *filename; |
| struct Curl_easy *data = state->data; | |
| - | const struct Curl_sockaddr_ex *remote_addr = NULL; |
| + | ssize_t sbytes; |
| CURLcode result = CURLE_OK; | |
| - | |
| - | /* Set ASCII mode if -B flag was used */ |
| - | if(data->state.prefer_ascii) |
| - | mode = "netascii"; |
| + | struct SingleRequest *k = &data->req; |
| + | size_t cb; /* Bytes currently read */ |
| + | char buffer[STRERROR_LEN]; |
| + | char *bufptr; |
| + | bool eos; |
| switch(event) { | |
| - | case TFTP_EVENT_INIT: /* Send the first packet out */ |
| - | case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ |
| - | /* Increment the retry counter, quit if over the limit */ |
| - | state->retries++; |
| - | if(state->retries > state->retry_max) { |
| - | state->error = TFTP_ERR_NORESPONSE; |
| - | state->state = TFTP_STATE_FIN; |
| - | return result; |
| - | } |
| + | case TFTP_EVENT_ACK: |
| + | case TFTP_EVENT_OACK: |
| + | if(event == TFTP_EVENT_ACK) { |
| + | /* Ack the packet */ |
| + | int rblock = getrpacketblock(&state->rpacket); |
| - | if(data->state.upload) { |
| - | /* If we are uploading, send an WRQ */ |
| - | setpacketevent(&state->spacket, TFTP_EVENT_WRQ); |
| - | if(data->state.infilesize != -1) |
| - | Curl_pgrsSetUploadSize(data, data->state.infilesize); |
| - | } |
| - | else { |
| - | /* If we are downloading, send an RRQ */ |
| - | setpacketevent(&state->spacket, TFTP_EVENT_RRQ); |
| - | } |
| - | /* As RFC3617 describes the separator slash is not actually part of the |
| - | filename so we skip the always-present first letter of the path |
| - | string. */ |
| - | if(!state->data->state.up.path[1]) { |
| - | failf(data, "Missing filename"); |
| - | return CURLE_TFTP_ILLEGAL; |
| - | } |
| - | result = Curl_urldecode(&state->data->state.up.path[1], 0, |
| - | &filename, NULL, REJECT_ZERO); |
| - | if(result) |
| - | return result; |
| + | if(rblock != state->block && |
| + | /* There is a bug in tftpd-hpa that causes it to send us an ack for |
| + | * 65535 when the block number wraps to 0. So when we are expecting |
| + | * 0, also accept 65535. See |
| + | * https://www.syslinux.org/archives/2010-September/015612.html |
| + | * */ |
| + | !(state->block == 0 && rblock == 65535)) { |
| + | /* This is not the expected block. Log it and up the retry counter */ |
| + | infof(data, "Received ACK for block %d, expecting %d", |
| + | rblock, state->block); |
| + | state->retries++; |
| + | /* Bail out if over the maximum */ |
| + | if(state->retries > state->retry_max) { |
| + | failf(data, "tftp_tx: giving up waiting for block %d ack", |
| + | state->block); |
| + | result = CURLE_SEND_ERROR; |
| + | } |
| + | else { |
| + | /* Re-send the data packet */ |
| + | sbytes = sendto(state->sockfd, (void *)state->spacket.data, |
| + | 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, |
| + | (struct sockaddr *)&state->remote_addr, |
| + | state->remote_addrlen); |
| + | /* Check all sbytes were sent */ |
| + | if(sbytes < 0) { |
| + | failf(data, "%s", |
| + | curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); |
| + | result = CURLE_SEND_ERROR; |
| + | } |
| + | } |
| - | if(strlen(filename) > (state->blksize - strlen(mode) - 4)) { |
| - | failf(data, "TFTP filename too long"); |
| - | curlx_free(filename); |
| - | return CURLE_TFTP_ILLEGAL; /* too long filename field */ |
| + | return result; |
| + | } |
| + | /* This is the expected packet. Reset the counters and send the next |
| + | block */ |
| + | state->rx_time = time(NULL); |
| + | state->block++; |
| } | |
| + | else |
| + | state->block = 1; /* first data block is 1 when using OACK */ |
| - | curl_msnprintf((char *)state->spacket.data + 2, |
| - | state->blksize, |
| - | "%s%c%s%c", filename, '\0', mode, '\0'); |
| - | sbytes = 4 + strlen(filename) + strlen(mode); |
| - | curlx_free(filename); |
| - | |
| - | /* optional addition of TFTP options */ |
| - | if(!data->set.tftp_no_options) { |
| - | char buf[64]; |
| - | /* add tsize option */ |
| - | curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, |
| - | data->state.upload && (data->state.infilesize != -1) ? |
| - | data->state.infilesize : 0); |
| - | |
| - | result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE); |
| - | if(result == CURLE_OK) |
| - | result = tftp_option_add(state, &sbytes, sbytes, buf); |
| - | |
| - | /* add blksize option */ |
| - | curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); |
| - | if(result == CURLE_OK) |
| - | result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE); |
| - | if(result == CURLE_OK) |
| - | result = tftp_option_add(state, &sbytes, sbytes, buf); |
| - | |
| - | /* add timeout option */ |
| - | curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time); |
| - | if(result == CURLE_OK) |
| - | result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL); |
| - | if(result == CURLE_OK) |
| - | result = tftp_option_add(state, &sbytes, sbytes, buf); |
| - | |
| - | if(result != CURLE_OK) { |
| - | failf(data, "TFTP buffer too small for options"); |
| - | return CURLE_TFTP_ILLEGAL; |
| - | } |
| + | state->retries = 0; |
| + | setpacketevent(&state->spacket, TFTP_EVENT_DATA); |
| + | setpacketblock(&state->spacket, state->block); |
| + | if(state->block > 1 && state->sbytes < state->blksize) { |
| + | state->state = TFTP_STATE_FIN; |
| + | return CURLE_OK; |
| } | |
| - | /* the typecase for the 3rd argument is mostly for systems that do |
| - | not have a size_t argument, like older unixes that want an 'int' */ |
| - | #ifdef __AMIGA__ |
| - | #define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) |
| - | #else |
| - | #define CURL_SENDTO_ARG5(x) (x) |
| - | #endif |
| - | remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET); |
| - | if(!remote_addr) |
| - | return CURLE_FAILED_INIT; |
| + | /* TFTP considers data block size < 512 bytes as an end of session. So |
| + | * in some cases we must wait for additional data to build full (512 bytes) |
| + | * data block. |
| + | * */ |
| + | state->sbytes = 0; |
| + | bufptr = (char *)state->spacket.data + 4; |
| + | do { |
| + | result = Curl_client_read(data, bufptr, state->blksize - state->sbytes, |
| + | &cb, &eos); |
| + | if(result) |
| + | return result; |
| + | state->sbytes += cb; |
| + | bufptr += cb; |
| + | } while(state->sbytes < state->blksize && cb); |
| - | senddata = sendto(state->sockfd, (void *)state->spacket.data, |
| - | (SEND_TYPE_ARG3)sbytes, 0, |
| - | CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr), |
| - | (curl_socklen_t)remote_addr->addrlen); |
| - | if(senddata != (ssize_t)sbytes) { |
| - | char buffer[STRERROR_LEN]; |
| + | sbytes = sendto(state->sockfd, (void *)state->spacket.data, |
| + | 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, |
| + | (struct sockaddr *)&state->remote_addr, |
| + | state->remote_addrlen); |
| + | /* Check all sbytes were sent */ |
| + | if(sbytes < 0) { |
| failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| return CURLE_SEND_ERROR; | |
| } | |
| + | /* Update the progress meter */ |
| + | k->writebytecount += state->sbytes; |
| + | Curl_pgrs_upload_inc(data, state->sbytes); |
| break; | |
| - | case TFTP_EVENT_OACK: |
| - | if(data->state.upload) { |
| - | result = tftp_connect_for_tx(state, event); |
| + | case TFTP_EVENT_TIMEOUT: |
| + | /* Increment the retry counter and log the timeout */ |
| + | state->retries++; |
| + | infof(data, "Timeout waiting for block %d ACK. " |
| + | " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); |
| + | /* Decide if we have had enough */ |
| + | if(state->retries > state->retry_max) { |
| + | state->error = TFTP_ERR_TIMEOUT; |
| + | state->state = TFTP_STATE_FIN; |
| } | |
| else { | |
| - | result = tftp_connect_for_rx(state, event); |
| + | /* Re-send the data packet */ |
| + | sbytes = sendto(state->sockfd, (void *)state->spacket.data, |
| + | 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, |
| + | (struct sockaddr *)&state->remote_addr, |
| + | state->remote_addrlen); |
| + | /* Check all sbytes were sent */ |
| + | if(sbytes < 0) { |
| + | failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); |
| + | return CURLE_SEND_ERROR; |
| + | } |
| + | /* since this was a re-send, we remain at the still byte position */ |
| + | Curl_pgrsSetUploadCounter(data, k->writebytecount); |
| } | |
| break; | |
| - | case TFTP_EVENT_ACK: /* Connected for transmit */ |
| - | result = tftp_connect_for_tx(state, event); |
| - | break; |
| - | |
| - | case TFTP_EVENT_DATA: /* Connected for receive */ |
| - | result = tftp_connect_for_rx(state, event); |
| - | break; |
| - | |
| case TFTP_EVENT_ERROR: | |
| state->state = TFTP_STATE_FIN; | |
| + | setpacketevent(&state->spacket, TFTP_EVENT_ERROR); |
| + | setpacketblock(&state->spacket, state->block); |
| + | (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, |
| + | (struct sockaddr *)&state->remote_addr, |
| + | state->remote_addrlen); |
| + | /* do not bother with the return code, but if the socket is still up we |
| + | * should be a good TFTP client and let the server know we are done */ |
| + | state->state = TFTP_STATE_FIN; |
| break; | |
| default: | |
| - | failf(state->data, "tftp_send_first: internal error"); |
| - | return CURLE_TFTP_ILLEGAL; |
| + | failf(data, "tftp_tx: internal error, event: %i", (int)(event)); |
| + | break; |
| } | |
| return result; | |
| } | |
| - | /* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit |
| - | boundary */ |
| - | #define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff) |
| + | static CURLcode tftp_connect_for_tx(struct tftp_conn *state, |
| + | tftp_event_t event) |
| + | { |
| + | CURLcode result; |
| + | #ifndef CURL_DISABLE_VERBOSE_STRINGS |
| + | struct Curl_easy *data = state->data; |
| + | |
| + | infof(data, "%s", "Connected for transmit"); |
| + | #endif |
| + | state->state = TFTP_STATE_TX; |
| + | result = tftp_set_timeouts(state); |
| + | if(result) |
| + | return result; |
| + | return tftp_tx(state, event); |
| + | } |
| /********************************************************** | |
| * | |
| @@ -609,217 +556,67 @@ static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event) | |
| state->remote_addrlen); | |
| if(sbytes < 0) { | |
| failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| - | return CURLE_SEND_ERROR; |
| - | } |
| - | |
| - | /* Check if completed (That is, a less than full packet is received) */ |
| - | if(state->rbytes < (ssize_t)state->blksize + 4) { |
| - | state->state = TFTP_STATE_FIN; |
| - | } |
| - | else { |
| - | state->state = TFTP_STATE_RX; |
| - | } |
| - | state->rx_time = time(NULL); |
| - | break; |
| - | |
| - | case TFTP_EVENT_OACK: |
| - | /* ACK option acknowledgement so we can move on to data */ |
| - | state->block = 0; |
| - | state->retries = 0; |
| - | setpacketevent(&state->spacket, TFTP_EVENT_ACK); |
| - | setpacketblock(&state->spacket, state->block); |
| - | sbytes = sendto(state->sockfd, (void *)state->spacket.data, |
| - | 4, SEND_4TH_ARG, |
| - | (struct sockaddr *)&state->remote_addr, |
| - | state->remote_addrlen); |
| - | if(sbytes < 0) { |
| - | failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); |
| - | return CURLE_SEND_ERROR; |
| - | } |
| - | |
| - | /* we are ready to RX data */ |
| - | state->state = TFTP_STATE_RX; |
| - | state->rx_time = time(NULL); |
| - | break; |
| - | |
| - | case TFTP_EVENT_TIMEOUT: |
| - | /* Increment the retry count and fail if over the limit */ |
| - | state->retries++; |
| - | infof(data, |
| - | "Timeout waiting for block %d ACK. Retries = %d", |
| - | NEXT_BLOCKNUM(state->block), state->retries); |
| - | if(state->retries > state->retry_max) { |
| - | state->error = TFTP_ERR_TIMEOUT; |
| - | state->state = TFTP_STATE_FIN; |
| - | } |
| - | else { |
| - | /* Resend the previous ACK */ |
| - | sbytes = sendto(state->sockfd, (void *)state->spacket.data, |
| - | 4, SEND_4TH_ARG, |
| - | (struct sockaddr *)&state->remote_addr, |
| - | state->remote_addrlen); |
| - | if(sbytes < 0) { |
| - | failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); |
| - | return CURLE_SEND_ERROR; |
| - | } |
| - | } |
| - | break; |
| - | |
| - | case TFTP_EVENT_ERROR: |
| - | setpacketevent(&state->spacket, TFTP_EVENT_ERROR); |
| - | setpacketblock(&state->spacket, state->block); |
| - | (void)sendto(state->sockfd, (void *)state->spacket.data, |
| - | 4, SEND_4TH_ARG, |
| - | (struct sockaddr *)&state->remote_addr, |
| - | state->remote_addrlen); |
| - | /* do not bother with the return code, but if the socket is still up we |
| - | * should be a good TFTP client and let the server know we are done */ |
| - | state->state = TFTP_STATE_FIN; |
| - | break; |
| - | |
| - | default: |
| - | failf(data, "%s", "tftp_rx: internal error"); |
| - | return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for |
| - | this */ |
| - | } |
| - | return CURLE_OK; |
| - | } |
| - | |
| - | /********************************************************** |
| - | * |
| - | * tftp_tx |
| - | * |
| - | * Event handler for the TX state |
| - | * |
| - | **********************************************************/ |
| - | static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) |
| - | { |
| - | struct Curl_easy *data = state->data; |
| - | ssize_t sbytes; |
| - | CURLcode result = CURLE_OK; |
| - | struct SingleRequest *k = &data->req; |
| - | size_t cb; /* Bytes currently read */ |
| - | char buffer[STRERROR_LEN]; |
| - | char *bufptr; |
| - | bool eos; |
| - | |
| - | switch(event) { |
| - | |
| - | case TFTP_EVENT_ACK: |
| - | case TFTP_EVENT_OACK: |
| - | if(event == TFTP_EVENT_ACK) { |
| - | /* Ack the packet */ |
| - | int rblock = getrpacketblock(&state->rpacket); |
| - | |
| - | if(rblock != state->block && |
| - | /* There is a bug in tftpd-hpa that causes it to send us an ack for |
| - | * 65535 when the block number wraps to 0. So when we are expecting |
| - | * 0, also accept 65535. See |
| - | * https://www.syslinux.org/archives/2010-September/015612.html |
| - | * */ |
| - | !(state->block == 0 && rblock == 65535)) { |
| - | /* This is not the expected block. Log it and up the retry counter */ |
| - | infof(data, "Received ACK for block %d, expecting %d", |
| - | rblock, state->block); |
| - | state->retries++; |
| - | /* Bail out if over the maximum */ |
| - | if(state->retries > state->retry_max) { |
| - | failf(data, "tftp_tx: giving up waiting for block %d ack", |
| - | state->block); |
| - | result = CURLE_SEND_ERROR; |
| - | } |
| - | else { |
| - | /* Re-send the data packet */ |
| - | sbytes = sendto(state->sockfd, (void *)state->spacket.data, |
| - | 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, |
| - | (struct sockaddr *)&state->remote_addr, |
| - | state->remote_addrlen); |
| - | /* Check all sbytes were sent */ |
| - | if(sbytes < 0) { |
| - | failf(data, "%s", |
| - | curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); |
| - | result = CURLE_SEND_ERROR; |
| - | } |
| - | } |
| - | |
| - | return result; |
| - | } |
| - | /* This is the expected packet. Reset the counters and send the next |
| - | block */ |
| - | state->rx_time = time(NULL); |
| - | state->block++; |
| + | return CURLE_SEND_ERROR; |
| } | |
| - | else |
| - | state->block = 1; /* first data block is 1 when using OACK */ |
| - | state->retries = 0; |
| - | setpacketevent(&state->spacket, TFTP_EVENT_DATA); |
| - | setpacketblock(&state->spacket, state->block); |
| - | if(state->block > 1 && state->sbytes < state->blksize) { |
| + | /* Check if completed (That is, a less than full packet is received) */ |
| + | if(state->rbytes < (ssize_t)state->blksize + 4) { |
| state->state = TFTP_STATE_FIN; | |
| - | return CURLE_OK; |
| } | |
| + | else { |
| + | state->state = TFTP_STATE_RX; |
| + | } |
| + | state->rx_time = time(NULL); |
| + | break; |
| - | /* TFTP considers data block size < 512 bytes as an end of session. So |
| - | * in some cases we must wait for additional data to build full (512 bytes) |
| - | * data block. |
| - | * */ |
| - | state->sbytes = 0; |
| - | bufptr = (char *)state->spacket.data + 4; |
| - | do { |
| - | result = Curl_client_read(data, bufptr, state->blksize - state->sbytes, |
| - | &cb, &eos); |
| - | if(result) |
| - | return result; |
| - | state->sbytes += cb; |
| - | bufptr += cb; |
| - | } while(state->sbytes < state->blksize && cb); |
| - | |
| + | case TFTP_EVENT_OACK: |
| + | /* ACK option acknowledgement so we can move on to data */ |
| + | state->block = 0; |
| + | state->retries = 0; |
| + | setpacketevent(&state->spacket, TFTP_EVENT_ACK); |
| + | setpacketblock(&state->spacket, state->block); |
| sbytes = sendto(state->sockfd, (void *)state->spacket.data, | |
| - | 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, |
| + | 4, SEND_4TH_ARG, |
| (struct sockaddr *)&state->remote_addr, | |
| state->remote_addrlen); | |
| - | /* Check all sbytes were sent */ |
| if(sbytes < 0) { | |
| failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| return CURLE_SEND_ERROR; | |
| } | |
| - | /* Update the progress meter */ |
| - | k->writebytecount += state->sbytes; |
| - | Curl_pgrs_upload_inc(data, state->sbytes); |
| + | |
| + | /* we are ready to RX data */ |
| + | state->state = TFTP_STATE_RX; |
| + | state->rx_time = time(NULL); |
| break; | |
| case TFTP_EVENT_TIMEOUT: | |
| - | /* Increment the retry counter and log the timeout */ |
| + | /* Increment the retry count and fail if over the limit */ |
| state->retries++; | |
| - | infof(data, "Timeout waiting for block %d ACK. " |
| - | " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries); |
| - | /* Decide if we have had enough */ |
| + | infof(data, |
| + | "Timeout waiting for block %d ACK. Retries = %d", |
| + | NEXT_BLOCKNUM(state->block), state->retries); |
| if(state->retries > state->retry_max) { | |
| state->error = TFTP_ERR_TIMEOUT; | |
| state->state = TFTP_STATE_FIN; | |
| } | |
| else { | |
| - | /* Re-send the data packet */ |
| + | /* Resend the previous ACK */ |
| sbytes = sendto(state->sockfd, (void *)state->spacket.data, | |
| - | 4 + (SEND_TYPE_ARG3)state->sbytes, SEND_4TH_ARG, |
| + | 4, SEND_4TH_ARG, |
| (struct sockaddr *)&state->remote_addr, | |
| state->remote_addrlen); | |
| - | /* Check all sbytes were sent */ |
| if(sbytes < 0) { | |
| failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); | |
| return CURLE_SEND_ERROR; | |
| } | |
| - | /* since this was a re-send, we remain at the still byte position */ |
| - | Curl_pgrsSetUploadCounter(data, k->writebytecount); |
| } | |
| break; | |
| case TFTP_EVENT_ERROR: | |
| - | state->state = TFTP_STATE_FIN; |
| setpacketevent(&state->spacket, TFTP_EVENT_ERROR); | |
| setpacketblock(&state->spacket, state->block); | |
| - | (void)sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, |
| + | (void)sendto(state->sockfd, (void *)state->spacket.data, |
| + | 4, SEND_4TH_ARG, |
| (struct sockaddr *)&state->remote_addr, | |
| state->remote_addrlen); | |
| /* do not bother with the return code, but if the socket is still up we | |
| @@ -828,8 +625,168 @@ static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) | |
| break; | |
| default: | |
| - | failf(data, "tftp_tx: internal error, event: %i", (int)(event)); |
| + | failf(data, "%s", "tftp_rx: internal error"); |
| + | return CURLE_TFTP_ILLEGAL; /* not really the perfect return code for |
| + | this */ |
| + | } |
| + | return CURLE_OK; |
| + | } |
| + | |
| + | static CURLcode tftp_connect_for_rx(struct tftp_conn *state, |
| + | tftp_event_t event) |
| + | { |
| + | CURLcode result; |
| + | #ifndef CURL_DISABLE_VERBOSE_STRINGS |
| + | struct Curl_easy *data = state->data; |
| + | |
| + | infof(data, "%s", "Connected for receive"); |
| + | #endif |
| + | state->state = TFTP_STATE_RX; |
| + | result = tftp_set_timeouts(state); |
| + | if(result) |
| + | return result; |
| + | return tftp_rx(state, event); |
| + | } |
| + | |
| + | static CURLcode tftp_send_first(struct tftp_conn *state, |
| + | tftp_event_t event) |
| + | { |
| + | size_t sbytes; |
| + | ssize_t senddata; |
| + | const char *mode = "octet"; |
| + | char *filename; |
| + | struct Curl_easy *data = state->data; |
| + | const struct Curl_sockaddr_ex *remote_addr = NULL; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | /* Set ASCII mode if -B flag was used */ |
| + | if(data->state.prefer_ascii) |
| + | mode = "netascii"; |
| + | |
| + | switch(event) { |
| + | |
| + | case TFTP_EVENT_INIT: /* Send the first packet out */ |
| + | case TFTP_EVENT_TIMEOUT: /* Resend the first packet out */ |
| + | /* Increment the retry counter, quit if over the limit */ |
| + | state->retries++; |
| + | if(state->retries > state->retry_max) { |
| + | state->error = TFTP_ERR_NORESPONSE; |
| + | state->state = TFTP_STATE_FIN; |
| + | return result; |
| + | } |
| + | |
| + | if(data->state.upload) { |
| + | /* If we are uploading, send an WRQ */ |
| + | setpacketevent(&state->spacket, TFTP_EVENT_WRQ); |
| + | if(data->state.infilesize != -1) |
| + | Curl_pgrsSetUploadSize(data, data->state.infilesize); |
| + | } |
| + | else { |
| + | /* If we are downloading, send an RRQ */ |
| + | setpacketevent(&state->spacket, TFTP_EVENT_RRQ); |
| + | } |
| + | /* As RFC3617 describes the separator slash is not actually part of the |
| + | filename so we skip the always-present first letter of the path |
| + | string. */ |
| + | if(!state->data->state.up.path[1]) { |
| + | failf(data, "Missing filename"); |
| + | return CURLE_TFTP_ILLEGAL; |
| + | } |
| + | result = Curl_urldecode(&state->data->state.up.path[1], 0, |
| + | &filename, NULL, REJECT_ZERO); |
| + | if(result) |
| + | return result; |
| + | |
| + | if(strlen(filename) > (state->blksize - strlen(mode) - 4)) { |
| + | failf(data, "TFTP filename too long"); |
| + | curlx_free(filename); |
| + | return CURLE_TFTP_ILLEGAL; /* too long filename field */ |
| + | } |
| + | |
| + | curl_msnprintf((char *)state->spacket.data + 2, |
| + | state->blksize, |
| + | "%s%c%s%c", filename, '\0', mode, '\0'); |
| + | sbytes = 4 + strlen(filename) + strlen(mode); |
| + | curlx_free(filename); |
| + | |
| + | /* optional addition of TFTP options */ |
| + | if(!data->set.tftp_no_options) { |
| + | char buf[64]; |
| + | /* add tsize option */ |
| + | curl_msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, |
| + | data->state.upload && (data->state.infilesize != -1) ? |
| + | data->state.infilesize : 0); |
| + | |
| + | result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE); |
| + | if(result == CURLE_OK) |
| + | result = tftp_option_add(state, &sbytes, sbytes, buf); |
| + | |
| + | /* add blksize option */ |
| + | curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); |
| + | if(result == CURLE_OK) |
| + | result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE); |
| + | if(result == CURLE_OK) |
| + | result = tftp_option_add(state, &sbytes, sbytes, buf); |
| + | |
| + | /* add timeout option */ |
| + | curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time); |
| + | if(result == CURLE_OK) |
| + | result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL); |
| + | if(result == CURLE_OK) |
| + | result = tftp_option_add(state, &sbytes, sbytes, buf); |
| + | |
| + | if(result != CURLE_OK) { |
| + | failf(data, "TFTP buffer too small for options"); |
| + | return CURLE_TFTP_ILLEGAL; |
| + | } |
| + | } |
| + | |
| + | /* the typecase for the 3rd argument is mostly for systems that do |
| + | not have a size_t argument, like older unixes that want an 'int' */ |
| + | #ifdef __AMIGA__ |
| + | #define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) |
| + | #else |
| + | #define CURL_SENDTO_ARG5(x) (x) |
| + | #endif |
| + | remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET); |
| + | if(!remote_addr) |
| + | return CURLE_FAILED_INIT; |
| + | |
| + | senddata = sendto(state->sockfd, (void *)state->spacket.data, |
| + | (SEND_TYPE_ARG3)sbytes, 0, |
| + | CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr), |
| + | (curl_socklen_t)remote_addr->addrlen); |
| + | if(senddata != (ssize_t)sbytes) { |
| + | char buffer[STRERROR_LEN]; |
| + | failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); |
| + | return CURLE_SEND_ERROR; |
| + | } |
| + | break; |
| + | |
| + | case TFTP_EVENT_OACK: |
| + | if(data->state.upload) { |
| + | result = tftp_connect_for_tx(state, event); |
| + | } |
| + | else { |
| + | result = tftp_connect_for_rx(state, event); |
| + | } |
| + | break; |
| + | |
| + | case TFTP_EVENT_ACK: /* Connected for transmit */ |
| + | result = tftp_connect_for_tx(state, event); |
| + | break; |
| + | |
| + | case TFTP_EVENT_DATA: /* Connected for receive */ |
| + | result = tftp_connect_for_rx(state, event); |
| + | break; |
| + | |
| + | case TFTP_EVENT_ERROR: |
| + | state->state = TFTP_STATE_FIN; |
| break; | |
| + | |
| + | default: |
| + | failf(state->data, "tftp_send_first: internal error"); |
| + | return CURLE_TFTP_ILLEGAL; |
| } | |
| return result; | |
| @@ -1377,4 +1334,33 @@ static CURLcode tftp_setup_connection(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| + | |
| + | /* |
| + | * TFTP protocol handler. |
| + | */ |
| + | const struct Curl_handler Curl_handler_tftp = { |
| + | "tftp", /* scheme */ |
| + | tftp_setup_connection, /* setup_connection */ |
| + | tftp_do, /* do_it */ |
| + | tftp_done, /* done */ |
| + | ZERO_NULL, /* do_more */ |
| + | tftp_connect, /* connect_it */ |
| + | tftp_multi_statemach, /* connecting */ |
| + | tftp_doing, /* doing */ |
| + | tftp_pollset, /* proto_pollset */ |
| + | tftp_pollset, /* doing_pollset */ |
| + | ZERO_NULL, /* domore_pollset */ |
| + | ZERO_NULL, /* perform_pollset */ |
| + | ZERO_NULL, /* disconnect */ |
| + | ZERO_NULL, /* write_resp */ |
| + | ZERO_NULL, /* write_resp_hd */ |
| + | ZERO_NULL, /* connection_check */ |
| + | ZERO_NULL, /* attach connection */ |
| + | ZERO_NULL, /* follow */ |
| + | PORT_TFTP, /* defport */ |
| + | CURLPROTO_TFTP, /* protocol */ |
| + | CURLPROTO_TFTP, /* family */ |
| + | PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */ |
| + | }; |
| + | |
| #endif | |