1 /* vi: set sw=4 ts=4: */
2 /* -------------------------------------------------------------------------
5 * A simple tftp client for busybox.
6 * Tries to follow RFC1350.
7 * Only "octet" mode supported.
8 * Optional blocksize negotiation (RFC2347 + RFC2348)
10 * Copyright (C) 2001 Magnus Damm <damm@opensource.se>
12 * Parts of the code based on:
14 * atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
15 * and Remi Lefebvre <remi@debian.org>
17 * utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
19 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
20 * ------------------------------------------------------------------------- */
25 #include <sys/types.h>
26 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
36 //#define CONFIG_FEATURE_TFTP_DEBUG
38 #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
39 #define TFTP_TIMEOUT 5 /* seconds */
41 /* opcodes we support */
50 static const char * const tftp_bb_error_msg[] = {
54 "Disk full or allocation error",
55 "Illegal TFTP operation",
56 "Unknown transfer ID",
57 "File already exists",
61 #ifdef CONFIG_FEATURE_TFTP_GET
62 # define tftp_cmd_get 1
64 # define tftp_cmd_get 0
66 #ifdef CONFIG_FEATURE_TFTP_PUT
67 # define tftp_cmd_put (tftp_cmd_get+1)
69 # define tftp_cmd_put 0
73 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
75 static int tftp_blocksize_check(int blocksize, int bufsize)
77 /* Check if the blocksize is valid:
78 * RFC2348 says between 8 and 65464,
79 * but our implementation makes it impossible
80 * to use blocksizes smaller than 22 octets.
83 if ((bufsize && (blocksize > bufsize)) ||
84 (blocksize < 8) || (blocksize > 65464)) {
85 bb_error_msg("bad blocksize");
92 static char *tftp_option_get(char *buf, int len, char *option)
100 /* Make sure the options are terminated correctly */
102 for (k = 0; k < len; k++) {
103 if (buf[k] == '\0') {
113 if (strcasecmp(buf, option) == 0) {
136 static inline int tftp(const int cmd, const struct hostent *host,
137 const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
139 const int cmd_get = cmd & tftp_cmd_get;
140 const int cmd_put = cmd & tftp_cmd_put;
141 const int bb_tftp_num_retries = 5;
143 struct sockaddr_in sa;
144 struct sockaddr_in from;
154 int timeout = bb_tftp_num_retries;
155 unsigned short block_nr = 1;
157 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
158 int want_option_ack = 0;
161 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
162 * size varies meaning BUFFERS_GO_ON_STACK would fail */
163 char *buf=xmalloc(tftp_bufsize + 4);
167 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { /* bb_xsocket? */
168 bb_perror_msg("socket");
175 bind(socketfd, (struct sockaddr *)&sa, len);
177 sa.sin_family = host->h_addrtype;
179 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
180 sizeof(sa.sin_addr));
196 /* first create the opcode part */
198 *((unsigned short *) cp) = htons(opcode);
202 /* add filename and mode */
204 if ((cmd_get && (opcode == TFTP_RRQ)) ||
205 (cmd_put && (opcode == TFTP_WRQ))) {
208 /* see if the filename fits into buf */
209 /* and fill in packet */
211 len = strlen(remotefile) + 1;
213 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
217 safe_strncpy(cp, remotefile, len);
221 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
222 bb_error_msg("too long remote-filename");
226 /* add "mode" part of the package */
228 memcpy(cp, "octet", 6);
231 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
233 len = tftp_bufsize - 4; /* data block size */
235 if (len != TFTP_BLOCKSIZE_DEFAULT) {
237 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
238 bb_error_msg("too long remote-filename");
242 /* add "blksize" + number of blocks */
244 memcpy(cp, "blksize", 8);
247 cp += snprintf(cp, 6, "%d", len) + 1;
254 /* add ack and data */
256 if ((cmd_get && (opcode == TFTP_ACK)) ||
257 (cmd_put && (opcode == TFTP_DATA))) {
259 *((unsigned short *) cp) = htons(block_nr);
265 if (cmd_put && (opcode == TFTP_DATA)) {
266 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
269 bb_perror_msg("read");
273 if (len != (tftp_bufsize - 4)) {
285 timeout = bb_tftp_num_retries; /* re-initialize */
290 #ifdef CONFIG_FEATURE_TFTP_DEBUG
291 fprintf(stderr, "sending %u bytes\n", len);
292 for (cp = buf; cp < &buf[len]; cp++)
293 fprintf(stderr, "%02x ", (unsigned char)*cp);
294 fprintf(stderr, "\n");
296 if (sendto(socketfd, buf, len, 0,
297 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
298 bb_perror_msg("send");
304 if (finished && (opcode == TFTP_ACK)) {
310 memset(&from, 0, sizeof(from));
311 fromlen = sizeof(from);
313 tv.tv_sec = TFTP_TIMEOUT;
317 FD_SET(socketfd, &rfds);
319 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
321 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
322 (struct sockaddr *) &from, &fromlen);
325 bb_perror_msg("recvfrom");
331 if (sa.sin_port == port) {
332 sa.sin_port = from.sin_port;
334 if (sa.sin_port == from.sin_port) {
338 /* fall-through for bad packets! */
339 /* discard the packet - treat as timeout */
340 timeout = bb_tftp_num_retries;
343 bb_error_msg("timeout");
348 bb_error_msg("last timeout");
353 bb_perror_msg("select");
357 } while (timeout && (len >= 0));
359 if ((finished) || (len < 0)) {
363 /* process received packet */
366 opcode = ntohs(*((unsigned short *) buf));
367 tmp = ntohs(*((unsigned short *) &buf[2]));
369 #ifdef CONFIG_FEATURE_TFTP_DEBUG
370 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
373 if (opcode == TFTP_ERROR) {
374 const char *msg = NULL;
376 if (buf[4] != '\0') {
378 buf[tftp_bufsize - 1] = '\0';
379 } else if (tmp < (sizeof(tftp_bb_error_msg)
382 msg = tftp_bb_error_msg[tmp];
386 bb_error_msg("server says: %s", msg);
392 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
393 if (want_option_ack) {
397 if (opcode == TFTP_OACK) {
399 /* server seems to support options */
403 res = tftp_option_get(&buf[2], len-2,
407 int blksize = atoi(res);
409 if (tftp_blocksize_check(blksize,
418 #ifdef CONFIG_FEATURE_TFTP_DEBUG
419 fprintf(stderr, "using blksize %u\n", blksize);
421 tftp_bufsize = blksize + 4;
427 * we should send ERROR 8 */
428 bb_error_msg("bad server option");
432 bb_error_msg("warning: blksize not supported by server"
433 " - reverting to 512");
435 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
439 if (cmd_get && (opcode == TFTP_DATA)) {
441 if (tmp == block_nr) {
443 len = bb_full_write(localfd, &buf[4], len - 4);
446 bb_perror_msg("write");
450 if (len != (tftp_bufsize - 4)) {
457 /* in case the last ack disappeared into the ether */
458 if ( tmp == (block_nr - 1) ) {
462 } else if (tmp + 1 == block_nr) {
463 /* Server lost our TFTP_ACK. Resend it */
470 if (cmd_put && (opcode == TFTP_ACK)) {
472 if (tmp == (unsigned short)(block_nr - 1)) {
483 #ifdef CONFIG_FEATURE_CLEAN_UP
489 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
492 int tftp_main(int argc, char **argv)
494 struct hostent *host = NULL;
495 const char *localfile = NULL;
496 const char *remotefile = NULL;
502 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
504 /* figure out what to pass to getopt */
506 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
507 char *sblocksize = NULL;
509 #define BS_ARG , &sblocksize
515 #ifdef CONFIG_FEATURE_TFTP_GET
517 #define GET_COMPL ":g"
523 #ifdef CONFIG_FEATURE_TFTP_PUT
525 #define PUT_COMPL ":p"
531 #if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
532 bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
533 #elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
534 bb_opt_complementally = GET_COMPL PUT_COMPL;
536 /* XXX: may be should #error ? */
540 cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
541 &localfile, &remotefile BS_ARG);
542 #ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
544 blocksize = atoi(sblocksize);
545 if (!tftp_blocksize_check(blocksize, 0)) {
551 cmd &= (tftp_cmd_get | tftp_cmd_put);
552 #ifdef CONFIG_FEATURE_TFTP_GET
553 if(cmd == tftp_cmd_get)
554 flags = O_WRONLY | O_CREAT | O_TRUNC;
556 #ifdef CONFIG_FEATURE_TFTP_PUT
557 if(cmd == tftp_cmd_put)
561 if(localfile == NULL)
562 localfile = remotefile;
563 if(remotefile == NULL)
564 remotefile = localfile;
565 /* XXX: I corrected this, but may be wrong too. vodz */
566 if(localfile==NULL || strcmp(localfile, "-") == 0) {
567 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
569 fd = open(localfile, flags, 0644);
572 bb_perror_msg_and_die("local file");
575 /* XXX: argv[optind] and/or argv[optind + 1] may be NULL! */
576 host = xgethostbyname(argv[optind]);
577 port = bb_lookup_port(argv[optind + 1], "udp", 69);
579 #ifdef CONFIG_FEATURE_TFTP_DEBUG
580 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
581 "localfile \"%s\".\n",
582 inet_ntoa(*((struct in_addr *) host->h_addr)),
583 remotefile, localfile);
586 result = tftp(cmd, host, remotefile, fd, port, blocksize);
588 #ifdef CONFIG_FEATURE_CLEAN_UP
589 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {