util: introduce in_addr_port_ifindex_name_from_string_auto() and in_addr_port_ifindex_name_to_string()

This commit is contained in:
Yu Watanabe
2020-07-21 03:55:34 +09:00
parent cbe194b39e
commit a723fb85da
5 changed files with 211 additions and 48 deletions
+56
View 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);
+1
View 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);
+108 -46
View 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;
m = strrchr(s, ':');
if (m) {
if (*s == '[') {
_cleanup_free_ char *ip_str = NULL;
if (!ret_port)
return -EINVAL;
if (*(m - 1) != ']')
return -EINVAL;
family = AF_INET6;
r = parse_ip_port(m + 1, &port);
if (r < 0)
return r;
ip_str = strndup(s + 1, m - s - 2);
if (!ip_str)
return -ENOMEM;
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;
}
if (ret_family)
*ret_family = family;
if (ret_address)
*ret_address = a;
if (ret_port)
*ret_port = port;
if (ret_ifindex)
*ret_ifindex = ifindex;
return r;
}
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;
assert(s);
m = strchr(s, '#');
if (m) {
name = strdup(m+1);
if (!name)
return -ENOMEM;
buf = strndup(s, m - s);
if (!buf)
return -ENOMEM;
s = buf;
}
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_server_name)
*ret_server_name = TAKE_PTR(name);
return r;
}
+13 -2
View 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);
}
+33
View 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();