introduce safe_poll (fixes a problem in top)
[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 "libbb.h"
23
24 #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
25
26 #define TFTP_BLOCKSIZE_DEFAULT 512      /* according to RFC 1350, don't change */
27 #define TFTP_TIMEOUT_MS         50
28 #define TFTP_MAXTIMEOUT_MS    2000
29 #define TFTP_NUM_RETRIES        12      /* number of backed-off 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
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( 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 pollfd pfd[1];
118 #define socketfd (pfd[0].fd)
119         int len;
120         int send_len;
121         USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
122         smallint finished = 0;
123         uint16_t opcode;
124         uint16_t block_nr = 1;
125         uint16_t recv_blk;
126         int retries, waittime_ms;
127         char *cp;
128
129         unsigned org_port;
130         len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
131
132         /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
133          * size varies meaning BUFFERS_GO_ON_STACK would fail */
134         /* We must keep the transmit and receive buffers seperate */
135         /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
136         char *xbuf = xmalloc(tftp_bufsize += 4);
137         char *rbuf = xmalloc(tftp_bufsize);
138
139         port = org_port = htons(port);
140
141         socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
142
143         /* build opcode */
144         opcode = TFTP_WRQ;
145         if (CMD_GET(cmd)) {
146                 opcode = TFTP_RRQ;
147         }
148         cp = xbuf + 2;
149         /* add filename and mode */
150         /* fill in packet if the filename fits into xbuf */
151         len = strlen(remotefile) + 1;
152         if (2 + len + sizeof("octet") >= tftp_bufsize) {
153                 bb_error_msg("remote filename is too long");
154                 goto ret;
155         }
156         strcpy(cp, remotefile);
157         cp += len;
158         /* add "mode" part of the package */
159         strcpy(cp, "octet");
160         cp += sizeof("octet");
161
162 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
163         len = tftp_bufsize - 4; /* data block size */
164         if (len != TFTP_BLOCKSIZE_DEFAULT) {
165                 /* rfc2348 says that 65464 is a max allowed value */
166                 if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
167                         bb_error_msg("remote filename is too long");
168                         goto ret;
169                 }
170                 /* add "blksize", <nul>, blocksize */
171                 strcpy(cp, "blksize");
172                 cp += sizeof("blksize");
173                 cp += snprintf(cp, 6, "%d", len) + 1;
174                 want_option_ack = 1;
175         }
176 #endif
177         /* First packet is built, so skip packet generation */
178         goto send_pkt;
179
180         /* Using mostly goto's - continue/break will be less clear
181          * in where we actually jump to */
182
183         while (1) {
184                 /* Build ACK or DATA */
185                 cp = xbuf + 2;
186                 *((uint16_t*)cp) = htons(block_nr);
187                 cp += 2;
188                 block_nr++;
189                 opcode = TFTP_ACK;
190                 if (CMD_PUT(cmd)) {
191                         opcode = TFTP_DATA;
192                         len = full_read(localfd, cp, tftp_bufsize - 4);
193                         if (len < 0) {
194                                 bb_perror_msg(bb_msg_read_error);
195                                 goto ret;
196                         }
197                         if (len != (tftp_bufsize - 4)) {
198                                 finished = 1;
199                         }
200                         cp += len;
201                 }
202  send_pkt:
203                 /* Send packet */
204                 *((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
205                 send_len = cp - xbuf;
206                 /* NB: send_len value is preserved in code below
207                  * for potential resend */
208
209                 retries = TFTP_NUM_RETRIES;     /* re-initialize */
210                 waittime_ms = TFTP_TIMEOUT_MS;
211
212  send_again:
213 #if ENABLE_DEBUG_TFTP
214                 fprintf(stderr, "sending %u bytes\n", send_len);
215                 for (cp = xbuf; cp < &xbuf[send_len]; cp++)
216                         fprintf(stderr, "%02x ", (unsigned char) *cp);
217                 fprintf(stderr, "\n");
218 #endif
219                 xsendto(socketfd, xbuf, send_len, &peer_lsa->sa, peer_lsa->len);
220                 /* Was it final ACK? then exit */
221                 if (finished && (opcode == TFTP_ACK))
222                         goto ret;
223
224  recv_again:
225                 /* Receive packet */
226                 /*pfd[0].fd = socketfd;*/
227                 pfd[0].events = POLLIN;
228                 switch (safe_poll(pfd, 1, waittime_ms)) {
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                         retries--;
252                         if (retries == 0) {
253                                 bb_error_msg("timeout");
254                                 goto ret;
255                         }
256
257                         /* exponential backoff with limit */
258                         waittime_ms += waittime_ms/2;
259                         if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
260                                 waittime_ms = TFTP_MAXTIMEOUT_MS;
261                         }
262
263                         goto send_again; /* resend last sent pkt */
264                 default:
265                         /*bb_perror_msg("poll"); - done in safe_poll */
266                         goto ret;
267                 }
268  process_pkt:
269                 /* Process recv'ed packet */
270                 opcode = ntohs( ((uint16_t*)rbuf)[0] );
271                 recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
272
273 #if ENABLE_DEBUG_TFTP
274                 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
275 #endif
276
277                 if (opcode == TFTP_ERROR) {
278                         static const char *const errcode_str[] = {
279                                 "",
280                                 "file not found",
281                                 "access violation",
282                                 "disk full",
283                                 "illegal TFTP operation",
284                                 "unknown transfer id",
285                                 "file already exists",
286                                 "no such user",
287                                 "bad option"
288                         };
289
290                         const char *msg = "";
291
292                         if (rbuf[4] != '\0') {
293                                 msg = &rbuf[4];
294                                 rbuf[tftp_bufsize - 1] = '\0';
295                         } else if (recv_blk < ARRAY_SIZE(errcode_str)) {
296                                 msg = errcode_str[recv_blk];
297                         }
298                         bb_error_msg("server error: (%u) %s", recv_blk, msg);
299                         goto ret;
300                 }
301
302 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
303                 if (want_option_ack) {
304                         want_option_ack = 0;
305
306                         if (opcode == TFTP_OACK) {
307                                 /* server seems to support options */
308                                 char *res;
309
310                                 res = tftp_option_get(&rbuf[2], len - 2, "blksize");
311                                 if (res) {
312                                         int blksize = xatoi_u(res);
313                                         if (!tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
314                                                 /* send ERROR 8 to server... */
315                                                 /* htons can be impossible to use in const initializer: */
316                                                 /*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/
317                                                 /* thus we open-code big-endian layout */
318                                                 static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 };
319                                                 xsendto(socketfd, error_8, 4, &peer_lsa->sa, peer_lsa->len);
320                                                 bb_error_msg("server proposes bad blksize %d, exiting", blksize);
321                                                 goto ret;
322                                         }
323 #if ENABLE_DEBUG_TFTP
324                                         fprintf(stderr, "using blksize %u\n",
325                                                         blksize);
326 #endif
327                                         tftp_bufsize = blksize + 4;
328                                         /* Send ACK for OACK ("block" no: 0) */
329                                         block_nr = 0;
330                                         continue;
331                                 }
332                                 /* rfc2347:
333                                  * "An option not acknowledged by the server
334                                  *  must be ignored by the client and server
335                                  *  as if it were never requested." */
336                         }
337
338                         bb_error_msg("blksize is not supported by server"
339                                                 " - reverting to 512");
340                         tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
341                 }
342 #endif
343                 /* block_nr is already advanced to next block# we expect
344                  * to get / block# we are about to send next time */
345
346                 if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
347                         if (recv_blk == block_nr) {
348                                 len = full_write(localfd, &rbuf[4], len - 4);
349                                 if (len < 0) {
350                                         bb_perror_msg(bb_msg_write_error);
351                                         goto ret;
352                                 }
353                                 if (len != (tftp_bufsize - 4)) {
354                                         finished = 1;
355                                 }
356                                 continue; /* send ACK */
357                         }
358                         if (recv_blk == (block_nr - 1)) {
359                                 /* Server lost our TFTP_ACK.  Resend it */
360                                 block_nr = recv_blk;
361                                 continue;
362                         }
363                 }
364
365                 if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
366                         /* did server ACK our last DATA pkt? */
367                         if (recv_blk == (uint16_t) (block_nr - 1)) {
368                                 if (finished)
369                                         goto ret;
370                                 continue; /* send next block */
371                         }
372                 }
373                 /* Awww... recv'd packet is not recognized! */
374                 goto recv_again;
375                 /* why recv_again? - rfc1123 says:
376                  * "The sender (i.e., the side originating the DATA packets)
377                  *  must never resend the current DATA packet on receipt
378                  *  of a duplicate ACK".
379                  * DATA pkts are resent ONLY on timeout.
380                  * Thus "goto send_again" will ba a bad mistake above.
381                  * See:
382                  * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
383                  */
384         }
385  ret:
386         if (ENABLE_FEATURE_CLEAN_UP) {
387                 close(socketfd);
388                 free(xbuf);
389                 free(rbuf);
390         }
391         return finished == 0; /* returns 1 on failure */
392 }
393
394 int tftp_main(int argc, char **argv);
395 int tftp_main(int argc, char **argv)
396 {
397         len_and_sockaddr *peer_lsa;
398         const char *localfile = NULL;
399         const char *remotefile = NULL;
400 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
401         const char *sblocksize = NULL;
402 #endif
403         int port;
404         USE_GETPUT(int cmd;)
405         int fd = -1;
406         int flags = 0;
407         int result;
408         int blocksize = TFTP_BLOCKSIZE_DEFAULT;
409
410         /* -p or -g is mandatory, and they are mutually exclusive */
411         opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
412                         USE_GETPUT("?g--p:p--g");
413
414         USE_GETPUT(cmd =) getopt32(argv,
415                         USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
416                                 "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
417                         &localfile, &remotefile
418                         USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
419         argv += optind;
420
421         flags = O_RDONLY;
422         if (CMD_GET(cmd))
423                 flags = O_WRONLY | O_CREAT | O_TRUNC;
424
425 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
426         if (sblocksize) {
427                 blocksize = xatoi_u(sblocksize);
428                 if (!tftp_blocksize_check(blocksize, 0)) {
429                         return EXIT_FAILURE;
430                 }
431         }
432 #endif
433
434         if (!localfile)
435                 localfile = remotefile;
436         if (!remotefile)
437                 remotefile = localfile;
438         /* Error if filename or host is not known */
439         if (!remotefile || !argv[0])
440                 bb_show_usage();
441
442         fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
443         if (!LONE_DASH(localfile)) {
444                 fd = xopen(localfile, flags);
445         }
446
447         port = bb_lookup_port(argv[1], "udp", 69);
448         peer_lsa = xhost2sockaddr(argv[0], port);
449
450 #if ENABLE_DEBUG_TFTP
451         fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n",
452                         xmalloc_sockaddr2dotted(&peer_lsa->sa),
453                         remotefile, localfile);
454 #endif
455
456         result = tftp( USE_GETPUT(cmd,) peer_lsa, remotefile, fd, port, blocksize);
457
458         if (ENABLE_FEATURE_CLEAN_UP)
459                 close(fd);
460         if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && CMD_GET(cmd)) {
461                 unlink(localfile);
462         }
463         return result;
464 }
465
466 #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */