From 6f1b1a05fb821d49c44195c6ff91fbe21b1b10ef Mon Sep 17 00:00:00 2001 From: Martin Schwenke Date: Mon, 11 Sep 2017 16:39:38 +1000 Subject: [PATCH] ctdb-protocol: Add ctdb_sock_addr_from_string() This and the supporting functions duplicate functionality (parse_ip() and parse_ip_port()) from common/system_util.c. The old functions will be removed at a later time. Signed-off-by: Martin Schwenke Reviewed-by: Amitay Isaacs --- ctdb/protocol/protocol_util.c | 126 ++++++++++++++++++++++++++++ ctdb/protocol/protocol_util.h | 2 + ctdb/tests/src/protocol_util_test.c | 62 +++++++++----- 3 files changed, 168 insertions(+), 22 deletions(-) diff --git a/ctdb/protocol/protocol_util.c b/ctdb/protocol/protocol_util.c index 26425d97a56..3c8c21a472a 100644 --- a/ctdb/protocol/protocol_util.c +++ b/ctdb/protocol/protocol_util.c @@ -174,6 +174,132 @@ const char *ctdb_sock_addr_to_string(TALLOC_CTX *mem_ctx, return cip; } +static int ipv4_from_string(const char *str, struct sockaddr_in *ip) +{ + int ret; + + *ip = (struct sockaddr_in) { + .sin_family = AF_INET, + }; + + ret = inet_pton(AF_INET, str, &ip->sin_addr); + if (ret != 1) { + return EINVAL; + } + +#ifdef HAVE_SOCK_SIN_LEN + ip->sin_len = sizeof(*ip); +#endif + return 0; +} + +static int ipv6_from_string(const char *str, struct sockaddr_in6 *ip6) +{ + int ret; + + *ip6 = (struct sockaddr_in6) { + .sin6_family = AF_INET6, + }; + + ret = inet_pton(AF_INET6, str, &ip6->sin6_addr); + if (ret != 1) { + return EINVAL; + } + +#ifdef HAVE_SOCK_SIN_LEN + ip6->sin6_len = sizeof(*ip6); +#endif + return 0; +} + +static int ip_from_string(const char *str, ctdb_sock_addr *addr) +{ + char *p; + int ret; + + if (addr == NULL) { + return EINVAL; + } + + ZERO_STRUCTP(addr); /* valgrind :-) */ + + /* IPv4 or IPv6 address? + * + * Use rindex() because we need the right-most ':' below for + * IPv4-mapped IPv6 addresses anyway... + */ + p = rindex(str, ':'); + if (p == NULL) { + ret = ipv4_from_string(str, &addr->ip); + } else { + uint8_t ipv4_mapped_prefix[12] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff + }; + + ret = ipv6_from_string(str, &addr->ip6); + if (ret != 0) { + return ret; + } + + /* + * Check for IPv4-mapped IPv6 address + * (e.g. ::ffff:192.0.2.128) - reparse as IPv4 if + * necessary + */ + if (memcmp(&addr->ip6.sin6_addr.s6_addr[0], + ipv4_mapped_prefix, + sizeof(ipv4_mapped_prefix)) == 0) { + /* Reparse as IPv4 */ + ret = ipv4_from_string(p+1, &addr->ip); + } + } + + return ret; +} + +int ctdb_sock_addr_from_string(const char *str, + ctdb_sock_addr *addr, bool with_port) +{ + char *p; + char s[64]; /* Much longer than INET6_ADDRSTRLEN */ + unsigned port; + char *endp = NULL; + size_t len; + bool ret; + + if (! with_port) { + ret = ip_from_string(str, addr); + return ret; + } + + /* Parse out port number and then IP address */ + + len = strlen(str); + if (len >= sizeof(s)) { + return EINVAL; + } + + strncpy(s, str, len+1); + + p = rindex(s, ':'); + if (p == NULL) { + return EINVAL; + } + + port = strtoul(p+1, &endp, 10); + if (endp == p+1 || *endp != '\0') { + /* Empty string or trailing garbage */ + return EINVAL; + } + + *p = '\0'; + ret = ip_from_string(s, addr); + + ctdb_sock_addr_set_port(addr, port); + + return ret; +} + unsigned int ctdb_sock_addr_port(ctdb_sock_addr *addr) { switch (addr->sa.sa_family) { diff --git a/ctdb/protocol/protocol_util.h b/ctdb/protocol/protocol_util.h index b8c4f0aae42..88819366e5d 100644 --- a/ctdb/protocol/protocol_util.h +++ b/ctdb/protocol/protocol_util.h @@ -39,6 +39,8 @@ int ctdb_sock_addr_to_buf(char *buf, socklen_t buflen, ctdb_sock_addr *addr, bool with_port); const char *ctdb_sock_addr_to_string(TALLOC_CTX *mem_ctx, ctdb_sock_addr *addr, bool with_port); +int ctdb_sock_addr_from_string(const char *str, + ctdb_sock_addr *addr, bool with_port); unsigned int ctdb_sock_addr_port(ctdb_sock_addr *addr); void ctdb_sock_addr_set_port(ctdb_sock_addr *addr, unsigned int port); int ctdb_sock_addr_cmp_ip(const ctdb_sock_addr *addr1, diff --git a/ctdb/tests/src/protocol_util_test.c b/ctdb/tests/src/protocol_util_test.c index 1fcf1a0648d..8926de0197b 100644 --- a/ctdb/tests/src/protocol_util_test.c +++ b/ctdb/tests/src/protocol_util_test.c @@ -26,33 +26,42 @@ #include "protocol/protocol_types.c" #include "protocol/protocol_util.c" -#include "common/system_util.c" +/* + * Test parsing of IPs, conversion to string + */ -/* Test parsing of IPs, conversion to string */ static void test_sock_addr_to_string(const char *ip, bool with_port) { ctdb_sock_addr sa; const char *s; - bool status; + int ret; - if (with_port) { - status = parse_ip_port(ip, &sa); - } else { - status = parse_ip(ip, NULL, 0, &sa); - } - assert(status); + ret = ctdb_sock_addr_from_string(ip, &sa, with_port); + assert(ret == 0); s = ctdb_sock_addr_to_string(NULL, &sa, with_port); assert(strcmp(ip, s) == 0); talloc_free(discard_const(s)); } -static void test_sock_addr_cmp(const char *ip1, const char *ip2, int res) +static void test_sock_addr_from_string_bad(const char *ip, bool with_port) +{ + ctdb_sock_addr sa; + int ret; + + ret = ctdb_sock_addr_from_string(ip, &sa, with_port); + assert(ret != 0); +} + +static void test_sock_addr_cmp(const char *ip1, const char *ip2, + bool with_port, int res) { ctdb_sock_addr sa1, sa2; int ret; - assert(parse_ip(ip1, NULL, 0, &sa1)); - assert(parse_ip(ip2, NULL, 0, &sa2)); + ret = ctdb_sock_addr_from_string(ip1, &sa1, with_port); + assert(ret == 0); + ret = ctdb_sock_addr_from_string(ip2, &sa2, with_port); + assert(ret == 0); ret = ctdb_sock_addr_cmp(&sa1, &sa2); if (ret < 0) { ret = -1; @@ -77,20 +86,29 @@ int main(int argc, char *argv[]) test_sock_addr_to_string("192.168.2.1:123", true); test_sock_addr_to_string("fe80::6af7:28ff:fefa:d136:234", true); + test_sock_addr_from_string_bad("0.0.0", false); + test_sock_addr_from_string_bad("0.0.0:0", true); + test_sock_addr_from_string_bad("fe80::6af7:28ff:fefa:d136", true); + test_sock_addr_from_string_bad("junk", false); + test_sock_addr_from_string_bad("0.0.0.0:0 trailing junk", true); - test_sock_addr_cmp("127.0.0.1", "127.0.0.1" , 0); - test_sock_addr_cmp("127.0.0.1", "127.0.0.2" , -1); - test_sock_addr_cmp("127.0.0.2", "127.0.0.1" , 1); - test_sock_addr_cmp("127.0.1.2", "127.0.2.1" , -1); - test_sock_addr_cmp("127.0.2.1", "127.0.1.2" , 1); - test_sock_addr_cmp("fe80::6af7:28ff:fefa:d136", "127.0.1.2" , 1); + test_sock_addr_cmp("127.0.0.1", "127.0.0.1" , false, 0); + test_sock_addr_cmp("127.0.0.1", "127.0.0.2" , false, -1); + test_sock_addr_cmp("127.0.0.2", "127.0.0.1" , false, 1); + test_sock_addr_cmp("127.0.1.2", "127.0.2.1" , false, -1); + test_sock_addr_cmp("127.0.2.1", "127.0.1.2" , false, 1); + test_sock_addr_cmp("fe80::6af7:28ff:fefa:d136", "127.0.1.2" , false, 1); test_sock_addr_cmp("fe80::6af7:28ff:fefa:d136", - "fe80::6af7:28ff:fefa:d136" , 0); + "fe80::6af7:28ff:fefa:d136" , false, 0); test_sock_addr_cmp("fe80::6af7:28ff:fefa:d136", - "fe80::6af7:28ff:fefa:d137" , -1); + "fe80::6af7:28ff:fefa:d137" , false, -1); test_sock_addr_cmp("fe80::6af7:28ff:fefa:d136", - "fe80:0000:0000:0000:6af7:28ff:fefa:d136" , 0); - test_sock_addr_cmp("::ffff:192.0.2.128", "192.0.2.128", 0); + "fe80:0000:0000:0000:6af7:28ff:fefa:d136" , + false, 0); + test_sock_addr_cmp("::ffff:192.0.2.128", "192.0.2.128", false, 0); + test_sock_addr_cmp("127.0.0.1:123", "127.0.0.1:124" , true, -1); + test_sock_addr_cmp("fe80::6af7:28ff:fefa:d136:123", + "fe80::6af7:28ff:fefa:d136:122" , true, 1); return 0; }