blob: 9b974778546fe4d8825997402dc16aa501ca11c1 [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. */
Glenn L McGrathad117d82001-10-05 04:40:37 +00006/* Only "octet" mode supported. */
7/* Optional blocksize negotiation (RFC2347 + RFC2348) */
Mark Whitley450736c2001-03-02 19:08:50 +00008/* */
9/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
10/* */
11/* Parts of the code based on: */
12/* */
13/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
14/* and Remi Lefebvre <remi@debian.org> */
15/* */
16/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
17/* */
18/* This program is free software; you can redistribute it and/or modify */
19/* it under the terms of the GNU General Public License as published by */
20/* the Free Software Foundation; either version 2 of the License, or */
21/* (at your option) any later version. */
22/* */
23/* This program is distributed in the hope that it will be useful, */
24/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
25/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
26/* General Public License for more details. */
27/* */
28/* You should have received a copy of the GNU General Public License */
29/* along with this program; if not, write to the Free Software */
30/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
31/* */
32/* ------------------------------------------------------------------------- */
33
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <sys/time.h>
40#include <sys/stat.h>
41#include <netdb.h>
42#include <netinet/in.h>
43#include <arpa/inet.h>
44#include <unistd.h>
45#include <fcntl.h>
46
47#include "busybox.h"
48
Eric Andersenbdfd0d72001-10-24 05:00:29 +000049//#define CONFIG_FEATURE_TFTP_DEBUG
Mark Whitley450736c2001-03-02 19:08:50 +000050
Glenn L McGrathad117d82001-10-05 04:40:37 +000051#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
52#define TFTP_TIMEOUT 5 /* seconds */
53
54/* opcodes we support */
55
56#define TFTP_RRQ 1
57#define TFTP_WRQ 2
58#define TFTP_DATA 3
59#define TFTP_ACK 4
60#define TFTP_ERROR 5
61#define TFTP_OACK 6
62
Manuel Novoa III cad53642003-03-19 09:13:01 +000063static const char *tftp_bb_error_msg[] = {
Mark Whitley450736c2001-03-02 19:08:50 +000064 "Undefined error",
65 "File not found",
66 "Access violation",
67 "Disk full or allocation error",
68 "Illegal TFTP operation",
69 "Unknown transfer ID",
70 "File already exists",
71 "No such user"
72};
73
Eric Andersen14f5c8d2005-04-16 19:39:00 +000074static const int tftp_cmd_get = 1;
75static const int tftp_cmd_put = 2;
Eric Andersen76fa8ea2001-08-20 17:47:49 +000076
Eric Andersenbdfd0d72001-10-24 05:00:29 +000077#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +000078
Eric Andersenc7bda1c2004-03-15 08:29:22 +000079static int tftp_blocksize_check(int blocksize, int bufsize)
Glenn L McGrathad117d82001-10-05 04:40:37 +000080{
Eric Andersenc7bda1c2004-03-15 08:29:22 +000081 /* Check if the blocksize is valid:
Glenn L McGrathad117d82001-10-05 04:40:37 +000082 * RFC2348 says between 8 and 65464,
83 * but our implementation makes it impossible
84 * to use blocksizes smaller than 22 octets.
85 */
86
Eric Andersenc7bda1c2004-03-15 08:29:22 +000087 if ((bufsize && (blocksize > bufsize)) ||
Glenn L McGrathad117d82001-10-05 04:40:37 +000088 (blocksize < 8) || (blocksize > 65464)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000089 bb_error_msg("bad blocksize");
Glenn L McGrathad117d82001-10-05 04:40:37 +000090 return 0;
91 }
92
93 return blocksize;
94}
95
Eric Andersenc7bda1c2004-03-15 08:29:22 +000096static char *tftp_option_get(char *buf, int len, char *option)
Glenn L McGrathad117d82001-10-05 04:40:37 +000097{
98 int opt_val = 0;
99 int opt_found = 0;
100 int k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000101
Glenn L McGrathad117d82001-10-05 04:40:37 +0000102 while (len > 0) {
103
104 /* Make sure the options are terminated correctly */
105
106 for (k = 0; k < len; k++) {
107 if (buf[k] == '\0') {
108 break;
109 }
110 }
111
112 if (k >= len) {
113 break;
114 }
115
116 if (opt_val == 0) {
117 if (strcasecmp(buf, option) == 0) {
118 opt_found = 1;
119 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000120 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000121 else {
122 if (opt_found) {
123 return buf;
124 }
125 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000126
Glenn L McGrathad117d82001-10-05 04:40:37 +0000127 k++;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000128
Glenn L McGrathad117d82001-10-05 04:40:37 +0000129 buf += k;
130 len -= k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000131
Glenn L McGrathad117d82001-10-05 04:40:37 +0000132 opt_val ^= 1;
133 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000134
Glenn L McGrathad117d82001-10-05 04:40:37 +0000135 return NULL;
136}
137
138#endif
139
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000140static inline int tftp(const int cmd, const struct hostent *host,
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000141 const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
Mark Whitley450736c2001-03-02 19:08:50 +0000142{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000143 const int cmd_get = cmd & tftp_cmd_get;
144 const int cmd_put = cmd & tftp_cmd_put;
145 const int bb_tftp_num_retries = 5;
146
Mark Whitley450736c2001-03-02 19:08:50 +0000147 struct sockaddr_in sa;
Mark Whitley450736c2001-03-02 19:08:50 +0000148 struct sockaddr_in from;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000149 struct timeval tv;
Mark Whitley450736c2001-03-02 19:08:50 +0000150 socklen_t fromlen;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000151 fd_set rfds;
Mark Whitley450736c2001-03-02 19:08:50 +0000152 char *cp;
153 unsigned short tmp;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000154 int socketfd;
155 int len;
156 int opcode = 0;
157 int finished = 0;
158 int timeout = bb_tftp_num_retries;
Glenn L McGrathc6997782004-02-22 03:33:53 +0000159 unsigned short block_nr = 1;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000160
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000161#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000162 int want_option_ack = 0;
163#endif
164
Eric Andersen744a1942001-11-10 11:16:39 +0000165 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
166 * size varies meaning BUFFERS_GO_ON_STACK would fail */
167 char *buf=xmalloc(tftp_bufsize + 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000168
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000169 tftp_bufsize += 4;
Mark Whitley450736c2001-03-02 19:08:50 +0000170
171 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000172 bb_perror_msg("socket");
Mark Whitley8bb7df42001-03-06 20:58:48 +0000173 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000174 }
175
176 len = sizeof(sa);
177
178 memset(&sa, 0, len);
Eric Andersene76c3b02001-04-05 03:14:39 +0000179 bind(socketfd, (struct sockaddr *)&sa, len);
Mark Whitley450736c2001-03-02 19:08:50 +0000180
181 sa.sin_family = host->h_addrtype;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000182 sa.sin_port = port;
Mark Whitley450736c2001-03-02 19:08:50 +0000183 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
184 sizeof(sa.sin_addr));
185
186 /* build opcode */
187
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000188 if (cmd_get) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000189 opcode = TFTP_RRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000190 }
191
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000192 if (cmd_put) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000193 opcode = TFTP_WRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000194 }
195
196 while (1) {
197
Mark Whitley450736c2001-03-02 19:08:50 +0000198 cp = buf;
199
Glenn L McGrathad117d82001-10-05 04:40:37 +0000200 /* first create the opcode part */
201
Mark Whitley450736c2001-03-02 19:08:50 +0000202 *((unsigned short *) cp) = htons(opcode);
203
204 cp += 2;
205
206 /* add filename and mode */
207
Glenn L McGrathad117d82001-10-05 04:40:37 +0000208 if ((cmd_get && (opcode == TFTP_RRQ)) ||
209 (cmd_put && (opcode == TFTP_WRQ))) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000210 int too_long = 0;
Mark Whitley450736c2001-03-02 19:08:50 +0000211
Glenn L McGrathad117d82001-10-05 04:40:37 +0000212 /* see if the filename fits into buf */
213 /* and fill in packet */
214
215 len = strlen(remotefile) + 1;
216
217 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
218 too_long = 1;
Mark Whitley450736c2001-03-02 19:08:50 +0000219 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000220 else {
221 safe_strncpy(cp, remotefile, len);
222 cp += len;
223 }
224
225 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000226 bb_error_msg("too long remote-filename");
Mark Whitley450736c2001-03-02 19:08:50 +0000227 break;
228 }
229
Glenn L McGrathad117d82001-10-05 04:40:37 +0000230 /* add "mode" part of the package */
231
232 memcpy(cp, "octet", 6);
233 cp += 6;
234
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000235#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000236
237 len = tftp_bufsize - 4; /* data block size */
238
239 if (len != TFTP_BLOCKSIZE_DEFAULT) {
240
241 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000242 bb_error_msg("too long remote-filename");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000243 break;
244 }
245
246 /* add "blksize" + number of blocks */
247
248 memcpy(cp, "blksize", 8);
249 cp += 8;
250
251 cp += snprintf(cp, 6, "%d", len) + 1;
252
253 want_option_ack = 1;
254 }
255#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000256 }
257
258 /* add ack and data */
259
Glenn L McGrathad117d82001-10-05 04:40:37 +0000260 if ((cmd_get && (opcode == TFTP_ACK)) ||
261 (cmd_put && (opcode == TFTP_DATA))) {
Mark Whitley450736c2001-03-02 19:08:50 +0000262
263 *((unsigned short *) cp) = htons(block_nr);
264
265 cp += 2;
266
267 block_nr++;
268
Glenn L McGrathad117d82001-10-05 04:40:37 +0000269 if (cmd_put && (opcode == TFTP_DATA)) {
Eric Andersen4872ed92004-06-22 10:18:30 +0000270 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000271
272 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000273 bb_perror_msg("read");
Mark Whitley450736c2001-03-02 19:08:50 +0000274 break;
275 }
276
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000277 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000278 finished++;
279 }
280
281 cp += len;
Mark Whitley450736c2001-03-02 19:08:50 +0000282 }
283 }
284
285
286 /* send packet */
287
288
Eric Andersen05e662a2003-07-26 08:16:10 +0000289 timeout = bb_tftp_num_retries; /* re-initialize */
Mark Whitley450736c2001-03-02 19:08:50 +0000290 do {
291
292 len = cp - buf;
293
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000294#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000295 fprintf(stderr, "sending %u bytes\n", len);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000296 for (cp = buf; cp < &buf[len]; cp++)
Glenn L McGrathfbe984e2004-03-05 13:04:39 +0000297 fprintf(stderr, "%02x ", (unsigned char)*cp);
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000298 fprintf(stderr, "\n");
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000299#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000300 if (sendto(socketfd, buf, len, 0,
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000301 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000302 bb_perror_msg("send");
Mark Whitley450736c2001-03-02 19:08:50 +0000303 len = -1;
304 break;
305 }
306
307
Eric Andersenb99aec02003-07-30 07:16:39 +0000308 if (finished && (opcode == TFTP_ACK)) {
Glenn L McGrath0f182712002-12-19 20:16:22 +0000309 break;
310 }
Mark Whitley450736c2001-03-02 19:08:50 +0000311
Glenn L McGrath0f182712002-12-19 20:16:22 +0000312 /* receive packet */
Mark Whitley450736c2001-03-02 19:08:50 +0000313
314 memset(&from, 0, sizeof(from));
315 fromlen = sizeof(from);
316
Glenn L McGrathad117d82001-10-05 04:40:37 +0000317 tv.tv_sec = TFTP_TIMEOUT;
Mark Whitley450736c2001-03-02 19:08:50 +0000318 tv.tv_usec = 0;
319
320 FD_ZERO(&rfds);
321 FD_SET(socketfd, &rfds);
322
Paul Fox1d4c88c2005-07-20 19:49:15 +0000323 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000324 case 1:
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000325 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
326 (struct sockaddr *) &from, &fromlen);
Mark Whitley450736c2001-03-02 19:08:50 +0000327
328 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000329 bb_perror_msg("recvfrom");
Mark Whitley450736c2001-03-02 19:08:50 +0000330 break;
331 }
332
333 timeout = 0;
334
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000335 if (sa.sin_port == port) {
Mark Whitley450736c2001-03-02 19:08:50 +0000336 sa.sin_port = from.sin_port;
Mark Whitley450736c2001-03-02 19:08:50 +0000337 }
Mark Whitley450736c2001-03-02 19:08:50 +0000338 if (sa.sin_port == from.sin_port) {
339 break;
340 }
341
342 /* fall-through for bad packets! */
343 /* discard the packet - treat as timeout */
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000344 timeout = bb_tftp_num_retries;
Mark Whitley450736c2001-03-02 19:08:50 +0000345
346 case 0:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000347 bb_error_msg("timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000348
Eric Andersenb99aec02003-07-30 07:16:39 +0000349 timeout--;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000350 if (timeout == 0) {
Mark Whitley450736c2001-03-02 19:08:50 +0000351 len = -1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000352 bb_error_msg("last timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000353 }
354 break;
355
356 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000357 bb_perror_msg("select");
Mark Whitley450736c2001-03-02 19:08:50 +0000358 len = -1;
359 }
360
361 } while (timeout && (len >= 0));
362
Glenn L McGrath0f182712002-12-19 20:16:22 +0000363 if ((finished) || (len < 0)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000364 break;
365 }
366
367 /* process received packet */
368
369
370 opcode = ntohs(*((unsigned short *) buf));
371 tmp = ntohs(*((unsigned short *) &buf[2]));
372
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000373#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000374 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000375#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000376
Glenn L McGrathad117d82001-10-05 04:40:37 +0000377 if (opcode == TFTP_ERROR) {
378 char *msg = NULL;
379
380 if (buf[4] != '\0') {
381 msg = &buf[4];
382 buf[tftp_bufsize - 1] = '\0';
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000383 } else if (tmp < (sizeof(tftp_bb_error_msg)
Glenn L McGrathad117d82001-10-05 04:40:37 +0000384 / sizeof(char *))) {
385
Manuel Novoa III cad53642003-03-19 09:13:01 +0000386 msg = (char *) tftp_bb_error_msg[tmp];
Glenn L McGrathad117d82001-10-05 04:40:37 +0000387 }
388
389 if (msg) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000390 bb_error_msg("server says: %s", msg);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000391 }
392
393 break;
394 }
395
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000396#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000397 if (want_option_ack) {
398
399 want_option_ack = 0;
400
401 if (opcode == TFTP_OACK) {
402
403 /* server seems to support options */
404
405 char *res;
406
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000407 res = tftp_option_get(&buf[2], len-2,
Glenn L McGrathad117d82001-10-05 04:40:37 +0000408 "blksize");
409
410 if (res) {
Glenn L McGrathfad90db2002-12-09 21:05:40 +0000411 int blksize = atoi(res);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000412
Glenn L McGrathfad90db2002-12-09 21:05:40 +0000413 if (tftp_blocksize_check(blksize,
Glenn L McGrathad117d82001-10-05 04:40:37 +0000414 tftp_bufsize - 4)) {
415
416 if (cmd_put) {
417 opcode = TFTP_DATA;
418 }
419 else {
420 opcode = TFTP_ACK;
421 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000422#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000423 fprintf(stderr, "using blksize %u\n", blksize);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000424#endif
Glenn L McGrath9bf9f1e2002-12-09 21:52:29 +0000425 tftp_bufsize = blksize + 4;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000426 block_nr = 0;
427 continue;
428 }
429 }
430 /* FIXME:
431 * we should send ERROR 8 */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000432 bb_error_msg("bad server option");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000433 break;
434 }
435
Manuel Novoa III cad53642003-03-19 09:13:01 +0000436 bb_error_msg("warning: blksize not supported by server"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000437 " - reverting to 512");
438
439 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
440 }
441#endif
442
443 if (cmd_get && (opcode == TFTP_DATA)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000444
445 if (tmp == block_nr) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000446
Eric Andersen4872ed92004-06-22 10:18:30 +0000447 len = bb_full_write(localfd, &buf[4], len - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000448
449 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000450 bb_perror_msg("write");
Mark Whitley450736c2001-03-02 19:08:50 +0000451 break;
452 }
453
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000454 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000455 finished++;
456 }
457
Glenn L McGrathad117d82001-10-05 04:40:37 +0000458 opcode = TFTP_ACK;
Mark Whitley450736c2001-03-02 19:08:50 +0000459 continue;
460 }
Rob Landleyf3133c42005-06-07 02:40:39 +0000461 /* in case the last ack disappeared into the ether */
462 if ( tmp == (block_nr - 1) ) {
463 --block_nr;
464 opcode = TFTP_ACK;
465 continue;
Paul Fox1d4c88c2005-07-20 19:49:15 +0000466 } else if (tmp + 1 == block_nr) {
467 /* Server lost our TFTP_ACK. Resend it */
468 block_nr = tmp;
469 opcode = TFTP_ACK;
470 continue;
Rob Landleyf3133c42005-06-07 02:40:39 +0000471 }
Mark Whitley450736c2001-03-02 19:08:50 +0000472 }
473
Glenn L McGrathad117d82001-10-05 04:40:37 +0000474 if (cmd_put && (opcode == TFTP_ACK)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000475
Glenn L McGrathc6997782004-02-22 03:33:53 +0000476 if (tmp == (unsigned short)(block_nr - 1)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000477 if (finished) {
478 break;
479 }
480
Glenn L McGrathad117d82001-10-05 04:40:37 +0000481 opcode = TFTP_DATA;
Mark Whitley450736c2001-03-02 19:08:50 +0000482 continue;
483 }
484 }
Mark Whitley450736c2001-03-02 19:08:50 +0000485 }
486
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000487#ifdef CONFIG_FEATURE_CLEAN_UP
Mark Whitley450736c2001-03-02 19:08:50 +0000488 close(socketfd);
489
Eric Andersen5ad22c92002-10-25 12:14:02 +0000490 free(buf);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000491#endif
492
Mark Whitley8bb7df42001-03-06 20:58:48 +0000493 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000494}
495
496int tftp_main(int argc, char **argv)
497{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000498 struct hostent *host = NULL;
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000499 const char *localfile = NULL;
500 const char *remotefile = NULL;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000501 int port;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000502 int cmd = 0;
503 int fd = -1;
504 int flags = 0;
505 int opt;
506 int result;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000507 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
Mark Whitley450736c2001-03-02 19:08:50 +0000508
Glenn L McGrathad117d82001-10-05 04:40:37 +0000509 /* figure out what to pass to getopt */
510
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000511#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000512#define BS "b:"
513#else
514#define BS
515#endif
516
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000517#ifdef CONFIG_FEATURE_TFTP_GET
Glenn L McGrathad117d82001-10-05 04:40:37 +0000518#define GET "g"
519#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000520#define GET
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"
525#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000526#define PUT
Glenn L McGrathad117d82001-10-05 04:40:37 +0000527#endif
528
529 while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000530 switch (opt) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000531#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000532 case 'b':
533 blocksize = atoi(optarg);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000534 if (!tftp_blocksize_check(blocksize, 0)) {
535 return EXIT_FAILURE;
536 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000537 break;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000538#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000539#ifdef CONFIG_FEATURE_TFTP_GET
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000540 case 'g':
541 cmd = tftp_cmd_get;
Glenn L McGrathd5d5e542004-02-26 07:47:20 +0000542 flags = O_WRONLY | O_CREAT | O_TRUNC;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000543 break;
544#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000545#ifdef CONFIG_FEATURE_TFTP_PUT
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000546 case 'p':
547 cmd = tftp_cmd_put;
Mark Whitley450736c2001-03-02 19:08:50 +0000548 flags = O_RDONLY;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000549 break;
550#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000551 case 'l':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000552 localfile = optarg;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000553 break;
554 case 'r':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000555 remotefile = optarg;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000556 break;
Mark Whitley450736c2001-03-02 19:08:50 +0000557 }
Mark Whitley450736c2001-03-02 19:08:50 +0000558 }
559
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000560 if ((cmd == 0) || (optind == argc)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000561 bb_show_usage();
Mark Whitley450736c2001-03-02 19:08:50 +0000562 }
Eric Andersen744ec1d2002-04-15 07:40:27 +0000563 if(localfile && strcmp(localfile, "-") == 0) {
Eric Andersen7829b8d2002-09-10 06:03:31 +0000564 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
Eric Andersena66a43e2002-04-13 09:30:25 +0000565 }
Eric Andersen744ec1d2002-04-15 07:40:27 +0000566 if(localfile == NULL)
567 localfile = remotefile;
568 if(remotefile == NULL)
569 remotefile = localfile;
Eric Andersena66a43e2002-04-13 09:30:25 +0000570 if (fd==-1) {
571 fd = open(localfile, flags, 0644);
572 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000573 if (fd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000574 bb_perror_msg_and_die("local file");
Mark Whitley450736c2001-03-02 19:08:50 +0000575 }
576
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}