+ 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;
+
+ m->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (m->dns_ipv6_fd < 0)
+ return -errno;
+
+ r = setsockopt(m->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
+ if (r < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ 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) {
+ int r;
+
+ assert(fd >= 0);
+ assert(mh);
+
+ for (;;) {
+ if (sendmsg(fd, mh, flags) >= 0)
+ return 0;
+
+ if (errno == EINTR)
+ continue;
+
+ if (errno != EAGAIN)
+ return -errno;
+
+ r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ETIMEDOUT;
+ }
+}
+
+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,
+ };
+ union {
+ struct cmsghdr header; /* For alignment */
+ uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))];
+ } control;
+ struct msghdr mh = {};
+ struct iovec iov;
+
+ assert(m);
+ assert(fd >= 0);
+ assert(addr);
+ assert(port > 0);
+ assert(p);
+
+ iov.iov_base = DNS_PACKET_DATA(p);
+ iov.iov_len = p->size;
+
+ sa.in.sin_addr = *addr;
+ sa.in.sin_port = htobe16(port),
+
+ mh.msg_iov = &iov;
+ mh.msg_iovlen = 1;
+ mh.msg_name = &sa.sa;
+ mh.msg_namelen = sizeof(sa.in);
+
+ if (ifindex > 0) {
+ struct cmsghdr *cmsg;
+ struct in_pktinfo *pi;
+
+ zero(control);
+
+ mh.msg_control = &control;
+ mh.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_len = mh.msg_controllen;
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+
+ pi = (struct in_pktinfo*) CMSG_DATA(cmsg);
+ pi->ipi_ifindex = ifindex;
+ }
+
+ return sendmsg_loop(fd, &mh, 0);
+}
+
+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,
+ };
+ union {
+ struct cmsghdr header; /* For alignment */
+ uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+ } control;
+ struct msghdr mh = {};
+ struct iovec iov;
+
+ assert(m);
+ assert(fd >= 0);
+ assert(addr);
+ assert(port > 0);
+ assert(p);
+
+ iov.iov_base = DNS_PACKET_DATA(p);
+ iov.iov_len = p->size;
+
+ sa.in6.sin6_addr = *addr;
+ sa.in6.sin6_port = htobe16(port),
+ sa.in6.sin6_scope_id = ifindex;
+
+ mh.msg_iov = &iov;
+ mh.msg_iovlen = 1;
+ mh.msg_name = &sa.sa;
+ mh.msg_namelen = sizeof(sa.in6);
+
+ if (ifindex > 0) {
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *pi;
+
+ zero(control);
+
+ mh.msg_control = &control;
+ mh.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_len = mh.msg_controllen;
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+
+ pi = (struct in6_pktinfo*) CMSG_DATA(cmsg);
+ pi->ipi6_ifindex = ifindex;
+ }
+
+ return sendmsg_loop(fd, &mh, 0);
+}
+
+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)
+ return manager_ipv6_send(m, fd, ifindex, &addr->in6, port, p);
+
+ return -EAFNOSUPPORT;
+}
+
+DnsServer* manager_find_dns_server(Manager *m, int family, const 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;
+ Iterator i;
+
+ /* If we don't know on which link a DNS packet would be
+ * delivered, let's find the largest MTU that works on all
+ * interfaces we know of */
+
+ HASHMAP_FOREACH(l, m->links, i) {
+ if (l->mtu <= 0)
+ continue;
+
+ if (mtu <= 0 || l->mtu < mtu)
+ mtu = l->mtu;
+ }
+
+ 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;
+ DnsTransaction *t = NULL;
+ Manager *m = userdata;
+ int r;
+
+ r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
+ if (r <= 0)