Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/networking/tftp.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 983 by niro, Fri Apr 24 18:33:46 2009 UTC revision 984 by niro, Sun May 30 11:32:42 2010 UTC
# Line 1  Line 1 
1  /* vi: set sw=4 ts=4: */  /* vi: set sw=4 ts=4: */
2  /* -------------------------------------------------------------------------  /*
  * tftp.c  
  *  
3   * A simple tftp client/server for busybox.   * A simple tftp client/server for busybox.
4   * Tries to follow RFC1350.   * Tries to follow RFC1350.
5   * Only "octet" mode supported.   * Only "octet" mode supported.
# Line 19  Line 17 
17   * tftpd added by Denys Vlasenko & Vladimir Dronnikov   * tftpd added by Denys Vlasenko & Vladimir Dronnikov
18   *   *
19   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
20   * ------------------------------------------------------------------------- */   */
   
21  #include "libbb.h"  #include "libbb.h"
22    
23  #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT  #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
# Line 64  enum { Line 61  enum {
61  };  };
62    
63  #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT  #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
64  #define USE_GETPUT(...)  #define IF_GETPUT(...)
65  #define CMD_GET(cmd) 1  #define CMD_GET(cmd) 1
66  #define CMD_PUT(cmd) 0  #define CMD_PUT(cmd) 0
67  #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT  #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
68  #define USE_GETPUT(...)  #define IF_GETPUT(...)
69  #define CMD_GET(cmd) 0  #define CMD_GET(cmd) 0
70  #define CMD_PUT(cmd) 1  #define CMD_PUT(cmd) 1
71  #else  #else
72  #define USE_GETPUT(...) __VA_ARGS__  #define IF_GETPUT(...) __VA_ARGS__
73  #define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET)  #define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET)
74  #define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT)  #define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT)
75  #endif  #endif
# Line 87  struct globals { Line 84  struct globals {
84   char *user_opt;   char *user_opt;
85   /* used in tftpd_main(), a bit big for stack: */   /* used in tftpd_main(), a bit big for stack: */
86   char block_buf[TFTP_BLKSIZE_DEFAULT];   char block_buf[TFTP_BLKSIZE_DEFAULT];
87    #if ENABLE_FEATURE_TFTP_PROGRESS_BAR
88     off_t pos;
89     off_t size;
90     const char *file;
91     bb_progress_t pmt;
92    #endif
93  };  };
94  #define G (*(struct globals*)&bb_common_bufsiz1)  #define G (*(struct globals*)&bb_common_bufsiz1)
95    struct BUG_G_too_big {
96     char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
97    };
98  #define block_buf        (G.block_buf   )  #define block_buf        (G.block_buf   )
99  #define user_opt         (G.user_opt    )  #define user_opt         (G.user_opt    )
100  #define error_pkt        (G.error_pkt   )  #define error_pkt        (G.error_pkt   )
# Line 97  struct globals { Line 103  struct globals {
103  #define error_pkt_reason (error_pkt[3])  #define error_pkt_reason (error_pkt[3])
104  #define error_pkt_str    (error_pkt + 4)  #define error_pkt_str    (error_pkt + 4)
105    
106    #if ENABLE_FEATURE_TFTP_PROGRESS_BAR
107    /* SIGALRM logic nicked from the wget applet */
108    static void progress_meter(int flag)
109    {
110     /* We can be called from signal handler */
111     int save_errno = errno;
112    
113     if (flag == -1) { /* first call to progress_meter */
114     bb_progress_init(&G.pmt);
115     }
116    
117     bb_progress_update(&G.pmt, G.file, 0, G.pos, G.size);
118    
119     if (flag == 0) {
120     /* last call to progress_meter */
121     alarm(0);
122     fputc('\n', stderr);
123     } else {
124     if (flag == -1) { /* first call to progress_meter */
125     signal_SA_RESTART_empty_mask(SIGALRM, progress_meter);
126     }
127     alarm(1);
128     }
129    
130     errno = save_errno;
131    }
132    static void tftp_progress_init(void)
133    {
134     progress_meter(-1);
135    }
136    static void tftp_progress_done(void)
137    {
138     progress_meter(0);
139    }
140    #else
141    # define tftp_progress_init() ((void)0)
142    # define tftp_progress_done() ((void)0)
143    #endif
144    
145  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
146    
# Line 113  static int tftp_blksize_check(const char Line 157  static int tftp_blksize_check(const char
157   bb_error_msg("bad blocksize '%s'", blksize_str);   bb_error_msg("bad blocksize '%s'", blksize_str);
158   return -1;   return -1;
159   }   }
160  #if ENABLE_TFTP_DEBUG  # if ENABLE_TFTP_DEBUG
161   bb_error_msg("using blksize %u", blksize);   bb_error_msg("using blksize %u", blksize);
162  #endif  # endif
163   return blksize;   return blksize;
164  }  }
165    
# Line 157  static char *tftp_get_option(const char Line 201  static char *tftp_get_option(const char
201  #endif  #endif
202    
203  static int tftp_protocol(  static int tftp_protocol(
204     /* NULL if tftp, !NULL if tftpd: */
205   len_and_sockaddr *our_lsa,   len_and_sockaddr *our_lsa,
206   len_and_sockaddr *peer_lsa,   len_and_sockaddr *peer_lsa,
207   const char *local_file   const char *local_file
208   USE_TFTP(, const char *remote_file)   IF_TFTP(, const char *remote_file)
  USE_FEATURE_TFTP_BLOCKSIZE(USE_TFTPD(, void *tsize))  
  USE_FEATURE_TFTP_BLOCKSIZE(, int blksize))  
 {  
209  #if !ENABLE_TFTP  #if !ENABLE_TFTP
210  #define remote_file NULL  # define remote_file NULL
 #endif  
 #if !(ENABLE_FEATURE_TFTP_BLOCKSIZE && ENABLE_TFTPD)  
 #define tsize NULL  
211  #endif  #endif
212     /* 1 for tftp; 1/0 for tftpd depending whether client asked about it: */
213     IF_FEATURE_TFTP_BLOCKSIZE(, int want_transfer_size)
214     IF_FEATURE_TFTP_BLOCKSIZE(, int blksize))
215    {
216  #if !ENABLE_FEATURE_TFTP_BLOCKSIZE  #if !ENABLE_FEATURE_TFTP_BLOCKSIZE
217   enum { blksize = TFTP_BLKSIZE_DEFAULT };   enum { blksize = TFTP_BLKSIZE_DEFAULT };
218  #endif  #endif
# Line 178  static int tftp_protocol( Line 221  static int tftp_protocol(
221  #define socket_fd (pfd[0].fd)  #define socket_fd (pfd[0].fd)
222   int len;   int len;
223   int send_len;   int send_len;
224   USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)   IF_FEATURE_TFTP_BLOCKSIZE(smallint expect_OACK = 0;)
225   smallint finished = 0;   smallint finished = 0;
226   uint16_t opcode;   uint16_t opcode;
227   uint16_t block_nr;   uint16_t block_nr;
# Line 188  static int tftp_protocol( Line 231  static int tftp_protocol(
231   int io_bufsize = blksize + 4;   int io_bufsize = blksize + 4;
232   char *cp;   char *cp;
233   /* Can't use RESERVE_CONFIG_BUFFER here since the allocation   /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
234   * size varies meaning BUFFERS_GO_ON_STACK would fail */   * size varies meaning BUFFERS_GO_ON_STACK would fail.
235   /* We must keep the transmit and receive buffers seperate */   *
236   /* In case we rcv a garbage pkt and we need to rexmit the last pkt */   * We must keep the transmit and receive buffers separate
237     * in case we rcv a garbage pkt - we need to rexmit the last pkt.
238     */
239   char *xbuf = xmalloc(io_bufsize);   char *xbuf = xmalloc(io_bufsize);
240   char *rbuf = xmalloc(io_bufsize);   char *rbuf = xmalloc(io_bufsize);
241    
242   socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);   socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);
243   setsockopt_reuseaddr(socket_fd);   setsockopt_reuseaddr(socket_fd);
244    
245   block_nr = 1;   if (!ENABLE_TFTP || our_lsa) { /* tftpd */
  cp = xbuf + 2;  
   
  if (!ENABLE_TFTP || our_lsa) {  
  /* tftpd */  
   
246   /* Create a socket which is:   /* Create a socket which is:
247   * 1. bound to IP:port peer sent 1st datagram to,   * 1. bound to IP:port peer sent 1st datagram to,
248   * 2. connected to peer's IP:port   * 2. connected to peer's IP:port
# Line 216  static int tftp_protocol( Line 256  static int tftp_protocol(
256   if (error_pkt_reason || error_pkt_str[0])   if (error_pkt_reason || error_pkt_str[0])
257   goto send_err_pkt;   goto send_err_pkt;
258    
  if (CMD_GET(option_mask32)) {  
  /* it's upload - we must ACK 1st packet (with filename)  
  * as if it's "block 0" */  
  block_nr = 0;  
  }  
   
259   if (user_opt) {   if (user_opt) {
260   struct passwd *pw = getpwnam(user_opt);   struct passwd *pw = xgetpwnam(user_opt);
  if (!pw)  
  bb_error_msg_and_die("unknown user %s", user_opt);  
261   change_identity(pw); /* initgroups, setgid, setuid */   change_identity(pw); /* initgroups, setgid, setuid */
262   }   }
263   }   }
264    
265   /* Open local file (must be after changing user) */   /* Prepare open mode */
266   if (CMD_PUT(option_mask32)) {   if (CMD_PUT(option_mask32)) {
267   open_mode = O_RDONLY;   open_mode = O_RDONLY;
268   } else {   } else {
# Line 242  static int tftp_protocol( Line 274  static int tftp_protocol(
274   }   }
275  #endif  #endif
276   }   }
277   if (!(option_mask32 & TFTPD_OPT)) {  
278   local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;   /* Examples of network traffic.
279   if (NOT_LONE_DASH(local_file))   * Note two cases when ACKs with block# of 0 are sent.
280   local_fd = xopen(local_file, open_mode);   *
281   } else {   * Download without options:
282   local_fd = open(local_file, open_mode);   * tftp -> "\0\1FILENAME\0octet\0"
283     *         "\0\3\0\1FILEDATA..." <- tftpd
284     * tftp -> "\0\4\0\1"
285     * ...
286     * Download with option of blksize 16384:
287     * tftp -> "\0\1FILENAME\0octet\0blksize\00016384\0"
288     *         "\0\6blksize\00016384\0" <- tftpd
289     * tftp -> "\0\4\0\0"
290     *         "\0\3\0\1FILEDATA..." <- tftpd
291     * tftp -> "\0\4\0\1"
292     * ...
293     * Upload without options:
294     * tftp -> "\0\2FILENAME\0octet\0"
295     *         "\0\4\0\0" <- tftpd
296     * tftp -> "\0\3\0\1FILEDATA..."
297     *         "\0\4\0\1" <- tftpd
298     * ...
299     * Upload with option of blksize 16384:
300     * tftp -> "\0\2FILENAME\0octet\0blksize\00016384\0"
301     *         "\0\6blksize\00016384\0" <- tftpd
302     * tftp -> "\0\3\0\1FILEDATA..."
303     *         "\0\4\0\1" <- tftpd
304     * ...
305     */
306     block_nr = 1;
307     cp = xbuf + 2;
308    
309     if (!ENABLE_TFTP || our_lsa) { /* tftpd */
310     /* Open file (must be after changing user) */
311     local_fd = open(local_file, open_mode, 0666);
312   if (local_fd < 0) {   if (local_fd < 0) {
313   error_pkt_reason = ERR_NOFILE;   error_pkt_reason = ERR_NOFILE;
314   strcpy((char*)error_pkt_str, "can't open file");   strcpy((char*)error_pkt_str, "can't open file");
315   goto send_err_pkt;   goto send_err_pkt;
316   }   }
  }  
   
  if (!ENABLE_TFTP || our_lsa) {  
317  /* gcc 4.3.1 would NOT optimize it out as it should! */  /* gcc 4.3.1 would NOT optimize it out as it should! */
318  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
319   if (blksize != TFTP_BLKSIZE_DEFAULT || tsize) {   if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) {
320   /* Create and send OACK packet. */   /* Create and send OACK packet. */
321   /* For the download case, block_nr is still 1 -   /* For the download case, block_nr is still 1 -
322   * we expect 1st ACK from peer to be for (block_nr-1),   * we expect 1st ACK from peer to be for (block_nr-1),
# Line 267  static int tftp_protocol( Line 325  static int tftp_protocol(
325   goto add_blksize_opt;   goto add_blksize_opt;
326   }   }
327  #endif  #endif
328   } else {   if (CMD_GET(option_mask32)) {
329  /* Removing it, or using if() statement instead of #if may lead to   /* It's upload and we don't send OACK.
330     * We must ACK 1st packet (with filename)
331     * as if it is "block 0" */
332     block_nr = 0;
333     }
334    
335     } else { /* tftp */
336     /* Open file (must be after changing user) */
337     local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;
338     if (NOT_LONE_DASH(local_file))
339     local_fd = xopen(local_file, open_mode);
340    /* Removing #if, or using if() statement instead of #if may lead to
341   * "warning: null argument where non-null required": */   * "warning: null argument where non-null required": */
342  #if ENABLE_TFTP  #if ENABLE_TFTP
343   /* tftp */   /* tftp */
# Line 298  static int tftp_protocol( Line 367  static int tftp_protocol(
367   }   }
368   strcpy(cp, remote_file);   strcpy(cp, remote_file);
369   cp += len;   cp += len;
370   /* add "mode" part of the package */   /* add "mode" part of the packet */
371   strcpy(cp, "octet");   strcpy(cp, "octet");
372   cp += sizeof("octet");   cp += sizeof("octet");
373    
374  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  # if ENABLE_FEATURE_TFTP_BLOCKSIZE
375   if (blksize == TFTP_BLKSIZE_DEFAULT)   if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size)
376   goto send_pkt;   goto send_pkt;
377    
378   /* Non-standard blocksize: add option to pkt */   /* Need to add option to pkt */
379   if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {   if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN tsize ") + sizeof(off_t)*3) {
380   bb_error_msg("remote filename is too long");   bb_error_msg("remote filename is too long");
381   goto ret;   goto ret;
382   }   }
383   want_option_ack = 1;   expect_OACK = 1;
384  #endif  # endif
385  #endif /* ENABLE_TFTP */  #endif /* ENABLE_TFTP */
386    
387  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
388   add_blksize_opt:   add_blksize_opt:
 #if ENABLE_TFTPD  
  if (tsize) {  
  struct stat st;  
  /* add "tsize", <nul>, size, <nul> */  
  strcpy(cp, "tsize");  
  cp += sizeof("tsize");  
  fstat(local_fd, &st);  
  cp += snprintf(cp, 10, "%u", (int) st.st_size) + 1;  
  }  
 #endif  
