source: branches/stable/mindi-busybox/networking/tftp.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • 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 size: 11.6 KB
Line 
1/* vi: set sw=4 ts=4: */
2/* -------------------------------------------------------------------------
3 * tftp.c
4 *
5 * A simple tftp client for busybox.
6 * Tries to follow RFC1350.
7 * Only "octet" mode supported.
8 * Optional blocksize negotiation (RFC2347 + RFC2348)
9 *
10 * Copyright (C) 2001 Magnus Damm <damm@opensource.se>
11 *
12 * Parts of the code based on:
13 *
14 * atftp:  Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
15 *                        and Remi Lefebvre <remi@debian.org>
16 *
17 * utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
18 *
19 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
20 * ------------------------------------------------------------------------- */
21
22#include "libbb.h"
23
24
25#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
26
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 */
30
31/* opcodes we support */
32#define TFTP_RRQ   1
33#define TFTP_WRQ   2
34#define TFTP_DATA  3
35#define TFTP_ACK   4
36#define TFTP_ERROR 5
37#define TFTP_OACK  6
38
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
47#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)
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
59
60static int tftp_blocksize_check(int blocksize, int bufsize)
61{
62    /* Check if the blocksize is valid:
63     * RFC2348 says between 8 and 65464,
64     * 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)
70    ) {
71        bb_error_msg("bad blocksize");
72        return 0;
73    }
74
75    return blocksize;
76}
77
78static char *tftp_option_get(char *buf, int len, const char *option)
79{
80    int opt_val = 0;
81    int opt_found = 0;
82    int k;
83
84    while (len > 0) {
85        /* Make sure the options are terminated correctly */
86        for (k = 0; k < len; k++) {
87            if (buf[k] == '\0') {
88                goto nul_found;
89            }
90        }
91        return NULL;
92 nul_found:
93        if (opt_val == 0) {
94            if (strcasecmp(buf, option) == 0) {
95                opt_found = 1;
96            }
97        } else if (opt_found) {
98            return buf;
99        }
100
101        k++;
102        buf += k;
103        len -= k;
104        opt_val ^= 1;
105    }
106
107    return NULL;
108}
109
110#endif
111
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)
116{
117    struct timeval tv;
118    fd_set rfds;
119    int socketfd;
120    int len;
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;
127    int timeout = TFTP_NUM_RETRIES;
128    char *cp;
129
130    unsigned org_port;
131    len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
132
133    /* 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    }
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 */
183
184    while (1) {
185        /* Build ACK or DATA */
186        cp = xbuf + 2;
187        *((uint16_t*)cp) = htons(block_nr);
188        cp += 2;
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;
325                }
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;
335        }
336#endif
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);
343                if (len < 0) {
344                    bb_perror_msg(bb_msg_write_error);
345                    goto ret;
346                }
347                if (len != (tftp_bufsize - 4)) {
348                    finished = 1;
349                }
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;
355                continue;
356            }
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 */
386}
387
388int tftp_main(int argc, char **argv);
389int tftp_main(int argc, char **argv)
390{
391    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
397    int port;
398    USE_GETPUT(int cmd;)
399    int fd = -1;
400    int flags = 0;
401    int result;
402    int blocksize = TFTP_BLOCKSIZE_DEFAULT;
403
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))
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;
432    /* Error if filename or host is not known */
433    if (!remotefile || !argv[0])
434        bb_show_usage();
435
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),
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);
456    }
457    return result;
458}
459
460#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
Note: See TracBrowser for help on using the repository browser.