Magellan Linux

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

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

revision 815 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 2  Line 2 
2  /* -------------------------------------------------------------------------  /* -------------------------------------------------------------------------
3   * tftp.c   * tftp.c
4   *   *
5   * A simple tftp client for busybox.   * A simple tftp client/server for busybox.
6   * Tries to follow RFC1350.   * Tries to follow RFC1350.
7   * Only "octet" mode supported.   * Only "octet" mode supported.
8   * Optional blocksize negotiation (RFC2347 + RFC2348)   * Optional blocksize negotiation (RFC2347 + RFC2348)
# Line 16  Line 16 
16   *   *
17   * utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>   * utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
18   *   *
19     * tftpd added by Denys Vlasenko & Vladimir Dronnikov
20     *
21   * 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.
22   * ------------------------------------------------------------------------- */   * ------------------------------------------------------------------------- */
23    
24  #include "busybox.h"  #include "libbb.h"
   
25    
26  #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT  #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
27    
28  #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */  #define TFTP_BLKSIZE_DEFAULT       512  /* according to RFC 1350, don't change */
29  #define TFTP_TIMEOUT 5 /* seconds */  #define TFTP_BLKSIZE_DEFAULT_STR "512"
30  #define TFTP_NUM_RETRIES 5 /* number of retries */  #define TFTP_TIMEOUT_MS             50
31    #define TFTP_MAXTIMEOUT_MS        2000
32    #define TFTP_NUM_RETRIES            12  /* number of backed-off retries */
33    
34  /* opcodes we support */  /* opcodes we support */
35  #define TFTP_RRQ   1  #define TFTP_RRQ   1
# Line 36  Line 39 
39  #define TFTP_ERROR 5  #define TFTP_ERROR 5
40  #define TFTP_OACK  6  #define TFTP_OACK  6
41    
42  static const char *const tftp_bb_error_msg[] = {  /* error codes sent over network (we use only 0, 1, 3 and 8) */
43   "Undefined error",  /* generic (error message is included in the packet) */
44   "File not found",  #define ERR_UNSPEC   0
45   "Access violation",  #define ERR_NOFILE   1
46   "Disk full or allocation error",  #define ERR_ACCESS   2
47   "Illegal TFTP operation",  /* disk full or allocation exceeded */
48   "Unknown transfer ID",  #define ERR_WRITE    3
49   "File already exists",  #define ERR_OP       4
50   "No such user"  #define ERR_BAD_ID   5
51    #define ERR_EXIST    6
52    #define ERR_BAD_USER 7
53    #define ERR_BAD_OPT  8
54    
55    /* masks coming from getopt32 */
56    enum {
57     TFTP_OPT_GET = (1 << 0),
58     TFTP_OPT_PUT = (1 << 1),
59     /* pseudo option: if set, it's tftpd */
60     TFTPD_OPT = (1 << 7) * ENABLE_TFTPD,
61     TFTPD_OPT_r = (1 << 8) * ENABLE_TFTPD,
62     TFTPD_OPT_c = (1 << 9) * ENABLE_TFTPD,
63     TFTPD_OPT_u = (1 << 10) * ENABLE_TFTPD,
64  };  };
65    
66  #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT  #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
67  #define USE_GETPUT(a)  #define USE_GETPUT(...)
68  #define CMD_GET(cmd) 1  #define CMD_GET(cmd) 1
69  #define CMD_PUT(cmd) 0  #define CMD_PUT(cmd) 0
70  #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT  #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
71  #define USE_GETPUT(a)  #define USE_GETPUT(...)
72  #define CMD_GET(cmd) 0  #define CMD_GET(cmd) 0
73  #define CMD_PUT(cmd) 1  #define CMD_PUT(cmd) 1
74  #else  #else
75  #define USE_GETPUT(a) a  #define USE_GETPUT(...) __VA_ARGS__
76  /* masks coming from getpot32 */  #define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET)
77  #define CMD_GET(cmd) ((cmd) & 1)  #define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT)
 #define CMD_PUT(cmd) ((cmd) & 2)  
