1 /**
2 * nmrpflash - Netgear Unbrick Utility
3 * Copyright (C) 2016 Joseph Lehner <joseph.c.lehner@gmail.com>
4 *
5 * nmrpflash is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * nmrpflash is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with nmrpflash. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include <signal.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <stdio.h>
26 #include <time.h>
27 #include "nmrpd.h"
28
29 #define NMRP_HDR_LEN 6
30 #define NMRP_OPT_HDR_LEN 4
31 #define NMRP_MIN_PKT_LEN (sizeof(struct eth_hdr) + NMRP_HDR_LEN)
32
33 #define ETH_P_NMRP 0x0912
34
35 #ifndef PACKED
36 #define PACKED __attribute__((__packed__))
37 #endif
38
39 #ifdef NMRPFLASH_WINDOWS
40 #define setenv(name, value, overwrite) SetEnvironmentVariable(name, value)
41 #endif
42
43 enum nmrp_code {
44 NMRP_C_NONE = 0,
45 NMRP_C_ADVERTISE = 1,
46 NMRP_C_CONF_REQ = 2,
47 NMRP_C_CONF_ACK = 3,
48 NMRP_C_CLOSE_REQ = 4,
49 NMRP_C_CLOSE_ACK = 5,
50 NMRP_C_KEEP_ALIVE_REQ = 6,
51 NMRP_C_KEEP_ALIVE_ACK = 7,
52 NMRP_C_TFTP_UL_REQ = 16
53 };
54
55 enum nmrp_opt_type {
56 NMRP_O_MAGIC_NO = 0x0001,
57 NMRP_O_DEV_IP = 0x0002,
58 NMRP_O_DEV_REGION = 0x0004,
59 NMRP_O_FW_UP = 0x0101,
60 NMRP_O_ST_UP = 0x0102,
61 NMRP_O_FILE_NAME = 0x0181
62 };
63
64 struct nmrp_opt {
65 uint16_t type;
66 uint16_t len;
67 char val[1];
68 } PACKED;
69
70 struct nmrp_msg {
71 uint16_t reserved;
72 uint8_t code;
73 uint8_t id;
74 uint16_t len;
75 char opts[44];
76 } PACKED;
77
78 struct nmrp_pkt {
79 struct eth_hdr eh;
80 struct nmrp_msg msg;
81 } PACKED;
82
msg_code_str(uint16_t code)83 static const char *msg_code_str(uint16_t code)
84 {
85 #define MSG_CODE(x) case NMRP_C_ ## x: return #x
86 static char buf[16];
87
88 switch (code) {
89 MSG_CODE(ADVERTISE);
90 MSG_CODE(CONF_REQ);
91 MSG_CODE(CONF_ACK);
92 MSG_CODE(CLOSE_REQ);
93 MSG_CODE(CLOSE_ACK);
94 MSG_CODE(KEEP_ALIVE_REQ);
95 MSG_CODE(KEEP_ALIVE_ACK);
96 MSG_CODE(TFTP_UL_REQ);
97 default:
98 snprintf(buf, sizeof(buf), "%04x", ntohs(code));
99 return buf;
100 }
101 #undef MSG_CODE
102 }
103
to_region_code(const char * region)104 static uint16_t to_region_code(const char *region)
105 {
106 #define REGION_CODE(r, c) if (!strcasecmp(region, r)) return htons(c)
107 REGION_CODE("NA", 0x0001);
108 REGION_CODE("WW", 0x0002);
109 REGION_CODE("GR", 0x0003);
110 REGION_CODE("PR", 0x0004);
111 REGION_CODE("RU", 0x0005);
112 REGION_CODE("BZ", 0x0006);
113 REGION_CODE("IN", 0x0007);
114 REGION_CODE("KO", 0x0008);
115 REGION_CODE("JP", 0x0009);
116 #undef REGION_CODE
117 return 0;
118 }
119
msg_dump(struct nmrp_msg * msg)120 static void msg_dump(struct nmrp_msg *msg)
121 {
122 int rem;
123
124 fprintf(stderr, "res=0x%04x, code=0x%02x, id=0x%02x, len=%u",
125 ntohs(msg->reserved), msg->code, msg->id, ntohs(msg->len));
126
127 rem = ntohs(msg->len) - NMRP_HDR_LEN;
128 fprintf(stderr, "%s\n", rem ? "" : " (no opts)");
129 }
130
msg_opt(struct nmrp_msg * msg,uint16_t type,uint16_t * len)131 static void *msg_opt(struct nmrp_msg *msg, uint16_t type, uint16_t* len)
132 {
133 struct nmrp_opt* opt = (struct nmrp_opt*)msg->opts;
134 size_t rem = ntohs(msg->len) - NMRP_HDR_LEN;
135 uint16_t olen;
136
137 do {
138 olen = ntohs(opt->len);
139 if (olen < NMRP_OPT_HDR_LEN || olen > rem) {
140 break;
141 }
142
143 if (ntohs(opt->type) == type) {
144 if (len) {
145 *len = olen;
146 }
147
148 return opt->val;
149 }
150
151 opt = (struct nmrp_opt*)(((char *)opt) + olen);
152 rem -= olen;
153 } while (rem);
154
155 return NULL;
156 }
157
msg_filename(struct nmrp_msg * msg)158 static char *msg_filename(struct nmrp_msg *msg)
159 {
160 static char buf[256];
161 uint16_t len;
162 char *p = msg_opt(msg, NMRP_O_FILE_NAME, &len);
163 if (p) {
164 len = MIN(sizeof(buf) - 1, len);
165 memcpy(buf, p, len);
166 buf[len] = '\0';
167 return buf;
168 }
169
170 return NULL;
171 }
172
msg_init(struct nmrp_msg * msg,uint16_t code)173 static inline void msg_init(struct nmrp_msg *msg, uint16_t code)
174 {
175 memset(msg, 0, sizeof(*msg));
176 msg->len = htons(NMRP_HDR_LEN);
177 msg->code = code;
178 }
179
msg_mkopt(struct nmrp_msg * msg,char * p,uint16_t type,const void * val,size_t len)180 static char *msg_mkopt(struct nmrp_msg *msg, char *p, uint16_t type, const void *val, size_t len)
181 {
182 struct nmrp_opt* opt = (struct nmrp_opt*)p;
183
184 len &= 0xffff;
185
186 msg->len = ntohs(msg->len);
187
188 if ((msg->len + len > sizeof(*msg))) {
189 fprintf(stderr, "Error: invalid option - this is a bug\n");
190 exit(1);
191 }
192
193 opt->type = htons(type);
194 opt->len = NMRP_OPT_HDR_LEN + len;
195
196 if (val) {
197 memcpy(opt->val, val, len);
198 }
199
200 msg->len += opt->len;
201 p += opt->len;
202
203 msg->len = htons(msg->len);
204 opt->len = htons(opt->len);
205
206 return p;
207 }
208
msg_mkadvertise(struct nmrp_msg * msg,const char * magic)209 static void msg_mkadvertise(struct nmrp_msg *msg, const char *magic)
210 {
211 msg_init(msg, NMRP_C_ADVERTISE);
212 msg_mkopt(msg, msg->opts, NMRP_O_MAGIC_NO, magic, strlen(magic));
213 }
214
msg_mkconfack(struct nmrp_msg * msg,uint32_t ipaddr,uint32_t ipmask,uint16_t region)215 static void msg_mkconfack(struct nmrp_msg *msg, uint32_t ipaddr, uint32_t ipmask, uint16_t region)
216 {
217 char *p;
218 uint32_t ip[2] = { ipaddr, ipmask };
219
220 msg_init(msg, NMRP_C_CONF_ACK);
221 p = msg_mkopt(msg, msg->opts, NMRP_O_DEV_IP, &ip, 8);
222 p = msg_mkopt(msg, p, NMRP_O_FW_UP, NULL, 0);
223
224 #ifdef NMRPFLASH_SET_REGION
225 if (region) {
226 p = msg_mkopt(msg, p, NMRP_O_DEV_REGION, ®ion, 2);
227 }
228 #endif
229 }
230
231 #ifdef NMRPFLASH_FUZZ
232 #define NMRP_ADVERTISE_TIMEOUT 0
233 #define ethsock_create(a, b) ((struct ethsock*)1)
234 #define ethsock_get_hwaddr(a) ethsock_get_hwaddr_fake(a)
235 #define ethsock_recv(sock, buf, len) read(STDIN_FILENO, buf, len)
236 #define ethsock_send(a, b, c) (0)
237 #define ethsock_set_timeout(a, b) (0)
238 #define ethsock_arp_add(a, b, c, d) (0)
239 #define ethsock_arp_del(a, b) (0)
240 #define ethsock_ip_add(a, b, c, d) (0)
241 #define ethsock_ip_del(a, b) (0)
242 #define ethsock_close(a) (0)
243 #define ethsock_for_each_ip(a, b, c) (1)
244 #define tftp_put(a) (0)
245
ethsock_get_hwaddr_fake(struct ethsock * sock)246 static uint8_t *ethsock_get_hwaddr_fake(struct ethsock* sock)
247 {
248 static uint8_t hwaddr[6] = { 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa };
249 return hwaddr;
250 }
251 #else
252 #define NMRP_ADVERTISE_TIMEOUT 60
253 #endif
254
pkt_send(struct ethsock * sock,struct nmrp_pkt * pkt)255 static int pkt_send(struct ethsock *sock, struct nmrp_pkt *pkt)
256 {
257 return ethsock_send(sock, pkt, sizeof(*pkt));
258 }
259
pkt_recv(struct ethsock * sock,struct nmrp_pkt * pkt)260 static int pkt_recv(struct ethsock *sock, struct nmrp_pkt *pkt)
261 {
262 ssize_t bytes, mlen;
263
264 memset(pkt, 0, sizeof(*pkt));
265 bytes = ethsock_recv(sock, pkt, sizeof(*pkt));
266 if (bytes < 0) {
267 return 1;
268 } else if (!bytes) {
269 return 2;
270 }
271
272 mlen = ntohs(pkt->msg.len);
273
274 if (bytes < (mlen + sizeof(pkt->eh))
275 || bytes < NMRP_MIN_PKT_LEN
276 || mlen < NMRP_HDR_LEN) {
277 fprintf(stderr, "Short packet (%d raw, %d message)\n",
278 (int)bytes, (int)mlen);
279 return 1;
280 } else if (mlen > sizeof(pkt->msg)) {
281 printf("Truncating %d byte message.\n", (int)mlen);
282 pkt->msg.len = htons(sizeof(pkt->msg));
283 }
284
285 return 0;
286 }
287
mac_parse(const char * str,uint8_t * hwaddr)288 static int mac_parse(const char *str, uint8_t *hwaddr)
289 {
290 int i;
291 unsigned data[6];
292
293 sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x%n",
294 data, data + 1, data + 2, data + 3, data + 4, data + 5, &i);
295
296 if (i == strlen(str)) {
297 for (i = 0; i != 6; ++i) {
298 if (data[i] > 255) {
299 break;
300 }
301
302 hwaddr[i] = data[i] & 0xff;
303 }
304
305 if (i == 6) {
306 return 1;
307 }
308 }
309 return 0;
310 }
311
312 struct is_valid_ip_arg
313 {
314 struct in_addr *ipaddr;
315 struct in_addr *ipmask;
316 int result;
317 };
318
is_valid_ip_cb(struct ethsock_ip_callback_args * args)319 static int is_valid_ip_cb(struct ethsock_ip_callback_args *args)
320 {
321 #define SUBNET(x) ((x)->ipaddr->s_addr & (x)->ipmask->s_addr)
322 struct is_valid_ip_arg *arg = args->arg;
323 if (SUBNET(args) == SUBNET(arg)) {
324 arg->result = args->ipaddr->s_addr != arg->ipaddr->s_addr;
325 return 0;
326 }
327
328 return 1;
329 #undef SUBNET
330 }
331
is_valid_ip(struct ethsock * sock,struct in_addr * ipaddr,struct in_addr * ipmask)332 static int is_valid_ip(struct ethsock *sock, struct in_addr *ipaddr,
333 struct in_addr *ipmask)
334 {
335 int status;
336 struct is_valid_ip_arg arg = {
337 .ipaddr = ipaddr,
338 .ipmask = ipmask,
339 .result = 0
340 };
341
342 status = ethsock_for_each_ip(sock, is_valid_ip_cb, &arg);
343 return status < 0 ? status : arg.result;
344 }
345
sigh(int sig)346 static void sigh(int sig)
347 {
348 g_interrupted = 1;
349 }
350
351 static const char *spinner = "\\|/-";
352
nmrp_do(struct nmrpd_args * args)353 int nmrp_do(struct nmrpd_args *args)
354 {
355 struct nmrp_pkt tx, rx;
356 uint8_t *src, dest[6];
357 uint16_t region;
358 char *filename;
359 time_t beg;
360 int i, timeout, status, ulreqs, expect, upload_ok, autoip, ka_reqs, fake;
361 struct ethsock *sock;
362 struct ethsock_ip_undo *ip_undo = NULL;
363 struct ethsock_arp_undo *arp_undo = NULL;
364 uint32_t intf_addr = 0;
365 void (*sigh_orig)(int);
366 struct in_addr ipaddr;
367 struct in_addr ipmask;
368
369 if (args->op != NMRP_UPLOAD_FW) {
370 fprintf(stderr, "Operation not implemented.\n");
371 return 1;
372 }
373
374 if (!mac_parse(args->mac, dest)) {
375 fprintf(stderr, "Invalid MAC address '%s'.\n", args->mac);
376 return 1;
377 }
378
379 ipmask.s_addr = inet_addr(args->ipmask);
380 if (ipmask.s_addr == INADDR_NONE
381 || netmask(bitcount(ipmask.s_addr)) != ipmask.s_addr) {
382 fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask);
383 return 1;
384 }
385
386 if (!args->ipaddr) {
387 autoip = true;
388 /* A random IP address. The MAC of the first device that was
389 * used to test this utility starts with a4:2b:8c, so we use
390 * 164 (0xa4) and 183 (0x2b + 0x8c).
391 *
392 * These addresses should not cause collisions on most networks,
393 * and if they do, the user is probably "poweruser" enough to
394 * be able to use the -a and -A options.
395 */
396 args->ipaddr = "10.164.183.252";
397
398 if (!args->ipaddr_intf) {
399 args->ipaddr_intf = "10.164.183.253";
400 }
401 } else if (args->ipaddr_intf) {
402 autoip = true;
403 } else {
404 autoip = false;
405 }
406
407 if ((ipaddr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) {
408 fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr);
409 return 1;
410 }
411
412 if (args->ipaddr_intf && (intf_addr = inet_addr(args->ipaddr_intf)) == INADDR_NONE) {
413 fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr_intf);
414 return 1;
415 }
416
417 if (args->file_local && strcmp(args->file_local, "-") && access(args->file_local, R_OK) == -1) {
418 fprintf(stderr, "Error accessing file '%s'.\n", args->file_local);
419 return 1;
420 }
421
422 if (args->file_remote) {
423 if (!tftp_is_valid_filename(args->file_remote)) {
424 fprintf(stderr, "Invalid remote filename '%s'.\n",
425 args->file_remote);
426 return 1;
427 }
428 }
429
430 if (args->region) {
431 region = to_region_code(args->region);
432 if (!region) {
433 fprintf(stderr, "Invalid region code '%s'.\n", args->region);
434 return 1;
435 }
436 } else {
437 region = 0;
438 }
439
440 status = 1;
441
442 sock = ethsock_create(args->intf, ETH_P_NMRP);
443 if (!sock) {
444 return 1;
445 }
446
447 sigh_orig = signal(SIGINT, sigh);
448
449 if (ethsock_is_unplugged(sock)) {
450 printf("Waiting for physical connection.\n");
451
452 bool unplugged = true;
453 time_t beg = time_monotonic();
454
455 while (!g_interrupted && (time_monotonic() - beg) < 20) {
456 if (!ethsock_is_unplugged(sock)) {
457 unplugged = false;
458 break;
459 }
460 }
461
462 if (unplugged) {
463 if (!g_interrupted) {
464 fprintf(stderr, "Error: Ethernet cable is unplugged.\n");
465 }
466 goto out;
467 }
468 }
469
470 if (!autoip) {
471 status = is_valid_ip(sock, &ipaddr, &ipmask);
472 if (status <= 0) {
473 if (!status) {
474 fprintf(stderr, "Address %s/%s cannot be used on interface %s.\n",
475 args->ipaddr, args->ipmask, args->intf);
476 }
477 goto out;
478 }
479 } else {
480 if (verbosity) {
481 printf("Adding %s to interface %s.\n", args->ipaddr_intf, args->intf);
482 }
483
484 if (ethsock_ip_add(sock, intf_addr, ipmask.s_addr, &ip_undo) != 0) {
485 goto out;
486 }
487 }
488
489 if (ethsock_set_timeout(sock, 200)) {
490 goto out;
491 }
492
493 src = ethsock_get_hwaddr(sock);
494 if (!src) {
495 goto out;
496 }
497
498 memcpy(tx.eh.ether_shost, src, 6);
499 memcpy(tx.eh.ether_dhost, dest, 6);
500 tx.eh.ether_type = htons(ETH_P_NMRP);
501
502 msg_mkadvertise(&tx.msg, "NTGR");
503
504 i = 0;
505 upload_ok = 0;
506 fake = 0;
507 timeout = args->blind ? 10 : NMRP_ADVERTISE_TIMEOUT;
508 beg = time_monotonic();
509
510 while (!g_interrupted) {
511 printf("\rAdvertising NMRP server on %s ... %c",
512 args->intf, spinner[i]);
513 fflush(stdout);
514 i = (i + 1) & 3;
515
516 if (pkt_send(sock, &tx) < 0) {
517 goto out;
518 }
519
520 status = pkt_recv(sock, &rx);
521 if (status == 0) {
522 if (memcmp(rx.eh.ether_dhost, src, 6) == 0) {
523 break;
524 } else if (verbosity) {
525 printf("\nIgnoring bogus response: %s -> %s.\n",
526 mac_to_str(rx.eh.ether_shost),
527 mac_to_str(rx.eh.ether_dhost));
528 }
529 } else if (status == 1) {
530 goto out;
531 } else {
532 /* because we don't want nmrpflash's exit status to be zero */
533 status = 1;
534 if ((time_monotonic() - beg) >= timeout) {
535 printf("\nNo response after %d seconds. ", timeout);
536 if (!args->blind) {
537 printf("Bailing out.\n");
538 goto out;
539 } else {
540 // we're blind, so fake a response from the MAC specified by -m
541 memcpy(rx.eh.ether_shost, dest, 6);
542 msg_init(&rx.msg, NMRP_C_CONF_REQ);
543 printf("Continuing blindly.");
544 fake = 1;
545 break;
546 }
547 }
548 }
549 }
550
551 printf("\n");
552
553 memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, 6);
554
555 if (ethsock_arp_add(sock, rx.eh.ether_shost, ipaddr.s_addr, &arp_undo) != 0) {
556 goto out;
557 }
558
559 if (ethsock_set_timeout(sock, args->rx_timeout)) {
560 goto out;
561 }
562
563 expect = NMRP_C_CONF_REQ;
564 ulreqs = 0;
565 ka_reqs = 0;
566
567 while (!g_interrupted) {
568 if (expect != NMRP_C_NONE && rx.msg.code != expect) {
569 fprintf(stderr, "Received %s while waiting for %s!\n",
570 msg_code_str(rx.msg.code), msg_code_str(expect));
571 }
572
573 msg_init(&tx.msg, NMRP_C_NONE);
574
575 status = 1;
576
577 switch (rx.msg.code) {
578 case NMRP_C_ADVERTISE:
579 printf("Received NMRP advertisement from %s.\n",
580 mac_to_str(rx.eh.ether_shost));
581 status = 1;
582 goto out;
583 case NMRP_C_CONF_REQ:
584 msg_mkconfack(&tx.msg, ipaddr.s_addr, ipmask.s_addr, region);
585 expect = NMRP_C_TFTP_UL_REQ;
586
587 if (!fake) {
588 printf("Received configuration request from %s.\n",
589 mac_to_str(rx.eh.ether_shost));
590 }
591
592 printf("Sending configuration: %s/%d.\n",
593 args->ipaddr, bitcount(ipmask.s_addr));
594
595 break;
596 case NMRP_C_TFTP_UL_REQ:
597 if (!upload_ok) {
598 if (++ulreqs > 5) {
599 printf("Bailing out after %d upload requests.\n",
600 ulreqs);
601 tx.msg.code = NMRP_C_CLOSE_REQ;
602 break;
603 }
604 } else {
605 if (verbosity) {
606 printf("Ignoring extra upload request.\n");
607 }
608 ethsock_set_timeout(sock, args->ul_timeout);
609 tx.msg.code = NMRP_C_KEEP_ALIVE_REQ;
610 break;
611 }
612
613 filename = msg_filename(&rx.msg);
614 if (filename) {
615 if (!args->file_remote) {
616 args->file_remote = filename;
617 }
618 printf("Received upload request: filename '%s'.\n", filename);
619 } else if (!args->file_remote) {
620 args->file_remote = leafname(args->file_local);
621 if (!fake) {
622 printf("Received upload request without filename.\n");
623 }
624 }
625
626 status = 0;
627
628 if (args->tftpcmd) {
629 printf("Executing '%s' ... \n", args->tftpcmd);
630 setenv("IP", inet_ntoa(ipaddr), 1);
631 setenv("PORT", lltostr(args->port, 10), 1);
632 setenv("MAC", mac_to_str(rx.eh.ether_shost), 1);
633 setenv("NETMASK", inet_ntoa(ipmask), 1);
634 //setenv("FILENAME", args->file_remote ? args->file_remote : "", 1);
635 status = system(args->tftpcmd);
636 }
637
638 if (!status && args->file_local) {
639 if (!autoip) {
640 status = is_valid_ip(sock, &ipaddr, &ipmask);
641 if (status < 0) {
642 goto out;
643 } else if (!status) {
644 printf("IP address of %s has changed. Please assign a "
645 "static ip to the interface.\n", args->intf);
646 tx.msg.code = NMRP_C_CLOSE_REQ;
647 break;
648 }
649 }
650
651 if (verbosity) {
652 printf("Using remote filename '%s'.\n",
653 args->file_remote);
654 }
655
656 if (!strcmp(args->file_local, "-")) {
657 printf("Uploading from stdin ... ");
658 } else {
659 printf("Uploading %s ... ", leafname(args->file_local));
660 }
661 fflush(stdout);
662 if (!(status = tftp_put(args))) {
663 printf("OK\n");
664 }
665
666 }
667
668 if (!status) {
669 if (args->blind) {
670 goto out;
671 }
672
673 printf("Waiting for remote to respond.\n");
674 upload_ok = 1;
675 ethsock_set_timeout(sock, args->ul_timeout);
676 tx.msg.code = NMRP_C_KEEP_ALIVE_REQ;
677 expect = NMRP_C_NONE;
678 } else if (status == -2) {
679 expect = NMRP_C_TFTP_UL_REQ;
680 } else {
681 goto out;
682 }
683
684 break;
685 case NMRP_C_KEEP_ALIVE_REQ:
686 tx.msg.code = NMRP_C_KEEP_ALIVE_ACK;
687 ethsock_set_timeout(sock, args->ul_timeout);
688 printf("\rReceived keep-alive request (%d). ", ++ka_reqs);
689 break;
690 case NMRP_C_CLOSE_REQ:
691 tx.msg.code = NMRP_C_CLOSE_ACK;
692 break;
693 case NMRP_C_CLOSE_ACK:
694 status = 0;
695 goto out;
696 default:
697 fprintf(stderr, "Unknown message code 0x%02x!\n",
698 rx.msg.code);
699 msg_dump(&rx.msg);
700 }
701
702 if (tx.msg.code != NMRP_C_NONE) {
703 if (pkt_send(sock, &tx) != 0 || tx.msg.code == NMRP_C_CLOSE_REQ) {
704 goto out;
705 }
706 }
707
708 if (rx.msg.code == NMRP_C_CLOSE_REQ) {
709 if (ka_reqs) {
710 printf("\n");
711 }
712
713 printf("Remote finished. Closing connection.\n");
714 break;
715 }
716
717 status = pkt_recv(sock, &rx);
718 if (status) {
719 if (status == 2) {
720 if (!args->blind) {
721 fprintf(stderr, "Timeout while waiting for %s.\n",
722 msg_code_str(expect));
723 goto out;
724 }
725
726 // fake response
727 msg_init(&rx.msg, expect);
728 memcpy(rx.eh.ether_shost, tx.eh.ether_dhost, 6);
729 fake = 1;
730 } else {
731 goto out;
732 }
733 } else {
734 fake = 0;
735 }
736
737 ethsock_set_timeout(sock, args->rx_timeout);
738
739 }
740
741 if (!g_interrupted) {
742 status = 0;
743 if (ulreqs) {
744 printf("Reboot your device now.\n");
745 } else {
746 printf("No upload request received.\n");
747 }
748 }
749
750 out:
751 signal(SIGINT, sigh_orig);
752 ethsock_arp_del(sock, &arp_undo);
753 ethsock_ip_del(sock, &ip_undo);
754 ethsock_close(sock);
755 return status;
756 }
757