diff -Naur curl-7.19.6-20090806/lib/tftp.c curl-7.19.6-20090806.tftp/lib/tftp.c
--- curl-7.19.6-20090806/lib/tftp.c	2009-07-25 04:00:06.000000000 +0200
+++ curl-7.19.6-20090806.tftp/lib/tftp.c	2009-08-07 00:13:35.000000000 +0200
@@ -18,7 +18,7 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
- * $Id: tftp.c,v 1.92 2009-07-24 22:20:22 gknauf Exp $
+ * $Id: tftp.c,v 1.13 2009/02/11 07:50:31 cmonroe Exp $
  ***************************************************************************/
 
 #include "setup.h"
@@ -70,13 +70,14 @@
 #include "connect.h"
 #include "strerror.h"
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "multiif.h"
 #include "url.h"
 #include "rawstr.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
 
-#include "curl_memory.h"
+#include "memory.h"
 #include "select.h"
 
 /* The last #include file should be: */
@@ -103,7 +104,8 @@
 } tftp_state_t;
 
 typedef enum {
-  TFTP_EVENT_INIT=0,
+  TFTP_EVENT_NONE = -1,
+  TFTP_EVENT_INIT = 0,
   TFTP_EVENT_RRQ = 1,
   TFTP_EVENT_WRQ = 2,
   TFTP_EVENT_DATA = 3,
@@ -137,20 +139,22 @@
   tftp_state_t    state;
   tftp_mode_t     mode;
   tftp_error_t    error;
+  tftp_event_t    event;
   struct connectdata      *conn;
   curl_socket_t   sockfd;
   int             retries;
-  time_t          retry_time;
-  time_t          retry_max;
+  int             retry_time;
+  int             retry_max;
   time_t          start_time;
   time_t          max_time;
+  time_t	  rx_time;
   unsigned short  block;
   struct Curl_sockaddr_storage   local_addr;
   struct Curl_sockaddr_storage   remote_addr;
-  curl_socklen_t  remote_addrlen;
-  ssize_t         rbytes;
-  size_t          sbytes;
-  size_t          blksize;
+  socklen_t       remote_addrlen;
+  int             rbytes;
+  int             sbytes;
+  int             blksize;
   int             requested_blksize;
   tftp_packet_t   rpacket;
   tftp_packet_t   spacket;
@@ -166,6 +170,11 @@
 static CURLcode tftp_done(struct connectdata *conn,
                                CURLcode, bool premature);
 static CURLcode tftp_setup_connection(struct connectdata * conn);
+static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done);
+static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done);
+static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
+                        int numsocks);
+CURLcode tftp_translate_code(tftp_error_t error);
 
 
 /*
@@ -179,17 +188,16 @@
   tftp_done,                            /* done */
   ZERO_NULL,                            /* do_more */
   tftp_connect,                         /* connect_it */
-  ZERO_NULL,                            /* connecting */
-  ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
+  tftp_multi_statemach,                 /* connecting */
+  tftp_doing,                           /* doing */
+  tftp_getsock,                         /* proto_getsock */
+  tftp_getsock,                         /* doing_getsock */
   ZERO_NULL,                            /* perform_getsock */
   tftp_disconnect,                      /* disconnect */
   PORT_TFTP,                            /* defport */
-  PROT_TFTP                             /* protocol */
+  PROT_FTP                             /* protocol */
 };
 
-
 /**********************************************************
  *
  * tftp_set_timeouts -
@@ -226,14 +234,14 @@
     timeout = maxtime ;
 
     /* Average restart after 5 seconds */
-    state->retry_max = timeout/5;
+    state->retry_max = (int)timeout/5;
 
     if(state->retry_max < 1)
       /* avoid division by zero below */
       state->retry_max = 1;
 
     /* Compute the re-start interval to suit the timeout */
-    state->retry_time = timeout/state->retry_max;
+    state->retry_time = (int)timeout/state->retry_max;
     if(state->retry_time<1)
       state->retry_time=1;
 
@@ -250,25 +258,29 @@
     timeout = maxtime/10 ;
 
     /* Average reposting an ACK after 15 seconds */
-    state->retry_max = timeout/15;
+    state->retry_max = (int)timeout/15;
   }