78  #endif  #endif
79  /* NB: in the code below  /* NB: in the code below
80   * CMD_GET(cmd) and CMD_GET(cmd) are mutually exclusive   * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
81   */   */
82    
83    
84    struct globals {
85     /* u16 TFTP_ERROR; u16 reason; both network-endian, then error text: */
86     uint8_t error_pkt[4 + 32];
87     char *user_opt;
88     /* used in tftpd_main(), a bit big for stack: */
89     char block_buf[TFTP_BLKSIZE_DEFAULT];
90    };
91    #define G (*(struct globals*)&bb_common_bufsiz1)
92    #define block_buf        (G.block_buf   )
93    #define user_opt         (G.user_opt    )
94    #define error_pkt        (G.error_pkt   )
95    #define INIT_G() do { } while (0)
96    
97    #define error_pkt_reason (error_pkt[3])
98    #define error_pkt_str    (error_pkt + 4)
99    
100    
101  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
102    
103  static int tftp_blocksize_check(int blocksize, int bufsize)  static int tftp_blksize_check(const char *blksize_str, int maxsize)
104  {  {
105   /* Check if the blocksize is valid:   /* Check if the blksize is valid:
106   * RFC2348 says between 8 and 65464,   * RFC2348 says between 8 and 65464,
107   * but our implementation makes it impossible   * but our implementation makes it impossible
108   * to use blocksizes smaller than 22 octets.   * to use blksizes smaller than 22 octets. */
109   */   unsigned blksize = bb_strtou(blksize_str, NULL, 10);
110     if (errno
111   if ((bufsize && (blocksize > bufsize))   || (blksize < 24) || (blksize > maxsize)
  || (blocksize < 8) || (blocksize > 65564)  
112   ) {   ) {
113   bb_error_msg("bad blocksize");   bb_error_msg("bad blocksize '%s'", blksize_str);
114   return 0;   return -1;
115   }   }
116    #if ENABLE_TFTP_DEBUG
117   return blocksize;   bb_error_msg("using blksize %u", blksize);
118    #endif
119     return blksize;
120  }  }
121    
122  static char *tftp_option_get(char *buf, int len, const char * const option)  static char *tftp_get_option(const char *option, char *buf, int len)
123  {  {
124   int opt_val = 0;   int opt_val = 0;
125   int opt_found = 0;   int opt_found = 0;
126   int k;   int k;
127    
128   while (len > 0) {   /* buf points to:
129   /* Make sure the options are terminated correctly */   * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */
130    
131     while (len > 0) {
132     /* Make sure options are terminated correctly */
133   for (k = 0; k < len; k++) {   for (k = 0; k < len; k++) {
134   if (buf[k] == '\0') {   if (buf[k] == '\0') {
135   break;   goto nul_found;
136   }   }
137   }   }
138     return NULL;
139   if (k >= len) {   nul_found:
140   break;   if (opt_val == 0) { /* it's "name" part */
  }  
   
  if (opt_val == 0) {  
141   if (strcasecmp(buf, option) == 0) {   if (strcasecmp(buf, option) == 0) {
142   opt_found = 1;   opt_found = 1;
143   }   }
144   } else {   } else if (opt_found) {
145   if (opt_found) {   return buf;
  return buf;  
  }  
146   }   }
147    
148   k++;   k++;
   
149   buf += k;   buf += k;
150   len -= k;   len -= k;
   
151   opt_val ^= 1;   opt_val ^= 1;
152   }   }
153    
# Line 128  static char *tftp_option_get(char *buf, Line 156  static char *tftp_option_get(char *buf,
156    
157  #endif  #endif
158    
159  static int tftp(  static int tftp_protocol(
160  #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT   len_and_sockaddr *our_lsa,
  const int cmd,  
 #endif  
161   len_and_sockaddr *peer_lsa,   len_and_sockaddr *peer_lsa,
162   const char *remotefile, const int localfd,   const char *local_file
163   unsigned port, int tftp_bufsize)   USE_TFTP(, const char *remote_file)
164     USE_FEATURE_TFTP_BLOCKSIZE(USE_TFTPD(, void *tsize))
165     USE_FEATURE_TFTP_BLOCKSIZE(, int blksize))
166  {  {
167   struct timeval tv;  #if !ENABLE_TFTP
168   fd_set rfds;  #define remote_file NULL
169   int socketfd;  #endif
170    #if !(ENABLE_FEATURE_TFTP_BLOCKSIZE && ENABLE_TFTPD)
171    #define tsize NULL
172    #endif
173    #if !ENABLE_FEATURE_TFTP_BLOCKSIZE
174     enum { blksize = TFTP_BLKSIZE_DEFAULT };
175    #endif
176    
177     struct pollfd pfd[1];
178    #define socket_fd (pfd[0].fd)
179   int len;   int len;
180   int opcode = 0;   int send_len;
181   int finished = 0;   USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
182   int timeout = TFTP_NUM_RETRIES;   smallint finished = 0;
183   uint16_t block_nr = 1;   uint16_t opcode;
184   uint16_t tmp;   uint16_t block_nr;
185     uint16_t recv_blk;
186     int open_mode, local_fd;
187     int retries, waittime_ms;
188     int io_bufsize = blksize + 4;
189   char *cp;   char *cp;
   
  USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)  
   
  unsigned org_port;  
  len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);  
   
190   /* Can't use RESERVE_CONFIG_BUFFER here since the allocation   /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
191   * size varies meaning BUFFERS_GO_ON_STACK would fail */   * size varies meaning BUFFERS_GO_ON_STACK would fail */
192   /* We must keep the transmit and receive buffers seperate */   /* We must keep the transmit and receive buffers seperate */
193   /* In case we rcv a garbage pkt and we need to rexmit the last pkt */   /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
194   char *xbuf = xmalloc(tftp_bufsize += 4);   char *xbuf = xmalloc(io_bufsize);
195   char *rbuf = xmalloc(tftp_bufsize);   char *rbuf = xmalloc(io_bufsize);
196    
197   port = org_port = htons(port);   socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);
198     setsockopt_reuseaddr(socket_fd);
199    
200   socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);   block_nr = 1;
201     cp = xbuf + 2;
202    
203   /* build opcode */   if (!ENABLE_TFTP || our_lsa) {
204   opcode = TFTP_WRQ;   /* tftpd */
205   if (CMD_GET(cmd)) {  
206   opcode = TFTP_RRQ;   /* Create a socket which is:
207     * 1. bound to IP:port peer sent 1st datagram to,
208     * 2. connected to peer's IP:port
209     * This way we will answer from the IP:port peer
210     * expects, will not get any other packets on
211     * the socket, and also plain read/write will work. */
212     xbind(socket_fd, &our_lsa->u.sa, our_lsa->len);
213     xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
214    
215     /* Is there an error already? Send pkt and bail out */
216     if (error_pkt_reason || error_pkt_str[0])
217     goto send_err_pkt;
218    
219     if (CMD_GET(option_mask32)) {
220     /* it's upload - we must ACK 1st packet (with filename)
221     * as if it's "block 0" */
222     block_nr = 0;
223     }
224    
225     if (user_opt) {
226     struct passwd *pw = getpwnam(user_opt);
227     if (!pw)
228     bb_error_msg_and_die("unknown user %s", user_opt);
229     change_identity(pw); /* initgroups, setgid, setuid */
230     }
231   }   }
232    
233   while (1) {   /* Open local file (must be after changing user) */
234   cp = xbuf;   if (CMD_PUT(option_mask32)) {
235     open_mode = O_RDONLY;
236   /* first create the opcode part */   } else {
237   /* (this 16bit store is aligned) */   open_mode = O_WRONLY | O_TRUNC | O_CREAT;
238   *((uint16_t*)cp) = htons(opcode);  #if ENABLE_TFTPD
239   cp += 2;   if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) {
240     /* tftpd without -c */
241   /* add filename and mode */   open_mode = O_WRONLY | O_TRUNC;
242   if (CMD_GET(cmd) ? (opcode == TFTP_RRQ) : (opcode == TFTP_WRQ)) {   }
243   int too_long = 0;  #endif
244     }
245   /* see if the filename fits into xbuf   if (!(option_mask32 & TFTPD_OPT)) {
246   * and fill in packet.  */   local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;
247   len = strlen(remotefile) + 1;   if (NOT_LONE_DASH(local_file))
248     local_fd = xopen(local_file, open_mode);
249   if ((cp + len) >= &xbuf[tftp_bufsize - 1]) {   } else {
250   too_long = 1;   local_fd = open(local_file, open_mode);
251   } else {   if (local_fd < 0) {
252   safe_strncpy(cp, remotefile, len);   error_pkt_reason = ERR_NOFILE;
253   cp += len;   strcpy((char*)error_pkt_str, "can't open file");
254   }   goto send_err_pkt;
255     }
256   if (too_long || (&xbuf[tftp_bufsize - 1] - cp) < sizeof("octet")) {   }
  bb_error_msg("remote filename too long");  
  break;  
  }  
   
  /* add "mode" part of the package */  
  memcpy(cp, "octet", sizeof("octet"));  
  cp += sizeof("octet");  
