aboutsummaryrefslogtreecommitdiff
path: root/networking/tftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/tftp.c')
-rw-r--r--networking/tftp.c561
1 files changed, 561 insertions, 0 deletions
diff --git a/networking/tftp.c b/networking/tftp.c
new file mode 100644
index 000000000..64d376fa7
--- /dev/null
+++ b/networking/tftp.c
@@ -0,0 +1,561 @@
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#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
26#define TFTP_TIMEOUT 5 /* seconds */
27#define TFTP_NUM_RETRIES 5 /* number of retries */
28
29static const char * const MODE_OCTET = "octet";
30#define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/
31
32static const char * const OPTION_BLOCKSIZE = "blksize";
33#define OPTION_BLOCKSIZE_LEN 8 /* sizeof(OPTION_BLOCKSIZE) */
34
35/* opcodes we support */
36#define TFTP_RRQ 1
37#define TFTP_WRQ 2
38#define TFTP_DATA 3
39#define TFTP_ACK 4
40#define TFTP_ERROR 5
41#define TFTP_OACK 6
42
43static const char *const tftp_bb_error_msg[] = {
44 "Undefined error",
45 "File not found",
46 "Access violation",
47 "Disk full or allocation error",
48 "Illegal TFTP operation",
49 "Unknown transfer ID",
50 "File already exists",
51 "No such user"
52};
53
54#define tftp_cmd_get ENABLE_FEATURE_TFTP_GET
55
56#if ENABLE_FEATURE_TFTP_PUT
57# define tftp_cmd_put (tftp_cmd_get+ENABLE_FEATURE_TFTP_PUT)
58#else
59# define tftp_cmd_put 0
60#endif
61
62
63#if ENABLE_FEATURE_TFTP_BLOCKSIZE
64
65static int tftp_blocksize_check(int blocksize, int bufsize)
66{
67 /* Check if the blocksize is valid:
68 * RFC2348 says between 8 and 65464,
69 * but our implementation makes it impossible
70 * to use blocksizes smaller than 22 octets.
71 */
72
73 if ((bufsize && (blocksize > bufsize)) ||
74 (blocksize < 8) || (blocksize > 65564)) {
75 bb_error_msg("bad blocksize");
76 return 0;
77 }
78
79 return blocksize;
80}
81
82static char *tftp_option_get(char *buf, int len, const char * const option)
83{
84 int opt_val = 0;
85 int opt_found = 0;
86 int k;
87
88 while (len > 0) {
89
90 /* Make sure the options are terminated correctly */
91
92 for (k = 0; k < len; k++) {
93 if (buf[k] == '\0') {
94 break;
95 }
96 }
97
98 if (k >= len) {
99 break;
100 }
101
102 if (opt_val == 0) {
103 if (strcasecmp(buf, option) == 0) {
104 opt_found = 1;
105 }
106 } else {
107 if (opt_found) {
108 return buf;
109 }
110 }
111
112 k++;
113
114 buf += k;
115 len -= k;
116
117 opt_val ^= 1;
118 }
119
120 return NULL;
121}
122
123#endif
124
125static int tftp(const int cmd, const struct hostent *host,
126 const char *remotefile, const int localfd,
127 const unsigned short port, int tftp_bufsize)
128{
129 struct sockaddr_in sa;
130 struct sockaddr_in from;
131 struct timeval tv;
132 socklen_t fromlen;
133 fd_set rfds;
134 int socketfd;
135 int len;
136 int opcode = 0;
137 int finished = 0;
138 int timeout = TFTP_NUM_RETRIES;
139 unsigned short block_nr = 1;
140 unsigned short tmp;
141 char *cp;
142
143 USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
144
145 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
146 * size varies meaning BUFFERS_GO_ON_STACK would fail */
147 char *buf=xmalloc(tftp_bufsize += 4);
148
149 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
150 /* need to unlink the localfile, so don't use xsocket here. */
151 bb_perror_msg("socket");
152 return EXIT_FAILURE;
153 }
154
155 len = sizeof(sa);
156
157 memset(&sa, 0, len);
158 xbind(socketfd, (struct sockaddr *)&sa, len);
159
160 sa.sin_family = host->h_addrtype;
161 sa.sin_port = port;
162 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
163 sizeof(sa.sin_addr));
164
165 /* build opcode */
166 if (cmd & tftp_cmd_get) {
167 opcode = TFTP_RRQ;
168 }
169 if (cmd & tftp_cmd_put) {
170 opcode = TFTP_WRQ;
171 }
172
173 while (1) {
174
175 cp = buf;
176
177 /* first create the opcode part */
178 *((unsigned short *) cp) = htons(opcode);
179 cp += 2;
180
181 /* add filename and mode */
182 if (((cmd & tftp_cmd_get) && (opcode == TFTP_RRQ)) ||
183 ((cmd & tftp_cmd_put) && (opcode == TFTP_WRQ)))
184 {
185 int too_long = 0;
186
187 /* see if the filename fits into buf
188 * and fill in packet. */
189 len = strlen(remotefile) + 1;
190
191 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
192 too_long = 1;
193 } else {
194 safe_strncpy(cp, remotefile, len);
195 cp += len;
196 }
197
198 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) {
199 bb_error_msg("remote filename too long");
200 break;
201 }
202
203 /* add "mode" part of the package */
204 memcpy(cp, MODE_OCTET, MODE_OCTET_LEN);
205 cp += MODE_OCTET_LEN;
206
207#if ENABLE_FEATURE_TFTP_BLOCKSIZE
208
209 len = tftp_bufsize - 4; /* data block size */
210
211 if (len != TFTP_BLOCKSIZE_DEFAULT) {
212
213 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
214 bb_error_msg("remote filename too long");
215 break;
216 }
217
218 /* add "blksize" + number of blocks */
219 memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN);
220 cp += OPTION_BLOCKSIZE_LEN;
221 cp += snprintf(cp, 6, "%d", len) + 1;
222
223 want_option_ack = 1;
224 }
225#endif
226 }
227
228 /* add ack and data */
229
230 if (((cmd & tftp_cmd_get) && (opcode == TFTP_ACK)) ||
231 ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA))) {
232
233 *((unsigned short *) cp) = htons(block_nr);
234
235 cp += 2;
236
237 block_nr++;
238
239 if ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA)) {
240 len = full_read(localfd, cp, tftp_bufsize - 4);
241
242 if (len < 0) {
243 bb_perror_msg(bb_msg_read_error);
244 break;
245 }
246
247 if (len != (tftp_bufsize - 4)) {
248 finished++;
249 }
250
251 cp += len;
252 }
253 }
254
255
256 /* send packet */
257
258
259 timeout = TFTP_NUM_RETRIES; /* re-initialize */
260 do {
261
262 len = cp - buf;
263
264#if ENABLE_DEBUG_TFTP
265 fprintf(stderr, "sending %u bytes\n", len);
266 for (cp = buf; cp < &buf[len]; cp++)
267 fprintf(stderr, "%02x ", (unsigned char) *cp);
268 fprintf(stderr, "\n");
269#endif
270 if (sendto(socketfd, buf, len, 0,
271 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
272 bb_perror_msg("send");
273 len = -1;
274 break;
275 }
276
277
278 if (finished && (opcode == TFTP_ACK)) {
279 break;
280 }
281
282 /* receive packet */
283
284 memset(&from, 0, sizeof(from));
285 fromlen = sizeof(from);
286
287 tv.tv_sec = TFTP_TIMEOUT;
288 tv.tv_usec = 0;
289
290 FD_ZERO(&rfds);
291 FD_SET(socketfd, &rfds);
292
293 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
294 case 1:
295 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
296 (struct sockaddr *) &from, &fromlen);
297
298 if (len < 0) {
299 bb_perror_msg("recvfrom");
300 break;
301 }
302
303 timeout = 0;
304
305 if (sa.sin_port == port) {
306 sa.sin_port = from.sin_port;
307 }
308 if (sa.sin_port == from.sin_port) {
309 break;
310 }
311
312 /* fall-through for bad packets! */
313 /* discard the packet - treat as timeout */
314 timeout = TFTP_NUM_RETRIES;
315 case 0:
316 bb_error_msg("timeout");
317
318 timeout--;
319 if (timeout == 0) {
320 len = -1;
321 bb_error_msg("last timeout");
322 }
323 break;
324 default:
325 bb_perror_msg("select");
326 len = -1;
327 }
328
329 } while (timeout && (len >= 0));
330
331 if ((finished) || (len < 0)) {
332 break;
333 }
334
335 /* process received packet */
336
337 opcode = ntohs(*((unsigned short *) buf));
338 tmp = ntohs(*((unsigned short *) &buf[2]));
339
340#if ENABLE_DEBUG_TFTP
341 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
342#endif
343
344 if (opcode == TFTP_ERROR) {
345 const char *msg = NULL;
346
347 if (buf[4] != '\0') {
348 msg = &buf[4];
349 buf[tftp_bufsize - 1] = '\0';
350 } else if (tmp < (sizeof(tftp_bb_error_msg)
351 / sizeof(char *))) {
352
353 msg = tftp_bb_error_msg[tmp];
354 }
355
356 if (msg) {
357 bb_error_msg("server says: %s", msg);
358 }
359
360 break;
361 }
362#if ENABLE_FEATURE_TFTP_BLOCKSIZE
363 if (want_option_ack) {
364
365 want_option_ack = 0;
366
367 if (opcode == TFTP_OACK) {
368
369 /* server seems to support options */
370
371 char *res;
372
373 res = tftp_option_get(&buf[2], len - 2, OPTION_BLOCKSIZE);
374
375 if (res) {
376 int blksize = xatoi_u(res);
377
378 if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
379
380 if (cmd & tftp_cmd_put) {
381 opcode = TFTP_DATA;
382 } else {
383 opcode = TFTP_ACK;
384 }
385#if ENABLE_DEBUG_TFTP
386 fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE,
387 blksize);
388#endif
389 tftp_bufsize = blksize + 4;
390 block_nr = 0;
391 continue;
392 }
393 }
394 /* FIXME:
395 * we should send ERROR 8 */
396 bb_error_msg("bad server option");
397 break;
398 }
399
400 bb_error_msg("warning: blksize not supported by server"
401 " - reverting to 512");
402
403 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
404 }
405#endif
406
407 if ((cmd & tftp_cmd_get) && (opcode == TFTP_DATA)) {
408
409 if (tmp == block_nr) {
410
411 len = full_write(localfd, &buf[4], len - 4);
412
413 if (len < 0) {
414 bb_perror_msg(bb_msg_write_error);
415 break;
416 }
417
418 if (len != (tftp_bufsize - 4)) {
419 finished++;
420 }
421
422 opcode = TFTP_ACK;
423 continue;
424 }
425 /* in case the last ack disappeared into the ether */
426 if (tmp == (block_nr - 1)) {
427 --block_nr;
428 opcode = TFTP_ACK;
429 continue;
430 } else if (tmp + 1 == block_nr) {
431 /* Server lost our TFTP_ACK. Resend it */
432 block_nr = tmp;
433 opcode = TFTP_ACK;
434 continue;
435 }
436 }
437
438 if ((cmd & tftp_cmd_put) && (opcode == TFTP_ACK)) {
439
440 if (tmp == (unsigned short) (block_nr - 1)) {
441 if (finished) {
442 break;
443 }
444
445 opcode = TFTP_DATA;
446 continue;
447 }
448 }
449 }
450
451#if ENABLE_FEATURE_CLEAN_UP
452 close(socketfd);
453 free(buf);
454#endif
455
456 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
457}
458
459int tftp_main(int argc, char **argv)
460{
461 struct hostent *host = NULL;
462 const char *localfile = NULL;
463 const char *remotefile = NULL;
464 int port;
465 int cmd = 0;
466 int fd = -1;
467 int flags = 0;
468 int result;
469 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
470
471 /* figure out what to pass to getopt */
472
473#if ENABLE_FEATURE_TFTP_BLOCKSIZE
474 char *sblocksize = NULL;
475
476#define BS "b:"
477#define BS_ARG , &sblocksize
478#else
479#define BS
480#define BS_ARG
481#endif
482
483#if ENABLE_FEATURE_TFTP_GET
484#define GET "g"
485#define GET_COMPL ":g"
486#else
487#define GET
488#define GET_COMPL
489#endif
490
491#if ENABLE_FEATURE_TFTP_PUT
492#define PUT "p"
493#define PUT_COMPL ":p"
494#else
495#define PUT
496#define PUT_COMPL
497#endif
498
499#if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
500 opt_complementary = GET_COMPL PUT_COMPL ":?g--p:p--g";
501#elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
502 opt_complementary = GET_COMPL PUT_COMPL;
503#endif
504
505 cmd = getopt32(argc, argv, GET PUT "l:r:" BS, &localfile, &remotefile BS_ARG);
506
507 cmd &= (tftp_cmd_get | tftp_cmd_put);
508#if ENABLE_FEATURE_TFTP_GET
509 if (cmd == tftp_cmd_get)
510 flags = O_WRONLY | O_CREAT | O_TRUNC;
511#endif
512#if ENABLE_FEATURE_TFTP_PUT
513 if (cmd == tftp_cmd_put)
514 flags = O_RDONLY;
515#endif
516
517#if ENABLE_FEATURE_TFTP_BLOCKSIZE
518 if (sblocksize) {
519 blocksize = xatoi_u(sblocksize);
520 if (!tftp_blocksize_check(blocksize, 0)) {
521 return EXIT_FAILURE;
522 }
523 }
524#endif
525
526 if (localfile == NULL)
527 localfile = remotefile;
528 if (remotefile == NULL)
529 remotefile = localfile;
530 if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
531 bb_show_usage();
532
533 if (localfile == NULL || strcmp(localfile, "-") == 0) {
534 fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO;
535 } else {
536 fd = open(localfile, flags, 0644); /* fail below */
537 }
538 if (fd < 0) {
539 bb_perror_msg_and_die("local file");
540 }
541
542 host = xgethostbyname(argv[optind]);
543 port = bb_lookup_port(argv[optind + 1], "udp", 69);
544
545#if ENABLE_DEBUG_TFTP
546 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
547 "localfile \"%s\".\n",
548 inet_ntoa(*((struct in_addr *) host->h_addr)),
549 remotefile, localfile);
550#endif
551
552 result = tftp(cmd, host, remotefile, fd, port, blocksize);
553
554 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
555 if (ENABLE_FEATURE_CLEAN_UP)
556 close(fd);
557 if (cmd == tftp_cmd_get && result != EXIT_SUCCESS)
558 unlink(localfile);
559 }
560 return result;
561}