summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2020-07-21 08:06:17 +0200
committerGitHub <noreply@github.com>2020-07-21 08:06:17 +0200
commit739b9a435416ad0c169cce859cb0a361ef08ceae (patch)
treeb15059d1d56ec0047ec812f8ddbf2d42556670d3 /src/shared
parent08de6f945d0eed5278a7fea7428e14b78fb83336 (diff)
parent711dd5db9e1935a20158727f4a27d12a64ba48e7 (diff)
downloadsystemd-739b9a435416ad0c169cce859cb0a361ef08ceae.tar.gz
Merge pull request #16353 from yuwata/network-dns-sni
resolve, network: more SNI and port number support
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/bus-get-properties.h2
-rw-r--r--src/shared/bus-message-util.c182
-rw-r--r--src/shared/bus-message-util.h18
-rw-r--r--src/shared/meson.build2
-rw-r--r--src/shared/socket-netlink.c195
-rw-r--r--src/shared/socket-netlink.h30
6 files changed, 392 insertions, 37 deletions
diff --git a/src/shared/bus-get-properties.h b/src/shared/bus-get-properties.h
index d853b79d8d..81af74309d 100644
--- a/src/shared/bus-get-properties.h
+++ b/src/shared/bus-get-properties.h
@@ -3,8 +3,6 @@
#include "sd-bus.h"
-#include "sd-bus.h"
-
#include "macro.h"
int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c
new file mode 100644
index 0000000000..85e1e98e19
--- /dev/null
+++ b/src/shared/bus-message-util.c
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-message-util.h"
+
+#include "resolve-util.h"
+
+int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret) {
+ int ifindex, r;
+
+ assert(message);
+ assert(ret);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(message, "i", &ifindex);
+ if (r < 0)
+ return r;
+
+ if (ifindex <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+
+ *ret = ifindex;
+
+ return 0;
+}
+
+int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret) {
+ int family, r;
+
+ assert(message);
+ assert(ret);
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ *ret = family;
+ return 0;
+}
+
+int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr) {
+ int family, r;
+ const void *d;
+ size_t sz;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "i", &family);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_array(message, 'y', &d, &sz);
+ if (r < 0)
+ return r;
+
+ if (!IN_SET(family, AF_INET, AF_INET6))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+ if (sz != FAMILY_ADDRESS_SIZE(family))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+
+ if (ret_family)
+ *ret_family = family;
+ if (ret_addr)
+ memcpy(ret_addr, d, sz);
+ return 0;
+}
+
+static int bus_message_read_dns_one(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ int *ret_family,
+ union in_addr_union *ret_address,
+ uint16_t *ret_port,
+ const char **ret_server_name) {
+ const char *server_name = NULL;
+ union in_addr_union a;
+ uint16_t port = 0;
+ int family, r;
+
+ assert(message);
+ assert(ret_family);
+ assert(ret_address);
+ assert(ret_port);
+ assert(ret_server_name);
+
+ r = sd_bus_message_enter_container(message, 'r', extended ? "iayqs" : "iay");
+ if (r <= 0)
+ return r;
+
+ r = bus_message_read_in_addr_auto(message, error, &family, &a);
+ if (r < 0)
+ return r;
+
+ if (!dns_server_address_valid(family, &a))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
+
+ if (extended) {
+ r = sd_bus_message_read(message, "q", &port);
+ if (r < 0)
+ return r;
+
+ if (IN_SET(port, 53, 853))
+ port = 0;
+
+ r = sd_bus_message_read(message, "s", &server_name);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ *ret_family = family;
+ *ret_address = a;
+ *ret_port = port;
+ *ret_server_name = server_name;
+
+ return 1;
+}
+
+int bus_message_read_dns_servers(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ struct in_addr_full ***ret_dns,
+ size_t *ret_n_dns) {
+
+ struct in_addr_full **dns = NULL;
+ size_t n = 0, allocated = 0;
+ int r;
+
+ assert(message);
+ assert(ret_dns);
+ assert(ret_n_dns);
+
+ r = sd_bus_message_enter_container(message, 'a', extended ? "(iayqs)" : "(iay)");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *server_name;
+ union in_addr_union a;
+ uint16_t port;
+ int family;
+
+ r = bus_message_read_dns_one(message, error, extended, &family, &a, &port, &server_name);
+ if (r < 0)
+ goto clear;
+ if (r == 0)
+ break;
+
+ if (!GREEDY_REALLOC(dns, allocated, n+1)) {
+ r = -ENOMEM;
+ goto clear;
+ }
+
+ r = in_addr_full_new(family, &a, port, 0, server_name, dns + n);
+ if (r < 0)
+ goto clear;
+
+ n++;
+ }
+
+ *ret_dns = TAKE_PTR(dns);
+ *ret_n_dns = n;
+ return 0;
+
+clear:
+ for (size_t i = 0; i < n; i++)
+ in_addr_full_free(dns[i]);
+ free(dns);
+
+ return r;
+}
diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h
new file mode 100644
index 0000000000..98ad035ee4
--- /dev/null
+++ b/src/shared/bus-message-util.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "in-addr-util.h"
+#include "socket-netlink.h"
+
+int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret);
+int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret);
+int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr);
+
+int bus_message_read_dns_servers(
+ sd_bus_message *message,
+ sd_bus_error *error,
+ bool extended,
+ struct in_addr_full ***ret_dns,
+ size_t *ret_n_dns);
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 1868e8aede..0da733c3fe 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -35,6 +35,8 @@ shared_sources = files('''
bus-log-control-api.h
bus-map-properties.c
bus-map-properties.h
+ bus-message-util.c
+ bus-message-util.h
bus-object.c
bus-object.h
bus-polkit.c
diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c
index 16b0e6a5c3..d72a70503a 100644
--- a/src/shared/socket-netlink.c
+++ b/src/shared/socket-netlink.c
@@ -327,68 +327,197 @@ 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;
+ if (ret_server_name)
+ *ret_server_name = TAKE_PTR(name);
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;
+struct in_addr_full *in_addr_full_free(struct in_addr_full *a) {
+ if (!a)
+ return NULL;
- assert(s);
+ free(a->server_name);
+ free(a->cached_server_string);
+ return mfree(a);
+}
- m = strchr(s, '#');
- if (m) {
- name = strdup(m+1);
+int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret) {
+ _cleanup_free_ char *name = NULL;
+ struct in_addr_full *x;
+
+ assert(ret);
+
+ if (!isempty(server_name)) {
+ name = strdup(server_name);
if (!name)
return -ENOMEM;
+ }
- buf = strndup(s, m - s);
- if (!buf)
- return -ENOMEM;
+ x = new(struct in_addr_full, 1);
+ if (!x)
+ return -ENOMEM;
- s = buf;
- }
+ *x = (struct in_addr_full) {
+ .family = family,
+ .address = *a,
+ .port = port,
+ .ifindex = ifindex,
+ .server_name = TAKE_PTR(name),
+ };
+
+ *ret = x;
+ return 0;
+}
+
+int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret) {
+ _cleanup_free_ char *server_name = NULL;
+ int family, ifindex, r;
+ union in_addr_union a;
+ uint16_t port;
- r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex);
+ assert(s);
+
+ r = in_addr_port_ifindex_name_from_string_auto(s, &family, &a, &port, &ifindex, &server_name);
if (r < 0)
return r;
- if (server_name)
- *server_name = TAKE_PTR(name);
+ return in_addr_full_new(family, &a, port, ifindex, server_name, ret);
+}
- return r;
+const char *in_addr_full_to_string(struct in_addr_full *a) {
+ assert(a);
+
+ if (!a->cached_server_string)
+ (void) in_addr_port_ifindex_name_to_string(
+ a->family,
+ &a->address,
+ a->port,
+ a->ifindex,
+ a->server_name,
+ &a->cached_server_string);
+
+ return a->cached_server_string;
}
diff --git a/src/shared/socket-netlink.h b/src/shared/socket-netlink.h
index 35c35db52d..9517f6dd6d 100644
--- a/src/shared/socket-netlink.h
+++ b/src/shared/socket-netlink.h
@@ -20,5 +20,31 @@ 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);
+}
+
+struct in_addr_full {
+ int family;
+ union in_addr_union address;
+ uint16_t port;
+ int ifindex;
+ char *server_name;
+ char *cached_server_string; /* Should not be handled directly, but through in_addr_full_to_string(). */
+};
+
+struct in_addr_full *in_addr_full_free(struct in_addr_full *a);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free);
+int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret);
+int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret);
+const char *in_addr_full_to_string(struct in_addr_full *a);