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
This commit is contained in:
Viktor Szakats
2025-12-21 16:19:52 +01:00
parent 3ee1d3b573
commit 07926b5982
7 changed files with 856 additions and 952 deletions

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */

View File

@@ -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;
/*
* 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
};
/* 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)
#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
};
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;
}
#endif
#endif /* CURL_DISABLE_HTTP */

View File

@@ -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 */

211
lib/smb.c
View File

@@ -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;
/*
* 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 */
};
/* 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;
}
#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 */

View File

@@ -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,6 +340,160 @@ static CURLcode tftp_option_add(struct tftp_conn *state, size_t *csize,
return CURLE_OK;
}
/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16bit
boundary */
#define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff)
/**********************************************************
*
* 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++;
}
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) {
state->state = TFTP_STATE_FIN;
return CURLE_OK;
}
/* 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);
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_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 {
/* 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_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(data, "tftp_tx: internal error, event: %i", (int)(event));
break;
}
return result;
}
static CURLcode tftp_connect_for_tx(struct tftp_conn *state,
tftp_event_t event)
{
@@ -399,6 +510,128 @@ static CURLcode tftp_connect_for_tx(struct tftp_conn *state,
return tftp_tx(state, event);
}
/**********************************************************
*
* tftp_rx
*
* Event handler for the RX state
*
**********************************************************/
static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event)
{
ssize_t sbytes;
int rblock;
struct Curl_easy *data = state->data;
char buffer[STRERROR_LEN];
switch(event) {
case TFTP_EVENT_DATA:
/* Is this the block we expect? */
rblock = getrpacketblock(&state->rpacket);
if(NEXT_BLOCKNUM(state->block) == rblock) {
/* This is the expected block. Reset counters and ACK it. */
state->retries = 0;
}
else if(state->block == rblock) {
/* This is the last recently received block again. Log it and ACK it
again. */
infof(data, "Received last DATA packet block %d again.", rblock);
}
else {
/* totally unexpected, just log it */
infof(data,
"Received unexpected DATA packet block %d, expecting block %d",
rblock, NEXT_BLOCKNUM(state->block));
break;
}
/* ACK this block. */
state->block = (unsigned short)rblock;
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;
}
/* 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;
}
static CURLcode tftp_connect_for_rx(struct tftp_conn *state,
tftp_event_t event)
{
@@ -559,282 +792,6 @@ static CURLcode tftp_send_first(struct tftp_conn *state,
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)
/**********************************************************
*
* tftp_rx
*
* Event handler for the RX state
*
**********************************************************/
static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event)
{
ssize_t sbytes;
int rblock;
struct Curl_easy *data = state->data;
char buffer[STRERROR_LEN];
switch(event) {
case TFTP_EVENT_DATA:
/* Is this the block we expect? */
rblock = getrpacketblock(&state->rpacket);
if(NEXT_BLOCKNUM(state->block) == rblock) {
/* This is the expected block. Reset counters and ACK it. */
state->retries = 0;
}
else if(state->block == rblock) {
/* This is the last recently received block again. Log it and ACK it
again. */
infof(data, "Received last DATA packet block %d again.", rblock);
}
else {
/* totally unexpected, just log it */
infof(data,
"Received unexpected DATA packet block %d, expecting block %d",
rblock, NEXT_BLOCKNUM(state->block));
break;
}
/* ACK this block. */
state->block = (unsigned short)rblock;
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;
}
/* 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++;
}
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) {
state->state = TFTP_STATE_FIN;
return CURLE_OK;
}
/* 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);
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_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 {
/* 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_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(data, "tftp_tx: internal error, event: %i", (int)(event));
break;
}
return result;
}
/**********************************************************
*
* tftp_translate_code
@@ -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