Merge pull request #16947 from keszybz/socket-parsing-rework

Socket parsing rework
This commit is contained in:
Lennart Poettering 2020-09-10 16:47:37 +02:00 committed by GitHub
commit 6ae05c9b14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 367 additions and 476 deletions

View File

@ -200,22 +200,24 @@
</para> </para>
<para>If the address string is a string in the format <para>If the address string is a string in the format
v.w.x.y:z, it is read as IPv4 specifier for listening on an <literal><replaceable>v.w.x.y</replaceable>:<replaceable>z</replaceable></literal>, it is interpeted
address v.w.x.y on a port z.</para> as IPv4 address <replaceable>v.w.x.y</replaceable> and port <replaceable>z</replaceable>.</para>
<para>If the address string is a string in the format [x]:y,
it is read as IPv6 address x on a port y. Note that this might
make the service available via IPv4, too, depending on the
<varname>BindIPv6Only=</varname> setting (see below).
</para>
<para>If the address string is a string in the format <para>If the address string is a string in the format
<literal>vsock:x:y</literal>, it is read as CID <literal>x</literal> on <literal>[<replaceable>x</replaceable>]:<replaceable>y</replaceable></literal>, it is interpreted as
a port <literal>y</literal> address in the IPv6 address <replaceable>x</replaceable> and port <replaceable>y</replaceable>. An optional
<constant>AF_VSOCK</constant> family. The CID is a unique 32-bit interface scope (interface name or number) may be specifed after a <literal>%</literal> symbol:
integer identifier in <constant>AF_VSOCK</constant> analogous to an IP <literal>[<replaceable>x</replaceable>]:<replaceable>y</replaceable>%<replaceable>dev</replaceable></literal>.
address. Specifying the CID is optional, and may be set to the empty Interface scopes are only useful with link-local addresses, because the kernel ignores them in other
string.</para> cases. Note that if an address is specified as IPv6, it might still make the service available via
IPv4 too, depending on the <varname>BindIPv6Only=</varname> setting (see below).</para>
<para>If the address string is a string in the format
<literal>vsock:<replaceable>x</replaceable>:<replaceable>y</replaceable></literal>, it is read as CID
<replaceable>x</replaceable> on a port <replaceable>y</replaceable> address in the
<constant>AF_VSOCK</constant> family. The CID is a unique 32-bit integer identifier in
<constant>AF_VSOCK</constant> analogous to an IP address. Specifying the CID is optional, and may be
set to the empty string.</para>
<para>Note that <constant>SOCK_SEQPACKET</constant> (i.e. <para>Note that <constant>SOCK_SEQPACKET</constant> (i.e.
<varname>ListenSequentialPacket=</varname>) is only available <varname>ListenSequentialPacket=</varname>) is only available

View File

