mirror of
https://github.com/curl/curl.git
synced 2026-01-18 17:21:26 +01:00
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:
64
lib/dict.c
64
lib/dict.c
@@ -65,41 +65,6 @@
|
||||
#define DICT_DEFINE3 "/LOOKUP:"
|
||||
|
||||
|
||||
/*
|
||||
* Forward declarations.
|
||||
*/
|
||||
|
||||
static CURLcode dict_do(struct Curl_easy *data, bool *done);
|
||||
|
||||
/*
|
||||
* DICT protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_dict = {
|
||||
"dict", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
dict_do, /* do_it */
|
||||
ZERO_NULL, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
ZERO_NULL, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_DICT, /* defport */
|
||||
CURLPROTO_DICT, /* protocol */
|
||||
CURLPROTO_DICT, /* family */
|
||||
PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */
|
||||
};
|
||||
|
||||
#define DYN_DICT_WORD 10000
|
||||
static char *unescape_word(const char *input)
|
||||
{
|
||||
@@ -306,4 +271,33 @@ error:
|
||||
curlx_free(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* DICT protocol handler.
|
||||
*/
|
||||
const struct Curl_handler Curl_handler_dict = {
|
||||
"dict", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
dict_do, /* do_it */
|
||||
ZERO_NULL, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
ZERO_NULL, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_DICT, /* defport */
|
||||
CURLPROTO_DICT, /* protocol */
|
||||
CURLPROTO_DICT, /* family */
|
||||
PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */
|
||||
};
|
||||
|
||||
#endif /* CURL_DISABLE_DICT */
|
||||
|
||||
97
lib/file.c
97
lib/file.c
@@ -82,49 +82,6 @@ struct FILEPROTO {
|
||||
int fd; /* open file descriptor to read from! */
|
||||
};
|
||||
|
||||
/*
|
||||
* Forward declarations.
|
||||
*/
|
||||
|
||||
static CURLcode file_do(struct Curl_easy *data, bool *done);
|
||||
static CURLcode file_done(struct Curl_easy *data,
|
||||
CURLcode status, bool premature);
|
||||
static CURLcode file_connect(struct Curl_easy *data, bool *done);
|
||||
static CURLcode file_disconnect(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool dead_connection);
|
||||
static CURLcode file_setup_connection(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
/*
|
||||
* FILE scheme handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_file = {
|
||||
"file", /* scheme */
|
||||
file_setup_connection, /* setup_connection */
|
||||
file_do, /* do_it */
|
||||
file_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
file_connect, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
ZERO_NULL, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
file_disconnect, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
0, /* defport */
|
||||
CURLPROTO_FILE, /* protocol */
|
||||
CURLPROTO_FILE, /* family */
|
||||
PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
|
||||
};
|
||||
|
||||
static void file_cleanup(struct FILEPROTO *file)
|
||||
{
|
||||
Curl_safefree(file->freepath);
|
||||
@@ -158,6 +115,19 @@ static CURLcode file_setup_connection(struct Curl_easy *data,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode file_done(struct Curl_easy *data,
|
||||
CURLcode status, bool premature)
|
||||
{
|
||||
struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
|
||||
(void)status;
|
||||
(void)premature;
|
||||
|
||||
if(file)
|
||||
file_cleanup(file);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* file_connect() gets called from Curl_protocol_connect() to allow us to
|
||||
* do protocol-specific actions at connect-time. We emulate a
|
||||
@@ -276,19 +246,6 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode file_done(struct Curl_easy *data,
|
||||
CURLcode status, bool premature)
|
||||
{
|
||||
struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
|
||||
(void)status;
|
||||
(void)premature;
|
||||
|
||||
if(file)
|
||||
file_cleanup(file);
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode file_disconnect(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool dead_connection)
|
||||
@@ -648,4 +605,32 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* FILE scheme handler.
|
||||
*/
|
||||
const struct Curl_handler Curl_handler_file = {
|
||||
"file", /* scheme */
|
||||
file_setup_connection, /* setup_connection */
|
||||
file_do, /* do_it */
|
||||
file_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
file_connect, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
ZERO_NULL, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
file_disconnect, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
0, /* defport */
|
||||
CURLPROTO_FILE, /* protocol */
|
||||
CURLPROTO_FILE, /* family */
|
||||
PROTOPT_NONETWORK | PROTOPT_NOURLQUERY /* flags */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
125
lib/gopher.c
125
lib/gopher.c
@@ -36,73 +36,7 @@
|
||||
#include "url.h"
|
||||
#include "escape.h"
|
||||
|
||||
/*
|
||||
* Forward declarations.
|
||||
*/
|
||||
|
||||
static CURLcode gopher_do(struct Curl_easy *data, bool *done);
|
||||
#ifdef USE_SSL
|
||||
static CURLcode gopher_connect(struct Curl_easy *data, bool *done);
|
||||
static CURLcode gopher_connecting(struct Curl_easy *data, bool *done);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Gopher protocol handler.
|
||||
* This is also a nice simple template to build off for simple
|
||||
* connect-command-download protocols.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_gopher = {
|
||||
"gopher", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
gopher_do, /* do_it */
|
||||
ZERO_NULL, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
ZERO_NULL, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_GOPHER, /* defport */
|
||||
CURLPROTO_GOPHER, /* protocol */
|
||||
CURLPROTO_GOPHER, /* family */
|
||||
PROTOPT_NONE /* flags */
|
||||
};
|
||||
|
||||
#ifdef USE_SSL
|
||||
const struct Curl_handler Curl_handler_gophers = {
|
||||
"gophers", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
gopher_do, /* do_it */
|
||||
ZERO_NULL, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
gopher_connect, /* connect_it */
|
||||
gopher_connecting, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
ZERO_NULL, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_GOPHER, /* defport */
|
||||
CURLPROTO_GOPHERS, /* protocol */
|
||||
CURLPROTO_GOPHER, /* family */
|
||||
PROTOPT_SSL /* flags */
|
||||
};
|
||||
|
||||
static CURLcode gopher_connect(struct Curl_easy *data, bool *done)
|
||||
{
|
||||
(void)data;
|
||||
@@ -231,4 +165,63 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
|
||||
Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gopher protocol handler.
|
||||
* This is also a nice simple template to build off for simple
|
||||
* connect-command-download protocols.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_gopher = {
|
||||
"gopher", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
gopher_do, /* do_it */
|
||||
ZERO_NULL, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
ZERO_NULL, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_GOPHER, /* defport */
|
||||
CURLPROTO_GOPHER, /* protocol */
|
||||
CURLPROTO_GOPHER, /* family */
|
||||
PROTOPT_NONE /* flags */
|
||||
};
|
||||
|
||||
#ifdef USE_SSL
|
||||
const struct Curl_handler Curl_handler_gophers = {
|
||||
"gophers", /* scheme */
|
||||
ZERO_NULL, /* setup_connection */
|
||||
gopher_do, /* do_it */
|
||||
ZERO_NULL, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
gopher_connect, /* connect_it */
|
||||
gopher_connecting, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
ZERO_NULL, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_GOPHER, /* defport */
|
||||
CURLPROTO_GOPHERS, /* protocol */
|
||||
CURLPROTO_GOPHER, /* family */
|
||||
PROTOPT_SSL /* flags */
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* CURL_DISABLE_GOPHER */
|
||||
|
||||
616
lib/http.c
616
lib/http.c
@@ -85,94 +85,6 @@
|
||||
#include "bufref.h"
|
||||
#include "curlx/strparse.h"
|
||||
|
||||
/*
|
||||
* Forward declarations.
|
||||
*/
|
||||
|
||||
static bool http_should_fail(struct Curl_easy *data, int httpcode);
|
||||
static bool http_exp100_is_waiting(struct Curl_easy *data);
|
||||
static CURLcode http_exp100_add_reader(struct Curl_easy *data);
|
||||
static void http_exp100_send_anyway(struct Curl_easy *data);
|
||||
static bool http_exp100_is_selected(struct Curl_easy *data);
|
||||
static void http_exp100_got100(struct Curl_easy *data);
|
||||
static CURLcode http_firstwrite(struct Curl_easy *data);
|
||||
static CURLcode http_header(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen);
|
||||
static CURLcode http_range(struct Curl_easy *data,
|
||||
Curl_HttpReq httpreq);
|
||||
static CURLcode http_req_set_TE(struct Curl_easy *data,
|
||||
struct dynbuf *req,
|
||||
int httpversion);
|
||||
static CURLcode http_size(struct Curl_easy *data);
|
||||
static CURLcode http_statusline(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req);
|
||||
static CURLcode http_useragent(struct Curl_easy *data);
|
||||
static CURLcode http_write_header(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen);
|
||||
|
||||
/*
|
||||
* HTTP handler interface.
|
||||
*/
|
||||
const struct Curl_handler Curl_handler_http = {
|
||||
"http", /* scheme */
|
||||
Curl_http_setup_conn, /* setup_connection */
|
||||
Curl_http, /* do_it */
|
||||
Curl_http_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
Curl_http_doing_pollset, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
Curl_http_perform_pollset, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
Curl_http_write_resp, /* write_resp */
|
||||
Curl_http_write_resp_hd, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
Curl_http_follow, /* follow */
|
||||
PORT_HTTP, /* defport */
|
||||
CURLPROTO_HTTP, /* protocol */
|
||||
CURLPROTO_HTTP, /* family */
|
||||
PROTOPT_CREDSPERREQUEST | /* flags */
|
||||
PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE
|
||||
|
||||
};
|
||||
|
||||
#ifdef USE_SSL
|
||||
/*
|
||||
* HTTPS handler interface.
|
||||
*/
|
||||
const struct Curl_handler Curl_handler_https = {
|
||||
"https", /* scheme */
|
||||
Curl_http_setup_conn, /* setup_connection */
|
||||
Curl_http, /* do_it */
|
||||
Curl_http_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
NULL, /* connecting */
|
||||
ZERO_NULL, /* doing */
|
||||
NULL, /* proto_pollset */
|
||||
Curl_http_doing_pollset, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
Curl_http_perform_pollset, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
Curl_http_write_resp, /* write_resp */
|
||||
Curl_http_write_resp_hd, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
Curl_http_follow, /* follow */
|
||||
PORT_HTTPS, /* defport */
|
||||
CURLPROTO_HTTPS, /* protocol */
|
||||
CURLPROTO_HTTP, /* family */
|
||||
PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
|
||||
PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg)
|
||||
{
|
||||
memset(neg, 0, sizeof(*neg));
|
||||
@@ -547,13 +459,83 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* http_should_fail() determines whether an HTTP response code has gotten us
|
||||
* into an error state or not.
|
||||
*
|
||||
* @retval FALSE communications should continue
|
||||
*
|
||||
* @retval TRUE communications should not continue
|
||||
*/
|
||||
static bool http_should_fail(struct Curl_easy *data, int httpcode)
|
||||
{
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
|
||||
/*
|
||||
** If we have not been asked to fail on error,
|
||||
** do not fail.
|
||||
*/
|
||||
if(!data->set.http_fail_on_error)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
** Any code < 400 is never terminal.
|
||||
*/
|
||||
if(httpcode < 400)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
** A 416 response to a resume request is presumably because the file is
|
||||
** already completely downloaded and thus not actually a fail.
|
||||
*/
|
||||
if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
|
||||
httpcode == 416)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
** Any code >= 400 that is not 401 or 407 is always
|
||||
** a terminal error
|
||||
*/
|
||||
if((httpcode != 401) && (httpcode != 407))
|
||||
return TRUE;
|
||||
|
||||
/*
|
||||
** All we have left to deal with is 401 and 407
|
||||
*/
|
||||
DEBUGASSERT((httpcode == 401) || (httpcode == 407));
|
||||
|
||||
/*
|
||||
** Examine the current authentication state to see if this is an error. The
|
||||
** idea is for this function to get called after processing all the headers
|
||||
** in a response message. So, if we have been to asked to authenticate a
|
||||
** particular stage, and we have done it, we are OK. If we are already
|
||||
** completely authenticated, it is not OK to get another 401 or 407.
|
||||
**
|
||||
** It is possible for authentication to go stale such that the client needs
|
||||
** to reauthenticate. Once that info is available, use it here.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Either we are not authenticating, or we are supposed to be authenticating
|
||||
** something else. This is an error.
|
||||
*/
|
||||
if((httpcode == 401) && !data->state.aptr.user)
|
||||
return TRUE;
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if((httpcode == 407) && !data->conn->bits.proxy_user_passwd)
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
return data->state.authproblem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_http_auth_act() gets called when all HTTP headers have been received
|
||||
* and it checks what authentication methods that are available and decides
|
||||
* which one (if any) to use. It will set 'newurl' if an auth method was
|
||||
* picked.
|
||||
*/
|
||||
|
||||
CURLcode Curl_http_auth_act(struct Curl_easy *data)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
@@ -1116,77 +1098,6 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* http_should_fail() determines whether an HTTP response code has gotten us
|
||||
* into an error state or not.
|
||||
*
|
||||
* @retval FALSE communications should continue
|
||||
*
|
||||
* @retval TRUE communications should not continue
|
||||
*/
|
||||
static bool http_should_fail(struct Curl_easy *data, int httpcode)
|
||||
{
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
|
||||
/*
|
||||
** If we have not been asked to fail on error,
|
||||
** do not fail.
|
||||
*/
|
||||
if(!data->set.http_fail_on_error)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
** Any code < 400 is never terminal.
|
||||
*/
|
||||
if(httpcode < 400)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
** A 416 response to a resume request is presumably because the file is
|
||||
** already completely downloaded and thus not actually a fail.
|
||||
*/
|
||||
if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
|
||||
httpcode == 416)
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
** Any code >= 400 that is not 401 or 407 is always
|
||||
** a terminal error
|
||||
*/
|
||||
if((httpcode != 401) && (httpcode != 407))
|
||||
return TRUE;
|
||||
|
||||
/*
|
||||
** All we have left to deal with is 401 and 407
|
||||
*/
|
||||
DEBUGASSERT((httpcode == 401) || (httpcode == 407));
|
||||
|
||||
/*
|
||||
** Examine the current authentication state to see if this is an error. The
|
||||
** idea is for this function to get called after processing all the headers
|
||||
** in a response message. So, if we have been to asked to authenticate a
|
||||
** particular stage, and we have done it, we are OK. If we are already
|
||||
** completely authenticated, it is not OK to get another 401 or 407.
|
||||
**
|
||||
** It is possible for authentication to go stale such that the client needs
|
||||
** to reauthenticate. Once that info is available, use it here.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Either we are not authenticating, or we are supposed to be authenticating
|
||||
** something else. This is an error.
|
||||
*/
|
||||
if((httpcode == 401) && !data->state.aptr.user)
|
||||
return TRUE;
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if((httpcode == 407) && !data->conn->bits.proxy_user_passwd)
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
return data->state.authproblem;
|
||||
}
|
||||
|
||||
static void http_switch_to_get(struct Curl_easy *data, int code)
|
||||
{
|
||||
const char *req = data->set.str[STRING_CUSTOMREQUEST];
|
||||
@@ -1532,6 +1443,145 @@ bool Curl_compareheader(const char *headerline, /* line to check */
|
||||
return FALSE; /* no match */
|
||||
}
|
||||
|
||||
struct cr_exp100_ctx {
|
||||
struct Curl_creader super;
|
||||
struct curltime start; /* time started waiting */
|
||||
enum expect100 state;
|
||||
};
|
||||
|
||||
/* Expect: 100-continue client reader, blocking uploads */
|
||||
|
||||
static void http_exp100_continue(struct Curl_easy *data,
|
||||
struct Curl_creader *reader)
|
||||
{
|
||||
struct cr_exp100_ctx *ctx = reader->ctx;
|
||||
if(ctx->state > EXP100_SEND_DATA) {
|
||||
ctx->state = EXP100_SEND_DATA;
|
||||
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode cr_exp100_read(struct Curl_easy *data,
|
||||
struct Curl_creader *reader,
|
||||
char *buf, size_t blen,
|
||||
size_t *nread, bool *eos)
|
||||
{
|
||||
struct cr_exp100_ctx *ctx = reader->ctx;
|
||||
timediff_t ms;
|
||||
|
||||
switch(ctx->state) {
|
||||
case EXP100_SENDING_REQUEST:
|
||||
if(!Curl_req_sendbuf_empty(data)) {
|
||||
/* The initial request data has not been fully sent yet. Do
|
||||
* not start the timer yet. */
|
||||
DEBUGF(infof(data, "cr_exp100_read, request not full sent yet"));
|
||||
*nread = 0;
|
||||
*eos = FALSE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
/* We are now waiting for a reply from the server or
|
||||
* a timeout on our side IFF the request has been fully sent. */
|
||||
DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
|
||||
"timeout %dms", data->set.expect_100_timeout));
|
||||
ctx->state = EXP100_AWAITING_CONTINUE;
|
||||
ctx->start = *Curl_pgrs_now(data);
|
||||
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
|
||||
*nread = 0;
|
||||
*eos = FALSE;
|
||||
return CURLE_OK;
|
||||
case EXP100_FAILED:
|
||||
DEBUGF(infof(data, "cr_exp100_read, expectation failed, error"));
|
||||
*nread = 0;
|
||||
*eos = FALSE;
|
||||
return CURLE_READ_ERROR;
|
||||
case EXP100_AWAITING_CONTINUE:
|
||||
ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->start);
|
||||
if(ms < data->set.expect_100_timeout) {
|
||||
DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired"));
|
||||
*nread = 0;
|
||||
*eos = FALSE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
/* we have waited long enough, continue anyway */
|
||||
http_exp100_continue(data, reader);
|
||||
infof(data, "Done waiting for 100-continue");
|
||||
FALLTHROUGH();
|
||||
default:
|
||||
DEBUGF(infof(data, "cr_exp100_read, pass through"));
|
||||
return Curl_creader_read(data, reader->next, buf, blen, nread, eos);
|
||||
}
|
||||
}
|
||||
|
||||
static void cr_exp100_done(struct Curl_easy *data,
|
||||
struct Curl_creader *reader, int premature)
|
||||
{
|
||||
struct cr_exp100_ctx *ctx = reader->ctx;
|
||||
ctx->state = premature ? EXP100_FAILED : EXP100_SEND_DATA;
|
||||
Curl_expire_done(data, EXPIRE_100_TIMEOUT);
|
||||
}
|
||||
|
||||
static const struct Curl_crtype cr_exp100 = {
|
||||
"cr-exp100",
|
||||
Curl_creader_def_init,
|
||||
cr_exp100_read,
|
||||
Curl_creader_def_close,
|
||||
Curl_creader_def_needs_rewind,
|
||||
Curl_creader_def_total_length,
|
||||
Curl_creader_def_resume_from,
|
||||
Curl_creader_def_cntrl,
|
||||
Curl_creader_def_is_paused,
|
||||
cr_exp100_done,
|
||||
sizeof(struct cr_exp100_ctx)
|
||||
};
|
||||
|
||||
static CURLcode http_exp100_add_reader(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *reader = NULL;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_creader_create(&reader, data, &cr_exp100, CURL_CR_PROTOCOL);
|
||||
if(!result)
|
||||
result = Curl_creader_add(data, reader);
|
||||
if(!result) {
|
||||
struct cr_exp100_ctx *ctx = reader->ctx;
|
||||
ctx->state = EXP100_SENDING_REQUEST;
|
||||
}
|
||||
|
||||
if(result && reader)
|
||||
Curl_creader_free(data, reader);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void http_exp100_got100(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
|
||||
if(r)
|
||||
http_exp100_continue(data, r);
|
||||
}
|
||||
|
||||
static bool http_exp100_is_waiting(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
|
||||
if(r) {
|
||||
struct cr_exp100_ctx *ctx = r->ctx;
|
||||
return ctx->state == EXP100_AWAITING_CONTINUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void http_exp100_send_anyway(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
|
||||
if(r)
|
||||
http_exp100_continue(data, r);
|
||||
}
|
||||
|
||||
static bool http_exp100_is_selected(struct Curl_easy *data)
|
||||
{
|
||||
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
|
||||
return !!r;
|
||||
}
|
||||
|
||||
/* this returns the socket to wait for in the DO and DOING state for the multi
|
||||
interface and then we are always _sending_ a request and thus we wait for
|
||||
the single socket to become writable only */
|
||||
@@ -1559,6 +1609,33 @@ CURLcode Curl_http_perform_pollset(struct Curl_easy *data,
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode http_write_header(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen)
|
||||
{
|
||||
CURLcode result;
|
||||
int writetype;
|
||||
|
||||
/* now, only output this if the header AND body are requested:
|
||||
*/
|
||||
Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
|
||||
|
||||
writetype = CLIENTWRITE_HEADER |
|
||||
((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0);
|
||||
|
||||
result = Curl_client_write(data, writetype, hd, hdlen);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_bump_headersize(data, hdlen, FALSE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
data->req.deductheadercount = (100 <= data->req.httpcode &&
|
||||
199 >= data->req.httpcode) ?
|
||||
data->req.headerbytecount : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_http_done() gets called after a single HTTP request has been
|
||||
* performed.
|
||||
@@ -3800,33 +3877,6 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode http_write_header(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen)
|
||||
{
|
||||
CURLcode result;
|
||||
int writetype;
|
||||
|
||||
/* now, only output this if the header AND body are requested:
|
||||
*/
|
||||
Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
|
||||
|
||||
writetype = CLIENTWRITE_HEADER |
|
||||
((data->req.httpcode / 100 == 1) ? CLIENTWRITE_1XX : 0);
|
||||
|
||||
result = Curl_client_write(data, writetype, hd, hdlen);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_bump_headersize(data, hdlen, FALSE);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
data->req.deductheadercount = (100 <= data->req.httpcode &&
|
||||
199 >= data->req.httpcode) ?
|
||||
data->req.headerbytecount : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode http_on_response(struct Curl_easy *data,
|
||||
const char *last_hd, size_t last_hd_len,
|
||||
const char *buf, size_t blen,
|
||||
@@ -4931,143 +4981,65 @@ void Curl_http_resp_free(struct http_resp *resp)
|
||||
}
|
||||
}
|
||||
|
||||
struct cr_exp100_ctx {
|
||||
struct Curl_creader super;
|
||||
struct curltime start; /* time started waiting */
|
||||
enum expect100 state;
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
71
lib/mqtt.c
71
lib/mqtt.c
@@ -89,48 +89,6 @@ struct MQTT {
|
||||
BIT(pingsent); /* 1 while we wait for ping response */
|
||||
};
|
||||
|
||||
/*
|
||||
* Forward declarations.
|
||||
*/
|
||||
|
||||
static CURLcode mqtt_do(struct Curl_easy *data, bool *done);
|
||||
static CURLcode mqtt_done(struct Curl_easy *data,
|
||||
CURLcode status, bool premature);
|
||||
static CURLcode mqtt_doing(struct Curl_easy *data, bool *done);
|
||||
static CURLcode mqtt_pollset(struct Curl_easy *data,
|
||||
struct easy_pollset *ps);
|
||||
static CURLcode mqtt_setup_conn(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
/*
|
||||
* MQTT protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_mqtt = {
|
||||
"mqtt", /* scheme */
|
||||
mqtt_setup_conn, /* setup_connection */
|
||||
mqtt_do, /* do_it */
|
||||
mqtt_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
mqtt_doing, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
mqtt_pollset, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_MQTT, /* defport */
|
||||
CURLPROTO_MQTT, /* protocol */
|
||||
CURLPROTO_MQTT, /* family */
|
||||
PROTOPT_NONE /* flags */
|
||||
};
|
||||
|
||||
static void mqtt_easy_dtor(void *key, size_t klen, void *entry)
|
||||
{
|
||||
struct MQTT *mq = entry;
|
||||
@@ -976,4 +934,33 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* MQTT protocol handler.
|
||||
*/
|
||||
|
||||
const struct Curl_handler Curl_handler_mqtt = {
|
||||
"mqtt", /* scheme */
|
||||
mqtt_setup_conn, /* setup_connection */
|
||||
mqtt_do, /* do_it */
|
||||
mqtt_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
ZERO_NULL, /* connect_it */
|
||||
ZERO_NULL, /* connecting */
|
||||
mqtt_doing, /* doing */
|
||||
ZERO_NULL, /* proto_pollset */
|
||||
mqtt_pollset, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_MQTT, /* defport */
|
||||
CURLPROTO_MQTT, /* protocol */
|
||||
CURLPROTO_MQTT, /* family */
|
||||
PROTOPT_NONE /* flags */
|
||||
};
|
||||
|
||||
#endif /* CURL_DISABLE_MQTT */
|
||||
|
||||
211
lib/smb.c
211
lib/smb.c
@@ -285,77 +285,6 @@ struct smb_tree_disconnect {
|
||||
# pragma pack(pop)
|
||||
#endif
|
||||
|
||||
/* Local API functions */
|
||||
static CURLcode smb_setup_connection(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
static CURLcode smb_connect(struct Curl_easy *data, bool *done);
|
||||
static CURLcode smb_connection_state(struct Curl_easy *data, bool *done);
|
||||
static CURLcode smb_do(struct Curl_easy *data, bool *done);
|
||||
static CURLcode smb_request_state(struct Curl_easy *data, bool *done);
|
||||
static CURLcode smb_pollset(struct Curl_easy *data,
|
||||
struct easy_pollset *ps);
|
||||
static CURLcode smb_parse_url_path(struct Curl_easy *data,
|
||||
struct smb_conn *smbc,
|
||||
struct smb_request *req);
|
||||
|
||||
/*
|
||||
* SMB handler interface
|
||||
*/
|
||||
const struct Curl_handler Curl_handler_smb = {
|
||||
"smb", /* scheme */
|
||||
smb_setup_connection, /* setup_connection */
|
||||
smb_do, /* do_it */
|
||||
ZERO_NULL, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
smb_connect, /* connect_it */
|
||||
smb_connection_state, /* connecting */
|
||||
smb_request_state, /* doing */
|
||||
smb_pollset, /* proto_pollset */
|
||||
smb_pollset, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_SMB, /* defport */
|
||||
CURLPROTO_SMB, /* protocol */
|
||||
CURLPROTO_SMB, /* family */
|
||||
PROTOPT_CONN_REUSE /* flags */
|
||||
};
|
||||
|
||||
#ifdef USE_SSL
|
||||
/*
|
||||
* SMBS handler interface
|
||||
*/
|
||||
const struct Curl_handler Curl_handler_smbs = {
|
||||
"smbs", /* scheme */
|
||||
smb_setup_connection, /* setup_connection */
|
||||
smb_do, /* do_it */
|
||||
ZERO_NULL, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
smb_connect, /* connect_it */
|
||||
smb_connection_state, /* connecting */
|
||||
smb_request_state, /* doing */
|
||||
smb_pollset, /* proto_pollset */
|
||||
smb_pollset, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_SMBS, /* defport */
|
||||
CURLPROTO_SMBS, /* protocol */
|
||||
CURLPROTO_SMB, /* family */
|
||||
PROTOPT_SSL | PROTOPT_CONN_REUSE /* flags */
|
||||
};
|
||||
#endif
|
||||
|
||||
#define MAX_PAYLOAD_SIZE 0x8000
|
||||
#define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000)
|
||||
#define CLIENTNAME "curl"
|
||||
@@ -460,6 +389,49 @@ static void smb_conn_dtor(void *key, size_t klen, void *entry)
|
||||
curlx_free(smbc);
|
||||
}
|
||||
|
||||
static CURLcode smb_parse_url_path(struct Curl_easy *data,
|
||||
struct smb_conn *smbc,
|
||||
struct smb_request *req)
|
||||
{
|
||||
char *path;
|
||||
char *slash;
|
||||
CURLcode result;
|
||||
|
||||
/* URL decode the path */
|
||||
result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* Parse the path for the share */
|
||||
smbc->share = curlx_strdup((*path == '/' || *path == '\\')
|
||||
? path + 1 : path);
|
||||
curlx_free(path);
|
||||
if(!smbc->share)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
slash = strchr(smbc->share, '/');
|
||||
if(!slash)
|
||||
slash = strchr(smbc->share, '\\');
|
||||
|
||||
/* The share must be present */
|
||||
if(!slash) {
|
||||
Curl_safefree(smbc->share);
|
||||
failf(data, "missing share in URL path for SMB");
|
||||
return CURLE_URL_MALFORMAT;
|
||||
}
|
||||
|
||||
/* Parse the path for the file path converting any forward slashes into
|
||||
backslashes */
|
||||
*slash++ = 0;
|
||||
req->path = slash;
|
||||
|
||||
for(; *slash; slash++) {
|
||||
if(*slash == '/')
|
||||
*slash = '\\';
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* this should setup things in the connection, not in the easy
|
||||
handle */
|
||||
static CURLcode smb_setup_connection(struct Curl_easy *data,
|
||||
@@ -1226,47 +1198,62 @@ static CURLcode smb_do(struct Curl_easy *data, bool *done)
|
||||
return CURLE_URL_MALFORMAT;
|
||||
}
|
||||
|
||||
static CURLcode smb_parse_url_path(struct Curl_easy *data,
|
||||
struct smb_conn *smbc,
|
||||
struct smb_request *req)
|
||||
{
|
||||
char *path;
|
||||
char *slash;
|
||||
CURLcode result;
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
624
lib/tftp.c
624
lib/tftp.c
@@ -142,49 +142,6 @@ struct tftp_conn {
|
||||
BIT(remote_pinned);
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event);
|
||||
static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event);
|
||||
static CURLcode tftp_connect(struct Curl_easy *data, bool *done);
|
||||
static CURLcode tftp_do(struct Curl_easy *data, bool *done);
|
||||
static CURLcode tftp_done(struct Curl_easy *data,
|
||||
CURLcode, bool premature);
|
||||
static CURLcode tftp_setup_connection(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done);
|
||||
static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done);
|
||||
static CURLcode tftp_pollset(struct Curl_easy *data,
|
||||
struct easy_pollset *ps);
|
||||
static CURLcode tftp_translate_code(tftp_error_t error);
|
||||
|
||||
/*
|
||||
* TFTP protocol handler.
|
||||
*/
|
||||
const struct Curl_handler Curl_handler_tftp = {
|
||||
"tftp", /* scheme */
|
||||
tftp_setup_connection, /* setup_connection */
|
||||
tftp_do, /* do_it */
|
||||
tftp_done, /* done */
|
||||
ZERO_NULL, /* do_more */
|
||||
tftp_connect, /* connect_it */
|
||||
tftp_multi_statemach, /* connecting */
|
||||
tftp_doing, /* doing */
|
||||
tftp_pollset, /* proto_pollset */
|
||||
tftp_pollset, /* doing_pollset */
|
||||
ZERO_NULL, /* domore_pollset */
|
||||
ZERO_NULL, /* perform_pollset */
|
||||
ZERO_NULL, /* disconnect */
|
||||
ZERO_NULL, /* write_resp */
|
||||
ZERO_NULL, /* write_resp_hd */
|
||||
ZERO_NULL, /* connection_check */
|
||||
ZERO_NULL, /* attach connection */
|
||||
ZERO_NULL, /* follow */
|
||||
PORT_TFTP, /* defport */
|
||||
CURLPROTO_TFTP, /* protocol */
|
||||
CURLPROTO_TFTP, /* family */
|
||||
PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */
|
||||
};
|
||||
|
||||
/**********************************************************
|
||||
*
|
||||
* tftp_set_timeouts -
|
||||
@@ -383,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
|
||||
|
||||
Reference in New Issue
Block a user