blob: ddcb5225f9c4cb32437bb1a2f30820854fbca665 [file] [log] [blame]
Mark Whitley450736c2001-03-02 19:08:50 +00001/* ------------------------------------------------------------------------- */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002/* tftp.c */
3/* */
4/* A simple tftp client for busybox. */
5/* Tries to follow RFC1350. */
6/* Only "octet" mode supported. */
Glenn L McGrathad117d82001-10-05 04:40:37 +00007/* Optional blocksize negotiation (RFC2347 + RFC2348) */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00008/* */
Mark Whitley450736c2001-03-02 19:08:50 +00009/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000010/* */
11/* Parts of the code based on: */
12/* */
Mark Whitley450736c2001-03-02 19:08:50 +000013/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
14/* and Remi Lefebvre <remi@debian.org> */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000015/* */
Mark Whitley450736c2001-03-02 19:08:50 +000016/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000017/* */
Mark Whitley450736c2001-03-02 19:08:50 +000018/* 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. */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000022/* */
Mark Whitley450736c2001-03-02 19:08:50 +000023/* 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. */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000027/* */
Mark Whitley450736c2001-03-02 19:08:50 +000028/* 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 */
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000031/* */
Mark Whitley450736c2001-03-02 19:08:50 +000032/* ------------------------------------------------------------------------- */
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
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +000063static const char * const 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
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +000074#ifdef CONFIG_FEATURE_TFTP_GET
75# define tftp_cmd_get 1
76#else
77# define tftp_cmd_get 0
78#endif
79#ifdef CONFIG_FEATURE_TFTP_PUT
80# define tftp_cmd_put (tftp_cmd_get+1)
81#else
82# define tftp_cmd_put 0
83#endif
84
Eric Andersen76fa8ea2001-08-20 17:47:49 +000085
Eric Andersenbdfd0d72001-10-24 05:00:29 +000086#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +000087
Eric Andersenc7bda1c2004-03-15 08:29:22 +000088static int tftp_blocksize_check(int blocksize, int bufsize)
Glenn L McGrathad117d82001-10-05 04:40:37 +000089{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000090 /* Check if the blocksize is valid:
Glenn L McGrathad117d82001-10-05 04:40:37 +000091 * RFC2348 says between 8 and 65464,
92 * but our implementation makes it impossible
93 * to use blocksizes smaller than 22 octets.
94 */
95
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000096 if ((bufsize && (blocksize > bufsize)) ||
Glenn L McGrathad117d82001-10-05 04:40:37 +000097 (blocksize < 8) || (blocksize > 65464)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000098 bb_error_msg("bad blocksize");
Glenn L McGrathad117d82001-10-05 04:40:37 +000099 return 0;
100 }
101
102 return blocksize;
103}
104
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000105static char *tftp_option_get(char *buf, int len, char *option)
Glenn L McGrathad117d82001-10-05 04:40:37 +0000106{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000107 int opt_val = 0;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000108 int opt_found = 0;
109 int k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000110
Glenn L McGrathad117d82001-10-05 04:40:37 +0000111 while (len > 0) {
112
113 /* Make sure the options are terminated correctly */
114
115 for (k = 0; k < len; k++) {
116 if (buf[k] == '\0') {
117 break;
118 }
119 }
120
121 if (k >= len) {
122 break;
123 }
124
125 if (opt_val == 0) {
126 if (strcasecmp(buf, option) == 0) {
127 opt_found = 1;
128 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000129 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000130 else {
131 if (opt_found) {
132 return buf;
133 }
134 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000135
Glenn L McGrathad117d82001-10-05 04:40:37 +0000136 k++;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000137
Glenn L McGrathad117d82001-10-05 04:40:37 +0000138 buf += k;
139 len -= k;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000140
Glenn L McGrathad117d82001-10-05 04:40:37 +0000141 opt_val ^= 1;
142 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000143
Glenn L McGrathad117d82001-10-05 04:40:37 +0000144 return NULL;
145}
146
147#endif
148
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000149static inline int tftp(const int cmd, const struct hostent *host,
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000150 const char *remotefile, int localfd, const unsigned short port, int tftp_bufsize)
Mark Whitley450736c2001-03-02 19:08:50 +0000151{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000152 const int cmd_get = cmd & tftp_cmd_get;
153 const int cmd_put = cmd & tftp_cmd_put;
154 const int bb_tftp_num_retries = 5;
155
Mark Whitley450736c2001-03-02 19:08:50 +0000156 struct sockaddr_in sa;
Mark Whitley450736c2001-03-02 19:08:50 +0000157 struct sockaddr_in from;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000158 struct timeval tv;
Mark Whitley450736c2001-03-02 19:08:50 +0000159 socklen_t fromlen;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000160 fd_set rfds;
Mark Whitley450736c2001-03-02 19:08:50 +0000161 char *cp;
162 unsigned short tmp;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000163 int socketfd;
164 int len;
165 int opcode = 0;
166 int finished = 0;
167 int timeout = bb_tftp_num_retries;
Glenn L McGrathc6997782004-02-22 03:33:53 +0000168 unsigned short block_nr = 1;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000169
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000170#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000171 int want_option_ack = 0;
172#endif
173
Eric Andersen744a1942001-11-10 11:16:39 +0000174 /* Can't use RESERVE_CONFIG_BUFFER here since the allocation
175 * size varies meaning BUFFERS_GO_ON_STACK would fail */
176 char *buf=xmalloc(tftp_bufsize + 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000177
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000178 tftp_bufsize += 4;
Mark Whitley450736c2001-03-02 19:08:50 +0000179
180 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000181 bb_perror_msg("socket");
Mark Whitley8bb7df42001-03-06 20:58:48 +0000182 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000183 }
184
185 len = sizeof(sa);
186
187 memset(&sa, 0, len);
Eric Andersene76c3b02001-04-05 03:14:39 +0000188 bind(socketfd, (struct sockaddr *)&sa, len);
Mark Whitley450736c2001-03-02 19:08:50 +0000189
190 sa.sin_family = host->h_addrtype;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000191 sa.sin_port = port;
Mark Whitley450736c2001-03-02 19:08:50 +0000192 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
193 sizeof(sa.sin_addr));
194
195 /* build opcode */
196
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000197 if (cmd_get) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000198 opcode = TFTP_RRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000199 }
200
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000201 if (cmd_put) {
Glenn L McGrathad117d82001-10-05 04:40:37 +0000202 opcode = TFTP_WRQ;
Mark Whitley450736c2001-03-02 19:08:50 +0000203 }
204
205 while (1) {
206
Mark Whitley450736c2001-03-02 19:08:50 +0000207 cp = buf;
208
Glenn L McGrathad117d82001-10-05 04:40:37 +0000209 /* first create the opcode part */
210
Mark Whitley450736c2001-03-02 19:08:50 +0000211 *((unsigned short *) cp) = htons(opcode);
212
213 cp += 2;
214
215 /* add filename and mode */
216
Glenn L McGrathad117d82001-10-05 04:40:37 +0000217 if ((cmd_get && (opcode == TFTP_RRQ)) ||
218 (cmd_put && (opcode == TFTP_WRQ))) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000219 int too_long = 0;
Mark Whitley450736c2001-03-02 19:08:50 +0000220
Glenn L McGrathad117d82001-10-05 04:40:37 +0000221 /* see if the filename fits into buf */
222 /* and fill in packet */
223
224 len = strlen(remotefile) + 1;
225
226 if ((cp + len) >= &buf[tftp_bufsize - 1]) {
227 too_long = 1;
Mark Whitley450736c2001-03-02 19:08:50 +0000228 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000229 else {
230 safe_strncpy(cp, remotefile, len);
231 cp += len;
232 }
233
234 if (too_long || ((&buf[tftp_bufsize - 1] - cp) < 6)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000235 bb_error_msg("too long remote-filename");
Mark Whitley450736c2001-03-02 19:08:50 +0000236 break;
237 }
238
Glenn L McGrathad117d82001-10-05 04:40:37 +0000239 /* add "mode" part of the package */
240
241 memcpy(cp, "octet", 6);
242 cp += 6;
243
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000244#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000245
246 len = tftp_bufsize - 4; /* data block size */
247
248 if (len != TFTP_BLOCKSIZE_DEFAULT) {
249
250 if ((&buf[tftp_bufsize - 1] - cp) < 15) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000251 bb_error_msg("too long remote-filename");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000252 break;
253 }
254
255 /* add "blksize" + number of blocks */
256
257 memcpy(cp, "blksize", 8);
258 cp += 8;
259
260 cp += snprintf(cp, 6, "%d", len) + 1;
261
262 want_option_ack = 1;
263 }
264#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000265 }
266
267 /* add ack and data */
268
Glenn L McGrathad117d82001-10-05 04:40:37 +0000269 if ((cmd_get && (opcode == TFTP_ACK)) ||
270 (cmd_put && (opcode == TFTP_DATA))) {
Mark Whitley450736c2001-03-02 19:08:50 +0000271
272 *((unsigned short *) cp) = htons(block_nr);
273
274 cp += 2;
275
276 block_nr++;
277
Glenn L McGrathad117d82001-10-05 04:40:37 +0000278 if (cmd_put && (opcode == TFTP_DATA)) {
Eric Andersen4872ed92004-06-22 10:18:30 +0000279 len = bb_full_read(localfd, cp, tftp_bufsize - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000280
281 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000282 bb_perror_msg("read");
Mark Whitley450736c2001-03-02 19:08:50 +0000283 break;
284 }
285
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000286 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000287 finished++;
288 }
289
290 cp += len;
Mark Whitley450736c2001-03-02 19:08:50 +0000291 }
292 }
293
294
295 /* send packet */
296
297
Eric Andersen05e662a2003-07-26 08:16:10 +0000298 timeout = bb_tftp_num_retries; /* re-initialize */
Mark Whitley450736c2001-03-02 19:08:50 +0000299 do {
300
301 len = cp - buf;
302
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000303#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000304 fprintf(stderr, "sending %u bytes\n", len);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000305 for (cp = buf; cp < &buf[len]; cp++)
Glenn L McGrathfbe984e2004-03-05 13:04:39 +0000306 fprintf(stderr, "%02x ", (unsigned char)*cp);
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000307 fprintf(stderr, "\n");
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000308#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000309 if (sendto(socketfd, buf, len, 0,
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000310 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000311 bb_perror_msg("send");
Mark Whitley450736c2001-03-02 19:08:50 +0000312 len = -1;
313 break;
314 }
315
316
Eric Andersenb99aec02003-07-30 07:16:39 +0000317 if (finished && (opcode == TFTP_ACK)) {
Glenn L McGrath0f182712002-12-19 20:16:22 +0000318 break;
319 }
Mark Whitley450736c2001-03-02 19:08:50 +0000320
Glenn L McGrath0f182712002-12-19 20:16:22 +0000321 /* receive packet */
Mark Whitley450736c2001-03-02 19:08:50 +0000322
323 memset(&from, 0, sizeof(from));
324 fromlen = sizeof(from);
325
Glenn L McGrathad117d82001-10-05 04:40:37 +0000326 tv.tv_sec = TFTP_TIMEOUT;
Mark Whitley450736c2001-03-02 19:08:50 +0000327 tv.tv_usec = 0;
328
329 FD_ZERO(&rfds);
330 FD_SET(socketfd, &rfds);
331
Paul Fox1d4c88c2005-07-20 19:49:15 +0000332 switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000333 case 1:
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000334 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
335 (struct sockaddr *) &from, &fromlen);
Mark Whitley450736c2001-03-02 19:08:50 +0000336
337 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000338 bb_perror_msg("recvfrom");
Mark Whitley450736c2001-03-02 19:08:50 +0000339 break;
340 }
341
342 timeout = 0;
343
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000344 if (sa.sin_port == port) {
Mark Whitley450736c2001-03-02 19:08:50 +0000345 sa.sin_port = from.sin_port;
Mark Whitley450736c2001-03-02 19:08:50 +0000346 }
Mark Whitley450736c2001-03-02 19:08:50 +0000347 if (sa.sin_port == from.sin_port) {
348 break;
349 }
350
351 /* fall-through for bad packets! */
352 /* discard the packet - treat as timeout */
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000353 timeout = bb_tftp_num_retries;
Mark Whitley450736c2001-03-02 19:08:50 +0000354
355 case 0:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000356 bb_error_msg("timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000357
Eric Andersenb99aec02003-07-30 07:16:39 +0000358 timeout--;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000359 if (timeout == 0) {
Mark Whitley450736c2001-03-02 19:08:50 +0000360 len = -1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000361 bb_error_msg("last timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000362 }
363 break;
364
365 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000366 bb_perror_msg("select");
Mark Whitley450736c2001-03-02 19:08:50 +0000367 len = -1;
368 }
369
370 } while (timeout && (len >= 0));
371
Glenn L McGrath0f182712002-12-19 20:16:22 +0000372 if ((finished) || (len < 0)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000373 break;
374 }
375
376 /* process received packet */
377
378
379 opcode = ntohs(*((unsigned short *) buf));
380 tmp = ntohs(*((unsigned short *) &buf[2]));
381
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000382#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000383 fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000384#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000385
Glenn L McGrathad117d82001-10-05 04:40:37 +0000386 if (opcode == TFTP_ERROR) {
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000387 const char *msg = NULL;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000388
389 if (buf[4] != '\0') {
390 msg = &buf[4];
391 buf[tftp_bufsize - 1] = '\0';
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000392 } else if (tmp < (sizeof(tftp_bb_error_msg)
Glenn L McGrathad117d82001-10-05 04:40:37 +0000393 / sizeof(char *))) {
394
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000395 msg = tftp_bb_error_msg[tmp];
Glenn L McGrathad117d82001-10-05 04:40:37 +0000396 }
397
398 if (msg) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000399 bb_error_msg("server says: %s", msg);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000400 }
401
402 break;
403 }
404
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000405#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
Glenn L McGrathad117d82001-10-05 04:40:37 +0000406 if (want_option_ack) {
407
408 want_option_ack = 0;
409
410 if (opcode == TFTP_OACK) {
411
412 /* server seems to support options */
413
414 char *res;
415
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000416 res = tftp_option_get(&buf[2], len-2,
Glenn L McGrathad117d82001-10-05 04:40:37 +0000417 "blksize");
418
419 if (res) {
Glenn L McGrathfad90db2002-12-09 21:05:40 +0000420 int blksize = atoi(res);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000421
Glenn L McGrathfad90db2002-12-09 21:05:40 +0000422 if (tftp_blocksize_check(blksize,
Glenn L McGrathad117d82001-10-05 04:40:37 +0000423 tftp_bufsize - 4)) {
424
425 if (cmd_put) {
426 opcode = TFTP_DATA;
427 }
428 else {
429 opcode = TFTP_ACK;
430 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000431#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000432 fprintf(stderr, "using blksize %u\n", blksize);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000433#endif
Glenn L McGrath9bf9f1e2002-12-09 21:52:29 +0000434 tftp_bufsize = blksize + 4;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000435 block_nr = 0;
436 continue;
437 }
438 }
439 /* FIXME:
440 * we should send ERROR 8 */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000441 bb_error_msg("bad server option");
Glenn L McGrathad117d82001-10-05 04:40:37 +0000442 break;
443 }
444
Manuel Novoa III cad53642003-03-19 09:13:01 +0000445 bb_error_msg("warning: blksize not supported by server"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000446 " - reverting to 512");
447
448 tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
449 }
450#endif
451
452 if (cmd_get && (opcode == TFTP_DATA)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000453
454 if (tmp == block_nr) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000455
Eric Andersen4872ed92004-06-22 10:18:30 +0000456 len = bb_full_write(localfd, &buf[4], len - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000457
458 if (len < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000459 bb_perror_msg("write");
Mark Whitley450736c2001-03-02 19:08:50 +0000460 break;
461 }
462
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000463 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000464 finished++;
465 }
466
Glenn L McGrathad117d82001-10-05 04:40:37 +0000467 opcode = TFTP_ACK;
Mark Whitley450736c2001-03-02 19:08:50 +0000468 continue;
469 }
Rob Landleyf3133c42005-06-07 02:40:39 +0000470 /* in case the last ack disappeared into the ether */
471 if ( tmp == (block_nr - 1) ) {
472 --block_nr;
473 opcode = TFTP_ACK;
474 continue;
Paul Fox1d4c88c2005-07-20 19:49:15 +0000475 } else if (tmp + 1 == block_nr) {
476 /* Server lost our TFTP_ACK. Resend it */
477 block_nr = tmp;
478 opcode = TFTP_ACK;
479 continue;
Rob Landleyf3133c42005-06-07 02:40:39 +0000480 }
Mark Whitley450736c2001-03-02 19:08:50 +0000481 }
482
Glenn L McGrathad117d82001-10-05 04:40:37 +0000483 if (cmd_put && (opcode == TFTP_ACK)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000484
Glenn L McGrathc6997782004-02-22 03:33:53 +0000485 if (tmp == (unsigned short)(block_nr - 1)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000486 if (finished) {
487 break;
488 }
489
Glenn L McGrathad117d82001-10-05 04:40:37 +0000490 opcode = TFTP_DATA;
Mark Whitley450736c2001-03-02 19:08:50 +0000491 continue;
492 }
493 }
Mark Whitley450736c2001-03-02 19:08:50 +0000494 }
495
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000496#ifdef CONFIG_FEATURE_CLEAN_UP
Mark Whitley450736c2001-03-02 19:08:50 +0000497 close(socketfd);
498
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000499 free(buf);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000500#endif
501
Mark Whitley8bb7df42001-03-06 20:58:48 +0000502 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000503}
504
505int tftp_main(int argc, char **argv)
506{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000507 struct hostent *host = NULL;
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000508 const char *localfile = NULL;
509 const char *remotefile = NULL;
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000510 int port;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000511 int cmd = 0;
512 int fd = -1;
513 int flags = 0;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000514 int result;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000515 int blocksize = TFTP_BLOCKSIZE_DEFAULT;
Mark Whitley450736c2001-03-02 19:08:50 +0000516
Glenn L McGrathad117d82001-10-05 04:40:37 +0000517 /* figure out what to pass to getopt */
518
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000519#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000520 char *sblocksize = NULL;
Glenn L McGrathad117d82001-10-05 04:40:37 +0000521#define BS "b:"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000522#define BS_ARG , &sblocksize
Glenn L McGrathad117d82001-10-05 04:40:37 +0000523#else
524#define BS
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000525#define BS_ARG
Glenn L McGrathad117d82001-10-05 04:40:37 +0000526#endif
527
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000528#ifdef CONFIG_FEATURE_TFTP_GET
Glenn L McGrathad117d82001-10-05 04:40:37 +0000529#define GET "g"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000530#define GET_COMPL ":g"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000531#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000532#define GET
Bernhard Reutner-Fischer886f6af2006-04-05 16:47:02 +0000533#define GET_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000534#endif
535
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000536#ifdef CONFIG_FEATURE_TFTP_PUT
Glenn L McGrathad117d82001-10-05 04:40:37 +0000537#define PUT "p"
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000538#define PUT_COMPL ":p"
Glenn L McGrathad117d82001-10-05 04:40:37 +0000539#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000540#define PUT
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000541#define PUT_COMPL
Glenn L McGrathad117d82001-10-05 04:40:37 +0000542#endif
543
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000544#if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
545 bb_opt_complementally = GET_COMPL PUT_COMPL ":?g--p:p--g";
546#elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
547 bb_opt_complementally = GET_COMPL PUT_COMPL;
548#else
549 /* XXX: may be should #error ? */
550#endif
551
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000552
553 cmd = bb_getopt_ulflags(argc, argv, GET PUT "l:r:" BS,
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000554 &localfile, &remotefile BS_ARG);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000555#ifdef CONFIG_FEATURE_TFTP_BLOCKSIZE
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000556 if(sblocksize) {
557 blocksize = atoi(sblocksize);
558 if (!tftp_blocksize_check(blocksize, 0)) {
559 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000560 }
Mark Whitley450736c2001-03-02 19:08:50 +0000561 }
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000562#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000563
Rob Landley5aabf4e2005-12-15 05:42:55 +0000564 cmd &= (tftp_cmd_get | tftp_cmd_put);
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000565#ifdef CONFIG_FEATURE_TFTP_GET
566 if(cmd == tftp_cmd_get)
567 flags = O_WRONLY | O_CREAT | O_TRUNC;
568#endif
569#ifdef CONFIG_FEATURE_TFTP_PUT
570 if(cmd == tftp_cmd_put)
571 flags = O_RDONLY;
572#endif
573
Eric Andersen744ec1d2002-04-15 07:40:27 +0000574 if(localfile == NULL)
575 localfile = remotefile;
576 if(remotefile == NULL)
577 remotefile = localfile;
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000578 /* XXX: I corrected this, but may be wrong too. vodz */
579 if(localfile==NULL || strcmp(localfile, "-") == 0) {
580 fd = fileno((cmd==tftp_cmd_get)? stdout : stdin);
581 } else if (fd==-1) {
Eric Andersena66a43e2002-04-13 09:30:25 +0000582 fd = open(localfile, flags, 0644);
583 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000584 if (fd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000585 bb_perror_msg_and_die("local file");
Mark Whitley450736c2001-03-02 19:08:50 +0000586 }
587
"Vladimir N. Oleynik"86ac0722005-10-17 10:47:19 +0000588 /* XXX: argv[optind] and/or argv[optind + 1] may be NULL! */
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000589 host = xgethostbyname(argv[optind]);
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000590 port = bb_lookup_port(argv[optind + 1], "udp", 69);
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000591
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000592#ifdef CONFIG_FEATURE_TFTP_DEBUG
Glenn L McGrathd33278d2004-02-22 07:20:25 +0000593 fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000594 "localfile \"%s\".\n",
595 inet_ntoa(*((struct in_addr *) host->h_addr)),
596 remotefile, localfile);
597#endif
598
599 result = tftp(cmd, host, remotefile, fd, port, blocksize);
Mark Whitley450736c2001-03-02 19:08:50 +0000600
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000601#ifdef CONFIG_FEATURE_CLEAN_UP
Eric Andersen70060d22004-03-27 10:02:48 +0000602 if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
Eric Andersena66a43e2002-04-13 09:30:25 +0000603 close(fd);
604 }
Glenn L McGrathad117d82001-10-05 04:40:37 +0000605#endif
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000606 return(result);
Glenn L McGrathad117d82001-10-05 04:40:37 +0000607}