257    
258     if (!ENABLE_TFTP || our_lsa) {
259    /* gcc 4.3.1 would NOT optimize it out as it should! */
260  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
261     if (blksize != TFTP_BLKSIZE_DEFAULT || tsize) {
262   len = tftp_bufsize - 4; /* data block size */   /* Create and send OACK packet. */
263     /* For the download case, block_nr is still 1 -
264   if (len != TFTP_BLOCKSIZE_DEFAULT) {   * we expect 1st ACK from peer to be for (block_nr-1),
265     * that is, for "block 0" which is our OACK pkt */
266   if ((&xbuf[tftp_bufsize - 1] - cp) < 15) {   opcode = TFTP_OACK;
267   bb_error_msg("remote filename too long");   goto add_blksize_opt;
268   break;   }
  }  
   
  /* add "blksize" + number of blocks  */  
  memcpy(cp, "blksize", sizeof("blksize"));  
  cp += sizeof("blksize");  
  cp += snprintf(cp, 6, "%d", len) + 1;  
   
  want_option_ack = 1;  
  }  
269  #endif  #endif
270     } else {
271    /* Removing it, or using if() statement instead of #if may lead to
272     * "warning: null argument where non-null required": */
273    #if ENABLE_TFTP
274     /* tftp */
275    
276     /* We can't (and don't really need to) bind the socket:
277     * we don't know from which local IP datagrams will be sent,
278     * but kernel will pick the same IP every time (unless routing
279     * table is changed), thus peer will see dgrams consistently
280     * coming from the same IP.
281     * We would like to connect the socket, but since peer's
282     * UDP code can be less perfect than ours, _peer's_ IP:port
283     * in replies may differ from IP:port we used to send
284     * our first packet. We can connect() only when we get
285     * first reply. */
286    
287     /* build opcode */
288     opcode = TFTP_WRQ;
289     if (CMD_GET(option_mask32)) {
290     opcode = TFTP_RRQ;
291   }   }
292     /* add filename and mode */
293     /* fill in packet if the filename fits into xbuf */
294     len = strlen(remote_file) + 1;
295     if (2 + len + sizeof("octet") >= io_bufsize) {
296     bb_error_msg("remote filename is too long");
297     goto ret;
298     }
299     strcpy(cp, remote_file);
300     cp += len;
301     /* add "mode" part of the package */
302     strcpy(cp, "octet");
303     cp += sizeof("octet");
304    
305   /* add ack and data */  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
306     if (blksize == TFTP_BLKSIZE_DEFAULT)
307   if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) {   goto send_pkt;
  /* TODO: unaligned access! */  
  *((uint16_t*)cp) = htons(block_nr);  
  cp += 2;  
  block_nr++;  
   
  if (CMD_PUT(cmd) && (opcode == TFTP_DATA)) {  
  len = full_read(localfd, cp, tftp_bufsize - 4);  
   
  if (len < 0) {  
  bb_perror_msg(bb_msg_read_error);  
  break;  
  }  
   
  if (len != (tftp_bufsize - 4)) {  
  finished++;  
  }  
308    
309   cp += len;   /* Non-standard blocksize: add option to pkt */
310   }   if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
311     bb_error_msg("remote filename is too long");
312     goto ret;
313   }   }
314     want_option_ack = 1;
315    #endif
316    #endif /* ENABLE_TFTP */
317    
318   /* send packet */  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
319     add_blksize_opt:
320   timeout = TFTP_NUM_RETRIES; /* re-initialize */  #if ENABLE_TFTPD
321   do {   if (tsize) {
322   len = cp - xbuf;   struct stat st;
323  #if ENABLE_DEBUG_TFTP   /* add "tsize", <nul>, size, <nul> */
324   fprintf(stderr, "sending %u bytes\n", len);   strcpy(cp, "tsize");
325   for (cp = xbuf; cp < &xbuf[len]; cp++)   cp += sizeof("tsize");
326   fprintf(stderr, "%02x ", (unsigned char) *cp);   fstat(local_fd, &st);
327   fprintf(stderr, "\n");   cp += snprintf(cp, 10, "%u", (int) st.st_size) + 1;
328     }
329    #endif
330     if (blksize != TFTP_BLKSIZE_DEFAULT) {
331     /* add "blksize", <nul>, blksize, <nul> */
332     strcpy(cp, "blksize");
333     cp += sizeof("blksize");
334     cp += snprintf(cp, 6, "%d", blksize) + 1;
335     }
336  #endif  #endif
337   if (sendto(socketfd, xbuf, len, 0,   /* First packet is built, so skip packet generation */
338   &peer_lsa->sa, peer_lsa->len) < 0) {   goto send_pkt;
339   bb_perror_msg("send");   }
  len = -1;  
  break;  
  }  
