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 | |
| 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.
| -rw-r--r-- | Config.h | 1 | ||||
| -rw-r--r-- | applets/usage.h | 12 | ||||
| -rw-r--r-- | include/usage.h | 12 | ||||
| -rw-r--r-- | networking/tftp.c | 293 | ||||
| -rw-r--r-- | tftp.c | 293 | ||||
| -rw-r--r-- | usage.h | 12 |
6 files changed, 515 insertions, 108 deletions
| @@ -378,6 +378,7 @@ | |||
| 378 | // Tell tftp what commands that should be supported. | 378 | // Tell tftp what commands that should be supported. |
| 379 | #define BB_FEATURE_TFTP_PUT | 379 | #define BB_FEATURE_TFTP_PUT |
| 380 | #define BB_FEATURE_TFTP_GET | 380 | #define BB_FEATURE_TFTP_GET |
| 381 | //#define BB_FEATURE_TFTP_BLOCKSIZE | ||
| 381 | // | 382 | // |
| 382 | // features for vi | 383 | // features for vi |
| 383 | #define BB_FEATURE_VI_COLON // ":" colon commands, no "ex" mode | 384 | #define BB_FEATURE_VI_COLON // ":" colon commands, no "ex" mode |
diff --git a/applets/usage.h b/applets/usage.h index 0b095a4da..9dc7fae56 100644 --- a/applets/usage.h +++ b/applets/usage.h | |||
| @@ -1615,13 +1615,19 @@ | |||
| 1615 | #else | 1615 | #else |
| 1616 | #define USAGE_TFTP_PUT(a) | 1616 | #define USAGE_TFTP_PUT(a) |
| 1617 | #endif | 1617 | #endif |
| 1618 | #ifdef BB_FEATURE_TFTP_BLOCKSIZE | ||
| 1619 | #define USAGE_TFTP_BS(a) a | ||
| 1620 | #else | ||
| 1621 | #define USAGE_TFTP_BS(a) | ||
| 1622 | #endif | ||
| 1618 | 1623 | ||
| 1619 | #define tftp_trivial_usage \ | 1624 | #define tftp_trivial_usage \ |
| 1620 | "[OPTION]... HOST [PORT]" | 1625 | "[OPTION]... HOST [PORT]" |
| 1621 | #define tftp_full_usage \ | 1626 | #define tftp_full_usage \ |
| 1622 | "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ | 1627 | "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ |
| 1623 | "Options:\n" \ | 1628 | "Options:\n" \ |
| 1624 | "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \ | 1629 | "\t-l FILE\tLocal FILE.\n" \ |
| 1630 | "\t-r FILE\tRemote FILE.\n" \ | ||
| 1625 | USAGE_TFTP_GET( \ | 1631 | USAGE_TFTP_GET( \ |
| 1626 | "\t-g\tGet file.\n" \ | 1632 | "\t-g\tGet file.\n" \ |
| 1627 | ) \ | 1633 | ) \ |
| @@ -1629,7 +1635,9 @@ | |||
| 1629 | USAGE_TFTP_PUT( \ | 1635 | USAGE_TFTP_PUT( \ |
| 1630 | "\t-p\tPut file.\n" \ | 1636 | "\t-p\tPut file.\n" \ |
| 1631 | ) \ | 1637 | ) \ |
| 1632 | "\t-r FILE\tTransfer remote FILE.\n" | 1638 | USAGE_TFTP_BS( \ |
| 1639 | "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \ | ||
| 1640 | ) | ||
| 1633 | 1641 | ||
| 1634 | #define touch_trivial_usage \ | 1642 | #define touch_trivial_usage \ |
| 1635 | "[-c] FILE [FILE ...]" | 1643 | "[-c] FILE [FILE ...]" |
diff --git a/include/usage.h b/include/usage.h index 0b095a4da..9dc7fae56 100644 --- a/include/usage.h +++ b/include/usage.h | |||
| @@ -1615,13 +1615,19 @@ | |||
| 1615 | #else | 1615 | #else |
| 1616 | #define USAGE_TFTP_PUT(a) | 1616 | #define USAGE_TFTP_PUT(a) |
| 1617 | #endif | 1617 | #endif |
| 1618 | #ifdef BB_FEATURE_TFTP_BLOCKSIZE | ||
| 1619 | #define USAGE_TFTP_BS(a) a | ||
| 1620 | #else | ||
| 1621 | #define USAGE_TFTP_BS(a) | ||
| 1622 | #endif | ||
| 1618 | 1623 | ||
| 1619 | #define tftp_trivial_usage \ | 1624 | #define tftp_trivial_usage \ |
| 1620 | "[OPTION]... HOST [PORT]" | 1625 | "[OPTION]... HOST [PORT]" |
| 1621 | #define tftp_full_usage \ | 1626 | #define tftp_full_usage \ |
| 1622 | "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ | 1627 | "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ |
| 1623 | "Options:\n" \ | 1628 | "Options:\n" \ |
| 1624 | "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \ | 1629 | "\t-l FILE\tLocal FILE.\n" \ |
| 1630 | "\t-r FILE\tRemote FILE.\n" \ | ||
| 1625 | USAGE_TFTP_GET( \ | 1631 | USAGE_TFTP_GET( \ |
| 1626 | "\t-g\tGet file.\n" \ | 1632 | "\t-g\tGet file.\n" \ |
| 1627 | ) \ | 1633 | ) \ |
| @@ -1629,7 +1635,9 @@ | |||
| 1629 | USAGE_TFTP_PUT( \ | 1635 | USAGE_TFTP_PUT( \ |
| 1630 | "\t-p\tPut file.\n" \ | 1636 | "\t-p\tPut file.\n" \ |
| 1631 | ) \ | 1637 | ) \ |
| 1632 | "\t-r FILE\tTransfer remote FILE.\n" | 1638 | USAGE_TFTP_BS( \ |
| 1639 | "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \ | ||
| 1640 | ) | ||
| 1633 | 1641 | ||
| 1634 | #define touch_trivial_usage \ | 1642 | #define touch_trivial_usage \ |
| 1635 | "[-c] FILE [FILE ...]" | 1643 | "[-c] FILE [FILE ...]" |
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 | } |
| @@ -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 | } |
| @@ -1615,13 +1615,19 @@ | |||
| 1615 | #else | 1615 | #else |
| 1616 | #define USAGE_TFTP_PUT(a) | 1616 | #define USAGE_TFTP_PUT(a) |
| 1617 | #endif | 1617 | #endif |
| 1618 | #ifdef BB_FEATURE_TFTP_BLOCKSIZE | ||
| 1619 | #define USAGE_TFTP_BS(a) a | ||
| 1620 | #else | ||
| 1621 | #define USAGE_TFTP_BS(a) | ||
| 1622 | #endif | ||
| 1618 | 1623 | ||
| 1619 | #define tftp_trivial_usage \ | 1624 | #define tftp_trivial_usage \ |
| 1620 | "[OPTION]... HOST [PORT]" | 1625 | "[OPTION]... HOST [PORT]" |
| 1621 | #define tftp_full_usage \ | 1626 | #define tftp_full_usage \ |
| 1622 | "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ | 1627 | "Transfers a file from/to a tftp server using \"octet\" mode.\n\n" \ |
| 1623 | "Options:\n" \ | 1628 | "Options:\n" \ |
| 1624 | "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \ | 1629 | "\t-l FILE\tLocal FILE.\n" \ |
| 1630 | "\t-r FILE\tRemote FILE.\n" \ | ||
| 1625 | USAGE_TFTP_GET( \ | 1631 | USAGE_TFTP_GET( \ |
| 1626 | "\t-g\tGet file.\n" \ | 1632 | "\t-g\tGet file.\n" \ |
| 1627 | ) \ | 1633 | ) \ |
| @@ -1629,7 +1635,9 @@ | |||
| 1629 | USAGE_TFTP_PUT( \ | 1635 | USAGE_TFTP_PUT( \ |
| 1630 | "\t-p\tPut file.\n" \ | 1636 | "\t-p\tPut file.\n" \ |
| 1631 | ) \ | 1637 | ) \ |
| 1632 | "\t-r FILE\tTransfer remote FILE.\n" | 1638 | USAGE_TFTP_BS( \ |
| 1639 | "\t-b SIZE\tTransfer blocks of SIZE octets.\n" \ | ||
| 1640 | ) | ||
| 1633 | 1641 | ||
| 1634 | #define touch_trivial_usage \ | 1642 | #define touch_trivial_usage \ |
| 1635 | "[-c] FILE [FILE ...]" | 1643 | "[-c] FILE [FILE ...]" |
