tftp: code diet, and I think retransmits were broken.
[oweals/busybox.git] / networking / tftp.c
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 "busybox.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(a)
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(a)
45 #define CMD_GET(cmd) 0
46 #define CMD_PUT(cmd) 1
47 #else
48 #define USE_GETPUT(a) a
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
60 static 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
78 static 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
112 static int tftp(
113 #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
114                 const int cmd,
115 #endif
116                 len_and_sockaddr *peer_lsa,
117                 const char *remotefile, const int localfd,
118                 unsigned port, int tftp_bufsize)
119 {
120         struct timeval tv;
121         fd_set rfds;
122         int socketfd;
123         int len;
124         int send_len;
125         USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
126         smallint finished = 0;
127         uint16_t opcode;
128         uint16_t block_nr = 1;
129         uint16_t recv_blk;
130         int timeout = TFTP_NUM_RETRIES;
131         char *cp;
132
133         unsigned org_port;
134         len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
135
136         /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
137          * size varies meaning BUFFERS_GO_ON_STACK would fail */
138         /* We must keep the transmit and receive buffers seperate */
139         /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
140         char *xbuf = xmalloc(tftp_bufsize += 4);
141         char *rbuf = xmalloc(tftp_bufsize);
142
143         port = org_port = htons(port);
144
145         socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
146
147         /* build opcode */
148         opcode = TFTP_WRQ;
149         if (CMD_GET(cmd)) {
150                 opcode = TFTP_RRQ;
151         }
152         cp = xbuf + 2;
153         /* add filename and mode */
154         /* fill in packet if the filename fits into xbuf */
155         len = strlen(remotefile) + 1;
156         if (2 + len + sizeof("octet") >= tftp_bufsize) {
157                 bb_error_msg("remote filename is too long");
158                 goto ret;
159         }
160         strcpy(cp, remotefile);
161         cp += len;
162         /* add "mode" part of the package */
163         strcpy(cp, "octet");
164         cp += sizeof("octet");
165
166 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
167         len = tftp_bufsize - 4; /* data block size */
168         if (len != TFTP_BLOCKSIZE_DEFAULT) {
169                 /* rfc2348 says that 65464 is a max allowed value */
170                 if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
171                         bb_error_msg("remote filename is too long");
172                         goto ret;
173                 }
174                 /* add "blksize", <nul>, blocksize */
175                 strcpy(cp, "blksize");
176                 cp += sizeof("blksize");
177                 cp += snprintf(cp, 6, "%d", len) + 1;
178                 want_option_ack = 1;
179         }
180 #endif
181         /* First packet is built, so skip packet generation */
182         goto send_pkt;
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                 timeout = TFTP_NUM_RETRIES;     /* re-initialize */
207                 while (1) {
208                         send_len = cp - xbuf;
209                         /* nb: need to preserve send_len value in code below
210                          * for potential resend! */
211  send_again:
212 #if ENABLE_DEBUG_TFTP
213                         fprintf(stderr, "sending %u bytes\n", send_len);
214                         for (cp = xbuf; cp < &xbuf[send_len]; cp++)
215                                 fprintf(stderr, "%02x ", (unsigned char) *cp);
216                         fprintf(stderr, "\n");
217 #endif
218                         xsendto(socketfd, xbuf, send_len, &peer_lsa->sa, peer_lsa->len);
219                         /* Was it final ACK? then exit */
220                         if (finished && (opcode == TFTP_ACK))
221                                 goto ret;
222
223                         /* Receive packet */
224  recv_again:
225                         tv.tv_sec = TFTP_TIMEOUT;
226                         tv.tv_usec = 0;
227                         FD_ZERO(&rfds);
228                         FD_SET(socketfd, &rfds);
229                         switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
230                                 unsigned from_port;
231                         case 1:
232                                 from->len = peer_lsa->len;
233                                 memset(&from->sa, 0, peer_lsa->len);
234                                 len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
235                                                         &from->sa, &from->len);
236                                 if (len < 0) {
237                                         bb_perror_msg("recvfrom");
238                                         goto ret;
239                                 }
240                                 from_port = get_nport(&from->sa);
241                                 if (port == org_port) {
242                                         /* Our first query went to port 69
243                                          * but reply will come from different one.
244                                          * Remember and use this new port */
245                                         port = from_port;
246                                         set_nport(peer_lsa, from_port);
247                                 }
248                                 if (port != from_port)
249                                         goto recv_again;
250                                 goto recvd_good;
251                         case 0:
252                                 timeout--;
253                                 if (timeout == 0) {
254                                         bb_error_msg("last timeout");
255                                         goto ret;
256                                 }
257                                 bb_error_msg("last timeout" + 5);
258                                 goto send_again; /* resend last sent pkt */
259                         default:
260                                 bb_perror_msg("select");
261                                 goto ret;
262                         }
263                 } /* while we don't see recv packet with correct port# */
264
265                 /* Process recv'ed packet */
266  recvd_good:
267                 opcode = ntohs( ((uint16_t*)rbuf)[0] );
268                 recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
269
270 #if ENABLE_DEBUG_TFTP
271                 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
272 #endif
273
274                 if (opcode == TFTP_ERROR) {
275                         static const char *const errcode_str[] = {
276                                 "",
277                                 "file not found",
278                                 "access violation",
279                                 "disk full",
280                                 "illegal TFTP operation",
281                                 "unknown transfer id",
282                                 "file already exists",
283                                 "no such user",
284                         };
285                         enum { NUM_ERRCODE = sizeof(errcode_str) / sizeof(errcode_str[0]) };
286
287                         const char *msg = "";
288
289                         if (rbuf[4] != '\0') {
290                                 msg = &rbuf[4];
291                                 rbuf[tftp_bufsize - 1] = '\0';
292                         } else if (recv_blk < NUM_ERRCODE) {
293                                 msg = errcode_str[recv_blk];
294                         }
295                         bb_error_msg("server error: (%u) %s", recv_blk, msg);
296                         goto ret;
297                 }
298
299 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
300                 if (want_option_ack) {
301                         want_option_ack = 0;
302
303                         if (opcode == TFTP_OACK) {
304                                 /* server seems to support options */
305                                 char *res;
306
307                                 res = tftp_option_get(&rbuf[2], len - 2, "blksize");
308                                 if (res) {
309                                         int blksize = xatoi_u(res);
310                                         if (!tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
311                                                 bb_error_msg("server proposes bad blksize %d, exiting", blksize);
312                                                 // FIXME: must also send ERROR 8 to server...
313                                                 goto ret;
314                                         }
315 #if ENABLE_DEBUG_TFTP
316                                         fprintf(stderr, "using blksize %u\n",
317                                                         blksize);
318 #endif
319                                         tftp_bufsize = blksize + 4;
320                                         block_nr = 0; // TODO: explain why???
321                                         continue;
322                                 }
323                                 /* rfc2347:
324                                  * "An option not acknowledged by the server
325                                  *  must be ignored by the client and server
326                                  *  as if it were never requested." */
327                         }
328
329                         bb_error_msg("blksize is not supported by server"
330                                                 " - reverting to 512");
331                         tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
332                 }
333 #endif
334                 /* block_nr is already advanced to next block# we expect
335                  * to get / block# we are about to send next time */
336
337                 if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
338                         if (recv_blk == block_nr) {
339                                 len = full_write(localfd, &rbuf[4], len - 4);
340                                 if (len < 0) {
341                                         bb_perror_msg(bb_msg_write_error);
342                                         goto ret;
343                                 }
344                                 if (len != (tftp_bufsize - 4)) {
345                                         finished = 1;
346                                 }
347                                 continue; /* send ACK */
348                         }
349                         if (recv_blk == (block_nr - 1)) {
350                                 /* Server lost our TFTP_ACK.  Resend it */
351                                 block_nr = recv_blk;
352                                 continue;
353                         } 
354                 }
355
356                 if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
357                         /* did server ACK our last DATA pkt? */
358                         if (recv_blk == (uint16_t) (block_nr - 1)) {
359                                 if (finished)
360                                         goto ret;
361                                 continue; /* send next block */
362                         }
363                 }
364                 /* Awww... recv'd packet is not recognized! */
365                 goto recv_again;
366                 /* why recv_again? - rfc1123 says:
367                  * "The sender (i.e., the side originating the DATA packets)
368                  *  must never resend the current DATA packet on receipt
369                  *  of a duplicate ACK".
370                  * DATA pkts are resent ONLY on timeout.
371                  * Thus "goto send_again" will ba a bad mistake above.
372                  * See:
373                  * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
374                  */
375         }
376  ret:
377         if (ENABLE_FEATURE_CLEAN_UP) {
378                 close(socketfd);
379                 free(xbuf);
380                 free(rbuf);
381         }
382         return finished == 0; /* returns 1 on failure */
383 }
384
385 int tftp_main(int argc, char **argv);
386 int tftp_main(int argc, char **argv)
387 {
388         len_and_sockaddr *peer_lsa;
389         const char *localfile = NULL;
390         const char *remotefile = NULL;
391 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
392         const char *sblocksize = NULL;
393 #endif
394         int port;
395         USE_GETPUT(int cmd;)
396         int fd = -1;
397         int flags = 0;
398         int result;
399         int blocksize = TFTP_BLOCKSIZE_DEFAULT;
400
401         /* -p or -g is mandatory, and they are mutually exclusive */
402         opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
403                         USE_GETPUT("?g--p:p--g");
404
405         USE_GETPUT(cmd =) getopt32(argc, argv,
406                         USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
407                                 "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
408                         &localfile, &remotefile
409                         USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
410         argv += optind;
411
412         flags = O_RDONLY;
413         if (CMD_GET(cmd))
414                 flags = O_WRONLY | O_CREAT | O_TRUNC;
415
416 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
417         if (sblocksize) {
418                 blocksize = xatoi_u(sblocksize);
419                 if (!tftp_blocksize_check(blocksize, 0)) {
420                         return EXIT_FAILURE;
421                 }
422         }
423 #endif
424
425         if (!localfile)
426                 localfile = remotefile;
427         if (!remotefile)
428                 remotefile = localfile;
429         /* Error if filename or host is not known */
430         if (!remotefile || !argv[0])
431                 bb_show_usage();
432
433         if (LONE_DASH(localfile)) {
434                 fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
435         } else {
436                 fd = xopen(localfile, flags);
437         }
438
439         port = bb_lookup_port(argv[1], "udp", 69);
440         peer_lsa = xhost2sockaddr(argv[0], port);
441
442 #if ENABLE_DEBUG_TFTP
443         fprintf(stderr, "using server \"%s\", "
444                         "remotefile \"%s\", localfile \"%s\"\n",
445                         xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),
446                         remotefile, localfile);
447 #endif
448
449         result = tftp(
450 #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
451                 cmd,
452 #endif
453                 peer_lsa, remotefile, fd, port, blocksize);
454
455         if (ENABLE_FEATURE_CLEAN_UP)
456                 close(fd);
457         if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && CMD_GET(cmd)) {
458                 unlink(localfile);
459         }
460         return result;
461 }
462
463 #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */