blob: 999b5d706c5c9c6aaf404aabe862a5c770ec3836 [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. */
6/* Only "octet" mode and 512-byte data blocks are supported. */
7/* */
8/* Copyright (C) 2001 Magnus Damm <damm@opensource.se> */
9/* */
10/* Parts of the code based on: */
11/* */
12/* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> */
13/* and Remi Lefebvre <remi@debian.org> */
14/* */
15/* utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> */
16/* */
17/* This program is free software; you can redistribute it and/or modify */
18/* it under the terms of the GNU General Public License as published by */
19/* the Free Software Foundation; either version 2 of the License, or */
20/* (at your option) any later version. */
21/* */
22/* This program is distributed in the hope that it will be useful, */
23/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
24/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
25/* General Public License for more details. */
26/* */
27/* You should have received a copy of the GNU General Public License */
28/* along with this program; if not, write to the Free Software */
29/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
30/* */
31/* ------------------------------------------------------------------------- */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <sys/types.h>
37#include <sys/socket.h>
38#include <sys/time.h>
39#include <sys/stat.h>
40#include <netdb.h>
41#include <netinet/in.h>
42#include <arpa/inet.h>
43#include <unistd.h>
44#include <fcntl.h>
45
46#include "busybox.h"
47
48//#define BB_FEATURE_TFTP_DEBUG
49
Mark Whitley450736c2001-03-02 19:08:50 +000050static const char *tftp_error_msg[] = {
51 "Undefined error",
52 "File not found",
53 "Access violation",
54 "Disk full or allocation error",
55 "Illegal TFTP operation",
56 "Unknown transfer ID",
57 "File already exists",
58 "No such user"
59};
60
Eric Andersen76fa8ea2001-08-20 17:47:49 +000061const int tftp_cmd_get = 1;
62const int tftp_cmd_put = 2;
63
64static inline int tftp(const int cmd, const struct hostent *host,
65 const char *serverfile, int localfd, const int port, int tftp_bufsize)
Mark Whitley450736c2001-03-02 19:08:50 +000066{
Eric Andersen76fa8ea2001-08-20 17:47:49 +000067 const int cmd_get = cmd & tftp_cmd_get;
68 const int cmd_put = cmd & tftp_cmd_put;
69 const int bb_tftp_num_retries = 5;
70
Mark Whitley450736c2001-03-02 19:08:50 +000071 struct sockaddr_in sa;
Mark Whitley450736c2001-03-02 19:08:50 +000072 struct sockaddr_in from;
Eric Andersen76fa8ea2001-08-20 17:47:49 +000073 struct timeval tv;
Mark Whitley450736c2001-03-02 19:08:50 +000074 socklen_t fromlen;
Eric Andersen76fa8ea2001-08-20 17:47:49 +000075 fd_set rfds;
Mark Whitley450736c2001-03-02 19:08:50 +000076 char *cp;
77 unsigned short tmp;
Eric Andersen76fa8ea2001-08-20 17:47:49 +000078 int socketfd;
79 int len;
80 int opcode = 0;
81 int finished = 0;
82 int timeout = bb_tftp_num_retries;
83 int block_nr = 1;
84 RESERVE_BB_BUFFER(buf, tftp_bufsize + 4); // Why 4 ?
Mark Whitley450736c2001-03-02 19:08:50 +000085
Eric Andersen76fa8ea2001-08-20 17:47:49 +000086 tftp_bufsize += 4;
Mark Whitley450736c2001-03-02 19:08:50 +000087
88 if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
89 perror_msg("socket");
Mark Whitley8bb7df42001-03-06 20:58:48 +000090 return EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +000091 }
92
93 len = sizeof(sa);
94
95 memset(&sa, 0, len);
Eric Andersene76c3b02001-04-05 03:14:39 +000096 bind(socketfd, (struct sockaddr *)&sa, len);
Mark Whitley450736c2001-03-02 19:08:50 +000097
98 sa.sin_family = host->h_addrtype;
99 sa.sin_port = htons(port);
100 memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
101 sizeof(sa.sin_addr));
102
103 /* build opcode */
104
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000105 if (cmd_get) {
106 opcode = 1; // read request = 1
Mark Whitley450736c2001-03-02 19:08:50 +0000107 }
108
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000109 if (cmd_put) {
110 opcode = 2; // write request = 2
Mark Whitley450736c2001-03-02 19:08:50 +0000111 }
112
113 while (1) {
114
115
116 /* build packet of type "opcode" */
117
118
119 cp = buf;
120
121 *((unsigned short *) cp) = htons(opcode);
122
123 cp += 2;
124
125 /* add filename and mode */
126
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000127 if ((cmd_get && (opcode == 1)) || // read request = 1
128 (cmd_put && (opcode == 2))) { // write request = 2
Mark Whitley450736c2001-03-02 19:08:50 +0000129
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000130 /* what is this trying to do ? */
131 while (cp != &buf[tftp_bufsize - 1]) {
Mark Whitley450736c2001-03-02 19:08:50 +0000132 if ((*cp = *serverfile++) == '\0')
133 break;
134 cp++;
135 }
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000136 /* and this ? */
137 if ((*cp != '\0') || (&buf[tftp_bufsize - 1] - cp) < 7) {
Mark Whitley8bb7df42001-03-06 20:58:48 +0000138 error_msg("too long server-filename");
Mark Whitley450736c2001-03-02 19:08:50 +0000139 break;
140 }
141
142 memcpy(cp + 1, "octet", 6);
143 cp += 7;
144 }
145
146 /* add ack and data */
147
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000148 if ((cmd_get && (opcode == 4)) || // acknowledgement = 4
149 (cmd_put && (opcode == 3))) { // data packet == 3
Mark Whitley450736c2001-03-02 19:08:50 +0000150
151 *((unsigned short *) cp) = htons(block_nr);
152
153 cp += 2;
154
155 block_nr++;
156
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000157 if (cmd_put && (opcode == 3)) { // data packet == 3
158 len = read(localfd, cp, tftp_bufsize - 4);
Mark Whitley450736c2001-03-02 19:08:50 +0000159
160 if (len < 0) {
161 perror_msg("read");
162 break;
163 }
164
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000165 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000166 finished++;
167 }
168
169 cp += len;
170 } else if (finished) {
171 break;
172 }
173 }
174
175
176 /* send packet */
177
178
179 do {
180
181 len = cp - buf;
182
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000183#ifdef BB_FEATURE_TFTP_DEBUG
184 printf("sending %u bytes\n", len);
185 for (cp = buf; cp < &buf[len]; cp++)
186 printf("%02x ", *cp);
187 printf("\n");
188#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000189 if (sendto(socketfd, buf, len, 0,
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000190 (struct sockaddr *) &sa, sizeof(sa)) < 0) {
Mark Whitley8bb7df42001-03-06 20:58:48 +0000191 perror_msg("send");
Mark Whitley450736c2001-03-02 19:08:50 +0000192 len = -1;
193 break;
194 }
195
196
197 /* receive packet */
198
199
200 memset(&from, 0, sizeof(from));
201 fromlen = sizeof(from);
202
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000203 tv.tv_sec = 5; // BB_TFPT_TIMEOUT = 5
Mark Whitley450736c2001-03-02 19:08:50 +0000204 tv.tv_usec = 0;
205
206 FD_ZERO(&rfds);
207 FD_SET(socketfd, &rfds);
208
209 switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
210 case 1:
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000211 len = recvfrom(socketfd, buf, tftp_bufsize, 0,
212 (struct sockaddr *) &from, &fromlen);
Mark Whitley450736c2001-03-02 19:08:50 +0000213
214 if (len < 0) {
215 perror_msg("recvfrom");
216 break;
217 }
218
219 timeout = 0;
220
221 if (sa.sin_port == htons(port)) {
222 sa.sin_port = from.sin_port;
Mark Whitley450736c2001-03-02 19:08:50 +0000223 }
Mark Whitley450736c2001-03-02 19:08:50 +0000224 if (sa.sin_port == from.sin_port) {
225 break;
226 }
227
228 /* fall-through for bad packets! */
229 /* discard the packet - treat as timeout */
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000230 timeout = bb_tftp_num_retries;
Mark Whitley450736c2001-03-02 19:08:50 +0000231
232 case 0:
Mark Whitley8bb7df42001-03-06 20:58:48 +0000233 error_msg("timeout");
Mark Whitley450736c2001-03-02 19:08:50 +0000234
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000235 if (timeout == 0) {
Mark Whitley450736c2001-03-02 19:08:50 +0000236 len = -1;
Mark Whitley8bb7df42001-03-06 20:58:48 +0000237 error_msg("last timeout");
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000238 } else {
239 timeout--;
Mark Whitley450736c2001-03-02 19:08:50 +0000240 }
241 break;
242
243 default:
244 perror_msg("select");
245 len = -1;
246 }
247
248 } while (timeout && (len >= 0));
249
250 if (len < 0) {
251 break;
252 }
253
254 /* process received packet */
255
256
257 opcode = ntohs(*((unsigned short *) buf));
258 tmp = ntohs(*((unsigned short *) &buf[2]));
259
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000260#ifdef BB_FEATURE_TFTP_DEBUG
261 printf("received %d bytes: %04x %04x\n", len, opcode, tmp);
262#endif
Mark Whitley450736c2001-03-02 19:08:50 +0000263
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000264 if (cmd_get && (opcode == 3)) { // data packet == 3
Mark Whitley450736c2001-03-02 19:08:50 +0000265
266 if (tmp == block_nr) {
267 len = write(localfd, &buf[4], len - 4);
268
269 if (len < 0) {
270 perror_msg("write");
271 break;
272 }
273
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000274 if (len != (tftp_bufsize - 4)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000275 finished++;
276 }
277
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000278 opcode = 4; // acknowledgement = 4
Mark Whitley450736c2001-03-02 19:08:50 +0000279 continue;
280 }
281 }
282
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000283 if (cmd_put && (opcode == 4)) { // acknowledgement = 4
Mark Whitley450736c2001-03-02 19:08:50 +0000284
285 if (tmp == (block_nr - 1)) {
286 if (finished) {
287 break;
288 }
289
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000290 opcode = 3; // data packet == 3
Mark Whitley450736c2001-03-02 19:08:50 +0000291 continue;
292 }
293 }
294
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000295 if (opcode == 5) { // error code == 5
Mark Whitley450736c2001-03-02 19:08:50 +0000296 char *msg = NULL;
297
298 if (buf[4] != '\0') {
299 msg = &buf[4];
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000300 buf[tftp_bufsize - 1] = '\0';
Mark Whitley450736c2001-03-02 19:08:50 +0000301 } else if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) {
302 msg = (char *) tftp_error_msg[tmp];
303 }
304
305 if (msg) {
Mark Whitley8bb7df42001-03-06 20:58:48 +0000306 error_msg("server says: %s", msg);
Mark Whitley450736c2001-03-02 19:08:50 +0000307 }
308
309 break;
310 }
311 }
312
313 close(socketfd);
314
Mark Whitley8bb7df42001-03-06 20:58:48 +0000315 return finished ? EXIT_SUCCESS : EXIT_FAILURE;
Mark Whitley450736c2001-03-02 19:08:50 +0000316}
317
318int tftp_main(int argc, char **argv)
319{
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000320 struct hostent *host = NULL;
321 char *localfile = NULL;
322 char *remotefile = NULL;
323 int port = 69;
324 int cmd = 0;
325 int fd = -1;
326 int flags = 0;
327 int opt;
328 int result;
329 int blocksize = 512;
Mark Whitley450736c2001-03-02 19:08:50 +0000330
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000331 while ((opt = getopt(argc, argv, "b:gpl:r:")) != -1) {
332 switch (opt) {
333 case 'b':
334 blocksize = atoi(optarg);
335 break;
336#ifdef BB_FEATURE_TFTP_GET
337 case 'g':
338 cmd = tftp_cmd_get;
Mark Whitley450736c2001-03-02 19:08:50 +0000339 flags = O_WRONLY | O_CREAT;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000340 break;
341#endif
342#ifdef BB_FEATURE_TFTP_PUT
343 case 'p':
344 cmd = tftp_cmd_put;
Mark Whitley450736c2001-03-02 19:08:50 +0000345 flags = O_RDONLY;
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000346 break;
347#endif
348 case 'l':
349 localfile = xstrdup(optarg);
350 break;
351 case 'r':
352 remotefile = xstrdup(optarg);
353 break;
Mark Whitley450736c2001-03-02 19:08:50 +0000354 }
Mark Whitley450736c2001-03-02 19:08:50 +0000355 }
356
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000357 if ((cmd == 0) || (optind == argc)) {
Mark Whitley450736c2001-03-02 19:08:50 +0000358 show_usage();
359 }
360
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000361 fd = open(localfile, flags, 0644);
362 if (fd < 0) {
Mark Whitley450736c2001-03-02 19:08:50 +0000363 perror_msg_and_die("local file");
364 }
365
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000366 host = xgethostbyname(argv[optind]);
Mark Whitley450736c2001-03-02 19:08:50 +0000367
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000368 if (optind + 2 == argc) {
369 port = atoi(argv[optind + 1]);
370 }
371
372#ifdef BB_FEATURE_TFTP_DEBUG
373 printf("using server \"%s\", serverfile \"%s\","
374 "localfile \"%s\".\n",
375 inet_ntoa(*((struct in_addr *) host->h_addr)),
376 remotefile, localfile);
377#endif
378
379 result = tftp(cmd, host, remotefile, fd, port, blocksize);
Mark Whitley450736c2001-03-02 19:08:50 +0000380 close(fd);
381
Eric Andersen76fa8ea2001-08-20 17:47:49 +0000382 return(result);
383}