340    
341   if (finished && (opcode == TFTP_ACK)) {   /* Using mostly goto's - continue/break will be less clear
342   break;   * in where we actually jump to */
343   }   while (1) {
344     /* Build ACK or DATA */
345     cp = xbuf + 2;
346     *((uint16_t*)cp) = htons(block_nr);
347     cp += 2;
348     block_nr++;
349     opcode = TFTP_ACK;
350     if (CMD_PUT(option_mask32)) {
351     opcode = TFTP_DATA;
352     len = full_read(local_fd, cp, blksize);
353     if (len < 0) {
354     goto send_read_err_pkt;
355     }
356     if (len != blksize) {
357     finished = 1;
358     }
359     cp += len;
360     }
361     send_pkt:
362     /* Send packet */
363     *((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
364     send_len = cp - xbuf;
365     /* NB: send_len value is preserved in code below
366     * for potential resend */
367    
368     retries = TFTP_NUM_RETRIES; /* re-initialize */
369     waittime_ms = TFTP_TIMEOUT_MS;
370    
371     send_again:
372    #if ENABLE_TFTP_DEBUG
373     fprintf(stderr, "sending %u bytes\n", send_len);
374     for (cp = xbuf; cp < &xbuf[send_len]; cp++)
375     fprintf(stderr, "%02x ", (unsigned char) *cp);
376     fprintf(stderr, "\n");
377    #endif
378     xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
379     /* Was it final ACK? then exit */
380     if (finished && (opcode == TFTP_ACK))
381     goto ret;
382    
  /* receive packet */  
383   recv_again:   recv_again:
384   tv.tv_sec = TFTP_TIMEOUT;   /* Receive packet */
385   tv.tv_usec = 0;   /*pfd[0].fd = socket_fd;*/
386     pfd[0].events = POLLIN;
387   FD_ZERO(&rfds);   switch (safe_poll(pfd, 1, waittime_ms)) {
388   FD_SET(socketfd, &rfds);   default:
389     /*bb_perror_msg("poll"); - done in safe_poll */
390   switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {   goto ret;
391   unsigned from_port;   case 0:
392   case 1:   retries--;
393   from->len = peer_lsa->len;   if (retries == 0) {
  memset(&from->sa, 0, peer_lsa->len);  
  len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,  
  &from->sa, &from->len);  
  if (len < 0) {  
  bb_perror_msg("recvfrom");  
  break;  
  }  
  from_port = get_nport(from);  
  if (port == org_port) {  
  /* Our first query went to port 69  
  * but reply will come from different one.  
  * Remember and use this new port */  
  port = from_port;  
  set_nport(peer_lsa, from_port);  
  }  
  if (port != from_port)  
  goto recv_again;  
  timeout = 0;  
  break;  
  case 0:  
394   bb_error_msg("timeout");   bb_error_msg("timeout");
395   timeout--;   goto ret; /* no err packet sent */
  if (timeout == 0) {  
  len = -1;  
  bb_error_msg("last timeout");  
  }  
  break;  
  default:  
  bb_perror_msg("select");  
  len = -1;  
396   }   }
397    
398   } while (timeout && (len >= 0));   /* exponential backoff with limit */
399     waittime_ms += waittime_ms/2;
400   if (finished || (len < 0)) {   if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
401   break;   waittime_ms = TFTP_MAXTIMEOUT_MS;
402     }
403    
404     goto send_again; /* resend last sent pkt */
405     case 1:
406     if (!our_lsa) {
407     /* tftp (not tftpd!) receiving 1st packet */
408     our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */
409     len = recvfrom(socket_fd, rbuf, io_bufsize, 0,
410     &peer_lsa->u.sa, &peer_lsa->len);
411     /* Our first dgram went to port 69
412     * but reply may come from different one.
413     * Remember and use this new port (and IP) */
414     if (len >= 0)
415     xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
416     } else {
417     /* tftpd, or not the very first packet:
418     * socket is connect()ed, can just read from it. */
419     /* Don't full_read()!
420     * This is not TCP, one read == one pkt! */
421     len = safe_read(socket_fd, rbuf, io_bufsize);
422     }
423     if (len < 0) {
424     goto send_read_err_pkt;
425     }
426     if (len < 4) { /* too small? */
427     goto recv_again;
428     }
429   }   }
430    
431   /* process received packet */   /* Process recv'ed packet */
  /* (both accesses seems to be aligned) */  
   
