diff options
author | Mark Whitley <markw@lineo.com> | 2001-03-02 19:08:50 +0000 |
---|---|---|
committer | Mark Whitley <markw@lineo.com> | 2001-03-02 19:08:50 +0000 |
commit | 450736cd3cf5e09a02620af7a93b41e0c1c67344 (patch) | |
tree | 9312ddb48e6404914c33a4bf9184d258f428eb22 /networking/tftp.c | |
parent | 016771834af01be118621f89d92a3ecb29516a4f (diff) | |
download | busybox-w32-450736cd3cf5e09a02620af7a93b41e0c1c67344.tar.gz busybox-w32-450736cd3cf5e09a02620af7a93b41e0c1c67344.tar.bz2 busybox-w32-450736cd3cf5e09a02620af7a93b41e0c1c67344.zip |
Added Magnus Damm's tftp applet to Busybox.
Diffstat (limited to 'networking/tftp.c')
-rw-r--r-- | networking/tftp.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/networking/tftp.c b/networking/tftp.c new file mode 100644 index 000000000..f56d6562e --- /dev/null +++ b/networking/tftp.c | |||
@@ -0,0 +1,424 @@ | |||
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 | |||
81 | static 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 | |||
92 | static 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 | |||
352 | int 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 | } | ||