+ assert_not_reached("Unknown scope protocol");
+}
+
+int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
+ assert(s);
+ assert(key);
+
+ if (s->protocol == DNS_PROTOCOL_DNS)
+ return true;
+
+ /* On mDNS and LLMNR, send A and AAAA queries only on the
+ * respective scopes */
+
+ if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
+ return false;
+
+ if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
+ return false;
+
+ return true;
+}
+
+int dns_scope_llmnr_membership(DnsScope *s, bool b) {
+ int fd;
+
+ if (s->family == AF_INET) {
+ struct ip_mreqn mreqn = {
+ .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
+ .imr_ifindex = s->link->ifindex,
+ };
+
+ fd = manager_llmnr_ipv4_udp_fd(s->manager);
+ if (fd < 0)
+ return fd;
+
+ if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
+ return -errno;
+
+ } else if (s->family == AF_INET6) {
+ struct ipv6_mreq mreq = {
+ .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
+ .ipv6mr_interface = s->link->ifindex,
+ };
+
+ fd = manager_llmnr_ipv6_udp_fd(s->manager);
+ if (fd < 0)
+ return fd;
+
+ if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
+ return -errno;
+ } else
+ return -EAFNOSUPPORT;
+
+ return 0;
+}
+
+int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) {
+ assert(s);
+ assert(address);
+
+ if (s->protocol != DNS_PROTOCOL_DNS)
+ return 1;
+
+ if (s->link)
+ return !!link_find_dns_server(s->link, family, address);
+ else
+ return !!manager_find_dns_server(s->manager, family, address);
+}
+
+static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *a, DnsPacket **ret) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ unsigned i;
+ int r;
+
+ assert(s);
+
+ if (q->n_keys <= 0 && a->n_rrs <= 0)
+ return -EINVAL;
+
+ r = dns_packet_new(&p, s->protocol, 0);
+ if (r < 0)
+ return r;
+
+ DNS_PACKET_HEADER(p)->id = id;
+ DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
+ 1 /* qr */,
+ 0 /* opcode */,
+ 0 /* c */,
+ 0 /* tc */,
+ 0 /* t */,
+ 0 /* (ra) */,
+ 0 /* (ad) */,
+ 0 /* (cd) */,
+ rcode));
+
+ if (q) {
+ for (i = 0; i < q->n_keys; i++) {
+ r = dns_packet_append_key(p, q->keys[i], NULL);
+ if (r < 0)
+ return r;
+ }
+
+ DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
+ }
+
+ if (a) {
+ for (i = 0; i < a->n_rrs; i++) {
+ r = dns_packet_append_rr(p, a->rrs[i], NULL);
+ if (r < 0)
+ return r;
+ }
+
+ DNS_PACKET_HEADER(p)->ancount = htobe16(a->n_rrs);
+ }
+
+ *ret = p;
+ p = NULL;
+
+ return 0;
+}
+
+void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ int r, fd;
+
+ assert(s);
+ assert(p);
+
+ if (p->protocol != DNS_PROTOCOL_LLMNR)
+ return;
+
+ if (p->ipproto == IPPROTO_UDP) {
+ /* Don't accept UDP queries directed to anything but
+ * the LLMNR multicast addresses. See RFC 4795,
+ * section 2.5.*/
+
+ if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
+ return;
+
+ if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
+ return;
+ }
+
+ r = dns_packet_extract(p);
+ if (r < 0) {
+ log_debug("Failed to extract resources from incoming packet: %s", strerror(-r));
+ return;
+ }
+
+ if (DNS_PACKET_C(p)) {
+ /* FIXME: Somebody notified us about a likely conflict */
+ return;
+ }