1
0
This repository has been archived on 2025-01-10. You can view files and clone it, but cannot push or open issues or pull requests.
alacn1_pace_v5471/updatefw.cpp
Anderson Luiz Alves a59c539a2c updatefw: updated usage
2017-07-30 16:24:58 -03:00

1470 lines
25 KiB
C++

/**
* Anderson Luiz Alves
* alacn1@gmail.com
*
* 2017-07-13
*
*
* license: public domain
*
* require: libssl-dev
* compile: g++ -o updatefw updatefw.cpp -lcrypto
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <openssl/md5.h> // libssl-dev
#include <netdb.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#define SZ_VERSION "v0.3"
#define ERR -1
#define ERR_HEADER -2
#define ERR_CHECKSUM -3
#define ERR_PARTITION -4
#define ERR_ARGS -50
#define ERR_MEM -100
#define ERR_FILEOPEN -101
#define ERR_FILECREATE -102
#define ERR_FILEREAD -103
#define ERR_FILEWRITE -104
#define ERR_FILESEEK -105
#define ERR_FILEEXISTS -106
#define ERR_SOCKET_ADDRESS -200
#define ERR_SOCKET_CONNECT -201
#define ERR_SOCKET_SEND -202
#define ERR_SOCKET_READ -203
#define BUFSIZE 4096
#define SENDSIZE 1280
#define SEND_WAIT_MSEC 2
#define PRODCODE 0xB14103
#define RECOVERY_ADDRESS "239.255.255.240"
#define RECOVERY_PORT "53428"
bool FORCE = false;
typedef int INT32;
typedef short INT16;
/** bigendian int */
struct INT32B
{
INT32 value;
INT32 invert(INT32 v) const
{
INT32 ret;
unsigned char *pret = (unsigned char *)&ret;
unsigned char *pv = (unsigned char *)&v;
for(size_t n=0; n<sizeof(v); ++n)
pret[n] = pv[sizeof(v)-n-1];
return ret;
}
INT32 get() const
{
return invert(value);
}
void set(INT32 v)
{
value = invert(v);
}
operator INT32 () const
{
return get();
}
INT32B& operator = (const INT32 v)
{
set(v);
return *this;
}
INT32B& operator += (const INT32 v)
{
set(get() + v);
return *this;
}
INT32B& operator -= (const INT32 v)
{
set(get() - v);
return *this;
}
};
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
// https://github.com/torvalds/linux/blob/master/fs/squashfs/squashfs_fs.h
#define SQUASHFS_MAGIC 0x73717368 // hsqs
#define SQUASHFS_MAGIC_SWAP 0x71736873 // shsq
#define ZLIB_COMPRESSION 1
#define LZMA_COMPRESSION 2
#define LZO_COMPRESSION 3
#define XZ_COMPRESSION 4
#define LZ4_COMPRESSION 5
struct SQUASHFS_SUPERBLOCK
{
INT32 s_magic;
INT32 inodes;
INT32 mkfs_time;
INT32 block_size;
INT32 fragments;
INT16 compression;
INT16 block_log;
INT16 flags;
INT16 no_ids;
INT16 s_major;
INT16 s_minor;
};
#define MAX_PARTITION 3
#define CHECKSUM_SIZE 16
struct PARTITION // 284
{
char name[64]; // size 64?
INT32B offset;
INT32B size;
char unk[212]; // x0
};
struct HEADER // 4096
{
char copyright[80]; // x0 // Copyright (c) 2006-2010 BeWAN Systems
char platform[80]; // x50 // V5471 iBoxNG GVT
char sprodcode[8]; // xa0 // B14103
char filename[80]; // xa8 // B14103-GVT-RC2-98509.bin
char date[24]; // xf8 // 2015-03-04-15:55:18
char releasename[8]; // x110 // GVT-RC2 // size 8?
char unk0[748]; // x118 // 00
INT32B bootstatus; // x404 // 0x4E415742 = will boot, ffffffff on checksum
INT32B check; // x408 // 0x4E415742
INT32B size; // x40c // firmware size
INT32B bootorder; // x410 // 00000000 on checksum
INT32B prodcode; // x414 // 0xB14103
INT32B svn_version; // x418
PARTITION partition[MAX_PARTITION]; // x41c
char unk1[2176];
unsigned char checksum[CHECKSUM_SIZE]; // xff0 // md5(0-xff0,x1000-) ^ magic
};
// magics for B14103
const unsigned char CHECKSUM_MAGIC[CHECKSUM_SIZE] = {
0x0A, 0x20, 0x20, 0x42,
0x7C, 0x22, 0x61, 0xDB,
0x12, 0x78, 0xB2, 0x9B,
0x27, 0xB2, 0xCE, 0x31
};
// firmware recovery fragment
struct FRAGMENT
{
INT32B seq;
INT32B unk0;
INT32B prodcode; // 0x00B14103
INT32B address; // 0x80800000
INT32B fwsize;
INT32B fragsize;
};
int err_mem()
{
printf("ERROR: memory\n");
return ERR_MEM;
}
int err_fileopen()
{
printf("ERROR: file open failed\n");
return ERR_FILEOPEN;
}
int err_filecreate()
{
printf("ERROR: file create failed\n");
return ERR_FILECREATE;
}
int err_fileread()
{
printf("ERROR: file read failed\n");
return ERR_FILEREAD;
}
int err_filewrite()
{
printf("ERROR: file write failed\n");
return ERR_FILEWRITE;
}
int err_fileseek(int offset)
{
printf("ERROR: seek offset %X failed\n", offset);
return ERR_FILESEEK;
}
int err_fileexists()
{
printf("ERROR: file already exists\n");
return ERR_FILEEXISTS;
}
int err_header()
{
printf("ERROR: invalid header\n");
return ERR_HEADER;
}
int err_socket_address()
{
printf("ERROR: get socket address failed\n");
return ERR_SOCKET_ADDRESS;
}
int err_socket_connect()
{
printf("ERROR: create socket failed\n");
return ERR_SOCKET_CONNECT;
}
int err_socket_send()
{
printf("ERROR: socket send failed\n");
return ERR_SOCKET_SEND;
}
void stripext(char *fname)
{
char *s = fname;
while(0 != *s) ++s;
for(; s > fname; --s)
{
if('.' == *s)
{
*s = 0;
break;
}
if(('\\' == *s) || ('/' == *s)) break;
}
}
const char* basename(const char *fname)
{
const char *s = fname;
while(0 != *s) ++s;
for(; s >= fname; --s)
if(('\\' == *s) || ('/' == *s))
return s+1;
return fname;
}
char* changeext(const char *fname, const char *newext)
{
char *s = (char*)malloc(
strlen(fname) + sizeof(char) +
strlen(newext) + sizeof(char));
if(0 == s) return 0;
strcpy(s, fname);
stripext(s);
strcat(s, ".");
strcat(s, newext);
return s;
}
bool exists(const char *fname)
{
struct stat t;
return (0 == stat(fname, &t));
}
int getchecksum(FILE *f, unsigned char *checksum, HEADER *header=0)
{
unsigned char buf[BUFSIZE];
// read header from file
if(0 == header)
{
if(0 != fseek(f, 0, SEEK_SET))
return err_fileseek(0);
clearerr(f);
if(fread(buf, 1, sizeof(HEADER), f) < sizeof(HEADER))
{
if(ferror(f)) return err_fileread();
return err_header();
}
}
else
// use arg header
{
if(0 != fseek(f, sizeof(HEADER), SEEK_SET))
return err_fileseek(sizeof(HEADER));
memcpy(buf, header, sizeof(HEADER));
}
// required for checksum
header = (HEADER*)buf;
header->bootorder = 0;
header->bootstatus = 0xffffffff;
// md5
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, header, sizeof(HEADER)-CHECKSUM_SIZE);
// read data
clearerr(f);
for(;;)
{
int r = fread(buf, 1, sizeof(buf), f);
if(r <= 0)
{
if(ferror(f)) return err_fileread();
break;
}
MD5_Update(&ctx, buf, r);
}
MD5_Final(checksum, &ctx);
for(int n=0; n<CHECKSUM_SIZE; ++n)
checksum[n] ^= CHECKSUM_MAGIC[n];
return 0;
}
int fwinfo(const char *fname)
{
printf("file: %s\n", fname);
FILE *f = fopen(fname, "rb");
if(0 == f) return err_fileopen();
// load header
HEADER header;
clearerr(f);
if(fread(&header, 1, sizeof(header), f) < sizeof(header))
{
if(ferror(f))
{
fclose(f);
return err_fileread();
}
fclose(f);
return err_header();
}
// display info
printf("copyright: %s\n", header.copyright);
printf("platform: %s\n", header.platform);
printf("sprodcode: %s\n", header.sprodcode);
printf("filename: %s\n", header.filename);
printf("date: %s\n", header.date);
printf("releasename: %s\n", header.releasename);
printf("prodcode: 0x%X\n", header.prodcode.get());
printf("svn version: %d\n", header.svn_version.get());
printf("somecheck: 0x%X\n", header.check.get());
printf("boot status: 0x%X\n", header.bootstatus.get());
printf("boot order: %d\n", header.bootorder.get());
printf("firmware size: %d", header.size.get());
printf(
"\n"
"partitions:\n"
);
PARTITION *part = &(header.partition[0]);
for(int idx=0; idx<MAX_PARTITION; ++idx, ++part)
{
// empty?
if((0 == part->name[0]) && (0 == part->offset) && (0 == part->size)) continue;
printf(" %d\n", idx);
printf(" name: %s\n", part->name);
int n;
n = part->offset;
printf(" offset: %d (0x%X)\n", n, n);
n = part->size;
printf(" size: %d (0x%X)\n", n, n);
}
printf(
"checksum: "
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"\n",
header.checksum[0], header.checksum[1], header.checksum[2], header.checksum[3],
header.checksum[4], header.checksum[5], header.checksum[6], header.checksum[7],
header.checksum[8], header.checksum[9], header.checksum[10], header.checksum[11],
header.checksum[12], header.checksum[13], header.checksum[14], header.checksum[15]
);
// verify checksum
unsigned char verify[CHECKSUM_SIZE];
int ret = getchecksum(f, verify, &header);
if(0 != ret)
{
fclose(f);
return ret;
}
if(0 == memcmp(header.checksum, verify, sizeof(verify)))
{
printf("checksum ok!\n");
}
else
{
printf(
"mismatch: "
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"\n",
verify[0], verify[1], verify[2], verify[3],
verify[4], verify[5], verify[6], verify[7],
verify[8], verify[9], verify[10], verify[11],
verify[12], verify[13], verify[14], verify[15]
);
}
// file size
if(0 != fseek(f, 0, SEEK_END))
{
fclose(f);
return err_fileseek(0);
}
int fsize = ftell(f);
if(fsize != header.size)
printf(
"WARNING: firmware size %d %s file size %d\n",
header.size.get(),
((header.size < fsize)? "<" : ">"),
fsize
);
fclose(f);
return 0;
}
int fwextractheader(const char *fname, const HEADER *header)
{
char *dname = changeext(fname, "header");
if(0 == dname) return err_mem();
printf("saving %s\n", dname);
if(exists(dname))
{
free(dname);
return err_fileexists();
}
FILE *d = fopen(dname, "w+b");
if(0 == d)
{
free(dname);
return err_filecreate();
}
if(fwrite(header, 1, sizeof(HEADER), d) < sizeof(HEADER))
{
fclose(d);
free(dname);
return err_filewrite();
}
fclose(d);
free(dname);
return 0;
}
int fwextractpart(const char *fname, FILE *f, const PARTITION *part, const char *pname)
{
char *dname = changeext(fname, pname);
if(0 == dname) return err_mem();
printf("saving %s\n", dname);
if(0 != strcmp(pname, part->name))
printf("WARNING: partition name is not %s\n", pname);
int size = part->size;
if(size <= 0)
{
free(dname);
printf("WARNING: empty partition\n");
return ERR_PARTITION;
}
if(0 != fseek(f, part->offset, SEEK_SET))
{
free(dname);
return err_fileseek(part->offset);
}
if(exists(dname))
{
free(dname);
return err_fileexists();
}
FILE *d = fopen(dname, "w+b");
if(0 == d)
{
free(dname);
return err_filecreate();
}
unsigned char buf[BUFSIZE];
clearerr(f);
while(size > 0)
{
int r = (size > sizeof(buf))? sizeof(buf) : size;
size -= r;
if(fread(buf, 1, r, f) < r)
{
fclose(d);
free(dname);
return err_fileread();
}
if(fwrite(buf, 1, r, d) < r)
{
fclose(d);
free(dname);
return err_filewrite();
}
}
fclose(d);
free(dname);
return 0;
}
int fwextract(const char *fname)
{
printf("extracting %s\n", fname);
FILE *f = fopen(fname, "rb");
if(0 == f) return err_fileopen();
// load header
HEADER header;
clearerr(f);
if(fread(&header, 1, sizeof(header), f) < sizeof(header))
{
if(ferror(f))
{
fclose(f);
return err_fileread();
}
fclose(f);
return err_header();
}
// verify checksum
unsigned char verify[CHECKSUM_SIZE];
if(
(0 != getchecksum(f, verify, &header)) ||
(0 != memcmp(header.checksum, verify, sizeof(verify)))
)
{
printf("WARNING: checksum mismatch!\n");
if(!FORCE)
{
fclose(f);
printf("use -F to ignore.\n");
return ERR_CHECKSUM;
}
}
int ret = 0, r;
// header
r = fwextractheader(fname, &header);
if(0 != r) ret = r;
// partitions
const char *pnames[] = {
"kernel",
"fs",
};
PARTITION *part = &(header.partition[0]);
for(int n=0; n<2; ++n)
{
r = fwextractpart(fname, f, part++, pnames[n]);
if(0 != r) ret = r;
}
fclose(f);
if(0 == ret) printf("done.\n");
return ret;
}
int partcopy(PARTITION *part, FILE *src, FILE *dst)
{
if(0 != fseek(src, part->offset, SEEK_SET))
return err_fileseek(part->offset);
unsigned char buf[BUFSIZE];
int size = part->size;
while(size > 0)
{
int r = (size > sizeof(buf))? sizeof(buf) : size;
size -= r;
if(fread(buf, 1, r, src) < r)
return err_fileread();
if(fwrite(buf, 1, r, dst) < r)
return err_filewrite();
}
return 0;
}
int partimport(PARTITION *part, const char *fpart, FILE *dst)
{
FILE *f = fopen(fpart, "rb");
if(0 == f) return err_fileopen();
unsigned char buf[BUFSIZE];
int size = 0;
clearerr(f);
while(!feof(f))
{
int r = fread(buf, 1, sizeof(buf), f);
if((r < sizeof(buf)) && ferror(f))
{
fclose(f);
return err_fileread();
}
if(fwrite(buf, 1, r, dst) < r)
{
fclose(f);
return err_filewrite();
}
size += r;
}
fclose(f);
part->size = size;
return 0;
}
int updatesquashfs(FILE *f, int offset)
{
SQUASHFS_SUPERBLOCK buf;
if(0 != fseek(f, offset, SEEK_SET))
return err_fileseek(offset);
if(fread(&buf, 1, sizeof(buf), f) < sizeof(buf))
return err_fileread();
if((SQUASHFS_MAGIC_SWAP == buf.s_magic) &&
(ZLIB_COMPRESSION == buf.compression))
return 0;
if((SQUASHFS_MAGIC_SWAP != buf.s_magic) &&
(SQUASHFS_MAGIC != buf.s_magic))
{
printf("ERROR: bad squashfs magic\n");
return ERR_PARTITION;
}
buf.s_magic = SQUASHFS_MAGIC_SWAP;
if((ZLIB_COMPRESSION != buf.compression) &&
(LZMA_COMPRESSION != buf.compression))
{
printf("ERROR: bad squashfs compression\n");
return ERR_PARTITION;
}
buf.compression = ZLIB_COMPRESSION;
if(0 != fseek(f, offset, SEEK_SET))
return err_fileseek(offset);
if(fwrite(&buf, 1, sizeof(buf), f) < sizeof(buf))
return err_filewrite();
return 0;
}
int fwupdate(
const char *fname,
const char *kernel,
const char *fs,
const char *releasename,
const char *out,
bool keep
)
{
FILE *f = fopen(fname, "rb");
if(0 == f) return err_fileopen();
// load header
HEADER header;
clearerr(f);
if(fread(&header, 1, sizeof(header), f) < sizeof(header))
{
if(ferror(f))
{
fclose(f);
return err_fileread();
}
fclose(f);
return err_header();
}
// verify checksum
unsigned char verify[CHECKSUM_SIZE];
if(
(0 != getchecksum(f, verify, &header)) ||
(0 != memcmp(header.checksum, verify, sizeof(verify)))
)
{
printf("WARNING: checksum mismatch!\n");
if(!FORCE)
{
fclose(f);
printf("use -F to ignore.\n");
return ERR_CHECKSUM;
}
}
// prodcode
if(header.prodcode != PRODCODE)
{
printf("WARNING: prodcode is not 0x%X\n", PRODCODE);
if(!FORCE)
{
fclose(f);
printf("use -F to ignore.\n");
return ERR_CHECKSUM;
}
}
// check partitions
const char *pnames[] = {
"kernel",
"fs",
};
const char *pname;
PARTITION *part = &(header.partition[0]);
for(int n=0; n<2; ++n)
{
pname = pnames[n];
if(0 != strcmp(pname, part->name))
{
printf("WARNING: partition %d name is not %s\n", n, pname);
if(!FORCE)
{
fclose(f);
printf("use -F to ignore.\n");
return ERR_PARTITION;
}
}
++part;
}
if((0 != part->name[0]) || (0 != part->size))
{
printf("ERROR: unknown partition\n");
fclose(f);
return ERR_PARTITION;
}
// out
printf("file: %s\n", out);
// header
memset(header.checksum, 0, sizeof(header.checksum));
if(!keep)
{
header.bootorder = 0;
header.bootstatus = 0xffffffff;
// date
time_t rawtime;
tm *ti;
time(&rawtime);
ti = localtime(&rawtime);
sprintf(
header.date,
"%.4d-%.2d-%.2d-%.2d:%.2d:%.2d",
ti->tm_year+1900,
ti->tm_mon+1,
ti->tm_mday,
ti->tm_hour,
ti->tm_min,
ti->tm_sec
);
// fname
memset(header.filename, 0, sizeof(header.filename));
strncpy(header.filename, basename(out), sizeof(header.filename));
}
header.size = sizeof(HEADER);
// releasename
if((0 != releasename) && (0 != *releasename))
{
memset(header.releasename, 0, sizeof(header.releasename));
strncpy(header.releasename, releasename, sizeof(header.releasename)-1);
if(strlen(header.releasename) < strlen(releasename))
printf("WARNING: releasename truncated: %s\n", header.releasename);
}
if(exists(out))
{
fclose(f);
return err_fileexists();
}
// create dest
FILE *d = fopen(out, "w+b");
if(0 == d)
{
fclose(f);
return err_filecreate();
}
// write header
if(fwrite(&header, 1, sizeof(HEADER), d) < sizeof(HEADER))
{
fclose(f);
fclose(d);
return err_filewrite();
}
int r;
// partitions
const char *pfiles[] = {
kernel,
fs
};
const char *pfile;
part = &(header.partition[0]);
for(int n=0; n<2; ++n, ++part)
{
pname = pnames[n];
pfile = pfiles[n];
if(0 != pfile)
{
printf("importing %s\n", pname);
r = partimport(part, pfile, d);
}
else
{
printf("copying %s\n", pname);
r = partcopy(part, f, d);
}
if(0 != r)
{
fclose(f);
fclose(d);
return r;
}
if(part->size <= 0)
{
printf("ERROR: %s partition empty\n", pname);
fclose(f);
fclose(d);
return ERR_PARTITION;
}
part->offset = header.size;
header.size += part->size;
}
// update squashfs
part = &(header.partition[1]);
r = updatesquashfs(d, part->offset);
if(0 != r)
{
fclose(f);
fclose(d);
return r;
}
// update checksum
r = getchecksum(d, verify, &header);
if(0 != r)
{
fclose(f);
fclose(d);
return r;
}
memcpy(header.checksum, verify, sizeof(verify));
// update header
printf("updating header\n");
if(0 != fseek(d, 0, SEEK_SET))
{
fclose(f);
fclose(d);
return err_fileseek(0);
}
if(fwrite(&header, 1, sizeof(HEADER), d) < sizeof(HEADER))
{
fclose(f);
fclose(d);
return err_filewrite();
}
// close
fclose(f);
fclose(d);
// show checksum
printf(
"checksum: "
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"%.2x%.2x%.2x%.2x"
"\n",
verify[0], verify[1], verify[2], verify[3],
verify[4], verify[5], verify[6], verify[7],
verify[8], verify[9], verify[10], verify[11],
verify[12], verify[13], verify[14], verify[15]
);
printf("done.\n");
return 0;
}
void msleep(int msec)
{
timespec t;
t.tv_sec = 0;
t.tv_nsec = 1000000 * msec;
nanosleep(&t, 0);
}
int recovery(const char *fname)
{
FILE *f = fopen(fname, "rb");
if(0 == f) return err_fileopen();
unsigned char buf[BUFSIZE];
memset(buf, 0, sizeof(buf));
// load header
HEADER *header = (HEADER*)buf;
clearerr(f);
if(fread(header, 1, sizeof(HEADER), f) < sizeof(HEADER))
{
if(ferror(f))
{
fclose(f);
return err_fileread();
}
fclose(f);
return err_header();
}
// verify checksum
unsigned char verify[CHECKSUM_SIZE];
if(
(0 != getchecksum(f, verify, header)) ||
(0 != memcmp(header->checksum, verify, sizeof(verify)))
)
{
printf("ERROR: bad firmware checksum.\n");
fclose(f);
return ERR_CHECKSUM;
}
// file size
if(0 != fseek(f, 0, SEEK_END))
{
fclose(f);
return err_fileseek(0);
}
int fsize = ftell(f);
if(0 != fseek(f, 0, SEEK_SET))
{
fclose(f);
return err_fileseek(0);
}
// address info
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_NUMERICSERV;
addrinfo *addrlist = 0;
if((0 != getaddrinfo(RECOVERY_ADDRESS, RECOVERY_PORT, &hints, &addrlist)) || (0 == addrlist))
{
fclose(f);
return err_socket_address();
}
// create socket
int s = -1;
for(addrinfo *a=addrlist; 0!=a; a=a->ai_next)
{
s = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
if(-1 == s) continue;
if(-1 == connect(s, a->ai_addr, a->ai_addrlen))
{
close(s);
continue;
}
// success
break;
}
freeaddrinfo(addrlist);
if(-1 == s)
{
fclose(f);
return err_socket_connect();
}
// local address
sockaddr_in saddr;
socklen_t saddrlen = sizeof(saddr);
char host[64];
memset(host, 0, sizeof(host));
if(
(0 == getsockname(s, (sockaddr*)&saddr, &saddrlen)) &&
(0 == getnameinfo(
(sockaddr*)&saddr, saddrlen,
host, sizeof(host),
0, 0,
NI_NUMERICHOST | NI_NUMERICSERV))
)
{
printf("from: %s\n", host);
}
// firmware fragment
memset(buf, 0, sizeof(buf));
FRAGMENT *frag = (FRAGMENT*)buf;
unsigned char *data = (buf + sizeof(FRAGMENT));
frag->prodcode = PRODCODE;
frag->address = 0x80800000;
frag->fwsize = fsize;
int q = fsize / 100;
int p = 0;
// read and send
printf("sending to %s:%s...\n", RECOVERY_ADDRESS, RECOVERY_PORT);
frag->seq = 0;
clearerr(f);
for(;;)
{
// read
int r = fread(data, 1, SENDSIZE, f);
if(r <= 0)
{
if(ferror(f))
{
close(s);
fclose(f);
return err_fileread();
}
break;
}
frag->fragsize = r;
// send
if(-1 == sendto(s, buf, sizeof(FRAGMENT)+r, 0, 0, 0))
{
close(s);
fclose(f);
return err_socket_send();
}
frag->seq += 1;
p += r;
if(p >= q)
{
p -= q;
printf(".");
fflush(stdout);
}
msleep(SEND_WAIT_MSEC);
}
printf("\n");
close(s);
fclose(f);
printf("done.\n");
return 0;
}
void info()
{
printf(
"updatefw " SZ_VERSION " - " __DATE__ " " __TIME__ "\n"
"made by Alacn (alacn1@gmail.com)\n"
"\n"
);
}
int usage()
{
printf(
"Tool to update pace v5471 firmware.\n"
"Usage:\n"
" updatefw [-i] fw.bin ...\n"
" Display firmware information.\n"
" -i information mode\n"
"\n"
" updatefw -x fw.bin ...\n"
" Extract firmware to fw.header, fw.kernel, fw.fs.\n"
" -x extract mode\n"
"\n"
" updatefw -u fw.bin [-k kernel] [-s fs] [-m rel] [-n] -o outfw.bin\n"
" Update firmware to a new file.\n"
" WARNING! you can generate bad firmware with valid checksum.\n"
" -u set source firmware\n"
" -k set kernel file\n"
" -s set squashfs file\n"
" -m set release name string\n"
" -n keep current value of fields: filename, date, boot\n"
" -o set output file\n"
"\n"
" updatefw -r fw.bin\n"
" Upload firmware to router in recovery mode.\n"
" -r set firmware to upload\n"
"\n"
" To enter multicast mode (recovery), turn on the router, holding reset or wps button.\n"
" Note that it will boot the uploaded firmware but won't save it.\n"
);
return 0;
}
int err_args()
{
printf("invalid arguments\n");
usage();
return ERR_ARGS;
}
inline int sizeof_checks()
{
if(sizeof(HEADER) != 4096)
{
printf("FATAL: sizeof(HEADER) != 4096\n");
return ERR;
}
if(sizeof(INT32) != 4)
{
printf("FATAL: sizeof(INT32) != 4\n");
return ERR;
}
if(sizeof(INT16) != 2)
{
printf("FATAL: sizeof(INT16) != 2\n");
return ERR;
}
return 0;
}
int main(int argc, char *argv[])
{
// compiler should strip this
if(0 != sizeof_checks()) return ERR;
if(argc < 2)
{
info();
usage();
return 0;
}
int ret, r, n;
const char *fname = 0;
const char *kernel = 0;
const char *fs = 0;
const char *out = 0;
const char *releasename = 0;
const char *s;
bool keep = false;
enum MODE
{
MODE_0,
MODE_INFO,
MODE_UPDATE,
MODE_EXTRACT,
MODE_RECOVERY,
};
MODE mode = MODE_0;
for(n=1; n<argc; ++n)
{
s = argv[n];
if(0 == strcmp("-i", s))
{
if((mode != MODE_0) && (mode != MODE_INFO))
return err_args();
mode = MODE_INFO;
}
else if(0 == strcmp("-x", s))
{
if((mode != MODE_0) && (mode != MODE_EXTRACT))
return err_args();
mode = MODE_EXTRACT;
}
else if(0 == strcmp("-u", s))
{
++n;
if(n >= argc) return err_args();
fname = argv[n];
if((mode != MODE_0) && (mode != MODE_UPDATE))
return err_args();
mode = MODE_UPDATE;
}
else if(0 == strcmp("-k", s))
{
++n;
if(n >= argc) return err_args();
kernel = argv[n];
if((mode != MODE_0) && (mode != MODE_UPDATE))
return err_args();
mode = MODE_UPDATE;
}
else if(0 == strcmp("-s", s))
{
++n;
if(n >= argc) return err_args();
fs = argv[n];
if((mode != MODE_0) && (mode != MODE_UPDATE))
return err_args();
mode = MODE_UPDATE;
}
else if(0 == strcmp("-o", s))
{
++n;
if(n >= argc) return err_args();
out = argv[n];
if((mode != MODE_0) && (mode != MODE_UPDATE))
return err_args();
mode = MODE_UPDATE;
}
else if(0 == strcmp("-m", s))
{
++n;
if(n >= argc) return err_args();
releasename = argv[n];
if((mode != MODE_0) && (mode != MODE_UPDATE))
return err_args();
mode = MODE_UPDATE;
}
else if(0 == strcmp("-r", s))
{
++n;
if(n >= argc) return err_args();
fname = argv[n];
if((mode != MODE_0) && (mode != MODE_RECOVERY))
return err_args();
mode = MODE_RECOVERY;
}
else if(0 == strcmp("-n", s))
{
keep = true;
}
else if(0 == strcmp("-F", s))
{
FORCE = true;
}
else if('-' == *s)
{
printf("invalid parameter: %s\n", s);
usage();
return ERR_ARGS;
}
else
{
// no mode yet? set MODE_INFO
if(mode == MODE_0) mode = MODE_INFO;
// multiple files only in info and extract
if((mode != MODE_INFO) && (mode != MODE_EXTRACT))
return err_args();
}
}
switch(mode)
{
case MODE_INFO:
ret = 0;
for(n=1; n<argc; ++n)
{
s = argv[n];
if('-' == *s) continue;
r = fwinfo(s);
if(0 != r) ret = r;
if(n+1 < argc) printf("\n\n");
}
return ret;
case MODE_EXTRACT:
ret = 0;
for(n=1; n<argc; ++n)
{
s = argv[n];
if('-' == *s) continue;
r = fwextract(s);
if(0 != r) ret = r;
if(n+1 < argc) printf("\n\n");
}
return ret;
case MODE_UPDATE:
if((0 == fname) || (0 == out)) return err_args(); // all required
if((0 == kernel) && (0 == fs) && (0 == releasename)) return err_args(); // at least one
return fwupdate(fname, kernel, fs, releasename, out, keep);
case MODE_RECOVERY:
return recovery(fname);
}
return ERR;
}