Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

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

    r1765 r2725  
    11/* vi: set sw=4 ts=4: */
    2 /* -------------------------------------------------------------------------
    3  * tftp.c
    4  *
    5  * A simple tftp client for busybox.
     2/*
     3 * A simple tftp client/server for busybox.
    64 * Tries to follow RFC1350.
    75 * Only "octet" mode supported.
     
    1715 * utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
    1816 *
    19  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
    20  * ------------------------------------------------------------------------- */
    21 
     17 * tftpd added by Denys Vlasenko & Vladimir Dronnikov
     18 *
     19 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
     20 */
    2221#include "libbb.h"
    2322
    24 
    2523#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
    2624
    27 #define TFTP_BLOCKSIZE_DEFAULT 512  /* according to RFC 1350, don't change */
    28 #define TFTP_TIMEOUT 5  /* seconds */
    29 #define TFTP_NUM_RETRIES 5 /* number of retries */
     25#define TFTP_BLKSIZE_DEFAULT       512  /* according to RFC 1350, don't change */
     26#define TFTP_BLKSIZE_DEFAULT_STR "512"
     27/* Was 50 ms but users asked to bump it up a bit */
     28#define TFTP_TIMEOUT_MS            100
     29#define TFTP_MAXTIMEOUT_MS        2000
     30#define TFTP_NUM_RETRIES            12  /* number of backed-off retries */
    3031
    3132/* opcodes we support */
     
    3738#define TFTP_OACK  6
    3839
     40/* error codes sent over network (we use only 0, 1, 3 and 8) */
     41/* generic (error message is included in the packet) */
     42#define ERR_UNSPEC   0
     43#define ERR_NOFILE   1
     44#define ERR_ACCESS   2
     45/* disk full or allocation exceeded */
     46#define ERR_WRITE    3
     47#define ERR_OP       4
     48#define ERR_BAD_ID   5
     49#define ERR_EXIST    6
     50#define ERR_BAD_USER 7
     51#define ERR_BAD_OPT  8
     52
     53/* masks coming from getopt32 */
     54enum {
     55    TFTP_OPT_GET = (1 << 0),
     56    TFTP_OPT_PUT = (1 << 1),
     57    /* pseudo option: if set, it's tftpd */
     58    TFTPD_OPT = (1 << 7) * ENABLE_TFTPD,
     59    TFTPD_OPT_r = (1 << 8) * ENABLE_TFTPD,
     60    TFTPD_OPT_c = (1 << 9) * ENABLE_TFTPD,
     61    TFTPD_OPT_u = (1 << 10) * ENABLE_TFTPD,
     62};
     63
    3964#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
    40 #define USE_GETPUT(...)
     65#define IF_GETPUT(...)
    4166#define CMD_GET(cmd) 1
    4267#define CMD_PUT(cmd) 0
    4368#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
    44 #define USE_GETPUT(...)
     69#define IF_GETPUT(...)
    4570#define CMD_GET(cmd) 0
    4671#define CMD_PUT(cmd) 1
    4772#else
    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)
     73#define IF_GETPUT(...) __VA_ARGS__
     74#define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET)
     75#define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT)
    5276#endif
    5377/* NB: in the code below
     
    5680
    5781
     82struct globals {
     83    /* u16 TFTP_ERROR; u16 reason; both network-endian, then error text: */
     84    uint8_t error_pkt[4 + 32];
     85    char *user_opt;
     86    /* used in tftpd_main(), a bit big for stack: */
     87    char block_buf[TFTP_BLKSIZE_DEFAULT];
     88#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
     89    off_t pos;
     90    off_t size;
     91    const char *file;
     92    bb_progress_t pmt;
     93#endif
     94} FIX_ALIASING;
     95#define G (*(struct globals*)&bb_common_bufsiz1)
     96struct BUG_G_too_big {
     97    char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
     98};
     99#define block_buf        (G.block_buf   )
     100#define user_opt         (G.user_opt    )
     101#define error_pkt        (G.error_pkt   )
     102#define INIT_G() do { } while (0)
     103
     104#define error_pkt_reason (error_pkt[3])
     105#define error_pkt_str    (error_pkt + 4)
     106
     107#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
     108static void tftp_progress_update(void)
     109{
     110    bb_progress_update(&G.pmt, G.file, 0, G.pos, G.size);
     111}
     112static void tftp_progress_init(void)
     113{
     114    bb_progress_init(&G.pmt);
     115    tftp_progress_update();
     116}
     117static void tftp_progress_done(void)
     118{
     119    if (G.pmt.inited) {
     120        tftp_progress_update();
     121        bb_putchar_stderr('\n');
     122        G.pmt.inited = 0;
     123    }
     124}
     125#else
     126# define tftp_progress_init() ((void)0)
     127# define tftp_progress_done() ((void)0)
     128#endif
     129
    58130#if ENABLE_FEATURE_TFTP_BLOCKSIZE
    59131
    60 static int tftp_blocksize_check(int blocksize, int bufsize)
     132static int tftp_blksize_check(const char *blksize_str, int maxsize)
    61133{
    62     /* Check if the blocksize is valid:
     134    /* Check if the blksize is valid:
    63135     * RFC2348 says between 8 and 65464,
    64136     * but our implementation makes it impossible
    65      * to use blocksizes smaller than 22 octets.
    66      */
    67 
    68     if ((bufsize && (blocksize > bufsize))
    69      || (blocksize < 8) || (blocksize > 65564)
     137     * to use blksizes smaller than 22 octets. */
     138    unsigned blksize = bb_strtou(blksize_str, NULL, 10);
     139    if (errno
     140     || (blksize < 24) || (blksize > maxsize)
    70141    ) {
    71         bb_error_msg("bad blocksize");
    72         return 0;
    73     }
    74 
    75     return blocksize;
     142        bb_error_msg("bad blocksize '%s'", blksize_str);
     143        return -1;
     144    }
     145# if ENABLE_TFTP_DEBUG
     146    bb_error_msg("using blksize %u", blksize);
     147# endif
     148    return blksize;
    76149}
    77150
    78 static char *tftp_option_get(char *buf, int len, const char *option)
     151static char *tftp_get_option(const char *option, char *buf, int len)
    79152{
    80153    int opt_val = 0;
     
    82155    int k;
    83156
     157    /* buf points to:
     158     * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */
     159
    84160    while (len > 0) {
    85         /* Make sure the options are terminated correctly */
     161        /* Make sure options are terminated correctly */
    86162        for (k = 0; k < len; k++) {
    87163            if (buf[k] == '\0') {
     
    91167        return NULL;
    92168 nul_found:
    93         if (opt_val == 0) {
     169        if (opt_val == 0) { /* it's "name" part */
    94170            if (strcasecmp(buf, option) == 0) {
    95171                opt_found = 1;
     
    110186#endif
    111187
    112 static int tftp( USE_GETPUT(const int cmd,)
     188static int tftp_protocol(
     189        /* NULL if tftp, !NULL if tftpd: */
     190        len_and_sockaddr *our_lsa,
    113191        len_and_sockaddr *peer_lsa,
    114         const char *remotefile, const int localfd,
    115         unsigned port, int tftp_bufsize)
     192        const char *local_file
     193        IF_TFTP(, const char *remote_file)
     194#if !ENABLE_TFTP
     195# define remote_file NULL
     196#endif
     197        /* 1 for tftp; 1/0 for tftpd depending whether client asked about it: */
     198        IF_FEATURE_TFTP_BLOCKSIZE(, int want_transfer_size)
     199        IF_FEATURE_TFTP_BLOCKSIZE(, int blksize))
    116200{
    117     struct timeval tv;
    118     fd_set rfds;
    119     int socketfd;
     201#if !ENABLE_FEATURE_TFTP_BLOCKSIZE
     202    enum { blksize = TFTP_BLKSIZE_DEFAULT };
     203#endif
     204
     205    struct pollfd pfd[1];
     206#define socket_fd (pfd[0].fd)
    120207    int len;
    121208    int send_len;
    122     USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
     209    IF_FEATURE_TFTP_BLOCKSIZE(smallint expect_OACK = 0;)
    123210    smallint finished = 0;
    124211    uint16_t opcode;
    125     uint16_t block_nr = 1;
     212    uint16_t block_nr;
    126213    uint16_t recv_blk;
    127     int timeout = TFTP_NUM_RETRIES;
     214    int open_mode, local_fd;
     215    int retries, waittime_ms;
     216    int io_bufsize = blksize + 4;
    128217    char *cp;
    129 
    130     unsigned org_port;
    131     len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
    132 
    133218    /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
    134      * size varies meaning BUFFERS_GO_ON_STACK would fail */
    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);
    143 
    144     /* build opcode */
    145     opcode = TFTP_WRQ;
    146     if (CMD_GET(cmd)) {
    147         opcode = TFTP_RRQ;
    148     }
     219     * size varies meaning BUFFERS_GO_ON_STACK would fail.
     220     *
     221     * We must keep the transmit and receive buffers separate
     222     * in case we rcv a garbage pkt - we need to rexmit the last pkt.
     223     */
     224    char *xbuf = xmalloc(io_bufsize);
     225    char *rbuf = xmalloc(io_bufsize);
     226
     227    socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);
     228    setsockopt_reuseaddr(socket_fd);
     229
     230    if (!ENABLE_TFTP || our_lsa) { /* tftpd */
     231        /* Create a socket which is:
     232         * 1. bound to IP:port peer sent 1st datagram to,
     233         * 2. connected to peer's IP:port
     234         * This way we will answer from the IP:port peer
     235         * expects, will not get any other packets on
     236         * the socket, and also plain read/write will work. */
     237        xbind(socket_fd, &our_lsa->u.sa, our_lsa->len);
     238        xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
     239
     240        /* Is there an error already? Send pkt and bail out */
     241        if (error_pkt_reason || error_pkt_str[0])
     242            goto send_err_pkt;
     243
     244        if (user_opt) {
     245            struct passwd *pw = xgetpwnam(user_opt);
     246            change_identity(pw); /* initgroups, setgid, setuid */
     247        }
     248    }
     249
     250    /* Prepare open mode */
     251    if (CMD_PUT(option_mask32)) {
     252        open_mode = O_RDONLY;
     253    } else {
     254        open_mode = O_WRONLY | O_TRUNC | O_CREAT;
     255#if ENABLE_TFTPD
     256        if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) {
     257            /* tftpd without -c */
     258            open_mode = O_WRONLY | O_TRUNC;
     259        }
     260#endif
     261    }
     262
     263    /* Examples of network traffic.
     264     * Note two cases when ACKs with block# of 0 are sent.
     265     *
     266     * Download without options:
     267     * tftp -> "\0\1FILENAME\0octet\0"
     268     *         "\0\3\0\1FILEDATA..." <- tftpd
     269     * tftp -> "\0\4\0\1"
     270     * ...
     271     * Download with option of blksize 16384:
     272     * tftp -> "\0\1FILENAME\0octet\0blksize\00016384\0"
     273     *         "\0\6blksize\00016384\0" <- tftpd
     274     * tftp -> "\0\4\0\0"
     275     *         "\0\3\0\1FILEDATA..." <- tftpd
     276     * tftp -> "\0\4\0\1"
     277     * ...
     278     * Upload without options:
     279     * tftp -> "\0\2FILENAME\0octet\0"
     280     *         "\0\4\0\0" <- tftpd
     281     * tftp -> "\0\3\0\1FILEDATA..."
     282     *         "\0\4\0\1" <- tftpd
     283     * ...
     284     * Upload with option of blksize 16384:
     285     * tftp -> "\0\2FILENAME\0octet\0blksize\00016384\0"
     286     *         "\0\6blksize\00016384\0" <- tftpd
     287     * tftp -> "\0\3\0\1FILEDATA..."
     288     *         "\0\4\0\1" <- tftpd
     289     * ...
     290     */
     291    block_nr = 1;
    149292    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 
     293
     294    if (!ENABLE_TFTP || our_lsa) { /* tftpd */
     295        /* Open file (must be after changing user) */
     296        local_fd = open(local_file, open_mode, 0666);
     297        if (local_fd < 0) {
     298            error_pkt_reason = ERR_NOFILE;
     299            strcpy((char*)error_pkt_str, "can't open file");
     300            goto send_err_pkt;
     301        }
     302/* gcc 4.3.1 would NOT optimize it out as it should! */
    163303#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")) {
     304        if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) {
     305            /* Create and send OACK packet. */
     306            /* For the download case, block_nr is still 1 -
     307             * we expect 1st ACK from peer to be for (block_nr-1),
     308             * that is, for "block 0" which is our OACK pkt */
     309            opcode = TFTP_OACK;
     310            goto add_blksize_opt;
     311        }
     312#endif
     313        if (CMD_GET(option_mask32)) {
     314            /* It's upload and we don't send OACK.
     315             * We must ACK 1st packet (with filename)
     316             * as if it is "block 0" */
     317            block_nr = 0;
     318        }
     319
     320    } else { /* tftp */
     321        /* Open file (must be after changing user) */
     322        local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;
     323        if (NOT_LONE_DASH(local_file))
     324            local_fd = xopen(local_file, open_mode);
     325/* Removing #if, or using if() statement instead of #if may lead to
     326 * "warning: null argument where non-null required": */
     327#if ENABLE_TFTP
     328        /* tftp */
     329
     330        /* We can't (and don't really need to) bind the socket:
     331         * we don't know from which local IP datagrams will be sent,
     332         * but kernel will pick the same IP every time (unless routing
     333         * table is changed), thus peer will see dgrams consistently
     334         * coming from the same IP.
     335         * We would like to connect the socket, but since peer's
     336         * UDP code can be less perfect than ours, _peer's_ IP:port
     337         * in replies may differ from IP:port we used to send
     338         * our first packet. We can connect() only when we get
     339         * first reply. */
     340
     341        /* build opcode */
     342        opcode = TFTP_WRQ;
     343        if (CMD_GET(option_mask32)) {
     344            opcode = TFTP_RRQ;
     345        }
     346        /* add filename and mode */
     347        /* fill in packet if the filename fits into xbuf */
     348        len = strlen(remote_file) + 1;
     349        if (2 + len + sizeof("octet") >= io_bufsize) {
    168350            bb_error_msg("remote filename is too long");
    169351            goto ret;
    170352        }
    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;
     353        strcpy(cp, remote_file);
     354        cp += len;
     355        /* add "mode" part of the packet */
     356        strcpy(cp, "octet");
     357        cp += sizeof("octet");
     358
     359# if ENABLE_FEATURE_TFTP_BLOCKSIZE
     360        if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size)
     361            goto send_pkt;
     362
     363        /* Need to add option to pkt */
     364        if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN tsize ") + sizeof(off_t)*3) {
     365            bb_error_msg("remote filename is too long");
     366            goto ret;
     367        }
     368        expect_OACK = 1;
     369# endif
     370#endif /* ENABLE_TFTP */
     371
     372#if ENABLE_FEATURE_TFTP_BLOCKSIZE
     373 add_blksize_opt:
     374        if (blksize != TFTP_BLKSIZE_DEFAULT) {
     375            /* add "blksize", <nul>, blksize, <nul> */
     376            strcpy(cp, "blksize");
     377            cp += sizeof("blksize");
     378            cp += snprintf(cp, 6, "%d", blksize) + 1;
     379        }
     380        if (want_transfer_size) {
     381            /* add "tsize", <nul>, size, <nul> (see RFC2349) */
     382            /* if tftp and downloading, we send "0" (since we opened local_fd with O_TRUNC)
     383             * and this makes server to send "tsize" option with the size */
     384            /* if tftp and uploading, we send file size (maybe dont, to not confuse old servers???) */
     385            /* if tftpd and downloading, we are answering to client's request */
     386            /* if tftpd and uploading: !want_transfer_size, this code is not executed */
     387            struct stat st;
     388            strcpy(cp, "tsize");
     389            cp += sizeof("tsize");
     390            st.st_size = 0;
     391            fstat(local_fd, &st);
     392            cp += sprintf(cp, "%"OFF_FMT"u", (off_t)st.st_size) + 1;
     393# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
     394            /* Save for progress bar. If 0 (tftp downloading),
     395             * we look at server's reply later */
     396            G.size = st.st_size;
     397            if (remote_file && st.st_size)
     398                tftp_progress_init();
     399# endif
     400        }
     401#endif
     402        /* First packet is built, so skip packet generation */
     403        goto send_pkt;
     404    }
    180405
    181406    /* Using mostly goto's - continue/break will be less clear
    182407     * in where we actually jump to */
    183 
    184408    while (1) {
    185409        /* Build ACK or DATA */
     
    189413        block_nr++;
    190414        opcode = TFTP_ACK;
    191         if (CMD_PUT(cmd)) {
     415        if (CMD_PUT(option_mask32)) {
    192416            opcode = TFTP_DATA;
    193             len = full_read(localfd, cp, tftp_bufsize - 4);
     417            len = full_read(local_fd, cp, blksize);
    194418            if (len < 0) {
    195                 bb_perror_msg(bb_msg_read_error);
    196                 goto ret;
    197             }
    198             if (len != (tftp_bufsize - 4)) {
     419                goto send_read_err_pkt;
     420            }
     421            if (len != blksize) {
    199422                finished = 1;
    200423            }
     
    207430        /* NB: send_len value is preserved in code below
    208431         * for potential resend */
     432
     433        retries = TFTP_NUM_RETRIES;  /* re-initialize */
     434        waittime_ms = TFTP_TIMEOUT_MS;
     435
    209436 send_again:
    210 #if ENABLE_DEBUG_TFTP
     437#if ENABLE_TFTP_DEBUG
    211438        fprintf(stderr, "sending %u bytes\n", send_len);
    212439        for (cp = xbuf; cp < &xbuf[send_len]; cp++)
     
    214441        fprintf(stderr, "\n");
    215442#endif
    216         xsendto(socketfd, xbuf, send_len, &peer_lsa->sa, peer_lsa->len);
     443        xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
     444
     445#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
     446        if (ENABLE_TFTP && remote_file) /* tftp */
     447            G.pos = (block_nr - 1) * (uoff_t)blksize;
     448        if (G.pmt.inited)
     449            tftp_progress_update();
     450#endif
    217451        /* Was it final ACK? then exit */
    218452        if (finished && (opcode == TFTP_ACK))
    219453            goto ret;
    220454
    221         timeout = TFTP_NUM_RETRIES; /* re-initialize */
    222455 recv_again:
    223456        /* 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;
     457        /*pfd[0].fd = socket_fd;*/
     458        pfd[0].events = POLLIN;
     459        switch (safe_poll(pfd, 1, waittime_ms)) {
     460        default:
     461            /*bb_perror_msg("poll"); - done in safe_poll */
     462            goto ret;
     463        case 0:
     464            retries--;
     465            if (retries == 0) {
     466                tftp_progress_done();
     467                bb_error_msg("timeout");
     468                goto ret; /* no err packet sent */
     469            }
     470
     471            /* exponential backoff with limit */
     472            waittime_ms += waittime_ms/2;
     473            if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
     474                waittime_ms = TFTP_MAXTIMEOUT_MS;
     475            }
     476
     477            goto send_again; /* resend last sent pkt */
    230478        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);
     479            if (!our_lsa) {
     480                /* tftp (not tftpd!) receiving 1st packet */
     481                our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */
     482                len = recvfrom(socket_fd, rbuf, io_bufsize, 0,
     483                        &peer_lsa->u.sa, &peer_lsa->len);
     484                /* Our first dgram went to port 69
     485                 * but reply may come from different one.
     486                 * Remember and use this new port (and IP) */
     487                if (len >= 0)
     488                    xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
     489            } else {
     490                /* tftpd, or not the very first packet:
     491                 * socket is connect()ed, can just read from it. */
     492                /* Don't full_read()!
     493                 * This is not TCP, one read == one pkt! */
     494                len = safe_read(socket_fd, rbuf, io_bufsize);
     495            }
    235496            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)
     497                goto send_read_err_pkt;
     498            }
     499            if (len < 4) { /* too small? */
    248500                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:
     501            }
     502        }
     503
    263504        /* Process recv'ed packet */
    264505        opcode = ntohs( ((uint16_t*)rbuf)[0] );
    265506        recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
    266 
    267 #if ENABLE_DEBUG_TFTP
     507#if ENABLE_TFTP_DEBUG
    268508        fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
    269509#endif
    270 
    271510        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             };
     511            static const char errcode_str[] ALIGN1 =
     512                "\0"
     513                "file not found\0"
     514                "access violation\0"
     515                "disk full\0"
     516                "bad operation\0"
     517                "unknown transfer id\0"
     518                "file already exists\0"
     519                "no such user\0"
     520                "bad option";
    283521
    284522            const char *msg = "";
    285523
    286             if (rbuf[4] != '\0') {
     524            if (len > 4 && rbuf[4] != '\0') {
    287525                msg = &rbuf[4];
    288                 rbuf[tftp_bufsize - 1] = '\0';
    289             } else if (recv_blk < ARRAY_SIZE(errcode_str)) {
    290                 msg = errcode_str[recv_blk];
     526                rbuf[io_bufsize - 1] = '\0'; /* paranoia */
     527            } else if (recv_blk <= 8) {
     528                msg = nth_string(errcode_str, recv_blk);
    291529            }
    292530            bb_error_msg("server error: (%u) %s", recv_blk, msg);
     
    295533
    296534#if ENABLE_FEATURE_TFTP_BLOCKSIZE
    297         if (want_option_ack) {
    298             want_option_ack = 0;
    299 
     535        if (expect_OACK) {
     536            expect_OACK = 0;
    300537            if (opcode == TFTP_OACK) {
    301538                /* server seems to support options */
    302539                char *res;
    303540
    304                 res = tftp_option_get(&rbuf[2], len - 2, "blksize");
     541                res = tftp_get_option("blksize", &rbuf[2], len - 2);
    305542                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;
     543                    blksize = tftp_blksize_check(res, blksize);
     544                    if (blksize < 0) {
     545                        error_pkt_reason = ERR_BAD_OPT;
     546                        goto send_err_pkt;
    316547                    }
    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) */
     548                    io_bufsize = blksize + 4;
     549                }
     550# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
     551                if (remote_file && G.size == 0) { /* if we don't know it yet */
     552                    res = tftp_get_option("tsize", &rbuf[2], len - 2);
     553                    if (res) {
     554                        G.size = bb_strtoull(res, NULL, 10);
     555                        if (G.size)
     556                            tftp_progress_init();
     557                    }
     558                }
     559# endif
     560                if (CMD_GET(option_mask32)) {
     561                    /* We'll send ACK for OACK,
     562                     * such ACK has "block no" of 0 */
    323563                    block_nr = 0;
    324                     continue;
    325564                }
    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");
    334             tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
     565                continue;
     566            }
     567            /* rfc2347:
     568             * "An option not acknowledged by the server
     569             * must be ignored by the client and server
     570             * as if it were never requested." */
     571            if (blksize != TFTP_BLKSIZE_DEFAULT)
     572                bb_error_msg("falling back to blocksize "TFTP_BLKSIZE_DEFAULT_STR);
     573            blksize = TFTP_BLKSIZE_DEFAULT;
     574            io_bufsize = TFTP_BLKSIZE_DEFAULT + 4;
    335575        }
    336576#endif
     
    338578         * to get / block# we are about to send next time */
    339579
    340         if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
     580        if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) {
    341581            if (recv_blk == block_nr) {
    342                 len = full_write(localfd, &rbuf[4], len - 4);
    343                 if (len < 0) {
    344                     bb_perror_msg(bb_msg_write_error);
    345                     goto ret;
     582                int sz = full_write(local_fd, &rbuf[4], len - 4);
     583                if (sz != len - 4) {
     584                    strcpy((char*)error_pkt_str, bb_msg_write_error);
     585                    error_pkt_reason = ERR_WRITE;
     586                    goto send_err_pkt;
    346587                }
    347                 if (len != (tftp_bufsize - 4)) {
     588                if (sz != blksize) {
    348589                    finished = 1;
    349590                }
    350591                continue; /* send ACK */
    351592            }
     593/* Disabled to cope with servers with Sorcerer's Apprentice Syndrome */
     594#if 0
    352595            if (recv_blk == (block_nr - 1)) {
    353596                /* Server lost our TFTP_ACK.  Resend it */
     
    355598                continue;
    356599            }
    357         }
    358 
    359         if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
    360             /* did server ACK our last DATA pkt? */
     600#endif
     601        }
     602
     603        if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) {
     604            /* did peer ACK our last DATA pkt? */
    361605            if (recv_blk == (uint16_t) (block_nr - 1)) {
    362606                if (finished)
     
    376620         * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
    377621         */
    378     }
     622    } /* end of "while (1)" */
    379623 ret:
    380624    if (ENABLE_FEATURE_CLEAN_UP) {
    381         close(socketfd);
     625        close(local_fd);
     626        close(socket_fd);
    382627        free(xbuf);
    383628        free(rbuf);
    384629    }
    385630    return finished == 0; /* returns 1 on failure */
     631
     632 send_read_err_pkt:
     633    strcpy((char*)error_pkt_str, bb_msg_read_error);
     634 send_err_pkt:
     635    if (error_pkt_str[0])
     636        bb_error_msg("%s", (char*)error_pkt_str);
     637    error_pkt[1] = TFTP_ERROR;
     638    xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str),
     639            &peer_lsa->u.sa, peer_lsa->len);
     640    return EXIT_FAILURE;
     641#undef remote_file
    386642}
    387643
    388 int tftp_main(int argc, char **argv);
    389 int tftp_main(int argc, char **argv)
     644#if ENABLE_TFTP
     645
     646int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     647int tftp_main(int argc UNUSED_PARAM, char **argv)
    390648{
    391649    len_and_sockaddr *peer_lsa;
    392     const char *localfile = NULL;
    393     const char *remotefile = NULL;
    394 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
    395     const char *sblocksize = NULL;
    396 #endif
     650    const char *local_file = NULL;
     651    const char *remote_file = NULL;
     652# if ENABLE_FEATURE_TFTP_BLOCKSIZE
     653    const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR;
     654    int blksize;
     655# endif
     656    int result;
    397657    int port;
    398     USE_GETPUT(int cmd;)
    399     int fd = -1;
    400     int flags = 0;
    401     int result;
    402     int blocksize = TFTP_BLOCKSIZE_DEFAULT;
     658    IF_GETPUT(int opt;)
     659
     660    INIT_G();
    403661
    404662    /* -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));
     663    opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:")
     664            IF_GETPUT("g--p:p--g:");
     665
     666    IF_GETPUT(opt =) getopt32(argv,
     667            IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p")
     668                "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:"),
     669            &local_file, &remote_file
     670            IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str));
    413671    argv += optind;
    414672
    415     flags = O_RDONLY;
    416     if (CMD_GET(cmd))
    417         flags = O_WRONLY | O_CREAT | O_TRUNC;
    418 
    419 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
    420     if (sblocksize) {
    421         blocksize = xatoi_u(sblocksize);
    422         if (!tftp_blocksize_check(blocksize, 0)) {
    423             return EXIT_FAILURE;
    424         }
    425     }
    426 #endif
    427 
    428     if (!localfile)
    429         localfile = remotefile;
    430     if (!remotefile)
    431         remotefile = localfile;
     673# if ENABLE_FEATURE_TFTP_BLOCKSIZE
     674    /* Check if the blksize is valid:
     675     * RFC2348 says between 8 and 65464 */
     676    blksize = tftp_blksize_check(blksize_str, 65564);
     677    if (blksize < 0) {
     678        //bb_error_msg("bad block size");
     679        return EXIT_FAILURE;
     680    }
     681# endif
     682
     683    if (remote_file) {
     684        if (!local_file) {
     685            const char *slash = strrchr(remote_file, '/');
     686            local_file = slash ? slash + 1 : remote_file;
     687        }
     688    } else {
     689        remote_file = local_file;
     690    }
     691
    432692    /* Error if filename or host is not known */
    433     if (!remotefile || !argv[0])
     693    if (!remote_file || !argv[0])
    434694        bb_show_usage();
    435 
    436     fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
    437     if (!LONE_DASH(localfile)) {
    438         fd = xopen(localfile, flags);
    439     }
    440695
    441696    port = bb_lookup_port(argv[1], "udp", 69);
    442697    peer_lsa = xhost2sockaddr(argv[0], port);
    443698
    444 #if ENABLE_DEBUG_TFTP
    445     fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n",
    446             xmalloc_sockaddr2dotted(&peer_lsa->sa),
    447             remotefile, localfile);
    448 #endif
    449 
    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);
     699# if ENABLE_TFTP_DEBUG
     700    fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",
     701            xmalloc_sockaddr2dotted(&peer_lsa->u.sa),
     702            remote_file, local_file);
     703# endif
     704
     705# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
     706    G.file = remote_file;
     707# endif
     708    result = tftp_protocol(
     709        NULL /*our_lsa*/, peer_lsa,
     710        local_file, remote_file
     711        IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */)
     712        IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
     713    );
     714    tftp_progress_done();
     715
     716    if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) {
     717        unlink(local_file);
    456718    }
    457719    return result;
    458720}
    459721
     722#endif /* ENABLE_TFTP */
     723
     724#if ENABLE_TFTPD
     725int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     726int tftpd_main(int argc UNUSED_PARAM, char **argv)
     727{
     728    len_and_sockaddr *our_lsa;
     729    len_and_sockaddr *peer_lsa;
     730    char *local_file, *mode;
     731    const char *error_msg;
     732    int opt, result, opcode;
     733    IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
     734    IF_FEATURE_TFTP_BLOCKSIZE(int want_transfer_size = 0;)
     735
     736    INIT_G();
     737
     738    our_lsa = get_sock_lsa(STDIN_FILENO);
     739    if (!our_lsa) {
     740        /* This is confusing:
     741         *bb_error_msg_and_die("stdin is not a socket");
     742         * Better: */
     743        bb_show_usage();
     744        /* Help text says that tftpd must be used as inetd service,
     745         * which is by far the most usual cause of get_sock_lsa
     746         * failure */
     747    }
     748    peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len);
     749    peer_lsa->len = our_lsa->len;
     750
     751    /* Shifting to not collide with TFTP_OPTs */
     752    opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:", &user_opt) << 8);
     753    argv += optind;
     754    if (argv[0])
     755        xchdir(argv[0]);
     756
     757    result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf),
     758            0 /* flags */,
     759            &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len);
     760
     761    error_msg = "malformed packet";
     762    opcode = ntohs(*(uint16_t*)block_buf);
     763    if (result < 4 || result >= sizeof(block_buf)
     764     || block_buf[result-1] != '\0'
     765     || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */
     766         IF_GETPUT(&&)
     767         IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */
     768        )
     769    ) {
     770        goto err;
     771    }
     772    local_file = block_buf + 2;
     773    if (local_file[0] == '.' || strstr(local_file, "/.")) {
     774        error_msg = "dot in file name";
     775        goto err;
     776    }
     777    mode = local_file + strlen(local_file) + 1;
     778    if (mode >= block_buf + result || strcmp(mode, "octet") != 0) {
     779        goto err;
     780    }
     781# if ENABLE_FEATURE_TFTP_BLOCKSIZE
     782    {
     783        char *res;
     784        char *opt_str = mode + sizeof("octet");
     785        int opt_len = block_buf + result - opt_str;
     786        if (opt_len > 0) {
     787            res = tftp_get_option("blksize", opt_str, opt_len);
     788            if (res) {
     789                blksize = tftp_blksize_check(res, 65564);
     790                if (blksize < 0) {
     791                    error_pkt_reason = ERR_BAD_OPT;
     792                    /* will just send error pkt */
     793                    goto do_proto;
     794                }
     795            }
     796            if (opcode != TFTP_WRQ /* download? */
     797            /* did client ask us about file size? */
     798             && tftp_get_option("tsize", opt_str, opt_len)
     799            ) {
     800                want_transfer_size = 1;
     801            }
     802        }
     803    }
     804# endif
     805
     806    if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) {
     807        if (opt & TFTPD_OPT_r) {
     808            /* This would mean "disk full" - not true */
     809            /*error_pkt_reason = ERR_WRITE;*/
     810            error_msg = bb_msg_write_error;
     811            goto err;
     812        }
     813        IF_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */
     814    } else {
     815        IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */
     816    }
     817
     818    /* NB: if error_pkt_str or error_pkt_reason is set up,
     819     * tftp_protocol() just sends one error pkt and returns */
     820
     821 do_proto:
     822    close(STDIN_FILENO); /* close old, possibly wildcard socket */
     823    /* tftp_protocol() will create new one, bound to particular local IP */
     824    result = tftp_protocol(
     825        our_lsa, peer_lsa,
     826        local_file IF_TFTP(, NULL /*remote_file*/)
     827        IF_FEATURE_TFTP_BLOCKSIZE(, want_transfer_size)
     828        IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
     829    );
     830
     831    return result;
     832 err:
     833    strcpy((char*)error_pkt_str, error_msg);
     834    goto do_proto;
     835}
     836
     837#endif /* ENABLE_TFTPD */
     838
    460839#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
Note: See TracChangeset for help on using the changeset viewer.