blob: 73a5adae5171cfc1eb52ce538339e3b38196c23a [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>
Mark Whitley450736c2001-03-02 19:08:50 +000027#include <sys/stat.h>
28#include <netdb.h>
29#include <netinet/in.h>
30#include <arpa/inet.h>
31#include <unistd.h>
32#include <fcntl.h>
33
34#include "busybox.h"
35
Eric Andersenbdfd0d72001-10-24 05:00:29 +000036//#define CONFIG_FEATURE_TFTP_DEBUG
Mark Whitley450736c2001-03-02 19:08:50 +000037
Glenn L McGrathad117d82001-10-05 04:40:37 +000038#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
39#define TFTP_TIMEOUT 5 /* seconds */
40
41/* opcodes we support */
42
43#define TFTP_RRQ 1
44#define TFTP_WRQ 2
45#define TFTP_DATA 3
46#define TFTP_ACK 4
47#define TFTP_ERROR 5
48#define TFTP_OACK 6
49
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +000050static const char * const tftp_bb_error_msg[] = {
Mark Whitley450736c2001-03-02 19:08:50 +000051 "Undefined error",
52 "File not found",
53 "Access violation",
54 "Disk full or allocation error",
55 "Illegal TFTP operation",
56 "Unknown transfer ID",
57 "File already exists",
58 "No such user"
59};
60
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +000061#ifdef CONFIG_FEATURE_TFTP_GET
62# define tftp_cmd_get 1
63#else
64# define tftp_cmd_get 0
65#endif
66#ifdef CONFIG_FEATURE_TFTP_PUT
67# define tftp_cmd_put (tftp_cmd_get+1)
68#else
69# define tftp_cmd_put 0
70#endif
71
Eric Andersen76fa8ea2001-08-20 17:47:49 +000072
Eric Andersenbdfd0d72001-10-24 05:00:29 +000073#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +000074
Eric Andersenc7bda1c2004-03-15 08:29:22 +000075static int tftp_blocksize_check(int blocksize, int bufsize)
Glenn L McGrathad117d82001-10-05 04:40:37 +000076{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000077 /* Check if the blocksize is valid:
Glenn L McGrathad117d82001-10-05 04:40:37 +000078 * RFC2348 says between 8 and 65464,
79 * but our implementation makes it impossible
80 * to use blocksizes smaller than 22 octets.
81 */
82
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000083 if ((bufsize && (blocksize > bufsize)) ||
Glenn L McGrathad117d82001-10-05 04:40:37 +000084 (blocksize < 8) || (blocksize > 65464)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000085 bb_error_msg("bad blocksize");
Glenn L McGrathad117d82001-10-05 04:40:37 +000086 return 0;
87 }
88
89 return blocksize;
90}
91
Eric Andersenc7bda1c2004-03-15 08:29:22 +000092static char *tftp_option_get(char *buf, int len, char *option)
Glenn L McGrathad117d82001-10-05 04:40:37 +000093{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000094 int opt_val = 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +000095 int opt_found = 0;
96 int k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000097
Glenn L McGrathad117d82001-10-05 04:40:37 +000098 while (len > 0) {
99
100 /* Make sure the options are terminated correctly */
101
102 for (k = 0; k < len; k++) {
103 if (buf[k] == '\0') {
104 break;
105 }
106 }
107
108 if (k >= len) {
109 break;
110 }
111
112 if (opt_val == 0) {
113 if (strcasecmp(buf, option) == 0) {
114 opt_found = 1;
115 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000116 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000117 else {
118 if (opt_found) {
119 return buf;
120 }
121 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000122
Glenn L McGrathad117d82001-10-05 04:40:37 +0000123 k++;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000124
Glenn L McGrathad117d82001-10-05 04:40:37 +0000125 buf += k;
126 len -= k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000127
Glenn L McGrathad117d82001-10-05 04:40:37 +0000128 opt_val ^= 1;
129 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000130
Glenn L McGrathad117d82001-10-05 04:40:37 +0000131 return NULL;
132}
133
134#endif
135
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000136static inline int tftp(const int cmd, const struct hostent *host,
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000137 const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
Mark Whitley450736c2001-03-02 19:08:50 +0000138{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000139 const int cmd_get = cmd & tftp_cmd_get;
140 const int cmd_put = cmd & tftp_cmd_put;
141 const int bb_tftp_num_retries = 5;
142
Mark Whitley450736c2001-03-02 19:08:50 +0000143 struct sockaddr_in sa;
Mark Whitley450736c2001-03-02 19:08:50 +0000144 struct sockaddr_in from;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000145 struct timeval tv;
Mark Whitley450736c2001-03-02 19:08:50 +0000146 socklen_t fromlen;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000147 fd_set rfds;
Mark Whitley450736c2001-03-02 19:08:50 +0000148 char *cp;
149 unsigned short tmp;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000150 int socketfd;
151 int len;
152 int opcode = 0;
153 int finished = 0;
154 int timeout = bb_tftp_num_retries;
Glenn L McGrathc6997782004-02-22 03:33:53 +0000155 unsigned short block_nr = 1;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000156
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000157#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000158 int want_option_ack = 0;
159#endif
160
Eric Andersen744a1942001-11-10 11:16:39 +0000161 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
162 * size varies meaning BUFFERS_GO_ON_STACK would fail */
163 char *buf=xmalloc(tftp_bufsize + 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000164
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000165 tftp_bufsize += 4;
Mark Whitley450736c2001-03-02 19:08:50 +0000166
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +0000167 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { /* bb_xsocket? */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000168 bb_perror_msg("socket");
Mark Whitley8bb7df42001-03-06 20:58:48 +0000169 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000170 }
171
172 len = sizeof(sa);
173
174 memset(&sa, 0, len);
Eric Andersene76c3b02001-04-05 03:14:39 +0000175 bind(socketfd, (struct sockaddr *)&sa, len);
Mark Whitley450736c2001-03-02 19:08:50 +0000176
177 sa.sin_family = host->h_addrtype;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000178 sa.sin_port = port;
Mark Whitley450736c2001-03-02 19:08:50 +0000179 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
180 sizeof(sa.sin_addr));
181
182 /* build opcode */
183
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000184 if (cmd_get) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000185 opcode = TFTP_RRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000186 }
187
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000188 if (cmd_put) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000189 opcode = TFTP_WRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000190 }
191
192 while (1) {
193
Mark Whitley450736c2001-03-02 19:08:50 +0000194 cp = buf;
195
Glenn L McGrathad117d82001-10-05 04:40:37 +0000196 /* first create the opcode part */
197
Mark Whitley450736c2001-03-02 19:08:50 +0000198 *((unsigned short *) cp) = htons(opcode);
199
200 cp += 2;
201
202 /* add filename and mode */
203
Glenn L McGrathad117d82001-10-05 04:40:37 +0000204 if ((cmd_get && (opcode == TFTP_RRQ)) ||
205 (cmd_put && (opcode == TFTP_WRQ))) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000206 int too_long = 0;
Mark Whitley450736c2001-03-02 19:08:50 +0000207
Glenn L McGrathad117d82001-10-05 04:40:37 +0000208 /* see if the filename fits into buf */
209 /* and fill in packet */
210
211 len = strlen(remotefile) + 1;
212
213 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
214 too_long = 1;
Mark Whitley450736c2001-03-02 19:08:50 +0000215 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000216 else {
217 safe_strncpy(cp, remotefile, len);
218 cp += len;
219 }
220
221 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000222 bb_error_msg("too long remote-filename");
Mark Whitley450736c2001-03-02 19:08:50 +0000223 break;
224 }
225
Glenn L McGrathad117d82001-10-05 04:40:37 +0000226 /* add "mode" part of the package */
227
228 memcpy(cp, "octet", 6);
229 cp += 6;
230
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000231#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000232
233 len = tftp_bufsize - 4; /* data block size */
234
235 if (len != TFTP_BLOCKSIZE_DEFAULT) {
236
237 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000238 bb_error_msg("too long remote-filename");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000239 break;
240 }
241
242 /* add "blksize" + number of blocks */
243
244 memcpy(cp, "blksize", 8);
245 cp += 8;
246
247 cp += snprintf(cp, 6, "%d", len) + 1;
248
249 want_option_ack = 1;
250 }
251#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000252 }
253
254 /* add ack and data */
255
Glenn L McGrathad117d82001-10-05 04:40:37 +0000256 if ((cmd_get && (opcode == TFTP_ACK)) ||
257 (cmd_put && (opcode == TFTP_DATA))) {
Mark Whitley450736c2001-03-02 19:08:50 +0000258
259 *((unsigned short *) cp) = htons(block_nr);
260
261 cp += 2;
262
263 block_nr++;
264
Glenn L McGrathad117d82001-10-05 04:40:37 +0000265 if (cmd_put && (opcode == TFTP_DATA)) {
Eric Andersen4872ed92004-06-22 10:18:30 +0000266 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000267
268 if (len < 0) {
Bernhard Reutner-Fischer1b9d7c92006-06-03 22:45:37 +0000269 bb_perror_msg(bb_msg_read_error);
Mark Whitley450736c2001-03-02 19:08:50 +0000270 break;
271 }
272
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000273 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000274 finished++;
275 }
276
277 cp += len;
Mark Whitley450736c2001-03-02 19:08:50 +0000278 }
279 }
280
281
282 /* send packet */
283
284
Eric Andersen05e662a2003-07-26 08:16:10 +0000285 timeout = bb_tftp_num_retries; /* re-initialize */
Mark Whitley450736c2001-03-02 19:08:50 +0000286 do {
287
288 len = cp - buf;
289
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000290#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000291 fprintf(stderr, "sending %u bytes\n", len);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000292 for (cp = buf; cp < &buf[len]; cp++)
Glenn L McGrathfbe984e2004-03-05 13:04:39 +0000293 fprintf(stderr, "%02x ", (unsigned char)*cp);
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000294 fprintf(stderr, "\n");
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000295#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000296 if (sendto(socketfd, buf, len, 0,
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000297 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000298 bb_perror_msg("send");
Mark Whitley450736c2001-03-02 19:08:50 +0000299 len = -1;
300 break;
301 }
302
303
Eric Andersenb99aec02003-07-30 07:16:39 +0000304 if (finished && (opcode == TFTP_ACK)) {
Glenn L McGrath0f182712002-12-19 20:16:22 +0000305 break;
306 }
Mark Whitley450736c2001-03-02 19:08:50 +0000307
Glenn L McGrath0f182712002-12-19 20:16:22 +0000308 /* receive packet */
Mark Whitley450736c2001-03-02 19:08:50 +0000309
310 memset(&from, 0, sizeof(from));
311 fromlen = sizeof(from);
312
Glenn L McGrathad117d82001-10-05 04:40:37 +0000313 tv.tv_sec = TFTP_TIMEOUT;
Mark Whitley450736c2001-03-02 19:08:50 +0000314 tv.tv_usec = 0;
315
316 FD_ZERO(&rfds);
317 FD_SET(socketfd, &rfds);
318
Paul Fox1d4c88c2005-07-20 19:49:15 +0000319 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000320 case 1:
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000321 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
322 (struct sockaddr *) &from, &fromlen);
Mark Whitley450736c2001-03-02 19:08:50 +0000323
324 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000325 bb_perror_msg("recvfrom");
Mark Whitley450736c2001-03-02 19:08:50 +0000326 break;
327 }
328
329 timeout = 0;
330
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000331 if (sa.sin_port == port) {
Mark Whitley450736c2001-03-02 19:08:50 +0000332 sa.sin_port = from.sin_port;
Mark Whitley450736c2001-03-02 19:08:50 +0000333 }
Mark Whitley450736c2001-03-02 19:08:50 +0000334 if (sa.sin_port == from.sin_port) {
335 break;
336 }
337
338 /* fall-through for bad packets! */
339 /* discard the packet - treat as timeout */
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000340 timeout = bb_tftp_num_retries;
Mark Whitley450736c2001-03-02 19:08:50 +0000341
342 case 0:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000343 bb_error_msg("timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000344
Eric Andersenb99aec02003-07-30 07:16:39 +0000345 timeout--;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000346 if (timeout == 0) {
Mark Whitley450736c2001-03-02 19:08:50 +0000347 len = -1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000348 bb_error_msg("last timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000349 }
350 break;
351
352 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000353 bb_perror_msg("select");
Mark Whitley450736c2001-03-02 19:08:50 +0000354 len = -1;
355 }
356
357 } while (timeout && (len >= 0));
358
Glenn L McGrath0f182712002-12-19 20:16:22 +0000359 if ((finished) || (len < 0)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000360 break;
361 }
362
363 /* process received packet */
364
365
366 opcode = ntohs(*((unsigned short *) buf));
367 tmp = ntohs(*((unsigned short *) &buf[2]));
368
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000369#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000370 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000371#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000372
Glenn L McGrathad117d82001-10-05 04:40:37 +0000373 if (opcode == TFTP_ERROR) {
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000374 const char *msg = NULL;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000375
376 if (buf[4] != '\0') {
377 msg = &buf[4];
378 buf[tftp_bufsize - 1] = '\0';
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000379 } else if (tmp < (sizeof(tftp_bb_error_msg)
Glenn L McGrathad117d82001-10-05 04:40:37 +0000380 / sizeof(char *))) {
381
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000382 msg = tftp_bb_error_msg[tmp];
Glenn L McGrathad117d82001-10-05 04:40:37 +0000383 }
384
385 if (msg) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000386 bb_error_msg("server says: %s", msg);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000387 }
388
389 break;
390 }
391
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000392#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000393 if (want_option_ack) {
394
395 want_option_ack = 0;
396
397 if (opcode == TFTP_OACK) {
398
399 /* server seems to support options */
400
401 char *res;
402
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000403 res = tftp_option_get(&buf[2], len-2,
Glenn L McGrathad117d82001-10-05 04:40:37 +0000404 "blksize");
405
406 if (res) {
Glenn L McGrathfad90db2002-12-09 21:05:40 +0000407 int blksize = atoi(res);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000408
Glenn L McGrathfad90db2002-12-09 21:05:40 +0000409 if (tftp_blocksize_check(blksize,
Glenn L McGrathad117d82001-10-05 04:40:37 +0000410 tftp_bufsize - 4)) {
411
412 if (cmd_put) {
413 opcode = TFTP_DATA;
414 }
415 else {
416 opcode = TFTP_ACK;
417 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000418#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000419 fprintf(stderr, "using blksize %u\n", blksize);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000420#endif
Glenn L McGrath9bf9f1e2002-12-09 21:52:29 +0000421 tftp_bufsize = blksize + 4;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000422 block_nr = 0;
423 continue;
424 }
425 }
426 /* FIXME:
427 * we should send ERROR 8 */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000428 bb_error_msg("bad server option");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000429 break;
430 }
431
Manuel Novoa III cad53642003-03-19 09:13:01 +0000432 bb_error_msg("warning: blksize not supported by server"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000433 " - reverting to 512");
434
435 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
436 }
437#endif
438
439 if (cmd_get && (opcode == TFTP_DATA)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000440
441 if (tmp == block_nr) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000442
Eric Andersen4872ed92004-06-22 10:18:30 +0000443 len = bb_full_write(localfd, &buf[4], len - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000444
445 if (len < 0) {
Bernhard Reutner-Fischer1b9d7c92006-06-03 22:45:37 +0000446 bb_perror_msg(bb_msg_write_error);
Mark Whitley450736c2001-03-02 19:08:50 +0000447 break;
448 }
449
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000450 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000451 finished++;
452 }
453
Glenn L McGrathad117d82001-10-05 04:40:37 +0000454 opcode = TFTP_ACK;
Mark Whitley450736c2001-03-02 19:08:50 +0000455 continue;
456 }
Rob Landleyf3133c42005-06-07 02:40:39 +0000457 /* in case the last ack disappeared into the ether */
458 if ( tmp == (block_nr - 1) ) {
459 --block_nr;
460 opcode = TFTP_ACK;
461 continue;
Paul Fox1d4c88c2005-07-20 19:49:15 +0000462 } else if (tmp + 1 == block_nr) {
463 /* Server lost our TFTP_ACK. Resend it */
464 block_nr = tmp;
465 opcode = TFTP_ACK;
466 continue;
Rob Landleyf3133c42005-06-07 02:40:39 +0000467 }
Mark Whitley450736c2001-03-02 19:08:50 +0000468 }
469
Glenn L McGrathad117d82001-10-05 04:40:37 +0000470 if (cmd_put && (opcode == TFTP_ACK)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000471
Glenn L McGrathc6997782004-02-22 03:33:53 +0000472 if (tmp == (unsigned short)(block_nr - 1)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000473 if (finished) {
474 break;
475 }
476
Glenn L McGrathad117d82001-10-05 04:40:37 +0000477 opcode = TFTP_DATA;
Mark Whitley450736c2001-03-02 19:08:50 +0000478 continue;
479 }
480 }
Mark Whitley450736c2001-03-02 19:08:50 +0000481 }
482
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000483#ifdef CONFIG_FEATURE_CLEAN_UP
Mark Whitley450736c2001-03-02 19:08:50 +0000484 close(socketfd);
485
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000486 free(buf);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000487#endif
488
Mark Whitley8bb7df42001-03-06 20:58:48 +0000489 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000490}
491
492int tftp_main(int argc, char **argv)
493{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000494 struct hostent *host = NULL;
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000495 const char *localfile = NULL;
496 const char *remotefile = NULL;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000497 int port;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000498 int cmd = 0;
499 int fd = -1;
500 int flags = 0;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000501 int result;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000502 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
Mark Whitley450736c2001-03-02 19:08:50 +0000503
Glenn L McGrathad117d82001-10-05 04:40:37 +0000504 /* figure out what to pass to getopt */
505
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000506#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000507 char *sblocksize = NULL;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000508#define BS "b:"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000509#define BS_ARG , &sblocksize
Glenn L McGrathad117d82001-10-05 04:40:37 +0000510#else
511#define BS
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000512#define BS_ARG
Glenn L McGrathad117d82001-10-05 04:40:37 +0000513#endif
514
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000515#ifdef CONFIG_FEATURE_TFTP_GET
Glenn L McGrathad117d82001-10-05 04:40:37 +0000516#define GET "g"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000517#define GET_COMPL ":g"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000518#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000519#define GET
Bernhard Reutner-Fischer886f6af2006-04-05 16:47:02 +0000520#define GET_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000521#endif
522
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000523#ifdef CONFIG_FEATURE_TFTP_PUT
Glenn L McGrathad117d82001-10-05 04:40:37 +0000524#define PUT "p"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000525#define PUT_COMPL ":p"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000526#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000527#define PUT
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000528#define PUT_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000529#endif
530
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000531#if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
532 bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
533#elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
534 bb_opt_complementally = GET_COMPL PUT_COMPL;
535#else
536 /* XXX: may be should #error ? */
537#endif
538
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000539
540 cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000541 &localfile, &remotefile BS_ARG);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000542#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000543 if(sblocksize) {
544 blocksize = atoi(sblocksize);
545 if (!tftp_blocksize_check(blocksize, 0)) {
546 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000547 }
Mark Whitley450736c2001-03-02 19:08:50 +0000548 }
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000549#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000550
Rob Landley5aabf4e2005-12-15 05:42:55 +0000551 cmd &= (tftp_cmd_get | tftp_cmd_put);
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000552#ifdef CONFIG_FEATURE_TFTP_GET
553 if(cmd == tftp_cmd_get)
554 flags = O_WRONLY | O_CREAT | O_TRUNC;
555#endif
556#ifdef CONFIG_FEATURE_TFTP_PUT
557 if(cmd == tftp_cmd_put)
558 flags = O_RDONLY;
559#endif
560
Eric Andersen744ec1d2002-04-15 07:40:27 +0000561 if(localfile == NULL)
562 localfile = remotefile;
563 if(remotefile == NULL)
564 remotefile = localfile;
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000565 /* XXX: I corrected this, but may be wrong too. vodz */
566 if(localfile==NULL || strcmp(localfile, "-") == 0) {
567 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
568 } else if (fd==-1) {
Eric Andersena66a43e2002-04-13 09:30:25 +0000569 fd = open(localfile, flags, 0644);
570 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000571 if (fd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000572 bb_perror_msg_and_die("local file");
Mark Whitley450736c2001-03-02 19:08:50 +0000573 }
574
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000575 /* XXX: argv[optind] and/or argv[optind + 1] may be NULL! */
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000576 host = xgethostbyname(argv[optind]);
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000577 port = bb_lookup_port(argv[optind + 1], "udp", 69);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000578
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000579#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000580 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000581 "localfile \"%s\".\n",
582 inet_ntoa(*((struct in_addr *) host->h_addr)),
583 remotefile, localfile);
584#endif
585
586 result = tftp(cmd, host, remotefile, fd, port, blocksize);
Mark Whitley450736c2001-03-02 19:08:50 +0000587
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000588#ifdef CONFIG_FEATURE_CLEAN_UP
Eric Andersen70060d22004-03-27 10:02:48 +0000589 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
Eric Andersena66a43e2002-04-13 09:30:25 +0000590 close(fd);
591 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000592#endif
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000593 return(result);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000594}