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
#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)
{
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 */
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);
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
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)
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
#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;
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 */
#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));
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;
#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];
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 */
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.
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,
}
}
-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 */
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;
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 */
# 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"
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,
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 */
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 -
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);
+}
/**********************************************************
*
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
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;
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