util: introduce in_addr_port_ifindex_name_from_string_auto() and in_addr_port_ifindex_name_to_string()
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user