/* dlinkflash - Tool flash older d-link routers via emergency web interface
*
* You must have 192.168.0.x/24 (where X=2-254) IP address on the
* interface connected to the d-link and use the emergency flashing interface
*
* Copyright 2014 Daniel Dickinson
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Minor updates by jno@jno.su (for -Wall mode)
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
void build_post_bin(uint8_t **post, size_t *post_len, const uint8_t *newdata, size_t datalen) {
uint8_t *newpost = NULL;
newpost = malloc((*post_len + datalen) * sizeof(uint8_t));
if (*post) {
memcpy(newpost, *post, *post_len);
} else {
*post_len = 0;
}
memcpy(newpost + *post_len, newdata, datalen);
*post_len += datalen;
if (*post)
free(*post);
*post = newpost;
}
void build_post(uint8_t **post, size_t *post_len, const char *newchar, size_t *content_len) {
build_post_bin(post, post_len, (uint8_t *)newchar, strlen(newchar));
if (content_len) {
*content_len += strlen(newchar);
}
}
void usage(char *exename) {
printf("Usage: %s [-d]\n", exename);
printf(" Interface attached to d-link must have IP addres 192.168.0.x/24 where x != 1\n");
exit(1);
}
int open_socket(void) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
unsigned int tcpflush = 1;
unsigned int recvbufsz = 512;
unsigned int smallwindow = 512;
unsigned int mss = 1024;
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbufsz, sizeof(recvbufsz));
setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &recvbufsz, sizeof(recvbufsz));
setsockopt(sock, IPPROTO_IP, TCP_NODELAY, &tcpflush, sizeof(tcpflush));
setsockopt(sock, IPPROTO_IP, TCP_MAXSEG, &mss, sizeof(mss));
setsockopt(sock, IPPROTO_IP, TCP_WINDOW_CLAMP, &smallwindow, sizeof(smallwindow));
struct sockaddr_in ipaddr;
in_addr_t hostip = inet_addr("192.168.0.1");
ipaddr.sin_family = AF_INET;
ipaddr.sin_port = htons(80);
ipaddr.sin_addr.s_addr = hostip;
if (connect(sock, (struct sockaddr *)&ipaddr, sizeof(struct sockaddr)) < 0) {
return -1;
}
return sock;
}
static int send_get(int *sock, uint8_t *get, size_t getlen, int debug) {
size_t socksent = 0;
size_t curpos = 0;
*sock = open_socket();
if (*sock < 0) {
perror("send_get");
return -1;
}
while (curpos < getlen) {
if ((getlen - curpos) >= 512) {
socksent = send(*sock, get + curpos, 512, 0);
if (debug)
fprintf(stderr, "Sent %lu bytes\n", socksent);
if (socksent < 0) {
perror("send_get");
close(*sock);
return -1;
}
} else {
socksent = send(*sock, get + curpos, getlen - curpos, 0);
if (debug)
fprintf(stderr, "Sent %lu bytes\n", socksent);
if (socksent < 0) {
perror("send_get");
close(*sock);
return -1;
}
}
curpos += socksent;
printf("\r%lu/%lu Bytes written: GET %g%% complete ", curpos, getlen, ((float)curpos / (float)getlen) * (float)100);
fflush(stdout);
}
printf("\nFinished sending GET. Waiting for response.\n");
return 0;
}
static int inline read_out_sock(int sock, int debug) {
char buf[1024];
int len;
do {
len = recv(sock, buf, 512, MSG_WAITALL);
if (len < 0)
return len;
if (debug) {
fprintf(stderr, "Got %d bytes data\n", len);
if (len > 0)
fprintf(stderr, "%.*s", len, buf);
}
} while (len > 0); /* len == 0 means normal EOF */
return 0;
}
static int inline append(uint8_t **old, size_t *old_len, const uint8_t *new, size_t new_len) {
assert(old && old_len && new);
uint8_t *temp = malloc((*old_len + new_len) * sizeof(uint8_t));
if (!temp) return -1;
if (*old)
memcpy(temp, *old, *old_len);
memcpy(temp + *old_len, new, new_len);
*old_len += new_len;
if (*old)
free(*old);
*old = temp;
return 0;
}
static void *load_file(const char *fname, size_t *fwlen) {
uint8_t *firmware = NULL;
size_t firmwarelen = 0;
size_t len = 0;
uint8_t buf[1024];
printf("Load firmware file %s\n", fname);
int firmwarefd = open(fname, 0);
if (firmwarefd < 0) {
perror(fname);
exit(1);
}
do {
len = read(firmwarefd, buf, sizeof(buf));
if (len < 0) {
perror(fname);
close(firmwarefd);
if (firmware)
free(firmware);
exit(2);
}
if (len == 0) {
fprintf(stderr, "Oops: no bytes read from %s at %lu\n", fname, firmwarelen);
continue;
}
assert(len > 0);
if (append(&firmware, &firmwarelen, buf, len) < 0) {
perror(fname);
close(firmwarefd);
if (firmware)
free(firmware);
exit(2);
}
} while (len > 0);
close(firmwarefd);
printf("Firmware %lu bytes long\n", firmwarelen);
if (fwlen) *fwlen = firmwarelen;
return firmware;
}
static size_t inline make_file_content(uint8_t **content, size_t *contentlen, const uint8_t *firmware, size_t firmwarelen) {
size_t nonnllen = 0;
build_post(content, contentlen, "---------------------------7de1fe13304\r\n", NULL);
nonnllen += 2;
build_post(content, contentlen, "Content-Disposition: form-data; name=\"files\"; filename=\"C:\\My Documents\\firmware.bin\"\r\n", &nonnllen);
build_post(content, contentlen, "Content-Type: application/octet-stream\r\n", &nonnllen);
build_post(content, contentlen, "\r\n", &nonnllen);
build_post_bin(content, contentlen, firmware, firmwarelen);
build_post(content, contentlen, "\r\n---------------------------7de1fe13304--\r\n", NULL);
return nonnllen + 4;
}
static void inline make_post_request(uint8_t **post, size_t *postlen, const uint8_t *firmware, size_t firmwarelen) {
char contentlenstr[1024] = "";
uint8_t *content = NULL;
size_t contentlen = 0;
size_t nonnllen = make_file_content(&content, &contentlen, firmware, firmwarelen);
/* IE6 off-by-one content-length error? */
sprintf(contentlenstr, "%lu\r\n", nonnllen + firmwarelen + 1);
build_post(post, postlen, "POST /cgi/index HTTP/1.1\r\n", NULL);
build_post(post, postlen, "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n", NULL);
build_post(post, postlen, "Referer: http://192.168.0.1\r\n", NULL);
build_post(post, postlen, "Accept-Language: en-ca\r\n", NULL);
build_post(post, postlen, "Content-Type: multipart/form-data; boundary=---------------------------7de1fe13304\r\n", NULL);
build_post(post, postlen, "Accept-Encoding: gzip, deflate\r\n", NULL);
build_post(post, postlen, "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; .NET CLR 1.1.4322)\r\n", NULL);
build_post(post, postlen, "Host: 192.168.0.1\r\n", NULL);
build_post(post, postlen, "Content-Length: ", NULL);
build_post(post, postlen, contentlenstr, NULL);
build_post(post, postlen, "Connection: Keep-Alive\r\n", NULL);
build_post(post, postlen, "Cache-Control: no-cache\r\n", NULL);
build_post(post, postlen, "\r\n", NULL);
build_post_bin(post, postlen, content, contentlen);
free(content);
}
static void inline make_get_request(uint8_t **get, size_t *len) {
build_post(get, len, "GET / HTTP/1.1\r\n", NULL);
build_post(get, len, "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n", NULL);
build_post(get, len, "Accept-Language: en-ca\r\n", NULL);
build_post(get, len, "Accept-Encoding: gzip, deflate\r\n", NULL);
build_post(get, len, "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; .NET CLR 1.1.4322)\r\n", NULL);
build_post(get, len, "Host: 192.168.0.1\r\n", NULL);
build_post(get, len, "Connection: Keep-Alive\r\n", NULL);
build_post(get, len, "\r\n", NULL);
}
static int inline ping_router(int debug) {
uint8_t *get = NULL;
size_t getlen = 0;
int sock;
printf("Initiating GET so that router is in state to receive POST....");
fflush(stdout);
make_get_request(&get, &getlen);
if (send_get(&sock, get, getlen, debug) < 0) {
free(get);
exit(7);
}
free(get);
if (read_out_sock(sock, debug) < 0) {
perror("recv-in-get");
shutdown(sock, SHUT_RDWR);
close(sock);
exit(7);
}
printf("Got page from which upload is done.\n");
shutdown(sock, SHUT_RDWR);
close(sock);
return 0;
}
static void wait_for_reply(int sock, int debug) {
regex_t pattern;
if (regcomp(&pattern, "count_down", REG_NOSUB)) {
fprintf(stderr, "Error compiling expression to detect success or failure\n");
close(sock);
exit(7);
}
char recvbuf[1024] = "";
int recvlen = 0;
int ok = 0;
do {
recvlen = recv(sock, recvbuf, 512, MSG_WAITALL);
if (recvlen < 0) {
perror("recv-on-post");
break;
}
if (recvlen == 0) {
if (debug)
fprintf(stderr, "Socket reported EOF\n");
break;
}
assert(recvlen > 0);
if (debug) {
fprintf(stderr, "Got %d data\n", recvlen);
if (recvlen > 0)
fprintf(stderr, "%.*s", recvlen, recvbuf);
}
if (!regexec(&pattern, recvbuf, 0, NULL, 0)) {
ok = 1;
printf("Firmware successfully sent. Please wait for device to reboot.\n");
sleep(2);
break;
}
} while (recvlen > 0);
regfree(&pattern);
shutdown(sock, SHUT_RDWR);
close(sock);
if (!ok) {
fprintf(stderr, "No indication of successful upload!\n");
exit(6);
}
}
static void upload(const char *fname, int debug) {
uint8_t *firmware = NULL; size_t firmwarelen = 0;
uint8_t *post = NULL; size_t postlen = 0;
firmware = load_file(fname, &firmwarelen);
make_post_request(&post, &postlen, firmware, firmwarelen);
free(firmware);
printf("Initiating transfer....");
fflush(stdout);
int sock = open_socket();
if (sock < 0) {
perror(fname);
free(post);
exit(9);
}
size_t socksent = 0;
size_t curpos = 0;
while (curpos < postlen) {
if ((postlen - curpos) >= 512) {
socksent = send(sock, post + curpos, 512, 0);
if (socksent < 0)
goto bailout;
if (debug)
fprintf(stderr, "Sent %lu bytes\n", socksent);
} else {
socksent = send(sock, post + curpos, postlen - curpos, 0);
if (socksent < 0)
goto bailout;
if (debug)
fprintf(stderr, "Sent %lu bytes\n", socksent);
}
curpos += socksent;
printf("\r%lu/%lu Bytes written: Upload %g%% complete ",
curpos, postlen, ((float)curpos * 100.0) / (float)postlen);
fflush(stdout);
}
printf("\nFinished sending post. Waiting for response.\n");
free(post);
wait_for_reply(sock, debug);
return;
bailout:
perror(fname);
close(sock);
free(post);
exit(5);
}
int main(int argc, char *argv[]) {
int debug = 0;
if (argc < 2 || argc > 3) {
usage(argv[0]);
}
if (argc == 3) {
if (!strncmp(argv[2], "-d", 2)) {
debug = 1;
} else {
usage(argv[0]);
}
}
if (ping_router(debug) < 0) {
perror(argv[1]);
exit(8);
}
upload(argv[1], debug);
return 0;
}