]> git.decadent.org.uk Git - ap-utils.git/blob - src/ap-tftp.c
Imported Upstream version 1.5~pre2
[ap-utils.git] / src / ap-tftp.c
1 /* ------------------------------------------------------------------------- */
2 /* ap-tftp.c                                                                 */
3 /*                                                                           */
4 /* A simple tftp client for upgrading ATMEL AT76C510 WiSOC-based APs.        */
5 /* Supports ATMEL+INTERSIL boards (1.4x.y firmware) and ATMEL+RFMD boards    */
6 /* (0.x.y.z firmware).                                                       */
7 /* Modelled around TELLUS (GEMTEK/ATMEL OEM) TFTP client functionality.      */
8 /* This program is part of AP-UTILS project (http://ap-utils.polesye.net)    */
9 /*                                                                           */
10 /* Copyright (C) 2004-2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz>  */
11 /*                                                                           */
12 /* Loosely based on a simple tftp client for busybox.                        */
13 /* Tries to follow RFC1350.                                                  */
14 /* Only "octet" mode and "put" method supported.                             */
15 /*                                                                           */
16 /* Version: 1.0                                                              */
17 /*                                                                           */
18 /* Not implemented:                                                          */
19 /* - uploading of OEM (default) settings                                     */
20 /* - uploading of PATCH code                                                 */
21 /*                                                                           */
22 /* Code based on:                                                            */
23 /*                                                                           */
24 /* bb tftp: Copyright (C) 2001 Magnus Damm <damm@opensource.se>              */
25 /*                                                                           */
26 /* atftp:   Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>  */
27 /*                             and Remi Lefebvre <remi@debian.org>           */
28 /*                                                                           */
29 /* utftp:   Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>                        */
30 /*                                                                           */
31 /* This program is free software; you can redistribute it and/or modify      */
32 /* it under the terms of the GNU General Public License as published by      */
33 /* the Free Software Foundation; either version 2 of the License, or         */
34 /* (at your option) any later version.                                       */
35 /*                                                                           */
36 /* This program is distributed in the hope that it will be useful,           */
37 /* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
38 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          */
39 /* General Public License for more details.                                  */
40 /*                                                                           */
41 /* You should have received a copy of the GNU General Public License         */
42 /* along with this program; if not, write to the Free Software               */
43 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA   */
44 /*                                                                           */
45 /* ------------------------------------------------------------------------- */
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <sys/time.h>
53 #include <sys/stat.h>
54 #include <netdb.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <unistd.h>
58 #include <fcntl.h>
59
60 #ifdef HAVE_GETTEXT
61 /* GNU gettext stuff*/
62 #include <locale.h>
63 #include <libgnuintl.h>
64 #define _(String) gettext (String)
65 #else
66 #define _(String) (String)
67 #endif
68
69 #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
70 #define TFTP_TIMEOUT 2             /* # seconds waiting for single response */
71 #define TFTP_RETRIES 5             /* # retries with TFTP_TIMEOUT */
72 #undef FEATURE_TFTP_DEBUG
73
74 /* opcodes we support */
75 #define TFTP_WRQ   2
76 #define TFTP_DATA  3
77 #define TFTP_ACK   4
78 #define TFTP_ERROR 5
79
80 #ifdef FEATURE_TFTP_DEBUG
81 #define YES _("yes")
82 #define NO _("no")
83 #endif
84 #define ERR_READFW _("Error while read()ing firmware file")
85
86 /* known errors server may respond with */
87 static const char *tftp_error_msg[] = {
88     _("Undefined error"),
89     _("File not found"),
90     _("Access violation"),
91     _("Disk full or allocation error"),
92     _("Illegal TFTP operation"),
93     _("Unknown transfer ID"),
94     _("File already exists"),
95     _("No such user")
96 };
97
98 int tftp(int ip, const char *remotefile, int localfd, const char *options)
99 {
100     const int port = 69;
101     struct sockaddr_in to;
102     struct sockaddr_in from;
103     struct timeval tv;
104     struct stat sb;
105     fd_set rfds;
106     unsigned short tmp;
107     int fromlen;
108     int opcode = TFTP_WRQ;
109     int tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT;
110     int timeout;
111     int block_nr = 1;
112     int finished = 0;
113     int socketfd;
114     int len;
115     int maxlen;
116     int optlen;
117     char *nl = "";
118     char *cp;
119     char *buf = malloc(tftp_bufsize);
120     char buf2[64];
121
122     printf("Trying to upload firmware to the AP...\n");
123
124     if (!strcmp(remotefile, "atbrfirm.bin")) {
125         optlen = 8;
126         maxlen = 94448;
127     } else {
128         /* remotefile = atsingle.bin */
129         optlen = 234;
130         maxlen = 0;
131     }
132
133     fstat(localfd, &sb);
134
135     if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
136         perror("socket()");
137         return EXIT_FAILURE;
138     }
139
140     len = sizeof(to);
141     memset(&to, 0, len);
142     bind(socketfd, (struct sockaddr *)&to, len);
143
144     to.sin_family = AF_INET;
145     to.sin_port = htons(port);
146     to.sin_addr.s_addr = ip;
147
148     while (1) {
149         timeout = TFTP_RETRIES;
150         cp = buf;
151
152         /* first create the opcode part */
153
154         *((unsigned short *) cp) = htons(opcode);
155         cp += 2;
156
157         if (opcode == TFTP_WRQ) {
158             /* see if the filename fits into buf */
159             len = strlen(remotefile) + 1;
160             if ((cp + len + 1 + 6 + optlen) >= &buf[tftp_bufsize - 1]) {
161                 printf(_("Remote-filename too long.\n"));
162                 break;
163             }
164
165             strncpy(cp, remotefile, len);
166             cp += len;
167
168             // ATMEL tftp client specific
169             *cp = 1;
170             cp++;
171
172             memcpy(cp, "octet", 6);
173             cp += 6;
174
175             // ATMEL tftp client specific ("upload auth. code")
176             memcpy(cp, options, optlen);
177             cp += optlen;
178         }
179
180         if (opcode == TFTP_DATA) {
181             *((unsigned short *) cp) = htons(block_nr);
182             cp += 2;
183
184             len = read(localfd, cp, tftp_bufsize);
185
186             if (len < 0) {
187                 sprintf(buf2, _("%sError in read()"), nl);
188                 perror(buf2);
189                 break;
190             }
191
192             block_nr++;
193
194             /*
195              * assure we wont upload more than maxlen bytes
196              * even if total file length > maxlen
197              */
198             if (maxlen && (block_nr - 2) * tftp_bufsize + len > maxlen) {
199                 len = maxlen - (block_nr - 2) * tftp_bufsize;
200                 cp[len] = '\0';
201             }
202
203             if (len != tftp_bufsize)
204                 finished++;
205
206             cp += len;
207         }
208
209         /* send packet */
210
211         do {
212             len = cp - buf;
213
214 #ifdef FEATURE_TFTP_DEBUG
215             printf(_("sending %u bytes\n"), len);
216             for (cp = buf; cp < &buf[len]; cp++)
217                 printf("%02x ", *cp & 0xFF);
218
219             printf("\n");
220 #endif
221             if (sendto(socketfd, buf, len, 0,
222                 (struct sockaddr *) &to, sizeof(to)) < 0)
223             {
224                 sprintf(buf2, _("%sError in sendto()"), nl);
225                 perror(buf2);
226                 len = -1;
227                 break;
228             }
229
230             /* receive packet */
231
232             memset(&from, 0, sizeof(from));
233             fromlen = sizeof(from);
234
235             tv.tv_sec = TFTP_TIMEOUT;
236             tv.tv_usec = 0;
237
238             FD_ZERO(&rfds);
239             FD_SET(socketfd, &rfds);
240
241             switch (select(FD_SETSIZE, &rfds, NULL, NULL, &tv)) {
242                 case 1:
243                     len = recvfrom(socketfd, buf, tftp_bufsize, 0,
244                         (struct sockaddr *) &from, &fromlen);
245
246                     if (len < 0) {
247                         sprintf(buf2, _("%sError in recvfrom()"), nl);
248                         perror(buf2);
249                         break;
250                     }
251
252                     /*
253                     if (to.sin_port == htons(port))
254                         to.sin_port = from.sin_port;
255                     */
256                     if (to.sin_port == from.sin_port) {
257 #ifndef FEATURE_TFTP_DEBUG
258                         if (opcode == TFTP_DATA) {
259                             float f = (block_nr - 2) * tftp_bufsize + len;
260
261                             sprintf(buf2, _("\rProgress: uploaded %.0f %%."),
262                                 f / sb.st_size * 100);
263                             write(fileno(stdout), buf2, strlen(buf2));
264                             nl = "\n";
265                         }
266 #endif
267                         timeout = 0;
268                         break;
269                     }
270
271                     /* assume invalid packet */
272                     printf(_("%sMalformed packet received. Aborting.\n"), nl);
273                     len = -1;
274                     break;
275
276                 case 0:
277                     timeout--;
278                     if (timeout == 0)
279                         len = -1;
280
281                     printf(_("%sTimed out waiting for response from server "
282                         "(%i/%i).\n"),nl, TFTP_RETRIES - timeout, TFTP_RETRIES);
283                     nl = "";
284                     break;
285
286                 default:
287                     len = -1;
288                     sprintf(buf2, _("%sError in select()"), nl);
289                     perror(buf2);
290             }
291         } while (timeout && (len >= 0));
292
293         if (len < 0)
294             break;
295
296         /* process received packet */
297
298         opcode = ntohs(*((unsigned short *) buf));
299         tmp = ntohs(*((unsigned short *) &buf[2]));
300
301 #ifdef FEATURE_TFTP_DEBUG
302         printf(_("Received %d bytes: %04x %04x\n"), len, opcode, tmp);
303 #endif
304
305         if (opcode == TFTP_ERROR) {
306             sprintf(buf, "code %i", tmp);
307             if (tmp < (sizeof(tftp_error_msg) / sizeof(char *))) {
308                 strcat(buf, " (");
309                 strcat(buf, tftp_error_msg[tmp]);
310                 strcat(buf, ")");
311             }
312
313             printf(_("%sError: server responded with %s. Aborting.\n"),nl,buf);
314             break;
315         }
316
317         if (opcode == TFTP_ACK) {
318             if (tmp == (block_nr - 1)) {
319                 if (finished) {
320                     printf(_("%sFlash programming in progress...\n"), nl);
321                     sleep(5);
322                     printf(_("Finished successfully.\n"));
323                     break;
324                 }
325
326                 opcode = TFTP_DATA;
327                 continue;
328             }
329         }
330     }
331
332     close(socketfd);
333     free(buf);
334
335     return finished ? EXIT_SUCCESS : EXIT_FAILURE;
336 }
337
338 void usage(char **argv)
339 {
340     printf (_("PLEASE BE _ABSOLUTELY_ SURE TO READ MANPAGE PRIOR USE!!!\n"));
341     printf (_("\nUsage: %s <-l firmware_file.rom> <IP>\n"), argv[0]);
342 }
343
344 int main(int argc, char **argv)
345 {
346     struct in_addr in;
347     struct stat sb;
348     char *localfile = NULL;
349     char *remotefile = NULL;
350     char *cp;
351     int fd;
352     int result = 0;
353     int i;
354     static char buf[256];
355
356 #ifdef HAVE_GETTEXT
357     /* locale support init */
358     setlocale(LC_ALL, "");
359     bindtextdomain("ap-utils", LOCALEDIR);
360     textdomain("ap-utils");
361 #endif
362
363     printf (_("TFTP client for upgrading firmware in ATMEL AT76C510 "
364             "WiSOC-based APs.\n"));
365     printf (_("(C) 2004-2005 Jan Rafaj "
366             "<jr-aputils at cedric dot unob dot cz>\n"));
367
368     while ((i = getopt(argc, argv, "l:h")) != -1) {
369         switch (i) {
370             case 'l': 
371                 localfile = strdup(optarg);
372                 break;
373             case 'h':
374                 usage(argv);
375                 return EXIT_SUCCESS;
376             default:
377                 return EXIT_FAILURE;
378         }
379     }
380
381     if (argc == 1) {
382         usage(argv);
383         return EXIT_SUCCESS;
384     }
385
386     /*
387      * either no mandatory opts, or too few mandatory opts, or more than 1
388      * non-opt arg. specified, or no non-option arg. specified
389      */
390     if (optind == 1 || optind < 3 || argc > 4 || optind == argc) {
391         printf(_("Error: invalid arguments given.\n"));
392         return EXIT_FAILURE;
393     }
394
395     /* host = gethostbyname(argv[optind]); */
396     for (cp = argv[optind], i = 0; *cp && (cp = index(cp, '.')); cp++, i++);
397     if (i < 3 || !(inet_aton(argv[optind], &in))) {
398         printf(_("Error: invalid IP address format given.\n"));
399         return EXIT_FAILURE;
400     }
401     inet_aton(argv[optind], &in);
402
403     fd = open(localfile, O_RDONLY, 0644);
404     if (fd < 0) {
405         perror(_("Error while open()ing firmware file"));
406         return EXIT_FAILURE;
407     }
408
409     fstat(fd, &sb);
410     if (sb.st_size < 94448 || sb.st_size > 524288) {
411         printf(_("Error: invalid firmware file given.\n"));
412         return EXIT_FAILURE;
413     }
414
415     while ((i = read(fd, buf, sizeof(buf))) != 0) {
416         if (i < 0) {
417             perror(ERR_READFW);
418             return EXIT_FAILURE;
419         }
420
421         if (i == sizeof(buf))
422             lseek(fd, -13, SEEK_CUR);
423
424         while (i--) {
425             if (!(memcmp(&buf[i], "ATMEL", 5)))
426                 result |= 1;
427
428             if (!(memcmp(&buf[i], "802.11 AP", 9)))
429                 result |= 2;
430
431             if (!(memcmp(&buf[i], "atbrfirm.bin", 12))) {
432                 result |= 16;
433                 remotefile = "atbrfirm.bin";
434             }
435
436             if (!(memcmp(&buf[i], "atsingle.bin", 12))) {
437                 result |= 32;
438                 remotefile = "atsingle.bin";
439             }
440
441             if (result > 18)
442                 break;
443         }
444
445         if (result > 18)
446             break;
447
448         memset(buf, 0, sizeof(buf));
449     }
450 #if FEATURE_TFTP_DEBUG
451     printf(_("Firmware file contains:\n"
452             "- string \"ATMEL\": %s\n"
453             "- string \"802.11 AP\": %s\n"
454             "- string \"atbrfirm.bin\": %s\n"
455             "- string \"atsingle.bin\": %s\n"),
456             result & 1 ? YES : NO, result & 2 ? YES : NO,
457             result & 16 ? YES : NO, result & 32 ? YES : NO);
458 #endif
459     if ((result & (1|2)) != (1|2) || (result & (16|32)) == (16|32) ||
460         (result & (16|32)) == 0)
461     {
462         printf(_("Error: invalid firmware file given.\n"));
463         return EXIT_FAILURE;
464     }
465
466     lseek(fd, 0, SEEK_SET);
467
468     cp = strrchr(localfile, '/');
469     if (cp)
470         cp++;
471     else
472         cp = localfile;
473
474     printf(_("Using:\n"
475         "- server: %s\n"
476         "- firmware file: \"%s\"\n"
477         "- name used for upload: \"%s\"\n"),
478         inet_ntoa(in), cp, remotefile);
479
480     if (result & 16) {
481         /* Firmware series 1.4x.y - atbrfirm.bin */
482
483         unsigned short sum = 0;
484         unsigned short *wp;
485         int maxlen = 94448;
486         int bufs_read = 0;
487
488         /* compute checksum */
489         while ((i = read(fd, buf, sizeof(buf))) != 0) {
490             if (i < 0) {
491                 perror(ERR_READFW);
492                 return EXIT_FAILURE;
493             }
494
495             /* assure we do not checksum more than maxlen bytes */
496             if ((bufs_read + i) > maxlen)
497                 i = maxlen - bufs_read;
498
499             bufs_read += i;
500
501             /* perform iterative checksumming */
502             i /= 2;
503             wp = (unsigned short *)buf;
504             while (i--) {
505                 if (sum + *wp > 0xFFFF)
506                     sum++;
507
508                 sum += *(wp++);
509             };
510
511             /* prevent eventual fread() if file length > maxlen */
512             if (bufs_read == maxlen)
513                 break;
514         }
515         sum ^= 0xFFFF;
516         lseek (fd, 0, SEEK_SET);
517
518         buf[0] = 0xf0;
519         buf[1] = 0x70;
520         buf[2] = sum;
521         buf[3] = sum >> 8;
522         buf[4] = 0xf0;
523         buf[5] = 0x70;
524         buf[6] = 0x01;
525         buf[7] = 0x00;
526
527         printf("- checksum: %x\n", ntohs(sum));
528     } else { /* result & 32 */
529         /* Firmware series 0.x.y.z - atsingle.bin */
530
531         i = read(fd, buf, 234);
532         if (i < 234) {
533             printf(ERR_READFW);
534             return EXIT_FAILURE;
535         }
536         if (lseek(fd, 256, SEEK_SET) == -1) {
537             perror(_("Error while lseek()ing in firmware file\n"));
538             return EXIT_FAILURE;
539         }
540     }
541
542     result = tftp(in.s_addr, remotefile, fd, buf);
543
544     return(result);
545 }