struct dynbuf value;
};
-static void dyn_array_free(struct dynbuf *db, size_t num_elements);
-static void pair_array_free(struct pair *pair_array, size_t num_elements);
-static CURLcode split_to_dyn_array(const char *source,
- struct dynbuf db[MAX_QUERY_COMPONENTS],
- size_t *num_splits);
-static bool is_reserved_char(const char c);
-static CURLcode uri_encode_path(struct Curl_str *original_path,
- struct dynbuf *new_path);
-static CURLcode encode_query_component(char *component, size_t len,
- struct dynbuf *db);
-static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
- struct dynbuf *out);
-static bool should_urlencode(struct Curl_str *service_name);
-
static void sha256_to_hex(char *dst, unsigned char *sha)
{
Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH,
}
}
+/*
+ * Frees all allocated strings in a dynbuf pair array, and the dynbuf itself
+ */
+static void pair_array_free(struct pair *pair_array, size_t num_elements)
+{
+ size_t index;
+
+ for(index = 0; index != num_elements; index++) {
+ curlx_dyn_free(&pair_array[index].key);
+ curlx_dyn_free(&pair_array[index].value);
+ }
+}
+
+/*
+ * Frees all allocated strings in a split dynbuf, and the dynbuf itself
+ */
+static void dyn_array_free(struct dynbuf *db, size_t num_elements)
+{
+ size_t index;
+
+ for(index = 0; index < num_elements; index++)
+ curlx_dyn_free((&db[index]));
+}
+
+/*
+ * Splits source string by SPLIT_BY, and creates an array of dynbuf in db.
+ * db is initialized by this function.
+ * Caller is responsible for freeing the array elements with dyn_array_free
+ */
+
+#define SPLIT_BY '&'
+
+static CURLcode split_to_dyn_array(const char *source,
+ struct dynbuf db[MAX_QUERY_COMPONENTS],
+ size_t *num_splits_out)
+{
+ CURLcode result = CURLE_OK;
+ size_t len = strlen(source);
+ size_t pos; /* Position in result buffer */
+ size_t start = 0; /* Start of current segment */
+ size_t segment_length = 0;
+ size_t index = 0;
+ size_t num_splits = 0;
+
+ /* Split source_ptr on SPLIT_BY and store the segment offsets and length in
+ * array */
+ for(pos = 0; pos < len; pos++) {
+ if(source[pos] == SPLIT_BY) {
+ if(segment_length) {
+ curlx_dyn_init(&db[index], segment_length + 1);
+ result = curlx_dyn_addn(&db[index], &source[start], segment_length);
+ if(result)
+ goto fail;
+
+ segment_length = 0;
+ index++;
+ if(++num_splits == MAX_QUERY_COMPONENTS) {
+ result = CURLE_TOO_LARGE;
+ goto fail;
+ }
+ }
+ start = pos + 1;
+ }
+ else {
+ segment_length++;
+ }
+ }
+
+ if(segment_length) {
+ curlx_dyn_init(&db[index], segment_length + 1);
+ result = curlx_dyn_addn(&db[index], &source[start], segment_length);
+ if(!result) {
+ if(++num_splits == MAX_QUERY_COMPONENTS)
+ result = CURLE_TOO_LARGE;
+ }
+ }
+fail:
+ *num_splits_out = num_splits;
+ return result;
+}
+
+static bool is_reserved_char(const char c)
+{
+ return (ISALNUM(c) || ISURLPUNTCS(c));
+}
+
+static CURLcode uri_encode_path(struct Curl_str *original_path,
+ struct dynbuf *new_path)
+{
+ const char *p = curlx_str(original_path);
+ size_t i;
+
+ for(i = 0; i < curlx_strlen(original_path); i++) {
+ /* Do not encode slashes or unreserved chars from RFC 3986 */
+ CURLcode result = CURLE_OK;
+ unsigned char c = p[i];
+ if(is_reserved_char(c) || c == '/')
+ result = curlx_dyn_addn(new_path, &c, 1);
+ else
+ result = curlx_dyn_addf(new_path, "%%%02X", c);
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode encode_query_component(char *component, size_t len,
+ struct dynbuf *db)
+{
+ size_t i;
+ for(i = 0; i < len; i++) {
+ CURLcode result = CURLE_OK;
+ unsigned char this_char = component[i];
+
+ if(is_reserved_char(this_char))
+ /* Escape unreserved chars from RFC 3986 */
+ result = curlx_dyn_addn(db, &this_char, 1);
+ else if(this_char == '+')
+ /* Encode '+' as space */
+ result = curlx_dyn_add(db, "%20");
+ else
+ result = curlx_dyn_addf(db, "%%%02X", this_char);
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Populates a dynbuf containing url_encode(url_decode(in))
+ */
+static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
+ struct dynbuf *out)
+{
+ char *out_s;
+ size_t out_s_len;
+ CURLcode result =
+ Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA);
+
+ if(!result) {
+ result = encode_query_component(out_s, out_s_len, out);
+ Curl_safefree(out_s);
+ }
+ return result;
+}
+
+static bool should_urlencode(struct Curl_str *service_name)
+{
+ /*
+ * These services require unmodified (not additionally URL-encoded) URL
+ * paths.
+ * should_urlencode == true is equivalent to should_urlencode_uri_path
+ * from the AWS SDK. Urls are already normalized by the curl URL parser
+ */
+
+ if(curlx_str_cmp(service_name, "s3") ||
+ curlx_str_cmp(service_name, "s3-express") ||
+ curlx_str_cmp(service_name, "s3-outposts")) {
+ return false;
+ }
+ return true;
+}
+
/* maximum length for the aws sivg4 parts */
#define MAX_SIGV4_LEN 64
#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
return result;
}
-/*
- * Frees all allocated strings in a dynbuf pair array, and the dynbuf itself
- */
-
-static void pair_array_free(struct pair *pair_array, size_t num_elements)
-{
- size_t index;
-
- for(index = 0; index != num_elements; index++) {
- curlx_dyn_free(&pair_array[index].key);
- curlx_dyn_free(&pair_array[index].value);
- }
-}
-
-/*
- * Frees all allocated strings in a split dynbuf, and the dynbuf itself
- */
-
-static void dyn_array_free(struct dynbuf *db, size_t num_elements)
-{
- size_t index;
-
- for(index = 0; index < num_elements; index++)
- curlx_dyn_free((&db[index]));
-}
-
-/*
- * Splits source string by SPLIT_BY, and creates an array of dynbuf in db.
- * db is initialized by this function.
- * Caller is responsible for freeing the array elements with dyn_array_free
- */
-
-#define SPLIT_BY '&'
-
-static CURLcode split_to_dyn_array(const char *source,
- struct dynbuf db[MAX_QUERY_COMPONENTS],
- size_t *num_splits_out)
-{
- CURLcode result = CURLE_OK;
- size_t len = strlen(source);
- size_t pos; /* Position in result buffer */
- size_t start = 0; /* Start of current segment */
- size_t segment_length = 0;
- size_t index = 0;
- size_t num_splits = 0;
-
- /* Split source_ptr on SPLIT_BY and store the segment offsets and length in
- * array */
- for(pos = 0; pos < len; pos++) {
- if(source[pos] == SPLIT_BY) {
- if(segment_length) {
- curlx_dyn_init(&db[index], segment_length + 1);
- result = curlx_dyn_addn(&db[index], &source[start], segment_length);
- if(result)
- goto fail;
-
- segment_length = 0;
- index++;
- if(++num_splits == MAX_QUERY_COMPONENTS) {
- result = CURLE_TOO_LARGE;
- goto fail;
- }
- }
- start = pos + 1;
- }
- else {
- segment_length++;
- }
- }
-
- if(segment_length) {
- curlx_dyn_init(&db[index], segment_length + 1);
- result = curlx_dyn_addn(&db[index], &source[start], segment_length);
- if(!result) {
- if(++num_splits == MAX_QUERY_COMPONENTS)
- result = CURLE_TOO_LARGE;
- }
- }
-fail:
- *num_splits_out = num_splits;
- return result;
-}
-
-static bool is_reserved_char(const char c)
-{
- return (ISALNUM(c) || ISURLPUNTCS(c));
-}
-
-static CURLcode uri_encode_path(struct Curl_str *original_path,
- struct dynbuf *new_path)
-{
- const char *p = curlx_str(original_path);
- size_t i;
-
- for(i = 0; i < curlx_strlen(original_path); i++) {
- /* Do not encode slashes or unreserved chars from RFC 3986 */
- CURLcode result = CURLE_OK;
- unsigned char c = p[i];
- if(is_reserved_char(c) || c == '/')
- result = curlx_dyn_addn(new_path, &c, 1);
- else
- result = curlx_dyn_addf(new_path, "%%%02X", c);
- if(result)
- return result;
- }
-
- return CURLE_OK;
-}
-
-static CURLcode encode_query_component(char *component, size_t len,
- struct dynbuf *db)
-{
- size_t i;
- for(i = 0; i < len; i++) {
- CURLcode result = CURLE_OK;
- unsigned char this_char = component[i];
-
- if(is_reserved_char(this_char))
- /* Escape unreserved chars from RFC 3986 */
- result = curlx_dyn_addn(db, &this_char, 1);
- else if(this_char == '+')
- /* Encode '+' as space */
- result = curlx_dyn_add(db, "%20");
- else
- result = curlx_dyn_addf(db, "%%%02X", this_char);
- if(result)
- return result;
- }
-
- return CURLE_OK;
-}
-
-/*
- * Populates a dynbuf containing url_encode(url_decode(in))
- */
-
-static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
- struct dynbuf *out)
-{
- char *out_s;
- size_t out_s_len;
- CURLcode result =
- Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA);
-
- if(!result) {
- result = encode_query_component(out_s, out_s_len, out);
- Curl_safefree(out_s);
- }
- return result;
-}
-
-static bool should_urlencode(struct Curl_str *service_name)
-{
- /*
- * These services require unmodified (not additionally URL-encoded) URL
- * paths.
- * should_urlencode == true is equivalent to should_urlencode_uri_path
- * from the AWS SDK. Urls are already normalized by the curl URL parser
- */
-
- if(curlx_str_cmp(service_name, "s3") ||
- curlx_str_cmp(service_name, "s3-express") ||
- curlx_str_cmp(service_name, "s3-outposts")) {
- return false;
- }
- return true;
-}
-
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_AWS */
#include "tool_libinfo.h"
#include "tool_strdup.h"
-static char *parse_filename(const char *ptr, size_t len);
-
#ifdef _WIN32
#define BOLD "\x1b[1m"
#define BOLDOFF "\x1b[22m"
#endif
#ifdef LINK
+/*
+ * Treat the Location: header specially, by writing a special escape
+ * sequence that adds a hyperlink to the displayed text. This makes
+ * the absolute URL of the redirect clickable in supported terminals,
+ * which could not happen otherwise for relative URLs. The Location:
+ * header is supposed to always be absolute so this theoretically
+ * should not be needed but the real world returns plenty of relative
+ * URLs here.
+ */
static void write_linked_location(CURL *curl, const char *location,
- size_t loclen, FILE *stream);
+ size_t loclen, FILE *stream)
+{
+ /* This would so simple if CURLINFO_REDIRECT_URL were available here */
+ CURLU *u = NULL;
+ char *copyloc = NULL, *locurl = NULL, *scheme = NULL, *finalurl = NULL;
+ const char *loc = location;
+ size_t llen = loclen;
+ int space_skipped = 0;
+ const char *vver = getenv("VTE_VERSION");
+
+ if(vver) {
+ curl_off_t num;
+ if(curlx_str_number(&vver, &num, CURL_OFF_T_MAX) ||
+ /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since
+ some of those versions have formatting bugs. (#10428) */
+ (num <= 4801))
+ goto locout;
+ }
+
+ /* Strip leading whitespace of the redirect URL */
+ while(llen && (*loc == ' ' || *loc == '\t')) {
+ ++loc;
+ --llen;
+ ++space_skipped;
+ }
+
+ /* Strip the trailing end-of-line characters, normally "\r\n" */
+ while(llen && (loc[llen - 1] == '\n' || loc[llen - 1] == '\r'))
+ --llen;
+
+ /* CURLU makes it easy to handle the relative URL case */
+ u = curl_url();
+ if(!u)
+ goto locout;
+
+ /* Create a null-terminated and whitespace-stripped copy of Location: */
+ copyloc = memdup0(loc, llen);
+ if(!copyloc)
+ goto locout;
+
+ /* The original URL to use as a base for a relative redirect URL */
+ if(curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &locurl))
+ goto locout;
+ if(curl_url_set(u, CURLUPART_URL, locurl, 0))
+ goto locout;
+
+ /* Redirected location. This can be either absolute or relative. */
+ if(curl_url_set(u, CURLUPART_URL, copyloc, 0))
+ goto locout;
+
+ if(curl_url_get(u, CURLUPART_URL, &finalurl, CURLU_NO_DEFAULT_PORT))
+ goto locout;
+
+ if(curl_url_get(u, CURLUPART_SCHEME, &scheme, 0))
+ goto locout;
+
+ if(!strcmp("http", scheme) ||
+ !strcmp("https", scheme) ||
+ !strcmp("ftp", scheme) ||
+ !strcmp("ftps", scheme)) {
+ curl_mfprintf(stream, "%.*s" LINK "%s" LINKST "%.*s" LINKOFF,
+ space_skipped, location,
+ finalurl,
+ (int)loclen - space_skipped, loc);
+ goto locdone;
+ }
+
+ /* Not a "safe" URL: do not linkify it */
+
+locout:
+ /* Write the normal output in case of error or unsafe */
+ fwrite(location, loclen, 1, stream);
+
+locdone:
+ if(u) {
+ curl_free(finalurl);
+ curl_free(scheme);
+ curl_url_cleanup(u);
+ curlx_free(copyloc);
+ }
+}
#endif
+/*
+ * Copies a filename part and returns an ALLOCATED data buffer.
+ */
+static char *parse_filename(const char *ptr, size_t len)
+{
+ char *copy;
+ char *p;
+ char *q;
+ char stop = '\0';
+
+ copy = memdup0(ptr, len);
+ if(!copy)
+ return NULL;
+
+ p = copy;
+ if(*p == '\'' || *p == '"') {
+ /* store the starting quote */
+ stop = *p;
+ p++;
+ }
+ else
+ stop = ';';
+
+ /* scan for the end letter and stop there */
+ q = strchr(p, stop);
+ if(q)
+ *q = '\0';
+
+ /* if the filename contains a path, only use filename portion */
+ q = strrchr(p, '/');
+ if(q) {
+ p = q + 1;
+ if(!*p) {
+ tool_safefree(copy);
+ return NULL;
+ }
+ }
+
+ /* If the filename contains a backslash, only use filename portion. The idea
+ is that even systems that do not handle backslashes as path separators
+ probably want the path removed for convenience. */
+ q = strrchr(p, '\\');
+ if(q) {
+ p = q + 1;
+ if(!*p) {
+ tool_safefree(copy);
+ return NULL;
+ }
+ }
+
+ /* make sure the filename does not end in \r or \n */
+ q = strchr(p, '\r');
+ if(q)
+ *q = '\0';
+
+ q = strchr(p, '\n');
+ if(q)
+ *q = '\0';
+
+ if(copy != p)
+ memmove(copy, p, strlen(p) + 1);
+
+#if defined(_WIN32) || defined(MSDOS)
+ {
+ char *sanitized;
+ SANITIZEcode sc = sanitize_file_name(&sanitized, copy, 0);
+ tool_safefree(copy);
+ if(sc)
+ return NULL;
+ copy = sanitized;
+ }
+#endif /* _WIN32 || MSDOS */
+
+ return copy;
+}
+
int tool_write_headers(struct HdrCbData *hdrcbdata, FILE *stream)
{
struct curl_slist *h = hdrcbdata->headlist;
}
return cb;
}
-
-/*
- * Copies a filename part and returns an ALLOCATED data buffer.
- */
-static char *parse_filename(const char *ptr, size_t len)
-{
- char *copy;
- char *p;
- char *q;
- char stop = '\0';
-
- copy = memdup0(ptr, len);
- if(!copy)
- return NULL;
-
- p = copy;
- if(*p == '\'' || *p == '"') {
- /* store the starting quote */
- stop = *p;
- p++;
- }
- else
- stop = ';';
-
- /* scan for the end letter and stop there */
- q = strchr(p, stop);
- if(q)
- *q = '\0';
-
- /* if the filename contains a path, only use filename portion */
- q = strrchr(p, '/');
- if(q) {
- p = q + 1;
- if(!*p) {
- tool_safefree(copy);
- return NULL;
- }
- }
-
- /* If the filename contains a backslash, only use filename portion. The idea
- is that even systems that do not handle backslashes as path separators
- probably want the path removed for convenience. */
- q = strrchr(p, '\\');
- if(q) {
- p = q + 1;
- if(!*p) {
- tool_safefree(copy);
- return NULL;
- }
- }
-
- /* make sure the filename does not end in \r or \n */
- q = strchr(p, '\r');
- if(q)
- *q = '\0';
-
- q = strchr(p, '\n');
- if(q)
- *q = '\0';
-
- if(copy != p)
- memmove(copy, p, strlen(p) + 1);
-
-#if defined(_WIN32) || defined(MSDOS)
- {
- char *sanitized;
- SANITIZEcode sc = sanitize_file_name(&sanitized, copy, 0);
- tool_safefree(copy);
- if(sc)
- return NULL;
- copy = sanitized;
- }
-#endif /* _WIN32 || MSDOS */
-
- return copy;
-}
-
-#ifdef LINK
-/*
- * Treat the Location: header specially, by writing a special escape
- * sequence that adds a hyperlink to the displayed text. This makes
- * the absolute URL of the redirect clickable in supported terminals,
- * which could not happen otherwise for relative URLs. The Location:
- * header is supposed to always be absolute so this theoretically
- * should not be needed but the real world returns plenty of relative
- * URLs here.
- */
-static void write_linked_location(CURL *curl, const char *location,
- size_t loclen, FILE *stream)
-{
- /* This would so simple if CURLINFO_REDIRECT_URL were available here */
- CURLU *u = NULL;
- char *copyloc = NULL, *locurl = NULL, *scheme = NULL, *finalurl = NULL;
- const char *loc = location;
- size_t llen = loclen;
- int space_skipped = 0;
- const char *vver = getenv("VTE_VERSION");
-
- if(vver) {
- curl_off_t num;
- if(curlx_str_number(&vver, &num, CURL_OFF_T_MAX) ||
- /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since
- some of those versions have formatting bugs. (#10428) */
- (num <= 4801))
- goto locout;
- }
-
- /* Strip leading whitespace of the redirect URL */
- while(llen && (*loc == ' ' || *loc == '\t')) {
- ++loc;
- --llen;
- ++space_skipped;
- }
-
- /* Strip the trailing end-of-line characters, normally "\r\n" */
- while(llen && (loc[llen - 1] == '\n' || loc[llen - 1] == '\r'))
- --llen;
-
- /* CURLU makes it easy to handle the relative URL case */
- u = curl_url();
- if(!u)
- goto locout;
-
- /* Create a null-terminated and whitespace-stripped copy of Location: */
- copyloc = memdup0(loc, llen);
- if(!copyloc)
- goto locout;
-
- /* The original URL to use as a base for a relative redirect URL */
- if(curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &locurl))
- goto locout;
- if(curl_url_set(u, CURLUPART_URL, locurl, 0))
- goto locout;
-
- /* Redirected location. This can be either absolute or relative. */
- if(curl_url_set(u, CURLUPART_URL, copyloc, 0))
- goto locout;
-
- if(curl_url_get(u, CURLUPART_URL, &finalurl, CURLU_NO_DEFAULT_PORT))
- goto locout;
-
- if(curl_url_get(u, CURLUPART_SCHEME, &scheme, 0))
- goto locout;
-
- if(!strcmp("http", scheme) ||
- !strcmp("https", scheme) ||
- !strcmp("ftp", scheme) ||
- !strcmp("ftps", scheme)) {
- curl_mfprintf(stream, "%.*s" LINK "%s" LINKST "%.*s" LINKOFF,
- space_skipped, location,
- finalurl,
- (int)loclen - space_skipped, loc);
- goto locdone;
- }
-
- /* Not a "safe" URL: do not linkify it */
-
-locout:
- /* Write the normal output in case of error or unsafe */
- fwrite(location, loclen, 1, stream);
-
-locdone:
- if(u) {
- curl_free(finalurl);
- curl_free(scheme);
- curl_url_cleanup(u);
- curlx_free(copyloc);
- }
-}
-#endif
errno, curlx_strerror(errno, errbuf, sizeof(errbuf)));
}
-static void init_httprequest(struct sws_httprequest *req)
-{
- req->checkindex = 0;
- req->offset = 0;
- req->testno = DOCNUMBER_NOTHING;
- req->partno = 0;
- req->connect_request = FALSE;
- req->open = TRUE;
- req->auth_req = FALSE;
- req->auth = FALSE;
- req->cl = 0;
- req->digest = FALSE;
- req->ntlm = FALSE;
- req->skip = 0;
- req->skipall = FALSE;
- req->noexpect = FALSE;
- req->delay = 0;
- req->writedelay = 0;
- req->rcmd = RCMD_NORMALREQ;
- req->prot_version = 0;
- req->callcount = 0;
- req->connect_port = 0;
- req->done_processing = 0;
- req->upgrade = 0;
- req->upgrade_request = 0;
-}
-
-static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req);
-
-/* returns 1 if the connection should be serviced again immediately, 0 if there
- is no data waiting, or < 0 if it should be closed */
-static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req)
-{
- int fail = 0;
- char *reqbuf = req->reqbuf;
- ssize_t got = 0;
- int overflow = 0;
-
- if(req->upgrade_request) {
- /* upgraded connection, work it differently until end of connection */
- logmsg("Upgraded connection, this is no longer HTTP/1");
- sws_send_doc(sock, req);
-
- /* dump the request received so far to the external file */
- reqbuf[req->offset] = '\0';
- sws_storerequest(reqbuf, req->offset);
- req->offset = 0;
-
- /* read websocket traffic */
- if(req->open) {
- logmsg("wait for websocket traffic");
- do {
- got = sread(sock, reqbuf + req->offset,
- sizeof(req->reqbuf) - req->offset);
- if(got > 0) {
- req->offset += got;
- logmsg("Got %zu bytes from client", got);
- }
-
- if((got == -1) &&
- ((SOCKERRNO == EAGAIN) || (SOCKERRNO == SOCKEWOULDBLOCK))) {
- int rc;
- fd_set input;
- fd_set output;
- struct timeval timeout = { 0 };
- timeout.tv_sec = 1; /* 1000 ms */
-
- logmsg("Got EAGAIN from sread");
- FD_ZERO(&input);
- FD_ZERO(&output);
- got = 0;
-#ifdef __DJGPP__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Warith-conversion"
-#endif
- FD_SET(sock, &input);
-#ifdef __DJGPP__
-#pragma GCC diagnostic pop
-#endif
- do {
- logmsg("Wait until readable");
- rc = select((int)sock + 1, &input, &output, NULL, &timeout);
- } while(rc < 0 && SOCKERRNO == SOCKEINTR && !got_exit_signal);
- logmsg("readable %d", rc);
- if(rc)
- got = 1;
- }
- } while(got > 0);
- }
- else {
- logmsg("NO wait for websocket traffic");
- }
- if(req->offset) {
- logmsg("log the websocket traffic");
- /* dump the incoming websocket traffic to the external file */
- reqbuf[req->offset] = '\0';
- sws_storerequest(reqbuf, req->offset);
- req->offset = 0;
- }
- init_httprequest(req);
-
- return -1;
- }
-
- if(req->offset >= sizeof(req->reqbuf) - 1) {
- /* buffer is already full; do nothing */
- overflow = 1;
- }
- else {
- if(req->skip)
- /* we are instructed to not read the entire thing, so we make sure to
- only read what we are supposed to and NOT read the entire thing the
- client wants to send! */
- got = sread(sock, reqbuf + req->offset, req->cl);
- else
- got = sread(sock, reqbuf + req->offset,
- sizeof(req->reqbuf) - 1 - req->offset);
-
- if(got_exit_signal)
- return -1;
- if(got == 0) {
- logmsg("Connection closed by client");
- fail = 1;
- }
- else if(got < 0) {
- char errbuf[STRERROR_LEN];
- int error = SOCKERRNO;
- if(EAGAIN == error || SOCKEWOULDBLOCK == error) {
- /* nothing to read at the moment */
- return 0;
- }
- logmsg("recv() returned error (%d) %s",
- error, curlx_strerror(error, errbuf, sizeof(errbuf)));
- fail = 1;
- }
- if(fail) {
- /* dump the request received so far to the external file */
- reqbuf[req->offset] = '\0';
- sws_storerequest(reqbuf, req->offset);
- return -1;
- }
-
- logmsg("Read %zd bytes", got);
-
- req->offset += (size_t)got;
- reqbuf[req->offset] = '\0';
-
- req->done_processing = sws_ProcessRequest(req);
- if(got_exit_signal)
- return -1;
- }
-
- if(overflow || (req->offset == sizeof(req->reqbuf) - 1 && got > 0)) {
- logmsg("Request would overflow buffer, closing connection");
- /* dump request received so far to external file anyway */
- reqbuf[sizeof(req->reqbuf) - 1] = '\0';
- fail = 1;
- }
- else if(req->offset > sizeof(req->reqbuf) - 1) {
- logmsg("Request buffer overflow, closing connection");
- /* dump request received so far to external file anyway */
- reqbuf[sizeof(req->reqbuf) - 1] = '\0';
- fail = 1;
- }
- else
- reqbuf[req->offset] = '\0';
-
- /* at the end of a request dump it to an external file */
- if(fail || req->done_processing)
- sws_storerequest(reqbuf, req->offset);
- if(got_exit_signal)
- return -1;
-
- return fail ? -1 : 1;
-}
-
/* returns -1 on failure */
static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req)
{
return 0;
}
+static void init_httprequest(struct sws_httprequest *req)
+{
+ req->checkindex = 0;
+ req->offset = 0;
+ req->testno = DOCNUMBER_NOTHING;
+ req->partno = 0;
+ req->connect_request = FALSE;
+ req->open = TRUE;
+ req->auth_req = FALSE;
+ req->auth = FALSE;
+ req->cl = 0;
+ req->digest = FALSE;
+ req->ntlm = FALSE;
+ req->skip = 0;
+ req->skipall = FALSE;
+ req->noexpect = FALSE;
+ req->delay = 0;
+ req->writedelay = 0;
+ req->rcmd = RCMD_NORMALREQ;
+ req->prot_version = 0;
+ req->callcount = 0;
+ req->connect_port = 0;
+ req->done_processing = 0;
+ req->upgrade = 0;
+ req->upgrade_request = 0;
+}
+
+/* returns 1 if the connection should be serviced again immediately, 0 if there
+ is no data waiting, or < 0 if it should be closed */
+static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req)
+{
+ int fail = 0;
+ char *reqbuf = req->reqbuf;
+ ssize_t got = 0;
+ int overflow = 0;
+
+ if(req->upgrade_request) {
+ /* upgraded connection, work it differently until end of connection */
+ logmsg("Upgraded connection, this is no longer HTTP/1");
+ sws_send_doc(sock, req);
+
+ /* dump the request received so far to the external file */
+ reqbuf[req->offset] = '\0';
+ sws_storerequest(reqbuf, req->offset);
+ req->offset = 0;
+
+ /* read websocket traffic */
+ if(req->open) {
+ logmsg("wait for websocket traffic");
+ do {
+ got = sread(sock, reqbuf + req->offset,
+ sizeof(req->reqbuf) - req->offset);
+ if(got > 0) {
+ req->offset += got;
+ logmsg("Got %zu bytes from client", got);
+ }
+
+ if((got == -1) &&
+ ((SOCKERRNO == EAGAIN) || (SOCKERRNO == SOCKEWOULDBLOCK))) {
+ int rc;
+ fd_set input;
+ fd_set output;
+ struct timeval timeout = { 0 };
+ timeout.tv_sec = 1; /* 1000 ms */
+
+ logmsg("Got EAGAIN from sread");
+ FD_ZERO(&input);
+ FD_ZERO(&output);
+ got = 0;
+#ifdef __DJGPP__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warith-conversion"
+#endif
+ FD_SET(sock, &input);
+#ifdef __DJGPP__
+#pragma GCC diagnostic pop
+#endif
+ do {
+ logmsg("Wait until readable");
+ rc = select((int)sock + 1, &input, &output, NULL, &timeout);
+ } while(rc < 0 && SOCKERRNO == SOCKEINTR && !got_exit_signal);
+ logmsg("readable %d", rc);
+ if(rc)
+ got = 1;
+ }
+ } while(got > 0);
+ }
+ else {
+ logmsg("NO wait for websocket traffic");
+ }
+ if(req->offset) {
+ logmsg("log the websocket traffic");
+ /* dump the incoming websocket traffic to the external file */
+ reqbuf[req->offset] = '\0';
+ sws_storerequest(reqbuf, req->offset);
+ req->offset = 0;
+ }
+ init_httprequest(req);
+
+ return -1;
+ }
+
+ if(req->offset >= sizeof(req->reqbuf) - 1) {
+ /* buffer is already full; do nothing */
+ overflow = 1;
+ }
+ else {
+ if(req->skip)
+ /* we are instructed to not read the entire thing, so we make sure to
+ only read what we are supposed to and NOT read the entire thing the
+ client wants to send! */
+ got = sread(sock, reqbuf + req->offset, req->cl);
+ else
+ got = sread(sock, reqbuf + req->offset,
+ sizeof(req->reqbuf) - 1 - req->offset);
+
+ if(got_exit_signal)
+ return -1;
+ if(got == 0) {
+ logmsg("Connection closed by client");
+ fail = 1;
+ }
+ else if(got < 0) {
+ char errbuf[STRERROR_LEN];
+ int error = SOCKERRNO;
+ if(EAGAIN == error || SOCKEWOULDBLOCK == error) {
+ /* nothing to read at the moment */
+ return 0;
+ }
+ logmsg("recv() returned error (%d) %s",
+ error, curlx_strerror(error, errbuf, sizeof(errbuf)));
+ fail = 1;
+ }
+ if(fail) {
+ /* dump the request received so far to the external file */
+ reqbuf[req->offset] = '\0';
+ sws_storerequest(reqbuf, req->offset);
+ return -1;
+ }
+
+ logmsg("Read %zd bytes", got);
+
+ req->offset += (size_t)got;
+ reqbuf[req->offset] = '\0';
+
+ req->done_processing = sws_ProcessRequest(req);
+ if(got_exit_signal)
+ return -1;
+ }
+
+ if(overflow || (req->offset == sizeof(req->reqbuf) - 1 && got > 0)) {
+ logmsg("Request would overflow buffer, closing connection");
+ /* dump request received so far to external file anyway */
+ reqbuf[sizeof(req->reqbuf) - 1] = '\0';
+ fail = 1;
+ }
+ else if(req->offset > sizeof(req->reqbuf) - 1) {
+ logmsg("Request buffer overflow, closing connection");
+ /* dump request received so far to external file anyway */
+ reqbuf[sizeof(req->reqbuf) - 1] = '\0';
+ fail = 1;
+ }
+ else
+ reqbuf[req->offset] = '\0';
+
+ /* at the end of a request dump it to an external file */
+ if(fail || req->done_processing)
+ sws_storerequest(reqbuf, req->offset);
+ if(got_exit_signal)
+ return -1;
+
+ return fail ? -1 : 1;
+}
+
static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
{
srvr_sockaddr_union_t serveraddr;
static sigjmp_buf timeoutbuf;
#endif
-#if defined(HAVE_ALARM) && defined(SIGALRM)
-static const unsigned int rexmtval = TIMEOUT;
-#endif
-
-/*****************************************************************************
- * FUNCTION PROTOTYPES *
- *****************************************************************************/
-
-static struct tftphdr *rw_init(int);
-
-static struct tftphdr *w_init(void);
-
-static struct tftphdr *r_init(void);
-
-static void read_ahead(struct testcase *test, int convert);
-
-static ssize_t write_behind(struct testcase *test, int convert);
-
-static int synchnet(curl_socket_t);
-
-static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size);
-
-static int validate_access(struct testcase *test,
- const char *filename, unsigned short mode);
-
-static void sendtftp(struct testcase *test, const struct formats *pf);
-
-static void recvtftp(struct testcase *test, const struct formats *pf);
-
-static void nak(int error);
-
-#if defined(HAVE_ALARM) && defined(SIGALRM)
-
-static void mysignal(int sig, void (*handler)(int));
-
-static void timer(int signum);
-
-static void justtimeout(int signum);
-
-#endif /* HAVE_ALARM && SIGALRM */
-
/*****************************************************************************
* FUNCTION IMPLEMENTATIONS *
*****************************************************************************/
#if defined(HAVE_ALARM) && defined(SIGALRM)
+static const unsigned int rexmtval = TIMEOUT;
/*
* Like signal(), but with well-defined semantics.
#endif /* HAVE_ALARM && SIGALRM */
+/*
+ * Send a nak packet (error message). Error code passed in is one of the
+ * standard TFTP codes, or a Unix errno offset by 100.
+ */
+static void nak(int error)
+{
+ struct tftphdr *tp;
+ int length;
+ struct errmsg *pe;
+
+ tp = &trsbuf.hdr;
+ tp->th_opcode = htons(opcode_ERROR);
+ tp->th_code = htons((unsigned short)error);
+ for(pe = errmsgs; pe->e_code >= 0; pe++)
+ if(pe->e_code == error)
+ break;
+ if(pe->e_code < 0) {
+ curlx_strerror(error - 100, pe->e_msg, sizeof(pe->e_msg));
+ tp->th_code = TFTP_EUNDEF; /* set 'undef' errorcode */
+ }
+ length = (int)strlen(pe->e_msg);
+
+ /* we use memcpy() instead of strcpy() in order to avoid buffer overflow
+ * report from glibc with FORTIFY_SOURCE */
+ memcpy(tp->th_msg, pe->e_msg, length + 1);
+ length += 5;
+ if(swrite(peer, &trsbuf.storage[0], length) != length)
+ logmsg("nak: fail\n");
+}
+
/*
* init for either read-ahead or write-behind.
* zero for write-behind, one for read-head.
return rw_init(1); /* read-ahead */
}
-/* Have emptied current buffer by sending to net and getting ack.
- Free it and return next buffer filled with data.
- */
-static int readit(struct testcase *test, struct tftphdr * volatile *dpp,
- int convert /* if true, convert to ASCII */)
-{
- struct bf *b;
-
- bfs[current].counter = BF_FREE; /* free old one */
- current = !current; /* "incr" current */
-
- b = &bfs[current]; /* look at new buffer */
- if(b->counter == BF_FREE) /* if empty */
- read_ahead(test, convert); /* fill it */
-
- *dpp = &b->buf.hdr; /* set caller's ptr */
- return b->counter;
-}
-
/*
* fill the input buffer, doing ASCII conversions if requested
* conversions are lf -> cr, lf and cr -> cr, nul
b->counter = (int)(p - dp->th_data);
}
-/* Update count associated with the buffer, get new buffer from the queue.
- Calls write_behind only if next buffer not available.
+/* Have emptied current buffer by sending to net and getting ack.
+ Free it and return next buffer filled with data.
*/
-static int writeit(struct testcase *test, struct tftphdr * volatile *dpp,
- int ct, int convert)
+static int readit(struct testcase *test, struct tftphdr * volatile *dpp,
+ int convert /* if true, convert to ASCII */)
{
- bfs[current].counter = ct; /* set size of data to write */
- current = !current; /* switch to other buffer */
- if(bfs[current].counter != BF_FREE) /* if not free */
- write_behind(test, convert); /* flush it */
- bfs[current].counter = BF_ALLOC; /* mark as allocated */
- *dpp = &bfs[current].buf.hdr;
- return ct; /* this is a lie of course */
+ struct bf *b;
+
+ bfs[current].counter = BF_FREE; /* free old one */
+ current = !current; /* "incr" current */
+
+ b = &bfs[current]; /* look at new buffer */
+ if(b->counter == BF_FREE) /* if empty */
+ read_ahead(test, convert); /* fill it */
+
+ *dpp = &b->buf.hdr; /* set caller's ptr */
+ return b->counter;
}
/*
return count;
}
+/* Update count associated with the buffer, get new buffer from the queue.
+ Calls write_behind only if next buffer not available.
+ */
+static int writeit(struct testcase *test, struct tftphdr * volatile *dpp,
+ int ct, int convert)
+{
+ bfs[current].counter = ct; /* set size of data to write */
+ current = !current; /* switch to other buffer */
+ if(bfs[current].counter != BF_FREE) /* if not free */
+ write_behind(test, convert); /* flush it */
+ bfs[current].counter = BF_ALLOC; /* mark as allocated */
+ *dpp = &bfs[current].buf.hdr;
+ return ct; /* this is a lie of course */
+}
+
/* When an error has occurred, it is possible that the two sides are out of
* synch. Ie: that what I think is the other side's response to packet N is
* really their response to packet N-1.
return j;
}
-static int test_tftpd(int argc, char **argv)
+/* Based on the testno, parse the correct server commands. */
+static int tftpd_parse_servercmd(struct testcase *req)
{
- srvr_sockaddr_union_t me;
- struct tftphdr *tp;
- ssize_t n = 0;
- int arg = 1;
- unsigned short port = 8999; /* UDP */
- curl_socket_t sock = CURL_SOCKET_BAD;
- int flag;
- int rc;
+ FILE *stream;
int error;
- char errbuf[STRERROR_LEN];
- struct testcase test;
- int result = 0;
- srvr_sockaddr_union_t from;
- curl_socklen_t fromlen;
-
- memset(&test, 0, sizeof(test));
- pidname = ".tftpd.pid";
- serverlogfile = "log/tftpd.log";
- serverlogslocked = 0;
+ stream = test2fopen(req->testno, logdir);
+ if(!stream) {
+ char errbuf[STRERROR_LEN];
+ error = errno;
+ logmsg("fopen() failed with error (%d) %s",
+ error, curlx_strerror(error, errbuf, sizeof(errbuf)));
+ logmsg(" Could not open test file %ld", req->testno);
+ return 1; /* done */
+ }
+ else {
+ char *orgcmd = NULL;
+ char *cmd = NULL;
+ size_t cmdsize = 0;
+ int num = 0;
- while(argc > arg) {
- const char *opt;
- curl_off_t num;
- if(!strcmp("--version", argv[arg])) {
- printf("tftpd IPv4%s\n",
-#ifdef USE_IPV6
- "/IPv6"
-#else
- ""
-#endif
- );
- return 0;
- }
- else if(!strcmp("--pidfile", argv[arg])) {
- arg++;
- if(argc > arg)
- pidname = argv[arg++];
- }
- else if(!strcmp("--portfile", argv[arg])) {
- arg++;
- if(argc > arg)
- portname = argv[arg++];
- }
- else if(!strcmp("--logfile", argv[arg])) {
- arg++;
- if(argc > arg)
- serverlogfile = argv[arg++];
- }
- else if(!strcmp("--logdir", argv[arg])) {
- arg++;
- if(argc > arg)
- logdir = argv[arg++];
- }
- else if(!strcmp("--ipv4", argv[arg])) {
-#ifdef USE_IPV6
- ipv_inuse = "IPv4";
- use_ipv6 = FALSE;
-#endif
- arg++;
- }
- else if(!strcmp("--ipv6", argv[arg])) {
-#ifdef USE_IPV6
- ipv_inuse = "IPv6";
- use_ipv6 = TRUE;
-#endif
- arg++;
+ /* get the custom server control "commands" */
+ error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
+ curlx_fclose(stream);
+ if(error) {
+ logmsg("getpart() failed with error (%d)", error);
+ return 1; /* done */
}
- else if(!strcmp("--port", argv[arg])) {
- arg++;
- if(argc > arg) {
- opt = argv[arg];
- if(!curlx_str_number(&opt, &num, 0xffff))
- port = (unsigned short)num;
- arg++;
+
+ cmd = orgcmd;
+ while(cmd && cmdsize) {
+ char *check;
+ if(sscanf(cmd, "writedelay: %d", &num) == 1) {
+ logmsg("instructed to delay %d secs between packets", num);
+ req->writedelay = num;
}
- }
- else if(!strcmp("--srcdir", argv[arg])) {
- arg++;
- if(argc > arg) {
- srcpath = argv[arg];
- arg++;
+ else {
+ logmsg("Unknown <servercmd> instruction found: %s", cmd);
}
- }
- else {
- puts("Usage: tftpd [option]\n"
- " --version\n"
- " --logfile [file]\n"
- " --logdir [directory]\n"
- " --pidfile [file]\n"
- " --portfile [file]\n"
- " --ipv4\n"
- " --ipv6\n"
- " --port [port]\n"
- " --srcdir [path]");
- return 0;
- }
- }
-
- snprintf(loglockfile, sizeof(loglockfile), "%s/%s/tftp-%s.lock",
- logdir, SERVERLOGS_LOCKDIR, ipv_inuse);
+ /* try to deal with CRLF or just LF */
+ check = strchr(cmd, '\r');
+ if(!check)
+ check = strchr(cmd, '\n');
- install_signal_handlers(true);
+ if(check) {
+ /* get to the letter following the newline */
+ while((*check == '\r') || (*check == '\n'))
+ check++;
-#ifdef USE_IPV6
- if(!use_ipv6)
-#endif
- sock = socket(AF_INET, SOCK_DGRAM, 0);
-#ifdef USE_IPV6
- else
- sock = socket(AF_INET6, SOCK_DGRAM, 0);
-#endif
-
- if(CURL_SOCKET_BAD == sock) {
- error = SOCKERRNO;
- logmsg("Error creating socket (%d) %s",
- error, curlx_strerror(error, errbuf, sizeof(errbuf)));
- result = 1;
- goto tftpd_cleanup;
+ if(!*check)
+ /* if we reached a zero, get out */
+ break;
+ cmd = check;
+ }
+ else
+ break;
+ }
+ free(orgcmd);
}
- flag = 1;
- if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) {
- error = SOCKERRNO;
- logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
- error, curlx_strerror(error, errbuf, sizeof(errbuf)));
- result = 1;
- goto tftpd_cleanup;
- }
+ return 0; /* OK! */
+}
-#ifdef USE_IPV6
- if(!use_ipv6) {
-#endif
- memset(&me.sa4, 0, sizeof(me.sa4));
- me.sa4.sin_family = AF_INET;
- me.sa4.sin_addr.s_addr = INADDR_ANY;
- me.sa4.sin_port = htons(port);
- rc = bind(sock, &me.sa, sizeof(me.sa4));
-#ifdef USE_IPV6
- }
- else {
- memset(&me.sa6, 0, sizeof(me.sa6));
- me.sa6.sin6_family = AF_INET6;
- me.sa6.sin6_addr = in6addr_any;
- me.sa6.sin6_port = htons(port);
- rc = bind(sock, &me.sa, sizeof(me.sa6));
- }
-#endif /* USE_IPV6 */
- if(rc) {
- error = SOCKERRNO;
- logmsg("Error binding socket on port %hu (%d) %s", port,
- error, curlx_strerror(error, errbuf, sizeof(errbuf)));
- result = 1;
- goto tftpd_cleanup;
- }
+/*
+ * Validate file access.
+ */
+static int validate_access(struct testcase *test,
+ const char *filename, unsigned short mode)
+{
+ char *ptr;
- if(!port) {
- /* The system was supposed to choose a port number, figure out which
- port we actually got and update the listener port value with it. */
- curl_socklen_t la_size;
- srvr_sockaddr_union_t localaddr;
-#ifdef USE_IPV6
- if(!use_ipv6)
-#endif
- la_size = sizeof(localaddr.sa4);
-#ifdef USE_IPV6
- else
- la_size = sizeof(localaddr.sa6);
-#endif
- memset(&localaddr.sa, 0, (size_t)la_size);
- if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
- error = SOCKERRNO;
- logmsg("getsockname() failed with error (%d) %s",
- error, curlx_strerror(error, errbuf, sizeof(errbuf)));
- sclose(sock);
- goto tftpd_cleanup;
- }
- switch(localaddr.sa.sa_family) {
- case AF_INET:
- port = ntohs(localaddr.sa4.sin_port);
- break;
-#ifdef USE_IPV6
- case AF_INET6:
- port = ntohs(localaddr.sa6.sin6_port);
- break;
-#endif
- default:
- break;
- }
- if(!port) {
- /* Real failure, listener port shall not be zero beyond this point. */
- logmsg("Apparently getsockname() succeeded, with listener port zero.");
- logmsg("A valid reason for this failure is a binary built without");
- logmsg("proper network library linkage. This might not be the only");
- logmsg("reason, but double check it before anything else.");
- result = 2;
- goto tftpd_cleanup;
- }
- }
+ logmsg("trying to get file: %s mode %x", filename, mode);
- tftpd_wrotepidfile = write_pidfile(pidname);
- if(!tftpd_wrotepidfile) {
- result = 1;
- goto tftpd_cleanup;
- }
+ if(!strncmp("verifiedserver", filename, 14)) {
+ char weare[128];
+ size_t count = snprintf(weare, sizeof(weare), "WE ROOLZ: %ld\r\n",
+ (long)our_getpid());
- if(portname) {
- tftpd_wroteportfile = write_portfile(portname, port);
- if(!tftpd_wroteportfile) {
- result = 1;
- goto tftpd_cleanup;
- }
+ logmsg("Are-we-friendly question received");
+ test->buffer = strdup(weare);
+ test->rptr = test->buffer; /* set read pointer */
+ test->bufsize = count; /* set total count */
+ test->rcount = count; /* set data left to read */
+ return 0; /* fine */
}
- logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
-
- for(;;) {
- fromlen = sizeof(from);
-#ifdef USE_IPV6
- if(!use_ipv6)
-#endif
- fromlen = sizeof(from.sa4);
-#ifdef USE_IPV6
- else
- fromlen = sizeof(from.sa6);
-#endif
- n = (ssize_t)recvfrom(sock, &trsbuf.storage[0], sizeof(trsbuf.storage), 0,
- &from.sa, &fromlen);
- if(got_exit_signal)
- break;
- if(n < 0) {
- logmsg("recvfrom");
- result = 3;
- break;
- }
+ /* find the last slash */
+ ptr = strrchr(filename, '/');
- set_advisor_read_lock(loglockfile);
- serverlogslocked = 1;
+ if(ptr) {
+ char partbuf[80] = "data";
+ long partno;
+ long testno;
+ const char *pval;
+ curl_off_t num;
+ FILE *stream;
-#ifdef USE_IPV6
- if(!use_ipv6) {
-#endif
- from.sa4.sin_family = AF_INET;
- peer = socket(AF_INET, SOCK_DGRAM, 0);
- if(CURL_SOCKET_BAD == peer) {
- logmsg("socket");
- result = 2;
- break;
- }
- if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) {
- logmsg("connect: fail");
- result = 1;
- break;
- }
-#ifdef USE_IPV6
- }
- else {
- from.sa6.sin6_family = AF_INET6;
- peer = socket(AF_INET6, SOCK_DGRAM, 0);
- if(CURL_SOCKET_BAD == peer) {
- logmsg("socket");
- result = 2;
- break;
- }
- if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) {
- logmsg("connect: fail");
- result = 1;
- break;
- }
- }
-#endif
+ ptr++; /* skip the slash */
- maxtimeout = 5 * TIMEOUT;
+ /* skip all non-numericals following the slash */
+ while(*ptr && !ISDIGIT(*ptr))
+ ptr++;
- tp = &trsbuf.hdr;
- tp->th_opcode = ntohs(tp->th_opcode);
- if(tp->th_opcode == opcode_RRQ || tp->th_opcode == opcode_WRQ) {
- memset(&test, 0, sizeof(test));
- if(do_tftp(&test, tp, n) < 0)
- break;
- free(test.buffer);
+ /* get the number */
+ pval = ptr;
+ if(!curlx_str_number(&pval, &num, INT_MAX))
+ testno = (long)num;
+ else {
+ logmsg("tftpd: failed to read the test number from '%s'", filename);
+ return TFTP_EACCESS;
}
- sclose(peer);
- peer = CURL_SOCKET_BAD;
-
- if(got_exit_signal)
- break;
- if(serverlogslocked) {
- serverlogslocked = 0;
- clear_advisor_read_lock(loglockfile);
+ if(testno > 10000) {
+ partno = testno % 10000;
+ testno /= 10000;
}
+ else
+ partno = 0;
- logmsg("end of one transfer");
- }
-
-tftpd_cleanup:
-
- if(test.ofile > 0)
- close(test.ofile);
-
- if((peer != sock) && (peer != CURL_SOCKET_BAD))
- sclose(peer);
-
- if(sock != CURL_SOCKET_BAD)
- sclose(sock);
+ logmsg("requested test number %ld part %ld", testno, partno);
- if(got_exit_signal)
- logmsg("signalled to die");
+ test->testno = testno;
- if(tftpd_wrotepidfile)
- unlink(pidname);
- if(tftpd_wroteportfile)
- unlink(portname);
+ (void)tftpd_parse_servercmd(test);
- if(serverlogslocked) {
- serverlogslocked = 0;
- clear_advisor_read_lock(loglockfile);
- }
+ stream = test2fopen(testno, logdir);
- restore_signal_handlers(true);
+ if(partno)
+ snprintf(partbuf, sizeof(partbuf), "data%ld", partno);
- if(got_exit_signal) {
- logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)",
- ipv_inuse, (int)port, (long)our_getpid(), exit_signal);
- /*
- * To properly set the return status of the process we
- * must raise the same signal SIGINT or SIGTERM that we
- * caught and let the old handler take care of it.
- */
- raise(exit_signal);
+ if(!stream) {
+ char errbuf[STRERROR_LEN];
+ int error = errno;
+ logmsg("fopen() failed with error (%d) %s",
+ error, curlx_strerror(error, errbuf, sizeof(errbuf)));
+ logmsg("Could not open test file for test: %ld", testno);
+ return TFTP_EACCESS;
+ }
+ else {
+ size_t count;
+ int error = getpart(&test->buffer, &count, "reply", partbuf, stream);
+ curlx_fclose(stream);
+ if(error) {
+ logmsg("getpart() failed with error (%d)", error);
+ return TFTP_EACCESS;
+ }
+ if(test->buffer) {
+ test->rptr = test->buffer; /* set read pointer */
+ test->bufsize = count; /* set total count */
+ test->rcount = count; /* set data left to read */
+ }
+ else
+ return TFTP_EACCESS;
+ }
+ }
+ else {
+ logmsg("no slash found in path");
+ return TFTP_EACCESS; /* failure */
}
- logmsg("========> tftpd quits");
- return result;
+ logmsg("file opened and all is good");
+ return 0;
+}
+
+/*
+ * Send the requested file.
+ */
+static void sendtftp(struct testcase *test, const struct formats *pf)
+{
+ int size;
+ ssize_t n;
+ /* These are volatile to live through a siglongjmp */
+ volatile unsigned short sendblock; /* block count */
+ struct tftphdr * volatile sdp = r_init(); /* data buffer */
+ struct tftphdr * const sap = &ackbuf.hdr; /* ack buffer */
+
+ sendblock = 1;
+#if defined(HAVE_ALARM) && defined(SIGALRM)
+ mysignal(SIGALRM, timer);
+#endif
+ do {
+ size = readit(test, (struct tftphdr * volatile *)&sdp, pf->f_convert);
+ if(size < 0) {
+ nak(errno + 100);
+ return;
+ }
+ sdp->th_opcode = htons(opcode_DATA);
+ sdp->th_block = htons(sendblock);
+ timeout = 0;
+#ifdef HAVE_SIGSETJMP
+ (void)sigsetjmp(timeoutbuf, 1);
+#endif
+ if(test->writedelay) {
+ logmsg("Pausing %d seconds before %d bytes", test->writedelay, size);
+ curlx_wait_ms(1000 * test->writedelay);
+ }
+
+send_data:
+ logmsg("write");
+ if(swrite(peer, sdp, size + 4) != size + 4) {
+ logmsg("write: fail");
+ return;
+ }
+ read_ahead(test, pf->f_convert);
+ for(;;) {
+#ifdef HAVE_ALARM
+ alarm(rexmtval); /* read the ack */
+#endif
+ logmsg("read");
+ n = sread(peer, &ackbuf.storage[0], sizeof(ackbuf.storage));
+ logmsg("read: %zd", n);
+#ifdef HAVE_ALARM
+ alarm(0);
+#endif
+ if(got_exit_signal)
+ return;
+ if(n < 0) {
+ logmsg("read: fail");
+ return;
+ }
+ sap->th_opcode = ntohs(sap->th_opcode);
+ sap->th_block = ntohs(sap->th_block);
+
+ if(sap->th_opcode == opcode_ERROR) {
+ logmsg("got ERROR");
+ return;
+ }
+
+ if(sap->th_opcode == opcode_ACK) {
+ if(sap->th_block == sendblock) {
+ break;
+ }
+ /* Re-synchronize with the other side */
+ (void)synchnet(peer);
+ if(sap->th_block == (sendblock - 1)) {
+ goto send_data;
+ }
+ }
+ }
+ sendblock++;
+ } while(size == SEGSIZE);
+}
+
+/*
+ * Receive a file.
+ */
+static void recvtftp(struct testcase *test, const struct formats *pf)
+{
+ ssize_t n, size;
+ /* These are volatile to live through a siglongjmp */
+ volatile unsigned short recvblock; /* block count */
+ struct tftphdr * volatile rdp; /* data buffer */
+ struct tftphdr *rap; /* ack buffer */
+
+ recvblock = 0;
+ rdp = w_init();
+#if defined(HAVE_ALARM) && defined(SIGALRM)
+ mysignal(SIGALRM, timer);
+#endif
+ rap = &ackbuf.hdr;
+ do {
+ timeout = 0;
+ rap->th_opcode = htons(opcode_ACK);
+ rap->th_block = htons(recvblock);
+ recvblock++;
+#ifdef HAVE_SIGSETJMP
+ (void)sigsetjmp(timeoutbuf, 1);
+#endif
+send_ack:
+ logmsg("write");
+ if(swrite(peer, &ackbuf.storage[0], 4) != 4) {
+ logmsg("write: fail");
+ goto abort;
+ }
+ write_behind(test, pf->f_convert);
+ for(;;) {
+#ifdef HAVE_ALARM
+ alarm(rexmtval);
+#endif
+ logmsg("read");
+ n = sread(peer, rdp, PKTSIZE);
+ logmsg("read: %zd", n);
+#ifdef HAVE_ALARM
+ alarm(0);
+#endif
+ if(got_exit_signal)
+ goto abort;
+ if(n < 0) { /* really? */
+ logmsg("read: fail");
+ goto abort;
+ }
+ rdp->th_opcode = ntohs(rdp->th_opcode);
+ rdp->th_block = ntohs(rdp->th_block);
+ if(rdp->th_opcode == opcode_ERROR)
+ goto abort;
+ if(rdp->th_opcode == opcode_DATA) {
+ if(rdp->th_block == recvblock) {
+ break; /* normal */
+ }
+ /* Re-synchronize with the other side */
+ (void)synchnet(peer);
+ if(rdp->th_block == (recvblock - 1))
+ goto send_ack; /* rexmit */
+ }
+ }
+
+ size = writeit(test, &rdp, (int)(n - 4), pf->f_convert);
+ if(size != (n - 4)) { /* ahem */
+ if(size < 0)
+ nak(errno + 100);
+ else
+ nak(TFTP_ENOSPACE);
+ goto abort;
+ }
+ } while(size == SEGSIZE);
+ write_behind(test, pf->f_convert);
+ /* close the output file as early as possible after upload completion */
+ if(test->ofile > 0) {
+ close(test->ofile);
+ test->ofile = 0;
+ }
+
+ rap->th_opcode = htons(opcode_ACK); /* send the "final" ack */
+ rap->th_block = htons(recvblock);
+ (void)swrite(peer, &ackbuf.storage[0], 4);
+#if defined(HAVE_ALARM) && defined(SIGALRM)
+ mysignal(SIGALRM, justtimeout); /* just abort read on timeout */
+ alarm(rexmtval);
+#endif
+ /* normally times out and quits */
+ n = sread(peer, &trsbuf.storage[0], sizeof(trsbuf.storage));
+#ifdef HAVE_ALARM
+ alarm(0);
+#endif
+ if(got_exit_signal)
+ goto abort;
+ if(n >= 4 && /* if read some data */
+ rdp->th_opcode == opcode_DATA && /* and got a data block */
+ recvblock == rdp->th_block) { /* then my last ack was lost */
+ (void)swrite(peer, &ackbuf.storage[0], 4); /* resend final ack */
+ }
+abort:
+ /* make sure the output file is closed in case of abort */
+ if(test->ofile > 0) {
+ close(test->ofile);
+ test->ofile = 0;
+ }
+ return;
}
/*
return 0;
}
-/* Based on the testno, parse the correct server commands. */
-static int tftpd_parse_servercmd(struct testcase *req)
+static int test_tftpd(int argc, char **argv)
{
- FILE *stream;
+ srvr_sockaddr_union_t me;
+ struct tftphdr *tp;
+ ssize_t n = 0;
+ int arg = 1;
+ unsigned short port = 8999; /* UDP */
+ curl_socket_t sock = CURL_SOCKET_BAD;
+ int flag;
+ int rc;
int error;
+ char errbuf[STRERROR_LEN];
+ struct testcase test;
+ int result = 0;
+ srvr_sockaddr_union_t from;
+ curl_socklen_t fromlen;
- stream = test2fopen(req->testno, logdir);
- if(!stream) {
- char errbuf[STRERROR_LEN];
- error = errno;
- logmsg("fopen() failed with error (%d) %s",
- error, curlx_strerror(error, errbuf, sizeof(errbuf)));
- logmsg(" Could not open test file %ld", req->testno);
- return 1; /* done */
- }
- else {
- char *orgcmd = NULL;
- char *cmd = NULL;
- size_t cmdsize = 0;
- int num = 0;
+ memset(&test, 0, sizeof(test));
- /* get the custom server control "commands" */
- error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
- curlx_fclose(stream);
- if(error) {
- logmsg("getpart() failed with error (%d)", error);
- return 1; /* done */
- }
+ pidname = ".tftpd.pid";
+ serverlogfile = "log/tftpd.log";
+ serverlogslocked = 0;
- cmd = orgcmd;
- while(cmd && cmdsize) {
- char *check;
- if(sscanf(cmd, "writedelay: %d", &num) == 1) {
- logmsg("instructed to delay %d secs between packets", num);
- req->writedelay = num;
- }
- else {
- logmsg("Unknown <servercmd> instruction found: %s", cmd);
+ while(argc > arg) {
+ const char *opt;
+ curl_off_t num;
+ if(!strcmp("--version", argv[arg])) {
+ printf("tftpd IPv4%s\n",
+#ifdef USE_IPV6
+ "/IPv6"
+#else
+ ""
+#endif
+ );
+ return 0;
+ }
+ else if(!strcmp("--pidfile", argv[arg])) {
+ arg++;
+ if(argc > arg)
+ pidname = argv[arg++];
+ }
+ else if(!strcmp("--portfile", argv[arg])) {
+ arg++;
+ if(argc > arg)
+ portname = argv[arg++];
+ }
+ else if(!strcmp("--logfile", argv[arg])) {
+ arg++;
+ if(argc > arg)
+ serverlogfile = argv[arg++];
+ }
+ else if(!strcmp("--logdir", argv[arg])) {
+ arg++;
+ if(argc > arg)
+ logdir = argv[arg++];
+ }
+ else if(!strcmp("--ipv4", argv[arg])) {
+#ifdef USE_IPV6
+ ipv_inuse = "IPv4";
+ use_ipv6 = FALSE;
+#endif
+ arg++;
+ }
+ else if(!strcmp("--ipv6", argv[arg])) {
+#ifdef USE_IPV6
+ ipv_inuse = "IPv6";
+ use_ipv6 = TRUE;
+#endif
+ arg++;
+ }
+ else if(!strcmp("--port", argv[arg])) {
+ arg++;
+ if(argc > arg) {
+ opt = argv[arg];
+ if(!curlx_str_number(&opt, &num, 0xffff))
+ port = (unsigned short)num;
+ arg++;
}
- /* try to deal with CRLF or just LF */
- check = strchr(cmd, '\r');
- if(!check)
- check = strchr(cmd, '\n');
-
- if(check) {
- /* get to the letter following the newline */
- while((*check == '\r') || (*check == '\n'))
- check++;
-
- if(!*check)
- /* if we reached a zero, get out */
- break;
- cmd = check;
+ }
+ else if(!strcmp("--srcdir", argv[arg])) {
+ arg++;
+ if(argc > arg) {
+ srcpath = argv[arg];
+ arg++;
}
- else
- break;
}
- free(orgcmd);
- }
-
- return 0; /* OK! */
-}
-
-/*
- * Validate file access.
- */
-static int validate_access(struct testcase *test,
- const char *filename, unsigned short mode)
-{
- char *ptr;
-
- logmsg("trying to get file: %s mode %x", filename, mode);
-
- if(!strncmp("verifiedserver", filename, 14)) {
- char weare[128];
- size_t count = snprintf(weare, sizeof(weare), "WE ROOLZ: %ld\r\n",
- (long)our_getpid());
-
- logmsg("Are-we-friendly question received");
- test->buffer = strdup(weare);
- test->rptr = test->buffer; /* set read pointer */
- test->bufsize = count; /* set total count */
- test->rcount = count; /* set data left to read */
- return 0; /* fine */
- }
-
- /* find the last slash */
- ptr = strrchr(filename, '/');
-
- if(ptr) {
- char partbuf[80] = "data";
- long partno;
- long testno;
- const char *pval;
- curl_off_t num;
- FILE *stream;
-
- ptr++; /* skip the slash */
-
- /* skip all non-numericals following the slash */
- while(*ptr && !ISDIGIT(*ptr))
- ptr++;
-
- /* get the number */
- pval = ptr;
- if(!curlx_str_number(&pval, &num, INT_MAX))
- testno = (long)num;
else {
- logmsg("tftpd: failed to read the test number from '%s'", filename);
- return TFTP_EACCESS;
- }
-
- if(testno > 10000) {
- partno = testno % 10000;
- testno /= 10000;
+ puts("Usage: tftpd [option]\n"
+ " --version\n"
+ " --logfile [file]\n"
+ " --logdir [directory]\n"
+ " --pidfile [file]\n"
+ " --portfile [file]\n"
+ " --ipv4\n"
+ " --ipv6\n"
+ " --port [port]\n"
+ " --srcdir [path]");
+ return 0;
}
- else
- partno = 0;
+ }
- logmsg("requested test number %ld part %ld", testno, partno);
+ snprintf(loglockfile, sizeof(loglockfile), "%s/%s/tftp-%s.lock",
+ logdir, SERVERLOGS_LOCKDIR, ipv_inuse);
- test->testno = testno;
+ install_signal_handlers(true);
- (void)tftpd_parse_servercmd(test);
+#ifdef USE_IPV6
+ if(!use_ipv6)
+#endif
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+#ifdef USE_IPV6
+ else
+ sock = socket(AF_INET6, SOCK_DGRAM, 0);
+#endif
- stream = test2fopen(testno, logdir);
+ if(CURL_SOCKET_BAD == sock) {
+ error = SOCKERRNO;
+ logmsg("Error creating socket (%d) %s",
+ error, curlx_strerror(error, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto tftpd_cleanup;
+ }
- if(partno)
- snprintf(partbuf, sizeof(partbuf), "data%ld", partno);
+ flag = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) {
+ error = SOCKERRNO;
+ logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
+ error, curlx_strerror(error, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto tftpd_cleanup;
+ }
- if(!stream) {
- char errbuf[STRERROR_LEN];
- int error = errno;
- logmsg("fopen() failed with error (%d) %s",
- error, curlx_strerror(error, errbuf, sizeof(errbuf)));
- logmsg("Could not open test file for test: %ld", testno);
- return TFTP_EACCESS;
- }
- else {
- size_t count;
- int error = getpart(&test->buffer, &count, "reply", partbuf, stream);
- curlx_fclose(stream);
- if(error) {
- logmsg("getpart() failed with error (%d)", error);
- return TFTP_EACCESS;
- }
- if(test->buffer) {
- test->rptr = test->buffer; /* set read pointer */
- test->bufsize = count; /* set total count */
- test->rcount = count; /* set data left to read */
- }
- else
- return TFTP_EACCESS;
- }
+#ifdef USE_IPV6
+ if(!use_ipv6) {
+#endif
+ memset(&me.sa4, 0, sizeof(me.sa4));
+ me.sa4.sin_family = AF_INET;
+ me.sa4.sin_addr.s_addr = INADDR_ANY;
+ me.sa4.sin_port = htons(port);
+ rc = bind(sock, &me.sa, sizeof(me.sa4));
+#ifdef USE_IPV6
}
else {
- logmsg("no slash found in path");
- return TFTP_EACCESS; /* failure */
+ memset(&me.sa6, 0, sizeof(me.sa6));
+ me.sa6.sin6_family = AF_INET6;
+ me.sa6.sin6_addr = in6addr_any;
+ me.sa6.sin6_port = htons(port);
+ rc = bind(sock, &me.sa, sizeof(me.sa6));
+ }
+#endif /* USE_IPV6 */
+ if(rc) {
+ error = SOCKERRNO;
+ logmsg("Error binding socket on port %hu (%d) %s", port,
+ error, curlx_strerror(error, errbuf, sizeof(errbuf)));
+ result = 1;
+ goto tftpd_cleanup;
}
- logmsg("file opened and all is good");
- return 0;
-}
-
-/*
- * Send the requested file.
- */
-static void sendtftp(struct testcase *test, const struct formats *pf)
-{
- int size;
- ssize_t n;
- /* These are volatile to live through a siglongjmp */
- volatile unsigned short sendblock; /* block count */
- struct tftphdr * volatile sdp = r_init(); /* data buffer */
- struct tftphdr * const sap = &ackbuf.hdr; /* ack buffer */
-
- sendblock = 1;
-#if defined(HAVE_ALARM) && defined(SIGALRM)
- mysignal(SIGALRM, timer);
+ if(!port) {
+ /* The system was supposed to choose a port number, figure out which
+ port we actually got and update the listener port value with it. */
+ curl_socklen_t la_size;
+ srvr_sockaddr_union_t localaddr;
+#ifdef USE_IPV6
+ if(!use_ipv6)
#endif
- do {
- size = readit(test, (struct tftphdr * volatile *)&sdp, pf->f_convert);
- if(size < 0) {
- nak(errno + 100);
- return;
- }
- sdp->th_opcode = htons(opcode_DATA);
- sdp->th_block = htons(sendblock);
- timeout = 0;
-#ifdef HAVE_SIGSETJMP
- (void)sigsetjmp(timeoutbuf, 1);
+ la_size = sizeof(localaddr.sa4);
+#ifdef USE_IPV6
+ else
+ la_size = sizeof(localaddr.sa6);
#endif
- if(test->writedelay) {
- logmsg("Pausing %d seconds before %d bytes", test->writedelay, size);
- curlx_wait_ms(1000 * test->writedelay);
- }
-
-send_data:
- logmsg("write");
- if(swrite(peer, sdp, size + 4) != size + 4) {
- logmsg("write: fail");
- return;
+ memset(&localaddr.sa, 0, (size_t)la_size);
+ if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
+ error = SOCKERRNO;
+ logmsg("getsockname() failed with error (%d) %s",
+ error, curlx_strerror(error, errbuf, sizeof(errbuf)));
+ sclose(sock);
+ goto tftpd_cleanup;
}
- read_ahead(test, pf->f_convert);
- for(;;) {
-#ifdef HAVE_ALARM
- alarm(rexmtval); /* read the ack */
-#endif
- logmsg("read");
- n = sread(peer, &ackbuf.storage[0], sizeof(ackbuf.storage));
- logmsg("read: %zd", n);
-#ifdef HAVE_ALARM
- alarm(0);
+ switch(localaddr.sa.sa_family) {
+ case AF_INET:
+ port = ntohs(localaddr.sa4.sin_port);
+ break;
+#ifdef USE_IPV6
+ case AF_INET6:
+ port = ntohs(localaddr.sa6.sin6_port);
+ break;
#endif
- if(got_exit_signal)
- return;
- if(n < 0) {
- logmsg("read: fail");
- return;
- }
- sap->th_opcode = ntohs(sap->th_opcode);
- sap->th_block = ntohs(sap->th_block);
+ default:
+ break;
+ }
+ if(!port) {
+ /* Real failure, listener port shall not be zero beyond this point. */
+ logmsg("Apparently getsockname() succeeded, with listener port zero.");
+ logmsg("A valid reason for this failure is a binary built without");
+ logmsg("proper network library linkage. This might not be the only");
+ logmsg("reason, but double check it before anything else.");
+ result = 2;
+ goto tftpd_cleanup;
+ }
+ }
- if(sap->th_opcode == opcode_ERROR) {
- logmsg("got ERROR");
- return;
- }
+ tftpd_wrotepidfile = write_pidfile(pidname);
+ if(!tftpd_wrotepidfile) {
+ result = 1;
+ goto tftpd_cleanup;
+ }
- if(sap->th_opcode == opcode_ACK) {
- if(sap->th_block == sendblock) {
- break;
- }
- /* Re-synchronize with the other side */
- (void)synchnet(peer);
- if(sap->th_block == (sendblock - 1)) {
- goto send_data;
- }
- }
+ if(portname) {
+ tftpd_wroteportfile = write_portfile(portname, port);
+ if(!tftpd_wroteportfile) {
+ result = 1;
+ goto tftpd_cleanup;
}
- sendblock++;
- } while(size == SEGSIZE);
-}
+ }
-/*
- * Receive a file.
- */
-static void recvtftp(struct testcase *test, const struct formats *pf)
-{
- ssize_t n, size;
- /* These are volatile to live through a siglongjmp */
- volatile unsigned short recvblock; /* block count */
- struct tftphdr * volatile rdp; /* data buffer */
- struct tftphdr *rap; /* ack buffer */
+ logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
- recvblock = 0;
- rdp = w_init();
-#if defined(HAVE_ALARM) && defined(SIGALRM)
- mysignal(SIGALRM, timer);
+ for(;;) {
+ fromlen = sizeof(from);
+#ifdef USE_IPV6
+ if(!use_ipv6)
#endif
- rap = &ackbuf.hdr;
- do {
- timeout = 0;
- rap->th_opcode = htons(opcode_ACK);
- rap->th_block = htons(recvblock);
- recvblock++;
-#ifdef HAVE_SIGSETJMP
- (void)sigsetjmp(timeoutbuf, 1);
+ fromlen = sizeof(from.sa4);
+#ifdef USE_IPV6
+ else
+ fromlen = sizeof(from.sa6);
#endif
-send_ack:
- logmsg("write");
- if(swrite(peer, &ackbuf.storage[0], 4) != 4) {
- logmsg("write: fail");
- goto abort;
+ n = (ssize_t)recvfrom(sock, &trsbuf.storage[0], sizeof(trsbuf.storage), 0,
+ &from.sa, &fromlen);
+ if(got_exit_signal)
+ break;
+ if(n < 0) {
+ logmsg("recvfrom");
+ result = 3;
+ break;
}
- write_behind(test, pf->f_convert);
- for(;;) {
-#ifdef HAVE_ALARM
- alarm(rexmtval);
-#endif
- logmsg("read");
- n = sread(peer, rdp, PKTSIZE);
- logmsg("read: %zd", n);
-#ifdef HAVE_ALARM
- alarm(0);
+
+ set_advisor_read_lock(loglockfile);
+ serverlogslocked = 1;
+
+#ifdef USE_IPV6
+ if(!use_ipv6) {
#endif
- if(got_exit_signal)
- goto abort;
- if(n < 0) { /* really? */
- logmsg("read: fail");
- goto abort;
+ from.sa4.sin_family = AF_INET;
+ peer = socket(AF_INET, SOCK_DGRAM, 0);
+ if(CURL_SOCKET_BAD == peer) {
+ logmsg("socket");
+ result = 2;
+ break;
}
- rdp->th_opcode = ntohs(rdp->th_opcode);
- rdp->th_block = ntohs(rdp->th_block);
- if(rdp->th_opcode == opcode_ERROR)
- goto abort;
- if(rdp->th_opcode == opcode_DATA) {
- if(rdp->th_block == recvblock) {
- break; /* normal */
- }
- /* Re-synchronize with the other side */
- (void)synchnet(peer);
- if(rdp->th_block == (recvblock - 1))
- goto send_ack; /* rexmit */
+ if(connect(peer, &from.sa, sizeof(from.sa4)) < 0) {
+ logmsg("connect: fail");
+ result = 1;
+ break;
+ }
+#ifdef USE_IPV6
+ }
+ else {
+ from.sa6.sin6_family = AF_INET6;
+ peer = socket(AF_INET6, SOCK_DGRAM, 0);
+ if(CURL_SOCKET_BAD == peer) {
+ logmsg("socket");
+ result = 2;
+ break;
+ }
+ if(connect(peer, &from.sa, sizeof(from.sa6)) < 0) {
+ logmsg("connect: fail");
+ result = 1;
+ break;
}
}
+#endif
- size = writeit(test, &rdp, (int)(n - 4), pf->f_convert);
- if(size != (n - 4)) { /* ahem */
- if(size < 0)
- nak(errno + 100);
- else
- nak(TFTP_ENOSPACE);
- goto abort;
+ maxtimeout = 5 * TIMEOUT;
+
+ tp = &trsbuf.hdr;
+ tp->th_opcode = ntohs(tp->th_opcode);
+ if(tp->th_opcode == opcode_RRQ || tp->th_opcode == opcode_WRQ) {
+ memset(&test, 0, sizeof(test));
+ if(do_tftp(&test, tp, n) < 0)
+ break;
+ free(test.buffer);
}
- } while(size == SEGSIZE);
- write_behind(test, pf->f_convert);
- /* close the output file as early as possible after upload completion */
- if(test->ofile > 0) {
- close(test->ofile);
- test->ofile = 0;
+ sclose(peer);
+ peer = CURL_SOCKET_BAD;
+
+ if(got_exit_signal)
+ break;
+
+ if(serverlogslocked) {
+ serverlogslocked = 0;
+ clear_advisor_read_lock(loglockfile);
+ }
+
+ logmsg("end of one transfer");
}
- rap->th_opcode = htons(opcode_ACK); /* send the "final" ack */
- rap->th_block = htons(recvblock);
- (void)swrite(peer, &ackbuf.storage[0], 4);
-#if defined(HAVE_ALARM) && defined(SIGALRM)
- mysignal(SIGALRM, justtimeout); /* just abort read on timeout */
- alarm(rexmtval);
-#endif
- /* normally times out and quits */
- n = sread(peer, &trsbuf.storage[0], sizeof(trsbuf.storage));
-#ifdef HAVE_ALARM
- alarm(0);
-#endif
+tftpd_cleanup:
+
+ if(test.ofile > 0)
+ close(test.ofile);
+
+ if((peer != sock) && (peer != CURL_SOCKET_BAD))
+ sclose(peer);
+
+ if(sock != CURL_SOCKET_BAD)
+ sclose(sock);
+
if(got_exit_signal)
- goto abort;
- if(n >= 4 && /* if read some data */
- rdp->th_opcode == opcode_DATA && /* and got a data block */
- recvblock == rdp->th_block) { /* then my last ack was lost */
- (void)swrite(peer, &ackbuf.storage[0], 4); /* resend final ack */
- }
-abort:
- /* make sure the output file is closed in case of abort */
- if(test->ofile > 0) {
- close(test->ofile);
- test->ofile = 0;
+ logmsg("signalled to die");
+
+ if(tftpd_wrotepidfile)
+ unlink(pidname);
+ if(tftpd_wroteportfile)
+ unlink(portname);
+
+ if(serverlogslocked) {
+ serverlogslocked = 0;
+ clear_advisor_read_lock(loglockfile);
}
- return;
-}
-/*
- * Send a nak packet (error message). Error code passed in is one of the
- * standard TFTP codes, or a Unix errno offset by 100.
- */
-static void nak(int error)
-{
- struct tftphdr *tp;
- int length;
- struct errmsg *pe;
+ restore_signal_handlers(true);
- tp = &trsbuf.hdr;
- tp->th_opcode = htons(opcode_ERROR);
- tp->th_code = htons((unsigned short)error);
- for(pe = errmsgs; pe->e_code >= 0; pe++)
- if(pe->e_code == error)
- break;
- if(pe->e_code < 0) {
- curlx_strerror(error - 100, pe->e_msg, sizeof(pe->e_msg));
- tp->th_code = TFTP_EUNDEF; /* set 'undef' errorcode */
+ if(got_exit_signal) {
+ logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)",
+ ipv_inuse, (int)port, (long)our_getpid(), exit_signal);
+ /*
+ * To properly set the return status of the process we
+ * must raise the same signal SIGINT or SIGTERM that we
+ * caught and let the old handler take care of it.
+ */
+ raise(exit_signal);
}
- length = (int)strlen(pe->e_msg);
- /* we use memcpy() instead of strcpy() in order to avoid buffer overflow
- * report from glibc with FORTIFY_SOURCE */
- memcpy(tp->th_msg, pe->e_msg, length + 1);
- length += 5;
- if(swrite(peer, &trsbuf.storage[0], length) != length)
- logmsg("nak: fail\n");
+ logmsg("========> tftpd quits");
+ return result;
}