@ -68,7 +68,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
if (a->sockaddr.in.sin_port == 0) if (a->sockaddr.in.sin_port == 0)
return -EINVAL; return -EINVAL;
if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM))
return -EINVAL; return -EINVAL;
return 0; return 0;
@ -80,7 +80,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
if (a->sockaddr.in6.sin6_port == 0) if (a->sockaddr.in6.sin6_port == 0)
return -EINVAL; return -EINVAL;
if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM))
return -EINVAL; return -EINVAL;
return 0; return 0;
@ -114,7 +114,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
} }
} }
if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET)) if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET))
return -EINVAL; return -EINVAL;
return 0; return 0;
@ -124,7 +124,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
if (a->size != sizeof(struct sockaddr_nl)) if (a->size != sizeof(struct sockaddr_nl))
return -EINVAL; return -EINVAL;
if (!IN_SET(a->type, SOCK_RAW, SOCK_DGRAM)) if (!IN_SET(a->type, 0, SOCK_RAW, SOCK_DGRAM))
return -EINVAL; return -EINVAL;
return 0; return 0;
@ -133,7 +133,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
if (a->size != sizeof(struct sockaddr_vm)) if (a->size != sizeof(struct sockaddr_vm))
return -EINVAL; return -EINVAL;
if (!IN_SET(a->type, SOCK_STREAM, SOCK_DGRAM)) if (!IN_SET(a->type, 0, SOCK_STREAM, SOCK_DGRAM))
return -EINVAL; return -EINVAL;
return 0; return 0;
@ -399,19 +399,23 @@ int sockaddr_pretty(
if (r < 0) if (r < 0)
return -ENOMEM; return -ENOMEM;
} else { } else {
char a[INET6_ADDRSTRLEN]; char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1];
inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
if (sa->in6.sin6_scope_id != 0)
format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX);
if (include_port) { if (include_port) {
r = asprintf(&p, r = asprintf(&p,
"[%s]:%u", "[%s]:%u%s%s",
a, a,
be16toh(sa->in6.sin6_port)); be16toh(sa->in6.sin6_port),
sa->in6.sin6_scope_id != 0 ? "%" : "",
sa->in6.sin6_scope_id != 0 ? ifname : "");
if (r < 0) if (r < 0)
return -ENOMEM; return -ENOMEM;
} else { } else {
p = strdup(a); p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a);
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
} }
@ -686,17 +690,19 @@ static const char* const ip_tos_table[] = {
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
bool ifname_valid_full(const char *p, bool alternative) { bool ifname_valid_full(const char *p, IfnameValidFlags flags) {
bool numeric = true; bool numeric = true;
/* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources
* but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We
* also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */
assert(!(flags & ~_IFNAME_VALID_ALL));
if (isempty(p)) if (isempty(p))
return false; return false;
if (alternative) { if (flags & IFNAME_VALID_ALTERNATIVE) {
if (strlen(p) >= ALTIFNAMSIZ) if (strlen(p) >= ALTIFNAMSIZ)
return false; return false;
} else { } else {
@ -707,22 +713,27 @@ bool ifname_valid_full(const char *p, bool alternative) {
if (dot_or_dot_dot(p)) if (dot_or_dot_dot(p))
return false; return false;
while (*p) { for (const char *t = p; *t; t++) {
if ((unsigned char) *p >= 127U) if ((unsigned char) *t >= 127U)
return false; return false;
if ((unsigned char) *p <= 32U) if ((unsigned char) *t <= 32U)
return false; return false;
if (IN_SET(*p, ':', '/')) if (IN_SET(*t, ':', '/'))
return false; return false;
numeric = numeric && (*p >= '0' && *p <= '9'); numeric = numeric && (*t >= '0' && *t <= '9');
p++;
} }
if (numeric) if (numeric) {
return false; if (!(flags & IFNAME_VALID_NUMERIC))
return false;
/* Verify that the number is well-formatted and in range. */
if (parse_ifindex(p) < 0)
return false;
}
return true; return true;
} }
@ -1107,12 +1118,10 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) {
* reference paths in the abstract namespace that include NUL bytes in the name. */ * reference paths in the abstract namespace that include NUL bytes in the name. */
l = strlen(path); l = strlen(path);
if (l == 0) if (l < 2)
return -EINVAL; return -EINVAL;
if (!IN_SET(path[0], '/', '@')) if (!IN_SET(path[0], '/', '@'))
return -EINVAL; return -EINVAL;
if (path[1] == 0)
return -EINVAL;
/* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than
* the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket

View File

@ -130,9 +130,14 @@ static inline int fd_inc_rcvbuf(int fd, size_t n) {
int ip_tos_to_string_alloc(int i, char **s); int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s); int ip_tos_from_string(const char *s);
bool ifname_valid_full(const char *p, bool alternative); typedef enum {
IFNAME_VALID_ALTERNATIVE = 1 << 0,
IFNAME_VALID_NUMERIC = 1 << 1,
_IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC,
} IfnameValidFlags;
bool ifname_valid_full(const char *p, IfnameValidFlags flags);
static inline bool ifname_valid(const char *p) { static inline bool ifname_valid(const char *p) {
return ifname_valid_full(p, false); return ifname_valid_full(p, 0);
} }
bool address_label_valid(const char *p); bool address_label_valid(const char *p);

View File

@ -39,7 +39,7 @@ Match.Type, config_parse_match_strv,
Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match_wlan_iftype) Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match_wlan_iftype)
Match.SSID, config_parse_match_strv, 0, offsetof(Network, match_ssid) Match.SSID, config_parse_match_strv, 0, offsetof(Network, match_ssid)
Match.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match_bssid) Match.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match_bssid)
Match.Name, config_parse_match_ifnames, 1, offsetof(Network, match_name) Match.Name, config_parse_match_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(Network, match_name)
Match.Property, config_parse_match_property, 0, offsetof(Network, match_property) Match.Property, config_parse_match_property, 0, offsetof(Network, match_property)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions) Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions)

View File

@ -35,9 +35,6 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
if (r < 0) if (r < 0)
return r; return r;
if (IN_SET(port, 53, 853))
port = 0;
/* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */ /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
if (!dns_server_address_valid(family, &address)) if (!dns_server_address_valid(family, &address))
return 0; return 0;
@ -50,12 +47,8 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
/* Filter out duplicates */ /* Filter out duplicates */
s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name); s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name);
if (s) { if (s) {
/* /* Drop the marker. This is used to find the servers that ceased to exist, see
* Drop the marker. This is used to find the servers * manager_mark_dns_servers() and manager_flush_marked_dns_servers(). */
* that ceased to exist, see
* manager_mark_dns_servers() and
* manager_flush_marked_dns_servers().
*/
dns_server_move_back_and_unmark(s); dns_server_move_back_and_unmark(s);
return 0; return 0;
} }
@ -447,7 +440,7 @@ int config_parse_dns_stub_listener_extra(
} }
} }
r = in_addr_port_from_string_auto(p, &stub->family, &stub->address, &stub->port); r = in_addr_port_ifindex_name_from_string_auto(p, &stub->family, &stub->address, &stub->port, NULL, NULL);
if (r < 0) { if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse address in %s=%s, ignoring assignment: %m", "Failed to parse address in %s=%s, ignoring assignment: %m",

View File

@ -15,7 +15,7 @@
* IP and UDP header sizes */ * IP and UDP header sizes */
#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U) #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
static int manager_dns_stub_udp_fd_extra(Manager *m, DnsStubListenerExtra *l); static int manager_dns_stub_fd_extra(Manager *m, DnsStubListenerExtra *l, int type);
static void dns_stub_listener_extra_hash_func(const DnsStubListenerExtra *a, struct siphash *state) { static void dns_stub_listener_extra_hash_func(const DnsStubListenerExtra *a, struct siphash *state) {
assert(a); assert(a);
@ -217,7 +217,7 @@ static int dns_stub_send(
* is because otherwise the kernel will choose it automatically based on the routing table and will * is because otherwise the kernel will choose it automatically based on the routing table and will
* thus pick 127.0.0.1 rather than 127.0.0.53. */ * thus pick 127.0.0.1 rather than 127.0.0.53. */
r = manager_send(m, r = manager_send(m,
manager_dns_stub_udp_fd_extra(m, l), manager_dns_stub_fd_extra(m, l, SOCK_DGRAM),
l ? p->ifindex : LOOPBACK_IFINDEX, /* force loopback iface if this is the main listener stub */ l ? p->ifindex : LOOPBACK_IFINDEX, /* force loopback iface if this is the main listener stub */
p->family, &p->sender, p->sender_port, &p->destination, p->family, &p->sender, p->sender_port, &p->destination,
reply); reply);
@ -477,151 +477,6 @@ static int on_dns_stub_packet_extra(sd_event_source *s, int fd, uint32_t revents
return on_dns_stub_packet_internal(s, fd, revents, l->manager, l); return on_dns_stub_packet_internal(s, fd, revents, l->manager, l);
} }
static int set_dns_stub_common_socket_options(int fd, int family) {
int r;
assert(fd >= 0);
assert(IN_SET(family, AF_INET, AF_INET6));
r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return r;
if (family == AF_INET) {
r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
if (r < 0)
return r;
r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
if (r < 0)
return r;
} else {
r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
if (r < 0)
return r;
r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
if (r < 0)
return r;
}
return 0;
}
static int manager_dns_stub_udp_fd(Manager *m) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(53),
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
};
_cleanup_close_ int fd = -1;
int r;
if (m->dns_stub_udp_event_source)
return sd_event_source_get_io_fd(m->dns_stub_udp_event_source);
fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return -errno;
r = set_dns_stub_common_socket_options(fd, AF_INET);
if (r < 0)
return r;
/* Make sure no traffic from outside the local host can leak to onto this socket */
r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX);
if (r < 0)
return r;
if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
return -errno;
r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
if (r < 0)
return r;
r = sd_event_source_set_io_fd_own(m->dns_stub_udp_event_source, true);
if (r < 0)
return r;
(void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
return TAKE_FD(fd);
}
static int manager_dns_stub_udp_fd_extra(Manager *m, DnsStubListenerExtra *l) {
_cleanup_free_ char *pretty = NULL;
_cleanup_close_ int fd = -1;
union sockaddr_union sa;
int r;
assert(m);
if (!l)
return manager_dns_stub_udp_fd(m);
if (l->udp_event_source)
return 0;
if (l->family == AF_INET)
sa = (union sockaddr_union) {
.in.sin_family = l->family,
.in.sin_port = htobe16(l->port != 0 ? l->port : 53U),
.in.sin_addr = l->address.in,
};
else
sa = (union sockaddr_union) {
.in6.sin6_family = l->family,
.in6.sin6_port = htobe16(l->port != 0 ? l->port : 53U),
.in6.sin6_addr = l->address.in6,
};
fd = socket(l->family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0) {
r = -errno;
goto fail;
}
if (l->family == AF_INET) {
r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
if (r < 0)
goto fail;
}
r = set_dns_stub_common_socket_options(fd, l->family);
if (r < 0)
goto fail;
if (bind(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &l->udp_event_source, fd, EPOLLIN, on_dns_stub_packet_extra, l);
if (r < 0)
goto fail;
r = sd_event_source_set_io_fd_own(l->udp_event_source, true);
if (r < 0)
goto fail;
(void) sd_event_source_set_description(l->udp_event_source, "dns-stub-udp-extra");
if (DEBUG_LOGGING) {
(void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
log_debug("Listening on UDP socket %s.", strnull(pretty));
}
return TAKE_FD(fd);
fail:
assert(r < 0);
(void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
if (r == -EADDRINUSE)
return log_warning_errno(r, "Another process is already listening on UDP socket %s: %m", strnull(pretty));
return log_warning_errno(r, "Failed to listen on UDP socket %s: %m", strnull(pretty));
}
static int on_dns_stub_stream_packet(DnsStream *s) { static int on_dns_stub_stream_packet(DnsStream *s) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
@ -678,7 +533,38 @@ static int on_dns_stub_stream_extra(sd_event_source *s, int fd, uint32_t revents
return on_dns_stub_stream_internal(s, fd, revents, l->manager, l); return on_dns_stub_stream_internal(s, fd, revents, l->manager, l);
} }
static int manager_dns_stub_tcp_fd(Manager *m) { static int set_dns_stub_common_socket_options(int fd, int family) {
int r;
assert(fd >= 0);
assert(IN_SET(family, AF_INET, AF_INET6));
r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return r;
if (family == AF_INET) {
r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
if (r < 0)
return r;
r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
if (r < 0)
return r;
} else {
r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
if (r < 0)
return r;
r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
if (r < 0)
return r;
}
return 0;
}
static int manager_dns_stub_fd(Manager *m, int type) {
union sockaddr_union sa = { union sockaddr_union sa = {
.in.sin_family = AF_INET, .in.sin_family = AF_INET,
.in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB), .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
@ -687,10 +573,13 @@ static int manager_dns_stub_tcp_fd(Manager *m) {
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
int r; int r;
if (m->dns_stub_tcp_event_source) assert(IN_SET(type, SOCK_DGRAM, SOCK_STREAM));
return sd_event_source_get_io_fd(m->dns_stub_tcp_event_source);
fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); sd_event_source **event_source = type == SOCK_DGRAM ? &m->dns_stub_udp_event_source : &m->dns_stub_tcp_event_source;
if (*event_source)
return sd_event_source_get_io_fd(*event_source);
fd = socket(AF_INET, type | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (fd < 0) if (fd < 0)
return -errno; return -errno;
@ -698,42 +587,53 @@ static int manager_dns_stub_tcp_fd(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, 1);
if (r < 0)
return r;
/* Make sure no traffic from outside the local host can leak to onto this socket */ /* Make sure no traffic from outside the local host can leak to onto this socket */
r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX); r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX);
if (r < 0) if (r < 0)
return r; return r;
r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, 1);
if (r < 0)
return r;
if (bind(fd, &sa.sa, sizeof(sa.in)) < 0) if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
return -errno; return -errno;
if (listen(fd, SOMAXCONN) < 0) if (type == SOCK_STREAM &&
listen(fd, SOMAXCONN) < 0)
return -errno; return -errno;
r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m); r = sd_event_add_io(m->event, event_source, fd, EPOLLIN,
type == SOCK_DGRAM ? on_dns_stub_packet : on_dns_stub_stream,
m);
if (r < 0) if (r < 0)
return r; return r;
r = sd_event_source_set_io_fd_own(m->dns_stub_tcp_event_source, true); r = sd_event_source_set_io_fd_own(*event_source, true);
if (r < 0) if (r < 0)
return r; return r;
(void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp"); (void) sd_event_source_set_description(*event_source,
type == SOCK_DGRAM ? "dns-stub-udp" : "dns-stub-tcp");
return TAKE_FD(fd); return TAKE_FD(fd);
} }
static int manager_dns_stub_tcp_fd_extra(Manager *m, DnsStubListenerExtra *l) { static int manager_dns_stub_fd_extra(Manager *m, DnsStubListenerExtra *l, int type) {
_cleanup_free_ char *pretty = NULL; _cleanup_free_ char *pretty = NULL;
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
union sockaddr_union sa; union sockaddr_union sa;
int r; int r;
if (l->tcp_event_source) assert(m);
return sd_event_source_get_io_fd(l->tcp_event_source);; assert(IN_SET(type, SOCK_DGRAM, SOCK_STREAM));
if (!l)
return manager_dns_stub_fd(m, type);
sd_event_source **event_source = type == SOCK_DGRAM ? &l->udp_event_source : &l->tcp_event_source;
if (*event_source)
return sd_event_source_get_io_fd(*event_source);
if (l->family == AF_INET) if (l->family == AF_INET)
sa = (union sockaddr_union) { sa = (union sockaddr_union) {
@ -748,7 +648,7 @@ static int manager_dns_stub_tcp_fd_extra(Manager *m, DnsStubListenerExtra *l) {
.in6.sin6_addr = l->address.in6, .in6.sin6_addr = l->address.in6,
}; };
fd = socket(l->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); fd = socket(l->family, type | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (fd < 0) { if (fd < 0) {
r = -errno; r = -errno;
goto fail; goto fail;
@ -758,8 +658,8 @@ static int manager_dns_stub_tcp_fd_extra(Manager *m, DnsStubListenerExtra *l) {
if (r < 0) if (r < 0)
goto fail; goto fail;
/* Do not set IP_TTL for extra DNS stub listners, as the address may not be local and in that /* Do not set IP_TTL for extra DNS stub listners, as the address may not be local and in that case
* case people may want ttl > 1. */ * people may want ttl > 1. */
if (l->family == AF_INET) if (l->family == AF_INET)
r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true); r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
@ -773,24 +673,30 @@ static int manager_dns_stub_tcp_fd_extra(Manager *m, DnsStubListenerExtra *l) {
goto fail; goto fail;
} }
if (listen(fd, SOMAXCONN) < 0) { if (type == SOCK_STREAM &&
listen(fd, SOMAXCONN) < 0) {
r = -errno; r = -errno;
goto fail; goto fail;
} }
r = sd_event_add_io(m->event, &l->tcp_event_source, fd, EPOLLIN, on_dns_stub_stream_extra, l); r = sd_event_add_io(m->event, event_source, fd, EPOLLIN,
type == SOCK_DGRAM ? on_dns_stub_packet_extra : on_dns_stub_stream_extra,
l);
if (r < 0) if (r < 0)
goto fail; goto fail;
r = sd_event_source_set_io_fd_own(l->tcp_event_source, true); r = sd_event_source_set_io_fd_own(*event_source, true);
if (r < 0) if (r < 0)
goto fail; goto fail;
(void) sd_event_source_set_description(l->tcp_event_source, "dns-stub-tcp-extra"); (void) sd_event_source_set_description(*event_source,
type == SOCK_DGRAM ? "dns-stub-udp-extra" : "dns-stub-tcp-extra");
if (DEBUG_LOGGING) { if (DEBUG_LOGGING) {
(void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty); (void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
log_debug("Listening on TCP socket %s.", strnull(pretty)); log_debug("Listening on %s socket %s.",
type == SOCK_DGRAM ? "UDP" : "TCP",
strnull(pretty));
} }
return TAKE_FD(fd); return TAKE_FD(fd);
@ -798,9 +704,11 @@ static int manager_dns_stub_tcp_fd_extra(Manager *m, DnsStubListenerExtra *l) {
fail: fail:
assert(r < 0); assert(r < 0);
(void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty); (void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
if (r == -EADDRINUSE) return log_warning_errno(r,
return log_warning_errno(r, "Another process is already listening on TCP socket %s: %m", strnull(pretty)); r == -EADDRINUSE ? "Another process is already listening on %s socket %s: %m" :
return log_warning_errno(r, "Failed to listen on TCP socket %s: %m", strnull(pretty)); "Failed to listen on %s socket %s: %m",
type == SOCK_DGRAM ? "UDP" : "TCP",
strnull(pretty));
} }
int manager_dns_stub_start(Manager *m) { int manager_dns_stub_start(Manager *m) {
@ -818,23 +726,21 @@ int manager_dns_stub_start(Manager *m) {
"UDP/TCP"); "UDP/TCP");
if (FLAGS_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_UDP)) if (FLAGS_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_UDP))
r = manager_dns_stub_udp_fd(m); r = manager_dns_stub_fd(m, SOCK_DGRAM);
if (r >= 0 && if (r >= 0 &&
FLAGS_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_TCP)) { FLAGS_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_TCP)) {
t = "TCP"; t = "TCP";
r = manager_dns_stub_tcp_fd(m); r = manager_dns_stub_fd(m, SOCK_STREAM);
} }
if (IN_SET(r, -EADDRINUSE, -EPERM)) { if (IN_SET(r, -EADDRINUSE, -EPERM)) {
if (r == -EADDRINUSE) log_warning_errno(r,
log_warning_errno(r, r == -EADDRINUSE ? "Another process is already listening on %s socket 127.0.0.53:53.\n"
"Another process is already listening on %s socket 127.0.0.53:53.\n" "Turning off local DNS stub support." :
"Turning off local DNS stub support.", t); "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
else "Turning off local DNS stub support.",
log_warning_errno(r, t);
"Failed to listen on %s socket 127.0.0.53:53: %m.\n"
"Turning off local DNS stub support.", t);
manager_dns_stub_stop(m); manager_dns_stub_stop(m);
} else if (r < 0) } else if (r < 0)
return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t); return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
@ -846,9 +752,9 @@ int manager_dns_stub_start(Manager *m) {
ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners) { ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners) {
if (FLAGS_SET(l->mode, DNS_STUB_LISTENER_UDP)) if (FLAGS_SET(l->mode, DNS_STUB_LISTENER_UDP))
(void) manager_dns_stub_udp_fd_extra(m, l); (void) manager_dns_stub_fd_extra(m, l, SOCK_DGRAM);
if (FLAGS_SET(l->mode, DNS_STUB_LISTENER_TCP)) if (FLAGS_SET(l->mode, DNS_STUB_LISTENER_TCP))
(void) manager_dns_stub_tcp_fd_extra(m, l); (void) manager_dns_stub_fd_extra(m, l, SOCK_STREAM);
} }
} }

View File

@ -62,75 +62,23 @@ int socket_address_parse(SocketAddress *a, const char *s) {
assert(a); assert(a);
assert(s); assert(s);
*a = (SocketAddress) { if (IN_SET(*s, '/', '@')) {
.type = SOCK_STREAM, /* AF_UNIX socket */
}; struct sockaddr_un un;
if (*s == '[') { r = sockaddr_un_set_path(&un, s);
uint16_t port;
/* IPv6 in [x:.....:z]:p notation */
e = strchr(s+1, ']');
if (!e)
return -EINVAL;
n = strndup(s+1, e-s-1);
if (!n)
return -ENOMEM;
errno = 0;
if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
return errno_or_else(EINVAL);
e++;
if (*e != ':')
return -EINVAL;
e++;
r = parse_ip_port(e, &port);
if (r < 0) if (r < 0)
return r; return r;
a->sockaddr.in6.sin6_family = AF_INET6; *a = (SocketAddress) {
a->sockaddr.in6.sin6_port = htobe16(port); .sockaddr.un = un,
a->size = sizeof(struct sockaddr_in6); .size = r,
};
} else if (*s == '/') {
/* AF_UNIX socket */
size_t l;
l = strlen(s);
if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when
* parsing (the kernel itself is less strict here in what it
* accepts) */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
memcpy(a->sockaddr.un.sun_path, s, l);
a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
} else if (*s == '@') {
/* Abstract AF_UNIX socket */
size_t l;
l = strlen(s+1);
if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminated sockets here
* when parsing, even though abstract namespace sockets
* explicitly allow embedded NUL bytes and don't consider
* them special. But it's simply annoying to debug such
* sockets. */
return -EINVAL;
a->sockaddr.un.sun_family = AF_UNIX;
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
} else if (startswith(s, "vsock:")) { } else if (startswith(s, "vsock:")) {
/* AF_VSOCK socket in vsock:cid:port notation */ /* AF_VSOCK socket in vsock:cid:port notation */
const char *cid_start = s + STRLEN("vsock:"); const char *cid_start = s + STRLEN("vsock:");
unsigned port; unsigned port, cid;
e = strchr(cid_start, ':'); e = strchr(cid_start, ':');
if (!e) if (!e)
@ -144,72 +92,82 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (!n) if (!n)
return -ENOMEM; return -ENOMEM;
if (!isempty(n)) { if (isempty(n))
r = safe_atou(n, &a->sockaddr.vm.svm_cid); cid = VMADDR_CID_ANY;
else {
r = safe_atou(n, &cid);
if (r < 0) if (r < 0)
return r; return r;
} else }
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
a->sockaddr.vm.svm_family = AF_VSOCK; *a = (SocketAddress) {
a->sockaddr.vm.svm_port = port; .sockaddr.vm = {
a->size = sizeof(struct sockaddr_vm); .svm_cid = cid,
.svm_family = AF_VSOCK,
.svm_port = port,
},
.size = sizeof(struct sockaddr_vm),
};
} else { } else {
uint16_t port; uint16_t port;
e = strchr(s, ':'); r = parse_ip_port(s, &port);
if (e) { if (r == -ERANGE)
r = parse_ip_port(e + 1, &port); return r; /* Valid port syntax, but the numerical value is wrong for a port. */
if (r < 0) if (r >= 0) {
return r;
n = strndup(s, e-s);
if (!n)
return -ENOMEM;
/* IPv4 in w.x.y.z:p notation? */
r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
if (r < 0)
return -errno;
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
a->sockaddr.in.sin_family = AF_INET;
a->sockaddr.in.sin_port = htobe16(port);
a->size = sizeof(struct sockaddr_in);
} else {
int idx;
/* Uh, our last resort, an interface name */
idx = resolve_ifname(NULL, n);
if (idx < 0)
return idx;
a->sockaddr.in6.sin6_family = AF_INET6;
a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
}
} else {
/* Just a port */ /* Just a port */
r = parse_ip_port(s, &port); if (socket_ipv6_is_supported())
*a = (SocketAddress) {
.sockaddr.in6 = {
.sin6_family = AF_INET6,
.sin6_port = htobe16(port),
.sin6_addr = in6addr_any,
},
.size = sizeof(struct sockaddr_in6),
};
else
*a = (SocketAddress) {
.sockaddr.in = {
.sin_family = AF_INET,
.sin_port = htobe16(port),
.sin_addr.s_addr = INADDR_ANY,
},
.size = sizeof(struct sockaddr_in),
};
} else {
union in_addr_union address;
int family, ifindex;
r = in_addr_port_ifindex_name_from_string_auto(s, &family, &address, &port, &ifindex, NULL);
if (r < 0) if (r < 0)
return r; return r;
if (socket_ipv6_is_supported()) { if (port == 0) /* No port, no go. */
a->sockaddr.in6.sin6_family = AF_INET6; return -EINVAL;
a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_addr = in6addr_any; if (family == AF_INET)
a->size = sizeof(struct sockaddr_in6); *a = (SocketAddress) {
} else { .sockaddr.in = {
a->sockaddr.in.sin_family = AF_INET; .sin_family = AF_INET,
a->sockaddr.in.sin_port = htobe16(port); .sin_addr = address.in,
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; .sin_port = htobe16(port),
a->size = sizeof(struct sockaddr_in); },
} .size = sizeof(struct sockaddr_in),
};
else if (family == AF_INET6)
*a = (SocketAddress) {
.sockaddr.in6 = {
.sin6_family = AF_INET6,
.sin6_addr = address.in6,
.sin6_port = htobe16(port),
.sin6_scope_id = ifindex,
},
.size = sizeof(struct sockaddr_in6),
};
else
assert_not_reached("Family quarrel");
} }
} }
@ -243,10 +201,6 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) {
assert(a); assert(a);
assert(s); assert(s);
*a = (SocketAddress) {
.type = SOCK_RAW,
};
r = extract_first_word(&s, &word, NULL, 0); r = extract_first_word(&s, &word, NULL, 0);
if (r < 0) if (r < 0)
return r; return r;
@ -263,12 +217,13 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) {
return r; return r;
} }
a->sockaddr.nl.nl_family = AF_NETLINK; *a = (SocketAddress) {
a->sockaddr.nl.nl_groups = group; .type = SOCK_RAW,
.sockaddr.nl.nl_family = AF_NETLINK,
a->type = SOCK_RAW; .sockaddr.nl.nl_groups = group,
a->size = sizeof(struct sockaddr_nl); .protocol = family,
a->protocol = family; .size = sizeof(struct sockaddr_nl),
};
return 0; return 0;
} }
@ -345,10 +300,14 @@ int in_addr_port_ifindex_name_from_string_auto(
/* This accepts the following: /* This accepts the following:
* 192.168.0.1:53#example.com * 192.168.0.1:53#example.com
* [2001:4860:4860::8888]:53%eth0#example.com */ * [2001:4860:4860::8888]:53%eth0#example.com
*
/* if ret_port is NULL, then strings with port cannot be specified. * If ret_port is NULL, then the port cannot be specified.
* Also, if ret_server_name is NULL, then server_name cannot be specified. */ * If ret_ifindex is NULL, then the interface index cannot be specified.
* If ret_server_name is NULL, then server_name cannot be specified.
*
* ret_family is always AF_INET or AF_INET6.
*/
m = strchr(s, '#'); m = strchr(s, '#');
if (m) { if (m) {
@ -369,15 +328,19 @@ int in_addr_port_ifindex_name_from_string_auto(
m = strchr(s, '%'); m = strchr(s, '%');
if (m) { if (m) {
if (!ret_ifindex)
return -EINVAL;
if (isempty(m + 1)) if (isempty(m + 1))
return -EINVAL; return -EINVAL;
if (ret_ifindex) { if (!ifname_valid_full(m + 1, IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC))
/* If we shall return the interface index, try to parse it */ return -EINVAL; /* We want to return -EINVAL for syntactically invalid names,
ifindex = resolve_interface(NULL, m + 1); * and -ENODEV for valid but nonexistent interfaces. */
if (ifindex < 0)
return ifindex; ifindex = resolve_interface(NULL, m + 1);
} if (ifindex < 0)
return ifindex;
s = buf2 = strndup(s, m - s); s = buf2 = strndup(s, m - s);
if (!buf2) if (!buf2)
@ -455,36 +418,6 @@ int in_addr_port_ifindex_name_from_string_auto(
return r; return r;
} }
int in_addr_port_from_string_auto(
const char *s,
int *ret_family,
union in_addr_union *ret_address,
uint16_t *ret_port) {
union in_addr_union addr;
int family, ifindex, r;
uint16_t port;
assert(s);
r = in_addr_port_ifindex_name_from_string_auto(s, &family, &addr, &port, &ifindex, NULL);
if (r < 0)
return r;
/* This does not accept interface specified. */
if (ifindex != 0)
return -EINVAL;
if (ret_family)
*ret_family = family;
if (ret_address)
*ret_address = addr;
if (ret_port)
*ret_port = port;
return r;
}
struct in_addr_full *in_addr_full_free(struct in_addr_full *a) { struct in_addr_full *in_addr_full_free(struct in_addr_full *a) {
if (!a) if (!a)
return NULL; return NULL;

View File

@ -33,7 +33,6 @@ static inline int in_addr_ifindex_name_from_string_auto(const char *s, int *fami
static inline int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { 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); return in_addr_ifindex_name_from_string_auto(s, family, ret, ifindex, NULL);
} }
int in_addr_port_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret_address, uint16_t *ret_port);
struct in_addr_full { struct in_addr_full {
int family; int family;

View File

@ -58,6 +58,8 @@ static void test_in_addr_prefix_from_string_one(
} }
static void test_in_addr_prefix_from_string(void) { static void test_in_addr_prefix_from_string(void) {
log_info("/* %s */", __func__);
test_in_addr_prefix_from_string_one("", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); test_in_addr_prefix_from_string_one("", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
test_in_addr_prefix_from_string_one("/", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); test_in_addr_prefix_from_string_one("/", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
test_in_addr_prefix_from_string_one("/8", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); test_in_addr_prefix_from_string_one("/8", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0);
@ -90,7 +92,7 @@ static void test_in_addr_prefix_to_string_valid(int family, const char *p) {
union in_addr_union u; union in_addr_union u;
unsigned char l; unsigned char l;
log_info("/* %s */", p); log_info("%s: %s", __func__, p);
assert_se(in_addr_prefix_from_string(p, family, &u, &l) >= 0); assert_se(in_addr_prefix_from_string(p, family, &u, &l) >= 0);
assert_se(in_addr_prefix_to_string(family, &u, l, &str) >= 0); assert_se(in_addr_prefix_to_string(family, &u, l, &str) >= 0);
@ -102,7 +104,7 @@ static void test_in_addr_prefix_to_string_unoptimized(int family, const char *p)
union in_addr_union u1, u2; union in_addr_union u1, u2;
unsigned char len1, len2; unsigned char len1, len2;
log_info("/* %s */", p); log_info("%s: %s", __func__, p);
assert_se(in_addr_prefix_from_string(p, family, &u1, &len1) >= 0); assert_se(in_addr_prefix_from_string(p, family, &u1, &len1) >= 0);
assert_se(in_addr_prefix_to_string(family, &u1, len1, &str1) >= 0); assert_se(in_addr_prefix_to_string(family, &u1, len1, &str1) >= 0);
@ -115,6 +117,8 @@ static void test_in_addr_prefix_to_string_unoptimized(int family, const char *p)
} }
static void test_in_addr_prefix_to_string(void) { static void test_in_addr_prefix_to_string(void) {
log_info("/* %s */", __func__);
test_in_addr_prefix_to_string_valid(AF_INET, "0.0.0.0/32"); test_in_addr_prefix_to_string_valid(AF_INET, "0.0.0.0/32");
test_in_addr_prefix_to_string_valid(AF_INET, "1.2.3.4/0"); test_in_addr_prefix_to_string_valid(AF_INET, "1.2.3.4/0");
test_in_addr_prefix_to_string_valid(AF_INET, "1.2.3.4/24"); test_in_addr_prefix_to_string_valid(AF_INET, "1.2.3.4/24");
@ -137,6 +141,8 @@ static void test_in_addr_random_prefix(void) {
_cleanup_free_ char *str = NULL; _cleanup_free_ char *str = NULL;
union in_addr_union a; union in_addr_union a;
log_info("/* %s */", __func__);
assert_se(in_addr_from_string(AF_INET, "192.168.10.1", &a) >= 0); assert_se(in_addr_from_string(AF_INET, "192.168.10.1", &a) >= 0);
assert_se(in_addr_random_prefix(AF_INET, &a, 31, 32) >= 0); assert_se(in_addr_random_prefix(AF_INET, &a, 31, 32) >= 0);

View File

@ -12,11 +12,20 @@ static void test_socket_address_parse_one(const char *in, int ret, int family, c
int r; int r;
r = socket_address_parse(&a, in); r = socket_address_parse(&a, in);
if (r >= 0) if (r >= 0) {
assert_se(socket_address_print(&a, &out) >= 0); r = socket_address_print(&a, &out);
if (r < 0)
log_error_errno(r, "Printing failed for \"%s\": %m", in);
assert(r >= 0);
assert_se(a.type == 0);
}
log_info("\"%s\" → %s → \"%s\" (expect \"%s\")", in, log_info("\"%s\" → %s %d → \"%s\" (expect %d / \"%s\")",
r >= 0 ? "" : "", empty_to_dash(out), r >= 0 ? expected ?: in : "-"); in,
r >= 0 ? "" : "", r,
empty_to_dash(out),
ret,
ret >= 0 ? expected ?: in : "-");
assert_se(r == ret); assert_se(r == ret);
if (r >= 0) { if (r >= 0) {
assert_se(a.sockaddr.sa.sa_family == family); assert_se(a.sockaddr.sa.sa_family == family);
@ -50,14 +59,24 @@ static void test_socket_address_parse(void) {
test_socket_address_parse_one("[::1]:0", -EINVAL, 0, NULL); test_socket_address_parse_one("[::1]:0", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]:65536", -ERANGE, 0, NULL); test_socket_address_parse_one("[::1]:65536", -ERANGE, 0, NULL);
test_socket_address_parse_one("[a:b:1]:8888", -EINVAL, 0, NULL); test_socket_address_parse_one("[a:b:1]:8888", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]%lo:1234", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]%lo:0", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]%lo", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]%lo%lo:1234", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]% lo:1234", -EINVAL, 0, NULL);
test_socket_address_parse_one("8888", 0, default_family, "[::]:8888"); test_socket_address_parse_one("8888", 0, default_family, "[::]:8888");
test_socket_address_parse_one("[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888", 0, AF_INET6, test_socket_address_parse_one("[2001:0db8:0000:85a3:0000:0000:ac1f:8001]:8888", 0, AF_INET6,
"[2001:db8:0:85a3::ac1f:8001]:8888"); "[2001:db8:0:85a3::ac1f:8001]:8888");
test_socket_address_parse_one("[::1]:8888", 0, AF_INET6, NULL); test_socket_address_parse_one("[::1]:8888", 0, AF_INET6, NULL);
test_socket_address_parse_one("[::1]:1234%lo", 0, AF_INET6, NULL);
test_socket_address_parse_one("[::1]:0%lo", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]%lo", -EINVAL, 0, NULL);
test_socket_address_parse_one("[::1]:1234%lo%lo", -ENODEV, 0, NULL);
test_socket_address_parse_one("[::1]:1234%xxxxasdf", -ENODEV, 0, NULL);
test_socket_address_parse_one("192.168.1.254:8888", 0, AF_INET, NULL); test_socket_address_parse_one("192.168.1.254:8888", 0, AF_INET, NULL);
test_socket_address_parse_one("/foo/bar", 0, AF_UNIX, NULL); test_socket_address_parse_one("/foo/bar", 0, AF_UNIX, NULL);
test_socket_address_parse_one("/", 0, AF_UNIX, NULL); test_socket_address_parse_one("/", -EINVAL, 0, NULL);
test_socket_address_parse_one("@abstract", 0, AF_UNIX, NULL); test_socket_address_parse_one("@abstract", 0, AF_UNIX, NULL);
{ {
@ -198,9 +217,13 @@ static void test_socket_address_is(void) {
log_info("/* %s */", __func__); log_info("/* %s */", __func__);
assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0); assert_se(socket_address_parse(&a, "192.168.1.1:8888") >= 0);
assert_se(socket_address_is(&a, "192.168.1.1:8888", SOCK_STREAM)); assert_se( socket_address_is(&a, "192.168.1.1:8888", 0 /* unspecified yet */));
assert_se(!socket_address_is(&a, "route", 0));
assert_se(!socket_address_is(&a, "route", SOCK_STREAM)); assert_se(!socket_address_is(&a, "route", SOCK_STREAM));
assert_se(!socket_address_is(&a, "192.168.1.1:8888", SOCK_RAW)); assert_se(!socket_address_is(&a, "192.168.1.1:8888", SOCK_RAW));
assert_se(!socket_address_is(&a, "192.168.1.1:8888", SOCK_STREAM));
a.type = SOCK_STREAM;
assert_se( socket_address_is(&a, "192.168.1.1:8888", SOCK_STREAM));
} }
static void test_socket_address_is_netlink(void) { static void test_socket_address_is_netlink(void) {
@ -209,7 +232,7 @@ static void test_socket_address_is_netlink(void) {
log_info("/* %s */", __func__); log_info("/* %s */", __func__);
assert_se(socket_address_parse_netlink(&a, "route 10") >= 0); assert_se(socket_address_parse_netlink(&a, "route 10") >= 0);
assert_se(socket_address_is_netlink(&a, "route 10")); assert_se( socket_address_is_netlink(&a, "route 10"));
assert_se(!socket_address_is_netlink(&a, "192.168.1.1:8888")); assert_se(!socket_address_is_netlink(&a, "192.168.1.1:8888"));
assert_se(!socket_address_is_netlink(&a, "route 1")); assert_se(!socket_address_is_netlink(&a, "route 1"));
} }
@ -284,66 +307,83 @@ 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"); 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) { static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str, int family, uint16_t port, int ifindex,
_cleanup_free_ char *name = NULL, *x = NULL; const char *server_name, const char *str_repr) {
union in_addr_union a; union in_addr_union a;
uint16_t p; uint16_t p;
int f, i; int f, i;
char *fake;
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, &name) >= 0); log_info("%s: %s", __func__, str);
assert_se(family == f);
assert_se(port == p); {
assert_se(ifindex == i); _cleanup_free_ char *name = NULL, *x = NULL;
assert_se(streq_ptr(server_name, name)); assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, &name) == 0);
assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0); assert_se(family == f);
assert_se(streq(str, x)); 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_repr ?: str, x));
}
if (port > 0)
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, NULL, &i, &fake) == -EINVAL);
else {
_cleanup_free_ char *name = NULL, *x = NULL;
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, NULL, &i, &name) == 0);
assert_se(family == f);
assert_se(ifindex == i);
assert_se(streq_ptr(server_name, name));
assert_se(in_addr_port_ifindex_name_to_string(f, &a, 0, i, name, &x) >= 0);
assert_se(streq(str_repr ?: str, x));
}
if (ifindex > 0)
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, NULL, &fake) == -EINVAL);
else {
_cleanup_free_ char *name = NULL, *x = NULL;
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, NULL, &name) == 0);
assert_se(family == f);
assert_se(port == p);
assert_se(streq_ptr(server_name, name));
assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, 0, name, &x) >= 0);
assert_se(streq(str_repr ?: str, x));
}
if (server_name)
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, NULL) == -EINVAL);
else {
_cleanup_free_ char *x = NULL;
assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, NULL) == 0);
assert_se(family == f);
assert_se(port == p);
assert_se(ifindex == i);
assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, NULL, &x) >= 0);
assert_se(streq(str_repr ?: str, x));
}
} }
static void test_in_addr_port_ifindex_name_from_string_auto(void) { static void test_in_addr_port_ifindex_name_from_string_auto(void) {
log_info("/* %s */", __func__); 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", AF_INET, 0, 0, NULL, 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#test.com", AF_INET, 0, 0, "test.com", NULL);
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", AF_INET, 53, 0, NULL, 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("192.168.0.1:53#example.com", AF_INET, 53, 0, "example.com", NULL);
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", AF_INET6, 0, 0, NULL, 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#hoge.com", AF_INET6, 0, 0, "hoge.com", NULL);
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%19", AF_INET6, 0, 19, NULL, 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%lo", AF_INET6, 0, 1, NULL, "fe80::18%1");
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", AF_INET6, 53, 0, NULL, NULL);
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, NULL);
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%lo", AF_INET6, 53, 1, NULL, "[fe80::18]:53%1");
test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com"); test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19#hoge.com", AF_INET6, 0, 19, "hoge.com", NULL);
} test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53#hoge.com", AF_INET6, 53, 0, "hoge.com", NULL);
test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL, NULL);
static void test_in_addr_port_from_string_auto_one(const char *str, int family, const char *address_string, uint16_t port) { test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com", NULL);
union in_addr_union a, b; test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%lo", AF_INET6, 53, 1, NULL, "[fe80::18]:53%1");
uint16_t p; test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%lo#hoge.com", AF_INET6, 53, 1, "hoge.com", "[fe80::18]:53%1#hoge.com");
int f;
assert_se(in_addr_port_from_string_auto(str, &f, &a, &p) >= 0);
assert_se(family == f);
assert_se(port == p);
assert_se(in_addr_from_string(family, address_string, &b) >= 0);
assert_se(in_addr_equal(family, &a, &b) == 1);
}
static void test_in_addr_port_from_string_auto(void) {
log_info("/* %s */", __func__);
assert_se(in_addr_port_from_string_auto("192.168.0.1#test.com", NULL, NULL, NULL) < 0);
assert_se(in_addr_port_from_string_auto("192.168.0.1:53#example.com", NULL, NULL, NULL) < 0);
assert_se(in_addr_port_from_string_auto("fe80::18#hoge.com", NULL, NULL, NULL) < 0);
assert_se(in_addr_port_from_string_auto("fe80::18%19", NULL, NULL, NULL) < 0);
assert_se(in_addr_port_from_string_auto("fe80::18%19#hoge.com", NULL, NULL, NULL) < 0);
assert_se(in_addr_port_from_string_auto("[fe80::18]:53#hoge.com", NULL, NULL, NULL) < 0);
assert_se(in_addr_port_from_string_auto("[fe80::18]:53%19", NULL, NULL, NULL) < 0);
assert_se(in_addr_port_from_string_auto("[fe80::18]:53%19#hoge.com", NULL, NULL, NULL) < 0);
test_in_addr_port_from_string_auto_one("192.168.0.1", AF_INET, "192.168.0.1", 0);
test_in_addr_port_from_string_auto_one("192.168.0.1:53", AF_INET, "192.168.0.1", 53);
test_in_addr_port_from_string_auto_one("fe80::18", AF_INET6, "fe80::18", 0);
test_in_addr_port_from_string_auto_one("[fe80::18]:53", AF_INET6, "fe80::18", 53);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -360,7 +400,6 @@ int main(int argc, char *argv[]) {
test_in_addr_ifindex_from_string_auto(); test_in_addr_ifindex_from_string_auto();
test_in_addr_ifindex_name_from_string_auto(); test_in_addr_ifindex_name_from_string_auto();
test_in_addr_port_ifindex_name_from_string_auto(); test_in_addr_port_ifindex_name_from_string_auto();
test_in_addr_port_from_string_auto();
return 0; return 0;
} }

