Ignore:
Timestamp:
Nov 6, 2007, 11:01:53 AM (16 years ago)
Author:
Bruno Cornec
Message:
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File:
1 edited

Legend:

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

    r821 r1770  
    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.