mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-24 21:34:08 +03:00
Merge pull request #16951 from yuwata/resolve-follow-ups-for-extra-dns-stub-listener
resolve: follow-ups for extra DNS stub listener
This commit is contained in:
commit
ad161cd6bb
@ -271,27 +271,27 @@
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>DNSStubListenerExtra=</varname></term>
|
||||
<listitem><para>Takes an IPv4 or IPv6 address to listen on. The address may optionally be prefixed by <literal>:</literal> and
|
||||
a protocol name (<literal>udp</literal> or <literal>tcp</literal>). When an IPv6 address is specified with a port number, then the
|
||||
address must be in the square brackets. This option can be specified multiple times. If an empty string is assigned, then the all
|
||||
previous assignments are cleared. It may also be optionally suffixed by a numeric port number with separator <literal>:</literal>.
|
||||
If the protocol is not specified, the service will listen on both <literal>UDP</literal> and <literal>TCP</literal>. If the port is not
|
||||
specified, then the service takes port as 53. This option may be used multiple times. Note that this is independent of the
|
||||
primary DNS stub configured with <varname>DNSStubListener=</varname>, and only configures <emphasis>additional</emphasis>
|
||||
sockets to listen on. Defaults to unset.</para>
|
||||
<listitem><para>Takes an IPv4 or IPv6 address to listen on. The address may be optionally
|
||||
prefixed with a protocol name (<literal>udp</literal> or <literal>tcp</literal>) separated with
|
||||
<literal>:</literal>. If the protocol is not specified, the service will listen on both UDP and
|
||||
TCP. It may be also optionally suffixed by a numeric port number with separator
|
||||
<literal>:</literal>. When an IPv6 address is specified with a port number, then the address
|
||||
must be in the square brackets. If the port is not specified, then the service uses port 53.
|
||||
Note that this is independent of the primary DNS stub configured with
|
||||
<varname>DNSStubListener=</varname>, and only configures <emphasis>additional</emphasis>
|
||||
sockets to listen on. This option can be specified multiple times. If an empty string is
|
||||
assigned, then the all previous assignments are cleared. Defaults to unset.</para>
|
||||
|
||||
<para>If the string in the format <literal>udp</literal>:[<replaceable>x</replaceable>]:<replaceable>y</replaceable>,
|
||||
it is read as protocol <literal>udp</literal> and IPv6 address x on a port y.</para>
|
||||
|
||||
<para>If the string in the format [<replaceable>x</replaceable>]:<replaceable>y</replaceable>, it is read as both protocol
|
||||
<literal>udp</literal> and <literal>tcp</literal>, IPv6 address x on a port y.</para>
|
||||
|
||||
<para>If the string in the format <replaceable>x</replaceable>, it is read as protocol both <literal>udp</literal> and
|
||||
<literal>tcp</literal>, IPv6/IPv4 address x on a port 53.</para>
|
||||
|
||||
<para>If the string in the format <literal>udp</literal>:<replaceable>x</replaceable>:<replaceable>y</replaceable>,
|
||||
it is read as protocol <literal>udp</literal> and IPv4 address x on a port y.</para>
|
||||
</listitem>
|
||||
<para>Examples:
|
||||
<programlisting>DNSStubListenerExtra=192.168.10.10
|
||||
DNSStubListenerExtra=2001:db8:0:f102::10
|
||||
DNSStubListenerExtra=192.168.10.11:9953
|
||||
DNSStubListenerExtra=[2001:db8:0:f102::11]:9953
|
||||
DNSStubListenerExtra=tcp:192.168.10.12
|
||||
DNSStubListenerExtra=udp:2001:db8:0:f102::12
|
||||
DNSStubListenerExtra=tcp:192.168.10.13:9953
|
||||
DNSStubListenerExtra=udp:[2001:db8:0:f102::13]:9953</programlisting>
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -402,46 +402,6 @@ int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned
|
||||
return 0;
|
||||
}
|
||||
|
||||
int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
|
||||
_cleanup_free_ char *x = NULL;
|
||||
size_t l;
|
||||
int r;
|
||||
|
||||
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. */
|
||||
|
||||
if (family != AF_INET6)
|
||||
goto fallback;
|
||||
if (ifindex <= 0)
|
||||
goto fallback;
|
||||
|
||||
r = in_addr_is_link_local(family, u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
goto fallback;
|
||||
|
||||
l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1;
|
||||
x = new(char, l);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
||||
errno = 0;
|
||||
if (!inet_ntop(family, u, x, l))
|
||||
return errno_or_else(EINVAL);
|
||||
|
||||
sprintf(strchr(x, 0), "%%%i", ifindex);
|
||||
|
||||
*ret = TAKE_PTR(x);
|
||||
return 0;
|
||||
|
||||
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;
|
||||
|
@ -41,8 +41,13 @@ int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, u
|
||||
int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, 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);
|
||||
static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
|
||||
return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret);
|
||||
}
|
||||
static inline int in_addr_port_to_string(int family, const union in_addr_union *u, uint16_t port, char **ret) {
|
||||
return in_addr_port_ifindex_name_to_string(family, u, port, 0, NULL, 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);
|
||||
|
||||
|
@ -63,6 +63,10 @@ struct sockaddr_vm {
|
||||
#define IP_TRANSPARENT 19
|
||||
#endif
|
||||
|
||||
#ifndef IPV6_FREEBIND
|
||||
#define IPV6_FREEBIND 78
|
||||
#endif
|
||||
|
||||
/* linux/sockios.h */
|
||||
#ifndef SIOCGSKNS
|
||||
#define SIOCGSKNS 0x894C
|
||||
|
@ -205,6 +205,35 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
|
||||
strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \
|
||||
})
|
||||
|
||||
#define SOCKADDR_LEN(sa) \
|
||||
({ \
|
||||
const union sockaddr_union *__sa = &(sa); \
|
||||
size_t _len; \
|
||||
switch(__sa->sa.sa_family) { \
|
||||
case AF_INET: \
|
||||
_len = sizeof(struct sockaddr_in); \
|
||||
break; \
|
||||
case AF_INET6: \
|
||||
_len = sizeof(struct sockaddr_in6); \
|
||||
break; \
|
||||
case AF_UNIX: \
|
||||
_len = SOCKADDR_UN_LEN(__sa->un); \
|
||||
break; \
|
||||
case AF_PACKET: \
|
||||
_len = SOCKADDR_LL_LEN(__sa->ll); \
|
||||
break; \
|
||||
case AF_NETLINK: \
|
||||
_len = sizeof(struct sockaddr_nl); \
|
||||
break; \
|
||||
case AF_VSOCK: \
|
||||
_len = sizeof(struct sockaddr_vm); \
|
||||
break; \
|
||||
default: \
|
||||
assert_not_reached("invalid socket family"); \
|
||||
} \
|
||||
_len; \
|
||||
})
|
||||
|
||||
int socket_ioctl_fd(void);
|
||||
|
||||
int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path);
|
||||
|
@ -30,20 +30,15 @@ static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MA
|
||||
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
|
||||
|
||||
static void dns_stub_listener_extra_hash_func(const DNSStubListenerExtra *a, struct siphash *state) {
|
||||
unsigned port;
|
||||
|
||||
assert(a);
|
||||
|
||||
siphash24_compress(&a->mode, sizeof(a->mode), state);
|
||||
siphash24_compress(&socket_address_family(&a->address), sizeof(a->address.type), state);
|
||||
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(socket_address_family(&a->address)), state);
|
||||
|
||||
(void) sockaddr_port(&a->address.sockaddr.sa, &port);
|
||||
siphash24_compress(&port, sizeof(port), state);
|
||||
siphash24_compress(&a->family, sizeof(a->family), state);
|
||||
siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state);
|
||||
siphash24_compress(&a->port, sizeof(a->port), state);
|
||||
}
|
||||
|
||||
static int dns_stub_listener_extra_compare_func(const DNSStubListenerExtra *a, const DNSStubListenerExtra *b) {
|
||||
unsigned p, q;
|
||||
int r;
|
||||
|
||||
assert(a);
|
||||
@ -53,18 +48,15 @@ static int dns_stub_listener_extra_compare_func(const DNSStubListenerExtra *a, c
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = CMP(socket_address_family(&a->address), socket_address_family(&b->address));
|
||||
r = CMP(a->family, b->family);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(socket_address_family(&a->address)));
|
||||
r = memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
(void) sockaddr_port(&a->address.sockaddr.sa, &p);
|
||||
(void) sockaddr_port(&b->address.sockaddr.sa, &q);
|
||||
|
||||
return CMP(p, q);
|
||||
return CMP(a->port, b->port);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
@ -72,7 +64,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
DNSStubListenerExtra,
|
||||
dns_stub_listener_extra_hash_func,
|
||||
dns_stub_listener_extra_compare_func,
|
||||
free);
|
||||
dns_stub_listener_extra_free);
|
||||
|
||||
static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
|
||||
_cleanup_free_ char *server_name = NULL;
|
||||
@ -444,10 +436,8 @@ int config_parse_dns_stub_listener_extra(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_free_ DNSStubListenerExtra *udp = NULL, *tcp = NULL;
|
||||
_cleanup_free_ char *word = NULL;
|
||||
_cleanup_free_ DNSStubListenerExtra *stub = NULL;
|
||||
Manager *m = userdata;
|
||||
bool both = false;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
@ -461,78 +451,41 @@ int config_parse_dns_stub_listener_extra(
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = rvalue;
|
||||
r = extract_first_word(&p, &word, ":", 0);
|
||||
if (r == -ENOMEM)
|
||||
r = dns_stub_listener_extra_new(&stub);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
if (r <= 0) {
|
||||
|
||||
p = startswith(rvalue, "udp:");
|
||||
if (p)
|
||||
stub->mode = DNS_STUB_LISTENER_UDP;
|
||||
else {
|
||||
p = startswith(rvalue, "tcp:");
|
||||
if (p)
|
||||
stub->mode = DNS_STUB_LISTENER_TCP;
|
||||
else {
|
||||
stub->mode = DNS_STUB_LISTENER_YES;
|
||||
p = rvalue;
|
||||
}
|
||||
}
|
||||
|
||||
r = in_addr_port_from_string_auto(p, &stub->family, &stub->address, &stub->port);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Invalid DNSStubListenExtra='%s', ignoring assignment", rvalue);
|
||||
"Failed to parse address in %s=%s, ignoring assignment: %m",
|
||||
lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First look for udp/tcp. If not specified then turn both TCP and UDP */
|
||||
if (!STR_IN_SET(word, "tcp", "udp")) {
|
||||
both = true;
|
||||
p = rvalue;
|
||||
r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, stub);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to store %s=%s, ignoring assignment: %m", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(word, "tcp") || both) {
|
||||
r = dns_stub_extra_new(&tcp);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
tcp->mode = DNS_STUB_LISTENER_TCP;
|
||||
}
|
||||
|
||||
if (streq(word, "udp") || both) {
|
||||
r = dns_stub_extra_new(&udp);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
udp->mode = DNS_STUB_LISTENER_UDP;
|
||||
}
|
||||
|
||||
if (tcp) {
|
||||
r = socket_addr_port_from_string_auto(p, 53, &tcp->address);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address in DNSStubListenExtra='%s', ignoring", rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (udp) {
|
||||
r = socket_addr_port_from_string_auto(p, 53, &udp->address);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address in DNSStubListenExtra='%s', ignoring", rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (tcp) {
|
||||
r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, tcp);
|
||||
if (r < 0) {
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
|
||||
log_warning_errno(r, "Failed to store TCP DNSStubListenExtra='%s', ignoring assignment: %m", rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (udp) {
|
||||
r = ordered_set_ensure_put(&m->dns_extra_stub_listeners, &dns_stub_listener_extra_hash_ops, udp);
|
||||
if (r < 0) {
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
|
||||
log_warning_errno(r, "Failed to store UDP DNSStubListenExtra='%s', ignoring assignment: %m", rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
TAKE_PTR(tcp);
|
||||
TAKE_PTR(udp);
|
||||
TAKE_PTR(stub);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ typedef enum DnsStubListenerMode DnsStubListenerMode;
|
||||
|
||||
enum DnsStubListenerMode {
|
||||
DNS_STUB_LISTENER_NO,
|
||||
DNS_STUB_LISTENER_UDP,
|
||||
DNS_STUB_LISTENER_TCP,
|
||||
DNS_STUB_LISTENER_YES,
|
||||
DNS_STUB_LISTENER_UDP = 1 << 0,
|
||||
DNS_STUB_LISTENER_TCP = 1 << 1,
|
||||
DNS_STUB_LISTENER_YES = DNS_STUB_LISTENER_UDP | DNS_STUB_LISTENER_TCP,
|
||||
_DNS_STUB_LISTENER_MODE_MAX,
|
||||
_DNS_STUB_LISTENER_MODE_INVALID = -1
|
||||
};
|
||||
|
@ -66,6 +66,7 @@ struct DnsPacket {
|
||||
DnsResourceRecord *opt;
|
||||
|
||||
/* Packet reception metadata */
|
||||
int fd; /* Used by UDP extra DNS stub listners */
|
||||
int ifindex;
|
||||
int family, ipproto;
|
||||
union in_addr_union sender, destination;
|
||||
|
@ -1,8 +1,11 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <net/if_arp.h>
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "missing_network.h"
|
||||
#include "missing_socket.h"
|
||||
#include "resolved-dns-stub.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
@ -11,25 +14,28 @@
|
||||
* IP and UDP header sizes */
|
||||
#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
|
||||
|
||||
static int manager_dns_stub_udp_fd(Manager *m);
|
||||
static int manager_dns_stub_tcp_fd(Manager *m);
|
||||
|
||||
int dns_stub_extra_new(DNSStubListenerExtra **ret) {
|
||||
int dns_stub_listener_extra_new(DNSStubListenerExtra **ret) {
|
||||
DNSStubListenerExtra *l;
|
||||
|
||||
l = new(DNSStubListenerExtra, 1);
|
||||
l = new0(DNSStubListenerExtra, 1);
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
*l = (DNSStubListenerExtra) {
|
||||
.fd = -1,
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DNSStubListenerExtra *dns_stub_listener_extra_free(DNSStubListenerExtra *p) {
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
p->udp_event_source = sd_event_source_unref(p->udp_event_source);
|
||||
p->tcp_event_source = sd_event_source_unref(p->tcp_event_source);
|
||||
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
static int dns_stub_make_reply_packet(
|
||||
DnsPacket **p,
|
||||
size_t max_size,
|
||||
@ -155,17 +161,11 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl
|
||||
if (s)
|
||||
r = dns_stream_write_packet(s, reply);
|
||||
else {
|
||||
int fd;
|
||||
|
||||
fd = manager_dns_stub_udp_fd(m);
|
||||
if (fd < 0)
|
||||
return log_debug_errno(fd, "Failed to get reply socket: %m");
|
||||
|
||||
/* Note that it is essential here that we explicitly choose the source IP address for this packet. This
|
||||
* 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. */
|
||||
|
||||
r = manager_send(m, fd, LOOPBACK_IFINDEX, p->family, &p->sender, p->sender_port, &p->destination, reply);
|
||||
r = manager_send(m, p->fd, p->ifindex, p->family, &p->sender, p->sender_port, &p->destination, reply);
|
||||
}
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to send reply packet: %m");
|
||||
@ -294,7 +294,7 @@ static int dns_stub_stream_complete(DnsStream *s, int error) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
|
||||
static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p, bool is_extra) {
|
||||
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
|
||||
int r;
|
||||
|
||||
@ -302,8 +302,9 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
|
||||
assert(p);
|
||||
assert(p->protocol == DNS_PROTOCOL_DNS);
|
||||
|
||||
if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
|
||||
in_addr_is_localhost(p->family, &p->destination) <= 0) {
|
||||
if (!is_extra &&
|
||||
(in_addr_is_localhost(p->family, &p->sender) <= 0 ||
|
||||
in_addr_is_localhost(p->family, &p->destination) <= 0)) {
|
||||
log_error("Got packet on unexpected IP range, refusing.");
|
||||
dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
|
||||
return;
|
||||
@ -384,9 +385,8 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
|
||||
TAKE_PTR(q);
|
||||
}
|
||||
|
||||
static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
static int on_dns_stub_packet_internal(sd_event_source *s, int fd, uint32_t revents, Manager *m, bool is_extra) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
Manager *m = userdata;
|
||||
int r;
|
||||
|
||||
r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
|
||||
@ -396,27 +396,50 @@ static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void
|
||||
if (dns_packet_validate_query(p) > 0) {
|
||||
log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p));
|
||||
|
||||
dns_stub_process_query(m, NULL, p);
|
||||
dns_stub_process_query(m, NULL, p, is_extra);
|
||||
} else
|
||||
log_debug("Invalid DNS stub UDP packet, ignoring.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dns_stub_common_socket_options(int fd) {
|
||||
static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
return on_dns_stub_packet_internal(s, fd, revents, userdata, false);
|
||||
}
|
||||
|
||||
static int on_dns_stub_packet_extra(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
return on_dns_stub_packet_internal(s, fd, revents, userdata, true);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (family == AF_INET) {
|
||||
r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
|
||||
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) {
|
||||
@ -428,14 +451,14 @@ static int manager_dns_stub_udp_fd(Manager *m) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
if (m->dns_stub_udp_fd >= 0)
|
||||
return m->dns_stub_udp_fd;
|
||||
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);
|
||||
r = set_dns_stub_common_socket_options(fd, AF_INET);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -451,70 +474,84 @@ static int manager_dns_stub_udp_fd(Manager *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 m->dns_stub_udp_fd = TAKE_FD(fd);
|
||||
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;
|
||||
|
||||
if (l->fd >= 0)
|
||||
if (l->udp_event_source)
|
||||
return 0;
|
||||
|
||||
fd = socket(socket_address_family(&l->address), SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 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;
|
||||
}
|
||||
|
||||
r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
|
||||
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;
|
||||
|
||||
r = set_dns_stub_common_socket_options(fd);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (bind(fd, &l->address.sockaddr.sa, l->address.size) < 0) {
|
||||
if (bind(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = sd_event_add_io(m->event, &l->dns_stub_extra_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
|
||||
r = sd_event_add_io(m->event, &l->udp_event_source, fd, EPOLLIN, on_dns_stub_packet_extra, m);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(l->dns_stub_extra_event_source, "dns-stub-udp-extra");
|
||||
r = sd_event_source_set_io_fd_own(l->udp_event_source, true);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
l->fd = TAKE_FD(fd);
|
||||
(void) sd_event_source_set_description(l->udp_event_source, "dns-stub-udp-extra");
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
(void) sockaddr_pretty(&l->address.sockaddr.sa, FAMILY_ADDRESS_SIZE(l->address.sockaddr.sa.sa_family), true, true, &pretty);
|
||||
(void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
|
||||
log_debug("Listening on UDP socket %s.", strnull(pretty));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return TAKE_FD(fd);
|
||||
|
||||
fail:
|
||||
(void) sockaddr_pretty(&l->address.sockaddr.sa, FAMILY_ADDRESS_SIZE(l->address.sockaddr.sa.sa_family), true, true, &pretty);
|
||||
if (r == -EADDRINUSE)
|
||||
return log_warning_errno(r,
|
||||
"Another process is already listening on UDP socket %s.\n"
|
||||
"Turning off local DNS stub extra support.", strnull(pretty));
|
||||
if (r == -EPERM)
|
||||
return log_warning_errno(r,
|
||||
"Failed to listen on UDP socket %s: %m.\n"
|
||||
"Turning off local DNS stub extra support.", strnull(pretty));
|
||||
|
||||
assert(r < 0);
|
||||
|
||||
return log_warning_errno(r, "Failed to listen on UDP socket %s, ignoring: %m", strnull(pretty));
|
||||
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_internal(DnsStream *s, bool is_extra) {
|
||||
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
|
||||
|
||||
assert(s);
|
||||
@ -525,16 +562,23 @@ static int on_dns_stub_stream_packet(DnsStream *s) {
|
||||
if (dns_packet_validate_query(p) > 0) {
|
||||
log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p));
|
||||
|
||||
dns_stub_process_query(s->manager, s, p);
|
||||
dns_stub_process_query(s->manager, s, p, is_extra);
|
||||
} else
|
||||
log_debug("Invalid DNS stub TCP packet, ignoring.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
static int on_dns_stub_stream_packet(DnsStream *s) {
|
||||
return on_dns_stub_stream_packet_internal(s, false);
|
||||
}
|
||||
|
||||
static int on_dns_stub_stream_packet_extra(DnsStream *s) {
|
||||
return on_dns_stub_stream_packet_internal(s, true);
|
||||
}
|
||||
|
||||
static int on_dns_stub_stream_internal(sd_event_source *s, int fd, uint32_t revents, Manager *m, bool is_extra) {
|
||||
DnsStream *stream;
|
||||
Manager *m = userdata;
|
||||
int cfd, r;
|
||||
|
||||
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
|
||||
@ -551,7 +595,7 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
|
||||
return r;
|
||||
}
|
||||
|
||||
stream->on_packet = on_dns_stub_stream_packet;
|
||||
stream->on_packet = is_extra ? on_dns_stub_stream_packet_extra : on_dns_stub_stream_packet;
|
||||
stream->complete = dns_stub_stream_complete;
|
||||
|
||||
/* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */
|
||||
@ -559,6 +603,14 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
return on_dns_stub_stream_internal(s, fd, revents, userdata, false);
|
||||
}
|
||||
|
||||
static int on_dns_stub_stream_extra(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
return on_dns_stub_stream_internal(s, fd, revents, userdata, true);
|
||||
}
|
||||
|
||||
static int manager_dns_stub_tcp_fd(Manager *m) {
|
||||
union sockaddr_union sa = {
|
||||
.in.sin_family = AF_INET,
|
||||
@ -568,14 +620,14 @@ static int manager_dns_stub_tcp_fd(Manager *m) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
if (m->dns_stub_tcp_fd >= 0)
|
||||
return m->dns_stub_tcp_fd;
|
||||
if (m->dns_stub_tcp_event_source)
|
||||
return sd_event_source_get_io_fd(m->dns_stub_tcp_event_source);
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
r = set_dns_stub_common_socket_options(fd);
|
||||
r = set_dns_stub_common_socket_options(fd, AF_INET);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -598,38 +650,58 @@ static int manager_dns_stub_tcp_fd(Manager *m) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_io_fd_own(m->dns_stub_tcp_event_source, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
|
||||
|
||||
return m->dns_stub_tcp_fd = TAKE_FD(fd);
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
|
||||
static int manager_dns_stub_tcp_fd_extra(Manager *m, DNSStubListenerExtra *l) {
|
||||
_cleanup_free_ char *pretty = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
union sockaddr_union sa;
|
||||
int r;
|
||||
|
||||
if (l->fd >= 0)
|
||||
return 0;
|
||||
if (l->tcp_event_source)
|
||||
return sd_event_source_get_io_fd(l->tcp_event_source);;
|
||||
|
||||
fd = socket(socket_address_family(&l->address), SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 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_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = set_dns_stub_common_socket_options(fd);
|
||||
r = set_dns_stub_common_socket_options(fd, l->family);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, 1);
|
||||
/* Do not set IP_TTL for extra DNS stub listners, as the address may not be local and in that
|
||||
* case people may want ttl > 1. */
|
||||
|
||||
if (l->family == AF_INET)
|
||||
r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
|
||||
else
|
||||
r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_FREEBIND, true);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (bind(fd, &l->address.sockaddr.sa, l->address.size) < 0) {
|
||||
if (bind(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
@ -639,35 +711,29 @@ static int manager_dns_stub_tcp_fd_extra(Manager *m, DNSStubListenerExtra *l) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = sd_event_add_io(m->event, &l->dns_stub_extra_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
|
||||
r = sd_event_add_io(m->event, &l->tcp_event_source, fd, EPOLLIN, on_dns_stub_stream_extra, m);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(l->dns_stub_extra_event_source, "dns-stub-tcp-extra");
|
||||
r = sd_event_source_set_io_fd_own(l->tcp_event_source, true);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
l->fd = TAKE_FD(fd);
|
||||
(void) sd_event_source_set_description(l->tcp_event_source, "dns-stub-tcp-extra");
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
(void) sockaddr_pretty(&l->address.sockaddr.sa, FAMILY_ADDRESS_SIZE(l->address.sockaddr.sa.sa_family), true, true, &pretty);
|
||||
(void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
|
||||
log_debug("Listening on TCP socket %s.", strnull(pretty));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return TAKE_FD(fd);
|
||||
|
||||
fail:
|
||||
(void) sockaddr_pretty(&l->address.sockaddr.sa, FAMILY_ADDRESS_SIZE(l->address.sockaddr.sa.sa_family), true, true, &pretty);
|
||||
if (r == -EADDRINUSE)
|
||||
return log_warning_errno(r,
|
||||
"Another process is already listening on TCP socket %s.\n"
|
||||
"Turning off local DNS stub extra support.", strnull(pretty));
|
||||
if (r == -EPERM)
|
||||
return log_warning_errno(r,
|
||||
"Failed to listen on TCP socket %s: %m.\n"
|
||||
"Turning off local DNS stub extra support.", strnull(pretty));
|
||||
|
||||
assert(r < 0);
|
||||
|
||||
return log_warning_errno(r, "Failed to listen on TCP socket %s, ignoring: %m", strnull(pretty));
|
||||
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 TCP socket %s: %m", strnull(pretty));
|
||||
return log_warning_errno(r, "Failed to listen on TCP socket %s: %m", strnull(pretty));
|
||||
}
|
||||
|
||||
int manager_dns_stub_start(Manager *m) {
|
||||
@ -684,11 +750,11 @@ int manager_dns_stub_start(Manager *m) {
|
||||
m->dns_stub_listener_mode == DNS_STUB_LISTENER_TCP ? "TCP" :
|
||||
"UDP/TCP");
|
||||
|
||||
if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP))
|
||||
if (FLAGS_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_UDP))
|
||||
r = manager_dns_stub_udp_fd(m);
|
||||
|
||||
if (r >= 0 &&
|
||||
IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_TCP)) {
|
||||
FLAGS_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_TCP)) {
|
||||
t = "TCP";
|
||||
r = manager_dns_stub_tcp_fd(m);
|
||||
}
|
||||
@ -710,16 +776,14 @@ int manager_dns_stub_start(Manager *m) {
|
||||
DNSStubListenerExtra *l;
|
||||
Iterator i;
|
||||
|
||||
log_debug("Creating stub listener extra using %s.",
|
||||
m->dns_stub_listener_mode == DNS_STUB_LISTENER_UDP ? "UDP" :
|
||||
m->dns_stub_listener_mode == DNS_STUB_LISTENER_TCP ? "TCP" :
|
||||
"UDP/TCP");
|
||||
log_debug("Creating extra stub listeners.");
|
||||
|
||||
ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners, i)
|
||||
if (l->mode == DNS_STUB_LISTENER_UDP)
|
||||
ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners, i) {
|
||||
if (FLAGS_SET(l->mode, DNS_STUB_LISTENER_UDP))
|
||||
(void) manager_dns_stub_udp_fd_extra(m, l);
|
||||
else
|
||||
if (FLAGS_SET(l->mode, DNS_STUB_LISTENER_TCP))
|
||||
(void) manager_dns_stub_tcp_fd_extra(m, l);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -730,19 +794,4 @@ void manager_dns_stub_stop(Manager *m) {
|
||||
|
||||
m->dns_stub_udp_event_source = sd_event_source_unref(m->dns_stub_udp_event_source);
|
||||
m->dns_stub_tcp_event_source = sd_event_source_unref(m->dns_stub_tcp_event_source);
|
||||
|
||||
m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
|
||||
m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
|
||||
}
|
||||
|
||||
void manager_dns_stub_stop_extra(Manager *m) {
|
||||
DNSStubListenerExtra *l;
|
||||
Iterator i;
|
||||
|
||||
assert(m);
|
||||
|
||||
ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners, i) {
|
||||
l->dns_stub_extra_event_source = sd_event_source_unref(l->dns_stub_extra_event_source);
|
||||
l->fd = safe_close(l->fd);
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
#include "resolved-manager.h"
|
||||
|
||||
int dns_stub_extra_new(DNSStubListenerExtra **ret);
|
||||
int dns_stub_listener_extra_new(DNSStubListenerExtra **ret);
|
||||
DNSStubListenerExtra *dns_stub_listener_extra_free(DNSStubListenerExtra *p);
|
||||
|
||||
void manager_dns_stub_stop(Manager *m);
|
||||
void manager_dns_stub_stop_extra(Manager *m);
|
||||
int manager_dns_stub_start(Manager *m);
|
||||
|
@ -578,8 +578,6 @@ int manager_new(Manager **ret) {
|
||||
.llmnr_ipv6_tcp_fd = -1,
|
||||
.mdns_ipv4_fd = -1,
|
||||
.mdns_ipv6_fd = -1,
|
||||
.dns_stub_udp_fd = -1,
|
||||
.dns_stub_tcp_fd = -1,
|
||||
.hostname_fd = -1,
|
||||
|
||||
.llmnr_support = DEFAULT_LLMNR_MODE,
|
||||
@ -701,7 +699,6 @@ Manager *manager_free(Manager *m) {
|
||||
|
||||
hashmap_free(m->links);
|
||||
hashmap_free(m->dns_transactions);
|
||||
ordered_set_free(m->dns_extra_stub_listeners);
|
||||
|
||||
sd_event_source_unref(m->network_event_source);
|
||||
sd_network_monitor_unref(m->network_monitor);
|
||||
@ -714,7 +711,7 @@ Manager *manager_free(Manager *m) {
|
||||
manager_dns_stub_stop(m);
|
||||
manager_varlink_done(m);
|
||||
|
||||
manager_dns_stub_stop_extra(m);
|
||||
ordered_set_free(m->dns_extra_stub_listeners);
|
||||
|
||||
bus_verify_polkit_async_registry_free(m->polkit_registry);
|
||||
|
||||
@ -793,6 +790,7 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
|
||||
|
||||
p->size = (size_t) l;
|
||||
|
||||
p->fd = fd;
|
||||
p->family = sa.sa.sa_family;
|
||||
p->ipproto = IPPROTO_UDP;
|
||||
if (p->family == AF_INET) {
|
||||
|
@ -32,11 +32,14 @@ typedef struct EtcHosts {
|
||||
} EtcHosts;
|
||||
|
||||
typedef struct DNSStubListenerExtra {
|
||||
int fd;
|
||||
|
||||
DnsStubListenerMode mode;
|
||||
SocketAddress address;
|
||||
sd_event_source *dns_stub_extra_event_source;
|
||||
|
||||
int family;
|
||||
union in_addr_union address;
|
||||
uint16_t port;
|
||||
|
||||
sd_event_source *udp_event_source;
|
||||
sd_event_source *tcp_event_source;
|
||||
} DNSStubListenerExtra;
|
||||
|
||||
struct Manager {
|
||||
@ -141,12 +144,9 @@ struct Manager {
|
||||
dev_t etc_hosts_dev;
|
||||
bool read_etc_hosts;
|
||||
|
||||
/* Local DNS stub on 127.0.0.53:53 */
|
||||
int dns_stub_udp_fd;
|
||||
int dns_stub_tcp_fd;
|
||||
|
||||
OrderedSet *dns_extra_stub_listeners;
|
||||
|
||||
/* Local DNS stub on 127.0.0.53:53 */
|
||||
sd_event_source *dns_stub_udp_event_source;
|
||||
sd_event_source *dns_stub_tcp_event_source;
|
||||
|
||||
|
@ -235,33 +235,6 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_addr_port_from_string_auto(const char *s, uint16_t default_port, SocketAddress *a) {
|
||||
union in_addr_union address;
|
||||
uint16_t port = 0;
|
||||
int family, r;
|
||||
|
||||
assert(a);
|
||||
assert(s);
|
||||
|
||||
r = in_addr_port_ifindex_name_from_string_auto(s, &family, &address, &port, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (family == AF_INET) {
|
||||
memcpy(&a->sockaddr.in.sin_addr, &address.in.s_addr, sizeof(a->sockaddr.in.sin_addr));
|
||||
a->sockaddr.in.sin_family = AF_INET;
|
||||
a->size = sizeof(struct sockaddr_in);
|
||||
a->sockaddr.in.sin_port = port ? htobe16(port) : htobe16(default_port);
|
||||
} else {
|
||||
memcpy(&a->sockaddr.in6.sin6_addr, &address.in6, sizeof(a->sockaddr.in6.sin6_addr));
|
||||
a->sockaddr.in6.sin6_family = AF_INET6;
|
||||
a->sockaddr.in6.sin6_port = port ? htobe16(port) : htobe16(default_port);
|
||||
a->size = sizeof(struct sockaddr_in6);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
unsigned group = 0;
|
||||
@ -482,6 +455,36 @@ int in_addr_port_ifindex_name_from_string_auto(
|
||||
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) {
|
||||
if (!a)
|
||||
return NULL;
|
||||
|
@ -16,7 +16,6 @@ int make_socket_fd(int log_level, const char* address, int type, int flags);
|
||||
int socket_address_parse(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
|
||||
int socket_address_parse_netlink(SocketAddress *a, const char *s);
|
||||
int socket_addr_port_from_string_auto(const char *s, uint16_t default_port, SocketAddress *a);
|
||||
|
||||
bool socket_address_is(const SocketAddress *a, const char *s, int type);
|
||||
bool socket_address_is_netlink(const SocketAddress *a, const char *s);
|
||||
@ -34,6 +33,7 @@ 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) {
|
||||
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 {
|
||||
int family;
|
||||
|
@ -177,10 +177,163 @@ static void test_in_addr_random_prefix(void) {
|
||||
str = mfree(str);
|
||||
}
|
||||
|
||||
static void test_in_addr_is_null(void) {
|
||||
union in_addr_union i = {};
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(in_addr_is_null(AF_INET, &i) == true);
|
||||
assert_se(in_addr_is_null(AF_INET6, &i) == true);
|
||||
|
||||
i.in.s_addr = 0x1000000;
|
||||
assert_se(in_addr_is_null(AF_INET, &i) == false);
|
||||
assert_se(in_addr_is_null(AF_INET6, &i) == false);
|
||||
|
||||
assert_se(in_addr_is_null(-1, &i) == -EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_intersect_one(unsigned f, const char *a, unsigned apl, const char *b, unsigned bpl, int result) {
|
||||
union in_addr_union ua, ub;
|
||||
|
||||
assert_se(in_addr_from_string(f, a, &ua) >= 0);
|
||||
assert_se(in_addr_from_string(f, b, &ub) >= 0);
|
||||
|
||||
assert_se(in_addr_prefix_intersect(f, &ua, apl, &ub, bpl) == result);
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_intersect(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "255.255.255.255", 32, "255.255.255.254", 32, 0);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "255.255.255.255", 0, "255.255.255.255", 32, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "0.0.0.0", 0, "47.11.8.15", 32, 1);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.1", 24, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "2.2.2.2", 24, "1.1.1.1", 24, 0);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.127", 25, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.127", 26, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 25, "1.1.1.127", 25, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 25, "1.1.1.255", 25, 0);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", 128, 0);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "::", 0, "beef:beef:beef:beef:beef:beef:beef:beef", 128, 1);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::2", 64, "1::2", 64, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "2::2", 64, "1::2", 64, 0);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 120, "1::007f", 121, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 120, "1::007f", 122, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 121, "1::007f", 121, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 121, "1::00ff", 121, 0);
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_next_one(unsigned f, const char *before, unsigned pl, const char *after) {
|
||||
union in_addr_union ubefore, uafter, t;
|
||||
|
||||
assert_se(in_addr_from_string(f, before, &ubefore) >= 0);
|
||||
|
||||
t = ubefore;
|
||||
assert_se((in_addr_prefix_next(f, &t, pl) > 0) == !!after);
|
||||
|
||||
if (after) {
|
||||
assert_se(in_addr_from_string(f, after, &uafter) >= 0);
|
||||
assert_se(in_addr_equal(f, &t, &uafter) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_next(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 24, "192.168.1.0");
|
||||
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 16, "192.169.0.0");
|
||||
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 20, "192.168.16.0");
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET, "0.0.0.0", 32, "0.0.0.1");
|
||||
test_in_addr_prefix_next_one(AF_INET, "255.255.255.255", 32, NULL);
|
||||
test_in_addr_prefix_next_one(AF_INET, "255.255.255.0", 24, NULL);
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 128, "4400::0001");
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 120, "4400::0100");
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 127, "4400::0002");
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 8, "4500::");
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 7, "4600::");
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET6, "::", 128, "::1");
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, NULL);
|
||||
test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120, NULL);
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_nth_one(unsigned f, const char *before, unsigned pl, uint64_t nth, const char *after) {
|
||||
union in_addr_union ubefore, uafter, t;
|
||||
|
||||
assert_se(in_addr_from_string(f, before, &ubefore) >= 0);
|
||||
|
||||
t = ubefore;
|
||||
assert_se((in_addr_prefix_nth(f, &t, pl, nth) > 0) == !!after);
|
||||
|
||||
if (after) {
|
||||
assert_se(in_addr_from_string(f, after, &uafter) >= 0);
|
||||
assert_se(in_addr_equal(f, &t, &uafter) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_nth(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 0, "192.168.0.0");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 1, "192.168.1.0");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 4, "192.168.4.0");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 25, 1, "192.168.0.128");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 25, 1, "192.168.255.128");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 24, 0, "192.168.255.0");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "255.255.255.255", 32, 1, NULL);
|
||||
test_in_addr_prefix_nth_one(AF_INET, "255.255.255.255", 0, 1, NULL);
|
||||
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 8, 1, "4500::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 7, 1, "4600::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 1, "4400:0:0:1::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 2, "4400:0:0:2::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 0xbad, "4400:0:0:0bad::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400:0:0:ffff::", 64, 1, "4400:0:1::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 56, ((uint64_t)1<<48) -1, "44ff:ffff:ffff:ff00::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 255, "ff00::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 256, NULL);
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 1, NULL);
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0, 1, NULL);
|
||||
}
|
||||
|
||||
static void test_in_addr_to_string_one(int f, const char *addr) {
|
||||
union in_addr_union ua;
|
||||
_cleanup_free_ char *r = NULL;
|
||||
|
||||
assert_se(in_addr_from_string(f, addr, &ua) >= 0);
|
||||
assert_se(in_addr_to_string(f, &ua, &r) >= 0);
|
||||
printf("test_in_addr_to_string_one: %s == %s\n", addr, r);
|
||||
assert_se(streq(addr, r));
|
||||
}
|
||||
|
||||
static void test_in_addr_to_string(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_to_string_one(AF_INET, "192.168.0.1");
|
||||
test_in_addr_to_string_one(AF_INET, "10.11.12.13");
|
||||
test_in_addr_to_string_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
||||
test_in_addr_to_string_one(AF_INET6, "::1");
|
||||
test_in_addr_to_string_one(AF_INET6, "fe80::");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_in_addr_prefix_from_string();
|
||||
test_in_addr_random_prefix();
|
||||
test_in_addr_prefix_to_string();
|
||||
test_in_addr_is_null();
|
||||
test_in_addr_prefix_intersect();
|
||||
test_in_addr_prefix_next();
|
||||
test_in_addr_prefix_nth();
|
||||
test_in_addr_to_string();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "missing_network.h"
|
||||
#include "tests.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "string-util.h"
|
||||
@ -213,39 +214,136 @@ static void test_socket_address_is_netlink(void) {
|
||||
assert_se(!socket_address_is_netlink(&a, "route 1"));
|
||||
}
|
||||
|
||||
static void test_socket_addr_port_from_string_auto_one(const char *in, uint16_t port, int ret, int family, const char *expected) {
|
||||
_cleanup_free_ char *out = NULL;
|
||||
SocketAddress a;
|
||||
int r;
|
||||
static void test_in_addr_ifindex_to_string_one(int f, const char *a, int ifindex, const char *b) {
|
||||
_cleanup_free_ char *r = NULL;
|
||||
union in_addr_union ua, uuaa;
|
||||
int ff, ifindex2;
|
||||
|
||||
r = socket_addr_port_from_string_auto(in, port, &a);
|
||||
if (r >= 0)
|
||||
assert_se(sockaddr_pretty(&a.sockaddr.sa, a.size, false, true, &out) >= 0);
|
||||
assert_se(in_addr_from_string(f, a, &ua) >= 0);
|
||||
assert_se(in_addr_ifindex_to_string(f, &ua, ifindex, &r) >= 0);
|
||||
printf("test_in_addr_ifindex_to_string_one: %s == %s\n", b, r);
|
||||
assert_se(streq(b, r));
|
||||
|
||||
log_info("\"%s\" → %s → \"%s\" (expect \"%s\")", in,
|
||||
r >= 0 ? "✓" : "✗", empty_to_dash(out), r >= 0 ? expected ?: in : "-");
|
||||
assert_se(r == ret);
|
||||
if (r >= 0) {
|
||||
assert_se(a.sockaddr.sa.sa_family == family);
|
||||
assert_se(streq(out, expected ?: in));
|
||||
}
|
||||
assert_se(in_addr_ifindex_from_string_auto(b, &ff, &uuaa, &ifindex2) >= 0);
|
||||
assert_se(ff == f);
|
||||
assert_se(in_addr_equal(f, &ua, &uuaa));
|
||||
assert_se(ifindex2 == ifindex || ifindex2 == 0);
|
||||
}
|
||||
|
||||
static void test_socket_addr_port_from_string_auto(void) {
|
||||
static void test_in_addr_ifindex_to_string(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_socket_addr_port_from_string_auto_one("junk", 51, -EINVAL, 0, NULL);
|
||||
test_socket_addr_port_from_string_auto_one("192.168.1.1", 53, 0, AF_INET, "192.168.1.1:53");
|
||||
test_socket_addr_port_from_string_auto_one(".168.1.1", 53, -EINVAL, 0, NULL);
|
||||
test_socket_addr_port_from_string_auto_one("989.168.1.1", 53, -EINVAL, 0, NULL);
|
||||
test_in_addr_ifindex_to_string_one(AF_INET, "192.168.0.1", 7, "192.168.0.1");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET, "10.11.12.13", 9, "10.11.12.13");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 10, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "::1", 11, "::1");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::", 12, "fe80::%12");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::", 0, "fe80::");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::14", 12, "fe80::14%12");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::15", -7, "fe80::15");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::16", LOOPBACK_IFINDEX, "fe80::16%1");
|
||||
}
|
||||
|
||||
test_socket_addr_port_from_string_auto_one("[::1]", 53, -EINVAL, 0, NULL);
|
||||
test_socket_addr_port_from_string_auto_one("[::1]8888", 53, -EINVAL, 0, NULL);
|
||||
test_socket_addr_port_from_string_auto_one("2001:db8:3c4d:15::1a2f:1a2b", 53, 0, AF_INET6, "[2001:db8:3c4d:15::1a2f:1a2b]:53");
|
||||
test_socket_addr_port_from_string_auto_one("[2001:db8:3c4d:15::1a2f:1a2b]:2001", 53, 0, AF_INET6, "[2001:db8:3c4d:15::1a2f:1a2b]:2001");
|
||||
test_socket_addr_port_from_string_auto_one("[::1]:0", 53, -EINVAL, 0, NULL);
|
||||
test_socket_addr_port_from_string_auto_one("[::1]:65536", 53, -ERANGE, 0, NULL);
|
||||
test_socket_addr_port_from_string_auto_one("[a:b:1]:8888", 53, -EINVAL, 0, NULL);
|
||||
static void test_in_addr_ifindex_from_string_auto(void) {
|
||||
int family, ifindex;
|
||||
union in_addr_union ua;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
/* Most in_addr_ifindex_from_string_auto() invocations have already been tested above, but let's test some more */
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto("fe80::17", &family, &ua, &ifindex) >= 0);
|
||||
assert_se(family == AF_INET6);
|
||||
assert_se(ifindex == 0);
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto("fe80::18%19", &family, &ua, &ifindex) >= 0);
|
||||
assert_se(family == AF_INET6);
|
||||
assert_se(ifindex == 19);
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto("fe80::18%lo", &family, &ua, &ifindex) >= 0);
|
||||
assert_se(family == AF_INET6);
|
||||
assert_se(ifindex == LOOPBACK_IFINDEX);
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto("fe80::19%thisinterfacecantexist", &family, &ua, &ifindex) == -ENODEV);
|
||||
}
|
||||
|
||||
static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const char *expected) {
|
||||
int family, ifindex;
|
||||
union in_addr_union ua;
|
||||
_cleanup_free_ char *server_name = NULL;
|
||||
|
||||
assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0);
|
||||
assert_se(streq_ptr(server_name, expected));
|
||||
}
|
||||
|
||||
static void test_in_addr_ifindex_name_from_string_auto(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL);
|
||||
test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com");
|
||||
test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL);
|
||||
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_in_addr_port_from_string_auto_one(const char *str, int family, const char *address_string, uint16_t port) {
|
||||
union in_addr_union a, b;
|
||||
uint16_t p;
|
||||
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[]) {
|
||||
@ -257,7 +355,12 @@ int main(int argc, char *argv[]) {
|
||||
test_socket_address_get_path();
|
||||
test_socket_address_is();
|
||||
test_socket_address_is_netlink();
|
||||
test_socket_addr_port_from_string_auto();
|
||||
|
||||
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_in_addr_port_from_string_auto();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -15,9 +15,7 @@
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "missing_network.h"
|
||||
#include "process-util.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
@ -84,256 +82,6 @@ static void test_socket_print_unix(void) {
|
||||
test_socket_print_unix_one("\0\a\b\n\255", 6, "@\\a\\b\\n\\255\\000");
|
||||
}
|
||||
|
||||
static void test_in_addr_is_null(void) {
|
||||
union in_addr_union i = {};
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(in_addr_is_null(AF_INET, &i) == true);
|
||||
assert_se(in_addr_is_null(AF_INET6, &i) == true);
|
||||
|
||||
i.in.s_addr = 0x1000000;
|
||||
assert_se(in_addr_is_null(AF_INET, &i) == false);
|
||||
assert_se(in_addr_is_null(AF_INET6, &i) == false);
|
||||
|
||||
assert_se(in_addr_is_null(-1, &i) == -EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_intersect_one(unsigned f, const char *a, unsigned apl, const char *b, unsigned bpl, int result) {
|
||||
union in_addr_union ua, ub;
|
||||
|
||||
assert_se(in_addr_from_string(f, a, &ua) >= 0);
|
||||
assert_se(in_addr_from_string(f, b, &ub) >= 0);
|
||||
|
||||
assert_se(in_addr_prefix_intersect(f, &ua, apl, &ub, bpl) == result);
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_intersect(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "255.255.255.255", 32, "255.255.255.254", 32, 0);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "255.255.255.255", 0, "255.255.255.255", 32, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "0.0.0.0", 0, "47.11.8.15", 32, 1);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.1", 24, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "2.2.2.2", 24, "1.1.1.1", 24, 0);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.127", 25, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 24, "1.1.1.127", 26, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 25, "1.1.1.127", 25, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET, "1.1.1.1", 25, "1.1.1.255", 25, 0);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", 128, 0);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "::", 0, "beef:beef:beef:beef:beef:beef:beef:beef", 128, 1);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::2", 64, "1::2", 64, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "2::2", 64, "1::2", 64, 0);
|
||||
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 120, "1::007f", 121, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 120, "1::007f", 122, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 121, "1::007f", 121, 1);
|
||||
test_in_addr_prefix_intersect_one(AF_INET6, "1::1", 121, "1::00ff", 121, 0);
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_next_one(unsigned f, const char *before, unsigned pl, const char *after) {
|
||||
union in_addr_union ubefore, uafter, t;
|
||||
|
||||
assert_se(in_addr_from_string(f, before, &ubefore) >= 0);
|
||||
|
||||
t = ubefore;
|
||||
assert_se((in_addr_prefix_next(f, &t, pl) > 0) == !!after);
|
||||
|
||||
if (after) {
|
||||
assert_se(in_addr_from_string(f, after, &uafter) >= 0);
|
||||
assert_se(in_addr_equal(f, &t, &uafter) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_next(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 24, "192.168.1.0");
|
||||
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 16, "192.169.0.0");
|
||||
test_in_addr_prefix_next_one(AF_INET, "192.168.0.0", 20, "192.168.16.0");
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET, "0.0.0.0", 32, "0.0.0.1");
|
||||
test_in_addr_prefix_next_one(AF_INET, "255.255.255.255", 32, NULL);
|
||||
test_in_addr_prefix_next_one(AF_INET, "255.255.255.0", 24, NULL);
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 128, "4400::0001");
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 120, "4400::0100");
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 127, "4400::0002");
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 8, "4500::");
|
||||
test_in_addr_prefix_next_one(AF_INET6, "4400::", 7, "4600::");
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET6, "::", 128, "::1");
|
||||
|
||||
test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, NULL);
|
||||
test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120, NULL);
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_nth_one(unsigned f, const char *before, unsigned pl, uint64_t nth, const char *after) {
|
||||
union in_addr_union ubefore, uafter, t;
|
||||
|
||||
assert_se(in_addr_from_string(f, before, &ubefore) >= 0);
|
||||
|
||||
t = ubefore;
|
||||
assert_se((in_addr_prefix_nth(f, &t, pl, nth) > 0) == !!after);
|
||||
|
||||
if (after) {
|
||||
assert_se(in_addr_from_string(f, after, &uafter) >= 0);
|
||||
assert_se(in_addr_equal(f, &t, &uafter) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_in_addr_prefix_nth(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 0, "192.168.0.0");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 1, "192.168.1.0");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 4, "192.168.4.0");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 25, 1, "192.168.0.128");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 25, 1, "192.168.255.128");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 24, 0, "192.168.255.0");
|
||||
test_in_addr_prefix_nth_one(AF_INET, "255.255.255.255", 32, 1, NULL);
|
||||
test_in_addr_prefix_nth_one(AF_INET, "255.255.255.255", 0, 1, NULL);
|
||||
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 8, 1, "4500::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 7, 1, "4600::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 1, "4400:0:0:1::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 2, "4400:0:0:2::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 0xbad, "4400:0:0:0bad::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400:0:0:ffff::", 64, 1, "4400:0:1::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "4400::", 56, ((uint64_t)1<<48) -1, "44ff:ffff:ffff:ff00::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 255, "ff00::");
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 256, NULL);
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 1, NULL);
|
||||
test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0, 1, NULL);
|
||||
}
|
||||
|
||||
static void test_in_addr_to_string_one(int f, const char *addr) {
|
||||
union in_addr_union ua;
|
||||
_cleanup_free_ char *r = NULL;
|
||||
|
||||
assert_se(in_addr_from_string(f, addr, &ua) >= 0);
|
||||
assert_se(in_addr_to_string(f, &ua, &r) >= 0);
|
||||
printf("test_in_addr_to_string_one: %s == %s\n", addr, r);
|
||||
assert_se(streq(addr, r));
|
||||
}
|
||||
|
||||
static void test_in_addr_to_string(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_to_string_one(AF_INET, "192.168.0.1");
|
||||
test_in_addr_to_string_one(AF_INET, "10.11.12.13");
|
||||
test_in_addr_to_string_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
||||
test_in_addr_to_string_one(AF_INET6, "::1");
|
||||
test_in_addr_to_string_one(AF_INET6, "fe80::");
|
||||
}
|
||||
|
||||
static void test_in_addr_ifindex_to_string_one(int f, const char *a, int ifindex, const char *b) {
|
||||
_cleanup_free_ char *r = NULL;
|
||||
union in_addr_union ua, uuaa;
|
||||
int ff, ifindex2;
|
||||
|
||||
assert_se(in_addr_from_string(f, a, &ua) >= 0);
|
||||
assert_se(in_addr_ifindex_to_string(f, &ua, ifindex, &r) >= 0);
|
||||
printf("test_in_addr_ifindex_to_string_one: %s == %s\n", b, r);
|
||||
assert_se(streq(b, r));
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto(b, &ff, &uuaa, &ifindex2) >= 0);
|
||||
assert_se(ff == f);
|
||||
assert_se(in_addr_equal(f, &ua, &uuaa));
|
||||
assert_se(ifindex2 == ifindex || ifindex2 == 0);
|
||||
}
|
||||
|
||||
static void test_in_addr_ifindex_to_string(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_ifindex_to_string_one(AF_INET, "192.168.0.1", 7, "192.168.0.1");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET, "10.11.12.13", 9, "10.11.12.13");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 10, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "::1", 11, "::1");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::", 12, "fe80::%12");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::", 0, "fe80::");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::14", 12, "fe80::14%12");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::15", -7, "fe80::15");
|
||||
test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::16", LOOPBACK_IFINDEX, "fe80::16%1");
|
||||
}
|
||||
|
||||
static void test_in_addr_ifindex_from_string_auto(void) {
|
||||
int family, ifindex;
|
||||
union in_addr_union ua;
|
||||
|
||||
log_info("/* %s */", __func__);
|
||||
/* Most in_addr_ifindex_from_string_auto() invocations have already been tested above, but let's test some more */
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto("fe80::17", &family, &ua, &ifindex) >= 0);
|
||||
assert_se(family == AF_INET6);
|
||||
assert_se(ifindex == 0);
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto("fe80::18%19", &family, &ua, &ifindex) >= 0);
|
||||
assert_se(family == AF_INET6);
|
||||
assert_se(ifindex == 19);
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto("fe80::18%lo", &family, &ua, &ifindex) >= 0);
|
||||
assert_se(family == AF_INET6);
|
||||
assert_se(ifindex == LOOPBACK_IFINDEX);
|
||||
|
||||
assert_se(in_addr_ifindex_from_string_auto("fe80::19%thisinterfacecantexist", &family, &ua, &ifindex) == -ENODEV);
|
||||
}
|
||||
|
||||
static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const char *expected) {
|
||||
int family, ifindex;
|
||||
union in_addr_union ua;
|
||||
_cleanup_free_ char *server_name = NULL;
|
||||
|
||||
assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0);
|
||||
assert_se(streq_ptr(server_name, expected));
|
||||
}
|
||||
|
||||
static void test_in_addr_ifindex_name_from_string_auto(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL);
|
||||
test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com");
|
||||
test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL);
|
||||
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,
|
||||
@ -759,16 +507,6 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
test_socket_print_unix();
|
||||
|
||||
test_in_addr_is_null();
|
||||
test_in_addr_prefix_intersect();
|
||||
test_in_addr_prefix_next();
|
||||
test_in_addr_prefix_nth();
|
||||
test_in_addr_to_string();
|
||||
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();
|
||||
|
||||
test_sockaddr_un_len();
|
||||
|
Loading…
Reference in New Issue
Block a user