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 #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
27 #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
28 #define TFTP_TIMEOUT 5 /* seconds */
29 #define TFTP_NUM_RETRIES 5 /* number of retries */
31 /* opcodes we support */
39 static const char *const tftp_bb_error_msg[] = {
43 "Disk full or allocation error",
44 "Illegal TFTP operation",
45 "Unknown transfer ID",
46 "File already exists",
50 #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
52 #define CMD_GET(cmd) 1
53 #define CMD_PUT(cmd) 0
54 #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
56 #define CMD_GET(cmd) 0
57 #define CMD_PUT(cmd) 1
59 #define USE_GETPUT(a) a
60 /* masks coming from getpot32 */
61 #define CMD_GET(cmd) ((cmd) & 1)
62 #define CMD_PUT(cmd) ((cmd) & 2)
64 /* NB: in the code below
65 * CMD_GET(cmd) and CMD_GET(cmd) are mutually exclusive
69 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
71 static int tftp_blocksize_check(int blocksize, int bufsize)
73 /* Check if the blocksize is valid:
74 * RFC2348 says between 8 and 65464,
75 * but our implementation makes it impossible
76 * to use blocksizes smaller than 22 octets.
79 if ((bufsize && (blocksize > bufsize))
80 || (blocksize < 8) || (blocksize > 65564)
82 bb_error_msg("bad blocksize");
89 static char *tftp_option_get(char *buf, int len, const char * const option)
96 /* Make sure the options are terminated correctly */
98 for (k = 0; k < len; k++) {
109 if (strcasecmp(buf, option) == 0) {
132 #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
135 len_and_sockaddr *peer_lsa,
136 const char *remotefile, const int localfd,
137 unsigned port, int tftp_bufsize)
145 int timeout = TFTP_NUM_RETRIES;
146 uint16_t block_nr = 1;
150 USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
153 len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
155 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
156 * size varies meaning BUFFERS_GO_ON_STACK would fail */
157 /* We must keep the transmit and receive buffers seperate */
158 /* In case we rcv a garbage pkt and we need to rexmit the last pkt */
159 char *xbuf = xmalloc(tftp_bufsize += 4);
160 char *rbuf = xmalloc(tftp_bufsize);
162 port = org_port = htons(port);
164 socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
175 /* first create the opcode part */
176 /* (this 16bit store is aligned) */
177 *((uint16_t*)cp) = htons(opcode);
180 /* add filename and mode */
181 if (CMD_GET(cmd) ? (opcode == TFTP_RRQ) : (opcode == TFTP_WRQ)) {
184 /* see if the filename fits into xbuf
185 * and fill in packet. */
186 len = strlen(remotefile) + 1;
188 if ((cp + len) >= &xbuf[tftp_bufsize - 1]) {
191 safe_strncpy(cp, remotefile, len);
195 if (too_long || (&xbuf[tftp_bufsize - 1] - cp) < sizeof("octet")) {
196 bb_error_msg("remote filename too long");
200 /* add "mode" part of the package */
201 memcpy(cp, "octet", sizeof("octet"));
202 cp += sizeof("octet");
204 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
206 len = tftp_bufsize - 4; /* data block size */
208 if (len != TFTP_BLOCKSIZE_DEFAULT) {
210 if ((&xbuf[tftp_bufsize - 1] - cp) < 15) {
211 bb_error_msg("remote filename too long");
215 /* add "blksize" + number of blocks */
216 memcpy(cp, "blksize", sizeof("blksize"));
217 cp += sizeof("blksize");
218 cp += snprintf(cp, 6, "%d", len) + 1;
225 /* add ack and data */
227 if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) {
228 /* TODO: unaligned access! */
229 *((uint16_t*)cp) = htons(block_nr);
233 if (CMD_PUT(cmd) && (opcode == TFTP_DATA)) {
234 len = full_read(localfd, cp, tftp_bufsize - 4);
237 bb_perror_msg(bb_msg_read_error);
241 if (len != (tftp_bufsize - 4)) {
251 timeout = TFTP_NUM_RETRIES; /* re-initialize */
254 #if ENABLE_DEBUG_TFTP
255 fprintf(stderr, "sending %u bytes\n", len);
256 for (cp = xbuf; cp < &xbuf[len]; cp++)
257 fprintf(stderr, "%02x ", (unsigned char) *cp);
258 fprintf(stderr, "\n");
260 if (sendto(socketfd, xbuf, len, 0,
261 &peer_lsa->sa, peer_lsa->len) < 0) {
262 bb_perror_msg("send");
267 if (finished && (opcode == TFTP_ACK)) {
273 tv.tv_sec = TFTP_TIMEOUT;
277 FD_SET(socketfd, &rfds);
279 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
282 from->len = peer_lsa->len;
283 memset(&from->sa, 0, peer_lsa->len);
284 len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
285 &from->sa, &from->len);
287 bb_perror_msg("recvfrom");
290 from_port = get_nport(&from->sa);
291 if (port == org_port) {
292 /* Our first query went to port 69
293 * but reply will come from different one.
294 * Remember and use this new port */
296 set_nport(peer_lsa, from_port);
298 if (port != from_port)
303 bb_error_msg("timeout");
307 bb_error_msg("last timeout");
311 bb_perror_msg("select");
315 } while (timeout && (len >= 0));
317 if (finished || (len < 0)) {
321 /* process received packet */
322 /* (both accesses seems to be aligned) */
324 opcode = ntohs( ((uint16_t*)rbuf)[0] );
325 tmp = ntohs( ((uint16_t*)rbuf)[1] );
327 #if ENABLE_DEBUG_TFTP
328 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
331 if (opcode == TFTP_ERROR) {
332 const char *msg = NULL;
334 if (rbuf[4] != '\0') {
336 rbuf[tftp_bufsize - 1] = '\0';
337 } else if (tmp < (sizeof(tftp_bb_error_msg)
339 msg = tftp_bb_error_msg[tmp];
343 bb_error_msg("server says: %s", msg);
348 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
349 if (want_option_ack) {
353 if (opcode == TFTP_OACK) {
354 /* server seems to support options */
357 res = tftp_option_get(&rbuf[2], len - 2, "blksize");
360 int blksize = xatoi_u(res);
362 if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
368 #if ENABLE_DEBUG_TFTP
369 fprintf(stderr, "using blksize %u\n",
372 tftp_bufsize = blksize + 4;
378 * we should send ERROR 8 */
379 bb_error_msg("bad server option");
383 bb_error_msg("warning: blksize not supported by server"
384 " - reverting to 512");
386 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
390 if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
391 if (tmp == block_nr) {
392 len = full_write(localfd, &rbuf[4], len - 4);
395 bb_perror_msg(bb_msg_write_error);
399 if (len != (tftp_bufsize - 4)) {
406 /* in case the last ack disappeared into the ether */
407 if (tmp == (block_nr - 1)) {
411 // tmp==(block_nr-1) and (tmp+1)==block_nr is always same, I think. wtf?
412 } else if (tmp + 1 == block_nr) {
413 /* Server lost our TFTP_ACK. Resend it */
420 if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
421 if (tmp == (uint16_t) (block_nr - 1)) {
432 if (ENABLE_FEATURE_CLEAN_UP) {
438 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
441 int tftp_main(int argc, char **argv);
442 int tftp_main(int argc, char **argv)
444 len_and_sockaddr *peer_lsa;
445 const char *localfile = NULL;
446 const char *remotefile = NULL;
447 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
448 const char *sblocksize = NULL;
455 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
457 /* -p or -g is mandatory, and they are mutually exclusive */
458 opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
459 USE_GETPUT("?g--p:p--g");
461 USE_GETPUT(cmd =) getopt32(argc, argv,
462 USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
463 "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
464 &localfile, &remotefile
465 USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
469 flags = O_WRONLY | O_CREAT | O_TRUNC;
471 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
473 blocksize = xatoi_u(sblocksize);
474 if (!tftp_blocksize_check(blocksize, 0)) {
480 if (localfile == NULL)
481 localfile = remotefile;
482 if (remotefile == NULL)
483 remotefile = localfile;
484 if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
487 if (localfile == NULL || LONE_DASH(localfile)) {
488 fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
490 fd = xopen3(localfile, flags, 0644);
493 port = bb_lookup_port(argv[optind + 1], "udp", 69);
494 peer_lsa = xhost2sockaddr(argv[optind], port);
496 #if ENABLE_DEBUG_TFTP
497 fprintf(stderr, "using server \"%s\", "
498 "remotefile \"%s\", localfile \"%s\".\n",
499 xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),
500 remotefile, localfile);
504 #if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
507 peer_lsa, remotefile, fd, port, blocksize);
510 if (ENABLE_FEATURE_CLEAN_UP)
512 if (CMD_GET(cmd) && result != EXIT_SUCCESS)
518 #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */