X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=nmrp.c;h=4501d63fa14fd5171a6638a10ee256e9e881584f;hb=1b27e82a105d7a130dcd2a36f903ca0377a0b041;hp=380277de10dd410ebfcc17a88e525a1fcfc538d8;hpb=c9206ddaeec2a5839b6720fef7f644aa668a6130;p=oweals%2Fnmrpflash.git diff --git a/nmrp.c b/nmrp.c index 380277d..4501d63 100644 --- a/nmrp.c +++ b/nmrp.c @@ -1,32 +1,44 @@ -#define _BSD_SOURCE -#include -#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 #include #include +#include +#include "nmrpd.h" #define NMRP_HDR_LEN 6 -#define NMRP_OPT_LEN 4 -#define NMRP_MAX_OPT 6 -#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 ETH_P_NMRP 0x0912 -#define IP_LEN 4 -#define PACKED __attribute__((__packed__)) -#define MAX_LOOP_RECV 1024 -#define IS_OOO_CODE(x) (x == NMRP_C_CLOSE_REQ \ - || x == NMRP_C_KEEP_ALIVE_REQ \ - || x == NMRP_C_TFTP_UL_REQ) +#ifndef PACKED +#define PACKED __attribute__((__packed__)) +#endif -extern int tftp_put(const char *filename, const char *ipaddr, uint16_t port); -extern int sock_set_rx_timeout(int fd, unsigned msec); +#ifdef NMRPFLASH_WINDOWS +#define setenv(name, value, overwrite) SetEnvironmentVariable(name, value) +#endif enum nmrp_code { NMRP_C_NONE = 0, @@ -52,13 +64,7 @@ enum nmrp_opt_type { struct nmrp_opt { uint16_t type; uint16_t len; - union { - uint8_t magic[4]; - struct { - uint8_t addr[IP_LEN]; - uint8_t mask[IP_LEN]; - } ip; - } val; + char val[1]; } PACKED; struct nmrp_msg { @@ -66,268 +72,441 @@ struct nmrp_msg { uint8_t code; uint8_t id; uint16_t len; - struct nmrp_opt opts[6]; - uint32_t num_opts; + char opts[44]; } 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 MSG_CODE(x) case NMRP_C_ ## x: return #x + static char buf[16]; + + switch (code) { + MSG_CODE(ADVERTISE); + MSG_CODE(CONF_REQ); + MSG_CODE(CONF_ACK); + MSG_CODE(CLOSE_REQ); + MSG_CODE(CLOSE_ACK); + MSG_CODE(KEEP_ALIVE_REQ); + MSG_CODE(KEEP_ALIVE_ACK); + MSG_CODE(TFTP_UL_REQ); + default: + snprintf(buf, sizeof(buf), "%04x", ntohs(code)); + return buf; } +#undef MSG_CODE } -static void msg_hton(struct nmrp_msg *msg) +static uint16_t to_region_code(const char *region) { - uint32_t i = 0; - - 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); - } +#define REGION_CODE(r, c) if (!strcasecmp(region, r)) return htons(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_hdr_ntoh(struct nmrp_msg *msg) +static void msg_dump(struct nmrp_msg *msg) { - msg->reserved = ntohs(msg->reserved); - msg->len = ntohs(msg->len); + int rem; + + fprintf(stderr, "res=0x%04x, code=0x%02x, id=0x%02x, len=%u", + ntohs(msg->reserved), msg->code, msg->id, ntohs(msg->len)); + + rem = ntohs(msg->len) - NMRP_HDR_LEN; + fprintf(stderr, "%s\n", rem ? "" : " (no opts)"); } -static int msg_ntoh(struct nmrp_msg *msg) +static void *msg_opt(struct nmrp_msg *msg, uint16_t type, uint16_t* len) { - struct nmrp_opt *opt = msg->opts; - int remaining; - - msg_hdr_ntoh(msg); - remaining = msg->len - NMRP_HDR_LEN; - - while (remaining > 0) { - if (remaining < NMRP_OPT_LEN) { - fprintf(stderr, "malformed message (rem=%d)\n", remaining); - return 1; + struct nmrp_opt* opt = (struct nmrp_opt*)msg->opts; + size_t rem = ntohs(msg->len) - NMRP_HDR_LEN; + uint16_t olen; + + do { + olen = ntohs(opt->len); + if (olen < NMRP_OPT_HDR_LEN || olen > rem) { + break; } - opt->type = ntohs(opt->type); - opt->len = ntohs(opt->len); + if (ntohs(opt->type) == type) { + if (len) { + *len = olen; + } - remaining -= opt->len; + return opt->val; + } + + opt = (struct nmrp_opt*)(((char *)opt) + olen); + rem -= olen; + } while (rem); + + return NULL; +} + +static char *msg_filename(struct nmrp_msg *msg) +{ + static char buf[256]; + uint16_t len; + char *p = msg_opt(msg, NMRP_O_FILE_NAME, &len); + if (p) { + len = MIN(sizeof(buf) - 1, len); + memcpy(buf, p, len); + buf[len] = '\0'; + return buf; } - return 0; + return NULL; } -static void msg_dump(struct nmrp_msg *msg) +static inline void msg_init(struct nmrp_msg *msg, uint16_t code) +{ + memset(msg, 0, sizeof(*msg)); + msg->len = htons(NMRP_HDR_LEN); + msg->code = code; +} + +static char *msg_mkopt(struct nmrp_msg *msg, char *p, uint16_t type, const void *val, size_t len) { - struct nmrp_opt *opt; - int remain_len, len, i; + struct nmrp_opt* opt = (struct nmrp_opt*)p; - printf("res=0x%04x, code=0x%02x, id=0x%02x, len=%u", msg->reserved, - msg->code, msg->id, msg->len); + len &= 0xffff; - remain_len = msg->len - NMRP_HDR_LEN; - printf("%s\n", remain_len ? "" : " (no opts)"); + msg->len = ntohs(msg->len); - opt = msg->opts; + if ((msg->len + len > sizeof(*msg))) { + fprintf(stderr, "Error: invalid option - this is a bug\n"); + exit(1); + } - while (remain_len > 0) { - len = opt->len; - printf(" opt type=%u, len=%u", opt->type, len); - for (i = 0; i != len - NMRP_OPT_LEN; ++i) { - if (!(i % 16)) { - printf("\n "); - } + opt->type = htons(type); + opt->len = NMRP_OPT_HDR_LEN + len; - printf("%02x ", ((char*)&opt->val)[i] & 0xff); - } - printf("\n"); - remain_len -= len; - opt = (struct nmrp_opt*)(((char*)opt) + len); + if (val) { + memcpy(opt->val, val, len); } + + msg->len += opt->len; + p += opt->len; + + msg->len = htons(msg->len); + opt->len = htons(opt->len); + + return p; } -static int intf_get_index_and_addr(int fd, const char *name, int *index, - uint8_t *hwaddr) +static void msg_mkadvertise(struct nmrp_msg *msg, const char *magic) { - struct ifreq ifr; + msg_init(msg, NMRP_C_ADVERTISE); + msg_mkopt(msg, msg->opts, NMRP_O_MAGIC_NO, magic, strlen(magic)); +} - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); +static void msg_mkconfack(struct nmrp_msg *msg, uint32_t ipaddr, uint32_t ipmask, uint16_t region) +{ + char *p; + uint32_t ip[2] = { ipaddr, ipmask }; - if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { - perror("ioctl(SIOCGIFINDEX)"); - return -1; - } - *index = ifr.ifr_ifindex; + msg_init(msg, NMRP_C_CONF_ACK); + p = msg_mkopt(msg, msg->opts, NMRP_O_DEV_IP, &ip, 8); + p = msg_mkopt(msg, p, NMRP_O_FW_UP, NULL, 0); - if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { - perror("ioctl(SIOCGIFHWADDR)"); - return -1; +#ifdef NMRPFLASH_SET_REGION + if (region) { + p = msg_mkopt(msg, p, NMRP_O_DEV_REGION, ®ion, 2); } - memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); +#endif +} - return 0; +#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_arp_add(a, b, c, d) (0) +#define ethsock_arp_del(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(int fd, struct sockaddr_ll *addr, struct nmrp_pkt *pkt) +static int pkt_send(struct ethsock *sock, struct nmrp_pkt *pkt) { - size_t len = ntohs(pkt->msg.len) + sizeof(pkt->eh); - return sendto(fd, pkt, len, 0, (struct sockaddr*)addr, sizeof(*addr)); + return ethsock_send(sock, pkt, sizeof(*pkt)); } -static int pkt_recv(int fd, 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; + ssize_t bytes, mlen; memset(pkt, 0, sizeof(*pkt)); - bytes = recvfrom(fd, 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 < NMRP_MIN_PKT_LEN) { - fprintf(stderr, "short packet (%zi bytes)\n", bytes); return 1; + } else if (!bytes) { + return 2; } - msg_hdr_ntoh(&pkt->msg); - len = pkt->msg.len + sizeof(pkt->eh); + mlen = ntohs(pkt->msg.len); - bytes = recvfrom(fd, pkt, len, MSG_DONTWAIT, NULL, NULL); - if (bytes < 0) { - perror("recvfrom(msg)"); - return 1; - } else if (bytes != len) { - fprintf(stderr, "short message (%zi bytes)\n", len); + if (bytes < (mlen + sizeof(pkt->eh)) + || bytes < NMRP_MIN_PKT_LEN + || mlen < NMRP_HDR_LEN) { + fprintf(stderr, "Short packet (%d raw, %d message)\n", + (int)bytes, (int)mlen); return 1; - } else { - if (msg_ntoh(&pkt->msg) != 0) { + } else if (mlen > sizeof(pkt->msg)) { + printf("Truncating %d byte message.\n", (int)mlen); + pkt->msg.len = htons(sizeof(pkt->msg)); + } + + return 0; +} + +static int mac_parse(const char *str, uint8_t *hwaddr) +{ + int i; + unsigned data[6]; + + sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x%n", + data, data + 1, data + 2, data + 3, data + 4, data + 5, &i); + + if (i == strlen(str)) { + for (i = 0; i != 6; ++i) { + if (data[i] > 255) { + break; + } + + hwaddr[i] = data[i] & 0xff; + } + + if (i == 6) { return 1; } - msg_dump(&pkt->msg); + } + 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 sock_bind_to_intf(int fd, const char *name) +static int is_valid_ip(struct ethsock *sock, struct in_addr *ipaddr, + struct in_addr *ipmask) { - struct ifreq ifr; - - strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) { - perror("setsockopt(SO_BINDTODEVICE)"); - return 1; - } - - return 0; + 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 const char *arg_filename = "EX2700-V1.0.1.8.img"; -static unsigned arg_rx_timeout = 250; -static unsigned arg_ul_timeout = 60000; -static const char *arg_filename = "bad.img"; -static const char *arg_ipaddr = "192.168.2.2"; -static const char *arg_ipmask = "255.255.255.0"; -static const char *arg_intf = "enp4s0"; -static uint16_t arg_port = 69; -#if 0 -static uint8_t target[ETH_ALEN] = { 0xa4, 0x2b, 0x8c, 0x10, 0xc2, 0x96 }; -#else -static uint8_t target[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; -#endif +static void sigh(int sig) +{ + g_interrupted = 1; +} static const char *spinner = "\\|/-"; -int main(int argc, char **argv) +int nmrp_do(struct nmrpd_args *args) { struct nmrp_pkt tx, rx; - struct sockaddr_ll addr; - uint8_t hwaddr[ETH_ALEN]; - int i, fd, err, ulreqs, expect; + uint8_t *src, dest[6]; + uint16_t region; + char *filename; + time_t beg; + 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 in_addr ipaddr; + struct in_addr ipmask; + + if (args->op != NMRP_UPLOAD_FW) { + fprintf(stderr, "Operation not implemented.\n"); + return 1; + } - err = 1; + if (!mac_parse(args->mac, dest)) { + fprintf(stderr, "Invalid MAC address '%s'.\n", args->mac); + return 1; + } - fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_NMRP)); - if (fd == -1) { - perror("socket"); + ipmask.s_addr = inet_addr(args->ipmask); + if (ipmask.s_addr == INADDR_NONE + || netmask(bitcount(ipmask.s_addr)) != ipmask.s_addr) { + fprintf(stderr, "Invalid subnet mask '%s'.\n", args->ipmask); return 1; } - if (intf_get_index_and_addr(fd, arg_intf, &addr.sll_ifindex, hwaddr)) { + 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 ((ipaddr.s_addr = inet_addr(args->ipaddr)) == INADDR_NONE) { + fprintf(stderr, "Invalid IP address '%s'.\n", args->ipaddr); return 1; } - if (sock_bind_to_intf(fd, arg_intf)) { + 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 (sock_set_rx_timeout(fd, arg_rx_timeout)) { + 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; } - addr.sll_family = PF_PACKET; - //addr.sll_hatype = ARPHRD_ETHER; - //addr.sll_pkttype = PACKET_OTHERHOST; - addr.sll_protocol = htons(ETH_P_NMRP); - addr.sll_halen = ETH_ALEN; - memcpy(addr.sll_addr, target, ETH_ALEN); + if (args->file_remote) { + if (!tftp_is_valid_filename(args->file_remote)) { + fprintf(stderr, "Invalid remote filename '%s'.\n", + args->file_remote); + return 1; + } + } - memcpy(tx.eh.ether_shost, hwaddr, ETH_ALEN); - memcpy(tx.eh.ether_dhost, target, ETH_ALEN); - tx.eh.ether_type = htons(ETH_P_NMRP); + if (args->region) { + region = 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; + } - 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'; + sigh_orig = signal(SIGINT, sigh); - msg_update_len(&tx.msg); - msg_hton(&tx.msg); + if (!autoip) { + status = is_valid_ip(sock, &ipaddr, &ipmask); + 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, ipmask.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); + + msg_mkadvertise(&tx.msg, "NTGR"); i = 0; + upload_ok = 0; + beg = time_monotonic(); - while (1) { - printf("\rAdvertising NMRP server on %s ... %c", arg_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(fd, &addr, &tx) < 0) { - perror("sendto"); + if (pkt_send(sock, &tx) < 0) { + xperror("sendto"); goto out; } - err = pkt_recv(fd, &rx); - if (err == 0) { - break; - } else if (err == 1) { - printf("ERR\n"); + status = pkt_recv(sock, &rx); + if (status == 0) { + if (memcmp(rx.eh.ether_dhost, src, 6) == 0) { + break; + } else if (verbosity) { + printf("\nIgnoring bogus response: %s -> %s.\n", + mac_to_str(rx.eh.ether_shost), + mac_to_str(rx.eh.ether_dhost)); + } + } else if (status == 1) { goto out; + } else { + /* 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; + } } } @@ -336,110 +515,181 @@ int main(int argc, char **argv) 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 %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_mkconfack(&tx.msg, ipaddr.s_addr, ipmask.s_addr, region); + expect = NMRP_C_TFTP_UL_REQ; - inet_aton(arg_ipaddr, - (struct in_addr*)tx.msg.opts[0].val.ip.addr); - inet_aton(arg_ipmask, - (struct in_addr*)tx.msg.opts[0].val.ip.mask); + printf("Received configuration request from %s.\n", + mac_to_str(rx.eh.ether_shost)); - tx.msg.opts[1].type = NMRP_O_FW_UP; - tx.msg.opts[1].len = NMRP_OPT_LEN; + memcpy(tx.eh.ether_dhost, rx.eh.ether_shost, 6); - expect = NMRP_C_TFTP_UL_REQ; + printf("Sending configuration: %s, netmask %s.\n", + args->ipaddr, args->ipmask); - 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("Sending configuration: ip %s, mask %s.\n", arg_ipaddr, - arg_ipmask); + if (ethsock_arp_add(sock, rx.eh.ether_shost, ipaddr.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; } - printf("Uploading %s ... ", arg_filename); - fflush(stdout); - err = tftp_put(arg_filename, arg_ipaddr, arg_port); - if (!err) { - printf("OK\nWaiting for router to respond.\n"); - sock_set_rx_timeout(fd, arg_ul_timeout); - expect = NMRP_C_CLOSE_REQ; - } else if (err != -3) { + + filename = msg_filename(&rx.msg); + if (filename) { + if (!args->file_remote) { + args->file_remote = filename; + } + printf("Received upload request: filename '%s'.\n", filename); + } else if (!args->file_remote) { + args->file_remote = leafname(args->file_local); + printf("Received upload request without filename.\n"); + } + + status = 0; + + if (args->tftpcmd) { + printf("Executing '%s' ... \n", args->tftpcmd); + setenv("IP", inet_ntoa(ipaddr), 1); + setenv("PORT", lltostr(args->port, 10), 1); + setenv("MAC", mac_to_str(rx.eh.ether_shost), 1); + setenv("NETMASK", inet_ntoa(ipmask), 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, &ipaddr, &ipmask); + 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); + if (!(status = tftp_put(args))) { + printf("OK\n"); + } + + } + + 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 { 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, "Unhandled message code 0x%02x!\n", + fprintf(stderr, "Unknown message code 0x%02x!\n", rx.msg.code); + msg_dump(&rx.msg); } if (tx.msg.code != NMRP_C_NONE) { - msg_update_len(&tx.msg); - msg_hton(&tx.msg); + if (pkt_send(sock, &tx) < 0) { + xperror("sendto"); + goto out; + } - if (pkt_send(fd, &addr, &tx) < 0) { - perror("sendto"); + if (tx.msg.code == NMRP_C_CLOSE_REQ) { goto out; } } if (rx.msg.code == NMRP_C_CLOSE_REQ) { - printf("Remote requested to close connection.\n"); + printf("Remote finished. Closing connection.\n"); break; } - err = pkt_recv(fd, &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(fd, arg_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(fd); - - return err; + signal(SIGINT, sigh_orig); + ethsock_arp_del(sock, &arp_undo); + ethsock_ip_del(sock, &ip_undo); + ethsock_close(sock); + return status; }