X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fresolve%2Fresolved-manager.c;h=f8d4db7aada58080d5168485e485adab83721d59;hb=0b58db658b5c3f586ac3a837427f1f7fec2abb2e;hp=f4fa197d49ff83e87beb575488a10c2c41c445c2;hpb=6073b6f26ab9fc6bf335faa7073ec443eef093fd;p=thirdparty%2Fsystemd.git diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index f4fa197d49f..c3c61a0893c 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -19,24 +19,35 @@ along with systemd; If not, see . ***/ -#include -#include -#include -#include -#include #include +#include +#include -#include "rtnl-util.h" -#include "event-util.h" -#include "network-util.h" +#include "af-list.h" +#include "alloc-util.h" +#include "dns-domain.h" +#include "fd-util.h" +#include "fileio-label.h" +#include "hostname-util.h" +#include "io-util.h" +#include "netlink-util.h" #include "network-internal.h" -#include "conf-parser.h" +#include "ordered-set.h" +#include "parse-util.h" +#include "random-util.h" +#include "resolved-bus.h" +#include "resolved-conf.h" +#include "resolved-llmnr.h" +#include "resolved-manager.h" +#include "resolved-resolv-conf.h" #include "socket-util.h" -#include "resolved.h" +#include "string-table.h" +#include "string-util.h" +#include "utf8.h" #define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC) -static int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userdata) { +static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { Manager *m = userdata; uint16_t type; Link *l; @@ -46,7 +57,7 @@ static int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userda assert(m); assert(mm); - r = sd_rtnl_message_get_type(mm, &type); + r = sd_netlink_message_get_type(mm, &type); if (r < 0) goto fail; @@ -58,10 +69,10 @@ static int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userda switch (type) { - case RTM_NEWLINK: - if (!l) { - log_debug("Found link %i", ifindex); + case RTM_NEWLINK:{ + bool is_new = !l; + if (!l) { r = link_new(m, &l, ifindex); if (r < 0) goto fail; @@ -71,11 +82,19 @@ static int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userda if (r < 0) goto fail; + r = link_update_monitor(l); + if (r < 0) + goto fail; + + if (is_new) + log_debug("Found new link %i/%s", ifindex, l->name); + break; + } case RTM_DELLINK: if (l) { - log_debug("Removing link %i", l->ifindex); + log_debug("Removing link %i/%s", l->ifindex, l->name); link_free(l); } @@ -85,11 +104,11 @@ static int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userda return 0; fail: - log_warning("Failed to process RTNL link message: %s", strerror(-r)); + log_warning_errno(r, "Failed to process RTNL link message: %m"); return 0; } -static int manager_process_address(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userdata) { +static int manager_process_address(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { Manager *m = userdata; union in_addr_union address; uint16_t type; @@ -101,7 +120,7 @@ static int manager_process_address(sd_rtnl *rtnl, sd_rtnl_message *mm, void *use assert(mm); assert(m); - r = sd_rtnl_message_get_type(mm, &type); + r = sd_netlink_message_get_type(mm, &type); if (r < 0) goto fail; @@ -120,9 +139,9 @@ static int manager_process_address(sd_rtnl *rtnl, sd_rtnl_message *mm, void *use switch (family) { case AF_INET: - r = sd_rtnl_message_read_in_addr(mm, IFA_LOCAL, &address.in); + r = sd_netlink_message_read_in_addr(mm, IFA_LOCAL, &address.in); if (r < 0) { - r = sd_rtnl_message_read_in_addr(mm, IFA_ADDRESS, &address.in); + r = sd_netlink_message_read_in_addr(mm, IFA_ADDRESS, &address.in); if (r < 0) goto fail; } @@ -130,9 +149,9 @@ static int manager_process_address(sd_rtnl *rtnl, sd_rtnl_message *mm, void *use break; case AF_INET6: - r = sd_rtnl_message_read_in6_addr(mm, IFA_LOCAL, &address.in6); + r = sd_netlink_message_read_in6_addr(mm, IFA_LOCAL, &address.in6); if (r < 0) { - r = sd_rtnl_message_read_in6_addr(mm, IFA_ADDRESS, &address.in6); + r = sd_netlink_message_read_in6_addr(mm, IFA_ADDRESS, &address.in6); if (r < 0) goto fail; } @@ -162,48 +181,46 @@ static int manager_process_address(sd_rtnl *rtnl, sd_rtnl_message *mm, void *use break; case RTM_DELADDR: - if (a) - link_address_free(a); + link_address_free(a); break; } return 0; fail: - log_warning("Failed to process RTNL address message: %s", strerror(-r)); + log_warning_errno(r, "Failed to process RTNL address message: %m"); return 0; } - static int manager_rtnl_listen(Manager *m) { - _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL; - sd_rtnl_message *i; + _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *i; int r; assert(m); - /* First, subscibe to interfaces coming and going */ - r = sd_rtnl_open(&m->rtnl, 3, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR); + /* First, subscribe to interfaces coming and going */ + r = sd_netlink_open(&m->rtnl); if (r < 0) return r; - r = sd_rtnl_attach_event(m->rtnl, m->event, 0); + r = sd_netlink_attach_event(m->rtnl, m->event, 0); if (r < 0) return r; - r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m); + r = sd_netlink_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m); if (r < 0) return r; - r = sd_rtnl_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m); + r = sd_netlink_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m); if (r < 0) return r; - r = sd_rtnl_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m); + r = sd_netlink_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m); if (r < 0) return r; - r = sd_rtnl_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m); + r = sd_netlink_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m); if (r < 0) return r; @@ -212,37 +229,37 @@ static int manager_rtnl_listen(Manager *m) { if (r < 0) return r; - r = sd_rtnl_message_request_dump(req, true); + r = sd_netlink_message_request_dump(req, true); if (r < 0) return r; - r = sd_rtnl_call(m->rtnl, req, 0, &reply); + r = sd_netlink_call(m->rtnl, req, 0, &reply); if (r < 0) return r; - for (i = reply; i; i = sd_rtnl_message_next(i)) { + for (i = reply; i; i = sd_netlink_message_next(i)) { r = manager_process_link(m->rtnl, i, m); if (r < 0) return r; } - req = sd_rtnl_message_unref(req); - reply = sd_rtnl_message_unref(reply); + req = sd_netlink_message_unref(req); + reply = sd_netlink_message_unref(reply); /* Finally, enumerate all addresses, too */ r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC); if (r < 0) return r; - r = sd_rtnl_message_request_dump(req, true); + r = sd_netlink_message_request_dump(req, true); if (r < 0) return r; - r = sd_rtnl_call(m->rtnl, req, 0, &reply); + r = sd_netlink_call(m->rtnl, req, 0, &reply); if (r < 0) return r; - for (i = reply; i; i = sd_rtnl_message_next(i)) { + for (i = reply; i; i = sd_netlink_message_next(i)) { r = manager_process_address(m->rtnl, i, m); if (r < 0) return r; @@ -264,12 +281,12 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void * HASHMAP_FOREACH(l, m->links, i) { r = link_update_monitor(l); if (r < 0) - log_warning("Failed to update monitor information for %i: %s", l->ifindex, strerror(-r)); + log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); } r = manager_write_resolv_conf(m); if (r < 0) - log_warning("Could not update resolv.conf: %s", strerror(-r)); + log_warning_errno(r, "Could not update resolv.conf: %m"); return 0; } @@ -298,84 +315,149 @@ static int manager_network_monitor_listen(Manager *m) { return 0; } -static int parse_dns_server_string(Manager *m, const char *string) { - char *word, *state; - size_t length; - int r; +static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { + _cleanup_free_ char *h = NULL, *n = NULL; + char label[DNS_LABEL_MAX]; + const char *p; + int r, k; - assert(m); - assert(string); + assert(llmnr_hostname); + assert(mdns_hostname); - FOREACH_WORD_QUOTED(word, length, string, state) { - char buffer[length+1]; - int family; - union in_addr_union addr; + /* Extract and normalize the first label of the locally + * configured hostname, and check it's not "localhost". */ - memcpy(buffer, word, length); - buffer[length] = 0; + h = gethostname_malloc(); + if (!h) + return log_oom(); - r = in_addr_from_string_auto(buffer, &family, &addr); - if (r < 0) { - log_warning("Ignoring invalid DNS address '%s'", buffer); - continue; - } + p = h; + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return log_error_errno(r, "Failed to unescape host name: %m"); + if (r == 0) { + log_error("Couldn't find a single label in hosntame."); + return -EINVAL; + } - /* filter out duplicates */ - if (manager_find_dns_server(m, family, &addr)) - continue; + k = dns_label_undo_idna(label, r, label, sizeof(label)); + if (k < 0) + return log_error_errno(k, "Failed to undo IDNA: %m"); + if (k > 0) + r = k; - r = dns_server_new(m, NULL, NULL, family, &addr); - if (r < 0) - return r; + if (!utf8_is_valid(label)) { + log_error("System hostname is not UTF-8 clean."); + return -EINVAL; + } + + r = dns_label_escape(label, r, &n); + if (r < 0) + return log_error_errno(r, "Failed to escape host name: %m"); + + if (is_localhost(n)) { + log_debug("System hostname is 'localhost', ignoring."); + return -EINVAL; } + r = dns_name_concat(n, "local", mdns_hostname); + if (r < 0) + return log_error_errno(r, "Failed to determine mDNS hostname: %m"); + + *llmnr_hostname = n; + n = NULL; + return 0; } -int config_parse_dnsv( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - +static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL; Manager *m = userdata; int r; - assert(filename); - assert(lvalue); - assert(rvalue); assert(m); - /* Empty assignment means clear the list */ - if (isempty(rvalue)) { - while (m->dns_servers) - dns_server_free(m->dns_servers); + r = determine_hostname(&llmnr_hostname, &mdns_hostname); + if (r < 0) + return 0; /* ignore invalid hostnames */ + + if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname)) + return 0; + + log_info("System hostname changed to '%s'.", llmnr_hostname); + + free(m->llmnr_hostname); + free(m->mdns_hostname); + + m->llmnr_hostname = llmnr_hostname; + m->mdns_hostname = mdns_hostname; + + llmnr_hostname = mdns_hostname = NULL; + + manager_refresh_rrs(m); + + return 0; +} + +static int manager_watch_hostname(Manager *m) { + int r; + + assert(m); + m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); + if (m->hostname_fd < 0) { + log_warning_errno(errno, "Failed to watch hostname: %m"); return 0; } - r = parse_dns_server_string(m, rvalue); + r = sd_event_add_io(m->event, &m->hostname_event_source, m->hostname_fd, 0, on_hostname_change, m); if (r < 0) { - log_error("Failed to parse DNS server string"); - return r; + if (r == -EPERM) + /* kernels prior to 3.2 don't support polling this file. Ignore the failure. */ + m->hostname_fd = safe_close(m->hostname_fd); + else + return log_error_errno(r, "Failed to add hostname event source: %m"); } + r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname); + if (r < 0) { + log_info("Defaulting to hostname 'linux'."); + m->llmnr_hostname = strdup("linux"); + if (!m->llmnr_hostname) + return log_oom(); + + m->mdns_hostname = strdup("linux.local"); + if (!m->mdns_hostname) + return log_oom(); + } else + log_info("Using system hostname '%s'.", m->llmnr_hostname); + return 0; } -int manager_parse_config_file(Manager *m) { +static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + _cleanup_free_ char *buffer = NULL; + _cleanup_fclose_ FILE *f = NULL; + Manager *m = userdata; + size_t size = 0; + DnsScope *scope; + + assert(s); + assert(si); assert(m); - return config_parse(NULL, "/etc/systemd/resolved.conf", NULL, - "Resolve\0", - config_item_perf_lookup, resolved_gperf_lookup, - false, false, true, m); + f = open_memstream(&buffer, &size); + if (!f) + return log_oom(); + + LIST_FOREACH(scopes, scope, m->dns_scopes) + dns_scope_dump(scope, f); + + if (fflush_and_check(f) < 0) + return log_oom(); + + log_dump(LOG_INFO, buffer); + return 0; } int manager_new(Manager **ret) { @@ -388,14 +470,13 @@ int manager_new(Manager **ret) { if (!m) return -ENOMEM; - m->dns_ipv4_fd = m->dns_ipv6_fd = -1; m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1; + m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1; + m->hostname_fd = -1; - m->use_llmnr = true; - - r = parse_dns_server_string(m, DNS_SERVERS); - if (r < 0) - return r; + m->llmnr_support = SUPPORT_YES; + m->read_resolv_conf = true; + m->need_builtin_fallbacks = true; r = sd_event_default(&m->event); if (r < 0) @@ -406,6 +487,10 @@ int manager_new(Manager **ret) { sd_event_set_watchdog(m->event, true); + r = manager_watch_hostname(m); + if (r < 0) + return r; + r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC); if (r < 0) return r; @@ -422,130 +507,82 @@ int manager_new(Manager **ret) { if (r < 0) return r; + (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m); + *ret = m; m = NULL; return 0; } +int manager_start(Manager *m) { + int r; + + assert(m); + + r = manager_llmnr_start(m); + if (r < 0) + return r; + + return 0; +} + Manager *manager_free(Manager *m) { Link *l; if (!m) return NULL; - while (m->dns_queries) - dns_query_free(m->dns_queries); - - hashmap_free(m->dns_query_transactions); + manager_flush_dns_servers(m, DNS_SERVER_SYSTEM); + manager_flush_dns_servers(m, DNS_SERVER_FALLBACK); while ((l = hashmap_first(m->links))) link_free(l); - hashmap_free(m->links); + + while (m->dns_queries) + dns_query_free(m->dns_queries); dns_scope_free(m->unicast_scope); - while (m->dns_servers) - dns_server_free(m->dns_servers); + hashmap_free(m->links); + hashmap_free(m->dns_transactions); sd_event_source_unref(m->network_event_source); sd_network_monitor_unref(m->network_monitor); - sd_event_source_unref(m->dns_ipv4_event_source); - sd_event_source_unref(m->dns_ipv6_event_source); - safe_close(m->dns_ipv4_fd); - safe_close(m->dns_ipv6_fd); + sd_netlink_unref(m->rtnl); + sd_event_source_unref(m->rtnl_event_source); - sd_event_source_unref(m->llmnr_ipv4_udp_event_source); - sd_event_source_unref(m->llmnr_ipv6_udp_event_source); - safe_close(m->llmnr_ipv4_udp_fd); - safe_close(m->llmnr_ipv6_udp_fd); + manager_llmnr_stop(m); + sd_bus_slot_unref(m->prepare_for_sleep_slot); sd_event_source_unref(m->bus_retry_event_source); sd_bus_unref(m->bus); - sd_event_unref(m->event); - free(m); - - return NULL; -} - -static void write_resolve_conf_server(DnsServer *s, FILE *f, unsigned *count) { - _cleanup_free_ char *t = NULL; - int r; - - assert(s); - assert(f); - assert(count); - - r = in_addr_to_string(s->family, &s->address, &t); - if (r < 0) { - log_warning("Invalid DNS address. Ignoring."); - return; - } - - if (*count == MAXNS) - fputs("# Too many DNS servers configured, the following entries may be ignored\n", f); - - fprintf(f, "nameserver %s\n", t); - (*count) ++; -} - -int manager_write_resolv_conf(Manager *m) { - const char *path = "/run/systemd/resolve/resolv.conf"; - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - unsigned count = 0; - DnsServer *s; - Iterator i; - Link *l; - int r; - - assert(m); + sd_event_source_unref(m->sigusr1_event_source); - r = fopen_temporary(path, &f, &temp_path); - if (r < 0) - return r; - - fchmod(fileno(f), 0644); - - fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n" - "# Third party programs must not access this file directly, but\n" - "# only through the symlink at /etc/resolv.conf. To manage\n" - "# resolv.conf(5) in a different way, replace the symlink by a\n" - "# static file or a different symlink.\n\n", f); - - HASHMAP_FOREACH(l, m->links, i) - LIST_FOREACH(servers, s, l->dns_servers) - write_resolve_conf_server(s, f, &count); + sd_event_unref(m->event); - LIST_FOREACH(servers, s, m->dns_servers) - write_resolve_conf_server(s, f, &count); + dns_resource_key_unref(m->llmnr_host_ipv4_key); + dns_resource_key_unref(m->llmnr_host_ipv6_key); - r = fflush_and_check(f); - if (r < 0) - goto fail; - - if (rename(temp_path, path) < 0) { - r = -errno; - goto fail; - } + sd_event_source_unref(m->hostname_event_source); + safe_close(m->hostname_fd); + free(m->llmnr_hostname); + free(m->mdns_hostname); - return 0; + free(m); -fail: - unlink(path); - unlink(temp_path); - return r; + return NULL; } int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; union { struct cmsghdr header; /* For alignment */ - uint8_t buffer[CMSG_SPACE(MAX(sizeof(struct in_pktinfo), sizeof(struct in6_pktinfo))) + uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) + CMSG_SPACE(int) /* ttl/hoplimit */ - + 1024 /* kernel appears to require extra buffer space */]; + + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */]; } control; union sockaddr_union sa; struct msghdr mh = {}; @@ -595,14 +632,18 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { p->size = (size_t) l; p->family = sa.sa.sa_family; - if (p->family == AF_INET) + p->ipproto = IPPROTO_UDP; + if (p->family == AF_INET) { p->sender.in = sa.in.sin_addr; - else if (p->family == AF_INET6) + p->sender_port = be16toh(sa.in.sin_port); + } else if (p->family == AF_INET6) { p->sender.in6 = sa.in6.sin6_addr; - else + p->sender_port = be16toh(sa.in6.sin6_port); + p->ifindex = sa.in6.sin6_scope_id; + } else return -EAFNOSUPPORT; - for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) { + CMSG_FOREACH(cmsg, &mh) { if (cmsg->cmsg_level == IPPROTO_IPV6) { assert(p->family == AF_INET6); @@ -612,7 +653,9 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { case IPV6_PKTINFO: { struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg); - p->ifindex = i->ipi6_ifindex; + if (p->ifindex <= 0) + p->ifindex = i->ipi6_ifindex; + p->destination.in6 = i->ipi6_addr; break; } @@ -630,114 +673,72 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { case IP_PKTINFO: { struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg); - p->ifindex = i->ipi_ifindex; + if (p->ifindex <= 0) + p->ifindex = i->ipi_ifindex; + p->destination.in = i->ipi_addr; break; } - case IP_RECVTTL: + case IP_TTL: p->ttl = *(int *) CMSG_DATA(cmsg); break; } } } + /* The Linux kernel sets the interface index to the loopback + * device if the packet came from the local host since it + * avoids the routing table in such a case. Let's unset the + * interface index in such a case. */ + if (p->ifindex == LOOPBACK_IFINDEX) + p->ifindex = 0; + + if (protocol != DNS_PROTOCOL_DNS) { + /* If we don't know the interface index still, we look for the + * first local interface with a matching address. Yuck! */ + if (p->ifindex <= 0) + p->ifindex = manager_find_ifindex(m, p->family, &p->destination); + } + *ret = p; p = NULL; return 1; } -static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsQueryTransaction *t = NULL; - Manager *m = userdata; +static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { int r; - r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p); - if (r <= 0) - return r; + assert(fd >= 0); + assert(mh); - if (dns_packet_validate_reply(p) >= 0) { - t = hashmap_get(m->dns_query_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); - if (!t) + for (;;) { + if (sendmsg(fd, mh, flags) >= 0) return 0; - dns_query_transaction_process_reply(t, p); - } else - log_debug("Invalid reply packet."); - - return 0; -} - -int manager_dns_ipv4_fd(Manager *m) { - const int one = 1; - int r; - - assert(m); - - if (m->dns_ipv4_fd >= 0) - return m->dns_ipv4_fd; - - m->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_ipv4_fd < 0) - return -errno; - - r = setsockopt(m->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } - - r = sd_event_add_io(m->event, &m->dns_ipv4_event_source, m->dns_ipv4_fd, EPOLLIN, on_dns_packet, m); - if (r < 0) - goto fail; - - return m->dns_ipv4_fd; - -fail: - m->dns_ipv4_fd = safe_close(m->dns_ipv4_fd); - return r; -} - -int manager_dns_ipv6_fd(Manager *m) { - const int one = 1; - int r; - - assert(m); - - if (m->dns_ipv6_fd >= 0) - return m->dns_ipv6_fd; + if (errno == EINTR) + continue; - m->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->dns_ipv6_fd < 0) - return -errno; + if (errno != EAGAIN) + return -errno; - r = setsockopt(m->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; + r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC); + if (r < 0) + return r; + if (r == 0) + return -ETIMEDOUT; } - - r = sd_event_add_io(m->event, &m->dns_ipv6_event_source, m->dns_ipv6_fd, EPOLLIN, on_dns_packet, m); - if (r < 0) - goto fail; - - return m->dns_ipv6_fd; - -fail: - m->dns_ipv6_fd = safe_close(m->dns_ipv6_fd); - return r; } -static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { +static int write_loop(int fd, void *message, size_t length) { int r; assert(fd >= 0); - assert(mh); + assert(message); for (;;) { - if (sendmsg(fd, mh, flags) >= 0) + if (write(fd, message, length) >= 0) return 0; if (errno == EINTR) @@ -754,7 +755,19 @@ static int sendmsg_loop(int fd, struct msghdr *mh, int flags) { } } -static int manager_ipv4_send(Manager *m, int fd, int ifindex, struct in_addr *addr, uint16_t port, DnsPacket *p) { +int manager_write(Manager *m, int fd, DnsPacket *p) { + int r; + + log_debug("Sending %s packet with id %u", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p)); + + r = write_loop(fd, DNS_PACKET_DATA(p), p->size); + if (r < 0) + return r; + + return 0; +} + +static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) { union sockaddr_union sa = { .in.sin_family = AF_INET, }; @@ -803,7 +816,7 @@ static int manager_ipv4_send(Manager *m, int fd, int ifindex, struct in_addr *ad return sendmsg_loop(fd, &mh, 0); } -static int manager_ipv6_send(Manager *m, int fd, int ifindex, struct in6_addr *addr, uint16_t port, DnsPacket *p) { +static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_addr *addr, uint16_t port, DnsPacket *p) { union sockaddr_union sa = { .in6.sin6_family = AF_INET6, }; @@ -853,13 +866,15 @@ static int manager_ipv6_send(Manager *m, int fd, int ifindex, struct in6_addr *a return sendmsg_loop(fd, &mh, 0); } -int manager_send(Manager *m, int fd, int ifindex, int family, union in_addr_union *addr, uint16_t port, DnsPacket *p) { +int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p) { assert(m); assert(fd >= 0); assert(addr); assert(port > 0); assert(p); + log_debug("Sending %s packet with id %u on interface %i/%s", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family)); + if (family == AF_INET) return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p); else if (family == AF_INET6) @@ -868,51 +883,6 @@ int manager_send(Manager *m, int fd, int ifindex, int family, union in_addr_unio return -EAFNOSUPPORT; } - -DnsServer* manager_find_dns_server(Manager *m, int family, union in_addr_union *in_addr) { - DnsServer *s; - - assert(m); - assert(in_addr); - - LIST_FOREACH(servers, s, m->dns_servers) { - - if (s->family == family && - in_addr_equal(family, &s->address, in_addr)) - return s; - } - - return NULL; -} - -DnsServer *manager_get_dns_server(Manager *m) { - assert(m); - - if (!m->current_dns_server) - m->current_dns_server = m->dns_servers; - - return m->current_dns_server; -} - -void manager_next_dns_server(Manager *m) { - assert(m); - - if (!m->current_dns_server) { - m->current_dns_server = m->dns_servers; - return; - } - - if (!m->current_dns_server) - return; - - if (m->current_dns_server->servers_next) { - m->current_dns_server = m->current_dns_server->servers_next; - return; - } - - m->current_dns_server = m->dns_servers; -} - uint32_t manager_find_mtu(Manager *m) { uint32_t mtu = 0; Link *l; @@ -933,178 +903,160 @@ uint32_t manager_find_mtu(Manager *m) { return mtu; } -static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - DnsQueryTransaction *t = NULL; - Manager *m = userdata; - int r; - - r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p); - if (r <= 0) - return r; +int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr) { + LinkAddress *a; - if (dns_packet_validate_reply(p) >= 0) { - t = hashmap_get(m->dns_query_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); - if (!t) - return 0; + assert(m); - dns_query_transaction_process_reply(t, p); - } + a = manager_find_link_address(m, family, in_addr); + if (a) + return a->link->ifindex; return 0; } -int manager_llmnr_ipv4_udp_fd(Manager *m) { - union sockaddr_union sa = { - .in.sin_family = AF_INET, - .in.sin_port = htobe16(5355), - }; - static const int one = 1, pmtu = IP_PMTUDISC_DONT; +void manager_refresh_rrs(Manager *m) { + Iterator i; + Link *l; + + assert(m); + + m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key); + m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key); + + HASHMAP_FOREACH(l, m->links, i) { + link_add_rrs(l, true); + link_add_rrs(l, false); + } +} + +int manager_next_hostname(Manager *m) { + const char *p; + uint64_t u, a; + char *h, *k; int r; assert(m); - if (m->llmnr_ipv4_udp_fd >= 0) - return m->llmnr_ipv4_udp_fd; + p = strchr(m->llmnr_hostname, 0); + assert(p); - m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv4_udp_fd < 0) - return -errno; + while (p > m->llmnr_hostname) { + if (!strchr("0123456789", p[-1])) + break; - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; + p--; } - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (*p == 0 || safe_atou64(p, &u) < 0 || u <= 0) + u = 1; - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + /* Add a random number to the old value. This way we can avoid + * that two hosts pick the same hostname, win on IPv4 and lose + * on IPv6 (or vice versa), and pick the same hostname + * replacement hostname, ad infinitum. We still want the + * numbers to go up monotonically, hence we just add a random + * value 1..10 */ - r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + random_bytes(&a, sizeof(a)); + u += 1 + a % 10; - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->llmnr_hostname), m->llmnr_hostname, u) < 0) + return -ENOMEM; - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); + r = dns_name_concat(h, "local", &k); if (r < 0) { - r = -errno; - goto fail; + free(h); + return r; } - /* Disable Don't-Fragment bit in the IP header */ - r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); - if (r < 0) { - r = -errno; - goto fail; - } + log_info("Hostname conflict, changing published hostname from '%s' to '%s'.", m->llmnr_hostname, h); - r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in)); - if (r < 0) { - r = -errno; - goto fail; - } + free(m->llmnr_hostname); + m->llmnr_hostname = h; - r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, m); - if (r < 0) - goto fail; + free(m->mdns_hostname); + m->mdns_hostname = k; - return m->llmnr_ipv4_udp_fd; + manager_refresh_rrs(m); -fail: - m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd); - return r; + return 0; } -int manager_llmnr_ipv6_udp_fd(Manager *m) { - union sockaddr_union sa = { - .in6.sin6_family = AF_INET6, - .in6.sin6_port = htobe16(5355), - }; - static const int one = 1; - int r; +LinkAddress* manager_find_link_address(Manager *m, int family, const union in_addr_union *in_addr) { + Iterator i; + Link *l; assert(m); - if (m->llmnr_ipv6_udp_fd >= 0) - return m->llmnr_ipv6_udp_fd; - - m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->llmnr_ipv6_udp_fd < 0) - return -errno; + HASHMAP_FOREACH(l, m->links, i) { + LinkAddress *a; - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; + a = link_find_address(l, family, in_addr); + if (a) + return a; } - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + return NULL; +} - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } +bool manager_our_packet(Manager *m, DnsPacket *p) { + assert(m); + assert(p); - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + return !!manager_find_link_address(m, p->family, &p->sender); +} - r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } +DnsScope* manager_find_scope(Manager *m, DnsPacket *p) { + Link *l; - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + assert(m); + assert(p); - r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); - if (r < 0) { - r = -errno; - goto fail; - } + l = hashmap_get(m->links, INT_TO_PTR(p->ifindex)); + if (!l) + return NULL; - r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6)); - if (r < 0) { - r = -errno; - goto fail; + if (p->protocol == DNS_PROTOCOL_LLMNR) { + if (p->family == AF_INET) + return l->llmnr_ipv4_scope; + else if (p->family == AF_INET6) + return l->llmnr_ipv6_scope; } - r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m); - if (r < 0) { - r = -errno; - goto fail; + return NULL; +} + +void manager_verify_all(Manager *m) { + DnsScope *s; + + assert(m); + + LIST_FOREACH(scopes, s, m->dns_scopes) + dns_zone_verify_all(&s->zone); +} + +int manager_is_own_hostname(Manager *m, const char *name) { + int r; + + assert(m); + assert(name); + + if (m->llmnr_hostname) { + r = dns_name_equal(name, m->llmnr_hostname); + if (r != 0) + return r; } - return m->llmnr_ipv6_udp_fd; + if (m->mdns_hostname) + return dns_name_equal(name, m->mdns_hostname); -fail: - m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd); - return r; + return 0; } + +static const char* const support_table[_SUPPORT_MAX] = { + [SUPPORT_NO] = "no", + [SUPPORT_YES] = "yes", + [SUPPORT_RESOLVE] = "resolve", +}; +DEFINE_STRING_TABLE_LOOKUP(support, Support);