1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * 4 * Copyright (C) 2012 OpenWrt.org 5 * Copyright (C) 2012 Mikko Hissa <mikko.hissa@uta.fi> 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <stdarg.h> 12 #include <libgen.h> 13 #include <errno.h> 14 #include <arpa/inet.h> 15 #include <unistd.h> 16 #include "md5.h" 17 18 #define HDR_LEN 0x60 19 #define BUF_SIZE 0x200 20 #define VERSION_SIZE 0x10 21 #define MD5_SIZE 0x10 22 #define PAD_SIZE 0x20 23 24 #define DEFAULT_BLOCK_SIZE 65535 25 26 #define DEFAULT_HEAD_VALUE 0x0 27 #define DEFAULT_VERSION "123" 28 #define DEFAULT_MAGIC 0x12345678 29 30 typedef struct { 31 uint32_t head; 32 uint32_t vendor_id; 33 uint32_t product_id; 34 uint8_t version[VERSION_SIZE]; 35 uint32_t firmware_type; 36 uint32_t filesize; 37 uint32_t zero; 38 uint8_t md5sum[MD5_SIZE]; 39 uint8_t pad[PAD_SIZE]; 40 uint32_t chksum; 41 uint32_t magic; 42 } img_header; 43 44 typedef struct { 45 uint8_t id; 46 char * name; 47 } firmware_type; 48 49 typedef enum { 50 NONE, ENCODE, DECODE 51 } op_mode; 52 53 static firmware_type FIRMWARE_TYPES[] = { 54 { 0x00, "combo" }, /* Used for new capwap-included style header */ 55 { 0x01, "bootloader" }, 56 { 0x02, "kernel" }, 57 { 0x03, "kernelapp" }, 58 { 0x04, "apps" }, 59 /* The types below this line vary by manufacturer */ 60 { 0x05, "littleapps (D-Link)/factoryapps (EnGenius)" }, 61 { 0x06, "sounds (D-Link)/littleapps (EnGenius)" }, 62 { 0x07, "userconfig (D-Link)/appdata (EnGenius)" }, 63 { 0x08, "userconfig (EnGenius)"}, 64 { 0x09, "odmapps (EnGenius)"}, 65 { 0x0a, "factoryapps (D-Link)" }, 66 { 0x0b, "odmapps (D-Link)" }, 67 { 0x0c, "langpack (D-Link)" } 68 }; 69 70 #define MOD_DEFAULT 0x616C6C00 71 #define SKU_DEFAULT 0x0 72 #define DATECODE_NONE 0xFFFFFFFF 73 #define FIRMWARE_TYPE_NONE 0xFF 74 75 struct capwap_header { 76 uint32_t mod; 77 uint32_t sku; 78 uint32_t firmware_ver[3]; 79 uint32_t datecode; 80 uint32_t capwap_ver[3]; 81 uint32_t model_size; 82 uint8_t model[]; 83 }; 84 85 static long get_file_size(const char *filename) 86 { 87 FILE *fp_file; 88 long result; 89 90 fp_file = fopen(filename, "r"); 91 if (!fp_file) 92 return -1; 93 fseek(fp_file, 0, SEEK_END); 94 result = ftell(fp_file); 95 fclose(fp_file); 96 return result; 97 } 98 99 static int header_checksum(void *data, size_t len) 100 { 101 int sum = 0; /* shouldn't this be unsigned ? */ 102 size_t i; 103 104 if (data != NULL && len > 0) { 105 for (i = 0; i < len; ++i) 106 sum += ((unsigned char *)data)[i]; 107 return sum; 108 } 109 110 return -1; 111 } 112 113 static int md5_file(const char *filename, uint8_t *dst) 114 { 115 FILE *fp_src; 116 MD5_CTX ctx; 117 char buf[BUF_SIZE]; 118 size_t bytes_read; 119 120 MD5_Init(&ctx); 121 122 fp_src = fopen(filename, "r+b"); 123 if (!fp_src) { 124 return -1; 125 } 126 while (!feof(fp_src)) { 127 bytes_read = fread(&buf, 1, BUF_SIZE, fp_src); 128 MD5_Update(&ctx, &buf, bytes_read); 129 } 130 fclose(fp_src); 131 132 MD5_Final(dst, &ctx); 133 134 return 0; 135 } 136 137 static int encode_image(const char *input_file_name, 138 const char *output_file_name, img_header *header, 139 struct capwap_header *cw_header, int block_size) 140 { 141 char buf[BUF_SIZE]; 142 size_t pad_len = 0; 143 size_t bytes_avail; 144 size_t bytes_read; 145 146 FILE *fp_output; 147 FILE *fp_input; 148 149 int model_size; 150 long magic; 151 size_t i; 152 153 fp_input = fopen(input_file_name, "r+b"); 154 if (!fp_input) { 155 fprintf(stderr, "Cannot open %s !!\n", input_file_name); 156 return -1; 157 } 158 159 fp_output = fopen(output_file_name, "w+b"); 160 if (!fp_output) { 161 fprintf(stderr, "Cannot open %s !!\n", output_file_name); 162 fclose(fp_input); 163 return -1; 164 } 165 166 header->filesize = get_file_size(input_file_name); 167 if (!header->filesize) { 168 fprintf(stderr, "File %s open/size error!\n", input_file_name); 169 fclose(fp_input); 170 fclose(fp_output); 171 return -1; 172 } 173 /* 174 * Zero padding 175 */ 176 if (block_size > 0) { 177 pad_len = block_size - (header->filesize % block_size); 178 } 179 180 if (md5_file(input_file_name, (uint8_t *) &header->md5sum) < 0) { 181 fprintf(stderr, "MD5 failed on file %s\n", input_file_name); 182 fclose(fp_input); 183 fclose(fp_output); 184 return -1; 185 } 186 header->zero = 0; 187 header->chksum = header_checksum(header, HDR_LEN); 188 if (cw_header) { 189 header->chksum += header_checksum(cw_header, 190 sizeof(struct capwap_header) + cw_header->model_size); 191 } 192 193 header->head = htonl(header->head); 194 header->vendor_id = htonl(header->vendor_id); 195 header->product_id = htonl(header->product_id); 196 header->firmware_type = htonl(header->firmware_type); 197 header->filesize = htonl(header->filesize); 198 header->chksum = htonl(header->chksum); 199 magic = header->magic; 200 header->magic = htonl(header->magic); 201 202 fwrite(header, HDR_LEN, 1, fp_output); 203 204 if (cw_header) { 205 model_size = cw_header->model_size; 206 cw_header->mod = htonl(cw_header->mod); 207 cw_header->sku = htonl(cw_header->sku); 208 cw_header->firmware_ver[0] = htonl(cw_header->firmware_ver[0]); 209 cw_header->firmware_ver[1] = htonl(cw_header->firmware_ver[1]); 210 cw_header->firmware_ver[2] = htonl(cw_header->firmware_ver[2]); 211 cw_header->datecode = htonl(cw_header->datecode); 212 cw_header->capwap_ver[0] = htonl(cw_header->capwap_ver[0]); 213 cw_header->capwap_ver[1] = htonl(cw_header->capwap_ver[1]); 214 cw_header->capwap_ver[2] = htonl(cw_header->capwap_ver[2]); 215 cw_header->model_size = htonl(cw_header->model_size); 216 fwrite(cw_header, sizeof(struct capwap_header) + model_size, 1, 217 fp_output); 218 } 219 220 while (!feof(fp_input) || pad_len > 0) { 221 222 if (!feof(fp_input)) 223 bytes_read = fread(&buf, 1, BUF_SIZE, fp_input); 224 else 225 bytes_read = 0; 226 227 /* 228 * No more bytes read, start padding 229 */ 230 if (bytes_read < BUF_SIZE && pad_len > 0) { 231 bytes_avail = BUF_SIZE - bytes_read; 232 memset( &buf[bytes_read], 0, bytes_avail); 233 bytes_read += bytes_avail < pad_len ? bytes_avail : pad_len; 234 pad_len -= bytes_avail < pad_len ? bytes_avail : pad_len; 235 } 236 237 for (i = 0; i < bytes_read; i++) 238 buf[i] ^= magic >> (i % 8) & 0xff; 239 fwrite(&buf, bytes_read, 1, fp_output); 240 } 241 242 fclose(fp_input); 243 fclose(fp_output); 244 return 1; 245 } 246 247 int decode_image(const char *input_file_name, const char *output_file_name) 248 { 249 struct capwap_header cw_header; 250 char buf[BUF_SIZE]; 251 img_header header; 252 253 char *pmodel = NULL; 254 FILE *fp_input; 255 FILE *fp_output; 256 257 size_t bytes_read; 258 size_t bytes_written; 259 unsigned int i; 260 261 fp_input = fopen(input_file_name, "r+b"); 262 if (!fp_input) { 263 fprintf(stderr, "Cannot open %s !!\n", input_file_name); 264 return -1; 265 } 266 267 fp_output = fopen(output_file_name, "w+b"); 268 if (!fp_output) { 269 fprintf(stderr, "Cannot open %s !!\n", output_file_name); 270 fclose(fp_input); 271 return -1; 272 } 273 274 if (fread(&header, 1, HDR_LEN, fp_input) != HDR_LEN) { 275 fprintf(stderr, "Incorrect header size reading base header!!"); 276 fclose(fp_input); 277 fclose(fp_output); 278 return -1; 279 } 280 281 header.head = ntohl(header.head); 282 header.vendor_id = ntohl(header.vendor_id); 283 header.product_id = ntohl(header.product_id); 284 header.firmware_type = ntohl(header.firmware_type); 285 header.filesize = ntohl(header.filesize); 286 header.chksum = ntohl(header.chksum); 287 header.magic = ntohl(header.magic); 288 289 /* read capwap header if firmware_type is zero */ 290 if (header.firmware_type == 0) { 291 if (fread(&cw_header, 1, sizeof(struct capwap_header), 292 fp_input) != sizeof(struct capwap_header)) { 293 fprintf(stderr, "Incorrect header size reading capwap_header!!"); 294 fclose(fp_input); 295 fclose(fp_output); 296 return -1; 297 } 298 cw_header.mod = ntohl(cw_header.mod); 299 cw_header.sku = ntohl(cw_header.sku); 300 cw_header.firmware_ver[0] = ntohl(cw_header.firmware_ver[0]); 301 cw_header.firmware_ver[1] = ntohl(cw_header.firmware_ver[1]); 302 cw_header.firmware_ver[2] = ntohl(cw_header.firmware_ver[2]); 303 cw_header.datecode = ntohl(cw_header.datecode); 304 cw_header.capwap_ver[0] = ntohl(cw_header.capwap_ver[0]); 305 cw_header.capwap_ver[1] = ntohl(cw_header.capwap_ver[1]); 306 cw_header.capwap_ver[2] = ntohl(cw_header.capwap_ver[2]); 307 cw_header.model_size = ntohl(cw_header.model_size); 308 309 pmodel = malloc(cw_header.model_size + 1); 310 if (pmodel) { 311 pmodel[cw_header.model_size] = '\0'; 312 if (fread(pmodel, 1, cw_header.model_size, fp_input) != 313 cw_header.model_size) { 314 fprintf(stderr, "Incorrect header size reading model name!!"); 315 free(pmodel); 316 fclose(fp_input); 317 fclose(fp_output); 318 return -1; 319 } 320 free(pmodel); 321 } else { 322 fprintf(stderr, "Incorrect header size reading model name!!"); 323 fclose(fp_input); 324 fclose(fp_output); 325 return -1; 326 } 327 } 328 329 bytes_written = 0; 330 while (!feof(fp_input)) { 331 332 bytes_read = fread(&buf, 1, BUF_SIZE, fp_input); 333 for (i = 0; i < bytes_read; i++) 334 buf[i] ^= header.magic >> (i % 8) & 0xff; 335 336 /* 337 * Handle padded source file 338 */ 339 if (bytes_written + bytes_read > header.filesize) { 340 bytes_read = header.filesize - bytes_written; 341 if (bytes_read > 0) 342 fwrite(&buf, bytes_read, 1, fp_output); 343 break; 344 } 345 346 fwrite(&buf, bytes_read, 1, fp_output); 347 bytes_written += bytes_read; 348 } 349 350 fclose(fp_input); 351 fclose(fp_output); 352 353 return 1; 354 } 355 356 static void usage(const char *progname, int status) 357 { 358 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; 359 size_t i; 360 361 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); 362 fprintf(stream, "\n" 363 "Options:\n" 364 " -e <file> encode image file <file>\n" 365 " -d <file> decode image file <file>\n" 366 " -o <file> write output to the file <file>\n" 367 " -t <type> set image type to <type>\n" 368 " valid image <type> values:\n"); 369 for (i = 0; i < sizeof(FIRMWARE_TYPES) / sizeof(firmware_type); i++) { 370 fprintf(stream, " %-5i= %s\n", FIRMWARE_TYPES[i].id, 371 FIRMWARE_TYPES[i].name); 372 } 373 fprintf(stream, " -v <version> set image version to <version>\n" 374 " -r <vendor> set image vendor id to <vendor>\n" 375 " -p <product> set image product id to <product>\n" 376 " -m <magic> set encoding magic <magic>\n" 377 " -z enable image padding to <blocksize>\n" 378 " -b <blocksize> set image <blocksize>, defaults to %u\n" 379 " -c <datecode> add capwap header with <datecode> (e.g. 171101)\n" 380 " -w <fw_ver> firmware version for capwap header (e.g. 3.0.1)\n" 381 " -x <cw_ver> capwap firmware version for capwap header (e.g. 1.8.53)\n" 382 " -n <name> model name for capwap header (e.g. ENS620EXT)\n" 383 " -h show this screen\n", DEFAULT_BLOCK_SIZE); 384 exit(status); 385 } 386 387 int main(int argc, char *argv[]) 388 { 389 static const char period[2] = "."; 390 struct capwap_header cw_header; 391 img_header header; 392 393 struct capwap_header *pcw_header = NULL; 394 char *output_file = NULL; 395 char *input_file = NULL; 396 char *progname = NULL; 397 char *mod_name = NULL; 398 char *token; 399 400 op_mode mode = NONE; 401 int tmp, pad = 0; 402 int block_size; 403 size_t i; 404 int opt; 405 406 block_size = DEFAULT_BLOCK_SIZE; 407 progname = basename(argv[0]); 408 409 memset(&header, 0, sizeof(img_header)); 410 header.magic = DEFAULT_MAGIC; 411 header.head = DEFAULT_HEAD_VALUE; 412 header.firmware_type = FIRMWARE_TYPE_NONE; 413 memset(&cw_header, 0, sizeof(struct capwap_header)); 414 cw_header.mod = MOD_DEFAULT; 415 cw_header.sku = SKU_DEFAULT; 416 cw_header.datecode = DATECODE_NONE; 417 strncpy( (char*)&header.version, DEFAULT_VERSION, VERSION_SIZE - 1); 418 419 while ((opt = getopt(argc, argv, ":o:e:d:t:v:r:p:m:b:c:w:x:n:h?z")) != -1) { 420 switch (opt) { 421 case 'e': 422 input_file = optarg; 423 mode = ENCODE; 424 break; 425 case 'd': 426 input_file = optarg; 427 mode = DECODE; 428 break; 429 case 'o': 430 output_file = optarg; 431 break; 432 case 't': 433 tmp = strtol(optarg, 0, 10); 434 for (i = 0; i < sizeof(FIRMWARE_TYPES) / sizeof(firmware_type); 435 i++) { 436 if (FIRMWARE_TYPES[i].id == tmp) { 437 header.firmware_type = FIRMWARE_TYPES[i].id; 438 break; 439 } 440 } 441 if (header.firmware_type == FIRMWARE_TYPE_NONE) { 442 fprintf(stderr, "Invalid firmware type \"\"!\n"); 443 usage(progname, EXIT_FAILURE); 444 } 445 break; 446 case 'v': 447 strncpy( (char*)&header.version, optarg, 448 VERSION_SIZE - 1); 449 break; 450 case 'r': 451 header.vendor_id = strtol(optarg, 0, 0); 452 break; 453 case 'p': 454 header.product_id = strtol(optarg, 0, 0); 455 break; 456 case 'm': 457 header.magic = strtoul(optarg, 0, 16); 458 break; 459 case 'z': 460 pad = 1; 461 break; 462 case 'b': 463 block_size = strtol(optarg, 0, 10); 464 break; 465 case 'c': 466 cw_header.datecode = strtoul(optarg, 0, 10); 467 break; 468 case 'w': 469 token = strtok(optarg, period); 470 i = 0; 471 while (token && (i < 3)) { 472 cw_header.firmware_ver[i++] = 473 strtoul(token, 0, 10); 474 token = strtok(NULL, period); 475 } 476 break; 477 case 'x': 478 token = strtok(optarg, period); 479 i = 0; 480 while (token && (i < 3)) { 481 cw_header.capwap_ver[i++] = 482 strtoul(token, 0, 10); 483 token = strtok(NULL, period); 484 } 485 break; 486 case 'n': 487 mod_name = optarg; 488 cw_header.model_size = strlen(mod_name); 489 break; 490 case 'h': 491 usage(progname, EXIT_SUCCESS); 492 break; 493 case ':': 494 fprintf(stderr, "Option -%c requires an operand\n", optopt); 495 usage(progname, EXIT_FAILURE); 496 break; 497 case '?': 498 fprintf(stderr, "Unrecognized option: -%c\n", optopt); 499 usage(progname, EXIT_FAILURE); 500 break; 501 default: 502 usage(progname, EXIT_FAILURE); 503 break; 504 } 505 } 506 507 /* Check required arguments */ 508 if (mode == NONE) { 509 fprintf(stderr, "A mode must be defined\n"); 510 usage(progname, EXIT_FAILURE); 511 } 512 513 if (input_file == NULL || output_file == NULL) { 514 fprintf(stderr, "Input and output files must be defined\n"); 515 usage(progname, EXIT_FAILURE); 516 } 517 518 if (mode == DECODE) { 519 if (decode_image(input_file, output_file) < 0) 520 return EXIT_FAILURE; 521 522 return EXIT_SUCCESS; 523 } 524 525 if ((header.firmware_type == 0) && 526 (cw_header.datecode == DATECODE_NONE)) { 527 fprintf(stderr, "Firmware type must be non-zero for non-capwap images\n"); 528 usage(progname, EXIT_FAILURE); 529 } 530 531 if (header.vendor_id == 0 || header.product_id == 0) { 532 fprintf(stderr, "Vendor ID and Product ID must be defined and non-zero\n"); 533 usage(progname, EXIT_FAILURE); 534 } 535 536 /* Check capwap header specific arguments */ 537 if (cw_header.datecode != DATECODE_NONE) { 538 if (!mod_name) { 539 fprintf(stderr, "Capwap header specified: model name must be specified\n"); 540 usage(progname, EXIT_FAILURE); 541 } 542 if (!cw_header.firmware_ver[0] && !cw_header.firmware_ver[1] && 543 !cw_header.firmware_ver[2]) { 544 fprintf(stderr, "Capwap header specified, fw_ver must be non-zero\n"); 545 } 546 if (!cw_header.capwap_ver[0] && !cw_header.capwap_ver[1] && 547 !cw_header.capwap_ver[2]) { 548 fprintf(stderr, "Capwap header specified, cw_ver must be non-zero\n"); 549 } 550 pcw_header = malloc(sizeof(struct capwap_header) + 551 cw_header.model_size); 552 if (pcw_header) { 553 memcpy(pcw_header, &cw_header, 554 sizeof(struct capwap_header)); 555 memcpy(&(pcw_header->model), mod_name, 556 cw_header.model_size); 557 } else { 558 fprintf(stderr, "Failed to allocate memory\n"); 559 return EXIT_FAILURE; 560 } 561 } 562 563 if (encode_image(input_file, output_file, &header, pcw_header, 564 pad ? block_size : 0) < 0) 565 return EXIT_FAILURE; 566 567 return EXIT_SUCCESS; 568 } 569
This page was automatically generated by LXR 0.3.1. • OpenWrt