]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
util: introduce in_addr_port_ifindex_name_from_string_auto() and in_addr_port_ifindex...
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 12 Jul 2020 21:49:41 +0000 (06:49 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 20 Jul 2020 18:55:34 +0000 (03:55 +0900)
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/shared/socket-netlink.c
src/shared/socket-netlink.h
src/test/test-socket-util.c

index 9feee663439acdeb325b63c2a65c8df81775bc86..828ea11816625e3cc298c03ead641682fb8cfd63 100644 (file)
@@ -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);
index 90d79a5ef5089431a4c4892b0af077d26e04e163..dc3f575bc977dfd0195a38f43722c5f0b22b56d5 100644 (file)
@@ -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);
 
index 16b0e6a5c3c609313819d1e3e20465094dddbc57..b95407f10ef4c3964344ed8eed54ebefbffee287 100644 (file)
@@ -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;
 }
index 35c35db52d1999ff7e8aa5f470a52e28d0f59c5c..01f7745be6d83c224ae9ad5df9b18c27e58d528b 100644 (file)
@@ -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);
+}
index 024e12a7993a4b449b7aa81cebd2bcf3d4ec3289..b007dd62764608bbff233dc1aff23f482037498c 100644 (file)
@@ -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();