aboutsummaryrefslogtreecommitdiff
path: root/networking/tftp.c
diff options
context:
space:
mode:
authorGlenn L McGrath <bug1@ihug.co.nz>2001-10-05 04:40:37 +0000
committerGlenn L McGrath <bug1@ihug.co.nz>2001-10-05 04:40:37 +0000
commitad117d8a213979fae1bf6ec9162c2e998800b096 (patch)
treebfc74a10998be45c79515b34f2288702cfeed726 /networking/tftp.c
parent24e2833cdfcba3505bbde9b56906bbcbb67aa2be (diff)
downloadbusybox-w32-ad117d8a213979fae1bf6ec9162c2e998800b096.tar.gz
busybox-w32-ad117d8a213979fae1bf6ec9162c2e998800b096.tar.bz2
busybox-w32-ad117d8a213979fae1bf6ec9162c2e998800b096.zip
Apply Magnus Damm's patch, adds tftp blocksize support, and some cleanups.
Diffstat (limited to 'networking/tftp.c')
-rw-r--r--networking/tftp.c293
1 files changed, 242 insertions, 51 deletions
diff --git a/networking/tftp.c b/networking/tftp.c
index 999b5d706..530b3d134 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -3,7 +3,8 @@
3/* */ 3/* */
4/* A simple tftp client for busybox. */ 4/* A simple tftp client for busybox. */
5/* Tries to follow RFC1350. */ 5/* Tries to follow RFC1350. */
6/* Only "octet" mode and 512-byte data blocks are supported. */ 6/* Only "octet" mode supported. */
7/* Optional blocksize negotiation (RFC2347 + RFC2348) */
7/* */ 8/* */
8/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */ 9/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
9/* */ 10/* */
@@ -47,6 +48,18 @@
47 48
48//#define BB_FEATURE_TFTP_DEBUG 49//#define BB_FEATURE_TFTP_DEBUG
49 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
50static const char *tftp_error_msg[] = { 63static const char *tftp_error_msg[] = {
51 "Undefined error", 64 "Undefined error",
52 "File not found", 65 "File not found",
@@ -61,8 +74,71 @@ static const char *tftp_error_msg[] = {
61const int tftp_cmd_get = 1; 74const int tftp_cmd_get = 1;
62const int tftp_cmd_put = 2; 75const int tftp_cmd_put = 2;
63 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
64static inline int tftp(const int cmd, const struct hostent *host, 140static inline int tftp(const int cmd, const struct hostent *host,
65 const char *serverfile, int localfd, const int port, int tftp_bufsize) 141 const char *remotefile, int localfd, const int port, int tftp_bufsize)
66{ 142{
67 const int cmd_get = cmd & tftp_cmd_get; 143 const int cmd_get = cmd & tftp_cmd_get;
68 const int cmd_put = cmd & tftp_cmd_put; 144 const int cmd_put = cmd & tftp_cmd_put;
@@ -81,7 +157,12 @@ static inline int tftp(const int cmd, const struct hostent *host,
81 int finished = 0; 157 int finished = 0;
82 int timeout = bb_tftp_num_retries; 158 int timeout = bb_tftp_num_retries;
83 int block_nr = 1; 159 int block_nr = 1;
84 RESERVE_BB_BUFFER(buf, tftp_bufsize + 4); // Why 4 ? 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 */
85 166
86 tftp_bufsize += 4; 167 tftp_bufsize += 4;
87 168
@@ -103,50 +184,79 @@ static inline int tftp(const int cmd, const struct hostent *host,
103 /* build opcode */ 184 /* build opcode */
104 185
105 if (cmd_get) { 186 if (cmd_get) {
106 opcode = 1; // read request = 1 187 opcode = TFTP_RRQ;
107 } 188 }
108 189
109 if (cmd_put) { 190 if (cmd_put) {
110 opcode = 2; // write request = 2 191 opcode = TFTP_WRQ;
111 } 192 }
112 193
113 while (1) { 194 while (1) {
114 195
115
116 /* build packet of type "opcode" */
117
118
119 cp = buf; 196 cp = buf;
120 197
198 /* first create the opcode part */
199
121 *((unsigned short *) cp) = htons(opcode); 200 *((unsigned short *) cp) = htons(opcode);
122 201
123 cp += 2; 202 cp += 2;
124 203
125 /* add filename and mode */ 204 /* add filename and mode */
126 205
127 if ((cmd_get && (opcode == 1)) || // read request = 1 206 if ((cmd_get && (opcode == TFTP_RRQ)) ||
128 (cmd_put && (opcode == 2))) { // write request = 2 207 (cmd_put && (opcode == TFTP_WRQ))) {
208 int too_long = 0;
129 209
130 /* what is this trying to do ? */ 210 /* see if the filename fits into buf */
131 while (cp != &buf[tftp_bufsize - 1]) { 211 /* and fill in packet */
132 if ((*cp = *serverfile++) == '\0') 212
133 break; 213 len = strlen(remotefile) + 1;
134 cp++; 214
215 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
216 too_long = 1;
135 } 217 }
136 /* and this ? */ 218 else {
137 if ((*cp != '\0') || (&buf[tftp_bufsize - 1] - cp) < 7) { 219 safe_strncpy(cp, remotefile, len);
138 error_msg("too long server-filename"); 220 cp += len;
221 }
222
223 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
224 error_msg("too long remote-filename");
139 break; 225 break;
140 } 226 }
141 227
142 memcpy(cp + 1, "octet", 6); 228 /* add "mode" part of the package */
143 cp += 7; 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
144 } 254 }
145 255
146 /* add ack and data */ 256 /* add ack and data */
147 257
148 if ((cmd_get && (opcode == 4)) || // acknowledgement = 4 258 if ((cmd_get && (opcode == TFTP_ACK)) ||
149 (cmd_put && (opcode == 3))) { // data packet == 3 259 (cmd_put && (opcode == TFTP_DATA))) {
150 260
151 *((unsigned short *) cp) = htons(block_nr); 261 *((unsigned short *) cp) = htons(block_nr);
152 262
@@ -154,7 +264,7 @@ static inline int tftp(const int cmd, const struct hostent *host,
154 264
155 block_nr++; 265 block_nr++;
156 266
157 if (cmd_put && (opcode == 3)) { // data packet == 3 267 if (cmd_put && (opcode == TFTP_DATA)) {
158 len = read(localfd, cp, tftp_bufsize - 4); 268 len = read(localfd, cp, tftp_bufsize - 4);
159 269
160 if (len < 0) { 270 if (len < 0) {
@@ -200,7 +310,7 @@ static inline int tftp(const int cmd, const struct hostent *host,
200 memset(&from, 0, sizeof(from)); 310 memset(&from, 0, sizeof(from));
201 fromlen = sizeof(from); 311 fromlen = sizeof(from);
202 312
203 tv.tv_sec = 5; // BB_TFPT_TIMEOUT = 5 313 tv.tv_sec = TFTP_TIMEOUT;
204 tv.tv_usec = 0; 314 tv.tv_usec = 0;
205 315
206 FD_ZERO(&rfds); 316 FD_ZERO(&rfds);
@@ -261,9 +371,76 @@ static inline int tftp(const int cmd, const struct hostent *host,
261 printf("received %d bytes: %04x %04x\n", len, opcode, tmp); 371 printf("received %d bytes: %04x %04x\n", len, opcode, tmp);
262#endif 372#endif
263 373
264 if (cmd_get && (opcode == 3)) { // data packet == 3 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)) {
265 441
266 if (tmp == block_nr) { 442 if (tmp == block_nr) {
443
267 len = write(localfd, &buf[4], len - 4); 444 len = write(localfd, &buf[4], len - 4);
268 445
269 if (len < 0) { 446 if (len < 0) {
@@ -275,43 +452,30 @@ static inline int tftp(const int cmd, const struct hostent *host,
275 finished++; 452 finished++;
276 } 453 }
277 454
278 opcode = 4; // acknowledgement = 4 455 opcode = TFTP_ACK;
279 continue; 456 continue;
280 } 457 }
281 } 458 }
282 459
283 if (cmd_put && (opcode == 4)) { // acknowledgement = 4 460 if (cmd_put && (opcode == TFTP_ACK)) {
284 461
285 if (tmp == (block_nr - 1)) { 462 if (tmp == (block_nr - 1)) {
286 if (finished) { 463 if (finished) {
287 break; 464 break;
288 } 465 }
289 466
290 opcode = 3; // data packet == 3 467 opcode = TFTP_DATA;
291 continue; 468 continue;
292 } 469 }
293 } 470 }
294
295 if (opcode == 5) { // error code == 5
296 char *msg = NULL;
297
298 if (buf[4] != '\0') {
299 msg = &buf[4];
300 buf[tftp_bufsize - 1] = '\0';
301 } else if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) {
302 msg = (char *) tftp_error_msg[tmp];
303 }
304
305 if (msg) {
306 error_msg("server says: %s", msg);
307 }
308
309 break;
310 }
311 } 471 }
312 472
473#ifdef BB_FEATURE_CLEAN_UP
313 close(socketfd); 474 close(socketfd);
314 475
476 RELEASE_BB_BUFFER(buf);
477#endif
478
315 return finished ? EXIT_SUCCESS : EXIT_FAILURE; 479 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
316} 480}
317 481
@@ -326,13 +490,38 @@ int tftp_main(int argc, char **argv)
326 int flags = 0; 490 int flags = 0;
327 int opt; 491 int opt;
328 int result; 492 int result;
329 int blocksize = 512; 493 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
494
495 /* figure out what to pass to getopt */
330 496
331 while ((opt = getopt(argc, argv, "b:gpl:r:")) != -1) { 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) {
332 switch (opt) { 516 switch (opt) {
517#ifdef BB_FEATURE_TFTP_BLOCKSIZE
333 case 'b': 518 case 'b':
334 blocksize = atoi(optarg); 519 blocksize = atoi(optarg);
520 if (!tftp_blocksize_check(blocksize, 0)) {
521 return EXIT_FAILURE;
522 }
335 break; 523 break;
524#endif
336#ifdef BB_FEATURE_TFTP_GET 525#ifdef BB_FEATURE_TFTP_GET
337 case 'g': 526 case 'g':
338 cmd = tftp_cmd_get; 527 cmd = tftp_cmd_get;
@@ -370,14 +559,16 @@ int tftp_main(int argc, char **argv)
370 } 559 }
371 560
372#ifdef BB_FEATURE_TFTP_DEBUG 561#ifdef BB_FEATURE_TFTP_DEBUG
373 printf("using server \"%s\", serverfile \"%s\"," 562 printf("using server \"%s\", remotefile \"%s\", "
374 "localfile \"%s\".\n", 563 "localfile \"%s\".\n",
375 inet_ntoa(*((struct in_addr *) host->h_addr)), 564 inet_ntoa(*((struct in_addr *) host->h_addr)),
376 remotefile, localfile); 565 remotefile, localfile);
377#endif 566#endif
378 567
379 result = tftp(cmd, host, remotefile, fd, port, blocksize); 568 result = tftp(cmd, host, remotefile, fd, port, blocksize);
380 close(fd);
381 569
570#ifdef BB_FEATURE_CLEAN_UP
571 close(fd);
572#endif
382 return(result); 573 return(result);
383} \ No newline at end of file 574}