blob: f56d6562e9917c3d3f9f3a4ae3a66b3e2b43bfbf [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");
113 return 1;
114 }
115
116 len = sizeof(sa);
117
118 memset(&sa, 0, len);
119 bind(socketfd, &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.\n");
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.\n");
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!\n");
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.\n", msg);
337 }
338
339 break;
340 }
341 }
342
343 close(socketfd);
344
345 if (finished) {
346 return 0;
347 }
348
349 return 1;
350}
351
352int tftp_main(int argc, char **argv)
353{
354 char *cp, *s;
355 char *serverstr;
356 struct hostent *host;
357 char *serverfile;
358 char *localfile;
359 int cmd, flags, fd, bad;
360
361 host = (void *) serverstr = serverfile = localfile = NULL;
362 flags = cmd = 0;
363 bad = 1;
364
365 if (argc > 3) {
366 if (BB_TFTP_GET && (strcmp(argv[1], "get") == 0)) {
367 cmd = BB_TFTP_GET;
368 flags = O_WRONLY | O_CREAT;
369 serverstr = argv[2];
370 localfile = argv[3];
371 }
372
373 if (BB_TFTP_PUT && (strcmp(argv[1], "put") == 0)) {
374 cmd = BB_TFTP_PUT;
375 flags = O_RDONLY;
376 localfile = argv[2];
377 serverstr = argv[3];
378 }
379
380 }
381
382 if (!(cmd & (BB_TFTP_GET | BB_TFTP_PUT))) {
383 show_usage();
384 }
385
386 for (cp = serverstr; *cp != '\0'; cp++)
387 if (*cp == ':')
388 break;
389
390 if (*cp == ':') {
391
392 serverfile = cp + 1;
393
394 if ((s = strdup(serverstr))) {
395 s[cp - serverstr] = '\0';
396
397 if ((host = gethostbyname(s))) {
398 bad = 0;
399 }
400 }
401
402 free(s);
403 }
404 if (bad) {
405 perror_msg_and_die("bad \"server:file\" combination");
406 }
407
408 if (BB_TFTP_DEBUG) {
409 printf("using server \"%s\", serverfile \"%s\","
410 "localfile \"%s\".\n",
411 inet_ntoa(*((struct in_addr *) host->h_addr)),
412 serverfile, localfile);
413 }
414
415 if ((fd = open(localfile, flags, 0644)) < 0) {
416 perror_msg_and_die("local file");
417 }
418
419 flags = tftp(cmd, host, serverfile, fd, 69);
420
421 close(fd);
422
423 return flags;
424}