1/* vi: set sw=4 ts=4: */
2/* -------------------------------------------------------------------------
3 * tftp.c
4 *
5 * A simple tftp client for busybox.
6 * Tries to follow RFC1350.
7 * Only "octet" mode supported.
8 * Optional blocksize negotiation (RFC2347 + RFC2348)
9 *
10 * Copyright (C) 2001 Magnus Damm <damm@opensource.se>
11 *
12 * Parts of the code based on:
13 *
14 * atftp:  Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
15 *                        and Remi Lefebvre <remi@debian.org>
16 *
17 * utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
18 *
19 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
20 * ------------------------------------------------------------------------- */
21
22#include "libbb.h"
23
24
25#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
26
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 */
30
31/* opcodes we support */
32#define TFTP_RRQ   1
33#define TFTP_WRQ   2
34#define TFTP_DATA  3
35#define TFTP_ACK   4
36#define TFTP_ERROR 5
37#define TFTP_OACK  6
38
39#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
40#define USE_GETPUT(...)
41#define CMD_GET(cmd) 1
42#define CMD_PUT(cmd) 0
43#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
44#define USE_GETPUT(...)
45#define CMD_GET(cmd) 0
46#define CMD_PUT(cmd) 1
47#else
48#define USE_GETPUT(...) __VA_ARGS__
49/* masks coming from getpot32 */
50#define CMD_GET(cmd) ((cmd) & 1)
51#define CMD_PUT(cmd) ((cmd) & 2)
52#endif
53/* NB: in the code below
54 * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
55 */
56
57
58#if ENABLE_FEATURE_TFTP_BLOCKSIZE
59
60static int tftp_blocksize_check(int blocksize, int bufsize)
61{
62	/* Check if the blocksize is valid:
63	 * RFC2348 says between 8 and 65464,
64	 * but our implementation makes it impossible
65	 * to use blocksizes smaller than 22 octets.
66	 */
67
68	if ((bufsize && (blocksize > bufsize))
69	 || (blocksize < 8) || (blocksize > 65564)
70	) {
71		bb_error_msg("bad blocksize");
72		return 0;
73	}
74
75	return blocksize;
76}
77
78static char *tftp_option_get(char *buf, int len, const char *option)
79{
80	int opt_val = 0;
81	int opt_found = 0;
82	int k;
83
84	while (len > 0) {
85		/* Make sure the options are terminated correctly */
86		for (k = 0; k < len; k++) {
87			if (buf[k] == '\0') {
88				goto nul_found;
89			}
90		}
91		return NULL;
92 nul_found:
93		if (opt_val == 0) {
94			if (strcasecmp(buf, option) == 0) {
95				opt_found = 1;
96			}
97		} else if (opt_found) {
98			return buf;
99		}
100
101		k++;
102		buf += k;
103		len -= k;
104		opt_val ^= 1;
105	}
106
107	return NULL;
108}
109
110#endif
111
112static int tftp( USE_GETPUT(const int cmd,)
113		len_and_sockaddr *peer_lsa,
114		const char *remotefile, const int localfd,
115		unsigned port, int tftp_bufsize)
116{
117	struct timeval tv;
118	fd_set rfds;
119	int socketfd;
120	int len;
121	int send_len;
122	USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
123	smallint finished = 0;
124	uint16_t opcode;
125	uint16_t block_nr = 1;
126	uint16_t recv_blk;
127	int timeout = TFTP_NUM_RETRIES;
128	char *cp;
129
130	unsigned org_port;
131	len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
132
133	/* Can't use RESERVE_CONFIG_BUFFER here since the allocation
134	 * size varies meaning BUFFERS_GO_ON_STACK would fail */
135	/* We must keep the transmit and receive buffers seperate */
136	/* In case we rcv a garbage pkt and we need to rexmit the last pkt */
137	char *xbuf = xmalloc(tftp_bufsize += 4);
138	char *rbuf = xmalloc(tftp_bufsize);
139
140	port = org_port = htons(port);
141
142	socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
143
144	/* build opcode */
145	opcode = TFTP_WRQ;
146	if (CMD_GET(cmd)) {
147		opcode = TFTP_RRQ;
148	}
149	cp = xbuf + 2;
150	/* add filename and mode */
151	/* fill in packet if the filename fits into xbuf */
152	len = strlen(remotefile) + 1;
153	if (2 + len + sizeof("octet") >= tftp_bufsize) {
154		bb_error_msg("remote filename is too long");
155		goto ret;
156	}
157	strcpy(cp, remotefile);
158	cp += len;
159	/* add "mode" part of the package */
160	strcpy(cp, "octet");
161	cp += sizeof("octet");
162
163#if ENABLE_FEATURE_TFTP_BLOCKSIZE
164	len = tftp_bufsize - 4;	/* data block size */
165	if (len != TFTP_BLOCKSIZE_DEFAULT) {
166		/* rfc2348 says that 65464 is a max allowed value */
167		if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
168			bb_error_msg("remote filename is too long");
169			goto ret;
170		}
171		/* add "blksize", <nul>, blocksize */
172		strcpy(cp, "blksize");
173		cp += sizeof("blksize");
174		cp += snprintf(cp, 6, "%d", len) + 1;
175		want_option_ack = 1;
176	}
177#endif
178	/* First packet is built, so skip packet generation */
179	goto send_pkt;
180
181	/* Using mostly goto's - continue/break will be less clear
182	 * in where we actually jump to */
183
184	while (1) {
185		/* Build ACK or DATA */
186		cp = xbuf + 2;
187		*((uint16_t*)cp) = htons(block_nr);
188		cp += 2;
189		block_nr++;
190		opcode = TFTP_ACK;
191		if (CMD_PUT(cmd)) {
192			opcode = TFTP_DATA;
193			len = full_read(localfd, cp, tftp_bufsize - 4);
194			if (len < 0) {
195				bb_perror_msg(bb_msg_read_error);
196				goto ret;
197			}
198			if (len != (tftp_bufsize - 4)) {
199				finished = 1;
200			}
201			cp += len;
202		}
203 send_pkt:
204		/* Send packet */
205		*((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
206		send_len = cp - xbuf;
207		/* NB: send_len value is preserved in code below
208		 * for potential resend */
209 send_again:
210#if ENABLE_DEBUG_TFTP
211		fprintf(stderr, "sending %u bytes\n", send_len);
212		for (cp = xbuf; cp < &xbuf[send_len]; cp++)
213			fprintf(stderr, "%02x ", (unsigned char) *cp);
214		fprintf(stderr, "\n");
215#endif
216		xsendto(socketfd, xbuf, send_len, &peer_lsa->sa, peer_lsa->len);
217		/* Was it final ACK? then exit */
218		if (finished && (opcode == TFTP_ACK))
219			goto ret;
220
221		timeout = TFTP_NUM_RETRIES;	/* re-initialize */
222 recv_again:
223		/* Receive packet */
224		tv.tv_sec = TFTP_TIMEOUT;
225		tv.tv_usec = 0;
226		FD_ZERO(&rfds);
227		FD_SET(socketfd, &rfds);
228		switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
229			unsigned from_port;
230		case 1:
231			from->len = peer_lsa->len;
232			memset(&from->sa, 0, peer_lsa->len);
233			len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
234						&from->sa, &from->len);
235			if (len < 0) {
236				bb_perror_msg("recvfrom");
237				goto ret;
238			}
239			from_port = get_nport(&from->sa);
240			if (port == org_port) {
241				/* Our first query went to port 69
242				 * but reply will come from different one.
243				 * Remember and use this new port */
244				port = from_port;
245				set_nport(peer_lsa, from_port);
246			}
247			if (port != from_port)
248				goto recv_again;
249			goto process_pkt;
250		case 0:
251			timeout--;
252			if (timeout == 0) {
253				bb_error_msg("last timeout");
254				goto ret;
255			}
256			bb_error_msg("last timeout" + 5);
257			goto send_again; /* resend last sent pkt */
258		default:
259			bb_perror_msg("select");
260			goto ret;
261		}
262 process_pkt:
263		/* Process recv'ed packet */
264		opcode = ntohs( ((uint16_t*)rbuf)[0] );
265		recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
266
267#if ENABLE_DEBUG_TFTP
268		fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
269#endif
270
271		if (opcode == TFTP_ERROR) {
272			static const char *const errcode_str[] = {
273				"",
274				"file not found",
275				"access violation",
276				"disk full",
277				"illegal TFTP operation",
278				"unknown transfer id",
279				"file already exists",
280				"no such user",
281				"bad option"
282			};
283
284			const char *msg = "";
285
286			if (rbuf[4] != '\0') {
287				msg = &rbuf[4];
288				rbuf[tftp_bufsize - 1] = '\0';
289			} else if (recv_blk < ARRAY_SIZE(errcode_str)) {
290				msg = errcode_str[recv_blk];
291			}
292			bb_error_msg("server error: (%u) %s", recv_blk, msg);
293			goto ret;
294		}
295
296#if ENABLE_FEATURE_TFTP_BLOCKSIZE
297		if (want_option_ack) {
298			want_option_ack = 0;
299
300			if (opcode == TFTP_OACK) {
301				/* server seems to support options */
302				char *res;
303
304				res = tftp_option_get(&rbuf[2], len - 2, "blksize");
305				if (res) {
306					int blksize = xatoi_u(res);
307					if (!tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
308						/* send ERROR 8 to server... */
309						/* htons can be impossible to use in const initializer: */
310						/*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/
311						/* thus we open-code big-endian layout */
312						static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 };
313						xsendto(socketfd, error_8, 4, &peer_lsa->sa, peer_lsa->len);
314						bb_error_msg("server proposes bad blksize %d, exiting", blksize);
315						goto ret;
316					}
317#if ENABLE_DEBUG_TFTP
318					fprintf(stderr, "using blksize %u\n",
319							blksize);
320#endif
321					tftp_bufsize = blksize + 4;
322					/* Send ACK for OACK ("block" no: 0) */
323					block_nr = 0;
324					continue;
325				}
326				/* rfc2347:
327				 * "An option not acknowledged by the server
328				 *  must be ignored by the client and server
329				 *  as if it were never requested." */
330			}
331
332			bb_error_msg("blksize is not supported by server"
333						" - reverting to 512");
334			tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
335		}
336#endif
337		/* block_nr is already advanced to next block# we expect
338		 * to get / block# we are about to send next time */
339
340		if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
341			if (recv_blk == block_nr) {
342				len = full_write(localfd, &rbuf[4], len - 4);
343				if (len < 0) {
344					bb_perror_msg(bb_msg_write_error);
345					goto ret;
346				}
347				if (len != (tftp_bufsize - 4)) {
348					finished = 1;
349				}
350				continue; /* send ACK */
351			}
352			if (recv_blk == (block_nr - 1)) {
353				/* Server lost our TFTP_ACK.  Resend it */
354				block_nr = recv_blk;
355				continue;
356			}
357		}
358
359		if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
360			/* did server ACK our last DATA pkt? */
361			if (recv_blk == (uint16_t) (block_nr - 1)) {
362				if (finished)
363					goto ret;
364				continue; /* send next block */
365			}
366		}
367		/* Awww... recv'd packet is not recognized! */
368		goto recv_again;
369		/* why recv_again? - rfc1123 says:
370		 * "The sender (i.e., the side originating the DATA packets)
371		 *  must never resend the current DATA packet on receipt
372		 *  of a duplicate ACK".
373		 * DATA pkts are resent ONLY on timeout.
374		 * Thus "goto send_again" will ba a bad mistake above.
375		 * See:
376		 * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
377		 */
378	}
379 ret:
380	if (ENABLE_FEATURE_CLEAN_UP) {
381		close(socketfd);
382		free(xbuf);
383		free(rbuf);
384	}
385	return finished == 0; /* returns 1 on failure */
386}
387
388int tftp_main(int argc, char **argv);
389int tftp_main(int argc, char **argv)
390{
391	len_and_sockaddr *peer_lsa;
392	const char *localfile = NULL;
393	const char *remotefile = NULL;
394#if ENABLE_FEATURE_TFTP_BLOCKSIZE
395	const char *sblocksize = NULL;
396#endif
397	int port;
398	USE_GETPUT(int cmd;)
399	int fd = -1;
400	int flags = 0;
401	int result;
402	int blocksize = TFTP_BLOCKSIZE_DEFAULT;
403
404	/* -p or -g is mandatory, and they are mutually exclusive */
405	opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
406			USE_GETPUT("?g--p:p--g");
407
408	USE_GETPUT(cmd =) getopt32(argv,
409			USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
410				"l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
411			&localfile, &remotefile
412			USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
413	argv += optind;
414
415	flags = O_RDONLY;
416	if (CMD_GET(cmd))
417		flags = O_WRONLY | O_CREAT | O_TRUNC;
418
419#if ENABLE_FEATURE_TFTP_BLOCKSIZE
420	if (sblocksize) {
421		blocksize = xatoi_u(sblocksize);
422		if (!tftp_blocksize_check(blocksize, 0)) {
423			return EXIT_FAILURE;
424		}
425	}
426#endif
427
428	if (!localfile)
429		localfile = remotefile;
430	if (!remotefile)
431		remotefile = localfile;
432	/* Error if filename or host is not known */
433	if (!remotefile || !argv[0])
434		bb_show_usage();
435
436	fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
437	if (!LONE_DASH(localfile)) {
438		fd = xopen(localfile, flags);
439	}
440
441	port = bb_lookup_port(argv[1], "udp", 69);
442	peer_lsa = xhost2sockaddr(argv[0], port);
443
444#if ENABLE_DEBUG_TFTP
445	fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n",
446			xmalloc_sockaddr2dotted(&peer_lsa->sa),
447			remotefile, localfile);
448#endif
449
450	result = tftp( USE_GETPUT(cmd,) peer_lsa, remotefile, fd, port, blocksize);
451
452	if (ENABLE_FEATURE_CLEAN_UP)
453		close(fd);
454	if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && CMD_GET(cmd)) {
455		unlink(localfile);
456	}
457	return result;
458}
459
460#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
461