aboutsummaryrefslogtreecommitdiff
path: root/tftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'tftp.c')
-rw-r--r--tftp.c574
1 files changed, 0 insertions, 574 deletions
diff --git a/tftp.c b/tftp.c
deleted file mode 100644
index 530b3d134..000000000
--- a/tftp.c
+++ /dev/null
@@ -1,574 +0,0 @@
1/* ------------------------------------------------------------------------- */
2/* tftp.c */
3/* */
4/* A simple tftp client for busybox. */
5/* Tries to follow RFC1350. */
6/* Only "octet" mode supported. */
7/* Optional blocksize negotiation (RFC2347 + RFC2348) */
8/* */
9/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
10/* */
11/* Parts of the code based on: */
12/* */
13/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
14/* and Remi Lefebvre <remi@debian.org> */
15/* */
16/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
17/* */
18/* This program is free software; you can redistribute it and/or modify */
19/* it under the terms of the GNU General Public License as published by */
20/* the Free Software Foundation; either version 2 of the License, or */
21/* (at your option) any later version. */
22/* */
23/* This program is distributed in the hope that it will be useful, */
24/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
25/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
26/* General Public License for more details. */
27/* */
28/* You should have received a copy of the GNU General Public License */
29/* along with this program; if not, write to the Free Software */
30/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
31/* */
32/* ------------------------------------------------------------------------- */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <sys/time.h>
40#include <sys/stat.h>
41#include <netdb.h>
42#include <netinet/in.h>
43#include <arpa/inet.h>
44#include <unistd.h>
45#include <fcntl.h>
46
47#include "busybox.h"
48
49//#define BB_FEATURE_TFTP_DEBUG
50
51#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
52#define TFTP_TIMEOUT 5 /* seconds */
53
54/* opcodes we support */
55
56#define TFTP_RRQ 1
57#define TFTP_WRQ 2
58#define TFTP_DATA 3
59#define TFTP_ACK 4
60#define TFTP_ERROR 5
61#define TFTP_OACK 6
62
63static const char *tftp_error_msg[] = {
64 "Undefined error",
65 "File not found",
66 "Access violation",
67 "Disk full or allocation error",
68 "Illegal TFTP operation",
69 "Unknown transfer ID",
70 "File already exists",
71 "No such user"
72};
73
74const int tftp_cmd_get = 1;
75const int tftp_cmd_put = 2;
76
77#ifdef BB_FEATURE_TFTP_BLOCKSIZE
78
79static int tftp_blocksize_check(int blocksize, int bufsize)
80{
81 /* Check if the blocksize is valid:
82 * RFC2348 says between 8 and 65464,
83 * but our implementation makes it impossible
84 * to use blocksizes smaller than 22 octets.
85 */
86
87 if ((bufsize && (blocksize > bufsize)) ||
88 (blocksize < 8) || (blocksize > 65464)) {
89 error_msg("bad blocksize");
90 return 0;
91 }
92
93 return blocksize;
94}
95
96static char *tftp_option_get(char *buf, int len, char *option)
97{
98 int opt_val = 0;
99 int opt_found = 0;
100 int k;
101
102 while (len > 0) {
103
104 /* Make sure the options are terminated correctly */
105
106 for (k = 0; k < len; k++) {
107 if (buf[k] == '\0') {
108 break;
109 }
110 }
111
112 if (k >= len) {
113 break;
114 }
115
116 if (opt_val == 0) {
117 if (strcasecmp(buf, option) == 0) {
118 opt_found = 1;
119 }
120 }
121 else {
122 if (opt_found) {
123 return buf;
124 }
125 }
126
127 k++;
128
129 buf += k;
130 len -= k;
131
132 opt_val ^= 1;
133 }
134
135 return NULL;
136}
137
138#endif
139
140static inline int tftp(const int cmd, const struct hostent *host,
141 const char *remotefile, int localfd, const int port, int tftp_bufsize)
142{
143 const int cmd_get = cmd & tftp_cmd_get;
144 const int cmd_put = cmd & tftp_cmd_put;
145 const int bb_tftp_num_retries = 5;
146
147 struct sockaddr_in sa;
148 struct sockaddr_in from;
149 struct timeval tv;
150 socklen_t fromlen;
151 fd_set rfds;
152 char *cp;
153 unsigned short tmp;
154 int socketfd;
155 int len;
156 int opcode = 0;
157 int finished = 0;
158 int timeout = bb_tftp_num_retries;
159 int block_nr = 1;
160
161#ifdef BB_FEATURE_TFTP_BLOCKSIZE
162 int want_option_ack = 0;
163#endif
164
165 RESERVE_BB_BUFFER(buf, tftp_bufsize + 4); /* Opcode + Block # + Data */
166
167 tftp_bufsize += 4;
168
169 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
170 perror_msg("socket");
171 return EXIT_FAILURE;
172 }
173
174 len = sizeof(sa);
175
176 memset(&sa, 0, len);
177 bind(socketfd, (struct sockaddr *)&sa, len);
178
179 sa.sin_family = host->h_addrtype;
180 sa.sin_port = htons(port);
181 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
182 sizeof(sa.sin_addr));
183
184 /* build opcode */
185
186 if (cmd_get) {
187 opcode = TFTP_RRQ;
188 }
189
190 if (cmd_put) {
191 opcode = TFTP_WRQ;
192 }
193
194 while (1) {
195
196 cp = buf;
197
198 /* first create the opcode part */
199
200 *((unsigned short *) cp) = htons(opcode);
201
202 cp += 2;
203
204 /* add filename and mode */
205
206 if ((cmd_get && (opcode == TFTP_RRQ)) ||
207 (cmd_put && (opcode == TFTP_WRQ))) {
208 int too_long = 0;
209
210 /* see if the filename fits into buf */
211 /* and fill in packet */
212
213 len = strlen(remotefile) + 1;
214
215 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
216 too_long = 1;
217 }
218 else {
219 safe_strncpy(cp, remotefile, len);
220 cp += len;
221 }
222
223 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
224 error_msg("too long remote-filename");
225 break;
226 }
227
228 /* add "mode" part of the package */
229
230 memcpy(cp, "octet", 6);
231 cp += 6;
232
233#ifdef BB_FEATURE_TFTP_BLOCKSIZE
234
235 len = tftp_bufsize - 4; /* data block size */
236
237 if (len != TFTP_BLOCKSIZE_DEFAULT) {
238
239 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
240 error_msg("too long remote-filename");
241 break;
242 }
243
244 /* add "blksize" + number of blocks */
245
246 memcpy(cp, "blksize", 8);
247 cp += 8;
248
249 cp += snprintf(cp, 6, "%d", len) + 1;
250
251 want_option_ack = 1;
252 }
253#endif
254 }
255
256 /* add ack and data */
257
258 if ((cmd_get && (opcode == TFTP_ACK)) ||
259 (cmd_put && (opcode == TFTP_DATA))) {
260
261 *((unsigned short *) cp) = htons(block_nr);
262
263 cp += 2;
264
265 block_nr++;
266
267 if (cmd_put && (opcode == TFTP_DATA)) {
268 len = read(localfd, cp, tftp_bufsize - 4);
269
270 if (len < 0) {
271 perror_msg("read");
272 break;
273 }
274
275 if (len != (tftp_bufsize - 4)) {
276 finished++;
277 }
278
279 cp += len;
280 } else if (finished) {
281 break;
282 }
283 }
284
285
286 /* send packet */
287
288
289 do {
290
291 len = cp - buf;
292
293#ifdef BB_FEATURE_TFTP_DEBUG
294 printf("sending %u bytes\n", len);
295 for (cp = buf; cp < &buf[len]; cp++)
296 printf("%02x ", *cp);
297 printf("\n");
298#endif
299 if (sendto(socketfd, buf, len, 0,
300 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
301 perror_msg("send");
302 len = -1;
303 break;
304 }
305
306
307 /* receive packet */
308
309
310 memset(&from, 0, sizeof(from));
311 fromlen = sizeof(from);
312
313 tv.tv_sec = TFTP_TIMEOUT;
314 tv.tv_usec = 0;
315
316 FD_ZERO(&rfds);
317 FD_SET(socketfd, &rfds);
318
319 switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
320 case 1:
321 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
322 (struct sockaddr *) &from, &fromlen);
323
324 if (len < 0) {
325 perror_msg("recvfrom");
326 break;
327 }
328
329 timeout = 0;
330
331 if (sa.sin_port == htons(port)) {
332 sa.sin_port = from.sin_port;
333 }
334 if (sa.sin_port == from.sin_port) {
335 break;
336 }
337
338 /* fall-through for bad packets! */
339 /* discard the packet - treat as timeout */
340 timeout = bb_tftp_num_retries;
341
342 case 0:
343 error_msg("timeout");
344
345 if (timeout == 0) {
346 len = -1;
347 error_msg("last timeout");
348 } else {
349 timeout--;
350 }
351 break;
352
353 default:
354 perror_msg("select");
355 len = -1;
356 }
357
358 } while (timeout && (len >= 0));
359
360 if (len < 0) {
361 break;
362 }
363
364 /* process received packet */
365
366
367 opcode = ntohs(*((unsigned short *) buf));
368 tmp = ntohs(*((unsigned short *) &buf[2]));
369
370#ifdef BB_FEATURE_TFTP_DEBUG
371 printf("received %d bytes: %04x %04x\n", len, opcode, tmp);
372#endif
373
374 if (opcode == TFTP_ERROR) {
375 char *msg = NULL;
376
377 if (buf[4] != '\0') {
378 msg = &buf[4];
379 buf[tftp_bufsize - 1] = '\0';
380 } else if (tmp < (sizeof(tftp_error_msg)
381 / sizeof(char *))) {
382
383 msg = (char *) tftp_error_msg[tmp];
384 }
385
386 if (msg) {
387 error_msg("server says: %s", msg);
388 }
389
390 break;
391 }
392
393#ifdef BB_FEATURE_TFTP_BLOCKSIZE
394 if (want_option_ack) {
395
396 want_option_ack = 0;
397
398 if (opcode == TFTP_OACK) {
399
400 /* server seems to support options */
401
402 char *res;
403
404 res = tftp_option_get(&buf[2], len-2,
405 "blksize");
406
407 if (res) {
408 int foo = atoi(res);
409
410 if (tftp_blocksize_check(foo,
411 tftp_bufsize - 4)) {
412
413 if (cmd_put) {
414 opcode = TFTP_DATA;
415 }
416 else {
417 opcode = TFTP_ACK;
418 }
419#ifdef BB_FEATURE_TFTP_DEBUG
420 printf("using blksize %u\n");
421#endif
422 tftp_bufsize = foo + 4;
423 block_nr = 0;
424 continue;
425 }
426 }
427 /* FIXME:
428 * we should send ERROR 8 */
429 error_msg("bad server option");
430 break;
431 }
432
433 error_msg("warning: blksize not supported by server"
434 " - reverting to 512");
435
436 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
437 }
438#endif
439
440 if (cmd_get && (opcode == TFTP_DATA)) {
441
442 if (tmp == block_nr) {
443
444 len = write(localfd, &buf[4], len - 4);
445
446 if (len < 0) {
447 perror_msg("write");
448 break;
449 }
450
451 if (len != (tftp_bufsize - 4)) {
452 finished++;
453 }
454
455 opcode = TFTP_ACK;
456 continue;
457 }
458 }
459
460 if (cmd_put && (opcode == TFTP_ACK)) {
461
462 if (tmp == (block_nr - 1)) {
463 if (finished) {
464 break;
465 }
466
467 opcode = TFTP_DATA;
468 continue;
469 }
470 }
471 }
472
473#ifdef BB_FEATURE_CLEAN_UP
474 close(socketfd);
475
476 RELEASE_BB_BUFFER(buf);
477#endif
478
479 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
480}
481
482int tftp_main(int argc, char **argv)
483{
484 struct hostent *host = NULL;
485 char *localfile = NULL;
486 char *remotefile = NULL;
487 int port = 69;
488 int cmd = 0;
489 int fd = -1;
490 int flags = 0;
491 int opt;
492 int result;
493 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
494
495 /* figure out what to pass to getopt */
496
497#ifdef BB_FEATURE_TFTP_BLOCKSIZE
498#define BS "b:"
499#else
500#define BS
501#endif
502
503#ifdef BB_FEATURE_TFTP_GET
504#define GET "g"
505#else
506#define GET
507#endif
508
509#ifdef BB_FEATURE_TFTP_PUT
510#define PUT "p"
511#else
512#define PUT
513#endif
514
515 while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
516 switch (opt) {
517#ifdef BB_FEATURE_TFTP_BLOCKSIZE
518 case 'b':
519 blocksize = atoi(optarg);
520 if (!tftp_blocksize_check(blocksize, 0)) {
521 return EXIT_FAILURE;
522 }
523 break;
524#endif
525#ifdef BB_FEATURE_TFTP_GET
526 case 'g':
527 cmd = tftp_cmd_get;
528 flags = O_WRONLY | O_CREAT;
529 break;
530#endif
531#ifdef BB_FEATURE_TFTP_PUT
532 case 'p':
533 cmd = tftp_cmd_put;
534 flags = O_RDONLY;
535 break;
536#endif
537 case 'l':
538 localfile = xstrdup(optarg);
539 break;
540 case 'r':
541 remotefile = xstrdup(optarg);
542 break;
543 }
544 }
545
546 if ((cmd == 0) || (optind == argc)) {
547 show_usage();
548 }
549
550 fd = open(localfile, flags, 0644);
551 if (fd < 0) {
552 perror_msg_and_die("local file");
553 }
554
555 host = xgethostbyname(argv[optind]);
556
557 if (optind + 2 == argc) {
558 port = atoi(argv[optind + 1]);
559 }
560
561#ifdef BB_FEATURE_TFTP_DEBUG
562 printf("using server \"%s\", remotefile \"%s\", "
563 "localfile \"%s\".\n",
564 inet_ntoa(*((struct in_addr *) host->h_addr)),
565 remotefile, localfile);
566#endif
567
568 result = tftp(cmd, host, remotefile, fd, port, blocksize);
569
570#ifdef BB_FEATURE_CLEAN_UP
571 close(fd);
572#endif
573 return(result);
574}