432   opcode = ntohs( ((uint16_t*)rbuf)[0] );   opcode = ntohs( ((uint16_t*)rbuf)[0] );
433   tmp = ntohs( ((uint16_t*)rbuf)[1] );   recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
434    #if ENABLE_TFTP_DEBUG
435  #if ENABLE_DEBUG_TFTP   fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
  fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);  
436  #endif  #endif
   
437   if (opcode == TFTP_ERROR) {   if (opcode == TFTP_ERROR) {
438   const char *msg = NULL;   static const char errcode_str[] ALIGN1 =
439     "\0"
440     "file not found\0"
441     "access violation\0"
442     "disk full\0"
443     "bad operation\0"
444     "unknown transfer id\0"
445     "file already exists\0"
446     "no such user\0"
447     "bad option";
448    
449   if (rbuf[4] != '\0') {   const char *msg = "";
  msg = &rbuf[4];  
  rbuf[tftp_bufsize - 1] = '\0';  
  } else if (tmp < (sizeof(tftp_bb_error_msg)  
   / sizeof(char *))) {  
  msg = tftp_bb_error_msg[tmp];  
  }  
450    
451   if (msg) {   if (len > 4 && rbuf[4] != '\0') {
452   bb_error_msg("server says: %s", msg);   msg = &rbuf[4];
453     rbuf[io_bufsize - 1] = '\0'; /* paranoia */
454     } else if (recv_blk <= 8) {
455     msg = nth_string(errcode_str, recv_blk);
456   }   }
457     bb_error_msg("server error: (%u) %s", recv_blk, msg);
458   break;   goto ret;
459   }   }
460    
461  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
462   if (want_option_ack) {   if (want_option_ack) {
   
463   want_option_ack = 0;   want_option_ack = 0;
   
464   if (opcode == TFTP_OACK) {   if (opcode == TFTP_OACK) {
465   /* server seems to support options */   /* server seems to support options */
466   char *res;   char *res;
467    
468   res = tftp_option_get(&rbuf[2], len - 2, "blksize");   res = tftp_get_option("blksize", &rbuf[2], len - 2);
   
469   if (res) {   if (res) {
470   int blksize = xatoi_u(res);   blksize = tftp_blksize_check(res, blksize);
471     if (blksize < 0) {
472   if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {   error_pkt_reason = ERR_BAD_OPT;
473   if (CMD_PUT(cmd)) {   goto send_err_pkt;
  opcode = TFTP_DATA;  
  } else {  
  opcode = TFTP_ACK;  
  }  
 #if ENABLE_DEBUG_TFTP  
  fprintf(stderr, "using blksize %u\n",  
  blksize);  
 #endif  
  tftp_bufsize = blksize + 4;  
  block_nr = 0;  
  continue;  
474   }   }
475     io_bufsize = blksize + 4;
476     /* Send ACK for OACK ("block" no: 0) */
477     block_nr = 0;
478     continue;
479   }   }
480   /* FIXME:   /* rfc2347:
481   * we should send ERROR 8 */   * "An option not acknowledged by the server
482   bb_error_msg("bad server option");   *  must be ignored by the client and server
483   break;   *  as if it were never requested." */
484   }   }
485     bb_error_msg("server only supports blocksize of 512");
486   bb_error_msg("warning: blksize not supported by server"   blksize = TFTP_BLKSIZE_DEFAULT;
487   " - reverting to 512");   io_bufsize = TFTP_BLKSIZE_DEFAULT + 4;
488     }
489   tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;  #endif
490   }   /* block_nr is already advanced to next block# we expect
491  #endif   * to get / block# we are about to send next time */
492    
493   if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {   if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) {
494   if (tmp == block_nr) {   if (recv_blk == block_nr) {
495   len = full_write(localfd, &rbuf[4], len - 4);   int sz = full_write(local_fd, &rbuf[4], len - 4);
496     if (sz != len - 4) {
497   if (len < 0) {   strcpy((char*)error_pkt_str, bb_msg_write_error);
498   bb_perror_msg(bb_msg_write_error);   error_pkt_reason = ERR_WRITE;
499   break;   goto send_err_pkt;
500   }   }
501     if (sz != blksize) {
502   if (len != (tftp_bufsize - 4)) {   finished = 1;
  finished++;  
503   }   }
504     continue; /* send ACK */
  opcode = TFTP_ACK;  
  continue;  
505   }   }
506   /* in case the last ack disappeared into the ether */   if (recv_blk == (block_nr - 1)) {
  if (tmp == (block_nr - 1)) {  
  --block_nr;  
  opcode = TFTP_ACK;  
  continue;  
 // tmp==(block_nr-1) and (tmp+1)==block_nr is always same, I think. wtf?  
  } else if (tmp + 1 == block_nr) {  
507   /* Server lost our TFTP_ACK.  Resend it */   /* Server lost our TFTP_ACK.  Resend it */
508   block_nr = tmp;   block_nr = recv_blk;
  opcode = TFTP_ACK;  
  continue;  
  }  
  }  
   
  if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {  
  if (tmp == (uint16_t) (block_nr - 1)) {  
  if (finished) {  
  break;  
  }  
   
  opcode = TFTP_DATA;  
509   continue;   continue;
510   }   }
511   }   }
  }  
512    
513     if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) {
514     /* did peer ACK our last DATA pkt? */
515     if (recv_blk == (uint16_t) (block_nr - 1)) {
516     if (finished)
517     goto ret;
518     continue; /* send next block */
519     }
520     }
521     /* Awww... recv'd packet is not recognized! */
522     goto recv_again;
523     /* why recv_again? - rfc1123 says:
524     * "The sender (i.e., the side originating the DATA packets)
525     *  must never resend the current DATA packet on receipt
526     *  of a duplicate ACK".
527     * DATA pkts are resent ONLY on timeout.
528     * Thus "goto send_again" will ba a bad mistake above.
529     * See:
530     * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
531     */
532     } /* end of "while (1)" */
533     ret:
534   if (ENABLE_FEATURE_CLEAN_UP) {   if (ENABLE_FEATURE_CLEAN_UP) {
535   close(socketfd);   close(local_fd);
536     close(socket_fd);
537   free(xbuf);   free(xbuf);
538   free(rbuf);   free(rbuf);
539   }   }
540     return finished == 0; /* returns 1 on failure */
541    
542   return finished ? EXIT_SUCCESS : EXIT_FAILURE;   send_read_err_pkt:
543     strcpy((char*)error_pkt_str, bb_msg_read_error);
544     send_err_pkt:
545     if (error_pkt_str[0])
546     bb_error_msg((char*)error_pkt_str);
547     error_pkt[1] = TFTP_ERROR;
548     xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str),
549     &peer_lsa->u.sa, peer_lsa->len);
550     return EXIT_FAILURE;
551    #undef remote_file
552    #undef tsize
553  }  }
554    
555  int tftp_main(int argc, char **argv)  #if ENABLE_TFTP
556    
557    int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
558    int tftp_main(int argc UNUSED_PARAM, char **argv)
559  {  {
560   len_and_sockaddr *peer_lsa;   len_and_sockaddr *peer_lsa;
561   const char *localfile = NULL;   const char *local_file = NULL;
562   const char *remotefile = NULL;   const char *remote_file = NULL;
563  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
564   const char *sblocksize = NULL;   const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR;
565     int blksize;
566  #endif  #endif
  int port;  
  USE_GETPUT(int cmd;)  
  int fd = -1;  
  int flags = 0;  
567   int result;   int result;
568   int blocksize = TFTP_BLOCKSIZE_DEFAULT;   int port;
569     USE_GETPUT(int opt;)
570    
571     INIT_G();
572    
573   /* -p or -g is mandatory, and they are mutually exclusive */   /* -p or -g is mandatory, and they are mutually exclusive */
574   opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")   opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
575   USE_GETPUT("?g--p:p--g");   USE_GETPUT("g--p:p--g:");
576    
577   USE_GETPUT(cmd =) getopt32(argc, argv,   USE_GETPUT(opt =) getopt32(argv,
578   USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")   USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
579   "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),   "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
580   &localfile, &remotefile   &local_file, &remote_file
581   USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));   USE_FEATURE_TFTP_BLOCKSIZE(, &blksize_str));
582     argv += optind;
  flags = O_RDONLY;  
  if (CMD_GET(cmd))  
  flags = O_WRONLY | O_CREAT | O_TRUNC;  
583    
584  #if ENABLE_FEATURE_TFTP_BLOCKSIZE  #if ENABLE_FEATURE_TFTP_BLOCKSIZE
585   if (sblocksize) {   /* Check if the blksize is valid:
586   blocksize = xatoi_u(sblocksize);   * RFC2348 says between 8 and 65464 */
587   if (!tftp_blocksize_check(blocksize, 0)) {   blksize = tftp_blksize_check(blksize_str, 65564);
588   return EXIT_FAILURE;   if (blksize < 0) {
589   }   //bb_error_msg("bad block size");
590     return EXIT_FAILURE;
591   }   }
592  #endif  #endif
593    
594   if (localfile == NULL)   if (!local_file)
595   localfile = remotefile;   local_file = remote_file;
596   if (remotefile == NULL)   if (!remote_file)
597   remotefile = localfile;   remote_file = local_file;
598   if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))   /* Error if filename or host is not known */
599     if (!remote_file || !argv[0])
600   bb_show_usage();   bb_show_usage();
601    
602   if (localfile == NULL || LONE_DASH(localfile)) {   port = bb_lookup_port(argv[1], "udp", 69);
603   fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;   peer_lsa = xhost2sockaddr(argv[0], port);
604   } else {  
605   fd = xopen3(localfile, flags, 0644);  #if ENABLE_TFTP_DEBUG
606     fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",
607     xmalloc_sockaddr2dotted(&peer_lsa->u.sa),
608     remote_file, local_file);
609    #endif
610    
611     result = tftp_protocol(
612     NULL /*our_lsa*/, peer_lsa,
613     local_file, remote_file
614     USE_FEATURE_TFTP_BLOCKSIZE(USE_TFTPD(, NULL /*tsize*/))
615     USE_FEATURE_TFTP_BLOCKSIZE(, blksize)
616     );
617    
618     if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) {
619     unlink(local_file);
620   }   }
621     return result;
622    }
623    
624   port = bb_lookup_port(argv[optind + 1], "udp", 69);  #endif /* ENABLE_TFTP */
  peer_lsa = host2sockaddr(argv[optind], port);  