389   if (blksize != TFTP_BLKSIZE_DEFAULT) {   if (blksize != TFTP_BLKSIZE_DEFAULT) {
390   /* add "blksize", <nul>, blksize, <nul> */   /* add "blksize", <nul>, blksize, <nul> */
391   strcpy(cp, "blksize");   strcpy(cp, "blksize");
392   cp += sizeof("blksize");   cp += sizeof("blksize");
393   cp += snprintf(cp, 6, "%d", blksize) + 1;   cp += snprintf(cp, 6, "%d", blksize) + 1;
394   }   }
395     if (want_transfer_size) {
396     /* add "tsize", <nul>, size, <nul> (see RFC2349) */
397     /* if tftp and downloading, we send "0" (since we opened local_fd with O_TRUNC)
398     * and this makes server to send "tsize" option with the size */
399     /* if tftp and uploading, we send file size (maybe dont, to not confuse old servers???) */
400     /* if tftpd and downloading, we are answering to client's request */
401     /* if tftpd and uploading: !want_transfer_size, this code is not executed */
402     struct stat st;
403     strcpy(cp, "tsize");
404     cp += sizeof("tsize");
405     st.st_size = 0;
406     fstat(local_fd, &st);
407     cp += sprintf(cp, "%"OFF_FMT"u", (off_t)st.st_size) + 1;
408    # if ENABLE_FEATURE_TFTP_PROGRESS_BAR
409     /* Save for progress bar. If 0 (tftp downloading),
410     * we look at server's reply later */
411     G.size = st.st_size;
412     if (remote_file && st.st_size)
413     tftp_progress_init();
414    # endif
415     }
416  #endif  #endif
417   /* First packet is built, so skip packet generation */   /* First packet is built, so skip packet generation */
418   goto send_pkt;   goto send_pkt;
# Line 376  static int tftp_protocol( Line 456  static int tftp_protocol(
456   fprintf(stderr, "\n");   fprintf(stderr, "\n");
457  #endif  #endif
458   xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);   xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
459    
460    #if ENABLE_FEATURE_TFTP_PROGRESS_BAR
461     if (ENABLE_TFTP && remote_file) { /* tftp */
462     G.pos = (block_nr - 1) * (uoff_t)blksize;
463     }
464    #endif
465   /* Was it final ACK? then exit */   /* Was it final ACK? then exit */
466   if (finished && (opcode == TFTP_ACK))   if (finished && (opcode == TFTP_ACK))
467   goto ret;   goto ret;
# Line 459  static int tftp_protocol( Line 545  static int tftp_protocol(
545   }   }
546    
547  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
548   if (want_option_ack) {   if (expect_OACK) {
549   want_option_ack = 0;   expect_OACK = 0;
550   if (opcode == TFTP_OACK) {   if (opcode == TFTP_OACK) {
551   /* server seems to support options */   /* server seems to support options */
552   char *res;   char *res;
# Line 473  static int tftp_protocol( Line 559  static int tftp_protocol(
559   goto send_err_pkt;   goto send_err_pkt;
560   }   }
561   io_bufsize = blksize + 4;   io_bufsize = blksize + 4;
562   /* Send ACK for OACK ("block" no: 0) */   }
563    # if ENABLE_FEATURE_TFTP_PROGRESS_BAR
564     if (remote_file && G.size == 0) { /* if we don't know it yet */
565     res = tftp_get_option("tsize", &rbuf[2], len - 2);
566     if (res) {
567     G.size = bb_strtoull(res, NULL, 10);
568     if (G.size)
569     tftp_progress_init();
570     }
571     }
572    # endif
573     if (CMD_GET(option_mask32)) {
574     /* We'll send ACK for OACK,
575     * such ACK has "block no" of 0 */
576   block_nr = 0;   block_nr = 0;
  continue;  
577   }   }
578   /* rfc2347:   continue;
  * "An option not acknowledged by the server  
  *  must be ignored by the client and server  
  *  as if it were never requested." */  
