Simplified version checking.
[oweals/busybox.git] / tftp.c
1 /* ------------------------------------------------------------------------- */
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
50 /* we don't need #ifdefs with these constants and optimization... */
51
52 #ifdef BB_FEATURE_TFTP_GET
53 #define BB_TFTP_GET (1 << 0)
54 #else
55 #define BB_TFTP_GET 0
56 #endif
57
58 #ifdef BB_FEATURE_TFTP_PUT
59 #define BB_TFTP_PUT (1 << 1)
60 #else
61 #define BB_TFTP_PUT 0
62 #endif
63
64 #ifdef BB_FEATURE_TFTP_DEBUG
65 #define BB_TFTP_DEBUG 1
66 #else
67 #define BB_TFTP_DEBUG 0
68 #endif
69
70 #define BB_TFTP_NO_RETRIES 5
71 #define BB_TFTP_TIMEOUT    5    /* seconds */
72
73 #define RRQ     1                       /* read request */
74 #define WRQ     2                       /* write request */
75 #define DATA    3               /* data packet */
76 #define ACK     4                       /* acknowledgement */
77 #define ERROR   5               /* error code */
78
79 #define BUFSIZE (512+4)
80
81 static const char *tftp_error_msg[] = {
82         "Undefined error",
83         "File not found",
84         "Access violation",
85         "Disk full or allocation error",
86         "Illegal TFTP operation",
87         "Unknown transfer ID",
88         "File already exists",
89         "No such user"
90 };
91
92 static inline int tftp(int cmd, struct hostent *host,
93                                            char *serverfile, int localfd, int port)
94 {
95         struct sockaddr_in sa;
96         int socketfd;
97         struct timeval tv;
98         fd_set rfds;
99         struct sockaddr_in from;
100         socklen_t fromlen;
101         char *cp;
102         unsigned short tmp;
103         int len, opcode, finished;
104         int timeout, block_nr;
105
106         RESERVE_BB_BUFFER(buf, BUFSIZE);
107
108         opcode = finished = timeout = 0;
109         block_nr = 1;
110
111         if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
112                 perror_msg("socket");
113                 return EXIT_FAILURE;
114         }
115
116         len = sizeof(sa);
117
118         memset(&sa, 0, len);
119         bind(socketfd, (struct sockaddr *)&sa, len);
120
121         sa.sin_family = host->h_addrtype;
122         sa.sin_port = htons(port);
123         memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
124                    sizeof(sa.sin_addr));
125
126         /* build opcode */
127
128         if (cmd & BB_TFTP_GET) {
129                 opcode = RRQ;
130         }
131
132         if (cmd & BB_TFTP_PUT) {
133                 opcode = WRQ;
134         }
135
136         while (1) {
137
138
139                 /* build packet of type "opcode" */
140
141
142                 cp = buf;
143
144                 *((unsigned short *) cp) = htons(opcode);
145
146                 cp += 2;
147
148                 /* add filename and mode */
149
150                 if ((BB_TFTP_GET && (opcode == RRQ)) ||
151                         (BB_TFTP_PUT && (opcode == WRQ))) {
152
153                         while (cp != &buf[BUFSIZE - 1]) {
154                                 if ((*cp = *serverfile++) == '\0')
155                                         break;
156                                 cp++;
157                         }
158
159                         if ((*cp != '\0') || (&buf[BUFSIZE - 1] - cp) < 7) {
160                                 error_msg("too long server-filename");
161                                 break;
162                         }
163
164                         memcpy(cp + 1, "octet", 6);
165                         cp += 7;
166                 }
167
168                 /* add ack and data */
169
170                 if ((BB_TFTP_GET && (opcode == ACK)) ||
171                         (BB_TFTP_PUT && (opcode == DATA))) {
172
173                         *((unsigned short *) cp) = htons(block_nr);
174
175                         cp += 2;
176
177                         block_nr++;
178
179                         if (BB_TFTP_PUT && (opcode == DATA)) {
180                                 len = read(localfd, cp, BUFSIZE - 4);
181
182                                 if (len < 0) {
183                                         perror_msg("read");
184                                         break;
185                                 }
186
187                                 if (len != (BUFSIZE - 4)) {
188                                         finished++;
189                                 }
190
191                                 cp += len;
192                         } else if (finished) {
193                                 break;
194                         }
195                 }
196
197
198                 /* send packet */
199
200
201                 do {
202
203                         len = cp - buf;
204
205                         if (BB_TFTP_DEBUG) {
206                                 printf("sending %u bytes\n", len);
207
208                                 for (cp = buf; cp < &buf[len]; cp++)
209                                         printf("%02x ", *cp);
210                                 printf("\n");
211                         }
212
213                         if (sendto(socketfd, buf, len, 0,
214                                            (struct sockaddr *) &sa, sizeof(sa)) < 0) {
215                                 perror_msg("send");
216                                 len = -1;
217                                 break;
218                         }
219
220
221                         /* receive packet */
222
223
224                         memset(&from, 0, sizeof(from));
225                         fromlen = sizeof(from);
226
227                         tv.tv_sec = BB_TFTP_TIMEOUT;
228                         tv.tv_usec = 0;
229
230                         FD_ZERO(&rfds);
231                         FD_SET(socketfd, &rfds);
232
233                         switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
234                         case 1:
235                                 len = recvfrom(socketfd, buf,
236                                                            BUFSIZE, 0,
237                                                            (struct sockaddr *) &from, &fromlen);
238
239                                 if (len < 0) {
240                                         perror_msg("recvfrom");
241                                         break;
242                                 }
243
244                                 timeout = 0;
245
246                                 if (sa.sin_port == htons(port)) {
247                                         sa.sin_port = from.sin_port;
248                                         break;
249                                 }
250
251                                 if (sa.sin_port == from.sin_port) {
252                                         break;
253                                 }
254
255                                 /* fall-through for bad packets! */
256                                 /* discard the packet - treat as timeout */
257
258                         case 0:
259                                 error_msg("timeout");
260
261                                 if (!timeout) {
262                                         timeout = BB_TFTP_NO_RETRIES;
263                                 } else {
264                                         timeout--;
265                                 }
266
267                                 if (!timeout) {
268                                         len = -1;
269                                         error_msg("last timeout");
270                                 }
271                                 break;
272
273                         default:
274                                 perror_msg("select");
275                                 len = -1;
276                         }
277
278                 } while (timeout && (len >= 0));
279
280                 if (len < 0) {
281                         break;
282                 }
283
284                 /* process received packet */
285
286
287                 opcode = ntohs(*((unsigned short *) buf));
288                 tmp = ntohs(*((unsigned short *) &buf[2]));
289
290                 if (BB_TFTP_DEBUG) {
291                         printf("received %d bytes: %04x %04x\n", len, opcode, tmp);
292                 }
293
294                 if (BB_TFTP_GET && (opcode == DATA)) {
295
296                         if (tmp == block_nr) {
297                                 len = write(localfd, &buf[4], len - 4);
298
299                                 if (len < 0) {
300                                         perror_msg("write");
301                                         break;
302                                 }
303
304                                 if (len != (BUFSIZE - 4)) {
305                                         finished++;
306                                 }
307
308                                 opcode = ACK;
309                                 continue;
310                         }
311                 }
312
313                 if (BB_TFTP_PUT && (opcode == ACK)) {
314
315                         if (tmp == (block_nr - 1)) {
316                                 if (finished) {
317                                         break;
318                                 }
319
320                                 opcode = DATA;
321                                 continue;
322                         }
323                 }
324
325                 if (opcode == ERROR) {
326                         char *msg = NULL;
327
328                         if (buf[4] != '\0') {
329                                 msg = &buf[4];
330                                 buf[BUFSIZE - 1] = '\0';
331                         } else if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) {
332                                 msg = (char *) tftp_error_msg[tmp];
333                         }
334
335                         if (msg) {
336                                 error_msg("server says: %s", msg);
337                         }
338
339                         break;
340                 }
341         }
342
343         close(socketfd);
344
345         return finished ? EXIT_SUCCESS : EXIT_FAILURE;
346 }
347
348 int tftp_main(int argc, char **argv)
349 {
350         char *cp, *s;
351         char *serverstr;
352         struct hostent *host;
353         char *serverfile;
354         char *localfile;
355         int cmd, flags, fd, bad;
356
357         host = (void *) serverstr = serverfile = localfile = NULL;
358         flags = cmd = 0;
359         bad = 1;
360
361         if (argc > 3) {
362                 if (BB_TFTP_GET && (strcmp(argv[1], "get") == 0)) {
363                         cmd = BB_TFTP_GET;
364                         flags = O_WRONLY | O_CREAT;
365                         serverstr = argv[2];
366                         localfile = argv[3];
367                 }
368
369                 if (BB_TFTP_PUT && (strcmp(argv[1], "put") == 0)) {
370                         cmd = BB_TFTP_PUT;
371                         flags = O_RDONLY;
372                         localfile = argv[2];
373                         serverstr = argv[3];
374                 }
375
376         }
377
378         if (!(cmd & (BB_TFTP_GET | BB_TFTP_PUT))) {
379                 show_usage();
380         }
381
382         for (cp = serverstr; *cp != '\0'; cp++)
383                 if (*cp == ':')
384                         break;
385
386         if (*cp == ':') {
387
388                 serverfile = cp + 1;
389
390                 s = xstrdup(serverstr);
391                 s[cp - serverstr] = '\0';
392
393                 host = xgethostbyname(s);
394
395                 free(s);
396         }
397
398         if (BB_TFTP_DEBUG) {
399                 printf("using server \"%s\", serverfile \"%s\","
400                            "localfile \"%s\".\n",
401                            inet_ntoa(*((struct in_addr *) host->h_addr)),
402                            serverfile, localfile);
403         }
404
405         if ((fd = open(localfile, flags, 0644)) < 0) {
406                 perror_msg_and_die("local file");
407         }
408
409         flags = tftp(cmd, host, serverfile, fd, 69);
410
411         close(fd);
412
413         return flags;
414 }