Loading CHANGES +4 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,10 @@ Changelog Daniel Stenberg (26 Jan 2009) - Chad Monroe provided the new CURLOPT_TFTP_BLKSIZE option that allows an app to set desired block size to use for TFTP transfers instead of the default 512 bytes. - The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to disable "rfc4507bis session ticket support". rfc4507bis was later turned into the proper RFC5077 it seems: http://tools.ietf.org/html/rfc5077 Loading RELEASE-NOTES +2 −1 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ This release includes the following changes: o Added CURLOPT_NOPROXY and the corresponding --noproxy o the OpenSSL-specific code disables TICKET (rfc5077) which is enabled by default in openssl 0.9.8j o Added CURLOPT_TFTP_BLKSIZE This release includes the following bugfixes: Loading @@ -27,6 +28,6 @@ This release would not have looked like this without help, code, reports and advice from friends like these: Lisa Xu, Daniel Fandrich, Craig A West, Alexey Borzov, Sharad Gupta, Peter Sylvester Peter Sylvester, Chad Monroe Thanks! (and sorry if I forgot to mention someone) docs/libcurl/curl_easy_setopt.3 +8 −0 Original line number Diff line number Diff line Loading @@ -1008,6 +1008,14 @@ Pass a long to tell libcurl how to act on transfer decoding. If set to zero, transfer decoding will be disabled, if set to 1 it is enabled (default). libcurl does chunked transfer decoding by default unless this option is set to zero. (added in 7.16.2) .SH TFTP OPTIONS .IP CURLOPT_TFTPBLKSIZE Specify block size to use for TFTP data transmission. Valid range as per RFC 2348 is 8-65464 bytes. The default of 512 bytes will be used if this option is not specified. The specified block size will only be used pending support by the remote server. If the server does not return an option acknowledgement or returns an option acknowledgement with no blksize, the default of 512 bytes will be used. (added in 7.19.4) .SH FTP OPTIONS .IP CURLOPT_FTPPORT Pass a pointer to a zero terminated string as parameter. It will be used to Loading include/curl/curl.h +3 −0 Original line number Diff line number Diff line Loading @@ -1159,6 +1159,9 @@ typedef enum { disables the use of proxy. */ CINIT(NOPROXY, OBJECTPOINT, 177), /* block size for TFTP transfers */ CINIT(TFTP_BLKSIZE, LONG, 178), CURLOPT_LASTENTRY /* the last unused */ } CURLoption; Loading lib/tftp.c +276 −45 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms Loading @@ -26,7 +26,6 @@ #ifndef CURL_DISABLE_TFTP /* -- WIN32 approved -- */ #include <stdio.h> #include <string.h> #include <stdarg.h> #include <stdlib.h> #include <ctype.h> Loading Loading @@ -83,8 +82,13 @@ /* The last #include file should be: */ #include "memdebug.h" /* RFC2348 allows the block size to be negotiated, but we don't support that */ #define TFTP_BLOCKSIZE 512 /* RFC2348 allows the block size to be negotiated */ #define TFTP_BLKSIZE_DEFAULT 512 #define TFTP_BLKSIZE_MIN 8 #define TFTP_BLKSIZE_MAX 65464 #define TFTP_OPTION_BLKSIZE "blksize" #define TFTP_OPTION_TSIZE "tsize" #define TFTP_OPTION_INTERVAL "interval" typedef enum { TFTP_MODE_NETASCII=0, Loading @@ -105,6 +109,7 @@ typedef enum { TFTP_EVENT_DATA = 3, TFTP_EVENT_ACK = 4, TFTP_EVENT_ERROR = 5, TFTP_EVENT_OACK = 6, TFTP_EVENT_TIMEOUT } tftp_event_t; Loading @@ -125,7 +130,7 @@ typedef enum { } tftp_error_t; typedef struct tftp_packet { unsigned char data[2 + 2 + TFTP_BLOCKSIZE]; unsigned char *data; } tftp_packet_t; typedef struct tftp_state_data { Loading @@ -144,7 +149,9 @@ typedef struct tftp_state_data { struct Curl_sockaddr_storage remote_addr; socklen_t remote_addrlen; ssize_t rbytes; int sbytes; size_t sbytes; size_t blksize; int requested_blksize; tftp_packet_t rpacket; tftp_packet_t spacket; } tftp_state_data_t; Loading @@ -154,6 +161,7 @@ typedef struct tftp_state_data { static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ; static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ; static CURLcode tftp_connect(struct connectdata *conn, bool *done); static CURLcode tftp_disconnect(struct connectdata *conn); static CURLcode tftp_do(struct connectdata *conn, bool *done); static CURLcode tftp_done(struct connectdata *conn, CURLcode, bool premature); Loading @@ -176,7 +184,7 @@ const struct Curl_handler Curl_handler_tftp = { ZERO_NULL, /* proto_getsock */ ZERO_NULL, /* doing_getsock */ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ tftp_disconnect, /* disconnect */ PORT_TFTP, /* defport */ PROT_TFTP /* protocol */ }; Loading Loading @@ -295,11 +303,143 @@ static unsigned short getrpacketblock(const tftp_packet_t *packet) return (unsigned short)((packet->data[2] << 8) | packet->data[3]); } static size_t Curl_strnlen(const char *string, size_t maxlen) { const char *end = memchr (string, '\0', maxlen); return end ? (size_t) (end - string) : maxlen; } static const char *tftp_option_get(const char *buf, size_t len, const char **option, const char **value) { size_t loc; loc = Curl_strnlen( buf, len ); loc++; /* NULL term */ if (loc >= len) return NULL; *option = buf; loc += Curl_strnlen( buf+loc, len-loc ); loc++; /* NULL term */ if (loc > len) return NULL; *value = &buf[strlen(*option) + 1]; return &buf[loc]; } static CURLcode tftp_parse_option_ack(tftp_state_data_t *state, const char *ptr, int len) { const char *tmp = ptr; struct SessionHandle *data = state->conn->data; /* if OACK doesn't contain blksize option, the default (512) must be used */ state->blksize = TFTP_BLKSIZE_DEFAULT; while (tmp < ptr + len) { const char *option, *value; tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value); if(tmp == NULL) { failf(data, "Malformed ACK packet, rejecting"); return CURLE_TFTP_ILLEGAL; } infof(data, "got option=(%s) value=(%s)\n", option, value); if(checkprefix(option, TFTP_OPTION_BLKSIZE)) { int blksize; blksize = strtol( value, NULL, 10 ); if(!blksize) { failf(data, "invalid blocksize value in OACK packet"); return CURLE_TFTP_ILLEGAL; } else if(blksize > TFTP_BLKSIZE_MAX) { failf(data, "%s (%d)", "blksize is larger than max supported", TFTP_BLKSIZE_MAX); return CURLE_TFTP_ILLEGAL; } else if(blksize < TFTP_BLKSIZE_MIN) { failf(data, "%s (%d)", "blksize is smaller than min supported", TFTP_BLKSIZE_MIN); return CURLE_TFTP_ILLEGAL; } else if (blksize > state->requested_blksize) { /* could realloc pkt buffers here, but the spec doesn't call out * support for the server requesting a bigger blksize than the client * requests */ failf(data, "%s (%d)", "server requested blksize larger than allocated", blksize); return CURLE_TFTP_ILLEGAL; } state->blksize = blksize; infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK", state->blksize, "requested", state->requested_blksize); } else if(checkprefix(option, TFTP_OPTION_TSIZE)) { long tsize = 0; tsize = strtol( value, NULL, 10 ); if(!tsize) { failf(data, "invalid tsize value in OACK packet"); return CURLE_TFTP_ILLEGAL; } Curl_pgrsSetDownloadSize(data, tsize); infof(data, "%s (%d)\n", "tsize parsed from OACK", tsize); } } return CURLE_OK; } static size_t tftp_option_add(tftp_state_data_t *state, size_t csize, char *buf, const char *option) { if( ( strlen(option) + csize + 1U ) > state->blksize ) return 0; strcpy(buf, option); return( strlen(option) + 1 ); } static int tftp_connect_for_tx(tftp_state_data_t *state, tftp_event_t event) { int res = 0; struct SessionHandle *data = state->conn->data; infof(data, "%s\n", "Connected for transmit"); state->state = TFTP_STATE_TX; res = tftp_set_timeouts(state); if(res) return(res); return tftp_tx(state, event); } static int tftp_connect_for_rx(tftp_state_data_t *state, tftp_event_t event) { int res = 0; struct SessionHandle *data = state->conn->data; infof(data, "%s\n", "Connected for receive"); state->state = TFTP_STATE_RX; res = tftp_set_timeouts(state); if(res) return(res); return tftp_rx(state, event); } static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) { int sbytes; const char *mode = "octet"; char *filename; char buf[8]; struct SessionHandle *data = state->conn->data; CURLcode res = CURLE_OK; Loading @@ -323,7 +463,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) /* If we are uploading, send an WRQ */ setpacketevent(&state->spacket, TFTP_EVENT_WRQ); state->conn->data->req.upload_fromhere = (char *)&state->spacket.data[4]; (char *)state->spacket.data+4; if(data->set.infilesize != -1) Curl_pgrsSetUploadSize(data, data->set.infilesize); } Loading @@ -332,17 +472,40 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) setpacketevent(&state->spacket, TFTP_EVENT_RRQ); } /* As RFC3617 describes the separator slash is not actually part of the file name so we skip the always-present first letter of the path string. */ file name so we skip the always-present first letter of the path string. */ filename = curl_easy_unescape(data, &state->conn->data->state.path[1], 0, NULL); if(!filename) return CURLE_OUT_OF_MEMORY; snprintf((char *)&state->spacket.data[2], TFTP_BLOCKSIZE, snprintf((char *)state->spacket.data+2, state->blksize, "%s%c%s%c", filename, '\0', mode, '\0'); sbytes = 4 + (int)strlen(filename) + (int)strlen(mode); sbytes = sendto(state->sockfd, (void *)&state->spacket, sbytes = 4 + strlen(filename) + strlen(mode); /* add tsize option */ sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, TFTP_OPTION_TSIZE); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, "0"); /* add blksize option */ snprintf( buf, sizeof(buf), "%d", state->requested_blksize ); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, TFTP_OPTION_BLKSIZE); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, buf ); /* add timeout option */ snprintf( buf, sizeof(buf), "%d", state->retry_time ); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, TFTP_OPTION_INTERVAL); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, buf ); sbytes = sendto(state->sockfd, (void *)state->spacket.data, sbytes, 0, state->conn->ip_addr->ai_addr, state->conn->ip_addr->ai_addrlen); Loading @@ -352,21 +515,22 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) Curl_safefree(filename); break; case TFTP_EVENT_OACK: if(data->set.upload) { res = tftp_connect_for_tx(state, event); } else { res = tftp_connect_for_rx(state, event); } break; case TFTP_EVENT_ACK: /* Connected for transmit */ infof(data, "%s\n", "Connected for transmit"); state->state = TFTP_STATE_TX; res = tftp_set_timeouts(state); if(res) res = tftp_connect_for_tx(state, event); break; return tftp_tx(state, event); case TFTP_EVENT_DATA: /* connected for receive */ infof(data, "%s\n", "Connected for receive"); state->state = TFTP_STATE_RX; res = tftp_set_timeouts(state); if(res) case TFTP_EVENT_DATA: /* Connected for receive */ res = tftp_connect_for_rx(state, event); break; return tftp_rx(state, event); case TFTP_EVENT_ERROR: state->state = TFTP_STATE_FIN; Loading Loading @@ -395,7 +559,6 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) switch(event) { case TFTP_EVENT_DATA: /* Is this the block we expect? */ rblock = getrpacketblock(&state->rpacket); if((state->block+1) != rblock) { Loading Loading @@ -424,7 +587,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) } /* Check if completed (That is, a less than full packet is received) */ if(state->rbytes < (ssize_t)sizeof(state->spacket)){ if(state->rbytes < (ssize_t)state->blksize+4){ state->state = TFTP_STATE_FIN; } else { Loading @@ -432,6 +595,25 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) } 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", Curl_strerror(state->conn, SOCKERRNO)); return CURLE_SEND_ERROR; } /* we're ready to RX data */ state->state = TFTP_STATE_RX; break; case TFTP_EVENT_TIMEOUT: /* Increment the retry count and fail if over the limit */ state->retries++; Loading @@ -443,7 +625,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) } else { /* Resend the previous ACK */ sbytes = sendto(state->sockfd, (void *)&state->spacket, sbytes = sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); Loading Loading @@ -519,11 +701,12 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) state->retries = 0; setpacketevent(&state->spacket, TFTP_EVENT_DATA); setpacketblock(&state->spacket, state->block); if(state->block > 1 && state->sbytes < TFTP_BLOCKSIZE) { if(state->block > 1 && state->sbytes < state->blksize) { state->state = TFTP_STATE_FIN; return CURLE_OK; } res = Curl_fillreadbuffer(state->conn, TFTP_BLOCKSIZE, &state->sbytes); res = Curl_fillreadbuffer(state->conn, state->blksize, (int *)&state->sbytes); if(res) return res; sbytes = sendto(state->sockfd, (void *)state->spacket.data, Loading Loading @@ -552,7 +735,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) } else { /* Re-send the data packet */ sbytes = sendto(state->sockfd, (void *)&state->spacket, sbytes = sendto(state->sockfd, (void *)state->spacket.data, 4+state->sbytes, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); Loading Loading @@ -615,6 +798,26 @@ static CURLcode tftp_state_machine(tftp_state_data_t *state, return res; } /********************************************************** * * tftp_disconnect * * The disconnect callback * **********************************************************/ static CURLcode tftp_disconnect(struct connectdata *conn) { tftp_state_data_t *state = conn->proto.tftpc; /* done, free dynamically allocated pkt buffers */ if(state) { Curl_safefree(state->rpacket.data); Curl_safefree(state->spacket.data); free(state); } return CURLE_OK; } /********************************************************** * Loading @@ -627,18 +830,37 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) { CURLcode code; tftp_state_data_t *state; int rc; int blksize, rc; blksize = TFTP_BLKSIZE_DEFAULT; /* If there already is a protocol-specific struct allocated for this sessionhandle, deal with it */ Curl_reset_reqproto(conn); state = conn->data->state.proto.tftp; if(!state) { state = conn->data->state.proto.tftp = calloc(sizeof(tftp_state_data_t), 1); state = conn->proto.tftpc = calloc(sizeof(tftp_state_data_t), 1); if(!state) return CURLE_OUT_OF_MEMORY; /* alloc pkt buffers based on specified blksize */ if(conn->data->set.tftp_blksize) { blksize = conn->data->set.tftp_blksize; if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN ) return CURLE_TFTP_ILLEGAL; } if(!state->rpacket.data) { state->rpacket.data = calloc(1, blksize + 2 + 2); if(!state->rpacket.data) return CURLE_OUT_OF_MEMORY; } if(!state->spacket.data) { state->spacket.data = calloc(1, blksize + 2 + 2); if(!state->spacket.data) return CURLE_OUT_OF_MEMORY; } conn->bits.close = FALSE; /* keep it open if possible */ Loading @@ -647,6 +869,8 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) state->sockfd = state->conn->sock[FIRSTSOCKET]; state->state = TFTP_STATE_START; state->error = TFTP_ERR_NONE; state->blksize = TFTP_BLKSIZE_DEFAULT; state->requested_blksize = blksize; ((struct sockaddr *)&state->local_addr)->sa_family = (unsigned short)(conn->ip_addr->ai_family); Loading Loading @@ -735,12 +959,12 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) */ Curl_reset_reqproto(conn); if(!data->state.proto.tftp) { if(!conn->proto.tftpc) { code = tftp_connect(conn, done); if(code) return code; } state = (tftp_state_data_t *)data->state.proto.tftp; state = (tftp_state_data_t *)conn->proto.tftpc; /* Run the TFTP State Machine */ for(code=tftp_state_machine(state, TFTP_EVENT_INIT); Loading Loading @@ -770,8 +994,8 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) /* Receive the packet */ fromlen = sizeof(fromaddr); state->rbytes = (ssize_t)recvfrom(state->sockfd, (void *)&state->rpacket, sizeof(state->rpacket), (void *)state->rpacket.data, state->blksize+4, 0, (struct sockaddr *)&fromaddr, &fromlen); Loading @@ -797,7 +1021,7 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) if(state->rbytes > 4 && ((state->block+1) == getrpacketblock(&state->rpacket))) { code = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)&state->rpacket.data[4], (char *)state->rpacket.data+4, state->rbytes-4); if(code) return code; Loading @@ -807,10 +1031,17 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) break; case TFTP_EVENT_ERROR: state->error = (tftp_error_t)getrpacketblock(&state->rpacket); infof(data, "%s\n", (const char *)&state->rpacket.data[4]); infof(data, "%s\n", (const char *)state->rpacket.data+4); break; case TFTP_EVENT_ACK: break; case TFTP_EVENT_OACK: code = tftp_parse_option_ack(state, (const char *)state->rpacket.data+2, state->rbytes-2); if(code) return code; break; case TFTP_EVENT_RRQ: case TFTP_EVENT_WRQ: default: Loading Loading
CHANGES +4 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,10 @@ Changelog Daniel Stenberg (26 Jan 2009) - Chad Monroe provided the new CURLOPT_TFTP_BLKSIZE option that allows an app to set desired block size to use for TFTP transfers instead of the default 512 bytes. - The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to disable "rfc4507bis session ticket support". rfc4507bis was later turned into the proper RFC5077 it seems: http://tools.ietf.org/html/rfc5077 Loading
RELEASE-NOTES +2 −1 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ This release includes the following changes: o Added CURLOPT_NOPROXY and the corresponding --noproxy o the OpenSSL-specific code disables TICKET (rfc5077) which is enabled by default in openssl 0.9.8j o Added CURLOPT_TFTP_BLKSIZE This release includes the following bugfixes: Loading @@ -27,6 +28,6 @@ This release would not have looked like this without help, code, reports and advice from friends like these: Lisa Xu, Daniel Fandrich, Craig A West, Alexey Borzov, Sharad Gupta, Peter Sylvester Peter Sylvester, Chad Monroe Thanks! (and sorry if I forgot to mention someone)
docs/libcurl/curl_easy_setopt.3 +8 −0 Original line number Diff line number Diff line Loading @@ -1008,6 +1008,14 @@ Pass a long to tell libcurl how to act on transfer decoding. If set to zero, transfer decoding will be disabled, if set to 1 it is enabled (default). libcurl does chunked transfer decoding by default unless this option is set to zero. (added in 7.16.2) .SH TFTP OPTIONS .IP CURLOPT_TFTPBLKSIZE Specify block size to use for TFTP data transmission. Valid range as per RFC 2348 is 8-65464 bytes. The default of 512 bytes will be used if this option is not specified. The specified block size will only be used pending support by the remote server. If the server does not return an option acknowledgement or returns an option acknowledgement with no blksize, the default of 512 bytes will be used. (added in 7.19.4) .SH FTP OPTIONS .IP CURLOPT_FTPPORT Pass a pointer to a zero terminated string as parameter. It will be used to Loading
include/curl/curl.h +3 −0 Original line number Diff line number Diff line Loading @@ -1159,6 +1159,9 @@ typedef enum { disables the use of proxy. */ CINIT(NOPROXY, OBJECTPOINT, 177), /* block size for TFTP transfers */ CINIT(TFTP_BLKSIZE, LONG, 178), CURLOPT_LASTENTRY /* the last unused */ } CURLoption; Loading
lib/tftp.c +276 −45 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms Loading @@ -26,7 +26,6 @@ #ifndef CURL_DISABLE_TFTP /* -- WIN32 approved -- */ #include <stdio.h> #include <string.h> #include <stdarg.h> #include <stdlib.h> #include <ctype.h> Loading Loading @@ -83,8 +82,13 @@ /* The last #include file should be: */ #include "memdebug.h" /* RFC2348 allows the block size to be negotiated, but we don't support that */ #define TFTP_BLOCKSIZE 512 /* RFC2348 allows the block size to be negotiated */ #define TFTP_BLKSIZE_DEFAULT 512 #define TFTP_BLKSIZE_MIN 8 #define TFTP_BLKSIZE_MAX 65464 #define TFTP_OPTION_BLKSIZE "blksize" #define TFTP_OPTION_TSIZE "tsize" #define TFTP_OPTION_INTERVAL "interval" typedef enum { TFTP_MODE_NETASCII=0, Loading @@ -105,6 +109,7 @@ typedef enum { TFTP_EVENT_DATA = 3, TFTP_EVENT_ACK = 4, TFTP_EVENT_ERROR = 5, TFTP_EVENT_OACK = 6, TFTP_EVENT_TIMEOUT } tftp_event_t; Loading @@ -125,7 +130,7 @@ typedef enum { } tftp_error_t; typedef struct tftp_packet { unsigned char data[2 + 2 + TFTP_BLOCKSIZE]; unsigned char *data; } tftp_packet_t; typedef struct tftp_state_data { Loading @@ -144,7 +149,9 @@ typedef struct tftp_state_data { struct Curl_sockaddr_storage remote_addr; socklen_t remote_addrlen; ssize_t rbytes; int sbytes; size_t sbytes; size_t blksize; int requested_blksize; tftp_packet_t rpacket; tftp_packet_t spacket; } tftp_state_data_t; Loading @@ -154,6 +161,7 @@ typedef struct tftp_state_data { static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) ; static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) ; static CURLcode tftp_connect(struct connectdata *conn, bool *done); static CURLcode tftp_disconnect(struct connectdata *conn); static CURLcode tftp_do(struct connectdata *conn, bool *done); static CURLcode tftp_done(struct connectdata *conn, CURLcode, bool premature); Loading @@ -176,7 +184,7 @@ const struct Curl_handler Curl_handler_tftp = { ZERO_NULL, /* proto_getsock */ ZERO_NULL, /* doing_getsock */ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ tftp_disconnect, /* disconnect */ PORT_TFTP, /* defport */ PROT_TFTP /* protocol */ }; Loading Loading @@ -295,11 +303,143 @@ static unsigned short getrpacketblock(const tftp_packet_t *packet) return (unsigned short)((packet->data[2] << 8) | packet->data[3]); } static size_t Curl_strnlen(const char *string, size_t maxlen) { const char *end = memchr (string, '\0', maxlen); return end ? (size_t) (end - string) : maxlen; } static const char *tftp_option_get(const char *buf, size_t len, const char **option, const char **value) { size_t loc; loc = Curl_strnlen( buf, len ); loc++; /* NULL term */ if (loc >= len) return NULL; *option = buf; loc += Curl_strnlen( buf+loc, len-loc ); loc++; /* NULL term */ if (loc > len) return NULL; *value = &buf[strlen(*option) + 1]; return &buf[loc]; } static CURLcode tftp_parse_option_ack(tftp_state_data_t *state, const char *ptr, int len) { const char *tmp = ptr; struct SessionHandle *data = state->conn->data; /* if OACK doesn't contain blksize option, the default (512) must be used */ state->blksize = TFTP_BLKSIZE_DEFAULT; while (tmp < ptr + len) { const char *option, *value; tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value); if(tmp == NULL) { failf(data, "Malformed ACK packet, rejecting"); return CURLE_TFTP_ILLEGAL; } infof(data, "got option=(%s) value=(%s)\n", option, value); if(checkprefix(option, TFTP_OPTION_BLKSIZE)) { int blksize; blksize = strtol( value, NULL, 10 ); if(!blksize) { failf(data, "invalid blocksize value in OACK packet"); return CURLE_TFTP_ILLEGAL; } else if(blksize > TFTP_BLKSIZE_MAX) { failf(data, "%s (%d)", "blksize is larger than max supported", TFTP_BLKSIZE_MAX); return CURLE_TFTP_ILLEGAL; } else if(blksize < TFTP_BLKSIZE_MIN) { failf(data, "%s (%d)", "blksize is smaller than min supported", TFTP_BLKSIZE_MIN); return CURLE_TFTP_ILLEGAL; } else if (blksize > state->requested_blksize) { /* could realloc pkt buffers here, but the spec doesn't call out * support for the server requesting a bigger blksize than the client * requests */ failf(data, "%s (%d)", "server requested blksize larger than allocated", blksize); return CURLE_TFTP_ILLEGAL; } state->blksize = blksize; infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK", state->blksize, "requested", state->requested_blksize); } else if(checkprefix(option, TFTP_OPTION_TSIZE)) { long tsize = 0; tsize = strtol( value, NULL, 10 ); if(!tsize) { failf(data, "invalid tsize value in OACK packet"); return CURLE_TFTP_ILLEGAL; } Curl_pgrsSetDownloadSize(data, tsize); infof(data, "%s (%d)\n", "tsize parsed from OACK", tsize); } } return CURLE_OK; } static size_t tftp_option_add(tftp_state_data_t *state, size_t csize, char *buf, const char *option) { if( ( strlen(option) + csize + 1U ) > state->blksize ) return 0; strcpy(buf, option); return( strlen(option) + 1 ); } static int tftp_connect_for_tx(tftp_state_data_t *state, tftp_event_t event) { int res = 0; struct SessionHandle *data = state->conn->data; infof(data, "%s\n", "Connected for transmit"); state->state = TFTP_STATE_TX; res = tftp_set_timeouts(state); if(res) return(res); return tftp_tx(state, event); } static int tftp_connect_for_rx(tftp_state_data_t *state, tftp_event_t event) { int res = 0; struct SessionHandle *data = state->conn->data; infof(data, "%s\n", "Connected for receive"); state->state = TFTP_STATE_RX; res = tftp_set_timeouts(state); if(res) return(res); return tftp_rx(state, event); } static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) { int sbytes; const char *mode = "octet"; char *filename; char buf[8]; struct SessionHandle *data = state->conn->data; CURLcode res = CURLE_OK; Loading @@ -323,7 +463,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) /* If we are uploading, send an WRQ */ setpacketevent(&state->spacket, TFTP_EVENT_WRQ); state->conn->data->req.upload_fromhere = (char *)&state->spacket.data[4]; (char *)state->spacket.data+4; if(data->set.infilesize != -1) Curl_pgrsSetUploadSize(data, data->set.infilesize); } Loading @@ -332,17 +472,40 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) setpacketevent(&state->spacket, TFTP_EVENT_RRQ); } /* As RFC3617 describes the separator slash is not actually part of the file name so we skip the always-present first letter of the path string. */ file name so we skip the always-present first letter of the path string. */ filename = curl_easy_unescape(data, &state->conn->data->state.path[1], 0, NULL); if(!filename) return CURLE_OUT_OF_MEMORY; snprintf((char *)&state->spacket.data[2], TFTP_BLOCKSIZE, snprintf((char *)state->spacket.data+2, state->blksize, "%s%c%s%c", filename, '\0', mode, '\0'); sbytes = 4 + (int)strlen(filename) + (int)strlen(mode); sbytes = sendto(state->sockfd, (void *)&state->spacket, sbytes = 4 + strlen(filename) + strlen(mode); /* add tsize option */ sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, TFTP_OPTION_TSIZE); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, "0"); /* add blksize option */ snprintf( buf, sizeof(buf), "%d", state->requested_blksize ); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, TFTP_OPTION_BLKSIZE); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, buf ); /* add timeout option */ snprintf( buf, sizeof(buf), "%d", state->retry_time ); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, TFTP_OPTION_INTERVAL); sbytes += tftp_option_add(state, sbytes, (char *)state->spacket.data+sbytes, buf ); sbytes = sendto(state->sockfd, (void *)state->spacket.data, sbytes, 0, state->conn->ip_addr->ai_addr, state->conn->ip_addr->ai_addrlen); Loading @@ -352,21 +515,22 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event) Curl_safefree(filename); break; case TFTP_EVENT_OACK: if(data->set.upload) { res = tftp_connect_for_tx(state, event); } else { res = tftp_connect_for_rx(state, event); } break; case TFTP_EVENT_ACK: /* Connected for transmit */ infof(data, "%s\n", "Connected for transmit"); state->state = TFTP_STATE_TX; res = tftp_set_timeouts(state); if(res) res = tftp_connect_for_tx(state, event); break; return tftp_tx(state, event); case TFTP_EVENT_DATA: /* connected for receive */ infof(data, "%s\n", "Connected for receive"); state->state = TFTP_STATE_RX; res = tftp_set_timeouts(state); if(res) case TFTP_EVENT_DATA: /* Connected for receive */ res = tftp_connect_for_rx(state, event); break; return tftp_rx(state, event); case TFTP_EVENT_ERROR: state->state = TFTP_STATE_FIN; Loading Loading @@ -395,7 +559,6 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) switch(event) { case TFTP_EVENT_DATA: /* Is this the block we expect? */ rblock = getrpacketblock(&state->rpacket); if((state->block+1) != rblock) { Loading Loading @@ -424,7 +587,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) } /* Check if completed (That is, a less than full packet is received) */ if(state->rbytes < (ssize_t)sizeof(state->spacket)){ if(state->rbytes < (ssize_t)state->blksize+4){ state->state = TFTP_STATE_FIN; } else { Loading @@ -432,6 +595,25 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) } 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", Curl_strerror(state->conn, SOCKERRNO)); return CURLE_SEND_ERROR; } /* we're ready to RX data */ state->state = TFTP_STATE_RX; break; case TFTP_EVENT_TIMEOUT: /* Increment the retry count and fail if over the limit */ state->retries++; Loading @@ -443,7 +625,7 @@ static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event) } else { /* Resend the previous ACK */ sbytes = sendto(state->sockfd, (void *)&state->spacket, sbytes = sendto(state->sockfd, (void *)state->spacket.data, 4, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); Loading Loading @@ -519,11 +701,12 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) state->retries = 0; setpacketevent(&state->spacket, TFTP_EVENT_DATA); setpacketblock(&state->spacket, state->block); if(state->block > 1 && state->sbytes < TFTP_BLOCKSIZE) { if(state->block > 1 && state->sbytes < state->blksize) { state->state = TFTP_STATE_FIN; return CURLE_OK; } res = Curl_fillreadbuffer(state->conn, TFTP_BLOCKSIZE, &state->sbytes); res = Curl_fillreadbuffer(state->conn, state->blksize, (int *)&state->sbytes); if(res) return res; sbytes = sendto(state->sockfd, (void *)state->spacket.data, Loading Loading @@ -552,7 +735,7 @@ static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event) } else { /* Re-send the data packet */ sbytes = sendto(state->sockfd, (void *)&state->spacket, sbytes = sendto(state->sockfd, (void *)state->spacket.data, 4+state->sbytes, SEND_4TH_ARG, (struct sockaddr *)&state->remote_addr, state->remote_addrlen); Loading Loading @@ -615,6 +798,26 @@ static CURLcode tftp_state_machine(tftp_state_data_t *state, return res; } /********************************************************** * * tftp_disconnect * * The disconnect callback * **********************************************************/ static CURLcode tftp_disconnect(struct connectdata *conn) { tftp_state_data_t *state = conn->proto.tftpc; /* done, free dynamically allocated pkt buffers */ if(state) { Curl_safefree(state->rpacket.data); Curl_safefree(state->spacket.data); free(state); } return CURLE_OK; } /********************************************************** * Loading @@ -627,18 +830,37 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) { CURLcode code; tftp_state_data_t *state; int rc; int blksize, rc; blksize = TFTP_BLKSIZE_DEFAULT; /* If there already is a protocol-specific struct allocated for this sessionhandle, deal with it */ Curl_reset_reqproto(conn); state = conn->data->state.proto.tftp; if(!state) { state = conn->data->state.proto.tftp = calloc(sizeof(tftp_state_data_t), 1); state = conn->proto.tftpc = calloc(sizeof(tftp_state_data_t), 1); if(!state) return CURLE_OUT_OF_MEMORY; /* alloc pkt buffers based on specified blksize */ if(conn->data->set.tftp_blksize) { blksize = conn->data->set.tftp_blksize; if(blksize > TFTP_BLKSIZE_MAX || blksize < TFTP_BLKSIZE_MIN ) return CURLE_TFTP_ILLEGAL; } if(!state->rpacket.data) { state->rpacket.data = calloc(1, blksize + 2 + 2); if(!state->rpacket.data) return CURLE_OUT_OF_MEMORY; } if(!state->spacket.data) { state->spacket.data = calloc(1, blksize + 2 + 2); if(!state->spacket.data) return CURLE_OUT_OF_MEMORY; } conn->bits.close = FALSE; /* keep it open if possible */ Loading @@ -647,6 +869,8 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done) state->sockfd = state->conn->sock[FIRSTSOCKET]; state->state = TFTP_STATE_START; state->error = TFTP_ERR_NONE; state->blksize = TFTP_BLKSIZE_DEFAULT; state->requested_blksize = blksize; ((struct sockaddr *)&state->local_addr)->sa_family = (unsigned short)(conn->ip_addr->ai_family); Loading Loading @@ -735,12 +959,12 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) */ Curl_reset_reqproto(conn); if(!data->state.proto.tftp) { if(!conn->proto.tftpc) { code = tftp_connect(conn, done); if(code) return code; } state = (tftp_state_data_t *)data->state.proto.tftp; state = (tftp_state_data_t *)conn->proto.tftpc; /* Run the TFTP State Machine */ for(code=tftp_state_machine(state, TFTP_EVENT_INIT); Loading Loading @@ -770,8 +994,8 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) /* Receive the packet */ fromlen = sizeof(fromaddr); state->rbytes = (ssize_t)recvfrom(state->sockfd, (void *)&state->rpacket, sizeof(state->rpacket), (void *)state->rpacket.data, state->blksize+4, 0, (struct sockaddr *)&fromaddr, &fromlen); Loading @@ -797,7 +1021,7 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) if(state->rbytes > 4 && ((state->block+1) == getrpacketblock(&state->rpacket))) { code = Curl_client_write(conn, CLIENTWRITE_BODY, (char *)&state->rpacket.data[4], (char *)state->rpacket.data+4, state->rbytes-4); if(code) return code; Loading @@ -807,10 +1031,17 @@ static CURLcode tftp_do(struct connectdata *conn, bool *done) break; case TFTP_EVENT_ERROR: state->error = (tftp_error_t)getrpacketblock(&state->rpacket); infof(data, "%s\n", (const char *)&state->rpacket.data[4]); infof(data, "%s\n", (const char *)state->rpacket.data+4); break; case TFTP_EVENT_ACK: break; case TFTP_EVENT_OACK: code = tftp_parse_option_ack(state, (const char *)state->rpacket.data+2, state->rbytes-2); if(code) return code; break; case TFTP_EVENT_RRQ: case TFTP_EVENT_WRQ: default: Loading