aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2009-01-26 22:43:06 +0000
committerDaniel Stenberg <daniel@haxx.se>2009-01-26 22:43:06 +0000
commit0516ce7786e9500c2e447d48aa9b3f24a6ca70f9 (patch)
treedfa3ecea7658d50e0cb11b77074a5ad21699a854
parentbb86462ed77ac82667c91c93705210b67d739e6e (diff)
- 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.
-rw-r--r--CHANGES4
-rw-r--r--RELEASE-NOTES3
-rw-r--r--docs/libcurl/curl_easy_setopt.38
-rw-r--r--include/curl/curl.h3
-rw-r--r--lib/tftp.c321
-rw-r--r--lib/url.c6
-rw-r--r--lib/urldata.h4
7 files changed, 302 insertions, 47 deletions
diff --git a/CHANGES b/CHANGES
index 20621acf0..2c14092da 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index 2684c8483..8eab2378c 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -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:
@@ -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)
diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3
index c9bc0365c..e0a6bc687 100644
--- a/docs/libcurl/curl_easy_setopt.3
+++ b/docs/libcurl/curl_easy_setopt.3
@@ -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
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 830e8a1d8..0b9ec4aef 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -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;
diff --git a/lib/tftp.c b/lib/tftp.c
index 9020a1a23..c0f56c647 100644
--- a/lib/tftp.c
+++ b/lib/tftp.c
@@ -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
@@ -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>
@@ -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,
@@ -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;
@@ -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 {
@@ -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;
@@ -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);
@@ -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 */
};
@@ -257,7 +265,7 @@ static CURLcode tftp_set_timeouts(tftp_state_data_t *state)
state->retry_time=1;
infof(state->conn->data,
- "set timeouts for state %d; Total %d, retry %d maxtry %d\n",
+ "set timeouts for state %d; Total %d, retry %d maxtry %d\n",
state->state, (state->max_time-state->start_time),
state->retry_time, state->retry_max);
@@ -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;
@@ -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);
}
@@ -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);
@@ -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)
- break;
- return tftp_tx(state, event);
+ res = tftp_connect_for_tx(state, event);
+ break;
- 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)
- break;
- return tftp_rx(state, event);
+ case TFTP_EVENT_DATA: /* Connected for receive */
+ res = tftp_connect_for_rx(state, event);
+ break;
case TFTP_EVENT_ERROR:
state->state = TFTP_STATE_FIN;
@@ -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) {
@@ -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 {
@@ -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++;
@@ -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);
@@ -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,
@@ -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);
@@ -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;
+}
/**********************************************************
*
@@ -627,17 +830,36 @@ 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);
- if(!state)
+ 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;
}
@@ -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);
@@ -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);
@@ -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);
@@ -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;
@@ -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:
diff --git a/lib/url.c b/lib/url.c
index 6cefda2c1..be5b20990 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -932,6 +932,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
*/
data->set.ftp_response_timeout = va_arg( param , long ) * 1000;
break;
+ case CURLOPT_TFTP_BLKSIZE:
+ /*
+ * TFTP option that specifies the block size to use for data transmission
+ */
+ data->set.tftp_blksize = va_arg(param, long);
+ break;
case CURLOPT_DIRLISTONLY:
/*
* An option that changes the command to one that asks for a list
diff --git a/lib/urldata.h b/lib/urldata.h
index 39d217c65..a06fb5eb4 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1047,6 +1047,7 @@ struct connectdata {
union {
struct ftp_conn ftpc;
struct ssh_conn sshc;
+ struct tftp_state_data *tftpc;
} proto;
int cselect_bits; /* bitmask of socket events */
@@ -1288,7 +1289,7 @@ struct UrlState {
struct HTTP *http;
struct HTTP *https; /* alias, just for the sake of being more readable */
struct FTP *ftp;
- void *tftp; /* private for tftp.c-eyes only */
+ /* void *tftp; not used */
struct FILEPROTO *file;
void *telnet; /* private for telnet.c-eyes only */
void *generic;
@@ -1425,6 +1426,7 @@ struct UserDefined {
long timeout; /* in milliseconds, 0 means no timeout */
long connecttimeout; /* in milliseconds, 0 means no timeout */
long ftp_response_timeout; /* in milliseconds, 0 means no timeout */
+ long tftp_blksize ; /* in bytes, 0 means use default */
curl_off_t infilesize; /* size of file to upload, -1 means unknown */
long low_speed_limit; /* bytes/second */
long low_speed_time; /* number of seconds */