xatonum.h: add comment
[oweals/busybox.git] / networking / tftp.c
index 3fb76ecbb32957d71b261c987bb12ff50fdec521..a2683971a87e6c88f9f6ef5f179b0841347efb2f 100644 (file)
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  * ------------------------------------------------------------------------- */
 
-#include "busybox.h"
-
+#include "libbb.h"
 
 #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
 
-#define TFTP_BLOCKSIZE_DEFAULT 512     /* according to RFC 1350, don't change */
-#define TFTP_TIMEOUT 5 /* seconds */
-#define TFTP_NUM_RETRIES 5 /* number of retries */
+#define TFTP_BLOCKSIZE_DEFAULT 512      /* according to RFC 1350, don't change */
+#define TFTP_TIMEOUT_MS         50
+#define TFTP_MAXTIMEOUT_MS    2000
+#define TFTP_NUM_RETRIES        12      /* number of backed-off retries */
 
 /* opcodes we support */
 #define TFTP_RRQ   1
@@ -114,9 +114,8 @@ static int tftp( USE_GETPUT(const int cmd,)
                const char *remotefile, const int localfd,
                unsigned port, int tftp_bufsize)
 {
-       struct timeval tv;
-       fd_set rfds;
-       int socketfd;
+       struct pollfd pfd[1];
+#define socketfd (pfd[0].fd)
        int len;
        int send_len;
        USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
@@ -124,7 +123,7 @@ static int tftp( USE_GETPUT(const int cmd,)
        uint16_t opcode;
        uint16_t block_nr = 1;
        uint16_t recv_blk;
-       int timeout = TFTP_NUM_RETRIES;
+       int retries, waittime_ms;
        char *cp;
 
        unsigned org_port;
@@ -206,6 +205,10 @@ static int tftp( USE_GETPUT(const int cmd,)
                send_len = cp - xbuf;
                /* NB: send_len value is preserved in code below
                 * for potential resend */
+
+               retries = TFTP_NUM_RETRIES;     /* re-initialize */
+               waittime_ms = TFTP_TIMEOUT_MS;
+
  send_again:
 #if ENABLE_DEBUG_TFTP
                fprintf(stderr, "sending %u bytes\n", send_len);
@@ -218,14 +221,11 @@ static int tftp( USE_GETPUT(const int cmd,)
                if (finished && (opcode == TFTP_ACK))
                        goto ret;
 
-               timeout = TFTP_NUM_RETRIES;     /* re-initialize */
  recv_again:
                /* Receive packet */
-               tv.tv_sec = TFTP_TIMEOUT;
-               tv.tv_usec = 0;
-               FD_ZERO(&rfds);
-               FD_SET(socketfd, &rfds);
-               switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
+               /*pfd[0].fd = socketfd;*/
+               pfd[0].events = POLLIN;
+               switch (safe_poll(pfd, 1, waittime_ms)) {
                        unsigned from_port;
                case 1:
                        from->len = peer_lsa->len;
@@ -248,15 +248,21 @@ static int tftp( USE_GETPUT(const int cmd,)
                                goto recv_again;
                        goto process_pkt;
                case 0:
-                       timeout--;
-                       if (timeout == 0) {
-                               bb_error_msg("last timeout");
+                       retries--;
+                       if (retries == 0) {
+                               bb_error_msg("timeout");
                                goto ret;
                        }
-                       bb_error_msg("last timeout" + 5);
+
+                       /* exponential backoff with limit */
+                       waittime_ms += waittime_ms/2;
+                       if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
+                               waittime_ms = TFTP_MAXTIMEOUT_MS;
+                       }
+
                        goto send_again; /* resend last sent pkt */
                default:
-                       bb_perror_msg("select");
+                       /*bb_perror_msg("poll"); - done in safe_poll */
                        goto ret;
                }
  process_pkt:
@@ -278,15 +284,15 @@ static int tftp( USE_GETPUT(const int cmd,)
                                "unknown transfer id",
                                "file already exists",
                                "no such user",
-                               "bad option",
+                               "bad option"
                        };
-                       enum { NUM_ERRCODE = sizeof(errcode_str) / sizeof(errcode_str[0]) };
+
                        const char *msg = "";
 
                        if (rbuf[4] != '\0') {
                                msg = &rbuf[4];
                                rbuf[tftp_bufsize - 1] = '\0';
-                       } else if (recv_blk < NUM_ERRCODE) {
+                       } else if (recv_blk < ARRAY_SIZE(errcode_str)) {
                                msg = errcode_str[recv_blk];
                        }
                        bb_error_msg("server error: (%u) %s", recv_blk, msg);
@@ -309,7 +315,7 @@ static int tftp( USE_GETPUT(const int cmd,)
                                                /* htons can be impossible to use in const initializer: */
                                                /*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/
                                                /* thus we open-code big-endian layout */
-                                               static const char error_8[4] = { 0,TFTP_ERROR, 0,8 };
+                                               static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 };
                                                xsendto(socketfd, error_8, 4, &peer_lsa->sa, peer_lsa->len);
                                                bb_error_msg("server proposes bad blksize %d, exiting", blksize);
                                                goto ret;
@@ -353,7 +359,7 @@ static int tftp( USE_GETPUT(const int cmd,)
                                /* Server lost our TFTP_ACK.  Resend it */
                                block_nr = recv_blk;
                                continue;
-                       } 
+                       }
                }
 
                if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
@@ -385,7 +391,7 @@ static int tftp( USE_GETPUT(const int cmd,)
        return finished == 0; /* returns 1 on failure */
 }
 
-int tftp_main(int argc, char **argv);
+int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int tftp_main(int argc, char **argv)
 {
        len_and_sockaddr *peer_lsa;
@@ -405,7 +411,7 @@ int tftp_main(int argc, char **argv)
        opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
                        USE_GETPUT("?g--p:p--g");
 
-       USE_GETPUT(cmd =) getopt32(argc, argv,
+       USE_GETPUT(cmd =) getopt32(argv,
                        USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
                                "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
                        &localfile, &remotefile
@@ -443,7 +449,7 @@ int tftp_main(int argc, char **argv)
 
 #if ENABLE_DEBUG_TFTP
        fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n",
-                       xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),
+                       xmalloc_sockaddr2dotted(&peer_lsa->sa),
                        remotefile, localfile);
 #endif