blob: 3c947318bc89f3def3e51fd7ab706eaf7a87802f [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 Andersen76fa8ea2001-08-20 17:47:49 +000074const int tftp_cmd_get = 1;
75const int tftp_cmd_put = 2;
76
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
323 switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
324 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 }
461 }
462
Glenn L McGrathad117d82001-10-05 04:40:37 +0000463 if (cmd_put && (opcode == TFTP_ACK)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000464
Glenn L McGrathc6997782004-02-22 03:33:53 +0000465 if (tmp == (unsigned short)(block_nr - 1)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000466 if (finished) {
467 break;
468 }
469
Glenn L McGrathad117d82001-10-05 04:40:37 +0000470 opcode = TFTP_DATA;
Mark Whitley450736c2001-03-02 19:08:50 +0000471 continue;
472 }
473 }
Mark Whitley450736c2001-03-02 19:08:50 +0000474 }
475
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000476#ifdef CONFIG_FEATURE_CLEAN_UP
Mark Whitley450736c2001-03-02 19:08:50 +0000477 close(socketfd);
478
Eric Andersen5ad22c92002-10-25 12:14:02 +0000479 free(buf);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000480#endif
481
Mark Whitley8bb7df42001-03-06 20:58:48 +0000482 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000483}
484
485int tftp_main(int argc, char **argv)
486{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000487 struct hostent *host = NULL;
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000488 const char *localfile = NULL;
489 const char *remotefile = NULL;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000490 int port;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000491 int cmd = 0;
492 int fd = -1;
493 int flags = 0;
494 int opt;
495 int result;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000496 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
Mark Whitley450736c2001-03-02 19:08:50 +0000497
Glenn L McGrathad117d82001-10-05 04:40:37 +0000498 /* figure out what to pass to getopt */
499
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000500#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000501#define BS "b:"
502#else
503#define BS
504#endif
505
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000506#ifdef CONFIG_FEATURE_TFTP_GET
Glenn L McGrathad117d82001-10-05 04:40:37 +0000507#define GET "g"
508#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000509#define GET
Glenn L McGrathad117d82001-10-05 04:40:37 +0000510#endif
511
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000512#ifdef CONFIG_FEATURE_TFTP_PUT
Glenn L McGrathad117d82001-10-05 04:40:37 +0000513#define PUT "p"
514#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000515#define PUT
Glenn L McGrathad117d82001-10-05 04:40:37 +0000516#endif
517
518 while ((opt = getopt(argc, argv, BS GET PUT "l:r:")) != -1) {
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000519 switch (opt) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000520#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000521 case 'b':
522 blocksize = atoi(optarg);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000523 if (!tftp_blocksize_check(blocksize, 0)) {
524 return EXIT_FAILURE;
525 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000526 break;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000527#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000528#ifdef CONFIG_FEATURE_TFTP_GET
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000529 case 'g':
530 cmd = tftp_cmd_get;
Glenn L McGrathd5d5e542004-02-26 07:47:20 +0000531 flags = O_WRONLY | O_CREAT | O_TRUNC;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000532 break;
533#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000534#ifdef CONFIG_FEATURE_TFTP_PUT
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000535 case 'p':
536 cmd = tftp_cmd_put;
Mark Whitley450736c2001-03-02 19:08:50 +0000537 flags = O_RDONLY;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000538 break;
539#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000540 case 'l':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000541 localfile = optarg;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000542 break;
543 case 'r':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000544 remotefile = optarg;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000545 break;
Mark Whitley450736c2001-03-02 19:08:50 +0000546 }
Mark Whitley450736c2001-03-02 19:08:50 +0000547 }
548
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000549 if ((cmd == 0) || (optind == argc)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000550 bb_show_usage();
Mark Whitley450736c2001-03-02 19:08:50 +0000551 }
Eric Andersen744ec1d2002-04-15 07:40:27 +0000552 if(localfile && strcmp(localfile, "-") == 0) {
Eric Andersen7829b8d2002-09-10 06:03:31 +0000553 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
Eric Andersena66a43e2002-04-13 09:30:25 +0000554 }
Eric Andersen744ec1d2002-04-15 07:40:27 +0000555 if(localfile == NULL)
556 localfile = remotefile;
557 if(remotefile == NULL)
558 remotefile = localfile;
Eric Andersena66a43e2002-04-13 09:30:25 +0000559 if (fd==-1) {
560 fd = open(localfile, flags, 0644);
561 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000562 if (fd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000563 bb_perror_msg_and_die("local file");
Mark Whitley450736c2001-03-02 19:08:50 +0000564 }
565
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000566 host = xgethostbyname(argv[optind]);
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000567 port = bb_lookup_port(argv[optind + 1], "udp", 69);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000568
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000569#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000570 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000571 "localfile \"%s\".\n",
572 inet_ntoa(*((struct in_addr *) host->h_addr)),
573 remotefile, localfile);
574#endif
575
576 result = tftp(cmd, host, remotefile, fd, port, blocksize);
Mark Whitley450736c2001-03-02 19:08:50 +0000577
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000578#ifdef CONFIG_FEATURE_CLEAN_UP
Eric Andersen70060d22004-03-27 10:02:48 +0000579 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
Eric Andersena66a43e2002-04-13 09:30:25 +0000580 close(fd);
581 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000582#endif
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000583 return(result);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000584}