-  /* But bound the total number  */
+  /* But bound the total number */
   if(state->retry_max<3)
     state->retry_max=3;
 
   if(state->retry_max>50)
     state->retry_max=50;
 
-  /* Compute the re-ACK interval to suit the timeout */
+  /* Compute the re-ACK interval to suit the timeout 
   state->retry_time = timeout/state->retry_max;
   if(state->retry_time<1)
     state->retry_time=1;
+  */
 
   infof(state->conn->data,
         "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);
 
+  /* init RX time */
+  time(&state->rx_time);
+
   return CURLE_OK;
 }
 
@@ -352,9 +364,9 @@
     infof(data, "got option=(%s) value=(%s)\n", option, value);
 
     if(checkprefix(option, TFTP_OPTION_BLKSIZE)) {
-      int blksize;
+      long int blksize;
 
-      blksize = (int)strtol( value, NULL, 10 );
+      blksize = strtol( value, NULL, 10 );
 
       if(!blksize) {
         failf(data, "invalid blocksize value in OACK packet");
@@ -379,7 +391,7 @@
         return CURLE_TFTP_ILLEGAL;
       }
 
-      state->blksize = blksize;
+      state->blksize = (int)blksize;
       infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK",
         state->blksize, "requested", state->requested_blksize);
     }
@@ -402,7 +414,7 @@
 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 )
+  if( ( strlen(option) + csize + 1 ) > (size_t)state->blksize )
         return 0;
   strcpy(buf, option);
   return( strlen(option) + 1 );
