diff options
author | Glenn L McGrath <bug1@ihug.co.nz> | 2001-10-05 04:40:37 +0000 |
---|---|---|
committer | Glenn L McGrath <bug1@ihug.co.nz> | 2001-10-05 04:40:37 +0000 |
commit | ad117d8a213979fae1bf6ec9162c2e998800b096 (patch) | |
tree | bfc74a10998be45c79515b34f2288702cfeed726 /networking/tftp.c | |
parent | 24e2833cdfcba3505bbde9b56906bbcbb67aa2be (diff) | |
download | busybox-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.c | 293 |
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 | |||
50 | static const char *tftp_error_msg[] = { | 63 | static 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[] = { | |||
61 | const int tftp_cmd_get = 1; | 74 | const int tftp_cmd_get = 1; |
62 | const int tftp_cmd_put = 2; | 75 | const int tftp_cmd_put = 2; |
63 | 76 | ||
77 | #ifdef BB_FEATURE_TFTP_BLOCKSIZE | ||
78 | |||
79 | static 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 | |||
96 | static 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 | |||
64 | static inline int tftp(const int cmd, const struct hostent *host, | 140 | static 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 | } |