blob: e1b6f403c74575cbc3a96388f395a8fb3376bd5b [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
41/* RFC2348 says between 8 and 65464 */
42#define TFTP_OCTECTS_MIN 8
43#define TFTP_OCTECTS_MAX 65464
44
45static const char * const MODE_OCTET = "octet";
46#define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/
47
48static const char * const OPTION_BLOCKSIZE = "blksize";
49#define OPTION_BLOCKSIZE_LEN 8 /* sizeof(OPTION_BLOCKSIZE) */
Glenn L McGrathad117d82001-10-05 04:40:37 +000050
51/* opcodes we support */
Glenn L McGrathad117d82001-10-05 04:40:37 +000052#define TFTP_RRQ 1
53#define TFTP_WRQ 2
54#define TFTP_DATA 3
55#define TFTP_ACK 4
56#define TFTP_ERROR 5
57#define TFTP_OACK 6
58
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000059static const char *const tftp_bb_error_msg[] = {
Mark Whitley450736c2001-03-02 19:08:50 +000060 "Undefined error",
61 "File not found",
62 "Access violation",
63 "Disk full or allocation error",
64 "Illegal TFTP operation",
65 "Unknown transfer ID",
66 "File already exists",
67 "No such user"
68};
69
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000070#define tftp_cmd_get ENABLE_FEATURE_TFTP_GET
71
72#if ENABLE_FEATURE_TFTP_PUT
73# define tftp_cmd_put (tftp_cmd_get+ENABLE_FEATURE_TFTP_PUT)
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +000074#else
75# define tftp_cmd_put 0
76#endif
77
Eric Andersen76fa8ea2001-08-20 17:47:49 +000078
Eric Andersenbdfd0d72001-10-24 05:00:29 +000079#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +000080
Eric Andersenc7bda1c2004-03-15 08:29:22 +000081static int tftp_blocksize_check(int blocksize, int bufsize)
Glenn L McGrathad117d82001-10-05 04:40:37 +000082{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000083 /* Check if the blocksize is valid:
Glenn L McGrathad117d82001-10-05 04:40:37 +000084 * RFC2348 says between 8 and 65464,
85 * but our implementation makes it impossible
86 * to use blocksizes smaller than 22 octets.
87 */
88
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000089 if ((bufsize && (blocksize > bufsize)) ||
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000090 (blocksize < TFTP_OCTECTS_MIN) || (blocksize > TFTP_OCTECTS_MAX)) {
91 bb_error_msg("bad blocksize");
92 return 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +000093 }
94
95 return blocksize;
96}
97
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +000098static char *tftp_option_get(char *buf, int len, const char const *option)
Glenn L McGrathad117d82001-10-05 04:40:37 +000099{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000100 int opt_val = 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000101 int opt_found = 0;
102 int k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000103
Glenn L McGrathad117d82001-10-05 04:40:37 +0000104 while (len > 0) {
105
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000106 /* Make sure the options are terminated correctly */
Glenn L McGrathad117d82001-10-05 04:40:37 +0000107
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000108 for (k = 0; k < len; k++) {
109 if (buf[k] == '\0') {
110 break;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000111 }
112 }
113
114 if (k >= len) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000115 break;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000116 }
117
118 if (opt_val == 0) {
119 if (strcasecmp(buf, option) == 0) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000120 opt_found = 1;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000121 }
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000122 } else {
123 if (opt_found) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000124 return buf;
125 }
126 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000127
Glenn L McGrathad117d82001-10-05 04:40:37 +0000128 k++;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000129
Glenn L McGrathad117d82001-10-05 04:40:37 +0000130 buf += k;
131 len -= k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000132
Glenn L McGrathad117d82001-10-05 04:40:37 +0000133 opt_val ^= 1;
134 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000135
Glenn L McGrathad117d82001-10-05 04:40:37 +0000136 return NULL;
137}
138
139#endif
140
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000141static int tftp(const int cmd, const struct hostent *host,
142 const char *remotefile, const int localfd,
143 const unsigned short port, int tftp_bufsize)
Mark Whitley450736c2001-03-02 19:08:50 +0000144{
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000145#define cmd_get cmd & tftp_cmd_get
146#define cmd_put cmd & tftp_cmd_put
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;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000152 int socketfd;
Bernhard Reutner-Fischer62f98562006-06-10 14:32:56 +0000153 int len;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000154 int opcode = 0;
155 int finished = 0;
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000156 int timeout = TFTP_NUM_RETRIES;
Glenn L McGrathc6997782004-02-22 03:33:53 +0000157 unsigned short block_nr = 1;
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000158 unsigned short tmp;
159 char *cp;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000160
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000161 USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
Glenn L McGrathad117d82001-10-05 04:40:37 +0000162
Eric Andersen744a1942001-11-10 11:16:39 +0000163 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
164 * size varies meaning BUFFERS_GO_ON_STACK would fail */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000165 char *buf=xmalloc(tftp_bufsize += 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000166
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000167 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
168 /* need to unlink the localfile, so don't use bb_xsocket here. */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000169 bb_perror_msg("socket");
Mark Whitley8bb7df42001-03-06 20:58:48 +0000170 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000171 }
172
173 len = sizeof(sa);
174
175 memset(&sa, 0, len);
Bernhard Reutner-Fischer3b1936d2006-06-10 11:39:09 +0000176 bb_xbind(socketfd, (struct sockaddr *)&sa, len);
Mark Whitley450736c2001-03-02 19:08:50 +0000177
178 sa.sin_family = host->h_addrtype;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000179 sa.sin_port = port;
Mark Whitley450736c2001-03-02 19:08:50 +0000180 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
181 sizeof(sa.sin_addr));
182
183 /* build opcode */
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 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000187 if (cmd_put) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000188 opcode = TFTP_WRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000189 }
190
191 while (1) {
192
Mark Whitley450736c2001-03-02 19:08:50 +0000193 cp = buf;
194
Glenn L McGrathad117d82001-10-05 04:40:37 +0000195 /* first create the opcode part */
Mark Whitley450736c2001-03-02 19:08:50 +0000196 *((unsigned short *) cp) = htons(opcode);
Mark Whitley450736c2001-03-02 19:08:50 +0000197 cp += 2;
198
199 /* add filename and mode */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000200 if (((cmd_get) && (opcode == TFTP_RRQ)) ||
201 ((cmd_put) && (opcode == TFTP_WRQ)))
202 {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000203 int too_long = 0;
Mark Whitley450736c2001-03-02 19:08:50 +0000204
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000205 /* see if the filename fits into buf
206 * and fill in packet. */
Glenn L McGrathad117d82001-10-05 04:40:37 +0000207 len = strlen(remotefile) + 1;
208
209 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000210 too_long = 1;
211 } else {
212 safe_strncpy(cp, remotefile, len);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000213 cp += len;
214 }
215
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000216 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) {
217 bb_error_msg("remote filename too long");
Mark Whitley450736c2001-03-02 19:08:50 +0000218 break;
219 }
220
Glenn L McGrathad117d82001-10-05 04:40:37 +0000221 /* add "mode" part of the package */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000222 memcpy(cp, MODE_OCTET, MODE_OCTET_LEN);
223 cp += MODE_OCTET_LEN;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000224
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000225#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000226
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000227 len = tftp_bufsize - 4; /* data block size */
Glenn L McGrathad117d82001-10-05 04:40:37 +0000228
229 if (len != TFTP_BLOCKSIZE_DEFAULT) {
230
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000231 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
232 bb_error_msg("remote filename too long");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000233 break;
234 }
235
236 /* add "blksize" + number of blocks */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000237 memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN);
238 cp += OPTION_BLOCKSIZE_LEN;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000239 cp += snprintf(cp, 6, "%d", len) + 1;
240
241 want_option_ack = 1;
242 }
243#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000244 }
245
246 /* add ack and data */
247
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000248 if (((cmd_get) && (opcode == TFTP_ACK)) ||
249 ((cmd_put) && (opcode == TFTP_DATA))) {
Mark Whitley450736c2001-03-02 19:08:50 +0000250
251 *((unsigned short *) cp) = htons(block_nr);
252
253 cp += 2;
254
255 block_nr++;
256
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000257 if ((cmd_put) && (opcode == TFTP_DATA)) {
Eric Andersen4872ed92004-06-22 10:18:30 +0000258 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000259
260 if (len < 0) {
Bernhard Reutner-Fischer1b9d7c92006-06-03 22:45:37 +0000261 bb_perror_msg(bb_msg_read_error);
Mark Whitley450736c2001-03-02 19:08:50 +0000262 break;
263 }
264
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000265 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000266 finished++;
267 }
268
269 cp += len;
Mark Whitley450736c2001-03-02 19:08:50 +0000270 }
271 }
272
273
274 /* send packet */
275
276
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000277 timeout = TFTP_NUM_RETRIES; /* re-initialize */
Mark Whitley450736c2001-03-02 19:08:50 +0000278 do {
279
280 len = cp - buf;
281
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000282#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000283 fprintf(stderr, "sending %u bytes\n", len);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000284 for (cp = buf; cp < &buf[len]; cp++)
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000285 fprintf(stderr, "%02x ", (unsigned char) *cp);
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000286 fprintf(stderr, "\n");
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000287#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000288 if (sendto(socketfd, buf, len, 0,
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000289 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000290 bb_perror_msg("send");
Mark Whitley450736c2001-03-02 19:08:50 +0000291 len = -1;
292 break;
293 }
294
295
Eric Andersenb99aec02003-07-30 07:16:39 +0000296 if (finished && (opcode == TFTP_ACK)) {
Glenn L McGrath0f182712002-12-19 20:16:22 +0000297 break;
298 }
Mark Whitley450736c2001-03-02 19:08:50 +0000299
Glenn L McGrath0f182712002-12-19 20:16:22 +0000300 /* receive packet */
Mark Whitley450736c2001-03-02 19:08:50 +0000301
302 memset(&from, 0, sizeof(from));
303 fromlen = sizeof(from);
304
Glenn L McGrathad117d82001-10-05 04:40:37 +0000305 tv.tv_sec = TFTP_TIMEOUT;
Mark Whitley450736c2001-03-02 19:08:50 +0000306 tv.tv_usec = 0;
307
308 FD_ZERO(&rfds);
309 FD_SET(socketfd, &rfds);
310
Bernhard Reutner-Fischer62f98562006-06-10 14:32:56 +0000311 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
312 case 1:
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000313 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000314 (struct sockaddr *) &from, &fromlen);
Mark Whitley450736c2001-03-02 19:08:50 +0000315
316 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000317 bb_perror_msg("recvfrom");
Mark Whitley450736c2001-03-02 19:08:50 +0000318 break;
319 }
320
321 timeout = 0;
322
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000323 if (sa.sin_port == port) {
Mark Whitley450736c2001-03-02 19:08:50 +0000324 sa.sin_port = from.sin_port;
Mark Whitley450736c2001-03-02 19:08:50 +0000325 }
Mark Whitley450736c2001-03-02 19:08:50 +0000326 if (sa.sin_port == from.sin_port) {
327 break;
328 }
329
330 /* fall-through for bad packets! */
331 /* discard the packet - treat as timeout */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000332 timeout = TFTP_NUM_RETRIES;
Bernhard Reutner-Fischer62f98562006-06-10 14:32:56 +0000333 case 0:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000334 bb_error_msg("timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000335
Eric Andersenb99aec02003-07-30 07:16:39 +0000336 timeout--;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000337 if (timeout == 0) {
Mark Whitley450736c2001-03-02 19:08:50 +0000338 len = -1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000339 bb_error_msg("last timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000340 }
341 break;
Bernhard Reutner-Fischer62f98562006-06-10 14:32:56 +0000342 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000343 bb_perror_msg("select");
Mark Whitley450736c2001-03-02 19:08:50 +0000344 len = -1;
345 }
346
347 } while (timeout && (len >= 0));
348
Glenn L McGrath0f182712002-12-19 20:16:22 +0000349 if ((finished) || (len < 0)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000350 break;
351 }
352
353 /* process received packet */
354
Mark Whitley450736c2001-03-02 19:08:50 +0000355 opcode = ntohs(*((unsigned short *) buf));
356 tmp = ntohs(*((unsigned short *) &buf[2]));
357
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000358#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000359 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000360#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000361
Glenn L McGrathad117d82001-10-05 04:40:37 +0000362 if (opcode == TFTP_ERROR) {
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000363 const char *msg = NULL;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000364
365 if (buf[4] != '\0') {
366 msg = &buf[4];
367 buf[tftp_bufsize - 1] = '\0';
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000368 } else if (tmp < (sizeof(tftp_bb_error_msg)
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000369 / sizeof(char *))) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000370
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000371 msg = tftp_bb_error_msg[tmp];
Glenn L McGrathad117d82001-10-05 04:40:37 +0000372 }
373
374 if (msg) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000375 bb_error_msg("server says: %s", msg);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000376 }
377
378 break;
379 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000380#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000381 if (want_option_ack) {
382
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000383 want_option_ack = 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000384
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000385 if (opcode == TFTP_OACK) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000386
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000387 /* server seems to support options */
Glenn L McGrathad117d82001-10-05 04:40:37 +0000388
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000389 char *res;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000390
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000391 res = tftp_option_get(&buf[2], len - 2, OPTION_BLOCKSIZE);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000392
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000393 if (res) {
394 int blksize = atoi(res);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000395
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000396 if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000397
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000398 if (cmd_put) {
399 opcode = TFTP_DATA;
400 } else {
401 opcode = TFTP_ACK;
402 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000403#ifdef CONFIG_FEATURE_TFTP_DEBUG
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000404 fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE,
405 blksize);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000406#endif
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000407 tftp_bufsize = blksize + 4;
408 block_nr = 0;
409 continue;
410 }
411 }
412 /* FIXME:
413 * we should send ERROR 8 */
414 bb_error_msg("bad server option");
415 break;
416 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000417
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000418 bb_error_msg("warning: blksize not supported by server"
419 " - reverting to 512");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000420
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000421 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000422 }
423#endif
424
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000425 if ((cmd_get) && (opcode == TFTP_DATA)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000426
427 if (tmp == block_nr) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000428
Eric Andersen4872ed92004-06-22 10:18:30 +0000429 len = bb_full_write(localfd, &buf[4], len - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000430
431 if (len < 0) {
Bernhard Reutner-Fischer1b9d7c92006-06-03 22:45:37 +0000432 bb_perror_msg(bb_msg_write_error);
Mark Whitley450736c2001-03-02 19:08:50 +0000433 break;
434 }
435
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000436 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000437 finished++;
438 }
439
Glenn L McGrathad117d82001-10-05 04:40:37 +0000440 opcode = TFTP_ACK;
Mark Whitley450736c2001-03-02 19:08:50 +0000441 continue;
442 }
Rob Landleyf3133c42005-06-07 02:40:39 +0000443 /* in case the last ack disappeared into the ether */
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000444 if (tmp == (block_nr - 1)) {
Rob Landleyf3133c42005-06-07 02:40:39 +0000445 --block_nr;
446 opcode = TFTP_ACK;
447 continue;
Paul Fox1d4c88c2005-07-20 19:49:15 +0000448 } else if (tmp + 1 == block_nr) {
449 /* Server lost our TFTP_ACK. Resend it */
450 block_nr = tmp;
451 opcode = TFTP_ACK;
452 continue;
Rob Landleyf3133c42005-06-07 02:40:39 +0000453 }
Mark Whitley450736c2001-03-02 19:08:50 +0000454 }
455
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000456 if ((cmd_put) && (opcode == TFTP_ACK)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000457
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000458 if (tmp == (unsigned short) (block_nr - 1)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000459 if (finished) {
460 break;
461 }
462
Glenn L McGrathad117d82001-10-05 04:40:37 +0000463 opcode = TFTP_DATA;
Mark Whitley450736c2001-03-02 19:08:50 +0000464 continue;
465 }
466 }
Mark Whitley450736c2001-03-02 19:08:50 +0000467 }
468
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000469#ifdef CONFIG_FEATURE_CLEAN_UP
Mark Whitley450736c2001-03-02 19:08:50 +0000470 close(socketfd);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000471 free(buf);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000472#endif
473
Mark Whitley8bb7df42001-03-06 20:58:48 +0000474 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000475}
476
477int tftp_main(int argc, char **argv)
478{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000479 struct hostent *host = NULL;
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000480 const char *localfile = NULL;
481 const char *remotefile = NULL;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000482 int port;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000483 int cmd = 0;
484 int fd = -1;
485 int flags = 0;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000486 int result;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000487 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
Mark Whitley450736c2001-03-02 19:08:50 +0000488
Glenn L McGrathad117d82001-10-05 04:40:37 +0000489 /* figure out what to pass to getopt */
490
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000491#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000492 char *sblocksize = NULL;
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000493
Glenn L McGrathad117d82001-10-05 04:40:37 +0000494#define BS "b:"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000495#define BS_ARG , &sblocksize
Glenn L McGrathad117d82001-10-05 04:40:37 +0000496#else
497#define BS
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000498#define BS_ARG
Glenn L McGrathad117d82001-10-05 04:40:37 +0000499#endif
500
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000501#ifdef CONFIG_FEATURE_TFTP_GET
Glenn L McGrathad117d82001-10-05 04:40:37 +0000502#define GET "g"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000503#define GET_COMPL ":g"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000504#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000505#define GET
Bernhard Reutner-Fischer886f6af2006-04-05 16:47:02 +0000506#define GET_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000507#endif
508
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000509#ifdef CONFIG_FEATURE_TFTP_PUT
Glenn L McGrathad117d82001-10-05 04:40:37 +0000510#define PUT "p"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000511#define PUT_COMPL ":p"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000512#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000513#define PUT
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000514#define PUT_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000515#endif
516
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000517#if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
518 bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
519#elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
520 bb_opt_complementally = GET_COMPL PUT_COMPL;
521#else
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000522#error "Either CONFIG_FEATURE_TFTP_GET or CONFIG_FEATURE_TFTP_PUT must be defined"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000523#endif
524
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000525
526 cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000527 &localfile, &remotefile BS_ARG);
528
529 cmd &= (tftp_cmd_get | tftp_cmd_put);
530#ifdef CONFIG_FEATURE_TFTP_GET
531 if (cmd == tftp_cmd_get)
532 flags = O_WRONLY | O_CREAT | O_TRUNC;
533#endif
534#ifdef CONFIG_FEATURE_TFTP_PUT
535 if (cmd == tftp_cmd_put)
536 flags = O_RDONLY;
537#endif
538
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000539#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000540 if (sblocksize) {
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000541 blocksize = atoi(sblocksize);
542 if (!tftp_blocksize_check(blocksize, 0)) {
543 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000544 }
Mark Whitley450736c2001-03-02 19:08:50 +0000545 }
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000546#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000547
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000548 if (localfile == NULL)
549 localfile = remotefile;
550 if (remotefile == NULL)
551 remotefile = localfile;
552 if ((localfile == NULL && remotefile == NULL) || (argv[optind] == NULL))
553 bb_show_usage();
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000554
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000555 if (localfile == NULL || strcmp(localfile, "-") == 0) {
556 fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO;
557 } else {
558 fd = open(localfile, flags, 0644); /* fail below */
Eric Andersena66a43e2002-04-13 09:30:25 +0000559 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000560 if (fd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000561 bb_perror_msg_and_die("local file");
Mark Whitley450736c2001-03-02 19:08:50 +0000562 }
563
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000564 host = xgethostbyname(argv[optind]);
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000565 port = bb_lookup_port(argv[optind + 1], "udp", 69);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000566
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000567#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000568 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000569 "localfile \"%s\".\n",
570 inet_ntoa(*((struct in_addr *) host->h_addr)),
571 remotefile, localfile);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000572#endif
573
574 result = tftp(cmd, host, remotefile, fd, port, blocksize);
Mark Whitley450736c2001-03-02 19:08:50 +0000575
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000576#ifdef CONFIG_FEATURE_CLEAN_UP
Eric Andersen70060d22004-03-27 10:02:48 +0000577 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000578 close(fd);
Eric Andersena66a43e2002-04-13 09:30:25 +0000579 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000580#endif
Bernhard Reutner-Fischerb25f98a2006-06-10 14:15:03 +0000581 if (cmd == tftp_cmd_get && result != EXIT_SUCCESS)
582 unlink(localfile);
583 return (result);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000584}