busybox/networking/tftp.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * A simple tftp client/server for busybox.
   4 * Tries to follow RFC1350.
   5 * Only "octet" mode supported.
   6 * Optional blocksize negotiation (RFC2347 + RFC2348)
   7 *
   8 * Copyright (C) 2001 Magnus Damm <damm@opensource.se>
   9 *
  10 * Parts of the code based on:
  11 *
  12 * atftp:  Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
  13 *                        and Remi Lefebvre <remi@debian.org>
  14 *
  15 * utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
  16 *
  17 * tftpd added by Denys Vlasenko & Vladimir Dronnikov
  18 *
  19 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  20 */
  21#include "libbb.h"
  22
  23#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
  24
  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 */
  31
  32/* opcodes we support */
  33#define TFTP_RRQ   1
  34#define TFTP_WRQ   2
  35#define TFTP_DATA  3
  36#define TFTP_ACK   4
  37#define TFTP_ERROR 5
  38#define TFTP_OACK  6
  39
  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
  64#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
  65#define IF_GETPUT(...)
  66#define CMD_GET(cmd) 1
  67#define CMD_PUT(cmd) 0
  68#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
  69#define IF_GETPUT(...)
  70#define CMD_GET(cmd) 0
  71#define CMD_PUT(cmd) 1
  72#else
  73#define IF_GETPUT(...) __VA_ARGS__
  74#define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET)
  75#define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT)
  76#endif
  77/* NB: in the code below
  78 * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
  79 */
  80
  81
  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
 108/* SIGALRM logic nicked from the wget applet */
 109static void progress_meter(int flag)
 110{
 111        /* We can be called from signal handler */
 112        int save_errno = errno;
 113
 114        if (flag == -1) { /* first call to progress_meter */
 115                bb_progress_init(&G.pmt);
 116        }
 117
 118        bb_progress_update(&G.pmt, G.file, 0, G.pos, G.size);
 119
 120        if (flag == 0) {
 121                /* last call to progress_meter */
 122                alarm(0);
 123                bb_putchar_stderr('\n');
 124        } else {
 125                if (flag == -1) { /* first call to progress_meter */
 126                        signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
 127                }
 128                alarm(1);
 129        }
 130
 131        errno = save_errno;
 132}
 133static void tftp_progress_init(void)
 134{
 135        progress_meter(-1);
 136}
 137static void tftp_progress_done(void)
 138{
 139        if (G.pmt.inited)
 140                progress_meter(0);
 141}
 142#else
 143# define tftp_progress_init() ((void)0)
 144# define tftp_progress_done() ((void)0)
 145#endif
 146
 147#if ENABLE_FEATURE_TFTP_BLOCKSIZE
 148
 149static int tftp_blksize_check(const char *blksize_str, int maxsize)
 150{
 151        /* Check if the blksize is valid:
 152         * RFC2348 says between 8 and 65464,
 153         * but our implementation makes it impossible
 154         * to use blksizes smaller than 22 octets. */
 155        unsigned blksize = bb_strtou(blksize_str, NULL, 10);
 156        if (errno
 157         || (blksize < 24) || (blksize > maxsize)
 158        ) {
 159                bb_error_msg("bad blocksize '%s'", blksize_str);
 160                return -1;
 161        }
 162# if ENABLE_TFTP_DEBUG
 163        bb_error_msg("using blksize %u", blksize);
 164# endif
 165        return blksize;
 166}
 167
 168static char *tftp_get_option(const char *option, char *buf, int len)
 169{
 170        int opt_val = 0;
 171        int opt_found = 0;
 172        int k;
 173
 174        /* buf points to:
 175         * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */
 176
 177        while (len > 0) {
 178                /* Make sure options are terminated correctly */
 179                for (k = 0; k < len; k++) {
 180                        if (buf[k] == '\0') {
 181                                goto nul_found;
 182                        }
 183                }
 184                return NULL;
 185 nul_found:
 186                if (opt_val == 0) { /* it's "name" part */
 187                        if (strcasecmp(buf, option) == 0) {
 188                                opt_found = 1;
 189                        }
 190                } else if (opt_found) {
 191                        return buf;
 192                }
 193
 194                k++;
 195                buf += k;
 196                len -= k;
 197                opt_val ^= 1;
 198        }
 199
 200        return NULL;
 201}
 202
 203#endif
 204
 205static int tftp_protocol(
 206                /* NULL if tftp, !NULL if tftpd: */
 207                len_and_sockaddr *our_lsa,
 208                len_and_sockaddr *peer_lsa,
 209                const char *local_file
 210                IF_TFTP(, const char *remote_file)
 211#if !ENABLE_TFTP
 212# define remote_file NULL
 213#endif
 214                /* 1 for tftp; 1/0 for tftpd depending whether client asked about it: */
 215                IF_FEATURE_TFTP_BLOCKSIZE(, int want_transfer_size)
 216                IF_FEATURE_TFTP_BLOCKSIZE(, int blksize))
 217{
 218#if !ENABLE_FEATURE_TFTP_BLOCKSIZE
 219        enum { blksize = TFTP_BLKSIZE_DEFAULT };
 220#endif
 221
 222        struct pollfd pfd[1];
 223#define socket_fd (pfd[0].fd)
 224        int len;
 225        int send_len;
 226        IF_FEATURE_TFTP_BLOCKSIZE(smallint expect_OACK = 0;)
 227        smallint finished = 0;
 228        uint16_t opcode;
 229        uint16_t block_nr;
 230        uint16_t recv_blk;
 231        int open_mode, local_fd;
 232        int retries, waittime_ms;
 233        int io_bufsize = blksize + 4;
 234        char *cp;
 235        /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
 236         * size varies meaning BUFFERS_GO_ON_STACK would fail.
 237         *
 238         * We must keep the transmit and receive buffers separate
 239         * in case we rcv a garbage pkt - we need to rexmit the last pkt.
 240         */
 241        char *xbuf = xmalloc(io_bufsize);
 242        char *rbuf = xmalloc(io_bufsize);
 243
 244        socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);
 245        setsockopt_reuseaddr(socket_fd);
 246
 247        if (!ENABLE_TFTP || our_lsa) { /* tftpd */
 248                /* Create a socket which is:
 249                 * 1. bound to IP:port peer sent 1st datagram to,
 250                 * 2. connected to peer's IP:port
 251                 * This way we will answer from the IP:port peer
 252                 * expects, will not get any other packets on
 253                 * the socket, and also plain read/write will work. */
 254                xbind(socket_fd, &our_lsa->u.sa, our_lsa->len);
 255                xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
 256
 257                /* Is there an error already? Send pkt and bail out */
 258                if (error_pkt_reason || error_pkt_str[0])
 259                        goto send_err_pkt;
 260
 261                if (user_opt) {
 262                        struct passwd *pw = xgetpwnam(user_opt);
 263                        change_identity(pw); /* initgroups, setgid, setuid */
 264                }
 265        }
 266
 267        /* Prepare open mode */
 268        if (CMD_PUT(option_mask32)) {
 269                open_mode = O_RDONLY;
 270        } else {
 271                open_mode = O_WRONLY | O_TRUNC | O_CREAT;
 272#if ENABLE_TFTPD
 273                if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) {
 274                        /* tftpd without -c */
 275                        open_mode = O_WRONLY | O_TRUNC;
 276                }
 277#endif
 278        }
 279
 280        /* Examples of network traffic.
 281         * Note two cases when ACKs with block# of 0 are sent.
 282         *
 283         * Download without options:
 284         * tftp -> "\0\1FILENAME\0octet\0"
 285         *         "\0\3\0\1FILEDATA..." <- tftpd
 286         * tftp -> "\0\4\0\1"
 287         * ...
 288         * Download with option of blksize 16384:
 289         * tftp -> "\0\1FILENAME\0octet\0blksize\00016384\0"
 290         *         "\0\6blksize\00016384\0" <- tftpd
 291         * tftp -> "\0\4\0\0"
 292         *         "\0\3\0\1FILEDATA..." <- tftpd
 293         * tftp -> "\0\4\0\1"
 294         * ...
 295         * Upload without options:
 296         * tftp -> "\0\2FILENAME\0octet\0"
 297         *         "\0\4\0\0" <- tftpd
 298         * tftp -> "\0\3\0\1FILEDATA..."
 299         *         "\0\4\0\1" <- tftpd
 300         * ...
 301         * Upload with option of blksize 16384:
 302         * tftp -> "\0\2FILENAME\0octet\0blksize\00016384\0"
 303         *         "\0\6blksize\00016384\0" <- tftpd
 304         * tftp -> "\0\3\0\1FILEDATA..."
 305         *         "\0\4\0\1" <- tftpd
 306         * ...
 307         */
 308        block_nr = 1;
 309        cp = xbuf + 2;
 310
 311        if (!ENABLE_TFTP || our_lsa) { /* tftpd */
 312                /* Open file (must be after changing user) */
 313                local_fd = open(local_file, open_mode, 0666);
 314                if (local_fd < 0) {
 315                        error_pkt_reason = ERR_NOFILE;
 316                        strcpy((char*)error_pkt_str, "can't open file");
 317                        goto send_err_pkt;
 318                }
 319/* gcc 4.3.1 would NOT optimize it out as it should! */
 320#if ENABLE_FEATURE_TFTP_BLOCKSIZE
 321                if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) {
 322                        /* Create and send OACK packet. */
 323                        /* For the download case, block_nr is still 1 -
 324                         * we expect 1st ACK from peer to be for (block_nr-1),
 325                         * that is, for "block 0" which is our OACK pkt */
 326                        opcode = TFTP_OACK;
 327                        goto add_blksize_opt;
 328                }
 329#endif
 330                if (CMD_GET(option_mask32)) {
 331                        /* It's upload and we don't send OACK.
 332                         * We must ACK 1st packet (with filename)
 333                         * as if it is "block 0" */
 334                        block_nr = 0;
 335                }
 336
 337        } else { /* tftp */
 338                /* Open file (must be after changing user) */
 339                local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;
 340                if (NOT_LONE_DASH(local_file))
 341                        local_fd = xopen(local_file, open_mode);
 342/* Removing #if, or using if() statement instead of #if may lead to
 343 * "warning: null argument where non-null required": */
 344#if ENABLE_TFTP
 345                /* tftp */
 346
 347                /* We can't (and don't really need to) bind the socket:
 348                 * we don't know from which local IP datagrams will be sent,
 349                 * but kernel will pick the same IP every time (unless routing
 350                 * table is changed), thus peer will see dgrams consistently
 351                 * coming from the same IP.
 352                 * We would like to connect the socket, but since peer's
 353                 * UDP code can be less perfect than ours, _peer's_ IP:port
 354                 * in replies may differ from IP:port we used to send
 355                 * our first packet. We can connect() only when we get
 356                 * first reply. */
 357
 358                /* build opcode */
 359                opcode = TFTP_WRQ;
 360                if (CMD_GET(option_mask32)) {
 361                        opcode = TFTP_RRQ;
 362                }
 363                /* add filename and mode */
 364                /* fill in packet if the filename fits into xbuf */
 365                len = strlen(remote_file) + 1;
 366                if (2 + len + sizeof("octet") >= io_bufsize) {
 367                        bb_error_msg("remote filename is too long");
 368                        goto ret;
 369                }
 370                strcpy(cp, remote_file);
 371                cp += len;
 372                /* add "mode" part of the packet */
 373                strcpy(cp, "octet");
 374                cp += sizeof("octet");
 375
 376# if ENABLE_FEATURE_TFTP_BLOCKSIZE
 377                if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size)
 378                        goto send_pkt;
 379
 380                /* Need to add option to pkt */
 381                if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN tsize ") + sizeof(off_t)*3) {
 382                        bb_error_msg("remote filename is too long");
 383                        goto ret;
 384                }
 385                expect_OACK = 1;
 386# endif
 387#endif /* ENABLE_TFTP */
 388
 389#if ENABLE_FEATURE_TFTP_BLOCKSIZE
 390 add_blksize_opt:
 391                if (blksize != TFTP_BLKSIZE_DEFAULT) {
 392                        /* add "blksize", <nul>, blksize, <nul> */
 393                        strcpy(cp, "blksize");
 394                        cp += sizeof("blksize");
 395                        cp += snprintf(cp, 6, "%d", blksize) + 1;
 396                }
 397                if (want_transfer_size) {
 398                        /* add "tsize", <nul>, size, <nul> (see RFC2349) */
 399                        /* if tftp and downloading, we send "0" (since we opened local_fd with O_TRUNC)
 400                         * and this makes server to send "tsize" option with the size */
 401                        /* if tftp and uploading, we send file size (maybe dont, to not confuse old servers???) */
 402                        /* if tftpd and downloading, we are answering to client's request */
 403                        /* if tftpd and uploading: !want_transfer_size, this code is not executed */
 404                        struct stat st;
 405                        strcpy(cp, "tsize");
 406                        cp += sizeof("tsize");
 407                        st.st_size = 0;
 408                        fstat(local_fd, &st);
 409                        cp += sprintf(cp, "%"OFF_FMT"u", (off_t)st.st_size) + 1;
 410# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
 411                        /* Save for progress bar. If 0 (tftp downloading),
 412                         * we look at server's reply later */
 413                        G.size = st.st_size;
 414                        if (remote_file && st.st_size)
 415                                tftp_progress_init();
 416# endif
 417                }
 418#endif
 419                /* First packet is built, so skip packet generation */
 420                goto send_pkt;
 421        }
 422
 423        /* Using mostly goto's - continue/break will be less clear
 424         * in where we actually jump to */
 425        while (1) {
 426                /* Build ACK or DATA */
 427                cp = xbuf + 2;
 428                *((uint16_t*)cp) = htons(block_nr);
 429                cp += 2;
 430                block_nr++;
 431                opcode = TFTP_ACK;
 432                if (CMD_PUT(option_mask32)) {
 433                        opcode = TFTP_DATA;
 434                        len = full_read(local_fd, cp, blksize);
 435                        if (len < 0) {
 436                                goto send_read_err_pkt;
 437                        }
 438                        if (len != blksize) {
 439                                finished = 1;
 440                        }
 441                        cp += len;
 442                }
 443 send_pkt:
 444                /* Send packet */
 445                *((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
 446                send_len = cp - xbuf;
 447                /* NB: send_len value is preserved in code below
 448                 * for potential resend */
 449
 450                retries = TFTP_NUM_RETRIES;  /* re-initialize */
 451                waittime_ms = TFTP_TIMEOUT_MS;
 452
 453 send_again:
 454#if ENABLE_TFTP_DEBUG
 455                fprintf(stderr, "sending %u bytes\n", send_len);
 456                for (cp = xbuf; cp < &xbuf[send_len]; cp++)
 457                        fprintf(stderr, "%02x ", (unsigned char) *cp);
 458                fprintf(stderr, "\n");
 459#endif
 460                xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
 461
 462#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
 463                if (ENABLE_TFTP && remote_file) { /* tftp */
 464                        G.pos = (block_nr - 1) * (uoff_t)blksize;
 465                }
 466#endif
 467                /* Was it final ACK? then exit */
 468                if (finished && (opcode == TFTP_ACK))
 469                        goto ret;
 470
 471 recv_again:
 472                /* Receive packet */
 473                /*pfd[0].fd = socket_fd;*/
 474                pfd[0].events = POLLIN;
 475                switch (safe_poll(pfd, 1, waittime_ms)) {
 476                default:
 477                        /*bb_perror_msg("poll"); - done in safe_poll */
 478                        goto ret;
 479                case 0:
 480                        retries--;
 481                        if (retries == 0) {
 482                                bb_error_msg("timeout");
 483                                goto ret; /* no err packet sent */
 484                        }
 485
 486                        /* exponential backoff with limit */
 487                        waittime_ms += waittime_ms/2;
 488                        if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
 489                                waittime_ms = TFTP_MAXTIMEOUT_MS;
 490                        }
 491
 492                        goto send_again; /* resend last sent pkt */
 493                case 1:
 494                        if (!our_lsa) {
 495                                /* tftp (not tftpd!) receiving 1st packet */
 496                                our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */
 497                                len = recvfrom(socket_fd, rbuf, io_bufsize, 0,
 498                                                &peer_lsa->u.sa, &peer_lsa->len);
 499                                /* Our first dgram went to port 69
 500                                 * but reply may come from different one.
 501                                 * Remember and use this new port (and IP) */
 502                                if (len >= 0)
 503                                        xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
 504                        } else {
 505                                /* tftpd, or not the very first packet:
 506                                 * socket is connect()ed, can just read from it. */
 507                                /* Don't full_read()!
 508                                 * This is not TCP, one read == one pkt! */
 509                                len = safe_read(socket_fd, rbuf, io_bufsize);
 510                        }
 511                        if (len < 0) {
 512                                goto send_read_err_pkt;
 513                        }
 514                        if (len < 4) { /* too small? */
 515                                goto recv_again;
 516                        }
 517                }
 518
 519                /* Process recv'ed packet */
 520                opcode = ntohs( ((uint16_t*)rbuf)[0] );
 521                recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
 522#if ENABLE_TFTP_DEBUG
 523                fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
 524#endif
 525                if (opcode == TFTP_ERROR) {
 526                        static const char errcode_str[] ALIGN1 =
 527                                "\0"
 528                                "file not found\0"
 529                                "access violation\0"
 530                                "disk full\0"
 531                                "bad operation\0"
 532                                "unknown transfer id\0"
 533                                "file already exists\0"
 534                                "no such user\0"
 535                                "bad option";
 536
 537                        const char *msg = "";
 538
 539                        if (len > 4 && rbuf[4] != '\0') {
 540                                msg = &rbuf[4];
 541                                rbuf[io_bufsize - 1] = '\0'; /* paranoia */
 542                        } else if (recv_blk <= 8) {
 543                                msg = nth_string(errcode_str, recv_blk);
 544                        }
 545                        bb_error_msg("server error: (%u) %s", recv_blk, msg);
 546                        goto ret;
 547                }
 548
 549#if ENABLE_FEATURE_TFTP_BLOCKSIZE
 550                if (expect_OACK) {
 551                        expect_OACK = 0;
 552                        if (opcode == TFTP_OACK) {
 553                                /* server seems to support options */
 554                                char *res;
 555
 556                                res = tftp_get_option("blksize", &rbuf[2], len - 2);
 557                                if (res) {
 558                                        blksize = tftp_blksize_check(res, blksize);
 559                                        if (blksize < 0) {
 560                                                error_pkt_reason = ERR_BAD_OPT;
 561                                                goto send_err_pkt;
 562                                        }
 563                                        io_bufsize = blksize + 4;
 564                                }
 565# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
 566                                if (remote_file && G.size == 0) { /* if we don't know it yet */
 567                                        res = tftp_get_option("tsize", &rbuf[2], len - 2);
 568                                        if (res) {
 569                                                G.size = bb_strtoull(res, NULL, 10);
 570                                                if (G.size)
 571                                                        tftp_progress_init();
 572                                        }
 573                                }
 574# endif
 575                                if (CMD_GET(option_mask32)) {
 576                                        /* We'll send ACK for OACK,
 577                                         * such ACK has "block no" of 0 */
 578                                        block_nr = 0;
 579                                }
 580                                continue;
 581                        }
 582                        /* rfc2347:
 583                         * "An option not acknowledged by the server
 584                         * must be ignored by the client and server
 585                         * as if it were never requested." */
 586                        if (blksize != TFTP_BLKSIZE_DEFAULT)
 587                                bb_error_msg("falling back to blocksize "TFTP_BLKSIZE_DEFAULT_STR);
 588                        blksize = TFTP_BLKSIZE_DEFAULT;
 589                        io_bufsize = TFTP_BLKSIZE_DEFAULT + 4;
 590                }
 591#endif
 592                /* block_nr is already advanced to next block# we expect
 593                 * to get / block# we are about to send next time */
 594
 595                if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) {
 596                        if (recv_blk == block_nr) {
 597                                int sz = full_write(local_fd, &rbuf[4], len - 4);
 598                                if (sz != len - 4) {
 599                                        strcpy((char*)error_pkt_str, bb_msg_write_error);
 600                                        error_pkt_reason = ERR_WRITE;
 601                                        goto send_err_pkt;
 602                                }
 603                                if (sz != blksize) {
 604                                        finished = 1;
 605                                }
 606                                continue; /* send ACK */
 607                        }
 608/* Disabled to cope with servers with Sorcerer's Apprentice Syndrome */
 609#if 0
 610                        if (recv_blk == (block_nr - 1)) {
 611                                /* Server lost our TFTP_ACK.  Resend it */
 612                                block_nr = recv_blk;
 613                                continue;
 614                        }
 615#endif
 616                }
 617
 618                if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) {
 619                        /* did peer ACK our last DATA pkt? */
 620                        if (recv_blk == (uint16_t) (block_nr - 1)) {
 621                                if (finished)
 622                                        goto ret;
 623                                continue; /* send next block */
 624                        }
 625                }
 626                /* Awww... recv'd packet is not recognized! */
 627                goto recv_again;
 628                /* why recv_again? - rfc1123 says:
 629                 * "The sender (i.e., the side originating the DATA packets)
 630                 *  must never resend the current DATA packet on receipt
 631                 *  of a duplicate ACK".
 632                 * DATA pkts are resent ONLY on timeout.
 633                 * Thus "goto send_again" will ba a bad mistake above.
 634                 * See:
 635                 * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
 636                 */
 637        } /* end of "while (1)" */
 638 ret:
 639        if (ENABLE_FEATURE_CLEAN_UP) {
 640                close(local_fd);
 641                close(socket_fd);
 642                free(xbuf);
 643                free(rbuf);
 644        }
 645        return finished == 0; /* returns 1 on failure */
 646
 647 send_read_err_pkt:
 648        strcpy((char*)error_pkt_str, bb_msg_read_error);
 649 send_err_pkt:
 650        if (error_pkt_str[0])
 651                bb_error_msg("%s", (char*)error_pkt_str);
 652        error_pkt[1] = TFTP_ERROR;
 653        xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str),
 654                        &peer_lsa->u.sa, peer_lsa->len);
 655        return EXIT_FAILURE;
 656#undef remote_file
 657}
 658
 659#if ENABLE_TFTP
 660
 661int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 662int tftp_main(int argc UNUSED_PARAM, char **argv)
 663{
 664        len_and_sockaddr *peer_lsa;
 665        const char *local_file = NULL;
 666        const char *remote_file = NULL;
 667# if ENABLE_FEATURE_TFTP_BLOCKSIZE
 668        const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR;
 669        int blksize;
 670# endif
 671        int result;
 672        int port;
 673        IF_GETPUT(int opt;)
 674
 675        INIT_G();
 676
 677        /* -p or -g is mandatory, and they are mutually exclusive */
 678        opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:")
 679                        IF_GETPUT("g--p:p--g:");
 680
 681        IF_GETPUT(opt =) getopt32(argv,
 682                        IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p")
 683                                "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:"),
 684                        &local_file, &remote_file
 685                        IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str));
 686        argv += optind;
 687
 688# if ENABLE_FEATURE_TFTP_BLOCKSIZE
 689        /* Check if the blksize is valid:
 690         * RFC2348 says between 8 and 65464 */
 691        blksize = tftp_blksize_check(blksize_str, 65564);
 692        if (blksize < 0) {
 693                //bb_error_msg("bad block size");
 694                return EXIT_FAILURE;
 695        }
 696# endif
 697
 698        if (remote_file) {
 699                if (!local_file) {
 700                        const char *slash = strrchr(remote_file, '/');
 701                        local_file = slash ? slash + 1 : remote_file;
 702                }
 703        } else {
 704                remote_file = local_file;
 705        }
 706
 707        /* Error if filename or host is not known */
 708        if (!remote_file || !argv[0])
 709                bb_show_usage();
 710
 711        port = bb_lookup_port(argv[1], "udp", 69);
 712        peer_lsa = xhost2sockaddr(argv[0], port);
 713
 714# if ENABLE_TFTP_DEBUG
 715        fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",
 716                        xmalloc_sockaddr2dotted(&peer_lsa->u.sa),
 717                        remote_file, local_file);
 718# endif
 719
 720# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
 721        G.file = remote_file;
 722# endif
 723        result = tftp_protocol(
 724                NULL /*our_lsa*/, peer_lsa,
 725                local_file, remote_file
 726                IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */)
 727                IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
 728        );
 729        tftp_progress_done();
 730
 731        if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) {
 732                unlink(local_file);
 733        }
 734        return result;
 735}
 736
 737#endif /* ENABLE_TFTP */
 738
 739#if ENABLE_TFTPD
 740int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 741int tftpd_main(int argc UNUSED_PARAM, char **argv)
 742{
 743        len_and_sockaddr *our_lsa;
 744        len_and_sockaddr *peer_lsa;
 745        char *local_file, *mode;
 746        const char *error_msg;
 747        int opt, result, opcode;
 748        IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
 749        IF_FEATURE_TFTP_BLOCKSIZE(int want_transfer_size = 0;)
 750
 751        INIT_G();
 752
 753        our_lsa = get_sock_lsa(STDIN_FILENO);
 754        if (!our_lsa) {
 755                /* This is confusing:
 756                 *bb_error_msg_and_die("stdin is not a socket");
 757                 * Better: */
 758                bb_show_usage();
 759                /* Help text says that tftpd must be used as inetd service,
 760                 * which is by far the most usual cause of get_sock_lsa
 761                 * failure */
 762        }
 763        peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len);
 764        peer_lsa->len = our_lsa->len;
 765
 766        /* Shifting to not collide with TFTP_OPTs */
 767        opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:", &user_opt) << 8);
 768        argv += optind;
 769        if (argv[0])
 770                xchdir(argv[0]);
 771
 772        result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf),
 773                        0 /* flags */,
 774                        &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len);
 775
 776        error_msg = "malformed packet";
 777        opcode = ntohs(*(uint16_t*)block_buf);
 778        if (result < 4 || result >= sizeof(block_buf)
 779         || block_buf[result-1] != '\0'
 780         || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */
 781             IF_GETPUT(&&)
 782             IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */
 783            )
 784        ) {
 785                goto err;
 786        }
 787        local_file = block_buf + 2;
 788        if (local_file[0] == '.' || strstr(local_file, "/.")) {
 789                error_msg = "dot in file name";
 790                goto err;
 791        }
 792        mode = local_file + strlen(local_file) + 1;
 793        if (mode >= block_buf + result || strcmp(mode, "octet") != 0) {
 794                goto err;
 795        }
 796# if ENABLE_FEATURE_TFTP_BLOCKSIZE
 797        {
 798                char *res;
 799                char *opt_str = mode + sizeof("octet");
 800                int opt_len = block_buf + result - opt_str;
 801                if (opt_len > 0) {
 802                        res = tftp_get_option("blksize", opt_str, opt_len);
 803                        if (res) {
 804                                blksize = tftp_blksize_check(res, 65564);
 805                                if (blksize < 0) {
 806                                        error_pkt_reason = ERR_BAD_OPT;
 807                                        /* will just send error pkt */
 808                                        goto do_proto;
 809                                }
 810                        }
 811                        if (opcode != TFTP_WRQ /* download? */
 812                        /* did client ask us about file size? */
 813                         && tftp_get_option("tsize", opt_str, opt_len)
 814                        ) {
 815                                want_transfer_size = 1;
 816                        }
 817                }
 818        }
 819# endif
 820
 821        if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) {
 822                if (opt & TFTPD_OPT_r) {
 823                        /* This would mean "disk full" - not true */
 824                        /*error_pkt_reason = ERR_WRITE;*/
 825                        error_msg = bb_msg_write_error;
 826                        goto err;
 827                }
 828                IF_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */
 829        } else {
 830                IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */
 831        }
 832
 833        /* NB: if error_pkt_str or error_pkt_reason is set up,
 834         * tftp_protocol() just sends one error pkt and returns */
 835
 836 do_proto:
 837        close(STDIN_FILENO); /* close old, possibly wildcard socket */
 838        /* tftp_protocol() will create new one, bound to particular local IP */
 839        result = tftp_protocol(
 840                our_lsa, peer_lsa,
 841                local_file IF_TFTP(, NULL /*remote_file*/)
 842                IF_FEATURE_TFTP_BLOCKSIZE(, want_transfer_size)
 843                IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
 844        );
 845
 846        return result;
 847 err:
 848        strcpy((char*)error_pkt_str, error_msg);
 849        goto do_proto;
 850}
 851
 852#endif /* ENABLE_TFTPD */
 853
 854#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
 855