579   }   }
580     /* rfc2347:
581     * "An option not acknowledged by the server
582     * must be ignored by the client and server
583     * as if it were never requested." */
584   bb_error_msg("server only supports blocksize of 512");   bb_error_msg("server only supports blocksize of 512");
585   blksize = TFTP_BLKSIZE_DEFAULT;   blksize = TFTP_BLKSIZE_DEFAULT;
586   io_bufsize = TFTP_BLKSIZE_DEFAULT + 4;   io_bufsize = TFTP_BLKSIZE_DEFAULT + 4;
# Line 503  static int tftp_protocol( Line 602  static int tftp_protocol(
602   }   }
603   continue; /* send ACK */   continue; /* send ACK */
604   }   }
605    /* Disabled to cope with servers with Sorcerer's Apprentice Syndrome */
606    #if 0
607   if (recv_blk == (block_nr - 1)) {   if (recv_blk == (block_nr - 1)) {
608   /* Server lost our TFTP_ACK.  Resend it */   /* Server lost our TFTP_ACK.  Resend it */
609   block_nr = recv_blk;   block_nr = recv_blk;
610   continue;   continue;
611   }   }
612    #endif
613   }   }
614    
615   if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) {   if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) {
# Line 543  static int tftp_protocol( Line 645  static int tftp_protocol(
645   strcpy((char*)error_pkt_str, bb_msg_read_error);   strcpy((char*)error_pkt_str, bb_msg_read_error);
646   send_err_pkt:   send_err_pkt:
647   if (error_pkt_str[0])   if (error_pkt_str[0])
648   bb_error_msg((char*)error_pkt_str);   bb_error_msg("%s", (char*)error_pkt_str);
649   error_pkt[1] = TFTP_ERROR;   error_pkt[1] = TFTP_ERROR;
650   xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str),   xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str),
651   &peer_lsa->u.sa, peer_lsa->len);   &peer_lsa->u.sa, peer_lsa->len);
652   return EXIT_FAILURE;   return EXIT_FAILURE;
653  #undef remote_file  #undef remote_file
 #undef tsize  
