X-Git-Url: https://git.decadent.org.uk/gitweb/?p=ap-utils.git;a=blobdiff_plain;f=src%2Fap-tftp.c;fp=src%2Fap-tftp.c;h=5e6ac579cbd05651b0292d994545aef51e9f810b;hp=990de43e7eea26f48004fc185cf89fadc1c25f7d;hb=17cd5711411f45df26b33c51288780627b7377e2;hpb=63444196dd1edb154f81d9418b3d0bc2367163e5 diff --git a/src/ap-tftp.c b/src/ap-tftp.c index 990de43..5e6ac57 100644 --- a/src/ap-tftp.c +++ b/src/ap-tftp.c @@ -1,20 +1,22 @@ /* ------------------------------------------------------------------------- */ /* ap-tftp.c */ /* */ +/* Version: 1.1 */ +/* */ +/* Copyright (C) 2004-2005 Jan Rafaj */ +/* */ /* A simple tftp client for upgrading ATMEL AT76C510 WiSOC-based APs. */ /* Supports ATMEL+INTERSIL boards (1.4x.y firmware) and ATMEL+RFMD boards */ /* (0.x.y.z firmware). */ -/* Modelled around TELLUS (GEMTEK/ATMEL OEM) TFTP client functionality. */ +/* Modelled around TELLUS (GEMTEK/ATMEL OEM) and SMARTBRIDGES TFTP clients */ +/* functionality. */ +/* Tested with TELLUS, D-Link and SmartBridges hardware. */ /* This program is part of AP-UTILS project (http://ap-utils.polesye.net) */ /* */ -/* Copyright (C) 2004-2005 Jan Rafaj */ -/* */ /* Loosely based on a simple tftp client for busybox. */ /* Tries to follow RFC1350. */ /* Only "octet" mode and "put" method supported. */ /* */ -/* Version: 1.0 */ -/* */ /* Not implemented: */ /* - uploading of OEM (default) settings */ /* - uploading of PATCH code */ @@ -56,6 +58,7 @@ #include #include #include +#include "ap-utils.h" #ifdef HAVE_GETTEXT /* GNU gettext stuff*/ @@ -66,7 +69,7 @@ #define _(String) (String) #endif -#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ +#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, do not change */ #define TFTP_TIMEOUT 2 /* # seconds waiting for single response */ #define TFTP_RETRIES 5 /* # retries with TFTP_TIMEOUT */ #undef FEATURE_TFTP_DEBUG @@ -83,8 +86,17 @@ #endif #define ERR_READFW _("Error while read()ing firmware file") + +/* This is necessary becouse of linkage to libap.a */ +short ap_type = ATMEL410; +char *community = NULL; +struct in_addr ap_ip; + +int tftp(int ip, const char *remotefile, int localfd, const char *wopt, + int woptlen, int maxdlen) +{ /* known errors server may respond with */ -static const char *tftp_error_msg[] = { +char *tftp_error_msg[8] = { _("Undefined error"), _("File not found"), _("Access violation"), @@ -94,14 +106,10 @@ static const char *tftp_error_msg[] = { _("File already exists"), _("No such user") }; - -int tftp(int ip, const char *remotefile, int localfd, const char *options) -{ const int port = 69; struct sockaddr_in to; struct sockaddr_in from; struct timeval tv; - struct stat sb; fd_set rfds; unsigned short tmp; int fromlen; @@ -112,8 +120,7 @@ int tftp(int ip, const char *remotefile, int localfd, const char *options) int finished = 0; int socketfd; int len; - int maxlen; - int optlen; + int bc = lseek(localfd, 0, SEEK_CUR); char *nl = ""; char *cp; char *buf = malloc(tftp_bufsize); @@ -121,17 +128,6 @@ int tftp(int ip, const char *remotefile, int localfd, const char *options) printf("Trying to upload firmware to the AP...\n"); - if (!strcmp(remotefile, "atbrfirm.bin")) { - optlen = 8; - maxlen = 94448; - } else { - /* remotefile = atsingle.bin */ - optlen = 234; - maxlen = 0; - } - - fstat(localfd, &sb); - if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket()"); return EXIT_FAILURE; @@ -157,7 +153,7 @@ int tftp(int ip, const char *remotefile, int localfd, const char *options) if (opcode == TFTP_WRQ) { /* see if the filename fits into buf */ len = strlen(remotefile) + 1; - if ((cp + len + 1 + 6 + optlen) >= &buf[tftp_bufsize - 1]) { + if ((cp + len + 1 + 6 + woptlen) >= &buf[tftp_bufsize - 1]) { printf(_("Remote-filename too long.\n")); break; } @@ -173,8 +169,8 @@ int tftp(int ip, const char *remotefile, int localfd, const char *options) cp += 6; // ATMEL tftp client specific ("upload auth. code") - memcpy(cp, options, optlen); - cp += optlen; + memcpy(cp, wopt, woptlen); + cp += woptlen; } if (opcode == TFTP_DATA) { @@ -192,13 +188,14 @@ int tftp(int ip, const char *remotefile, int localfd, const char *options) block_nr++; /* - * assure we wont upload more than maxlen bytes - * even if total file length > maxlen + * assure we wont upload more than maxdlen bytes + * even if total file length > maxdlen */ - if (maxlen && (block_nr - 2) * tftp_bufsize + len > maxlen) { - len = maxlen - (block_nr - 2) * tftp_bufsize; + if (bc + len > maxdlen) { + len = maxdlen - bc; cp[len] = '\0'; } + bc += len; if (len != tftp_bufsize) finished++; @@ -256,10 +253,8 @@ int tftp(int ip, const char *remotefile, int localfd, const char *options) if (to.sin_port == from.sin_port) { #ifndef FEATURE_TFTP_DEBUG if (opcode == TFTP_DATA) { - float f = (block_nr - 2) * tftp_bufsize + len; - - sprintf(buf2, _("\rProgress: uploaded %.0f %%."), - f / sb.st_size * 100); + sprintf(buf2, _("\rProgress: uploaded %3i%%."), + bc * 100 / maxdlen); write(fileno(stdout), buf2, strlen(buf2)); nl = "\n"; } @@ -338,12 +333,13 @@ int tftp(int ip, const char *remotefile, int localfd, const char *options) void usage(char **argv) { printf (_("PLEASE BE _ABSOLUTELY_ SURE TO READ MANPAGE PRIOR USE!!!\n")); - printf (_("\nUsage: %s <-l firmware_file.rom> \n"), argv[0]); + printf (_( + "\nUsage: %s -i IP -f firmware_file.rom [-c community] [-h]\n"), + argv[0]); } int main(int argc, char **argv) { - struct in_addr in; struct stat sb; char *localfile = NULL; char *remotefile = NULL; @@ -351,7 +347,9 @@ int main(int argc, char **argv) int fd; int result = 0; int i; - static char buf[256]; + static char wopt[256]; + int woptlen; + int maxdlen; #ifdef HAVE_GETTEXT /* locale support init */ @@ -365,75 +363,87 @@ int main(int argc, char **argv) printf (_("(C) 2004-2005 Jan Rafaj " "\n")); - while ((i = getopt(argc, argv, "l:h")) != -1) { + if (argc == 1) { + usage(argv); + return EXIT_SUCCESS; + } + + ap_ip.s_addr = 0; + + while ((i = getopt(argc, argv, "i:f:c:h")) != -1) { switch (i) { - case 'l': + case 'i': + /* host = gethostbyname(optarg); */ + for (cp = optarg, i = 0; *cp && (cp = index(cp, '.')); cp++, i++); + if (i < 3 || !(inet_aton(optarg, &ap_ip))) { + printf(_("Error: invalid IP address format given.\n")); + result = EXIT_FAILURE; + goto quit; + } + break; + case 'f': localfile = strdup(optarg); break; + case 'c': + community = strdup(optarg); + break; case 'h': usage(argv); - return EXIT_SUCCESS; + result = EXIT_SUCCESS; + goto quit; default: - return EXIT_FAILURE; + result = EXIT_FAILURE; + goto quit; } } - if (argc == 1) { - usage(argv); - return EXIT_SUCCESS; - } - /* - * either no mandatory opts, or too few mandatory opts, or more than 1 - * non-opt arg. specified, or no non-option arg. specified + * either any of mandatory options (IP, firmware path) not specified, + * or some extra non-option arguments specified */ - if (optind == 1 || optind < 3 || argc > 4 || optind == argc) { - printf(_("Error: invalid arguments given.\n")); - return EXIT_FAILURE; + if (ap_ip.s_addr == 0 || !localfile || optind != argc) { + printf(_("Error: invalid argument combination.\n")); + result = EXIT_FAILURE; + goto quit; } - /* host = gethostbyname(argv[optind]); */ - for (cp = argv[optind], i = 0; *cp && (cp = index(cp, '.')); cp++, i++); - if (i < 3 || !(inet_aton(argv[optind], &in))) { - printf(_("Error: invalid IP address format given.\n")); - return EXIT_FAILURE; - } - inet_aton(argv[optind], &in); - fd = open(localfile, O_RDONLY, 0644); if (fd < 0) { perror(_("Error while open()ing firmware file")); - return EXIT_FAILURE; + result = EXIT_FAILURE; + goto quit; } fstat(fd, &sb); if (sb.st_size < 94448 || sb.st_size > 524288) { printf(_("Error: invalid firmware file given.\n")); - return EXIT_FAILURE; + result = EXIT_FAILURE; + goto quit; } - while ((i = read(fd, buf, sizeof(buf))) != 0) { + while ((i = read(fd, wopt, sizeof(wopt))) != 0) { if (i < 0) { perror(ERR_READFW); - return EXIT_FAILURE; + result = EXIT_FAILURE; + goto quit; } - if (i == sizeof(buf)) + if (i == sizeof(wopt)) lseek(fd, -13, SEEK_CUR); while (i--) { - if (!(memcmp(&buf[i], "ATMEL", 5))) + if (!(memcmp(&wopt[i], "ATMEL", 5))) result |= 1; - if (!(memcmp(&buf[i], "802.11 AP", 9))) + if (!(memcmp(&wopt[i], "802.11 AP", 9))) result |= 2; - if (!(memcmp(&buf[i], "atbrfirm.bin", 12))) { + if (!(memcmp(&wopt[i], "atbrfirm.bin", 12))) { result |= 16; remotefile = "atbrfirm.bin"; } - if (!(memcmp(&buf[i], "atsingle.bin", 12))) { + if (!(memcmp(&wopt[i], "atsingle.bin", 12))) { result |= 32; remotefile = "atsingle.bin"; } @@ -445,7 +455,7 @@ int main(int argc, char **argv) if (result > 18) break; - memset(buf, 0, sizeof(buf)); + memset(wopt, 0, sizeof(wopt)); } #if FEATURE_TFTP_DEBUG printf(_("Firmware file contains:\n" @@ -460,7 +470,8 @@ int main(int argc, char **argv) (result & (16|32)) == 0) { printf(_("Error: invalid firmware file given.\n")); - return EXIT_FAILURE; + result = EXIT_FAILURE; + goto quit; } lseek(fd, 0, SEEK_SET); @@ -475,32 +486,118 @@ int main(int argc, char **argv) "- server: %s\n" "- firmware file: \"%s\"\n" "- name used for upload: \"%s\"\n"), - inet_ntoa(in), cp, remotefile); + inet_ntoa(ap_ip), cp, remotefile); if (result & 16) { /* Firmware series 1.4x.y - atbrfirm.bin */ + /* + * For most devices using 1.4x.y firmware: + * - at most 94448 bytes needs to be written (file content above + * this offset is simply cut off) + * - checksum is only performed on the uploaded (first 94448) + * bytes of the firmware file + * - auth. code has 8 bytes in length (see below for its structure) + * SmartBridges, however, differs in this: + * - the TFTP firmware upgrade utility first checks whether the OUI + * portion of its MAC address is 00301A (SMARTBRIDGES PTE. LTD.), + * followed by 09 (apparently AT76C510-based SmartBridges devices), + * and will continue only upon match. This is to prevent performing + * this SmartBridges-style upgrade to non-SmartBridges devices, that + * could otherwise end up with damaged flash content. Note that + * this is still somewhat clumsy, since there's a remote possibility + * that someone redefined his/her ATMEL AP MAC address with + * SmartBridges OUI and added 09, so this check would not prevent + * damage in this case (ouch!). There's however, a better check + * we could come up up with: lets borrow a part of the code from + * get_mib_details() to find out whether the target device appears + * to be running firmware with ATMEL410 SBRIDGES MIB, and proceed + * with 'SmartBridges' upgrade method only, if it does. + * - entire file is being written; there's no 'first 94448 bytes only' + * limitation + * - 8-byte auth. code (see below) is followed by: + * - (char): length of the following "password" string + * - (string): "password" string - the firmware will perform + * actual upgrade only if this "password" matches with + * any of the three communities defined in the AP + * (sysadmin's note: finally something reasonable) + */ + unsigned short sum = 0; unsigned short *wp; - int maxlen = 94448; - int bufs_read = 0; + int readen = 0; + + if (community) { + /* + * OK, looks like we want to upgrade SmartBridges hardware => no + * firmware file cutoff, and lets check whether the target device + * appears to have SmartBridges firmware. If not, bail out. + */ +/* + char operEthernetAddress[12] = { + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, + 0x01, 0x01, 0x02, 0x03, 0x00 + }; +*/ + char AuthRadiusIP[] = { + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, + 0x01, 0x02, 0x06, 0x03, 0x00 + }; + varbind varbinds[1]; + + maxdlen = sb.st_size; + +/* + varbinds[0].oid = operEthernetAddress; + varbinds[0].len_oid = sizeof(operEthernetAddress); +*/ + varbinds[0].oid = AuthRadiusIP; + varbinds[0].len_oid = sizeof(AuthRadiusIP); + varbinds[0].value = NULL; + varbinds[0].len_val = 0; + varbinds[0].type = NULL_VALUE; + if (snmp(varbinds, 1, GET) > 0) { +/* + i = varbinds[0].value[0] << 24 | + varbinds[0].value[1] << 16 | + varbinds[0].value[2] << 8 | + varbinds[0].value[3]; + if (i != 0x00301A09) { +*/ + if (varbinds[0].len_val != 4) { + printf(_("Error: device with the given IP does not seem " + "to run SmartBridges firmware.\n")); + result = EXIT_FAILURE; + goto quit; + } + } else { + printf(_("Error: SNMP authorization error or device unavailable" + ".\n")); + result = EXIT_FAILURE; + goto quit; + } + } else { + /* non-SB hardware */ + maxdlen = 94448; + } /* compute checksum */ - while ((i = read(fd, buf, sizeof(buf))) != 0) { + while ((i = read(fd, wopt, sizeof(wopt))) != 0) { if (i < 0) { perror(ERR_READFW); - return EXIT_FAILURE; + result = EXIT_FAILURE; + goto quit; } - /* assure we do not checksum more than maxlen bytes */ - if ((bufs_read + i) > maxlen) - i = maxlen - bufs_read; + /* assure we do not checksum more than maxdlen bytes */ + if ((readen + i) > maxdlen) + i = maxdlen - readen; - bufs_read += i; + readen += i; /* perform iterative checksumming */ i /= 2; - wp = (unsigned short *)buf; + wp = (unsigned short *)wopt; while (i--) { if (sum + *wp > 0xFFFF) sum++; @@ -508,38 +605,66 @@ int main(int argc, char **argv) sum += *(wp++); }; - /* prevent eventual fread() if file length > maxlen */ - if (bufs_read == maxlen) + /* prevent eventual fread() if file length > maxdlen */ + if (readen == maxdlen) break; } sum ^= 0xFFFF; lseek (fd, 0, SEEK_SET); - buf[0] = 0xf0; - buf[1] = 0x70; - buf[2] = sum; - buf[3] = sum >> 8; - buf[4] = 0xf0; - buf[5] = 0x70; - buf[6] = 0x01; - buf[7] = 0x00; + /* + * wopt[0] - wopt[1] are fw length in unsigned short form + * wopt[2] - wopt[3] are fw checksum in unsigned short form + * wopt[4] - wopt[7] are fw length in unsigned int form + */ + wopt[0] = maxdlen; + wopt[1] = maxdlen >> 8; + wopt[2] = sum; + wopt[3] = sum >> 8; + wopt[4] = maxdlen; + wopt[5] = maxdlen >> 8; + wopt[6] = maxdlen >> 16; + wopt[7] = maxdlen >> 24; printf("- checksum: %x\n", ntohs(sum)); + + woptlen = 8; + + if (community) { + wopt[8] = strlen(community); + memcpy((void *) &wopt[9], (void *)community, strlen(community)); + woptlen += (1 + strlen(community)); + } } else { /* result & 32 */ /* Firmware series 0.x.y.z - atsingle.bin */ - i = read(fd, buf, 234); + /* For 0.x.y.z firmware: + * - first 234 bytes from the file are used as auth. code + * - the actual firmware is being uploaded since offset 256 in the file + */ + + i = read(fd, wopt, 234); if (i < 234) { printf(ERR_READFW); - return EXIT_FAILURE; + result = EXIT_FAILURE; + goto quit; } if (lseek(fd, 256, SEEK_SET) == -1) { perror(_("Error while lseek()ing in firmware file\n")); - return EXIT_FAILURE; + result = EXIT_FAILURE; + goto quit; } + woptlen = 234; + maxdlen = sb.st_size; /* no limit */ } - result = tftp(in.s_addr, remotefile, fd, buf); + result = tftp(ap_ip.s_addr, remotefile, fd, wopt, woptlen, maxdlen); + +quit: + if (localfile) + free(localfile); + if (community) + free(community); return(result); }