View File

@ -26,13 +26,13 @@ assert_cc(SUN_PATH_LEN == 108);
static void test_ifname_valid(void) { static void test_ifname_valid(void) {
log_info("/* %s */", __func__); log_info("/* %s */", __func__);
assert(ifname_valid("foo")); assert( ifname_valid("foo"));
assert(ifname_valid("eth0")); assert( ifname_valid("eth0"));
assert(!ifname_valid("0")); assert(!ifname_valid("0"));
assert(!ifname_valid("99")); assert(!ifname_valid("99"));
assert(ifname_valid("a99")); assert( ifname_valid("a99"));
assert(ifname_valid("99a")); assert( ifname_valid("99a"));
assert(!ifname_valid(NULL)); assert(!ifname_valid(NULL));
assert(!ifname_valid("")); assert(!ifname_valid(""));
@ -44,9 +44,13 @@ static void test_ifname_valid(void) {
assert(ifname_valid("foo.bar")); assert(ifname_valid("foo.bar"));
assert(!ifname_valid("x:y")); assert(!ifname_valid("x:y"));
assert(ifname_valid("xxxxxxxxxxxxxxx")); assert( ifname_valid_full("xxxxxxxxxxxxxxx", 0));
assert(!ifname_valid("xxxxxxxxxxxxxxxx")); assert(!ifname_valid_full("xxxxxxxxxxxxxxxx", 0));
assert(ifname_valid_full("xxxxxxxxxxxxxxxx", true)); assert( ifname_valid_full("xxxxxxxxxxxxxxxx", IFNAME_VALID_ALTERNATIVE));
assert( ifname_valid_full("xxxxxxxxxxxxxxxx", IFNAME_VALID_ALTERNATIVE));
assert(!ifname_valid_full("999", IFNAME_VALID_ALTERNATIVE));
assert( ifname_valid_full("999", IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC));
assert(!ifname_valid_full("0", IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC));
} }
static void test_socket_print_unix_one(const char *in, size_t len_in, const char *expected) { static void test_socket_print_unix_one(const char *in, size_t len_in, const char *expected) {
@ -504,17 +508,11 @@ int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG); test_setup_logging(LOG_DEBUG);
test_ifname_valid(); test_ifname_valid();
test_socket_print_unix(); test_socket_print_unix();
test_sockaddr_equal(); test_sockaddr_equal();
test_sockaddr_un_len(); test_sockaddr_un_len();
test_in_addr_is_multicast(); test_in_addr_is_multicast();
test_getpeercred_getpeergroups(); test_getpeercred_getpeergroups();
test_passfd_read(); test_passfd_read();
test_passfd_contents_read(); test_passfd_contents_read();
test_receive_nopassfd(); test_receive_nopassfd();

View File

@ -7,6 +7,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "ethtool-util.h" #include "ethtool-util.h"
#include "link-config.h" #include "link-config.h"
#include "network-internal.h" #include "network-internal.h"
#include "socket-util.h"
%} %}
struct ConfigPerfItem; struct ConfigPerfItem;
%null_strings %null_strings
@ -36,7 +37,7 @@ Link.MACAddressPolicy, config_parse_mac_address_policy, 0,
Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac) Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac)
Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy)
Link.Name, config_parse_ifname, 0, offsetof(link_config, name) Link.Name, config_parse_ifname, 0, offsetof(link_config, name)
Link.AlternativeName, config_parse_ifnames, 1, offsetof(link_config, alternative_names) Link.AlternativeName, config_parse_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(link_config, alternative_names)
Link.AlternativeNamesPolicy, config_parse_alternative_names_policy, 0, offsetof(link_config, alternative_names_policy) Link.AlternativeNamesPolicy, config_parse_alternative_names_policy, 0, offsetof(link_config, alternative_names_policy)
Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias) Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias)
Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(link_config, mtu) Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(link_config, mtu)