@@ -411,11 +423,9 @@
 static CURLcode tftp_connect_for_tx(tftp_state_data_t *state, tftp_event_t event)
 {
   CURLcode res;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
   struct SessionHandle *data = state->conn->data;
 
   infof(data, "%s\n", "Connected for transmit");
-#endif
   state->state = TFTP_STATE_TX;
   res = tftp_set_timeouts(state);
   if(res != CURLE_OK)
@@ -426,11 +436,9 @@
 static CURLcode tftp_connect_for_rx(tftp_state_data_t *state, tftp_event_t event)
 {
   CURLcode res;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
   struct SessionHandle *data = state->conn->data;
 
   infof(data, "%s\n", "Connected for receive");
-#endif
   state->state = TFTP_STATE_RX;
   res = tftp_set_timeouts(state);
   if(res != CURLE_OK)
@@ -441,6 +449,7 @@
 static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
 {
   size_t sbytes;
+  ssize_t senddata;
   const char *mode = "octet";
   char *filename;
   char buf[8];
@@ -501,18 +510,19 @@
                               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 );
+    /* add timeout option, this is the max time the session may live */
+    snprintf( buf, sizeof(buf), "%d", state->retry_time*state->retry_max );
     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 );
 
-    if (sendto(state->sockfd, (void *)state->spacket.data,
-               sbytes, 0,
-               state->conn->ip_addr->ai_addr,
-               state->conn->ip_addr->ai_addrlen) < 0) {
+    senddata = sendto(state->sockfd, (void *)state->spacket.data,
+                    sbytes, 0,
+                    state->conn->ip_addr->ai_addr,
+                    state->conn->ip_addr->ai_addrlen);
+    if(senddata != (ssize_t)sbytes) {
       failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
     }
     Curl_safefree(filename);
@@ -555,6 +565,7 @@
  **********************************************************/
 static CURLcode tftp_rx(tftp_state_data_t *state, tftp_event_t event)
 {
+  ssize_t sbytes;
   int rblock;
   struct SessionHandle *data = state->conn->data;
 
@@ -579,10 +590,11 @@
     state->retries = 0;
     setpacketevent(&state->spacket, TFTP_EVENT_ACK);
     setpacketblock(&state->spacket, state->block);
-    if(sendto(state->sockfd, (void *)state->spacket.data,
-              4, SEND_4TH_ARG,
-              (struct sockaddr *)&state->remote_addr,
-              state->remote_addrlen) < 0) {
+    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;
     }
@@ -594,6 +606,7 @@
     else {
       state->state = TFTP_STATE_RX;
     }
+    time(&state->rx_time);
     break;
 
   case TFTP_EVENT_OACK:
@@ -602,16 +615,18 @@
     state->retries = 0;
     setpacketevent(&state->spacket, TFTP_EVENT_ACK);
     setpacketblock(&state->spacket, state->block);
-    if(sendto(state->sockfd, (void *)state->spacket.data,
-              4, SEND_4TH_ARG,
-              (struct sockaddr *)&state->remote_addr,
-              state->remote_addrlen) < 0) {
+    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;
+    time(&state->rx_time);
     break;
 
   case TFTP_EVENT_TIMEOUT:
@@ -624,11 +639,13 @@
       state->state = TFTP_STATE_FIN;
     }
     else {
-      /* Resend the previous ACK and check all sbytes were sent */
-      if(sendto(state->sockfd, (void *)state->spacket.data,
-                4, SEND_4TH_ARG,
-                (struct sockaddr *)&state->remote_addr,
-                state->remote_addrlen) < 0) {
+      /* Resend the previous ACK */
+      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                      4, SEND_4TH_ARG,
+                      (struct sockaddr *)&state->remote_addr,
+                      state->remote_addrlen);
+      /* Check all sbytes were sent */
+      if(sbytes<0) {
         failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
         return CURLE_SEND_ERROR;
       }
@@ -636,6 +653,14 @@
     break;
 
   case TFTP_EVENT_ERROR:
+    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
+    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);
+    /* don't bother with the return code, but if the socket is still up we
+     * should be a good TFTP client and let the server know we're done */
     state->state = TFTP_STATE_FIN;
     break;
 
@@ -657,8 +682,8 @@
 static CURLcode tftp_tx(tftp_state_data_t *state, tftp_event_t event)
 {
   struct SessionHandle *data = state->conn->data;
+  ssize_t sbytes;
   int rblock;
-  int readcount;
   CURLcode res = CURLE_OK;
   struct SingleRequest *k = &data->req;
 
@@ -680,38 +705,40 @@
         res = CURLE_SEND_ERROR;
       }
       else {
-        /* Re-send the data packet and check all sbytes were sent */
-        if(sendto(state->sockfd, (void *)&state->spacket,
-                  4+state->sbytes, SEND_4TH_ARG,
-                  (struct sockaddr *)&state->remote_addr,
-                  state->remote_addrlen) < 0) {
+        /* Re-send the data packet */
+        sbytes = sendto(state->sockfd, (void *)&state->spacket,
+                        4+state->sbytes, SEND_4TH_ARG,
+                        (struct sockaddr *)&state->remote_addr,
+                        state->remote_addrlen);
+        /* Check all sbytes were sent */
+        if(sbytes<0) {
           failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
           res = CURLE_SEND_ERROR;
         }
       }
       return res;
     }
-    /* fall-through */
-  case TFTP_EVENT_OACK:
     /* This is the expected packet.  Reset the counters and send the next
        block */
+    time(&state->rx_time);
     state->block++;
     state->retries = 0;
     setpacketevent(&state->spacket, TFTP_EVENT_DATA);
     setpacketblock(&state->spacket, state->block);
-    if(state->block > 1 && state->sbytes < state->blksize) {
+    if(state->block > 1 && state->sbytes < (int)state->blksize) {
       state->state = TFTP_STATE_FIN;
       return CURLE_OK;
     }
-    res = Curl_fillreadbuffer(state->conn, (int)state->blksize, &readcount);
-    state->sbytes = readcount;
+    res = Curl_fillreadbuffer(state->conn, (size_t)state->blksize,
+                              &state->sbytes);
     if(res)
       return res;
-    /* Send the data packet and check all sbytes were sent */
-    if(sendto(state->sockfd, (void *)state->spacket.data,
-              4+state->sbytes, SEND_4TH_ARG,
-              (struct sockaddr *)&state->remote_addr,
-              state->remote_addrlen) < 0) {
+    sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                    4+state->sbytes, SEND_4TH_ARG,
+                    (struct sockaddr *)&state->remote_addr,
+                    state->remote_addrlen);
+    /* Check all sbytes were sent */
+    if(sbytes<0) {
       failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
       return CURLE_SEND_ERROR;
     }
@@ -731,11 +758,13 @@
       state->state = TFTP_STATE_FIN;
     }
     else {
-      /* Re-send the data packet and check all sbytes were sent */
-      if(sendto(state->sockfd, (void *)state->spacket.data,
-                4+state->sbytes, SEND_4TH_ARG,
-                (struct sockaddr *)&state->remote_addr,
-                state->remote_addrlen) < 0) {
+      /* Re-send the data packet */
+      sbytes = sendto(state->sockfd, (void *)state->spacket.data,
+                      4+state->sbytes, SEND_4TH_ARG,
+                      (struct sockaddr *)&state->remote_addr,
+                      state->remote_addrlen);
+      /* Check all sbytes were sent */
+      if(sbytes<0) {
         failf(data, "%s", Curl_strerror(state->conn, SOCKERRNO));
         return CURLE_SEND_ERROR;
       }
@@ -746,6 +775,15 @@
 
   case TFTP_EVENT_ERROR:
     state->state = TFTP_STATE_FIN;
+    setpacketevent(&state->spacket, TFTP_EVENT_ERROR);
+    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);
+    /* don't bother with the return code, but if the socket is still up we
+     * should be a good TFTP client and let the server know we're done */
+    state->state = TFTP_STATE_FIN;
     break;
 
   default:
@@ -758,6 +796,59 @@
 
 /**********************************************************
  *
+ * tftp_translate_code
+ *
+ * Translate internal error codes to CURL error codes 
+ *
+ **********************************************************/
+CURLcode tftp_translate_code(tftp_error_t error)
+{
+  CURLcode code = CURLE_OK;
+
+  if(error != TFTP_ERR_NONE) {
+    switch(error) {
+    case TFTP_ERR_NOTFOUND:
+      code = CURLE_TFTP_NOTFOUND;
+      break;
+    case TFTP_ERR_PERM:
+      code = CURLE_TFTP_PERM;
+      break;
+    case TFTP_ERR_DISKFULL:
+      code = CURLE_REMOTE_DISK_FULL;
+      break;
+    case TFTP_ERR_UNDEF:
+    case TFTP_ERR_ILLEGAL:
+      code = CURLE_TFTP_ILLEGAL;
+      break;
+    case TFTP_ERR_UNKNOWNID:
+      code = CURLE_TFTP_UNKNOWNID;
+      break;
+    case TFTP_ERR_EXISTS:
+      code = CURLE_REMOTE_FILE_EXISTS;
+      break;
+    case TFTP_ERR_NOSUCHUSER:
+      code = CURLE_TFTP_NOSUCHUSER;
+      break;
+    case TFTP_ERR_TIMEOUT:
+      code = CURLE_OPERATION_TIMEDOUT;
+      break;
+    case TFTP_ERR_NORESPONSE:
+      code = CURLE_COULDNT_CONNECT;
+      break;
+    default:
+      code= CURLE_ABORTED_BY_CALLBACK;
+      break;
+    }
+  }
+  else {
+    code = CURLE_OK;
+  }
+
+  return(code);
+}
+
+/**********************************************************
+ *
  * tftp_state_machine
  *
  * The tftp state machine event dispatcher
@@ -833,9 +924,12 @@
      sessionhandle, deal with it */
   Curl_reset_reqproto(conn);
 
-  state = conn->proto.tftpc = calloc(sizeof(tftp_state_data_t), 1);
-  if(!state)
-    return CURLE_OUT_OF_MEMORY;
+  state = conn->proto.tftpc;
+  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) {
@@ -858,9 +952,7 @@
       return CURLE_OUT_OF_MEMORY;
   }
 
-  conn->bits.close = TRUE; /* we don't keep TFTP connections up bascially
-                              because there's none or very little gain for UDP
-                           */
+  conn->bits.close = TRUE; /* always close */
 
   state->conn = conn;
   state->sockfd = state->conn->sock[FIRSTSOCKET];
@@ -915,204 +1007,363 @@
 static CURLcode tftp_done(struct connectdata *conn, CURLcode status,
                                bool premature)
 {
+  CURLcode code = CURLE_OK;
+  tftp_state_data_t *state = (tftp_state_data_t *)conn->proto.tftpc;
+
   (void)status; /* unused */
   (void)premature; /* not used */
 
   Curl_pgrsDone(conn);
 
-  return CURLE_OK;
-}
+  /* If we have encountered an error */
+  code = tftp_translate_code(state->error);
 
+  return code;
+}
 
 /**********************************************************
  *
- * tftp
+ * tftp_getsock
  *
- * The do callback
- *
- * This callback handles the entire TFTP transfer
+ * The getsock callback
  *
  **********************************************************/
+static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
+                       int numsocks)
+{
+  if(!numsocks)
+    return GETSOCK_BLANK;
 
-static CURLcode tftp_do(struct connectdata *conn, bool *done)
+  socks[0] = conn->sock[FIRSTSOCKET];
+
+  return GETSOCK_READSOCK(0);
+}
+
+/**********************************************************
+ *
+ * tftp_receive_packet
+ *
+ * Called once select fires and data is ready on the socket 
+ *
+ **********************************************************/
+static CURLcode tftp_receive_packet(struct connectdata *conn) 
 {
-  struct SessionHandle  *data = conn->data;
-  tftp_state_data_t     *state;
-  tftp_event_t          event;
-  CURLcode              code;
-  int                   rc;
   struct Curl_sockaddr_storage fromaddr;
-  curl_socklen_t        fromlen;
-  int                   check_time = 0;
-  struct SingleRequest *k = &data->req;
+  socklen_t             fromlen;
+  CURLcode 		result = CURLE_OK;
+  struct SessionHandle  *data = conn->data;
+  tftp_state_data_t	*state = (tftp_state_data_t *)conn->proto.tftpc;
+  struct SingleRequest	*k = &data->req;
 
-  *done = TRUE;
+  /* Receive the packet */
+  fromlen = sizeof(fromaddr);
+  state->rbytes = (int)recvfrom(state->sockfd,
+                                   (void *)state->rpacket.data,
+                                   state->blksize+4,
+                                   0,
+                                   (struct sockaddr *)&fromaddr,
+                                   &fromlen);
+  if(state->remote_addrlen==0) {
+    memcpy(&state->remote_addr, &fromaddr, fromlen);
+    state->remote_addrlen = fromlen;
+  }
 
-  /*
-    Since connections can be re-used between SessionHandles, this might be a
-    connection already existing but on a fresh SessionHandle struct so we must
-    make sure we have a good 'struct TFTP' to play with. For new connections,
-    the struct TFTP is allocated and setup in the tftp_connect() function.
-  */
-  Curl_reset_reqproto(conn);
+  /* Sanity check packet length */
+  if(state->rbytes < 4) {
+    failf(data, "Received too short packet");
+    /* Not a timeout, but how best to handle it? */
+    state->event = TFTP_EVENT_TIMEOUT;
+  }
+  else {
+    /* The event is given by the TFTP packet time */
+    state->event = (tftp_event_t)getrpacketevent(&state->rpacket);
 
-  if(!conn->proto.tftpc) {
-    code = tftp_connect(conn, done);
-    if(code)
-      return code;
+    switch(state->event) {
+    case TFTP_EVENT_DATA:
+      /* Don't pass to the client empty or retransmitted packets */
+      if(state->rbytes > 4 &&
+          ((state->block+1) == getrpacketblock(&state->rpacket))) {
+        result = Curl_client_write(conn, CLIENTWRITE_BODY,
+                                 (char *)state->rpacket.data+4,
+                                 state->rbytes-4);
+        if(result) {
+          tftp_state_machine(state, TFTP_EVENT_ERROR);
+          return result;
+	}
+        k->bytecount += state->rbytes-4;
+        Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
+      }
+      break;
+    case TFTP_EVENT_ERROR:
+      state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
+      infof(data, "%s\n", (const char *)state->rpacket.data+4);
+      break;
+    case TFTP_EVENT_ACK:
+      break;
+    case TFTP_EVENT_OACK:
+      result = tftp_parse_option_ack(state,
+                                   (const char *)state->rpacket.data+2,
+                                   state->rbytes-2);
+      if(result)
+        return result;
+      break;
+    case TFTP_EVENT_RRQ:
+    case TFTP_EVENT_WRQ:
+    default:
+      failf(data, "%s", "Internal error: Unexpected packet");
+      break;
+    }
+
+    /* Update the progress meter */
+    if(Curl_pgrsUpdate(conn)) {
+      tftp_state_machine(state, TFTP_EVENT_ERROR);
+      return CURLE_ABORTED_BY_CALLBACK;
+    }
   }
-  state = (tftp_state_data_t *)conn->proto.tftpc;
+  return result;
+}
+
+/**********************************************************
+ *
+ * tftp_state_timeout 
+ *
+ * Check if timeouts have been reached 
+ *
+ **********************************************************/
+static long tftp_state_timeout(struct connectdata *conn, tftp_event_t *event)
+{
+  time_t 		current;
+  struct SessionHandle  *data = conn->data;
+  tftp_state_data_t	*state = (tftp_state_data_t *)conn->proto.tftpc;
+
+  if (event)
+    *event = TFTP_EVENT_NONE;
+
+  time(&current);
+  if(current > state->max_time) {
+    DEBUGF(infof(data, "timeout: %d > %d\n",
+                 current, state->max_time));
+    state->error = TFTP_ERR_TIMEOUT;
+    state->state = TFTP_STATE_FIN;
+    return(0);
+  } 
+  else if (current > state->rx_time+state->retry_time) {
+    if (event)
+      *event = TFTP_EVENT_TIMEOUT;
+    time(&state->rx_time); /* update even though we received nothing */
+    return(state->max_time-current);
+  }
+  else {
+    return(state->max_time-current);
+  }
+}
+
+/**********************************************************
+ *
+ * tftp_easy_statemach 
+ *
+ * Handle easy request until completion 
+ *
+ **********************************************************/
+static CURLcode tftp_easy_statemach(struct connectdata *conn)
+{
+  int			rc;
+  int			check_time = 0;
+  CURLcode		result = CURLE_OK;
+  struct SessionHandle  *data = conn->data;
+  tftp_state_data_t	*state = (tftp_state_data_t *)conn->proto.tftpc;
 
   /* Run the TFTP State Machine */
-  for(code=tftp_state_machine(state, TFTP_EVENT_INIT);
-      (state->state != TFTP_STATE_FIN) && (code == CURLE_OK);
-      code=tftp_state_machine(state, event) ) {
+  for(;
+      (state->state != TFTP_STATE_FIN) && (result == CURLE_OK);
+      result=tftp_state_machine(state, state->event) ) {
 
     /* Wait until ready to read or timeout occurs */
     rc=Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD,
-                         (int)(state->retry_time * 1000));
+                         state->retry_time * 1000);
 
     if(rc == -1) {
       /* bail out */
       int error = SOCKERRNO;
       failf(data, "%s", Curl_strerror(conn, error));
-      event = TFTP_EVENT_ERROR;
+      state->event = TFTP_EVENT_ERROR;
     }
     else if(rc==0) {
       /* A timeout occured */
-      event = TFTP_EVENT_TIMEOUT;
+      state->event = TFTP_EVENT_TIMEOUT;
 
       /* Force a look at transfer timeouts */
       check_time = 0;
 
     }
     else {
-
-      /* Receive the packet */
-      fromlen = sizeof(fromaddr);
-      state->rbytes = (ssize_t)recvfrom(state->sockfd,
-                                        (void *)state->rpacket.data,
-                                        state->blksize+4,
-                                        0,
-                                        (struct sockaddr *)&fromaddr,
-                                        &fromlen);
-      if(state->remote_addrlen==0) {
-        memcpy(&state->remote_addr, &fromaddr, fromlen);
-        state->remote_addrlen = fromlen;
-      }
-
-      /* Sanity check packet length */
-      if(state->rbytes < 4) {
-        failf(data, "Received too short packet");
-        /* Not a timeout, but how best to handle it? */
-        event = TFTP_EVENT_TIMEOUT;
-      }
-      else {
-
-        /* The event is given by the TFTP packet time */
-        event = (tftp_event_t)getrpacketevent(&state->rpacket);
-
-        switch(event) {
-        case TFTP_EVENT_DATA:
-          /* Don't pass to the client empty or retransmitted packets */
-          if(state->rbytes > 4 &&
-              ((state->block+1) == getrpacketblock(&state->rpacket))) {
-            code = Curl_client_write(conn, CLIENTWRITE_BODY,
-                                     (char *)state->rpacket.data+4,
-                                     state->rbytes-4);
-            if(code)
-              return code;
-            k->bytecount += state->rbytes-4;
-            Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
-          }
-          break;
-        case TFTP_EVENT_ERROR:
-          state->error = (tftp_error_t)getrpacketblock(&state->rpacket);
-          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,
-                                       (int)state->rbytes-2);
-          if(code)
-            return code;
-          break;
-        case TFTP_EVENT_RRQ:
-        case TFTP_EVENT_WRQ:
-        default:
-          failf(data, "%s", "Internal error: Unexpected packet");
-          break;
-        }
-
-        /* Update the progress meter */
-        if(Curl_pgrsUpdate(conn))
-          return CURLE_ABORTED_BY_CALLBACK;
-      }
+        result = tftp_receive_packet(conn);
     }
 
     /* Check for transfer timeout every 10 blocks, or after timeout */
     if(check_time%10==0) {
-      time_t current;
-      time(&current);
-      if(current>state->max_time) {
-        DEBUGF(infof(data, "timeout: %d > %d\n",
-                     current, state->max_time));
-        state->error = TFTP_ERR_TIMEOUT;
-        state->state = TFTP_STATE_FIN;
-      }
+      /* ignore the event here as Curl_socket_ready() handles
+       * retransmission timeouts inside the easy state mach */
+      tftp_state_timeout(conn, NULL);
     }
 
+    if(result)
+      return(result);
   }
-  if(code)
-    return code;
 
   /* Tell curl we're done */
-  code = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
-  if(code)
-    return code;
+  result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
 
-  /* If we have encountered an error */
-  if(state->error != TFTP_ERR_NONE) {
+  return(result);
+}
 
-    /* Translate internal error codes to curl error codes */
-    switch(state->error) {
-    case TFTP_ERR_NOTFOUND:
-      code = CURLE_TFTP_NOTFOUND;
-      break;
-    case TFTP_ERR_PERM:
-      code = CURLE_TFTP_PERM;
-      break;
-    case TFTP_ERR_DISKFULL:
-      code = CURLE_REMOTE_DISK_FULL;
-      break;
-    case TFTP_ERR_UNDEF:
-    case TFTP_ERR_ILLEGAL:
-      code = CURLE_TFTP_ILLEGAL;
-      break;
-    case TFTP_ERR_UNKNOWNID:
-      code = CURLE_TFTP_UNKNOWNID;
-      break;
-    case TFTP_ERR_EXISTS:
-      code = CURLE_REMOTE_FILE_EXISTS;
-      break;
-    case TFTP_ERR_NOSUCHUSER:
-      code = CURLE_TFTP_NOSUCHUSER;
-      break;
-    case TFTP_ERR_TIMEOUT:
-      code = CURLE_OPERATION_TIMEDOUT;
-      break;
-    case TFTP_ERR_NORESPONSE:
-      code = CURLE_COULDNT_CONNECT;
-      break;
-    default:
-      code= CURLE_ABORTED_BY_CALLBACK;
-      break;
+/**********************************************************
+ *
+ * tftp_multi_statemach 
+ *
+ * Handle single RX socket event and return 
+ *
+ **********************************************************/
+static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done)
+{
+  int			rc;
+  tftp_event_t		event;
+  CURLcode		result = CURLE_OK;
+  struct SessionHandle  *data = conn->data;
+  tftp_state_data_t	*state = (tftp_state_data_t *)conn->proto.tftpc;
+  long 			timeout_ms = tftp_state_timeout(conn, &event);
+
+  *done = FALSE;
+
+  if(timeout_ms <= 0) {
+    failf(data, "TFTP response timeout");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+  else if (event != TFTP_EVENT_NONE) {
+    result = tftp_state_machine(state, event);
+    if(result != CURLE_OK)
+      return(result);
+    *done = (bool)(state->state == TFTP_STATE_FIN);
+    if(*done)
+      /* Tell curl we're done */
+      result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+  }
+  else {
+    /* no timeouts to handle, check our socket */	
+    rc = Curl_socket_ready(state->sockfd, CURL_SOCKET_BAD, 0);
+
+    if(rc == -1) {
+      /* bail out */
+      int error = SOCKERRNO;
+      failf(data, "%s", Curl_strerror(conn, error));
+      state->event = TFTP_EVENT_ERROR;
     }
+    else if(rc != 0) {
+      result = tftp_receive_packet(conn); 
+      if(result != CURLE_OK)
+        return(result);
+      result = tftp_state_machine(state, state->event);
+      if(result != CURLE_OK)
+        return(result);
+      *done = (bool)(state->state == TFTP_STATE_FIN);
+      if(*done)
+        /* Tell curl we're done */
+        result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
+    }
+    /* if rc == 0, then select() timed out */
   }
-  else
-    code = CURLE_OK;
+
+  return result;
+}
+
+/**********************************************************
+ *
+ * tftp_doing 
+ *
+ * Called from multi.c while DOing 
+ *
+ **********************************************************/
+static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done)
+{
+  CURLcode result;
+  result = tftp_multi_statemach(conn, dophase_done);
+
+  if(*dophase_done) {
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+  }
+  return result;
+}
+
+/**********************************************************
+ *
+ * tftp_peform 
+ *
+ * Entry point for transfer from tftp_do, sarts state mach 
+ *
+ **********************************************************/
+static CURLcode tftp_perform(struct connectdata *conn, bool *dophase_done)
+{
+  CURLcode 		result = CURLE_OK;
+  tftp_state_data_t	*state = (tftp_state_data_t *)conn->proto.tftpc;
+
+  *dophase_done = FALSE;
+
+  result = tftp_state_machine(state, TFTP_EVENT_INIT);
+
+  if(state->state == TFTP_STATE_FIN || result != CURLE_OK)
+    return(result);
+
+  if(conn->data->state.used_interface == Curl_if_multi)
+    tftp_multi_statemach(conn, dophase_done);
+  else {
+    result = tftp_easy_statemach(conn);
+    *dophase_done = TRUE; /* with the easy interface we are done here */
+  }
+
+  if(*dophase_done)
+    DEBUGF(infof(conn->data, "DO phase is complete\n"));
+
+  return result;
+}
+
+
+/**********************************************************
+ *
+ * tftp_do
+ *
+ * The do callback
+ *
+ * This callback initiates the TFTP transfer 
+ *
+ **********************************************************/
+
+static CURLcode tftp_do(struct connectdata *conn, bool *done)
+{
+  tftp_state_data_t     *state;
+  CURLcode              code;
+
+  *done = FALSE;
+
+  /*
+    Since connections can be re-used between SessionHandles, this might be a
+    connection already existing but on a fresh SessionHandle struct so we must
+    make sure we have a good 'struct TFTP' to play with. For new connections,
+    the struct TFTP is allocated and setup in the tftp_connect() function.
+  */
+  Curl_reset_reqproto(conn);
+
+  if(!conn->proto.tftpc) {
+    code = tftp_connect(conn, done);
+    if(code)
+      return code;
+  }
+  state = (tftp_state_data_t *)conn->proto.tftpc;
+
+  code = tftp_perform(conn, done);
+
+  /* If we have encountered an error */
+  code = tftp_translate_code(state->error);
+
   return code;
 }
 
@@ -1133,7 +1384,8 @@
 
   if(type) {
     *type = 0;                   /* it was in the middle of the hostname */
-    command = Curl_raw_toupper(type[6]);
+    command = (char) toupper((int) type[6]);
+    /* command = Curl_raw_toupper(type[6]); */
 
     switch (command) {
     case 'A': /* ASCII mode */
diff -Naur curl-7.19.6-20090806/lib/urldata.h curl-7.19.6-20090806.tftp/lib/urldata.h
--- curl-7.19.6-20090806/lib/urldata.h	2009-07-23 04:00:02.000000000 +0200
+++ curl-7.19.6-20090806.tftp/lib/urldata.h	2009-08-06 23:30:43.000000000 +0200
@@ -926,8 +926,8 @@
 #define PROT_SSL     (1<<22) /* protocol requires SSL */
 #define PROT_MISSING (1<<23)
 
-#define PROT_CLOSEACTION PROT_FTP /* these ones need action before socket
-                                     close */
+/* these ones need action before socket close */
+#define PROT_CLOSEACTION (PROT_FTP | PROT_TFTP) 
 #define PROT_DUALCHANNEL PROT_FTP /* these protocols use two connections */
 
   /* 'dns_entry' is the particular host we use. This points to an entry in the

