diff options
Diffstat (limited to 'src/tools/irm/irm_utils.c')
| -rw-r--r-- | src/tools/irm/irm_utils.c | 233 |
1 files changed, 232 insertions, 1 deletions
diff --git a/src/tools/irm/irm_utils.c b/src/tools/irm/irm_utils.c index 69873097..c43accec 100644 --- a/src/tools/irm/irm_utils.c +++ b/src/tools/irm/irm_utils.c @@ -77,10 +77,21 @@ */ -#include <string.h> +#if defined(__linux__) || defined(__CYGWIN__) +#define _DEFAULT_SOURCE +#else +#define _POSIX_C_SOURCE 200809L +#endif + +#include <ouroboros/ipcp.h> #include "irm_utils.h" +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + int matches(const char * cmd, const char * pattern) { @@ -123,3 +134,223 @@ int wildcard_match(const char * pattern, } } } + +/* Splits "<addr>[:<port>]"; bare IPv6 needs no brackets. */ +static int parse_udp_str(const char * str, + char * host, + int * port) +{ + struct in6_addr v6; + char buf[POA_HOST_STRLEN + 1]; + char * p; + char * end; + long n; + + *port = POA_UDP_PORT; + + if (strlen(str) > POA_HOST_STRLEN) + goto fail; + + strcpy(buf, str); + + if (buf[0] == '[') { + p = strchr(buf, ']'); + if (p == NULL) + goto fail; + + *p++ = '\0'; + + strcpy(host, buf + 1); + + if (*p == '\0') + return 0; + + if (*p != ':') + goto fail; + + ++p; + } else if (inet_pton(AF_INET6, buf, &v6) == 1) { + strcpy(host, buf); + return 0; + } else { + p = strrchr(buf, ':'); + if (p == NULL) { + strcpy(host, buf); + return 0; + } + + *p++ = '\0'; + + strcpy(host, buf); + } + + n = strtol(p, &end, 10); + if (*p == '\0' || *end != '\0' || n < 1 || n > 65535) + goto fail; + + *port = (int) n; + + return 0; + fail: + printf("Invalid UDP address: \"%s\".\n", str); + return -1; +} + +/* An unresolved name is left for the IRMd, which picks the family. */ +int poa_addr_set_udp(struct poa_addr * addr, + const char * str) +{ + char host[POA_HOST_STRLEN + 1]; + int port; + + if (parse_udp_str(str, host, &port) < 0) + return -1; + + if (inet_pton(AF_INET, host, &addr->udp4.ip_addr) == 1) { + addr->type = POA_UDP4; + addr->udp4.port = port; + return 0; + } + + if (inet_pton(AF_INET6, host, &addr->udp6.ip_addr) == 1) { + addr->type = POA_UDP6; + addr->udp6.port = port; + return 0; + } + + addr->type = POA_UDP; + addr->udp4.port = port; + + strcpy(addr->hostname, host); + + return 0; +} + +/* + * Parses a hex ethertype; rejects garbage and out-of-range values. + * Overflow clamps to LONG_MAX and lands in the range check. + */ +int parse_ethertype(const char * str, + uint16_t * ethertype) +{ + char * end; + long val; + + val = strtol(str, &end, 16); + + if (end == str || *end != '\0') + return -1; + + if (val < 0 || val > 0xFFFF) + return -1; + + *ethertype = (uint16_t) val; + + return 0; +} + +int poa_addr_set_eth(struct poa_addr * addr, + const char * devstr, + uint16_t ethertype) +{ + addr->type = POA_ETH; + + addr->eth.src.ethertype = ethertype; + addr->eth.dst.ethertype = ethertype; + + if (devstr != NULL) { + if (strlen(devstr) > DEV_NAME_SIZE) { + printf("Invalid device name: \"%s\".\n", devstr); + return -1; + } + + strcpy(addr->eth.src.dev, devstr); + } + + return 0; +} + +/* Matches src/ipcpd/ipcp.c; keep in sync. */ +void poa_spec_str(const struct poa_spec * poa, + char * buf, + size_t len) +{ + char addr[INET6_ADDRSTRLEN]; + + switch (poa->type) { + case POA_UDP4: + if (inet_ntop(AF_INET, &poa->udp4.ip_addr, + addr, sizeof(addr)) == NULL) + break; + + snprintf(buf, len, "udp4 %s:%u", addr, poa->udp4.port); + return; + case POA_UDP6: + if (inet_ntop(AF_INET6, &poa->udp6.ip_addr, + addr, sizeof(addr)) == NULL) + break; + + snprintf(buf, len, "udp6 [%s]:%u", addr, poa->udp6.port); + return; + case POA_ETH: + snprintf(buf, len, "eth %s 0x%04X", poa->eth.dev, + poa->eth.ethertype); + return; + default: + break; + } + + snprintf(buf, len, "(unknown)"); +} + +int poa_spec_set(struct poa_spec * poa, + const char * udpstr, + const char * devstr, + uint16_t ethertype) +{ + char host[POA_HOST_STRLEN + 1]; + int port; + + memset(poa, 0, sizeof(*poa)); + + if ((udpstr != NULL) + (devstr != NULL) > 1) { + printf("A PoA is an address or a device.\n"); + return -1; + } + + if (udpstr != NULL) { + if (parse_udp_str(udpstr, host, &port) < 0) + return -1; + + if (inet_pton(AF_INET, host, &poa->udp4.ip_addr) == 1) { + poa->type = POA_UDP4; + poa->udp4.port = port; + return 0; + } + + if (inet_pton(AF_INET6, host, &poa->udp6.ip_addr) == 1) { + poa->type = POA_UDP6; + poa->udp6.port = port; + return 0; + } + + printf("Invalid IP address: \"%s\".\n", udpstr); + return -1; + } + + if (devstr != NULL) { + if (strlen(devstr) > DEV_NAME_SIZE) { + printf("Invalid device name: \"%s\".\n", devstr); + return -1; + } + + poa->type = POA_ETH; + poa->eth.ethertype = ethertype; + + strcpy(poa->eth.dev, devstr); + + return 0; + } + + return -1; +} |
