From: Yu Watanabe Date: Sun, 12 Jul 2020 21:49:41 +0000 (+0900) Subject: util: introduce in_addr_port_ifindex_name_from_string_auto() and in_addr_port_ifindex... X-Git-Tag: v246-rc2~32^2~22 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a723fb85dac86fb975e52aeed68dcd62d3f2b3b4;p=thirdparty%2Fsystemd.git util: introduce in_addr_port_ifindex_name_from_string_auto() and in_addr_port_ifindex_name_to_string() --- diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 9feee663439..828ea118166 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -14,6 +14,7 @@ #include "macro.h" #include "parse-util.h" #include "random-util.h" +#include "string-util.h" #include "strxcpyx.h" #include "util.h" @@ -445,6 +446,61 @@ fallback: return in_addr_to_string(family, u, ret); } +int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) { + _cleanup_free_ char *ip_str = NULL, *x = NULL; + int r; + + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(u); + assert(ret); + + /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly + * handle IPv6 link-local addresses. */ + + r = in_addr_to_string(family, u, &ip_str); + if (r < 0) + return r; + + if (family == AF_INET6) { + r = in_addr_is_link_local(family, u); + if (r < 0) + return r; + if (r == 0) + ifindex = 0; + } else + ifindex = 0; /* For IPv4 address, ifindex is always ignored. */ + + if (port == 0 && ifindex == 0 && isempty(server_name)) { + *ret = TAKE_PTR(ip_str); + return 0; + } + + const char *separator = isempty(server_name) ? "" : "#"; + server_name = strempty(server_name); + + if (port > 0) { + if (family == AF_INET6) { + if (ifindex > 0) + r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name); + else + r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name); + } else + r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name); + } else { + if (ifindex > 0) + r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name); + else { + x = strjoin(ip_str, separator, server_name); + r = x ? 0 : -ENOMEM; + } + } + if (r < 0) + return -ENOMEM; + + *ret = TAKE_PTR(x); + return 0; +} + int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { union in_addr_union buffer; assert(s); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 90d79a5ef50..dc3f575bc97 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -41,6 +41,7 @@ int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen int in_addr_to_string(int family, const union in_addr_union *u, char **ret); int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); +int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c index 16b0e6a5c3c..b95407f10ef 100644 --- a/src/shared/socket-netlink.c +++ b/src/shared/socket-netlink.c @@ -327,68 +327,130 @@ int make_socket_fd(int log_level, const char* address, int type, int flags) { return fd; } -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) { - _cleanup_free_ char *buf = NULL; - const char *suffix; - int r, ifindex = 0; +int in_addr_port_ifindex_name_from_string_auto( + const char *s, + int *ret_family, + union in_addr_union *ret_address, + uint16_t *ret_port, + int *ret_ifindex, + char **ret_server_name) { + + _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *name = NULL; + int family, ifindex = 0, r; + union in_addr_union a; + uint16_t port = 0; + const char *m; assert(s); - assert(family); - assert(ret_addr); - /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") - * if one is found. */ + /* This accepts the following: + * 192.168.0.1:53#example.com + * [2001:4860:4860::8888]:53%eth0#example.com */ + + /* if ret_port is NULL, then strings with port cannot be specified. + * Also, if ret_server_name is NULL, then server_name cannot be specified. */ + + m = strchr(s, '#'); + if (m) { + if (!ret_server_name) + return -EINVAL; + + if (isempty(m + 1)) + return -EINVAL; + + name = strdup(m + 1); + if (!name) + return -ENOMEM; + + s = buf1 = strndup(s, m - s); + if (!buf1) + return -ENOMEM; + } + + m = strchr(s, '%'); + if (m) { + if (isempty(m + 1)) + return -EINVAL; - suffix = strchr(s, '%'); - if (suffix) { if (ret_ifindex) { /* If we shall return the interface index, try to parse it */ - ifindex = resolve_interface(NULL, suffix + 1); + ifindex = resolve_interface(NULL, m + 1); if (ifindex < 0) return ifindex; } - s = buf = strndup(s, suffix - s); - if (!buf) + s = buf2 = strndup(s, m - s); + if (!buf2) return -ENOMEM; } - r = in_addr_from_string_auto(s, family, ret_addr); - if (r < 0) - return r; - - if (ret_ifindex) - *ret_ifindex = ifindex; + m = strrchr(s, ':'); + if (m) { + if (*s == '[') { + _cleanup_free_ char *ip_str = NULL; - return r; -} + if (!ret_port) + return -EINVAL; -int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) { - _cleanup_free_ char *buf = NULL, *name = NULL; - const char *m; - int r; + if (*(m - 1) != ']') + return -EINVAL; - assert(s); + family = AF_INET6; - m = strchr(s, '#'); - if (m) { - name = strdup(m+1); - if (!name) - return -ENOMEM; + r = parse_ip_port(m + 1, &port); + if (r < 0) + return r; - buf = strndup(s, m - s); - if (!buf) - return -ENOMEM; + ip_str = strndup(s + 1, m - s - 2); + if (!ip_str) + return -ENOMEM; - s = buf; + r = in_addr_from_string(family, ip_str, &a); + if (r < 0) + return r; + } else { + /* First try to parse the string as IPv6 address without port number */ + r = in_addr_from_string(AF_INET6, s, &a); + if (r < 0) { + /* Then the input should be IPv4 address with port number */ + _cleanup_free_ char *ip_str = NULL; + + if (!ret_port) + return -EINVAL; + + family = AF_INET; + + ip_str = strndup(s, m - s); + if (!ip_str) + return -ENOMEM; + + r = in_addr_from_string(family, ip_str, &a); + if (r < 0) + return r; + + r = parse_ip_port(m + 1, &port); + if (r < 0) + return r; + } else + family = AF_INET6; + } + } else { + family = AF_INET; + r = in_addr_from_string(family, s, &a); + if (r < 0) + return r; } - r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex); - if (r < 0) - return r; - - if (server_name) - *server_name = TAKE_PTR(name); + if (ret_family) + *ret_family = family; + if (ret_address) + *ret_address = a; + if (ret_port) + *ret_port = port; + if (ret_ifindex) + *ret_ifindex = ifindex; + if (ret_server_name) + *ret_server_name = TAKE_PTR(name); return r; } diff --git a/src/shared/socket-netlink.h b/src/shared/socket-netlink.h index 35c35db52d1..01f7745be6d 100644 --- a/src/shared/socket-netlink.h +++ b/src/shared/socket-netlink.h @@ -20,5 +20,16 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s); bool socket_address_is(const SocketAddress *a, const char *s, int type); bool socket_address_is_netlink(const SocketAddress *a, const char *s); -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); -int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name); +int in_addr_port_ifindex_name_from_string_auto( + const char *s, + int *ret_family, + union in_addr_union *ret_address, + uint16_t *ret_port, + int *ret_ifindex, + char **ret_server_name); +static inline int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) { + return in_addr_port_ifindex_name_from_string_auto(s, family, ret, NULL, ifindex, server_name); +} +static inline int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { + return in_addr_ifindex_name_from_string_auto(s, family, ret, ifindex, NULL); +} diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index 024e12a7993..b007dd62764 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -302,6 +302,38 @@ static void test_in_addr_ifindex_name_from_string_auto(void) { test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com"); } +static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str, int family, uint16_t port, int ifindex, const char *server_name) { + _cleanup_free_ char *name = NULL, *x = NULL; + union in_addr_union a; + uint16_t p; + int f, i; + + assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, &name) >= 0); + assert_se(family == f); + assert_se(port == p); + assert_se(ifindex == i); + assert_se(streq_ptr(server_name, name)); + assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0); + assert_se(streq(str, x)); +} + +static void test_in_addr_port_ifindex_name_from_string_auto(void) { + log_info("/* %s */", __func__); + + test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1", AF_INET, 0, 0, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1#test.com", AF_INET, 0, 0, "test.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53", AF_INET, 53, 0, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53#example.com", AF_INET, 53, 0, "example.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18", AF_INET6, 0, 0, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18#hoge.com", AF_INET6, 0, 0, "hoge.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19", AF_INET6, 0, 19, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53", AF_INET6, 53, 0, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19#hoge.com", AF_INET6, 0, 19, "hoge.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53#hoge.com", AF_INET6, 53, 0, "hoge.com"); + test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL); + test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com"); +} + static void test_sockaddr_equal(void) { union sockaddr_union a = { .in.sin_family = AF_INET, @@ -735,6 +767,7 @@ int main(int argc, char *argv[]) { test_in_addr_ifindex_to_string(); test_in_addr_ifindex_from_string_auto(); test_in_addr_ifindex_name_from_string_auto(); + test_in_addr_port_ifindex_name_from_string_auto(); test_sockaddr_equal();