Systemd/src/resolve/resolved-llmnr.c

451 lines
17 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <netinet/in.h>
#include <resolv.h>
#include "errno-util.h"
#include "fd-util.h"
#include "resolved-llmnr.h"
#include "resolved-manager.h"
void manager_llmnr_stop(Manager *m) {
assert(m);
m->llmnr_ipv4_udp_event_source = sd_event_source_unref(m->llmnr_ipv4_udp_event_source);
m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
m->llmnr_ipv6_udp_event_source = sd_event_source_unref(m->llmnr_ipv6_udp_event_source);
m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
m->llmnr_ipv4_tcp_event_source = sd_event_source_unref(m->llmnr_ipv4_tcp_event_source);
m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
m->llmnr_ipv6_tcp_event_source = sd_event_source_unref(m->llmnr_ipv6_tcp_event_source);
m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
}
int manager_llmnr_start(Manager *m) {
int r;
assert(m);
if (m->llmnr_support == RESOLVE_SUPPORT_NO)
return 0;
r = manager_llmnr_ipv4_udp_fd(m);
if (r == -EADDRINUSE)
goto eaddrinuse;
if (r < 0)
return r;
r = manager_llmnr_ipv4_tcp_fd(m);
if (r == -EADDRINUSE)
goto eaddrinuse;
if (r < 0)
return r;
if (socket_ipv6_is_supported()) {
r = manager_llmnr_ipv6_udp_fd(m);
if (r == -EADDRINUSE)
goto eaddrinuse;
if (r < 0)
return r;
r = manager_llmnr_ipv6_tcp_fd(m);
if (r == -EADDRINUSE)
goto eaddrinuse;
if (r < 0)
return r;
}
return 0;
eaddrinuse:
log_warning("Another LLMNR responder prohibits binding the socket to the same port. Turning off LLMNR support.");
m->llmnr_support = RESOLVE_SUPPORT_NO;
manager_llmnr_stop(m);
return 0;
}
static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsTransaction *t = NULL;
Manager *m = userdata;
DnsScope *scope;
int r;
resolved: respond to local resolver requests on 127.0.0.53:53 In order to improve compatibility with local clients that speak DNS directly (and do not use NSS or our bus API) listen locally on 127.0.0.53:53 and process any queries made that way. Note that resolved does not implement a full DNS server on this port, but simply enough to allow normal, local clients to resolve RRs through resolved. Specifically it does not implement queries without the RD bit set (these are requests where recursive lookups are explicitly disabled), and neither queries with DNSSEC DO set in combination with DNSSEC CD (i.e. DNSSEC lookups with validation turned off). It also refuses zone transfers and obsolete RR types. All lookups done this way will be rejected with a clean error code, so that the client side can repeat the query with a reduced feature set. The code will set the DNSSEC AD flag however, depending on whether the data resolved has been validated (or comes from a local, trusted source). Lookups made via this mechanisms are propagated to LLMNR and mDNS as necessary, but this is only partially useful as DNS packets cannot carry IP scope data (i.e. the ifindex), and hence link-local addresses returned cannot be used properly (and given that LLMNR/mDNS are mostly about link-local communication this is quite a limitation). Also, given that DNS tends to use IDNA for non-ASCII names, while LLMNR/mDNS uses UTF-8 lookups cannot be mapped 1:1. In general this should improve compatibility with clients bypassing NSS but it is highly recommended for clients to instead use NSS or our native bus API. This patch also beefs up the DnsStream logic, as it reuses the code for local TCP listening. DnsStream now provides proper reference counting for its objects. In order to avoid feedback loops resolved will no silently ignore 127.0.0.53 specified as DNS server when reading configuration. resolved listens on 127.0.0.53:53 instead of 127.0.0.1:53 in order to leave the latter free for local, external DNS servers or forwarders. This also changes the "etc.conf" tmpfiles snippet to create a symlink from /etc/resolv.conf to /usr/lib/systemd/resolv.conf by default, thus making this stub the default mode of operation if /etc is not populated.
2016-06-21 00:58:47 +02:00
assert(s);
assert(fd >= 0);
assert(m);
r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
if (r <= 0)
return r;
if (manager_our_packet(m, p))
return 0;
scope = manager_find_scope(m, p);
if (!scope) {
log_debug("Got LLMNR UDP packet on unknown scope. Ignoring.");
return 0;
}
if (dns_packet_validate_reply(p) > 0) {
resolved: respond to local resolver requests on 127.0.0.53:53 In order to improve compatibility with local clients that speak DNS directly (and do not use NSS or our bus API) listen locally on 127.0.0.53:53 and process any queries made that way. Note that resolved does not implement a full DNS server on this port, but simply enough to allow normal, local clients to resolve RRs through resolved. Specifically it does not implement queries without the RD bit set (these are requests where recursive lookups are explicitly disabled), and neither queries with DNSSEC DO set in combination with DNSSEC CD (i.e. DNSSEC lookups with validation turned off). It also refuses zone transfers and obsolete RR types. All lookups done this way will be rejected with a clean error code, so that the client side can repeat the query with a reduced feature set. The code will set the DNSSEC AD flag however, depending on whether the data resolved has been validated (or comes from a local, trusted source). Lookups made via this mechanisms are propagated to LLMNR and mDNS as necessary, but this is only partially useful as DNS packets cannot carry IP scope data (i.e. the ifindex), and hence link-local addresses returned cannot be used properly (and given that LLMNR/mDNS are mostly about link-local communication this is quite a limitation). Also, given that DNS tends to use IDNA for non-ASCII names, while LLMNR/mDNS uses UTF-8 lookups cannot be mapped 1:1. In general this should improve compatibility with clients bypassing NSS but it is highly recommended for clients to instead use NSS or our native bus API. This patch also beefs up the DnsStream logic, as it reuses the code for local TCP listening. DnsStream now provides proper reference counting for its objects. In order to avoid feedback loops resolved will no silently ignore 127.0.0.53 specified as DNS server when reading configuration. resolved listens on 127.0.0.53:53 instead of 127.0.0.1:53 in order to leave the latter free for local, external DNS servers or forwarders. This also changes the "etc.conf" tmpfiles snippet to create a symlink from /etc/resolv.conf to /usr/lib/systemd/resolv.conf by default, thus making this stub the default mode of operation if /etc is not populated.
2016-06-21 00:58:47 +02:00
log_debug("Got LLMNR UDP reply packet for id %u", DNS_PACKET_ID(p));
dns_scope_check_conflicts(scope, p);
t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
if (t)
dns_transaction_process_reply(t, p);
} else if (dns_packet_validate_query(p) > 0) {
resolved: respond to local resolver requests on 127.0.0.53:53 In order to improve compatibility with local clients that speak DNS directly (and do not use NSS or our bus API) listen locally on 127.0.0.53:53 and process any queries made that way. Note that resolved does not implement a full DNS server on this port, but simply enough to allow normal, local clients to resolve RRs through resolved. Specifically it does not implement queries without the RD bit set (these are requests where recursive lookups are explicitly disabled), and neither queries with DNSSEC DO set in combination with DNSSEC CD (i.e. DNSSEC lookups with validation turned off). It also refuses zone transfers and obsolete RR types. All lookups done this way will be rejected with a clean error code, so that the client side can repeat the query with a reduced feature set. The code will set the DNSSEC AD flag however, depending on whether the data resolved has been validated (or comes from a local, trusted source). Lookups made via this mechanisms are propagated to LLMNR and mDNS as necessary, but this is only partially useful as DNS packets cannot carry IP scope data (i.e. the ifindex), and hence link-local addresses returned cannot be used properly (and given that LLMNR/mDNS are mostly about link-local communication this is quite a limitation). Also, given that DNS tends to use IDNA for non-ASCII names, while LLMNR/mDNS uses UTF-8 lookups cannot be mapped 1:1. In general this should improve compatibility with clients bypassing NSS but it is highly recommended for clients to instead use NSS or our native bus API. This patch also beefs up the DnsStream logic, as it reuses the code for local TCP listening. DnsStream now provides proper reference counting for its objects. In order to avoid feedback loops resolved will no silently ignore 127.0.0.53 specified as DNS server when reading configuration. resolved listens on 127.0.0.53:53 instead of 127.0.0.1:53 in order to leave the latter free for local, external DNS servers or forwarders. This also changes the "etc.conf" tmpfiles snippet to create a symlink from /etc/resolv.conf to /usr/lib/systemd/resolv.conf by default, thus making this stub the default mode of operation if /etc is not populated.
2016-06-21 00:58:47 +02:00
log_debug("Got LLMNR UDP query packet for id %u", DNS_PACKET_ID(p));
dns_scope_process_query(scope, NULL, p);
} else
log_debug("Invalid LLMNR UDP packet, ignoring.");
return 0;
}
int manager_llmnr_ipv4_udp_fd(Manager *m) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(LLMNR_PORT),
};
2018-10-18 22:58:03 +02:00
_cleanup_close_ int s = -1;
int r;
assert(m);
if (m->llmnr_ipv4_udp_fd >= 0)
return m->llmnr_ipv4_udp_fd;
2018-10-18 22:58:03 +02:00
s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s < 0)
return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to create socket: %m");
/* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_TTL, 255);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_TTL: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_TTL, 255);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_TTL: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_LOOP, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_LOOP: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_PKTINFO: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_RECVTTL: %m");
/* Disable Don't-Fragment bit in the IP header */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MTU_DISCOVER: %m");
/* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
2018-10-18 22:58:03 +02:00
r = bind(s, &sa.sa, sizeof(sa.in));
if (r < 0) {
2018-10-18 22:58:03 +02:00
if (errno != EADDRINUSE)
return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m");
log_warning("LLMNR-IPv4(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
r = bind(s, &sa.sa, sizeof(sa.in));
if (r < 0)
return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
}
2018-10-18 22:58:03 +02:00
r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, s, EPOLLIN, on_llmnr_packet, m);
if (r < 0)
2018-10-18 22:58:03 +02:00
return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to create event source: %m");
(void) sd_event_source_set_description(m->llmnr_ipv4_udp_event_source, "llmnr-ipv4-udp");
2018-10-18 22:58:03 +02:00
return m->llmnr_ipv4_udp_fd = TAKE_FD(s);
}
int manager_llmnr_ipv6_udp_fd(Manager *m) {
union sockaddr_union sa = {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(LLMNR_PORT),
};
2018-10-18 22:58:03 +02:00
_cleanup_close_ int s = -1;
int r;
assert(m);
if (m->llmnr_ipv6_udp_fd >= 0)
return m->llmnr_ipv6_udp_fd;
2018-10-18 22:58:03 +02:00
s = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s < 0)
return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to create socket: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_UNICAST_HOPS: %m");
/* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_HOPS: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_LOOP: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_V6ONLY: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVPKTINFO: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVHOPLIMIT: %m");
/* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
2018-10-18 22:58:03 +02:00
r = bind(s, &sa.sa, sizeof(sa.in6));
if (r < 0) {
2018-10-18 22:58:03 +02:00
if (errno != EADDRINUSE)
return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m");
log_warning("LLMNR-IPv6(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
r = bind(s, &sa.sa, sizeof(sa.in6));
if (r < 0)
return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
}
2018-10-18 22:58:03 +02:00
r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, s, EPOLLIN, on_llmnr_packet, m);
2016-01-08 02:19:43 +01:00
if (r < 0)
2018-10-18 22:58:03 +02:00
return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to create event source: %m");
(void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp");
2018-10-18 22:58:03 +02:00
return m->llmnr_ipv6_udp_fd = TAKE_FD(s);
}
static int on_llmnr_stream_packet(DnsStream *s) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsScope *scope;
assert(s);
p = dns_stream_take_read_packet(s);
assert(p);
scope = manager_find_scope(s->manager, p);
resolved: respond to local resolver requests on 127.0.0.53:53 In order to improve compatibility with local clients that speak DNS directly (and do not use NSS or our bus API) listen locally on 127.0.0.53:53 and process any queries made that way. Note that resolved does not implement a full DNS server on this port, but simply enough to allow normal, local clients to resolve RRs through resolved. Specifically it does not implement queries without the RD bit set (these are requests where recursive lookups are explicitly disabled), and neither queries with DNSSEC DO set in combination with DNSSEC CD (i.e. DNSSEC lookups with validation turned off). It also refuses zone transfers and obsolete RR types. All lookups done this way will be rejected with a clean error code, so that the client side can repeat the query with a reduced feature set. The code will set the DNSSEC AD flag however, depending on whether the data resolved has been validated (or comes from a local, trusted source). Lookups made via this mechanisms are propagated to LLMNR and mDNS as necessary, but this is only partially useful as DNS packets cannot carry IP scope data (i.e. the ifindex), and hence link-local addresses returned cannot be used properly (and given that LLMNR/mDNS are mostly about link-local communication this is quite a limitation). Also, given that DNS tends to use IDNA for non-ASCII names, while LLMNR/mDNS uses UTF-8 lookups cannot be mapped 1:1. In general this should improve compatibility with clients bypassing NSS but it is highly recommended for clients to instead use NSS or our native bus API. This patch also beefs up the DnsStream logic, as it reuses the code for local TCP listening. DnsStream now provides proper reference counting for its objects. In order to avoid feedback loops resolved will no silently ignore 127.0.0.53 specified as DNS server when reading configuration. resolved listens on 127.0.0.53:53 instead of 127.0.0.1:53 in order to leave the latter free for local, external DNS servers or forwarders. This also changes the "etc.conf" tmpfiles snippet to create a symlink from /etc/resolv.conf to /usr/lib/systemd/resolv.conf by default, thus making this stub the default mode of operation if /etc is not populated.
2016-06-21 00:58:47 +02:00
if (!scope)
log_debug("Got LLMNR TCP packet on unknown scope. Ignoring.");
else if (dns_packet_validate_query(p) > 0) {
log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(p));
dns_scope_process_query(scope, s, p);
} else
resolved: respond to local resolver requests on 127.0.0.53:53 In order to improve compatibility with local clients that speak DNS directly (and do not use NSS or our bus API) listen locally on 127.0.0.53:53 and process any queries made that way. Note that resolved does not implement a full DNS server on this port, but simply enough to allow normal, local clients to resolve RRs through resolved. Specifically it does not implement queries without the RD bit set (these are requests where recursive lookups are explicitly disabled), and neither queries with DNSSEC DO set in combination with DNSSEC CD (i.e. DNSSEC lookups with validation turned off). It also refuses zone transfers and obsolete RR types. All lookups done this way will be rejected with a clean error code, so that the client side can repeat the query with a reduced feature set. The code will set the DNSSEC AD flag however, depending on whether the data resolved has been validated (or comes from a local, trusted source). Lookups made via this mechanisms are propagated to LLMNR and mDNS as necessary, but this is only partially useful as DNS packets cannot carry IP scope data (i.e. the ifindex), and hence link-local addresses returned cannot be used properly (and given that LLMNR/mDNS are mostly about link-local communication this is quite a limitation). Also, given that DNS tends to use IDNA for non-ASCII names, while LLMNR/mDNS uses UTF-8 lookups cannot be mapped 1:1. In general this should improve compatibility with clients bypassing NSS but it is highly recommended for clients to instead use NSS or our native bus API. This patch also beefs up the DnsStream logic, as it reuses the code for local TCP listening. DnsStream now provides proper reference counting for its objects. In order to avoid feedback loops resolved will no silently ignore 127.0.0.53 specified as DNS server when reading configuration. resolved listens on 127.0.0.53:53 instead of 127.0.0.1:53 in order to leave the latter free for local, external DNS servers or forwarders. This also changes the "etc.conf" tmpfiles snippet to create a symlink from /etc/resolv.conf to /usr/lib/systemd/resolv.conf by default, thus making this stub the default mode of operation if /etc is not populated.
2016-06-21 00:58:47 +02:00
log_debug("Invalid LLMNR TCP packet, ignoring.");
resolved: respond to local resolver requests on 127.0.0.53:53 In order to improve compatibility with local clients that speak DNS directly (and do not use NSS or our bus API) listen locally on 127.0.0.53:53 and process any queries made that way. Note that resolved does not implement a full DNS server on this port, but simply enough to allow normal, local clients to resolve RRs through resolved. Specifically it does not implement queries without the RD bit set (these are requests where recursive lookups are explicitly disabled), and neither queries with DNSSEC DO set in combination with DNSSEC CD (i.e. DNSSEC lookups with validation turned off). It also refuses zone transfers and obsolete RR types. All lookups done this way will be rejected with a clean error code, so that the client side can repeat the query with a reduced feature set. The code will set the DNSSEC AD flag however, depending on whether the data resolved has been validated (or comes from a local, trusted source). Lookups made via this mechanisms are propagated to LLMNR and mDNS as necessary, but this is only partially useful as DNS packets cannot carry IP scope data (i.e. the ifindex), and hence link-local addresses returned cannot be used properly (and given that LLMNR/mDNS are mostly about link-local communication this is quite a limitation). Also, given that DNS tends to use IDNA for non-ASCII names, while LLMNR/mDNS uses UTF-8 lookups cannot be mapped 1:1. In general this should improve compatibility with clients bypassing NSS but it is highly recommended for clients to instead use NSS or our native bus API. This patch also beefs up the DnsStream logic, as it reuses the code for local TCP listening. DnsStream now provides proper reference counting for its objects. In order to avoid feedback loops resolved will no silently ignore 127.0.0.53 specified as DNS server when reading configuration. resolved listens on 127.0.0.53:53 instead of 127.0.0.1:53 in order to leave the latter free for local, external DNS servers or forwarders. This also changes the "etc.conf" tmpfiles snippet to create a symlink from /etc/resolv.conf to /usr/lib/systemd/resolv.conf by default, thus making this stub the default mode of operation if /etc is not populated.
2016-06-21 00:58:47 +02:00
dns_stream_unref(s);
return 0;
}
static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
DnsStream *stream;
Manager *m = userdata;
int cfd, r;
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (cfd < 0) {
if (ERRNO_IS_ACCEPT_AGAIN(errno))
return 0;
return -errno;
}
r = dns_stream_new(m, &stream, DNS_STREAM_LLMNR_RECV, DNS_PROTOCOL_LLMNR, cfd, NULL);
if (r < 0) {
safe_close(cfd);
return r;
}
stream->on_packet = on_llmnr_stream_packet;
/* We don't configure a "complete" handler here, we rely on the default handler than simply drops the
* reference to the stream, thus freeing it */
return 0;
}
int manager_llmnr_ipv4_tcp_fd(Manager *m) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(LLMNR_PORT),
};
2018-10-18 22:58:03 +02:00
_cleanup_close_ int s = -1;
int r;
assert(m);
if (m->llmnr_ipv4_tcp_fd >= 0)
return m->llmnr_ipv4_tcp_fd;
2018-10-18 22:58:03 +02:00
s = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s < 0)
return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to create socket: %m");
/* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
r = setsockopt_int(s, IPPROTO_IP, IP_TTL, 1);
2018-10-18 22:58:03 +02:00
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_TTL: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_PKTINFO: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_RECVTTL: %m");
/* Disable Don't-Fragment bit in the IP header */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_MTU_DISCOVER: %m");
/* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
2018-10-18 22:58:03 +02:00
r = bind(s, &sa.sa, sizeof(sa.in));
if (r < 0) {
2018-10-18 22:58:03 +02:00
if (errno != EADDRINUSE)
return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
log_warning("LLMNR-IPv4(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
r = bind(s, &sa.sa, sizeof(sa.in));
if (r < 0)
return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
}
2018-10-18 22:58:03 +02:00
r = listen(s, SOMAXCONN);
if (r < 0)
return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to listen the stream: %m");
2018-10-18 22:58:03 +02:00
r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m);
if (r < 0)
2018-10-18 22:58:03 +02:00
return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to create event source: %m");
(void) sd_event_source_set_description(m->llmnr_ipv4_tcp_event_source, "llmnr-ipv4-tcp");
2018-10-18 22:58:03 +02:00
return m->llmnr_ipv4_tcp_fd = TAKE_FD(s);
}
int manager_llmnr_ipv6_tcp_fd(Manager *m) {
union sockaddr_union sa = {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(LLMNR_PORT),
};
2018-10-18 22:58:03 +02:00
_cleanup_close_ int s = -1;
int r;
assert(m);
if (m->llmnr_ipv6_tcp_fd >= 0)
return m->llmnr_ipv6_tcp_fd;
2018-10-18 22:58:03 +02:00
s = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (s < 0)
return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to create socket: %m");
/* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 1);
2018-10-18 22:58:03 +02:00
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_UNICAST_HOPS: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_V6ONLY: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVPKTINFO: %m");
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVHOPLIMIT: %m");
/* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
2018-10-18 22:58:03 +02:00
r = bind(s, &sa.sa, sizeof(sa.in6));
if (r < 0) {
2018-10-18 22:58:03 +02:00
if (errno != EADDRINUSE)
return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
log_warning("LLMNR-IPv6(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
/* try again with SO_REUSEADDR */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
r = bind(s, &sa.sa, sizeof(sa.in6));
if (r < 0)
return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
} else {
/* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
2018-10-18 22:58:03 +02:00
r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
if (r < 0)
return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
}
2018-10-18 22:58:03 +02:00
r = listen(s, SOMAXCONN);
if (r < 0)
return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to listen the stream: %m");
2018-10-18 22:58:03 +02:00
r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m);
if (r < 0)
2018-10-18 22:58:03 +02:00
return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to create event source: %m");
(void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp");
2018-10-18 22:58:03 +02:00
return m->llmnr_ipv6_tcp_fd = TAKE_FD(s);
}