blob: b830f18a58d5351ddfe5ec6b19100804f4d427e5 [file] [log] [blame]
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +00001/* 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 * ------------------------------------------------------------------------- */
Mark Whitley450736c2001-03-02 19:08:50 +000021
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/time.h>
28#include <sys/stat.h>
29#include <netdb.h>
30#include <netinet/in.h>
31#include <arpa/inet.h>
32#include <unistd.h>
33#include <fcntl.h>
34
35#include "busybox.h"
36
Eric Andersenbdfd0d72001-10-24 05:00:29 +000037//#define CONFIG_FEATURE_TFTP_DEBUG
Mark Whitley450736c2001-03-02 19:08:50 +000038
Glenn L McGrathad117d82001-10-05 04:40:37 +000039#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
40#define TFTP_TIMEOUT 5 /* seconds */
41
42/* opcodes we support */
43
44#define TFTP_RRQ 1
45#define TFTP_WRQ 2
46#define TFTP_DATA 3
47#define TFTP_ACK 4
48#define TFTP_ERROR 5
49#define TFTP_OACK 6
50
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +000051static const char * const tftp_bb_error_msg[] = {
Mark Whitley450736c2001-03-02 19:08:50 +000052 "Undefined error",
53 "File not found",
54 "Access violation",
55 "Disk full or allocation error",
56 "Illegal TFTP operation",
57 "Unknown transfer ID",
58 "File already exists",
59 "No such user"
60};
61
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +000062#ifdef CONFIG_FEATURE_TFTP_GET
63# define tftp_cmd_get 1
64#else
65# define tftp_cmd_get 0
66#endif
67#ifdef CONFIG_FEATURE_TFTP_PUT
68# define tftp_cmd_put (tftp_cmd_get+1)
69#else
70# define tftp_cmd_put 0
71#endif
72
Eric Andersen76fa8ea2001-08-20 17:47:49 +000073
Eric Andersenbdfd0d72001-10-24 05:00:29 +000074#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +000075
Eric Andersenc7bda1c2004-03-15 08:29:22 +000076static int tftp_blocksize_check(int blocksize, int bufsize)
Glenn L McGrathad117d82001-10-05 04:40:37 +000077{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000078 /* Check if the blocksize is valid:
Glenn L McGrathad117d82001-10-05 04:40:37 +000079 * RFC2348 says between 8 and 65464,
80 * but our implementation makes it impossible
81 * to use blocksizes smaller than 22 octets.
82 */
83
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000084 if ((bufsize && (blocksize > bufsize)) ||
Glenn L McGrathad117d82001-10-05 04:40:37 +000085 (blocksize < 8) || (blocksize > 65464)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000086 bb_error_msg("bad blocksize");
Glenn L McGrathad117d82001-10-05 04:40:37 +000087 return 0;
88 }
89
90 return blocksize;
91}
92
Eric Andersenc7bda1c2004-03-15 08:29:22 +000093static char *tftp_option_get(char *buf, int len, char *option)
Glenn L McGrathad117d82001-10-05 04:40:37 +000094{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000095 int opt_val = 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +000096 int opt_found = 0;
97 int k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000098
Glenn L McGrathad117d82001-10-05 04:40:37 +000099 while (len > 0) {
100
101 /* Make sure the options are terminated correctly */
102
103 for (k = 0; k < len; k++) {
104 if (buf[k] == '\0') {
105 break;
106 }
107 }
108
109 if (k >= len) {
110 break;
111 }
112
113 if (opt_val == 0) {
114 if (strcasecmp(buf, option) == 0) {
115 opt_found = 1;
116 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000117 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000118 else {
119 if (opt_found) {
120 return buf;
121 }
122 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000123
Glenn L McGrathad117d82001-10-05 04:40:37 +0000124 k++;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000125
Glenn L McGrathad117d82001-10-05 04:40:37 +0000126 buf += k;
127 len -= k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000128
Glenn L McGrathad117d82001-10-05 04:40:37 +0000129 opt_val ^= 1;
130 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000131
Glenn L McGrathad117d82001-10-05 04:40:37 +0000132 return NULL;
133}
134
135#endif
136
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000137static inline int tftp(const int cmd, const struct hostent *host,
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000138 const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
Mark Whitley450736c2001-03-02 19:08:50 +0000139{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000140 const int cmd_get = cmd & tftp_cmd_get;
141 const int cmd_put = cmd & tftp_cmd_put;
142 const int bb_tftp_num_retries = 5;
143
Mark Whitley450736c2001-03-02 19:08:50 +0000144 struct sockaddr_in sa;
Mark Whitley450736c2001-03-02 19:08:50 +0000145 struct sockaddr_in from;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000146 struct timeval tv;
Mark Whitley450736c2001-03-02 19:08:50 +0000147 socklen_t fromlen;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000148 fd_set rfds;
Mark Whitley450736c2001-03-02 19:08:50 +0000149 char *cp;
150 unsigned short tmp;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000151 int socketfd;
152 int len;
153 int opcode = 0;
154 int finished = 0;
155 int timeout = bb_tftp_num_retries;
Glenn L McGrathc6997782004-02-22 03:33:53 +0000156 unsigned short block_nr = 1;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000157
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000158#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000159 int want_option_ack = 0;
160#endif
161
Eric Andersen744a1942001-11-10 11:16:39 +0000162 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
163 * size varies meaning BUFFERS_GO_ON_STACK would fail */
164 char *buf=xmalloc(tftp_bufsize + 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000165
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000166 tftp_bufsize += 4;
Mark Whitley450736c2001-03-02 19:08:50 +0000167
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +0000168 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { /* bb_xsocket? */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000169 bb_perror_msg("socket");
Mark Whitley8bb7df42001-03-06 20:58:48 +0000170 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000171 }
172
173 len = sizeof(sa);
174
175 memset(&sa, 0, len);
Eric Andersene76c3b02001-04-05 03:14:39 +0000176 bind(socketfd, (struct sockaddr *)&sa, len);
Mark Whitley450736c2001-03-02 19:08:50 +0000177
178 sa.sin_family = host->h_addrtype;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000179 sa.sin_port = port;
Mark Whitley450736c2001-03-02 19:08:50 +0000180 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
181 sizeof(sa.sin_addr));
182
183 /* build opcode */
184
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000185 if (cmd_get) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000186 opcode = TFTP_RRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000187 }
188
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000189 if (cmd_put) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000190 opcode = TFTP_WRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000191 }
192
193 while (1) {
194
Mark Whitley450736c2001-03-02 19:08:50 +0000195 cp = buf;
196
Glenn L McGrathad117d82001-10-05 04:40:37 +0000197 /* first create the opcode part */
198
Mark Whitley450736c2001-03-02 19:08:50 +0000199 *((unsigned short *) cp) = htons(opcode);
200
201 cp += 2;
202
203 /* add filename and mode */
204
Glenn L McGrathad117d82001-10-05 04:40:37 +0000205 if ((cmd_get && (opcode == TFTP_RRQ)) ||
206 (cmd_put && (opcode == TFTP_WRQ))) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000207 int too_long = 0;
Mark Whitley450736c2001-03-02 19:08:50 +0000208
Glenn L McGrathad117d82001-10-05 04:40:37 +0000209 /* see if the filename fits into buf */
210 /* and fill in packet */
211
212 len = strlen(remotefile) + 1;
213
214 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
215 too_long = 1;
Mark Whitley450736c2001-03-02 19:08:50 +0000216 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000217 else {
218 safe_strncpy(cp, remotefile, len);
219 cp += len;
220 }
221
222 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000223 bb_error_msg("too long remote-filename");
Mark Whitley450736c2001-03-02 19:08:50 +0000224 break;
225 }
226
Glenn L McGrathad117d82001-10-05 04:40:37 +0000227 /* add "mode" part of the package */
228
229 memcpy(cp, "octet", 6);
230 cp += 6;
231
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000232#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000233
234 len = tftp_bufsize - 4; /* data block size */
235
236 if (len != TFTP_BLOCKSIZE_DEFAULT) {
237
238 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000239 bb_error_msg("too long remote-filename");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000240 break;
241 }
242
243 /* add "blksize" + number of blocks */
244
245 memcpy(cp, "blksize", 8);
246 cp += 8;
247
248 cp += snprintf(cp, 6, "%d", len) + 1;
249
250 want_option_ack = 1;
251 }
252#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000253 }
254
255 /* add ack and data */
256
Glenn L McGrathad117d82001-10-05 04:40:37 +0000257 if ((cmd_get && (opcode == TFTP_ACK)) ||
258 (cmd_put && (opcode == TFTP_DATA))) {
Mark Whitley450736c2001-03-02 19:08:50 +0000259
260 *((unsigned short *) cp) = htons(block_nr);
261
262 cp += 2;
263
264 block_nr++;
265
Glenn L McGrathad117d82001-10-05 04:40:37 +0000266 if (cmd_put && (opcode == TFTP_DATA)) {
Eric Andersen4872ed92004-06-22 10:18:30 +0000267 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000268
269 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000270 bb_perror_msg("read");
Mark Whitley450736c2001-03-02 19:08:50 +0000271 break;
272 }
273
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000274 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000275 finished++;
276 }
277
278 cp += len;
Mark Whitley450736c2001-03-02 19:08:50 +0000279 }
280 }
281
282
283 /* send packet */
284
285
Eric Andersen05e662a2003-07-26 08:16:10 +0000286 timeout = bb_tftp_num_retries; /* re-initialize */
Mark Whitley450736c2001-03-02 19:08:50 +0000287 do {
288
289 len = cp - buf;
290
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000291#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000292 fprintf(stderr, "sending %u bytes\n", len);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000293 for (cp = buf; cp < &buf[len]; cp++)
Glenn L McGrathfbe984e2004-03-05 13:04:39 +0000294 fprintf(stderr, "%02x ", (unsigned char)*cp);
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000295 fprintf(stderr, "\n");
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000296#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000297 if (sendto(socketfd, buf, len, 0,
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000298 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000299 bb_perror_msg("send");
Mark Whitley450736c2001-03-02 19:08:50 +0000300 len = -1;
301 break;
302 }
303
304
Eric Andersenb99aec02003-07-30 07:16:39 +0000305 if (finished && (opcode == TFTP_ACK)) {
Glenn L McGrath0f182712002-12-19 20:16:22 +0000306 break;
307 }
Mark Whitley450736c2001-03-02 19:08:50 +0000308
Glenn L McGrath0f182712002-12-19 20:16:22 +0000309 /* receive packet */
Mark Whitley450736c2001-03-02 19:08:50 +0000310
311 memset(&from, 0, sizeof(from));
312 fromlen = sizeof(from);
313
Glenn L McGrathad117d82001-10-05 04:40:37 +0000314 tv.tv_sec = TFTP_TIMEOUT;
Mark Whitley450736c2001-03-02 19:08:50 +0000315 tv.tv_usec = 0;
316
317 FD_ZERO(&rfds);
318 FD_SET(socketfd, &rfds);
319
Paul Fox1d4c88c2005-07-20 19:49:15 +0000320 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000321 case 1:
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000322 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
323 (struct sockaddr *) &from, &fromlen);
Mark Whitley450736c2001-03-02 19:08:50 +0000324
325 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000326 bb_perror_msg("recvfrom");
Mark Whitley450736c2001-03-02 19:08:50 +0000327 break;
328 }
329
330 timeout = 0;
331
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000332 if (sa.sin_port == port) {
Mark Whitley450736c2001-03-02 19:08:50 +0000333 sa.sin_port = from.sin_port;
Mark Whitley450736c2001-03-02 19:08:50 +0000334 }
Mark Whitley450736c2001-03-02 19:08:50 +0000335 if (sa.sin_port == from.sin_port) {
336 break;
337 }
338
339 /* fall-through for bad packets! */
340 /* discard the packet - treat as timeout */
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000341 timeout = bb_tftp_num_retries;
Mark Whitley450736c2001-03-02 19:08:50 +0000342
343 case 0:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000344 bb_error_msg("timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000345
Eric Andersenb99aec02003-07-30 07:16:39 +0000346 timeout--;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000347 if (timeout == 0) {
Mark Whitley450736c2001-03-02 19:08:50 +0000348 len = -1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000349 bb_error_msg("last timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000350 }
351 break;
352
353 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000354 bb_perror_msg("select");
Mark Whitley450736c2001-03-02 19:08:50 +0000355 len = -1;
356 }
357
358 } while (timeout && (len >= 0));
359
Glenn L McGrath0f182712002-12-19 20:16:22 +0000360 if ((finished) || (len < 0)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000361 break;
362 }
363
364 /* process received packet */
365
366
367 opcode = ntohs(*((unsigned short *) buf));
368 tmp = ntohs(*((unsigned short *) &buf[2]));
369
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000370#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000371 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000372#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000373
Glenn L McGrathad117d82001-10-05 04:40:37 +0000374 if (opcode == TFTP_ERROR) {
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000375 const char *msg = NULL;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000376
377 if (buf[4] != '\0') {
378 msg = &buf[4];
379 buf[tftp_bufsize - 1] = '\0';
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000380 } else if (tmp < (sizeof(tftp_bb_error_msg)
Glenn L McGrathad117d82001-10-05 04:40:37 +0000381 / sizeof(char *))) {
382
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000383 msg = tftp_bb_error_msg[tmp];
Glenn L McGrathad117d82001-10-05 04:40:37 +0000384 }
385
386 if (msg) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000387 bb_error_msg("server says: %s", msg);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000388 }
389
390 break;
391 }
392
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000393#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000394 if (want_option_ack) {
395
396 want_option_ack = 0;
397
398 if (opcode == TFTP_OACK) {
399
400 /* server seems to support options */
401
402 char *res;
403
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000404 res = tftp_option_get(&buf[2], len-2,
Glenn L McGrathad117d82001-10-05 04:40:37 +0000405 "blksize");
406
407 if (res) {
Glenn L McGrathfad90db2002-12-09 21:05:40 +0000408 int blksize = atoi(res);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000409
Glenn L McGrathfad90db2002-12-09 21:05:40 +0000410 if (tftp_blocksize_check(blksize,
Glenn L McGrathad117d82001-10-05 04:40:37 +0000411 tftp_bufsize - 4)) {
412
413 if (cmd_put) {
414 opcode = TFTP_DATA;
415 }
416 else {
417 opcode = TFTP_ACK;
418 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000419#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000420 fprintf(stderr, "using blksize %u\n", blksize);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000421#endif
Glenn L McGrath9bf9f1e2002-12-09 21:52:29 +0000422 tftp_bufsize = blksize + 4;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000423 block_nr = 0;
424 continue;
425 }
426 }
427 /* FIXME:
428 * we should send ERROR 8 */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000429 bb_error_msg("bad server option");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000430 break;
431 }
432
Manuel Novoa III cad53642003-03-19 09:13:01 +0000433 bb_error_msg("warning: blksize not supported by server"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000434 " - reverting to 512");
435
436 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
437 }
438#endif
439
440 if (cmd_get && (opcode == TFTP_DATA)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000441
442 if (tmp == block_nr) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000443
Eric Andersen4872ed92004-06-22 10:18:30 +0000444 len = bb_full_write(localfd, &buf[4], len - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000445
446 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000447 bb_perror_msg("write");
Mark Whitley450736c2001-03-02 19:08:50 +0000448 break;
449 }
450
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000451 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000452 finished++;
453 }
454
Glenn L McGrathad117d82001-10-05 04:40:37 +0000455 opcode = TFTP_ACK;
Mark Whitley450736c2001-03-02 19:08:50 +0000456 continue;
457 }
Rob Landleyf3133c42005-06-07 02:40:39 +0000458 /* in case the last ack disappeared into the ether */
459 if ( tmp == (block_nr - 1) ) {
460 --block_nr;
461 opcode = TFTP_ACK;
462 continue;
Paul Fox1d4c88c2005-07-20 19:49:15 +0000463 } else if (tmp + 1 == block_nr) {
464 /* Server lost our TFTP_ACK. Resend it */
465 block_nr = tmp;
466 opcode = TFTP_ACK;
467 continue;
Rob Landleyf3133c42005-06-07 02:40:39 +0000468 }
Mark Whitley450736c2001-03-02 19:08:50 +0000469 }
470
Glenn L McGrathad117d82001-10-05 04:40:37 +0000471 if (cmd_put && (opcode == TFTP_ACK)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000472
Glenn L McGrathc6997782004-02-22 03:33:53 +0000473 if (tmp == (unsigned short)(block_nr - 1)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000474 if (finished) {
475 break;
476 }
477
Glenn L McGrathad117d82001-10-05 04:40:37 +0000478 opcode = TFTP_DATA;
Mark Whitley450736c2001-03-02 19:08:50 +0000479 continue;
480 }
481 }
Mark Whitley450736c2001-03-02 19:08:50 +0000482 }
483
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000484#ifdef CONFIG_FEATURE_CLEAN_UP
Mark Whitley450736c2001-03-02 19:08:50 +0000485 close(socketfd);
486
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000487 free(buf);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000488#endif
489
Mark Whitley8bb7df42001-03-06 20:58:48 +0000490 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000491}
492
493int tftp_main(int argc, char **argv)
494{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000495 struct hostent *host = NULL;
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000496 const char *localfile = NULL;
497 const char *remotefile = NULL;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000498 int port;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000499 int cmd = 0;
500 int fd = -1;
501 int flags = 0;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000502 int result;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000503 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
Mark Whitley450736c2001-03-02 19:08:50 +0000504
Glenn L McGrathad117d82001-10-05 04:40:37 +0000505 /* figure out what to pass to getopt */
506
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000507#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000508 char *sblocksize = NULL;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000509#define BS "b:"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000510#define BS_ARG , &sblocksize
Glenn L McGrathad117d82001-10-05 04:40:37 +0000511#else
512#define BS
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000513#define BS_ARG
Glenn L McGrathad117d82001-10-05 04:40:37 +0000514#endif
515
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000516#ifdef CONFIG_FEATURE_TFTP_GET
Glenn L McGrathad117d82001-10-05 04:40:37 +0000517#define GET "g"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000518#define GET_COMPL ":g"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000519#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000520#define GET
Bernhard Reutner-Fischer886f6af2006-04-05 16:47:02 +0000521#define GET_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000522#endif
523
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000524#ifdef CONFIG_FEATURE_TFTP_PUT
Glenn L McGrathad117d82001-10-05 04:40:37 +0000525#define PUT "p"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000526#define PUT_COMPL ":p"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000527#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000528#define PUT
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000529#define PUT_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000530#endif
531
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000532#if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
533 bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
534#elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
535 bb_opt_complementally = GET_COMPL PUT_COMPL;
536#else
537 /* XXX: may be should #error ? */
538#endif
539
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000540
541 cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000542 &localfile, &remotefile BS_ARG);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000543#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000544 if(sblocksize) {
545 blocksize = atoi(sblocksize);
546 if (!tftp_blocksize_check(blocksize, 0)) {
547 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000548 }
Mark Whitley450736c2001-03-02 19:08:50 +0000549 }
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000550#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000551
Rob Landley5aabf4e2005-12-15 05:42:55 +0000552 cmd &= (tftp_cmd_get | tftp_cmd_put);
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000553#ifdef CONFIG_FEATURE_TFTP_GET
554 if(cmd == tftp_cmd_get)
555 flags = O_WRONLY | O_CREAT | O_TRUNC;
556#endif
557#ifdef CONFIG_FEATURE_TFTP_PUT
558 if(cmd == tftp_cmd_put)
559 flags = O_RDONLY;
560#endif
561
Eric Andersen744ec1d2002-04-15 07:40:27 +0000562 if(localfile == NULL)
563 localfile = remotefile;
564 if(remotefile == NULL)
565 remotefile = localfile;
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000566 /* XXX: I corrected this, but may be wrong too. vodz */
567 if(localfile==NULL || strcmp(localfile, "-") == 0) {
568 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
569 } else if (fd==-1) {
Eric Andersena66a43e2002-04-13 09:30:25 +0000570 fd = open(localfile, flags, 0644);
571 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000572 if (fd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000573 bb_perror_msg_and_die("local file");
Mark Whitley450736c2001-03-02 19:08:50 +0000574 }
575
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000576 /* XXX: argv[optind] and/or argv[optind + 1] may be NULL! */
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000577 host = xgethostbyname(argv[optind]);
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000578 port = bb_lookup_port(argv[optind + 1], "udp", 69);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000579
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000580#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000581 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000582 "localfile \"%s\".\n",
583 inet_ntoa(*((struct in_addr *) host->h_addr)),
584 remotefile, localfile);
585#endif
586
587 result = tftp(cmd, host, remotefile, fd, port, blocksize);
Mark Whitley450736c2001-03-02 19:08:50 +0000588
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000589#ifdef CONFIG_FEATURE_CLEAN_UP
Eric Andersen70060d22004-03-27 10:02:48 +0000590 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
Eric Andersena66a43e2002-04-13 09:30:25 +0000591 close(fd);
592 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000593#endif
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000594 return(result);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000595}