Ignore:
Timestamp:
Nov 4, 2007, 3:16:40 AM (17 years ago)
Author:
Bruno Cornec
Message:

Update to busybox 1.7.2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.5/mindi-busybox/networking/tftp.c

    r821 r1765  
    2020 * ------------------------------------------------------------------------- */
    2121
    22 #include <stdio.h>
    23 #include <stdlib.h>
    24 #include <string.h>
    25 #include <sys/types.h>
    26 #include <sys/socket.h>
    27 #include <sys/stat.h>
    28 #include <netdb.h>
    29 #include <netinet/in.h>
    30 #include <arpa/inet.h>
    31 #include <unistd.h>
    32 #include <fcntl.h>
    33 
    34 #include "busybox.h"
    35 
     22#include "libbb.h"
     23
     24
     25#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
    3626
    3727#define TFTP_BLOCKSIZE_DEFAULT 512  /* according to RFC 1350, don't change */
    3828#define TFTP_TIMEOUT 5  /* seconds */
    3929#define TFTP_NUM_RETRIES 5 /* number of retries */
    40 
    41 static const char * const MODE_OCTET = "octet";
    42 #define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/
    43 
    44 static const char * const OPTION_BLOCKSIZE = "blksize";
    45 #define OPTION_BLOCKSIZE_LEN 8 /* sizeof(OPTION_BLOCKSIZE) */
    4630
    4731/* opcodes we support */
     
    5337#define TFTP_OACK  6
    5438
    55 static const char *const tftp_bb_error_msg[] = {
    56     "Undefined error",
    57     "File not found",
    58     "Access violation",
    59     "Disk full or allocation error",
    60     "Illegal TFTP operation",
    61     "Unknown transfer ID",
    62     "File already exists",
    63     "No such user"
    64 };
    65 
    66 #define tftp_cmd_get ENABLE_FEATURE_TFTP_GET
    67 
    68 #if ENABLE_FEATURE_TFTP_PUT
    69 # define tftp_cmd_put (tftp_cmd_get+ENABLE_FEATURE_TFTP_PUT)
     39#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
     40#define USE_GETPUT(...)
     41#define CMD_GET(cmd) 1
     42#define CMD_PUT(cmd) 0
     43#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
     44#define USE_GETPUT(...)
     45#define CMD_GET(cmd) 0
     46#define CMD_PUT(cmd) 1
    7047#else
    71 # define tftp_cmd_put 0
    72 #endif
    73 
    74 
    75 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
     48#define USE_GETPUT(...) __VA_ARGS__
     49/* masks coming from getpot32 */
     50#define CMD_GET(cmd) ((cmd) & 1)
     51#define CMD_PUT(cmd) ((cmd) & 2)
     52#endif
     53/* NB: in the code below
     54 * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
     55 */
     56
     57
     58#if ENABLE_FEATURE_TFTP_BLOCKSIZE
    7659
    7760static int tftp_blocksize_check(int blocksize, int bufsize)
     
    8366     */
    8467
    85     if ((bufsize && (blocksize > bufsize)) ||
    86         (blocksize < 8) || (blocksize > 65564)) {
     68    if ((bufsize && (blocksize > bufsize))
     69     || (blocksize < 8) || (blocksize > 65564)
     70    ) {
    8771        bb_error_msg("bad blocksize");
    8872        return 0;
     
    9276}
    9377
    94 static char *tftp_option_get(char *buf, int len, const char * const option)
     78static char *tftp_option_get(char *buf, int len, const char *option)
    9579{
    9680    int opt_val = 0;
     
    9983
    10084    while (len > 0) {
    101 
    10285        /* Make sure the options are terminated correctly */
    103 
    10486        for (k = 0; k < len; k++) {
    10587            if (buf[k] == '\0') {
    106                 break;
    107             }
    108         }
    109 
    110         if (k >= len) {
    111             break;
    112         }
    113 
     88                goto nul_found;
     89            }
     90        }
     91        return NULL;
     92 nul_found:
    11493        if (opt_val == 0) {
    11594            if (strcasecmp(buf, option) == 0) {
    11695                opt_found = 1;
    11796            }
    118         } else {
    119             if (opt_found) {
    120                 return buf;
    121             }
     97        } else if (opt_found) {
     98            return buf;
    12299        }
    123100
    124101        k++;
    125 
    126102        buf += k;
    127103        len -= k;
    128 
    129104        opt_val ^= 1;
    130105    }
     
    135110#endif
    136111
    137 static int tftp(const int cmd, const struct hostent *host,
    138            const char *remotefile, const int localfd,
    139            const unsigned short port, int tftp_bufsize)
     112static int tftp( USE_GETPUT(const int cmd,)
     113        len_and_sockaddr *peer_lsa,
     114        const char *remotefile, const int localfd,
     115        unsigned port, int tftp_bufsize)
    140116{
    141     struct sockaddr_in sa;
    142     struct sockaddr_in from;
    143117    struct timeval tv;
    144     socklen_t fromlen;
    145118    fd_set rfds;
    146119    int socketfd;
    147120    int len;
    148     int opcode = 0;
    149     int finished = 0;
     121    int send_len;
     122    USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
     123    smallint finished = 0;
     124    uint16_t opcode;
     125    uint16_t block_nr = 1;
     126    uint16_t recv_blk;
    150127    int timeout = TFTP_NUM_RETRIES;
    151     unsigned short block_nr = 1;
    152     unsigned short tmp;
    153128    char *cp;
    154129
    155     USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
     130    unsigned org_port;
     131    len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
    156132
    157133    /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
    158134     * size varies meaning BUFFERS_GO_ON_STACK would fail */
    159     char *buf=xmalloc(tftp_bufsize += 4);
    160 
    161     if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
    162         /* need to unlink the localfile, so don't use bb_xsocket here. */
    163         bb_perror_msg("socket");
    164         return EXIT_FAILURE;
    165     }
    166 
    167     len = sizeof(sa);
    168 
    169     memset(&sa, 0, len);
    170     bb_xbind(socketfd, (struct sockaddr *)&sa, len);
    171 
    172     sa.sin_family = host->h_addrtype;
    173     sa.sin_port = port;
    174     memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
    175            sizeof(sa.sin_addr));
     135    /* We must keep the transmit and receive buffers seperate */
     136    /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
     137    char *xbuf = xmalloc(tftp_bufsize += 4);
     138    char *rbuf = xmalloc(tftp_bufsize);
     139
     140    port = org_port = htons(port);
     141
     142    socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
    176143
    177144    /* build opcode */
    178     if (cmd & tftp_cmd_get) {
     145    opcode = TFTP_WRQ;
     146    if (CMD_GET(cmd)) {
    179147        opcode = TFTP_RRQ;
    180148    }
    181     if (cmd & tftp_cmd_put) {
    182         opcode = TFTP_WRQ;
    183     }
     149    cp = xbuf + 2;
     150    /* add filename and mode */
     151    /* fill in packet if the filename fits into xbuf */
     152    len = strlen(remotefile) + 1;
     153    if (2 + len + sizeof("octet") >= tftp_bufsize) {
     154        bb_error_msg("remote filename is too long");
     155        goto ret;
     156    }
     157    strcpy(cp, remotefile);
     158    cp += len;
     159    /* add "mode" part of the package */
     160    strcpy(cp, "octet");
     161    cp += sizeof("octet");
     162
     163#if ENABLE_FEATURE_TFTP_BLOCKSIZE
     164    len = tftp_bufsize - 4; /* data block size */
     165    if (len != TFTP_BLOCKSIZE_DEFAULT) {
     166        /* rfc2348 says that 65464 is a max allowed value */
     167        if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
     168            bb_error_msg("remote filename is too long");
     169            goto ret;
     170        }
     171        /* add "blksize", <nul>, blocksize */
     172        strcpy(cp, "blksize");
     173        cp += sizeof("blksize");
     174        cp += snprintf(cp, 6, "%d", len) + 1;
     175        want_option_ack = 1;
     176    }
     177#endif
     178    /* First packet is built, so skip packet generation */
     179    goto send_pkt;
     180
     181    /* Using mostly goto's - continue/break will be less clear
     182     * in where we actually jump to */
    184183
    185184    while (1) {
    186 
    187         cp = buf;
    188 
    189         /* first create the opcode part */
    190         *((unsigned short *) cp) = htons(opcode);
     185        /* Build ACK or DATA */
     186        cp = xbuf + 2;
     187        *((uint16_t*)cp) = htons(block_nr);
    191188        cp += 2;
    192 
    193         /* add filename and mode */
    194         if (((cmd & tftp_cmd_get) && (opcode == TFTP_RRQ)) ||
    195             ((cmd & tftp_cmd_put) && (opcode == TFTP_WRQ)))
    196         {
    197             int too_long = 0;
    198 
    199             /* see if the filename fits into buf
    200              * and fill in packet.  */
    201             len = strlen(remotefile) + 1;
    202 
    203             if ((cp + len) >= &buf[tftp_bufsize - 1]) {
    204                 too_long = 1;
    205             } else {
    206                 safe_strncpy(cp, remotefile, len);
    207                 cp += len;
    208             }
    209 
    210             if (too_long || ((&buf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) {
    211                 bb_error_msg("remote filename too long");
    212                 break;
    213             }
    214 
    215             /* add "mode" part of the package */
    216             memcpy(cp, MODE_OCTET, MODE_OCTET_LEN);
    217             cp += MODE_OCTET_LEN;
    218 
    219 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
    220 
    221             len = tftp_bufsize - 4; /* data block size */
    222 
    223             if (len != TFTP_BLOCKSIZE_DEFAULT) {
    224 
    225                 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
    226                     bb_error_msg("remote filename too long");
    227                     break;
     189        block_nr++;
     190        opcode = TFTP_ACK;
     191        if (CMD_PUT(cmd)) {
     192            opcode = TFTP_DATA;
     193            len = full_read(localfd, cp, tftp_bufsize - 4);
     194            if (len < 0) {
     195                bb_perror_msg(bb_msg_read_error);
     196                goto ret;
     197            }
     198            if (len != (tftp_bufsize - 4)) {
     199                finished = 1;
     200            }
     201            cp += len;
     202        }
     203 send_pkt:
     204        /* Send packet */
     205        *((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
     206        send_len = cp - xbuf;
     207        /* NB: send_len value is preserved in code below
     208         * for potential resend */
     209 send_again:
     210#if ENABLE_DEBUG_TFTP
     211        fprintf(stderr, "sending %u bytes\n", send_len);
     212        for (cp = xbuf; cp < &xbuf[send_len]; cp++)
     213            fprintf(stderr, "%02x ", (unsigned char) *cp);
     214        fprintf(stderr, "\n");
     215#endif
     216        xsendto(socketfd, xbuf, send_len, &peer_lsa->sa, peer_lsa->len);
     217        /* Was it final ACK? then exit */
     218        if (finished && (opcode == TFTP_ACK))
     219            goto ret;
     220
     221        timeout = TFTP_NUM_RETRIES; /* re-initialize */
     222 recv_again:
     223        /* Receive packet */
     224        tv.tv_sec = TFTP_TIMEOUT;
     225        tv.tv_usec = 0;
     226        FD_ZERO(&rfds);
     227        FD_SET(socketfd, &rfds);
     228        switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
     229            unsigned from_port;
     230        case 1:
     231            from->len = peer_lsa->len;
     232            memset(&from->sa, 0, peer_lsa->len);
     233            len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
     234                        &from->sa, &from->len);
     235            if (len < 0) {
     236                bb_perror_msg("recvfrom");
     237                goto ret;
     238            }
     239            from_port = get_nport(&from->sa);
     240            if (port == org_port) {
     241                /* Our first query went to port 69
     242                 * but reply will come from different one.
     243                 * Remember and use this new port */
     244                port = from_port;
     245                set_nport(peer_lsa, from_port);
     246            }
     247            if (port != from_port)
     248                goto recv_again;
     249            goto process_pkt;
     250        case 0:
     251            timeout--;
     252            if (timeout == 0) {
     253                bb_error_msg("last timeout");
     254                goto ret;
     255            }
     256            bb_error_msg("last timeout" + 5);
     257            goto send_again; /* resend last sent pkt */
     258        default:
     259            bb_perror_msg("select");
     260            goto ret;
     261        }
     262 process_pkt:
     263        /* Process recv'ed packet */
     264        opcode = ntohs( ((uint16_t*)rbuf)[0] );
     265        recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
     266
     267#if ENABLE_DEBUG_TFTP
     268        fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
     269#endif
     270
     271        if (opcode == TFTP_ERROR) {
     272            static const char *const errcode_str[] = {
     273                "",
     274                "file not found",
     275                "access violation",
     276                "disk full",
     277                "illegal TFTP operation",
     278                "unknown transfer id",
     279                "file already exists",
     280                "no such user",
     281                "bad option"
     282            };
     283
     284            const char *msg = "";
     285
     286            if (rbuf[4] != '\0') {
     287                msg = &rbuf[4];
     288                rbuf[tftp_bufsize - 1] = '\0';
     289            } else if (recv_blk < ARRAY_SIZE(errcode_str)) {
     290                msg = errcode_str[recv_blk];
     291            }
     292            bb_error_msg("server error: (%u) %s", recv_blk, msg);
     293            goto ret;
     294        }
     295
     296#if ENABLE_FEATURE_TFTP_BLOCKSIZE
     297        if (want_option_ack) {
     298            want_option_ack = 0;
     299
     300            if (opcode == TFTP_OACK) {
     301                /* server seems to support options */
     302                char *res;
     303
     304                res = tftp_option_get(&rbuf[2], len - 2, "blksize");
     305                if (res) {
     306                    int blksize = xatoi_u(res);
     307                    if (!tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
     308                        /* send ERROR 8 to server... */
     309                        /* htons can be impossible to use in const initializer: */
     310                        /*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/
     311                        /* thus we open-code big-endian layout */
     312                        static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 };
     313                        xsendto(socketfd, error_8, 4, &peer_lsa->sa, peer_lsa->len);
     314                        bb_error_msg("server proposes bad blksize %d, exiting", blksize);
     315                        goto ret;
     316                    }
     317#if ENABLE_DEBUG_TFTP
     318                    fprintf(stderr, "using blksize %u\n",
     319                            blksize);
     320#endif
     321                    tftp_bufsize = blksize + 4;
     322                    /* Send ACK for OACK ("block" no: 0) */
     323                    block_nr = 0;
     324                    continue;
    228325                }
    229 
    230                 /* add "blksize" + number of blocks  */
    231                 memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN);
    232                 cp += OPTION_BLOCKSIZE_LEN;
    233                 cp += snprintf(cp, 6, "%d", len) + 1;
    234 
    235                 want_option_ack = 1;
    236             }
    237 #endif
    238         }
    239 
    240         /* add ack and data */
    241 
    242         if (((cmd & tftp_cmd_get) && (opcode == TFTP_ACK)) ||
    243             ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA))) {
    244 
    245             *((unsigned short *) cp) = htons(block_nr);
    246 
    247             cp += 2;
    248 
    249             block_nr++;
    250 
    251             if ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA)) {
    252                 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
    253 
    254                 if (len < 0) {
    255                     bb_perror_msg(bb_msg_read_error);
    256                     break;
    257                 }
    258 
    259                 if (len != (tftp_bufsize - 4)) {
    260                     finished++;
    261                 }
    262 
    263                 cp += len;
    264             }
    265         }
    266 
    267 
    268         /* send packet */
    269 
    270 
    271         timeout = TFTP_NUM_RETRIES; /* re-initialize */
    272         do {
    273 
    274             len = cp - buf;
    275 
    276 #ifdef CONFIG_DEBUG_TFTP
    277             fprintf(stderr, "sending %u bytes\n", len);
    278             for (cp = buf; cp < &buf[len]; cp++)
    279                 fprintf(stderr, "%02x ", (unsigned char) *cp);
    280             fprintf(stderr, "\n");
    281 #endif
    282             if (sendto(socketfd, buf, len, 0,
    283                        (struct sockaddr *) &sa, sizeof(sa)) < 0) {
    284                 bb_perror_msg("send");
    285                 len = -1;
    286                 break;
    287             }
    288 
    289 
    290             if (finished && (opcode == TFTP_ACK)) {
    291                 break;
    292             }
    293 
    294             /* receive packet */
    295 
    296             memset(&from, 0, sizeof(from));
    297             fromlen = sizeof(from);
    298 
    299             tv.tv_sec = TFTP_TIMEOUT;
    300             tv.tv_usec = 0;
    301 
    302             FD_ZERO(&rfds);
    303             FD_SET(socketfd, &rfds);
    304 
    305             switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
    306             case 1:
    307                 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
    308                                (struct sockaddr *) &from, &fromlen);
    309 
    310                 if (len < 0) {
    311                     bb_perror_msg("recvfrom");
    312                     break;
    313                 }
    314 
    315                 timeout = 0;
    316 
    317                 if (sa.sin_port == port) {
    318                     sa.sin_port = from.sin_port;
    319                 }
    320                 if (sa.sin_port == from.sin_port) {
    321                     break;
    322                 }
    323 
    324                 /* fall-through for bad packets! */
    325                 /* discard the packet - treat as timeout */
    326                 timeout = TFTP_NUM_RETRIES;
    327             case 0:
    328                 bb_error_msg("timeout");
    329 
    330                 timeout--;
    331                 if (timeout == 0) {
    332                     len = -1;
    333                     bb_error_msg("last timeout");
    334                 }
    335                 break;
    336             default:
    337                 bb_perror_msg("select");
    338                 len = -1;
    339             }
    340 
    341         } while (timeout && (len >= 0));
    342 
    343         if ((finished) || (len < 0)) {
    344             break;
    345         }
    346 
    347         /* process received packet */
    348 
    349         opcode = ntohs(*((unsigned short *) buf));
    350         tmp = ntohs(*((unsigned short *) &buf[2]));
    351 
    352 #ifdef CONFIG_DEBUG_TFTP
    353         fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
    354 #endif
    355 
    356         if (opcode == TFTP_ERROR) {
    357             const char *msg = NULL;
    358 
    359             if (buf[4] != '\0') {
    360                 msg = &buf[4];
    361                 buf[tftp_bufsize - 1] = '\0';
    362             } else if (tmp < (sizeof(tftp_bb_error_msg)
    363                               / sizeof(char *))) {
    364 
    365                 msg = tftp_bb_error_msg[tmp];
    366             }
    367 
    368             if (msg) {
    369                 bb_error_msg("server says: %s", msg);
    370             }
    371 
    372             break;
    373         }
    374 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
    375         if (want_option_ack) {
    376 
    377             want_option_ack = 0;
    378 
    379             if (opcode == TFTP_OACK) {
    380 
    381                 /* server seems to support options */
    382 
    383                 char *res;
    384 
    385                 res = tftp_option_get(&buf[2], len - 2, OPTION_BLOCKSIZE);
    386 
    387                 if (res) {
    388                     int blksize = atoi(res);
    389 
    390                     if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
    391 
    392                         if (cmd & tftp_cmd_put) {
    393                             opcode = TFTP_DATA;
    394                         } else {
    395                             opcode = TFTP_ACK;
    396                         }
    397 #ifdef CONFIG_DEBUG_TFTP
    398                         fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE,
    399                                 blksize);
    400 #endif
    401                         tftp_bufsize = blksize + 4;
    402                         block_nr = 0;
    403                         continue;
    404                     }
    405                 }
    406                 /* FIXME:
    407                  * we should send ERROR 8 */
    408                 bb_error_msg("bad server option");
    409                 break;
    410             }
    411 
    412             bb_error_msg("warning: blksize not supported by server"
    413                          " - reverting to 512");
    414 
     326                /* rfc2347:
     327                 * "An option not acknowledged by the server
     328                 *  must be ignored by the client and server
     329                 *  as if it were never requested." */
     330            }
     331
     332            bb_error_msg("blksize is not supported by server"
     333                        " - reverting to 512");
    415334            tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
    416335        }
    417336#endif
    418 
    419         if ((cmd & tftp_cmd_get) && (opcode == TFTP_DATA)) {
    420 
    421             if (tmp == block_nr) {
    422 
    423                 len = bb_full_write(localfd, &buf[4], len - 4);
    424 
     337        /* block_nr is already advanced to next block# we expect
     338         * to get / block# we are about to send next time */
     339
     340        if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
     341            if (recv_blk == block_nr) {
     342                len = full_write(localfd, &rbuf[4], len - 4);
    425343                if (len < 0) {
    426344                    bb_perror_msg(bb_msg_write_error);
    427                     break;
     345                    goto ret;
    428346                }
    429 
    430347                if (len != (tftp_bufsize - 4)) {
    431                     finished++;
     348                    finished = 1;
    432349                }
    433 
    434                 opcode = TFTP_ACK;
     350                continue; /* send ACK */
     351            }
     352            if (recv_blk == (block_nr - 1)) {
     353                /* Server lost our TFTP_ACK.  Resend it */
     354                block_nr = recv_blk;
    435355                continue;
    436356            }
    437             /* in case the last ack disappeared into the ether */
    438             if (tmp == (block_nr - 1)) {
    439                 --block_nr;
    440                 opcode = TFTP_ACK;
    441                 continue;
    442             } else if (tmp + 1 == block_nr) {
    443                 /* Server lost our TFTP_ACK.  Resend it */
    444                 block_nr = tmp;
    445                 opcode = TFTP_ACK;
    446                 continue;
    447             }
    448         }
    449 
    450         if ((cmd & tftp_cmd_put) && (opcode == TFTP_ACK)) {
    451 
    452             if (tmp == (unsigned short) (block_nr - 1)) {
    453                 if (finished) {
    454                     break;
    455                 }
    456 
    457                 opcode = TFTP_DATA;
    458                 continue;
    459             }
    460         }
    461     }
    462 
    463 #ifdef CONFIG_FEATURE_CLEAN_UP
    464     close(socketfd);
    465     free(buf);
    466 #endif
    467 
    468     return finished ? EXIT_SUCCESS : EXIT_FAILURE;
     357        }
     358
     359        if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
     360            /* did server ACK our last DATA pkt? */
     361            if (recv_blk == (uint16_t) (block_nr - 1)) {
     362                if (finished)
     363                    goto ret;
     364                continue; /* send next block */
     365            }
     366        }
     367        /* Awww... recv'd packet is not recognized! */
     368        goto recv_again;
     369        /* why recv_again? - rfc1123 says:
     370         * "The sender (i.e., the side originating the DATA packets)
     371         *  must never resend the current DATA packet on receipt
     372         *  of a duplicate ACK".
     373         * DATA pkts are resent ONLY on timeout.
     374         * Thus "goto send_again" will ba a bad mistake above.
     375         * See:
     376         * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
     377         */
     378    }
     379 ret:
     380    if (ENABLE_FEATURE_CLEAN_UP) {
     381        close(socketfd);
     382        free(xbuf);
     383        free(rbuf);
     384    }
     385    return finished == 0; /* returns 1 on failure */
    469386}
    470387
     388int tftp_main(int argc, char **argv);
    471389int tftp_main(int argc, char **argv)
    472390{
    473     struct hostent *host = NULL;
     391    len_and_sockaddr *peer_lsa;
    474392    const char *localfile = NULL;
    475393    const char *remotefile = NULL;
     394#if ENABLE_FEATURE_TFTP_BLOCKSIZE
     395    const char *sblocksize = NULL;
     396#endif
    476397    int port;
    477     int cmd = 0;
     398    USE_GETPUT(int cmd;)
    478399    int fd = -1;
    479400    int flags = 0;
     
    481402    int blocksize = TFTP_BLOCKSIZE_DEFAULT;
    482403
    483     /* figure out what to pass to getopt */
    484 
    485 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
    486     char *sblocksize = NULL;
    487 
    488 #define BS "b:"
    489 #define BS_ARG , &sblocksize
    490 #else
    491 #define BS
    492 #define BS_ARG
    493 #endif
    494 
    495 #ifdef CONFIG_FEATURE_TFTP_GET
    496 #define GET "g"
    497 #define GET_COMPL ":g"
    498 #else
    499 #define GET
    500 #define GET_COMPL
    501 #endif
    502 
    503 #ifdef CONFIG_FEATURE_TFTP_PUT
    504 #define PUT "p"
    505 #define PUT_COMPL ":p"
    506 #else
    507 #define PUT
    508 #define PUT_COMPL
    509 #endif
    510 
    511 #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
    512     bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
    513 #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
    514     bb_opt_complementally = GET_COMPL PUT_COMPL;
    515 #endif
    516 
    517 
    518     cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
    519                             &localfile, &remotefile BS_ARG);
    520 
    521     cmd &= (tftp_cmd_get | tftp_cmd_put);
    522 #ifdef CONFIG_FEATURE_TFTP_GET
    523     if (cmd == tftp_cmd_get)
     404    /* -p or -g is mandatory, and they are mutually exclusive */
     405    opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
     406            USE_GETPUT("?g--p:p--g");
     407
     408    USE_GETPUT(cmd =) getopt32(argv,
     409            USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
     410                "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
     411            &localfile, &remotefile
     412            USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
     413    argv += optind;
     414
     415    flags = O_RDONLY;
     416    if (CMD_GET(cmd))
    524417        flags = O_WRONLY | O_CREAT | O_TRUNC;
    525 #endif
    526 #ifdef CONFIG_FEATURE_TFTP_PUT
    527     if (cmd == tftp_cmd_put)
    528         flags = O_RDONLY;
    529 #endif
    530 
    531 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
     418
     419#if ENABLE_FEATURE_TFTP_BLOCKSIZE
    532420    if (sblocksize) {
    533         blocksize = atoi(sblocksize);
     421        blocksize = xatoi_u(sblocksize);
    534422        if (!tftp_blocksize_check(blocksize, 0)) {
    535423            return EXIT_FAILURE;
     
    538426#endif
    539427
    540     if (localfile == NULL)
     428    if (!localfile)
    541429        localfile = remotefile;
    542     if (remotefile == NULL)
     430    if (!remotefile)
    543431        remotefile = localfile;
    544     if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
     432    /* Error if filename or host is not known */
     433    if (!remotefile || !argv[0])
    545434        bb_show_usage();
    546435
    547     if (localfile == NULL || strcmp(localfile, "-") == 0) {
    548         fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO;
    549     } else {
    550         fd = open(localfile, flags, 0644); /* fail below */
    551     }
    552     if (fd < 0) {
    553         bb_perror_msg_and_die("local file");
    554     }
    555 
    556     host = xgethostbyname(argv[optind]);
    557     port = bb_lookup_port(argv[optind + 1], "udp", 69);
    558 
    559 #ifdef CONFIG_DEBUG_TFTP
    560     fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
    561             "localfile \"%s\".\n",
    562             inet_ntoa(*((struct in_addr *) host->h_addr)),
     436    fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
     437    if (!LONE_DASH(localfile)) {
     438        fd = xopen(localfile, flags);
     439    }
     440
     441    port = bb_lookup_port(argv[1], "udp", 69);
     442    peer_lsa = xhost2sockaddr(argv[0], port);
     443
     444#if ENABLE_DEBUG_TFTP
     445    fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n",
     446            xmalloc_sockaddr2dotted(&peer_lsa->sa),
    563447            remotefile, localfile);
    564448#endif
    565449
    566     result = tftp(cmd, host, remotefile, fd, port, blocksize);
    567 
    568     if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
    569         if (ENABLE_FEATURE_CLEAN_UP)
    570             close(fd);
    571         if (cmd == tftp_cmd_get && result != EXIT_SUCCESS)
    572             unlink(localfile);
    573     }
    574     return (result);
     450    result = tftp( USE_GETPUT(cmd,) peer_lsa, remotefile, fd, port, blocksize);
     451
     452    if (ENABLE_FEATURE_CLEAN_UP)
     453        close(fd);
     454    if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && CMD_GET(cmd)) {
     455        unlink(localfile);
     456    }
     457    return result;
    575458}
     459
     460#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
Note: See TracChangeset for help on using the changeset viewer.