1/* ------------------------------------------------------------------------- */
2/* tftp.c                                                                    */
3/*                                                                           */
4/* A simple tftp client for busybox.                                         */
5/* Tries to follow RFC1350.                                                  */
6/* Only "octet" mode and 512-byte data blocks are supported.                 */
7/*                                                                           */
8/* Copyright (C) 2001 Magnus Damm <damm@opensource.se>                       */
9/*                                                                           */
10/* Parts of the code based on:                                               */
11/*                                                                           */
12/* atftp:  Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>   */
13/*                        and Remi Lefebvre <remi@debian.org>                */
14/*                                                                           */
15/* utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>                         */
16/*                                                                           */
17/* This program is free software; you can redistribute it and/or modify      */
18/* it under the terms of the GNU General Public License as published by      */
19/* the Free Software Foundation; either version 2 of the License, or         */
20/* (at your option) any later version.                                       */
21/*                                                                           */
22/* This program is distributed in the hope that it will be useful,           */
23/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
24/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          */
25/* General Public License for more details.                                  */
26/*                                                                           */
27/* You should have received a copy of the GNU General Public License         */
28/* along with this program; if not, write to the Free Software               */
29/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA   */
30/*                                                                           */
31/* ------------------------------------------------------------------------- */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <sys/types.h>
37#include <sys/socket.h>
38#include <sys/time.h>
39#include <sys/stat.h>
40#include <netdb.h>
41#include <netinet/in.h>
42#include <arpa/inet.h>
43#include <unistd.h>
44#include <fcntl.h>
45
46#include "busybox.h"
47
48//#define BB_FEATURE_TFTP_DEBUG
49
50/* we don't need #ifdefs with these constants and optimization... */
51
52#ifdef BB_FEATURE_TFTP_GET
53#define BB_TFTP_GET (1 << 0)
54#else
55#define BB_TFTP_GET 0
56#endif
57
58#ifdef BB_FEATURE_TFTP_PUT
59#define BB_TFTP_PUT (1 << 1)
60#else
61#define BB_TFTP_PUT 0
62#endif
63
64#ifdef BB_FEATURE_TFTP_DEBUG
65#define BB_TFTP_DEBUG 1
66#else
67#define BB_TFTP_DEBUG 0
68#endif
69
70#define BB_TFTP_NO_RETRIES 5
71#define BB_TFTP_TIMEOUT    5	/* seconds */
72
73#define	RRQ	1			/* read request */
74#define	WRQ	2			/* write request */
75#define	DATA	3		/* data packet */
76#define	ACK	4			/* acknowledgement */
77#define	ERROR	5		/* error code */
78
79#define BUFSIZE (512+4)
80
81static const char *tftp_error_msg[] = {
82	"Undefined error",
83	"File not found",
84	"Access violation",
85	"Disk full or allocation error",
86	"Illegal TFTP operation",
87	"Unknown transfer ID",
88	"File already exists",
89	"No such user"
90};
91
92static inline int tftp(int cmd, struct hostent *host,
93					   char *serverfile, int localfd, int port)
94{
95	struct sockaddr_in sa;
96	int socketfd;
97	struct timeval tv;
98	fd_set rfds;
99	struct sockaddr_in from;
100	socklen_t fromlen;
101	char *cp;
102	unsigned short tmp;
103	int len, opcode, finished;
104	int timeout, block_nr;
105
106	RESERVE_BB_BUFFER(buf, BUFSIZE);
107
108	opcode = finished = timeout = 0;
109	block_nr = 1;
110
111	if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
112		perror_msg("socket");
113		return EXIT_FAILURE;
114	}
115
116	len = sizeof(sa);
117
118	memset(&sa, 0, len);
119	bind(socketfd, (struct sockaddr *)&sa, len);
120
121	sa.sin_family = host->h_addrtype;
122	sa.sin_port = htons(port);
123	memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
124		   sizeof(sa.sin_addr));
125
126	/* build opcode */
127
128	if (cmd & BB_TFTP_GET) {
129		opcode = RRQ;
130	}
131
132	if (cmd & BB_TFTP_PUT) {
133		opcode = WRQ;
134	}
135
136	while (1) {
137
138
139		/* build packet of type "opcode" */
140
141
142		cp = buf;
143
144		*((unsigned short *) cp) = htons(opcode);
145
146		cp += 2;
147
148		/* add filename and mode */
149
150		if ((BB_TFTP_GET && (opcode == RRQ)) ||
151			(BB_TFTP_PUT && (opcode == WRQ))) {
152
153			while (cp != &buf[BUFSIZE - 1]) {
154				if ((*cp = *serverfile++) == '\0')
155					break;
156				cp++;
157			}
158
159			if ((*cp != '\0') || (&buf[BUFSIZE - 1] - cp) < 7) {
160				error_msg("too long server-filename");
161				break;
162			}
163
164			memcpy(cp + 1, "octet", 6);
165			cp += 7;
166		}
167
168		/* add ack and data */
169
170		if ((BB_TFTP_GET && (opcode == ACK)) ||
171			(BB_TFTP_PUT && (opcode == DATA))) {
172
173			*((unsigned short *) cp) = htons(block_nr);
174
175			cp += 2;
176
177			block_nr++;
178
179			if (BB_TFTP_PUT && (opcode == DATA)) {
180				len = read(localfd, cp, BUFSIZE - 4);
181
182				if (len < 0) {
183					perror_msg("read");
184					break;
185				}
186
187				if (len != (BUFSIZE - 4)) {
188					finished++;
189				}
190
191				cp += len;
192			} else if (finished) {
193				break;
194			}
195		}
196
197
198		/* send packet */
199
200
201		do {
202
203			len = cp - buf;
204
205			if (BB_TFTP_DEBUG) {
206				printf("sending %u bytes\n", len);
207
208				for (cp = buf; cp < &buf[len]; cp++)
209					printf("%02x ", *cp);
210				printf("\n");
211			}
212
213			if (sendto(socketfd, buf, len, 0,
214					   (struct sockaddr *) &sa, sizeof(sa)) < 0) {
215				perror_msg("send");
216				len = -1;
217				break;
218			}
219
220
221			/* receive packet */
222
223
224			memset(&from, 0, sizeof(from));
225			fromlen = sizeof(from);
226
227			tv.tv_sec = BB_TFTP_TIMEOUT;
228			tv.tv_usec = 0;
229
230			FD_ZERO(&rfds);
231			FD_SET(socketfd, &rfds);
232
233			switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
234			case 1:
235				len = recvfrom(socketfd, buf,
236							   BUFSIZE, 0,
237							   (struct sockaddr *) &from, &fromlen);
238
239				if (len < 0) {
240					perror_msg("recvfrom");
241					break;
242				}
243
244				timeout = 0;
245
246				if (sa.sin_port == htons(port)) {
247					sa.sin_port = from.sin_port;
248					break;
249				}
250
251				if (sa.sin_port == from.sin_port) {
252					break;
253				}
254
255				/* fall-through for bad packets! */
256				/* discard the packet - treat as timeout */
257
258			case 0:
259				error_msg("timeout");
260
261				if (!timeout) {
262					timeout = BB_TFTP_NO_RETRIES;
263				} else {
264					timeout--;
265				}
266
267				if (!timeout) {
268					len = -1;
269					error_msg("last timeout");
270				}
271				break;
272
273			default:
274				perror_msg("select");
275				len = -1;
276			}
277
278		} while (timeout && (len >= 0));
279
280		if (len < 0) {
281			break;
282		}
283
284		/* process received packet */
285
286
287		opcode = ntohs(*((unsigned short *) buf));
288		tmp = ntohs(*((unsigned short *) &buf[2]));
289
290		if (BB_TFTP_DEBUG) {
291			printf("received %d bytes: %04x %04x\n", len, opcode, tmp);
292		}
293
294		if (BB_TFTP_GET && (opcode == DATA)) {
295
296			if (tmp == block_nr) {
297				len = write(localfd, &buf[4], len - 4);
298
299				if (len < 0) {
300					perror_msg("write");
301					break;
302				}
303
304				if (len != (BUFSIZE - 4)) {
305					finished++;
306				}
307
308				opcode = ACK;
309				continue;
310			}
311		}
312
313		if (BB_TFTP_PUT && (opcode == ACK)) {
314
315			if (tmp == (block_nr - 1)) {
316				if (finished) {
317					break;
318				}
319
320				opcode = DATA;
321				continue;
322			}
323		}
324
325		if (opcode == ERROR) {
326			char *msg = NULL;
327
328			if (buf[4] != '\0') {
329				msg = &buf[4];
330				buf[BUFSIZE - 1] = '\0';
331			} else if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) {
332				msg = (char *) tftp_error_msg[tmp];
333			}
334
335			if (msg) {
336				error_msg("server says: %s", msg);
337			}
338
339			break;
340		}
341	}
342
343	close(socketfd);
344
345	return finished ? EXIT_SUCCESS : EXIT_FAILURE;
346}
347
348int tftp_main(int argc, char **argv)
349{
350	char *cp, *s;
351	char *serverstr;
352	struct hostent *host;
353	char *serverfile;
354	char *localfile;
355	int cmd, flags, fd, bad;
356
357	host = (void *) serverstr = serverfile = localfile = NULL;
358	flags = cmd = 0;
359	bad = 1;
360
361	if (argc > 3) {
362		if (BB_TFTP_GET && (strcmp(argv[1], "get") == 0)) {
363			cmd = BB_TFTP_GET;
364			flags = O_WRONLY | O_CREAT;
365			serverstr = argv[2];
366			localfile = argv[3];
367		}
368
369		if (BB_TFTP_PUT && (strcmp(argv[1], "put") == 0)) {
370			cmd = BB_TFTP_PUT;
371			flags = O_RDONLY;
372			localfile = argv[2];
373			serverstr = argv[3];
374		}
375
376	}
377
378	if (!(cmd & (BB_TFTP_GET | BB_TFTP_PUT))) {
379		show_usage();
380	}
381
382	for (cp = serverstr; *cp != '\0'; cp++)
383		if (*cp == ':')
384			break;
385
386	if (*cp == ':') {
387
388		serverfile = cp + 1;
389
390		s = xstrdup(serverstr);
391		s[cp - serverstr] = '\0';
392
393		host = xgethostbyname(s);
394
395		free(s);
396	}
397
398	if (BB_TFTP_DEBUG) {
399		printf("using server \"%s\", serverfile \"%s\","
400			   "localfile \"%s\".\n",
401			   inet_ntoa(*((struct in_addr *) host->h_addr)),
402			   serverfile, localfile);
403	}
404
405	if ((fd = open(localfile, flags, 0644)) < 0) {
406		perror_msg_and_die("local file");
407	}
408
409	flags = tftp(cmd, host, serverfile, fd, 69);
410
411	close(fd);
412
413	return flags;
414}
415