From: Viktor Szakats Date: Sun, 21 Dec 2025 15:19:52 +0000 (+0100) Subject: lib: reorder protocol functions to avoid forward declarations (misc) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=07926b59828936117a7c6662f850bc09edb14357;p=thirdparty%2Fcurl.git lib: reorder protocol functions to avoid forward declarations (misc) For protocols: dict, file, gopher, tftp, http, mqtt, smb. Move protocol hander table to the end of sources, rearrange static functions is reverse dependency order as necessary. Closes #20274 --- diff --git a/lib/dict.c b/lib/dict.c index 5839e804c2..54563b4e45 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -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 */ diff --git a/lib/file.c b/lib/file.c index 982f254b2d..9ba536bf2e 100644 --- a/lib/file.c +++ b/lib/file.c @@ -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 diff --git a/lib/gopher.c b/lib/gopher.c index 186530de9f..9d35c68f31 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -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 */ diff --git a/lib/http.c b/lib/http.c index f452fe2550..76d904c05a 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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 */ diff --git a/lib/mqtt.c b/lib/mqtt.c index 2ba1ca73e9..5f5c91a49a 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -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 */ diff --git a/lib/smb.c b/lib/smb.c index 003a38f19f..e4a1df1412 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -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 */ diff --git a/lib/tftp.c b/lib/tftp.c index 69571bbdde..0c65c57c59 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -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