| 1 | #include <stdio.h> |
| 2 | #include <stdint.h> |
| 3 | #include <stdlib.h> |
| 4 | #include <unistd.h> |
| 5 | #include <libgen.h> |
| 6 | #include <stdarg.h> |
| 7 | #include <getopt.h> |
| 8 | #include <string.h> |
| 9 | #include <errno.h> |
| 10 | |
| 11 | #include <netinet/in.h> // htonl |
| 12 | |
| 13 | // Usage: mkdapimg [-p] [-m <model>] -s <sig> -i <input> -o <output> |
| 14 | // |
| 15 | // e.g.: mkdapimg -s RT3052-AP-DAP1350-3 -i sysupgarde.bin -o factory.bin |
| 16 | // |
| 17 | // If the model string <model> is not given, we will assume that |
| 18 | // the leading characters upto the first "-" is the model. |
| 19 | // |
| 20 | // The "-p" (patch) option is used to patch the exisiting image with the |
| 21 | // specified model and signature. |
| 22 | // The "-x" (fix) option will recalculate the payload size and checksum |
| 23 | // during the patch mode operation. |
| 24 | |
| 25 | // The img_hdr_struct was taken from the D-Link SDK: |
| 26 | // DAP-1350_A1_FW1.11NA_GPL/GPL_Source_Code/Uboot/DAP-1350/httpd/header.h |
| 27 | |
| 28 | #define MAX_MODEL_NAME_LEN 20 |
| 29 | #define MAX_SIG_LEN 30 |
| 30 | |
| 31 | struct img_hdr_struct { |
| 32 | uint32_t checksum; |
| 33 | char model[MAX_MODEL_NAME_LEN]; |
| 34 | char sig[MAX_SIG_LEN]; |
| 35 | uint8_t partition; |
| 36 | uint8_t hdr_len; |
| 37 | uint8_t rsv1; |
| 38 | uint8_t rsv2; |
| 39 | uint32_t flash_byte_cnt; |
| 40 | } imghdr ; |
| 41 | |
| 42 | char *progname; |
| 43 | |
| 44 | void |
| 45 | perrexit(int code, char *msg) |
| 46 | { |
| 47 | fprintf(stderr, "%s: %s: %s\n", progname, msg, strerror(errno)); |
| 48 | exit(code); |
| 49 | } |
| 50 | |
| 51 | void |
| 52 | usage() |
| 53 | { |
| 54 | fprintf(stderr, "usage: %s [-p] [-m model] -s signature -i input -o output\n", progname); |
| 55 | exit(1); |
| 56 | } |
| 57 | |
| 58 | int |
| 59 | main(int ac, char *av[]) |
| 60 | { |
| 61 | char model[MAX_MODEL_NAME_LEN+1]; |
| 62 | char signature[MAX_SIG_LEN+1]; |
| 63 | int patchmode = 0; |
| 64 | int fixmode = 0; |
| 65 | |
| 66 | FILE *ifile, *ofile; |
| 67 | int c; |
| 68 | uint32_t cksum; |
| 69 | uint32_t bcnt; |
| 70 | |
| 71 | progname = basename(av[0]); |
| 72 | memset(model, 0, sizeof(model)); |
| 73 | memset(signature, 0, sizeof(signature)); |
| 74 | |
| 75 | while ( 1 ) { |
| 76 | int c; |
| 77 | |
| 78 | c = getopt(ac, av, "pxm:s:i:o:"); |
| 79 | if (c == -1) |
| 80 | break; |
| 81 | |
| 82 | switch (c) { |
| 83 | case 'p': |
| 84 | patchmode = 1; |
| 85 | break; |
| 86 | case 'x': |
| 87 | fixmode = 1; |
| 88 | break; |
| 89 | case 'm': |
| 90 | if (strlen(optarg) > MAX_MODEL_NAME_LEN) { |
| 91 | fprintf(stderr, "%s: model name exceeds %d chars\n", |
| 92 | progname, MAX_MODEL_NAME_LEN); |
| 93 | exit(1); |
| 94 | } |
| 95 | strcpy(model, optarg); |
| 96 | break; |
| 97 | case 's': |
| 98 | if (strlen(optarg) > MAX_SIG_LEN) { |
| 99 | fprintf(stderr, "%s: signature exceeds %d chars\n", |
| 100 | progname, MAX_SIG_LEN); |
| 101 | exit(1); |
| 102 | } |
| 103 | strcpy(signature, optarg); |
| 104 | break; |
| 105 | case 'i': |
| 106 | if ((ifile = fopen(optarg, "r")) == NULL) |
| 107 | perrexit(1, optarg); |
| 108 | break; |
| 109 | case 'o': |
| 110 | if ((ofile = fopen(optarg, "w")) == NULL) |
| 111 | perrexit(1, optarg); |
| 112 | break; |
| 113 | default: |
| 114 | usage(); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | if (signature[0] == 0 || ifile == NULL || ofile == NULL) { |
| 119 | usage(); |
| 120 | } |
| 121 | |
| 122 | if (model[0] == 0) { |
| 123 | char *p = strchr(signature, '-'); |
| 124 | if (p == NULL) { |
| 125 | fprintf(stderr, "%s: model name unknown\n", progname); |
| 126 | exit(1); |
| 127 | } |
| 128 | if (p - signature > MAX_MODEL_NAME_LEN) { |
| 129 | *p = 0; |
| 130 | fprintf(stderr, "%s: auto model name failed, string %s too long\n", progname, signature); |
| 131 | exit(1); |
| 132 | } |
| 133 | strncpy(model, signature, p - signature); |
| 134 | } |
| 135 | |
| 136 | if (patchmode) { |
| 137 | if (fread(&imghdr, sizeof(imghdr), 1, ifile) < 0) |
| 138 | perrexit(2, "fread on input"); |
| 139 | } |
| 140 | |
| 141 | for (bcnt = 0, cksum = 0 ; (c = fgetc(ifile)) != EOF ; bcnt++) |
| 142 | cksum += c & 0xff; |
| 143 | |
| 144 | if (fseek(ifile, patchmode ? sizeof(imghdr) : 0, SEEK_SET) < 0) |
| 145 | perrexit(2, "fseek on input"); |
| 146 | |
| 147 | if (patchmode == 0) { |
| 148 | // Fill in the header |
| 149 | memset(&imghdr, 0, sizeof(imghdr)); |
| 150 | imghdr.checksum = htonl(cksum); |
| 151 | imghdr.partition = 0 ; // don't care? |
| 152 | imghdr.hdr_len = sizeof(imghdr); |
| 153 | imghdr.flash_byte_cnt = htonl(bcnt); |
| 154 | } else { |
| 155 | if (ntohl(imghdr.checksum) != cksum) { |
| 156 | fprintf(stderr, "%s: patch mode, checksum mismatch\n", |
| 157 | progname); |
| 158 | if (fixmode) { |
| 159 | fprintf(stderr, "%s: fixing\n", progname); |
| 160 | imghdr.checksum = htonl(cksum); |
| 161 | } else |
| 162 | exit(3); |
| 163 | } else if (ntohl(imghdr.flash_byte_cnt) != bcnt) { |
| 164 | fprintf(stderr, "%s: patch mode, size mismatch\n", |
| 165 | progname); |
| 166 | if (fixmode) { |
| 167 | fprintf(stderr, "%s: fixing\n", progname); |
| 168 | imghdr.flash_byte_cnt = htonl(bcnt); |
| 169 | } else |
| 170 | exit(3); |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | strncpy(imghdr.model, model, MAX_MODEL_NAME_LEN); |
| 175 | strncpy(imghdr.sig, signature, MAX_SIG_LEN); |
| 176 | |
| 177 | if (fwrite(&imghdr, sizeof(imghdr), 1, ofile) < 0) |
| 178 | perrexit(2, "fwrite header on output"); |
| 179 | |
| 180 | while ((c = fgetc(ifile)) != EOF) { |
| 181 | if (fputc(c, ofile) == EOF) |
| 182 | perrexit(2, "fputc on output"); |
| 183 | } |
| 184 | |
| 185 | if (ferror(ifile)) |
| 186 | perrexit(2, "fgetc on input"); |
| 187 | |
| 188 | |
| 189 | fclose(ofile); |
| 190 | fclose(ifile); |
| 191 | } |
| 192 | |