X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=nmrp.c;h=8cf82d6ed560b9da1812ce21b5d941fd6eecf30c;hb=11371109c8f808d8a7401d236789265426b4486d;hp=61b592db5f6d743a56a4a68201df9c9c5eea9575;hpb=6e5156e1375495cb05d23995be8605d88abed1f5;p=oweals%2Fnmrpflash.git diff --git a/nmrp.c b/nmrp.c index 61b592d..8cf82d6 100644 --- a/nmrp.c +++ b/nmrp.c @@ -1,10 +1,23 @@ -#define _BSD_SOURCE -#include -#include -#include -#include -#include -#include +/** + * nmrpflash - Netgear Unbrick Utility + * Copyright (C) 2016 Joseph Lehner + * + * nmrpflash 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. + * + * nmrpflash 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 nmrpflash. If not, see . + * + */ + +#include #include #include #include @@ -14,14 +27,30 @@ #include "nmrpd.h" #define NMRP_HDR_LEN 6 -#define NMRP_OPT_LEN 4 -#define NMRP_MIN_PKT_LEN (sizeof(struct ether_header) + NMRP_HDR_LEN) +#define NMRP_OPT_HDR_LEN 4 +#define NMRP_MIN_PKT_LEN (sizeof(struct eth_hdr) + NMRP_HDR_LEN) + +#define NMRP_MAX_OPT_SIZE 12 +#define NMRP_MAX_OPT_NUM 3 + +#define NMRP_OPT_NEXT(x) ((struct nmrp_opt*)(((char*)x) + x->len)) #define ETH_P_NMRP 0x0912 #define IP_LEN 4 -#define PACKED __attribute__((__packed__)) #define MAX_LOOP_RECV 1024 +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef PACKED +#define PACKED __attribute__((__packed__)) +#endif + +#ifdef NMRPFLASH_WINDOWS +#define setenv(name, value, overwrite) SetEnvironmentVariable(name, value) +#endif + enum nmrp_code { NMRP_C_NONE = 0, NMRP_C_ADVERTISE = 1, @@ -48,9 +77,10 @@ struct nmrp_opt { uint16_t len; union { uint8_t magic[4]; + uint16_t region; struct { - uint8_t addr[IP_LEN]; - uint8_t mask[IP_LEN]; + uint8_t addr[4]; + uint8_t mask[4]; } ip; } val; } PACKED; @@ -60,65 +90,79 @@ struct nmrp_msg { uint8_t code; uint8_t id; uint16_t len; - struct nmrp_opt opts[2]; + /* only opts[0] is valid! think of this as a char* */ + struct nmrp_opt opts[NMRP_MAX_OPT_NUM]; + uint8_t padding[8]; + /* this is NOT part of the transmitted packet */ uint32_t num_opts; } PACKED; struct nmrp_pkt { - struct ether_header eh; + struct eth_hdr eh; struct nmrp_msg msg; } PACKED; -static void msg_update_len(struct nmrp_msg *msg) +static const char *msg_code_str(uint16_t code) { - uint32_t i = 0; - msg->len = NMRP_HDR_LEN; - for (; i != msg->num_opts; ++i) { - msg->len += msg->opts[i].len; +#define CASE_CODE(x) case NMRP_C_ ## x: return #x + static char buf[16]; + + switch (code) { + CASE_CODE(ADVERTISE); + CASE_CODE(CONF_REQ); + CASE_CODE(CONF_ACK); + CASE_CODE(CLOSE_REQ); + CASE_CODE(CLOSE_ACK); + CASE_CODE(KEEP_ALIVE_REQ); + CASE_CODE(KEEP_ALIVE_ACK); + CASE_CODE(TFTP_UL_REQ); + default: + snprintf(buf, sizeof(buf), "%04x", code); + return buf; } +#undef CASE_CODE +} + +static uint16_t to_region_code(const char *region) +{ +#define REGION_CODE(r, c) if (!strcasecmp(region, r)) return c + REGION_CODE("NA", 0x0001); + REGION_CODE("WW", 0x0002); + REGION_CODE("GR", 0x0003); + REGION_CODE("PR", 0x0004); + REGION_CODE("RU", 0x0005); + REGION_CODE("BZ", 0x0006); + REGION_CODE("IN", 0x0007); + REGION_CODE("KO", 0x0008); + REGION_CODE("JP", 0x0009); +#undef REGION_CODE + return 0; } -static void msg_dump(struct nmrp_msg *msg, int dump_opts) +static void msg_dump(struct nmrp_msg *msg) { - struct nmrp_opt *opt; - int remain_len, len, i; + int remain_len; fprintf(stderr, "res=0x%04x, code=0x%02x, id=0x%02x, len=%u", msg->reserved, msg->code, msg->id, msg->len); remain_len = msg->len - NMRP_HDR_LEN; fprintf(stderr, "%s\n", remain_len ? "" : " (no opts)"); - - if (dump_opts) { - opt = msg->opts; - - while (remain_len > 0) { - len = opt->len; - fprintf(stderr, " opt type=%u, len=%u", opt->type, len); - for (i = 0; i != len - NMRP_OPT_LEN; ++i) { - if (!(i % 16)) { - fprintf(stderr, "\n "); - } - - fprintf(stderr, "%02x ", ((char*)&opt->val)[i] & 0xff); - } - fprintf(stderr, "\n"); - remain_len -= len; - opt = (struct nmrp_opt*)(((char*)opt) + len); - } - } } static void msg_hton(struct nmrp_msg *msg) { uint32_t i = 0; + struct nmrp_opt *opt = msg->opts, *next; msg->reserved = htons(msg->reserved); msg->len = htons(msg->len); for (; i != msg->num_opts; ++i) { - msg->opts[i].len = htons(msg->opts[i].len); - msg->opts[i].type = htons(msg->opts[i].type); + next = NMRP_OPT_NEXT(opt); + opt->len = htons(opt->len); + opt->type = htons(opt->type); + opt = next; } } @@ -132,116 +176,152 @@ static int msg_ntoh(struct nmrp_msg *msg) { struct nmrp_opt *opt = msg->opts; int remaining; - - msg_hdr_ntoh(msg); + remaining = msg->len - NMRP_HDR_LEN; + // FIXME maximum of two options supported, maximum option + // size is 12 + if (remaining < NMRP_MAX_OPT_NUM * NMRP_MAX_OPT_SIZE) { + while (remaining > 0) { + if (remaining < NMRP_OPT_HDR_LEN) { + break; + } + + opt->type = ntohs(opt->type); + opt->len = ntohs(opt->len); + + if (!opt->len || opt->len > NMRP_MAX_OPT_SIZE) { + break; + } + + remaining -= opt->len; + opt = NMRP_OPT_NEXT(opt); + } + + if (!remaining) { + return 0; + } + } + + fprintf(stderr, "Unexpected message format.\n"); + msg_dump(msg); + return 1; +} + +static void *msg_opt_data(struct nmrp_msg *msg, uint16_t type, uint16_t *len) +{ + static char buf[128]; + struct nmrp_opt *opt = msg->opts; + int remaining = msg->len - NMRP_HDR_LEN; + + memset(buf, 0, sizeof(buf)); + while (remaining > 0) { - if (remaining < NMRP_OPT_LEN) { - fprintf(stderr, "Malformed message.\n"); - msg_dump(msg, 0); - return 1; + if (opt->type == type) { + if (opt->len == NMRP_OPT_HDR_LEN) { + return NULL; + } + *len = opt->len - NMRP_OPT_HDR_LEN; + memcpy(buf, &opt->val, MIN(*len, sizeof(buf)-1)); + return buf; } - opt->type = ntohs(opt->type); - opt->len = ntohs(opt->len); + if (!opt->len) { + break; + } remaining -= opt->len; - ++opt; + opt = NMRP_OPT_NEXT(opt); } - if (remaining) { - fprintf(stderr, "Trailing data in message.\n"); - msg_dump(msg, 0); - return 1; - } - - return 0; + return NULL; } -static int intf_get_info(int sock, const char *name, int *index, - uint8_t *hwaddr) +static void msg_opt_add(struct nmrp_msg *msg, uint16_t type, void *data, + uint16_t len) { - struct ifreq ifr; + uint32_t i = 0; + struct nmrp_opt *opt = msg->opts; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); + if (len + NMRP_OPT_HDR_LEN > NMRP_MAX_OPT_SIZE + || msg->num_opts == NMRP_MAX_OPT_NUM) { + fprintf(stderr, "Invalid option - this is a bug.\n"); + } - if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) { - perror("ioctl(SIOCGIFINDEX)"); - return -1; + for (; i <= msg->num_opts; ++i) { + opt = NMRP_OPT_NEXT(opt); } - *index = ifr.ifr_ifindex; - if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) { - perror("ioctl(SIOCGIFHWADDR)"); - return -1; + opt->len = NMRP_OPT_HDR_LEN + len; + opt->type = type; + + if (len) { + memcpy(&opt->val, data, len); } - memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - return 0; + msg->len += opt->len; + ++msg->num_opts; } -static int pkt_send(int sock, struct sockaddr_ll *addr, struct nmrp_pkt *pkt) +static inline void msg_init(struct nmrp_msg *msg, uint16_t code) +{ + memset(msg, 0, sizeof(*msg)); + msg->len = NMRP_HDR_LEN; + msg->code = code; +} + +#ifdef NMRPFLASH_FUZZ +#define NMRP_INITIAL_TIMEOUT 0 +#define ethsock_create(a, b) ((struct ethsock*)1) +#define ethsock_get_hwaddr(a) ethsock_get_hwaddr_fake(a) +#define ethsock_recv(sock, buf, len) read(STDIN_FILENO, buf, len) +#define ethsock_send(a, b, c) (0) +#define ethsock_set_timeout(a, b) (0) +#define ethsock_ip_add(a, b, c, d) (0) +#define ethsock_ip_del(a, b) (0) +#define ethsock_close(a) (0) +#define tftp_put(a) (0) + +static uint8_t *ethsock_get_hwaddr_fake(struct ethsock* sock) +{ + static uint8_t hwaddr[6] = { 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa }; + return hwaddr; +} +#else +#define NMRP_INITIAL_TIMEOUT 60 +#endif + +static int pkt_send(struct ethsock *sock, struct nmrp_pkt *pkt) { size_t len = ntohs(pkt->msg.len) + sizeof(pkt->eh); - return sendto(sock, pkt, len, 0, (struct sockaddr*)addr, sizeof(*addr)); + return ethsock_send(sock, pkt, MAX(len, 64)); } -static int pkt_recv(int sock, struct nmrp_pkt *pkt) +static int pkt_recv(struct ethsock *sock, struct nmrp_pkt *pkt) { - struct sockaddr_ll from; - socklen_t addrlen; ssize_t bytes, len; memset(pkt, 0, sizeof(*pkt)); - bytes = recvfrom(sock, pkt, NMRP_MIN_PKT_LEN, MSG_PEEK, - (struct sockaddr*)&from, &addrlen); - + bytes = ethsock_recv(sock, pkt, sizeof(*pkt)); if (bytes < 0) { - if (errno == EAGAIN) { - return 2; - } - perror("recvfrom(pkt)"); return 1; - } else if (ntohs(pkt->eh.ether_type) != ETH_P_NMRP) { - return 3; + } else if (!bytes) { + return 2; } else if (bytes < NMRP_MIN_PKT_LEN) { - fprintf(stderr, "Short packet (%zi bytes)\n", bytes); + fprintf(stderr, "Short packet (%d bytes)\n", (int)bytes); return 1; } msg_hdr_ntoh(&pkt->msg); len = pkt->msg.len + sizeof(pkt->eh); - bytes = recvfrom(sock, pkt, len, MSG_DONTWAIT, NULL, NULL); - if (bytes < 0) { - perror("recvfrom(msg)"); - return 1; - } else if (bytes != len) { - fprintf(stderr, "Unexpected message length (%zi bytes).\n", len); - return 1; - } else { - if (msg_ntoh(&pkt->msg) != 0) { - return 1; - } - return 0; - } - - return 1; -} - -static int sock_bind_to_intf(int sock, const char *name) -{ - struct ifreq ifr; - - strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); - if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) { - perror("setsockopt(SO_BINDTODEVICE)"); + if (bytes < len) { + fprintf(stderr, "Short packet (expected %d, got %d).\n", + (int)len, (int)bytes); return 1; } - return 0; + return msg_ntoh(&pkt->msg); } static int mac_parse(const char *str, uint8_t *hwaddr) @@ -268,16 +348,64 @@ static int mac_parse(const char *str, uint8_t *hwaddr) return 0; } +struct is_valid_ip_arg +{ + struct in_addr *ipaddr; + struct in_addr *ipmask; + int result; +}; + +static int is_valid_ip_cb(struct ethsock_ip_callback_args *args) +{ +#define SUBNET(x) ((x)->ipaddr->s_addr & (x)->ipmask->s_addr) + struct is_valid_ip_arg *arg = args->arg; + if (SUBNET(args) == SUBNET(arg)) { + arg->result = args->ipaddr->s_addr != arg->ipaddr->s_addr; + return 0; + } + + return 1; +#undef SUBNET +} + +static int is_valid_ip(struct ethsock *sock, struct in_addr *ipaddr, + struct in_addr *ipmask) +{ + int status; + struct is_valid_ip_arg arg = { + .ipaddr = ipaddr, + .ipmask = ipmask, + .result = 0 + }; + + status = ethsock_for_each_ip(sock, is_valid_ip_cb, &arg); + return status < 0 ? status : arg.result; +} + +static void sigh(int sig) +{ + g_interrupted = 1; +} + static const char *spinner = "\\|/-"; int nmrp_do(struct nmrpd_args *args) { struct nmrp_pkt tx, rx; - struct sockaddr_ll addr; - uint8_t src[ETH_ALEN], dest[ETH_ALEN]; - struct in_addr ipaddr, ipmask; + uint8_t *src, dest[6]; + uint16_t len, region; + char *filename; time_t beg; - int i, sock, err, ulreqs, expect; + int i, status, ulreqs, expect, upload_ok, autoip; + struct ethsock *sock; + struct ethsock_ip_undo *ip_undo = NULL; + struct ethsock_arp_undo *arp_undo = NULL; + uint32_t intf_addr; + void (*sigh_orig)(int); + struct { + struct in_addr addr; + struct in_addr mask; + } PACKED ipconf; if (args->op != NMRP_UPLOAD_FW) { fprintf(stderr, "Operation not implemented.\n"); @@ -285,90 +413,135 @@ int nmrp_do(struct nmrpd_args *args) } if (!mac_parse(args->mac, dest)) { - fprintf(stderr, "Invalid MAC address %s.\n", args->mac); + fprintf(stderr, "Invalid MAC address '%s'.\n", args->mac); return 1; } - if (!inet_aton(args->ipaddr, &ipaddr)) { - fprintf(stderr, "Invalid IP address %s.\n", args->ipaddr); + ipconf.mask.s_addr = inet_addr(args->ipmask); + if (ipconf.mask.s_addr == INADDR_NONE + || netmask(bitcount(ipconf.mask.s_addr)) != ipconf.mask.s_addr) { + fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask); return 1; } - if (!inet_aton(args->ipmask, &ipmask)) { - fprintf(stderr, "Invalid subnet mask %s.\n", args->ipmask); - return 1; + if (!args->ipaddr) { + autoip = true; + /* The MAC of the device that was used to test this utility starts + * with a4:2b:8c, hence 164 (0xa4) and 183 (0x2b + 0x8c) + */ + args->ipaddr = "10.164.183.252"; + + if (!args->ipaddr_intf) { + args->ipaddr_intf = "10.164.183.253"; + } + } else if (args->ipaddr_intf) { + autoip = true; + } else { + autoip = false; } - if (access(args->filename, R_OK) == -1) { - fprintf(stderr, "Error accessing file %s.\n", args->filename); + if ((ipconf.addr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) { + fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr); return 1; } - err = 1; - - sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_NMRP)); - if (sock == -1) { - perror("socket"); + if (args->ipaddr_intf && (intf_addr = inet_addr(args->ipaddr_intf)) == INADDR_NONE) { + fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr_intf); return 1; } - if (intf_get_info(sock, args->intf, &addr.sll_ifindex, src)) { + if (args->file_local && strcmp(args->file_local, "-") && access(args->file_local, R_OK) == -1) { + fprintf(stderr, "Error accessing file '%s'.\n", args->file_local); return 1; } - if (sock_bind_to_intf(sock, args->intf)) { - return 1; + if (args->file_remote) { + if (!tftp_is_valid_filename(args->file_remote)) { + fprintf(stderr, "Invalid remote filename '%s'.\n", + args->file_remote); + return 1; + } } - if (sock_set_rx_timeout(sock, args->rx_timeout)) { + if (args->region) { + region = htons(to_region_code(args->region)); + if (!region) { + fprintf(stderr, "Invalid region code '%s'.\n", args->region); + return 1; + } + } else { + region = 0; + } + + status = 1; + + sock = ethsock_create(args->intf, ETH_P_NMRP); + if (!sock) { return 1; } - addr.sll_family = AF_PACKET; - addr.sll_protocol = htons(ETH_P_NMRP); - addr.sll_halen = ETH_ALEN; - memcpy(addr.sll_addr, dest, ETH_ALEN); + sigh_orig = signal(SIGINT, sigh); - memcpy(tx.eh.ether_shost, src, ETH_ALEN); - memcpy(tx.eh.ether_dhost, dest, ETH_ALEN); + if (!autoip) { + status = is_valid_ip(sock, &ipconf.addr, &ipconf.mask); + if (status <= 0) { + if (!status) { + fprintf(stderr, "Address %s/%s cannot be used on interface %s.\n", + args->ipaddr, args->ipmask, args->intf); + } + goto out; + } + } else { + if (verbosity) { + printf("Adding %s to interface %s.\n", args->ipaddr_intf, args->intf); + } + + if (ethsock_ip_add(sock, intf_addr, ipconf.mask.s_addr, &ip_undo) != 0) { + goto out; + } + } + + if (ethsock_set_timeout(sock, args->rx_timeout)) { + goto out; + } + + src = ethsock_get_hwaddr(sock); + if (!src) { + goto out; + } + + memcpy(tx.eh.ether_shost, src, 6); + memcpy(tx.eh.ether_dhost, dest, 6); tx.eh.ether_type = htons(ETH_P_NMRP); - tx.msg.reserved = 0; - tx.msg.code = NMRP_C_ADVERTISE; - tx.msg.id = 0; - tx.msg.num_opts = 1; - tx.msg.opts[0].type = NMRP_O_MAGIC_NO; - tx.msg.opts[0].len = NMRP_OPT_LEN + 4; - tx.msg.opts[0].val.magic[0] = 'N'; - tx.msg.opts[0].val.magic[1] = 'T'; - tx.msg.opts[0].val.magic[2] = 'G'; - tx.msg.opts[0].val.magic[3] = 'R'; - - msg_update_len(&tx.msg); + msg_init(&tx.msg, NMRP_C_ADVERTISE); + msg_opt_add(&tx.msg, NMRP_O_MAGIC_NO, "NTGR", 4); msg_hton(&tx.msg); i = 0; - beg = time(NULL); + upload_ok = 0; + beg = time_monotonic(); - while (1) { - printf("\rAdvertising NMRP server on %s ... %c", args->intf, - spinner[i]); + while (!g_interrupted) { + printf("\rAdvertising NMRP server on %s ... %c", + args->intf, spinner[i]); fflush(stdout); i = (i + 1) & 3; - if (pkt_send(sock, &addr, &tx) < 0) { - perror("sendto"); + if (pkt_send(sock, &tx) < 0) { + xperror("sendto"); goto out; } - err = pkt_recv(sock, &rx); - if (err == 0 && memcmp(rx.eh.ether_dhost, src, ETH_ALEN) == 0) { + status = pkt_recv(sock, &rx); + if (status == 0 && memcmp(rx.eh.ether_dhost, src, 6) == 0) { break; - } else if (err == 1) { - printf("ERR\n"); + } else if (status == 1) { goto out; } else { - if ((time(NULL) - beg) >= 60) { + /* because we don't want nmrpflash's exit status to be zero */ + status = 1; + if ((time_monotonic() - beg) >= NMRP_INITIAL_TIMEOUT) { printf("\nNo response after 60 seconds. Bailing out.\n"); goto out; } @@ -380,106 +553,160 @@ int nmrp_do(struct nmrpd_args *args) expect = NMRP_C_CONF_REQ; ulreqs = 0; - do { + while (!g_interrupted) { if (expect != NMRP_C_NONE && rx.msg.code != expect) { - fprintf(stderr, "Received code 0x%02x while waiting for 0x%02x!\n", - rx.msg.code, expect); + fprintf(stderr, "Received %s while waiting for %s!\n", + msg_code_str(rx.msg.code), msg_code_str(expect)); } - tx.msg.code = NMRP_C_NONE; - tx.msg.reserved = 0; - tx.msg.id = 0; - tx.msg.num_opts = 0; - tx.msg.len = 0; + msg_init(&tx.msg, NMRP_C_NONE); - err = 1; + status = 1; switch (rx.msg.code) { case NMRP_C_ADVERTISE: - printf("Received NMRP advertisement from " - "%02x:%02x:%02x:%02x:%02x:%02x.\n", - rx.eh.ether_shost[0], rx.eh.ether_shost[1], - rx.eh.ether_shost[2], rx.eh.ether_shost[3], - rx.eh.ether_shost[4], rx.eh.ether_shost[5]); - err = 1; + printf("Received NMRP advertisement from %s.\n", + mac_to_str(rx.eh.ether_shost)); + status = 1; goto out; case NMRP_C_CONF_REQ: tx.msg.code = NMRP_C_CONF_ACK; - tx.msg.num_opts = 2; - tx.msg.opts[0].type = NMRP_O_DEV_IP; - tx.msg.opts[0].len = NMRP_OPT_LEN + 2 * IP_LEN; + msg_opt_add(&tx.msg, NMRP_O_DEV_IP, &ipconf, 8); + msg_opt_add(&tx.msg, NMRP_O_FW_UP, NULL, 0); - memcpy(tx.msg.opts[0].val.ip.addr, &ipaddr, IP_LEN); - memcpy(tx.msg.opts[0].val.ip.mask, &ipmask, IP_LEN); - - tx.msg.opts[1].type = NMRP_O_FW_UP; - tx.msg.opts[1].len = NMRP_OPT_LEN; +#ifdef NMRPFLASH_SET_REGION + if (region) { + msg_opt_add(&tx.msg, NMRP_O_DEV_REGION, ®ion, 2); + } +#endif expect = NMRP_C_TFTP_UL_REQ; - printf("Configuration request received from " - "%02x:%02x:%02x:%02x:%02x:%02x.\n", - rx.eh.ether_shost[0], rx.eh.ether_shost[1], - rx.eh.ether_shost[2], rx.eh.ether_shost[3], - rx.eh.ether_shost[4], rx.eh.ether_shost[5]); + printf("Received configuration request from %s.\n", + mac_to_str(rx.eh.ether_shost)); - memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, ETH_ALEN); - memcpy(addr.sll_addr, rx.eh.ether_shost, ETH_ALEN); + memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, 6); - printf("Sending configuration: ip %s, mask %s.\n", + printf("Sending configuration: %s, netmask %s.\n", args->ipaddr, args->ipmask); + if (ethsock_arp_add(sock, rx.eh.ether_shost, ipconf.addr.s_addr, &arp_undo) != 0) { + goto out; + } + break; case NMRP_C_TFTP_UL_REQ: - if (++ulreqs > 5) { - fprintf(stderr, "Device re-requested file upload %d " - "times; aborting.\n", ulreqs); - tx.msg.code = NMRP_C_CLOSE_REQ; + if (!upload_ok) { + if (++ulreqs > 5) { + printf("Bailing out after %d upload requests.\n", + ulreqs); + tx.msg.code = NMRP_C_CLOSE_REQ; + break; + } + } else { + if (verbosity) { + printf("Ignoring extra upload request.\n"); + } + ethsock_set_timeout(sock, args->ul_timeout); + tx.msg.code = NMRP_C_KEEP_ALIVE_REQ; break; } - if (!args->tftpcmd) { - printf("Uploading %s ... ", args->filename); - fflush(stdout); - err = tftp_put(args); - } else { - printf("Running %s ... ", args->tftpcmd); + len = 0; + filename = msg_opt_data(&rx.msg, NMRP_O_FILE_NAME, &len); + if (filename) { + if (!args->file_remote) { + args->file_remote = filename; + } + printf("Received upload request: filename '%.*s'.\n", + len, filename); + } else if (!args->file_remote) { + args->file_remote = args->file_local; + printf("Received upload request with empty filename.\n"); + } + + status = 0; + + if (args->tftpcmd) { + printf("Executing '%s' ... \n", args->tftpcmd); + setenv("IP", inet_ntoa(ipconf.addr), 1); + setenv("PORT", lltostr(args->port, 10), 1); + setenv("MAC", mac_to_str(rx.eh.ether_shost), 1); + setenv("NETMASK", inet_ntoa(ipconf.mask), 1); + //setenv("FILENAME", args->file_remote ? args->file_remote : "", 1); + status = system(args->tftpcmd); + } + + if (!status && args->file_local) { + if (!autoip) { + status = is_valid_ip(sock, &ipconf.addr, &ipconf.mask); + if (status < 0) { + goto out; + } else if (!status) { + printf("IP address of %s has changed. Please assign a " + "static ip to the interface.\n", args->intf); + tx.msg.code = NMRP_C_CLOSE_REQ; + break; + } + } + + if (verbosity) { + printf("Using remote filename '%s'.\n", + args->file_remote); + } + + if (!strcmp(args->file_local, "-")) { + printf("Uploading from stdin ... "); + } else { + printf("Uploading %s ... ", leafname(args->file_local)); + } fflush(stdout); - err = system(args->tftpcmd); + if (!(status = tftp_put(args))) { + printf("OK\n"); + } + } - if (!err) { - printf("OK\nWaiting for remote to respond.\n"); - sock_set_rx_timeout(sock, args->ul_timeout); - expect = NMRP_C_CLOSE_REQ; + if (!status) { + printf("Waiting for remote to respond.\n"); + upload_ok = 1; + ethsock_set_timeout(sock, args->ul_timeout); + tx.msg.code = NMRP_C_KEEP_ALIVE_REQ; + expect = NMRP_C_NONE; + } else if (status == -2) { + expect = NMRP_C_TFTP_UL_REQ; } else { - printf("\n"); goto out; } break; case NMRP_C_KEEP_ALIVE_REQ: tx.msg.code = NMRP_C_KEEP_ALIVE_ACK; + ethsock_set_timeout(sock, args->ul_timeout); + printf("Received keep-alive request.\n"); break; case NMRP_C_CLOSE_REQ: tx.msg.code = NMRP_C_CLOSE_ACK; break; case NMRP_C_CLOSE_ACK: - err = 0; + status = 0; goto out; default: fprintf(stderr, "Unknown message code 0x%02x!\n", rx.msg.code); - msg_dump(&rx.msg, 0); + msg_dump(&rx.msg); } if (tx.msg.code != NMRP_C_NONE) { - msg_update_len(&tx.msg); msg_hton(&tx.msg); - if (pkt_send(sock, &addr, &tx) < 0) { - perror("sendto"); + if (pkt_send(sock, &tx) < 0) { + xperror("sendto"); + goto out; + } + + if (tx.msg.code == NMRP_C_CLOSE_REQ) { goto out; } } @@ -489,22 +716,32 @@ int nmrp_do(struct nmrpd_args *args) break; } - err = pkt_recv(sock, &rx); - if (err) { - if (err == 2) { - fprintf(stderr, "Timeout while waiting for 0x%02x.\n", expect); + status = pkt_recv(sock, &rx); + if (status) { + if (status == 2) { + fprintf(stderr, "Timeout while waiting for %s.\n", + msg_code_str(expect)); } goto out; } - sock_set_rx_timeout(sock, args->rx_timeout); + ethsock_set_timeout(sock, args->rx_timeout); - } while (1); + } - err = 0; + if (!g_interrupted) { + status = 0; + if (ulreqs) { + printf("Reboot your device now.\n"); + } else { + printf("No upload request received.\n"); + } + } out: - close(sock); - - return err; + signal(SIGINT, sigh_orig); + ethsock_arp_del(sock, &arp_undo); + ethsock_ip_del(sock, &ip_undo); + ethsock_close(sock); + return status; }