source: MondoRescue/branches/2.2.2/mindi-busybox/networking/tftp.c@ 1247

Last change on this file since 1247 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

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