625    
626  #if ENABLE_DEBUG_TFTP  #if ENABLE_TFTPD
627   fprintf(stderr, "using server \"%s\", "  
628   "remotefile \"%s\", localfile \"%s\".\n",  /* TODO: libbb candidate? */
629   xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),  static len_and_sockaddr *get_sock_lsa(int s)
630   remotefile, localfile);  {
631  #endif   len_and_sockaddr *lsa;
632     socklen_t len = 0;
633    
634   result = tftp(   if (getsockname(s, NULL, &len) != 0)
635  #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT   return NULL;
636   cmd,   lsa = xzalloc(LSA_LEN_SIZE + len);
637     lsa->len = len;
638     getsockname(s, &lsa->u.sa, &lsa->len);
639     return lsa;
640    }
641    
642    int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
643    int tftpd_main(int argc UNUSED_PARAM, char **argv)
644    {
645     len_and_sockaddr *our_lsa;
646     len_and_sockaddr *peer_lsa;
647     char *local_file, *mode;
648     const char *error_msg;
649     int opt, result, opcode;
650     USE_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
651     USE_FEATURE_TFTP_BLOCKSIZE(char *tsize = NULL;)
652    
653     INIT_G();
654    
655     our_lsa = get_sock_lsa(STDIN_FILENO);
656     if (!our_lsa) {
657     /* This is confusing:
658     *bb_error_msg_and_die("stdin is not a socket");
659     * Better: */
660     bb_show_usage();
661     /* Help text says that tftpd must be used as inetd service,
662     * which is by far the most usual cause of get_sock_lsa
663     * failure */
664     }
665     peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len);
666     peer_lsa->len = our_lsa->len;
667    
668     /* Shifting to not collide with TFTP_OPTs */
669     opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:", &user_opt) << 8);
670     argv += optind;
671     if (argv[0])
672     xchdir(argv[0]);
673    
674     result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf),
675     0 /* flags */,
676     &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len);
677    
678     error_msg = "malformed packet";
679     opcode = ntohs(*(uint16_t*)block_buf);
680     if (result < 4 || result >= sizeof(block_buf)
681     || block_buf[result-1] != '\0'
682     || (USE_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */
683         USE_GETPUT(&&)
684         USE_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */
685        )
686     ) {
687     goto err;
688     }
689     local_file = block_buf + 2;
690     if (local_file[0] == '.' || strstr(local_file, "/.")) {
691     error_msg = "dot in file name";
692     goto err;
693     }
694     mode = local_file + strlen(local_file) + 1;
695     if (mode >= block_buf + result || strcmp(mode, "octet") != 0) {
696     goto err;
697     }
698    #if ENABLE_FEATURE_TFTP_BLOCKSIZE
699     {
700     char *res;
701     char *opt_str = mode + sizeof("octet");
702     int opt_len = block_buf + result - opt_str;
703     if (opt_len > 0) {
704     res = tftp_get_option("blksize", opt_str, opt_len);
705     if (res) {
706     blksize = tftp_blksize_check(res, 65564);
707     if (blksize < 0) {
708     error_pkt_reason = ERR_BAD_OPT;
709     /* will just send error pkt */
710     goto do_proto;
711     }
712     }
713     /* did client ask us about file size? */
714     tsize = tftp_get_option("tsize", opt_str, opt_len);
715     }
716     }
717  #endif  #endif
  peer_lsa, remotefile, fd, port, blocksize);  
718    
719   if (fd > 1) {   if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) {
720   if (ENABLE_FEATURE_CLEAN_UP)   if (opt & TFTPD_OPT_r) {
721   close(fd);   /* This would mean "disk full" - not true */
722   if (CMD_GET(cmd) && result != EXIT_SUCCESS)   /*error_pkt_reason = ERR_WRITE;*/
723   unlink(localfile);   error_msg = bb_msg_write_error;
724     goto err;
725     }
726     USE_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */
727     } else {
728     USE_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */
729   }   }
730    
731     /* NB: if error_pkt_str or error_pkt_reason is set up,
732     * tftp_protocol() just sends one error pkt and returns */
733    
734     do_proto:
735     close(STDIN_FILENO); /* close old, possibly wildcard socket */
736     /* tftp_protocol() will create new one, bound to particular local IP */
737     result = tftp_protocol(
738     our_lsa, peer_lsa,
739     local_file USE_TFTP(, NULL /*remote_file*/)
740     USE_FEATURE_TFTP_BLOCKSIZE(, tsize)
741     USE_FEATURE_TFTP_BLOCKSIZE(, blksize)
742     );
743    
744   return result;   return result;
745     err:
746     strcpy((char*)error_pkt_str, error_msg);
747     goto do_proto;
748  }  }
749    
750    #endif /* ENABLE_TFTPD */
751    
752  #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */  #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */

Legend:
Removed from v.815  
changed lines
  Added in v.816