blob: b0572c89009c676088b4db32733a3f315eefa5da [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
Mark Whitley450736c2001-03-02 19:08:50 +000036
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000037#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
38#define TFTP_TIMEOUT 5 /* seconds */
39#define TFTP_NUM_RETRIES 5 /* number of retries */
40
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000041static const char * const MODE_OCTET = "octet";
42#define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/
43
44static const char * const OPTION_BLOCKSIZE = "blksize";
45#define OPTION_BLOCKSIZE_LEN 8 /* sizeof(OPTION_BLOCKSIZE) */
Glenn L McGrathad117d82001-10-05 04:40:37 +000046
47/* opcodes we support */
Glenn L McGrathad117d82001-10-05 04:40:37 +000048#define TFTP_RRQ 1
49#define TFTP_WRQ 2
50#define TFTP_DATA 3
51#define TFTP_ACK 4
52#define TFTP_ERROR 5
53#define TFTP_OACK 6
54
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000055static const char *const tftp_bb_error_msg[] = {
Mark Whitley450736c2001-03-02 19:08:50 +000056 "Undefined error",
57 "File not found",
58 "Access violation",
59 "Disk full or allocation error",
60 "Illegal TFTP operation",
61 "Unknown transfer ID",
62 "File already exists",
63 "No such user"
64};
65
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000066#define tftp_cmd_get ENABLE_FEATURE_TFTP_GET
67
68#if ENABLE_FEATURE_TFTP_PUT
69# define tftp_cmd_put (tftp_cmd_get+ENABLE_FEATURE_TFTP_PUT)
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +000070#else
71# define tftp_cmd_put 0
72#endif
73
Eric Andersen76fa8ea2001-08-20 17:47:49 +000074
Eric Andersenbdfd0d72001-10-24 05:00:29 +000075#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +000076
Eric Andersenc7bda1c2004-03-15 08:29:22 +000077static int tftp_blocksize_check(int blocksize, int bufsize)
Glenn L McGrathad117d82001-10-05 04:40:37 +000078{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000079 /* Check if the blocksize is valid:
Glenn L McGrathad117d82001-10-05 04:40:37 +000080 * RFC2348 says between 8 and 65464,
81 * but our implementation makes it impossible
82 * to use blocksizes smaller than 22 octets.
83 */
84
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000085 if ((bufsize && (blocksize > bufsize)) ||
Rob Landley14d70652006-06-18 15:23:13 +000086 (blocksize < 8) || (blocksize > 65564)) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000087 bb_error_msg("bad blocksize");
88 return 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +000089 }
90
91 return blocksize;
92}
93
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +000094static char *tftp_option_get(char *buf, int len, const char * const option)
Glenn L McGrathad117d82001-10-05 04:40:37 +000095{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000096 int opt_val = 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +000097 int opt_found = 0;
98 int k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000099
Glenn L McGrathad117d82001-10-05 04:40:37 +0000100 while (len > 0) {
101
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000102 /* Make sure the options are terminated correctly */
Glenn L McGrathad117d82001-10-05 04:40:37 +0000103
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000104 for (k = 0; k < len; k++) {
105 if (buf[k] == '\0') {
106 break;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000107 }
108 }
109
110 if (k >= len) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000111 break;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000112 }
113
114 if (opt_val == 0) {
115 if (strcasecmp(buf, option) == 0) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000116 opt_found = 1;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000117 }
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000118 } else {
119 if (opt_found) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000120 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
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000137static int tftp(const int cmd, const struct hostent *host,
138 const char *remotefile, const int localfd,
139 const unsigned short port, int tftp_bufsize)
Mark Whitley450736c2001-03-02 19:08:50 +0000140{
141 struct sockaddr_in sa;
Mark Whitley450736c2001-03-02 19:08:50 +0000142 struct sockaddr_in from;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000143 struct timeval tv;
Mark Whitley450736c2001-03-02 19:08:50 +0000144 socklen_t fromlen;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000145 fd_set rfds;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000146 int socketfd;
Bernhard Reutner-Fischer62f98562006-06-10 14:32:56 +0000147 int len;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000148 int opcode = 0;
149 int finished = 0;
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000150 int timeout = TFTP_NUM_RETRIES;
Glenn L McGrathc6997782004-02-22 03:33:53 +0000151 unsigned short block_nr = 1;
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000152 unsigned short tmp;
153 char *cp;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000154
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000155 USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
Glenn L McGrathad117d82001-10-05 04:40:37 +0000156
Eric Andersen744a1942001-11-10 11:16:39 +0000157 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
158 * size varies meaning BUFFERS_GO_ON_STACK would fail */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000159 char *buf=xmalloc(tftp_bufsize += 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000160
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000161 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
162 /* need to unlink the localfile, so don't use bb_xsocket here. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000163 bb_perror_msg("socket");
Mark Whitley8bb7df42001-03-06 20:58:48 +0000164 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000165 }
166
167 len = sizeof(sa);
168
169 memset(&sa, 0, len);
Bernhard Reutner-Fischer3b1936d2006-06-10 11:39:09 +0000170 bb_xbind(socketfd, (struct sockaddr *)&sa, len);
Mark Whitley450736c2001-03-02 19:08:50 +0000171
172 sa.sin_family = host->h_addrtype;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000173 sa.sin_port = port;
Mark Whitley450736c2001-03-02 19:08:50 +0000174 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
175 sizeof(sa.sin_addr));
176
177 /* build opcode */
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000178 if (cmd & tftp_cmd_get) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000179 opcode = TFTP_RRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000180 }
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000181 if (cmd & tftp_cmd_put) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000182 opcode = TFTP_WRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000183 }
184
185 while (1) {
186
Mark Whitley450736c2001-03-02 19:08:50 +0000187 cp = buf;
188
Glenn L McGrathad117d82001-10-05 04:40:37 +0000189 /* first create the opcode part */
Mark Whitley450736c2001-03-02 19:08:50 +0000190 *((unsigned short *) cp) = htons(opcode);
Mark Whitley450736c2001-03-02 19:08:50 +0000191 cp += 2;
192
193 /* add filename and mode */
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000194 if (((cmd & tftp_cmd_get) && (opcode == TFTP_RRQ)) ||
195 ((cmd & tftp_cmd_put) && (opcode == TFTP_WRQ)))
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000196 {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000197 int too_long = 0;
Mark Whitley450736c2001-03-02 19:08:50 +0000198
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000199 /* see if the filename fits into buf
200 * and fill in packet. */
Glenn L McGrathad117d82001-10-05 04:40:37 +0000201 len = strlen(remotefile) + 1;
202
203 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000204 too_long = 1;
205 } else {
206 safe_strncpy(cp, remotefile, len);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000207 cp += len;
208 }
209
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000210 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) {
211 bb_error_msg("remote filename too long");
Mark Whitley450736c2001-03-02 19:08:50 +0000212 break;
213 }
214
Glenn L McGrathad117d82001-10-05 04:40:37 +0000215 /* add "mode" part of the package */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000216 memcpy(cp, MODE_OCTET, MODE_OCTET_LEN);
217 cp += MODE_OCTET_LEN;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000218
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000219#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000220
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000221 len = tftp_bufsize - 4; /* data block size */
Glenn L McGrathad117d82001-10-05 04:40:37 +0000222
223 if (len != TFTP_BLOCKSIZE_DEFAULT) {
224
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000225 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
226 bb_error_msg("remote filename too long");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000227 break;
228 }
229
230 /* add "blksize" + number of blocks */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000231 memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN);
232 cp += OPTION_BLOCKSIZE_LEN;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000233 cp += snprintf(cp, 6, "%d", len) + 1;
234
235 want_option_ack = 1;
236 }
237#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000238 }
239
240 /* add ack and data */
241
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000242 if (((cmd & tftp_cmd_get) && (opcode == TFTP_ACK)) ||
243 ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA))) {
Mark Whitley450736c2001-03-02 19:08:50 +0000244
245 *((unsigned short *) cp) = htons(block_nr);
246
247 cp += 2;
248
249 block_nr++;
250
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000251 if ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA)) {
Eric Andersen4872ed92004-06-22 10:18:30 +0000252 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000253
254 if (len < 0) {
Bernhard Reutner-Fischer1b9d7c92006-06-03 22:45:37 +0000255 bb_perror_msg(bb_msg_read_error);
Mark Whitley450736c2001-03-02 19:08:50 +0000256 break;
257 }
258
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000259 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000260 finished++;
261 }
262
263 cp += len;
Mark Whitley450736c2001-03-02 19:08:50 +0000264 }
265 }
266
267
268 /* send packet */
269
270
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000271 timeout = TFTP_NUM_RETRIES; /* re-initialize */
Mark Whitley450736c2001-03-02 19:08:50 +0000272 do {
273
274 len = cp - buf;
275
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000276#ifdef CONFIG_DEBUG_TFTP
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000277 fprintf(stderr, "sending %u bytes\n", len);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000278 for (cp = buf; cp < &buf[len]; cp++)
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000279 fprintf(stderr, "%02x ", (unsigned char) *cp);
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000280 fprintf(stderr, "\n");
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000281#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000282 if (sendto(socketfd, buf, len, 0,
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000283 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000284 bb_perror_msg("send");
Mark Whitley450736c2001-03-02 19:08:50 +0000285 len = -1;
286 break;
287 }
288
289
Eric Andersenb99aec02003-07-30 07:16:39 +0000290 if (finished && (opcode == TFTP_ACK)) {
Glenn L McGrath0f182712002-12-19 20:16:22 +0000291 break;
292 }
Mark Whitley450736c2001-03-02 19:08:50 +0000293
Glenn L McGrath0f182712002-12-19 20:16:22 +0000294 /* receive packet */
Mark Whitley450736c2001-03-02 19:08:50 +0000295
296 memset(&from, 0, sizeof(from));
297 fromlen = sizeof(from);
298
Glenn L McGrathad117d82001-10-05 04:40:37 +0000299 tv.tv_sec = TFTP_TIMEOUT;
Mark Whitley450736c2001-03-02 19:08:50 +0000300 tv.tv_usec = 0;
301
302 FD_ZERO(&rfds);
303 FD_SET(socketfd, &rfds);
304
Bernhard Reutner-Fischer62f98562006-06-10 14:32:56 +0000305 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
306 case 1:
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000307 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000308 (struct sockaddr *) &from, &fromlen);
Mark Whitley450736c2001-03-02 19:08:50 +0000309
310 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000311 bb_perror_msg("recvfrom");
Mark Whitley450736c2001-03-02 19:08:50 +0000312 break;
313 }
314
315 timeout = 0;
316
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000317 if (sa.sin_port == port) {
Mark Whitley450736c2001-03-02 19:08:50 +0000318 sa.sin_port = from.sin_port;
Mark Whitley450736c2001-03-02 19:08:50 +0000319 }
Mark Whitley450736c2001-03-02 19:08:50 +0000320 if (sa.sin_port == from.sin_port) {
321 break;
322 }
323
324 /* fall-through for bad packets! */
325 /* discard the packet - treat as timeout */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000326 timeout = TFTP_NUM_RETRIES;
Bernhard Reutner-Fischer62f98562006-06-10 14:32:56 +0000327 case 0:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000328 bb_error_msg("timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000329
Eric Andersenb99aec02003-07-30 07:16:39 +0000330 timeout--;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000331 if (timeout == 0) {
Mark Whitley450736c2001-03-02 19:08:50 +0000332 len = -1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000333 bb_error_msg("last timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000334 }
335 break;
Bernhard Reutner-Fischer62f98562006-06-10 14:32:56 +0000336 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000337 bb_perror_msg("select");
Mark Whitley450736c2001-03-02 19:08:50 +0000338 len = -1;
339 }
340
341 } while (timeout && (len >= 0));
342
Glenn L McGrath0f182712002-12-19 20:16:22 +0000343 if ((finished) || (len < 0)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000344 break;
345 }
346
347 /* process received packet */
348
Mark Whitley450736c2001-03-02 19:08:50 +0000349 opcode = ntohs(*((unsigned short *) buf));
350 tmp = ntohs(*((unsigned short *) &buf[2]));
351
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000352#ifdef CONFIG_DEBUG_TFTP
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000353 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000354#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000355
Glenn L McGrathad117d82001-10-05 04:40:37 +0000356 if (opcode == TFTP_ERROR) {
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000357 const char *msg = NULL;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000358
359 if (buf[4] != '\0') {
360 msg = &buf[4];
361 buf[tftp_bufsize - 1] = '\0';
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000362 } else if (tmp < (sizeof(tftp_bb_error_msg)
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000363 / sizeof(char *))) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000364
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000365 msg = tftp_bb_error_msg[tmp];
Glenn L McGrathad117d82001-10-05 04:40:37 +0000366 }
367
368 if (msg) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000369 bb_error_msg("server says: %s", msg);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000370 }
371
372 break;
373 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000374#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000375 if (want_option_ack) {
376
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000377 want_option_ack = 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000378
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000379 if (opcode == TFTP_OACK) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000380
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000381 /* server seems to support options */
Glenn L McGrathad117d82001-10-05 04:40:37 +0000382
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000383 char *res;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000384
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000385 res = tftp_option_get(&buf[2], len - 2, OPTION_BLOCKSIZE);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000386
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000387 if (res) {
388 int blksize = atoi(res);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000389
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000390 if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000391
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000392 if (cmd & tftp_cmd_put) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000393 opcode = TFTP_DATA;
394 } else {
395 opcode = TFTP_ACK;
396 }
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000397#ifdef CONFIG_DEBUG_TFTP
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000398 fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE,
399 blksize);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000400#endif
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000401 tftp_bufsize = blksize + 4;
402 block_nr = 0;
403 continue;
404 }
405 }
406 /* FIXME:
407 * we should send ERROR 8 */
408 bb_error_msg("bad server option");
409 break;
410 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000411
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000412 bb_error_msg("warning: blksize not supported by server"
413 " - reverting to 512");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000414
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000415 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000416 }
417#endif
418
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000419 if ((cmd & tftp_cmd_get) && (opcode == TFTP_DATA)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000420
421 if (tmp == block_nr) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000422
Eric Andersen4872ed92004-06-22 10:18:30 +0000423 len = bb_full_write(localfd, &buf[4], len - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000424
425 if (len < 0) {
Bernhard Reutner-Fischer1b9d7c92006-06-03 22:45:37 +0000426 bb_perror_msg(bb_msg_write_error);
Mark Whitley450736c2001-03-02 19:08:50 +0000427 break;
428 }
429
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000430 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000431 finished++;
432 }
433
Glenn L McGrathad117d82001-10-05 04:40:37 +0000434 opcode = TFTP_ACK;
Mark Whitley450736c2001-03-02 19:08:50 +0000435 continue;
436 }
Rob Landleyf3133c42005-06-07 02:40:39 +0000437 /* in case the last ack disappeared into the ether */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000438 if (tmp == (block_nr - 1)) {
Rob Landleyf3133c42005-06-07 02:40:39 +0000439 --block_nr;
440 opcode = TFTP_ACK;
441 continue;
Paul Fox1d4c88c2005-07-20 19:49:15 +0000442 } else if (tmp + 1 == block_nr) {
443 /* Server lost our TFTP_ACK. Resend it */
444 block_nr = tmp;
445 opcode = TFTP_ACK;
446 continue;
Rob Landleyf3133c42005-06-07 02:40:39 +0000447 }
Mark Whitley450736c2001-03-02 19:08:50 +0000448 }
449
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000450 if ((cmd & tftp_cmd_put) && (opcode == TFTP_ACK)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000451
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000452 if (tmp == (unsigned short) (block_nr - 1)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000453 if (finished) {
454 break;
455 }
456
Glenn L McGrathad117d82001-10-05 04:40:37 +0000457 opcode = TFTP_DATA;
Mark Whitley450736c2001-03-02 19:08:50 +0000458 continue;
459 }
460 }
Mark Whitley450736c2001-03-02 19:08:50 +0000461 }
462
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000463#ifdef CONFIG_FEATURE_CLEAN_UP
Mark Whitley450736c2001-03-02 19:08:50 +0000464 close(socketfd);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000465 free(buf);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000466#endif
467
Mark Whitley8bb7df42001-03-06 20:58:48 +0000468 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000469}
470
471int tftp_main(int argc, char **argv)
472{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000473 struct hostent *host = NULL;
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000474 const char *localfile = NULL;
475 const char *remotefile = NULL;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000476 int port;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000477 int cmd = 0;
478 int fd = -1;
479 int flags = 0;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000480 int result;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000481 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
Mark Whitley450736c2001-03-02 19:08:50 +0000482
Glenn L McGrathad117d82001-10-05 04:40:37 +0000483 /* figure out what to pass to getopt */
484
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000485#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000486 char *sblocksize = NULL;
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000487
Glenn L McGrathad117d82001-10-05 04:40:37 +0000488#define BS "b:"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000489#define BS_ARG , &sblocksize
Glenn L McGrathad117d82001-10-05 04:40:37 +0000490#else
491#define BS
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000492#define BS_ARG
Glenn L McGrathad117d82001-10-05 04:40:37 +0000493#endif
494
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000495#ifdef CONFIG_FEATURE_TFTP_GET
Glenn L McGrathad117d82001-10-05 04:40:37 +0000496#define GET "g"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000497#define GET_COMPL ":g"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000498#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000499#define GET
Bernhard Reutner-Fischer886f6af2006-04-05 16:47:02 +0000500#define GET_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000501#endif
502
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000503#ifdef CONFIG_FEATURE_TFTP_PUT
Glenn L McGrathad117d82001-10-05 04:40:37 +0000504#define PUT "p"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000505#define PUT_COMPL ":p"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000506#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000507#define PUT
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000508#define PUT_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000509#endif
510
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000511#if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
512 bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
513#elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
514 bb_opt_complementally = GET_COMPL PUT_COMPL;
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000515#endif
516
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000517
518 cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000519 &localfile, &remotefile BS_ARG);
520
521 cmd &= (tftp_cmd_get | tftp_cmd_put);
522#ifdef CONFIG_FEATURE_TFTP_GET
523 if (cmd == tftp_cmd_get)
524 flags = O_WRONLY | O_CREAT | O_TRUNC;
525#endif
526#ifdef CONFIG_FEATURE_TFTP_PUT
527 if (cmd == tftp_cmd_put)
528 flags = O_RDONLY;
529#endif
530
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000531#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000532 if (sblocksize) {
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000533 blocksize = atoi(sblocksize);
534 if (!tftp_blocksize_check(blocksize, 0)) {
535 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000536 }
Mark Whitley450736c2001-03-02 19:08:50 +0000537 }
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000538#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000539
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000540 if (localfile == NULL)
541 localfile = remotefile;
542 if (remotefile == NULL)
543 remotefile = localfile;
544 if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
545 bb_show_usage();
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000546
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000547 if (localfile == NULL || strcmp(localfile, "-") == 0) {
548 fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO;
549 } else {
550 fd = open(localfile, flags, 0644); /* fail below */
Eric Andersena66a43e2002-04-13 09:30:25 +0000551 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000552 if (fd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000553 bb_perror_msg_and_die("local file");
Mark Whitley450736c2001-03-02 19:08:50 +0000554 }
555
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000556 host = xgethostbyname(argv[optind]);
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000557 port = bb_lookup_port(argv[optind + 1], "udp", 69);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000558
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000559#ifdef CONFIG_DEBUG_TFTP
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000560 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000561 "localfile \"%s\".\n",
562 inet_ntoa(*((struct in_addr *) host->h_addr)),
563 remotefile, localfile);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000564#endif
565
566 result = tftp(cmd, host, remotefile, fd, port, blocksize);
Mark Whitley450736c2001-03-02 19:08:50 +0000567
Eric Andersen70060d22004-03-27 10:02:48 +0000568 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
Bernhard Reutner-Fischer32bf1f92006-06-14 17:29:10 +0000569 if (ENABLE_FEATURE_CLEAN_UP)
570 close(fd);
571 if (cmd == tftp_cmd_get && result != EXIT_SUCCESS)
572 unlink(localfile);
Eric Andersena66a43e2002-04-13 09:30:25 +0000573 }
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000574 return (result);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000575}