blob: bb75c88ecf9e6d2af972aa75e5e1d217a78f8fbc [file] [log] [blame]
Mark Whitley450736c2001-03-02 19:08:50 +00001/* ------------------------------------------------------------------------- */
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");
Mark Whitley8bb7df42001-03-06 20:58:48 +0000113 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000114 }
115
116 len = sizeof(sa);
117
118 memset(&sa, 0, len);
Eric Andersene76c3b02001-04-05 03:14:39 +0000119 bind(socketfd, (struct sockaddr *)&sa, len);
Mark Whitley450736c2001-03-02 19:08:50 +0000120
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) {
Mark Whitley8bb7df42001-03-06 20:58:48 +0000160 error_msg("too long server-filename");
Mark Whitley450736c2001-03-02 19:08:50 +0000161 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) {
Mark Whitley8bb7df42001-03-06 20:58:48 +0000215 perror_msg("send");
Mark Whitley450736c2001-03-02 19:08:50 +0000216 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:
Mark Whitley8bb7df42001-03-06 20:58:48 +0000259 error_msg("timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000260
261 if (!timeout) {
262 timeout = BB_TFTP_NO_RETRIES;
263 } else {
264 timeout--;
265 }
266
267 if (!timeout) {
268 len = -1;
Mark Whitley8bb7df42001-03-06 20:58:48 +0000269 error_msg("last timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000270 }
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) {
Mark Whitley8bb7df42001-03-06 20:58:48 +0000336 error_msg("server says: %s", msg);
Mark Whitley450736c2001-03-02 19:08:50 +0000337 }
338
339 break;
340 }
341 }
342
343 close(socketfd);
344
Mark Whitley8bb7df42001-03-06 20:58:48 +0000345 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000346}
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
Eric Andersen6b2c23d2001-03-23 17:10:19 +0000390 s = xstrdup(serverstr);
391 s[cp - serverstr] = '\0';
Mark Whitley450736c2001-03-02 19:08:50 +0000392
Matt Kraaic55b8d42001-05-16 15:40:51 +0000393 host = xgethostbyname(s);
Mark Whitley450736c2001-03-02 19:08:50 +0000394
395 free(s);
396 }
Mark Whitley450736c2001-03-02 19:08:50 +0000397
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}