- Chad Monroe provided the new CURLOPT_TFTP_BLKSIZE option that allows an app
authorDaniel Stenberg <daniel@haxx.se>
Mon, 26 Jan 2009 22:43:06 +0000 (22:43 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 26 Jan 2009 22:43:06 +0000 (22:43 +0000)
  to set desired block size to use for TFTP transfers instead of the default
  512 bytes.

CHANGES
RELEASE-NOTES
docs/libcurl/curl_easy_setopt.3
include/curl/curl.h
lib/tftp.c
lib/url.c
lib/urldata.h

diff --git a/CHANGES b/CHANGES
index 20621ac..2c14092 100644 (file)
--- 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
index 2684c84..8eab237 100644 (file)
@@ -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)
index c9bc036..e0a6bc6 100644 (file)
@@ -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
index 830e8a1..0b9ec4a 100644 (file)
@@ -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;
 
index 9020a1a..c0f56c6 100644 (file)
@@ -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>
 /* 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:
index 6cefda2..be5b209 100644 (file)
--- 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
index 39d217c..a06fb5e 100644 (file)
@@ -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 */