654  }  }
655    
656  #if ENABLE_TFTP  #if ENABLE_TFTP
# Line 560  int tftp_main(int argc UNUSED_PARAM, cha Line 661  int tftp_main(int argc UNUSED_PARAM, cha
661   len_and_sockaddr *peer_lsa;   len_and_sockaddr *peer_lsa;
662   const char *local_file = NULL;   const char *local_file = NULL;
663   const char *remote_file = NULL;   const char *remote_file = NULL;
664  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  # if ENABLE_FEATURE_TFTP_BLOCKSIZE
665   const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR;   const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR;
666   int blksize;   int blksize;
667  #endif  # endif
668   int result;   int result;
669   int port;   int port;
670   USE_GETPUT(int opt;)   IF_GETPUT(int opt;)
671    
672   INIT_G();   INIT_G();
673    
674   /* -p or -g is mandatory, and they are mutually exclusive */   /* -p or -g is mandatory, and they are mutually exclusive */
675   opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")   opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:")
676   USE_GETPUT("g--p:p--g:");   IF_GETPUT("g--p:p--g:");
677    
678   USE_GETPUT(opt =) getopt32(argv,   IF_GETPUT(opt =) getopt32(argv,
679   USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")   IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p")
680   "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),   "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:"),
681   &local_file, &remote_file   &local_file, &remote_file
682   USE_FEATURE_TFTP_BLOCKSIZE(, &blksize_str));   IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str));
683   argv += optind;   argv += optind;
684    
685  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  # if ENABLE_FEATURE_TFTP_BLOCKSIZE
686   /* Check if the blksize is valid:   /* Check if the blksize is valid:
687   * RFC2348 says between 8 and 65464 */   * RFC2348 says between 8 and 65464 */
688   blksize = tftp_blksize_check(blksize_str, 65564);   blksize = tftp_blksize_check(blksize_str, 65564);
# Line 589  int tftp_main(int argc UNUSED_PARAM, cha Line 690  int tftp_main(int argc UNUSED_PARAM, cha
690   //bb_error_msg("bad block size");   //bb_error_msg("bad block size");
691   return EXIT_FAILURE;   return EXIT_FAILURE;
692   }   }
693  #endif  # endif
694    
695   if (!local_file)   if (remote_file) {
696   local_file = remote_file;   if (!local_file) {
697   if (!remote_file)   const char *slash = strrchr(remote_file, '/');
698     local_file = slash ? slash + 1 : remote_file;
699     }
700     } else {
701   remote_file = local_file;   remote_file = local_file;
702     }
703    
704   /* Error if filename or host is not known */   /* Error if filename or host is not known */
705   if (!remote_file || !argv[0])   if (!remote_file || !argv[0])
706   bb_show_usage();   bb_show_usage();
# Line 602  int tftp_main(int argc UNUSED_PARAM, cha Line 708  int tftp_main(int argc UNUSED_PARAM, cha
708   port = bb_lookup_port(argv[1], "udp", 69);   port = bb_lookup_port(argv[1], "udp", 69);
709   peer_lsa = xhost2sockaddr(argv[0], port);   peer_lsa = xhost2sockaddr(argv[0], port);
710    
711  #if ENABLE_TFTP_DEBUG  # if ENABLE_TFTP_DEBUG
712   fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",   fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",
713   xmalloc_sockaddr2dotted(&peer_lsa->u.sa),   xmalloc_sockaddr2dotted(&peer_lsa->u.sa),
714   remote_file, local_file);   remote_file, local_file);
715  #endif  # endif
716    
717    # if ENABLE_FEATURE_TFTP_PROGRESS_BAR
718     G.file = remote_file;
719    # endif
720   result = tftp_protocol(   result = tftp_protocol(
721   NULL /*our_lsa*/, peer_lsa,   NULL /*our_lsa*/, peer_lsa,
722   local_file, remote_file   local_file, remote_file
723   USE_FEATURE_TFTP_BLOCKSIZE(USE_TFTPD(, NULL /*tsize*/))   IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */)
724   USE_FEATURE_TFTP_BLOCKSIZE(, blksize)   IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
725   );   );
726     tftp_progress_done();
727    
728   if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) {   if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) {
729   unlink(local_file);   unlink(local_file);
# Line 624  int tftp_main(int argc UNUSED_PARAM, cha Line 734  int tftp_main(int argc UNUSED_PARAM, cha
734  #endif /* ENABLE_TFTP */  #endif /* ENABLE_TFTP */
735    
736  #if ENABLE_TFTPD  #if ENABLE_TFTPD
   
 /* TODO: libbb candidate? */  
 static len_and_sockaddr *get_sock_lsa(int s)  
 {  
  len_and_sockaddr *lsa;  
  socklen_t len = 0;  
   
  if (getsockname(s, NULL, &len) != 0)  
  return NULL;  
  lsa = xzalloc(LSA_LEN_SIZE + len);  
  lsa->len = len;  
  getsockname(s, &lsa->u.sa, &lsa->len);  
  return lsa;  
 }  
   
737  int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;  int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
738  int tftpd_main(int argc UNUSED_PARAM, char **argv)  int tftpd_main(int argc UNUSED_PARAM, char **argv)
739  {  {
# Line 647  int tftpd_main(int argc UNUSED_PARAM, ch Line 742  int tftpd_main(int argc UNUSED_PARAM, ch
742   char *local_file, *mode;   char *local_file, *mode;
743   const char *error_msg;   const char *error_msg;
744   int opt, result, opcode;   int opt, result, opcode;
745   USE_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)   IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
746   USE_FEATURE_TFTP_BLOCKSIZE(char *tsize = NULL;)   IF_FEATURE_TFTP_BLOCKSIZE(int want_transfer_size = 0;)
747    
748   INIT_G();   INIT_G();
749    
# Line 679  int tftpd_main(int argc UNUSED_PARAM, ch Line 774  int tftpd_main(int argc UNUSED_PARAM, ch
774   opcode = ntohs(*(uint16_t*)block_buf);   opcode = ntohs(*(uint16_t*)block_buf);
775   if (result < 4 || result >= sizeof(block_buf)   if (result < 4 || result >= sizeof(block_buf)
776   || block_buf[result-1] != '\0'   || block_buf[result-1] != '\0'
777   || (USE_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */   || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */
778       USE_GETPUT(&&)       IF_GETPUT(&&)
779       USE_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */       IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */
780      )      )
781   ) {   ) {
782   goto err;   goto err;
# Line 695  int tftpd_main(int argc UNUSED_PARAM, ch Line 790  int tftpd_main(int argc UNUSED_PARAM, ch
790   if (mode >= block_buf + result || strcmp(mode, "octet") != 0) {   if (mode >= block_buf + result || strcmp(mode, "octet") != 0) {
791   goto err;   goto err;
792   }   }
793  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  # if ENABLE_FEATURE_TFTP_BLOCKSIZE
794   {   {
795   char *res;   char *res;
796   char *opt_str = mode + sizeof("octet");   char *opt_str = mode + sizeof("octet");
# Line 710  int tftpd_main(int argc UNUSED_PARAM, ch Line 805  int tftpd_main(int argc UNUSED_PARAM, ch
805   goto do_proto;   goto do_proto;
806   }   }
807   }   }
808     if (opcode != TFTP_WRQ /* download? */
809   /* did client ask us about file size? */   /* did client ask us about file size? */
810   tsize = tftp_get_option("tsize", opt_str, opt_len);   && tftp_get_option("tsize", opt_str, opt_len)
811     ) {
812     want_transfer_size = 1;
813     }
814   }   }
815   }   }
816  #endif  # endif
817    
818   if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) {   if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) {
819   if (opt & TFTPD_OPT_r) {   if (opt & TFTPD_OPT_r) {
# Line 723  int tftpd_main(int argc UNUSED_PARAM, ch Line 822  int tftpd_main(int argc UNUSED_PARAM, ch
822   error_msg = bb_msg_write_error;   error_msg = bb_msg_write_error;
823   goto err;   goto err;
824   }   }
825   USE_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */   IF_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */
826   } else {   } else {
827   USE_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */   IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */
828   }   }
829    
830   /* NB: if error_pkt_str or error_pkt_reason is set up,   /* NB: if error_pkt_str or error_pkt_reason is set up,
# Line 736  int tftpd_main(int argc UNUSED_PARAM, ch Line 835  int tftpd_main(int argc UNUSED_PARAM, ch
835   /* tftp_protocol() will create new one, bound to particular local IP */   /* tftp_protocol() will create new one, bound to particular local IP */
836   result = tftp_protocol(   result = tftp_protocol(
837   our_lsa, peer_lsa,   our_lsa, peer_lsa,
838   local_file USE_TFTP(, NULL /*remote_file*/)   local_file IF_TFTP(, NULL /*remote_file*/)
839   USE_FEATURE_TFTP_BLOCKSIZE(, tsize)   IF_FEATURE_TFTP_BLOCKSIZE(, want_transfer_size)
840   USE_FEATURE_TFTP_BLOCKSIZE(, blksize)   IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
841   );   );
842    
843   return result;   return result;

Legend:
Removed from v.983  
changed lines
  Added in v.984