1 /* ------------------------------------------------------------------------- */
4 /* A simple tftp client for busybox. */
5 /* Tries to follow RFC1350. */
6 /* Only "octet" mode supported. */
7 /* Optional blocksize negotiation (RFC2347 + RFC2348) */
9 /* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
11 /* Parts of the code based on: */
13 /* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
14 /* and Remi Lefebvre <remi@debian.org> */
16 /* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
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. */
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. */
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 */
32 /* ------------------------------------------------------------------------- */
37 #include <sys/types.h>
38 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
49 //#define CONFIG_FEATURE_TFTP_DEBUG
51 #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
52 #define TFTP_TIMEOUT 5 /* seconds */
54 /* opcodes we support */
63 static const char * const tftp_bb_error_msg[] = {
67 "Disk full or allocation error",
68 "Illegal TFTP operation",
69 "Unknown transfer ID",
70 "File already exists",
74 #ifdef CONFIG_FEATURE_TFTP_GET
75 # define tftp_cmd_get 1
77 # define tftp_cmd_get 0
79 #ifdef CONFIG_FEATURE_TFTP_PUT
80 # define tftp_cmd_put (tftp_cmd_get+1)
82 # define tftp_cmd_put 0
86 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
88 static int tftp_blocksize_check(int blocksize, int bufsize)
90 /* Check if the blocksize is valid:
91 * RFC2348 says between 8 and 65464,
92 * but our implementation makes it impossible
93 * to use blocksizes smaller than 22 octets.
96 if ((bufsize && (blocksize > bufsize)) ||
97 (blocksize < 8) || (blocksize > 65464)) {
98 bb_error_msg("bad blocksize");
105 static char *tftp_option_get(char *buf, int len, char *option)
113 /* Make sure the options are terminated correctly */
115 for (k = 0; k < len; k++) {
116 if (buf[k] == '\0') {
126 if (strcasecmp(buf, option) == 0) {
149 static inline int tftp(const int cmd, const struct hostent *host,
150 const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
152 const int cmd_get = cmd & tftp_cmd_get;
153 const int cmd_put = cmd & tftp_cmd_put;
154 const int bb_tftp_num_retries = 5;
156 struct sockaddr_in sa;
157 struct sockaddr_in from;
167 int timeout = bb_tftp_num_retries;
168 unsigned short block_nr = 1;
170 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
171 int want_option_ack = 0;
174 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
175 * size varies meaning BUFFERS_GO_ON_STACK would fail */
176 char *buf=xmalloc(tftp_bufsize + 4);
180 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
181 bb_perror_msg("socket");
188 bind(socketfd, (struct sockaddr *)&sa, len);
190 sa.sin_family = host->h_addrtype;
192 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
193 sizeof(sa.sin_addr));
209 /* first create the opcode part */
211 *((unsigned short *) cp) = htons(opcode);
215 /* add filename and mode */
217 if ((cmd_get && (opcode == TFTP_RRQ)) ||
218 (cmd_put && (opcode == TFTP_WRQ))) {
221 /* see if the filename fits into buf */
222 /* and fill in packet */
224 len = strlen(remotefile) + 1;
226 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
230 safe_strncpy(cp, remotefile, len);
234 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
235 bb_error_msg("too long remote-filename");
239 /* add "mode" part of the package */
241 memcpy(cp, "octet", 6);
244 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
246 len = tftp_bufsize - 4; /* data block size */
248 if (len != TFTP_BLOCKSIZE_DEFAULT) {
250 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
251 bb_error_msg("too long remote-filename");
255 /* add "blksize" + number of blocks */
257 memcpy(cp, "blksize", 8);
260 cp += snprintf(cp, 6, "%d", len) + 1;
267 /* add ack and data */
269 if ((cmd_get && (opcode == TFTP_ACK)) ||
270 (cmd_put && (opcode == TFTP_DATA))) {
272 *((unsigned short *) cp) = htons(block_nr);
278 if (cmd_put && (opcode == TFTP_DATA)) {
279 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
282 bb_perror_msg("read");
286 if (len != (tftp_bufsize - 4)) {
298 timeout = bb_tftp_num_retries; /* re-initialize */
303 #ifdef CONFIG_FEATURE_TFTP_DEBUG
304 fprintf(stderr, "sending %u bytes\n", len);
305 for (cp = buf; cp < &buf[len]; cp++)
306 fprintf(stderr, "%02x ", (unsigned char)*cp);
307 fprintf(stderr, "\n");
309 if (sendto(socketfd, buf, len, 0,
310 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
311 bb_perror_msg("send");
317 if (finished && (opcode == TFTP_ACK)) {
323 memset(&from, 0, sizeof(from));
324 fromlen = sizeof(from);
326 tv.tv_sec = TFTP_TIMEOUT;
330 FD_SET(socketfd, &rfds);
332 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
334 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
335 (struct sockaddr *) &from, &fromlen);
338 bb_perror_msg("recvfrom");
344 if (sa.sin_port == port) {
345 sa.sin_port = from.sin_port;
347 if (sa.sin_port == from.sin_port) {
351 /* fall-through for bad packets! */
352 /* discard the packet - treat as timeout */
353 timeout = bb_tftp_num_retries;
356 bb_error_msg("timeout");
361 bb_error_msg("last timeout");
366 bb_perror_msg("select");
370 } while (timeout && (len >= 0));
372 if ((finished) || (len < 0)) {
376 /* process received packet */
379 opcode = ntohs(*((unsigned short *) buf));
380 tmp = ntohs(*((unsigned short *) &buf[2]));
382 #ifdef CONFIG_FEATURE_TFTP_DEBUG
383 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
386 if (opcode == TFTP_ERROR) {
387 const char *msg = NULL;
389 if (buf[4] != '\0') {
391 buf[tftp_bufsize - 1] = '\0';
392 } else if (tmp < (sizeof(tftp_bb_error_msg)
395 msg = tftp_bb_error_msg[tmp];
399 bb_error_msg("server says: %s", msg);
405 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
406 if (want_option_ack) {
410 if (opcode == TFTP_OACK) {
412 /* server seems to support options */
416 res = tftp_option_get(&buf[2], len-2,
420 int blksize = atoi(res);
422 if (tftp_blocksize_check(blksize,
431 #ifdef CONFIG_FEATURE_TFTP_DEBUG
432 fprintf(stderr, "using blksize %u\n", blksize);
434 tftp_bufsize = blksize + 4;
440 * we should send ERROR 8 */
441 bb_error_msg("bad server option");
445 bb_error_msg("warning: blksize not supported by server"
446 " - reverting to 512");
448 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
452 if (cmd_get && (opcode == TFTP_DATA)) {
454 if (tmp == block_nr) {
456 len = bb_full_write(localfd, &buf[4], len - 4);
459 bb_perror_msg("write");
463 if (len != (tftp_bufsize - 4)) {
470 /* in case the last ack disappeared into the ether */
471 if ( tmp == (block_nr - 1) ) {
475 } else if (tmp + 1 == block_nr) {
476 /* Server lost our TFTP_ACK. Resend it */
483 if (cmd_put && (opcode == TFTP_ACK)) {
485 if (tmp == (unsigned short)(block_nr - 1)) {
496 #ifdef CONFIG_FEATURE_CLEAN_UP
502 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
505 int tftp_main(int argc, char **argv)
507 struct hostent *host = NULL;
508 const char *localfile = NULL;
509 const char *remotefile = NULL;
515 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
517 /* figure out what to pass to getopt */
519 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
520 char *sblocksize = NULL;
522 #define BS_ARG , &sblocksize
528 #ifdef CONFIG_FEATURE_TFTP_GET
530 #define GET_COMPL ":g"
536 #ifdef CONFIG_FEATURE_TFTP_PUT
538 #define PUT_COMPL ":p"
544 #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
545 bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
546 #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
547 bb_opt_complementally = GET_COMPL PUT_COMPL;
549 /* XXX: may be should #error ? */
553 cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
554 &localfile, &remotefile BS_ARG);
555 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
557 blocksize = atoi(sblocksize);
558 if (!tftp_blocksize_check(blocksize, 0)) {
564 cmd &= (tftp_cmd_get | tftp_cmd_put);
565 #ifdef CONFIG_FEATURE_TFTP_GET
566 if(cmd == tftp_cmd_get)
567 flags = O_WRONLY | O_CREAT | O_TRUNC;
569 #ifdef CONFIG_FEATURE_TFTP_PUT
570 if(cmd == tftp_cmd_put)
574 if(localfile == NULL)
575 localfile = remotefile;
576 if(remotefile == NULL)
577 remotefile = localfile;
578 /* XXX: I corrected this, but may be wrong too. vodz */
579 if(localfile==NULL || strcmp(localfile, "-") == 0) {
580 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
582 fd = open(localfile, flags, 0644);
585 bb_perror_msg_and_die("local file");
588 /* XXX: argv[optind] and/or argv[optind + 1] may be NULL! */
589 host = xgethostbyname(argv[optind]);
590 port = bb_lookup_port(argv[optind + 1], "udp", 69);
592 #ifdef CONFIG_FEATURE_TFTP_DEBUG
593 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
594 "localfile \"%s\".\n",
595 inet_ntoa(*((struct in_addr *) host->h_addr)),
596 remotefile, localfile);
599 result = tftp(cmd, host, remotefile, fd, port, blocksize);
601 #ifdef CONFIG_FEATURE_CLEAN_UP
602 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {