X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-scope.c;h=5141a8d8047920a37c8e071848c8c0c6002f700b;hb=02dd6e189a6d2b7f3884ad4cdb3d8c85e009c565;hp=e74fcd4491710a8c64e5ddee27817c38e876f1c8;hpb=46f08bea4b09e2cce4b50e3c082df4a92a22598c;p=elogind.git diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index e74fcd449..5141a8d80 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -21,6 +21,7 @@ #include +#include "missing.h" #include "strv.h" #include "socket-util.h" #include "af-list.h" @@ -55,6 +56,8 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int } DnsScope* dns_scope_free(DnsScope *s) { + DnsQueryTransaction *t; + if (!s) return NULL; @@ -62,16 +65,20 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_scope_llmnr_membership(s, false); - while (s->transactions) { - DnsQuery *q; + while ((t = s->transactions)) { + + /* Abort the transaction, but make sure it is not + * freed while we still look at it */ - q = s->transactions->query; - dns_query_transaction_free(s->transactions); + t->block_gc++; + dns_query_transaction_complete(t, DNS_QUERY_ABORTED); + t->block_gc--; - dns_query_finish(q); + dns_query_transaction_free(t); } dns_cache_flush(&s->cache); + dns_zone_flush(&s->zone); LIST_REMOVE(scopes, s->manager->dns_scopes, s); strv_free(s->domains); @@ -125,6 +132,9 @@ int dns_scope_send(DnsScope *s, DnsPacket *p) { if (s->protocol == DNS_PROTOCOL_DNS) { DnsServer *srv; + if (DNS_PACKET_QDCOUNT(p) > 1) + return -ENOTSUP; + srv = dns_scope_get_server(s); if (!srv) return -ESRCH; @@ -158,12 +168,10 @@ int dns_scope_send(DnsScope *s, DnsPacket *p) { if (family == AF_INET) { addr.in = LLMNR_MULTICAST_IPV4_ADDRESS; - /* fd = manager_dns_ipv4_fd(s->manager); */ fd = manager_llmnr_ipv4_udp_fd(s->manager); } else if (family == AF_INET6) { addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS; fd = manager_llmnr_ipv6_udp_fd(s->manager); - /* fd = manager_dns_ipv6_fd(s->manager); */ } else return -EAFNOSUPPORT; if (fd < 0) @@ -178,42 +186,86 @@ int dns_scope_send(DnsScope *s, DnsPacket *p) { return 1; } -int dns_scope_tcp_socket(DnsScope *s) { +int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) { _cleanup_close_ int fd = -1; union sockaddr_union sa = {}; socklen_t salen; - int one, ifindex, ret; - DnsServer *srv; - int r; + static const int one = 1; + int ret, r; assert(s); + assert((family == AF_UNSPEC) == !address); - srv = dns_scope_get_server(s); - if (!srv) - return -ESRCH; + if (family == AF_UNSPEC) { + DnsServer *srv; - if (s->link) - ifindex = s->link->ifindex; + srv = dns_scope_get_server(s); + if (!srv) + return -ESRCH; - sa.sa.sa_family = srv->family; - if (srv->family == AF_INET) { - sa.in.sin_port = htobe16(53); - sa.in.sin_addr = srv->address.in; - salen = sizeof(sa.in); - } else if (srv->family == AF_INET6) { - sa.in6.sin6_port = htobe16(53); - sa.in6.sin6_addr = srv->address.in6; - sa.in6.sin6_scope_id = ifindex; - salen = sizeof(sa.in6); - } else - return -EAFNOSUPPORT; + sa.sa.sa_family = srv->family; + if (srv->family == AF_INET) { + sa.in.sin_port = htobe16(port); + sa.in.sin_addr = srv->address.in; + salen = sizeof(sa.in); + } else if (srv->family == AF_INET6) { + sa.in6.sin6_port = htobe16(port); + sa.in6.sin6_addr = srv->address.in6; + sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0; + salen = sizeof(sa.in6); + } else + return -EAFNOSUPPORT; + } else { + sa.sa.sa_family = family; + + if (family == AF_INET) { + sa.in.sin_port = htobe16(port); + sa.in.sin_addr = address->in; + salen = sizeof(sa.in); + } else if (family == AF_INET6) { + sa.in6.sin6_port = htobe16(port); + sa.in6.sin6_addr = address->in6; + sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0; + salen = sizeof(sa.in6); + } else + return -EAFNOSUPPORT; + } - fd = socket(srv->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) return -errno; - one = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + if (r < 0) + return -errno; + + if (s->link) { + uint32_t ifindex = htobe32(s->link->ifindex); + + if (sa.sa.sa_family == AF_INET) { + r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)); + if (r < 0) + return -errno; + } else if (sa.sa.sa_family == AF_INET6) { + r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex)); + if (r < 0) + return -errno; + } + } + + if (s->protocol == DNS_PROTOCOL_LLMNR) { + /* RFC 4795, section 2.5 requires the TTL to be set to 1 */ + + if (sa.sa.sa_family == AF_INET) { + r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); + if (r < 0) + return -errno; + } else if (sa.sa.sa_family == AF_INET6) { + r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); + if (r < 0) + return -errno; + } + } r = connect(fd, &sa.sa, salen); if (r < 0 && errno != EINPROGRESS) @@ -221,6 +273,7 @@ int dns_scope_tcp_socket(DnsScope *s) { ret = fd; fd = -1; + return ret; } @@ -231,37 +284,37 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) { assert(domain); STRV_FOREACH(i, s->domains) - if (dns_name_endswith(domain, *i)) + if (dns_name_endswith(domain, *i) > 0) return DNS_SCOPE_YES; - if (dns_name_root(domain)) + if (dns_name_root(domain) != 0) return DNS_SCOPE_NO; if (is_localhost(domain)) return DNS_SCOPE_NO; if (s->protocol == DNS_PROTOCOL_DNS) { - if (dns_name_endswith(domain, "254.169.in-addr.arpa") || - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") || - dns_name_single_label(domain)) - return DNS_SCOPE_NO; + if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && + dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 && + dns_name_single_label(domain) == 0) + return DNS_SCOPE_MAYBE; - return DNS_SCOPE_MAYBE; + return DNS_SCOPE_NO; } if (s->protocol == DNS_PROTOCOL_MDNS) { - if (dns_name_endswith(domain, "254.169.in-addr.arpa") || - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") || - dns_name_endswith(domain, "local")) + if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 || + dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 || + (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0)) return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; } if (s->protocol == DNS_PROTOCOL_LLMNR) { - if (dns_name_endswith(domain, "254.169.in-addr.arpa") || - dns_name_endswith(domain, "0.8.e.f.ip6.arpa") || - dns_name_single_label(domain)) + if (dns_name_endswith(domain, "in-addr.arpa") > 0 || + dns_name_endswith(domain, "ip6.arpa") > 0 || + dns_name_single_label(domain) > 0) return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; @@ -322,3 +375,153 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) { 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 *answer, DnsAnswer *soa, DnsPacket **ret) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + unsigned i; + int r; + + assert(s); + + if (q->n_keys <= 0 && answer->n_rrs <= 0 && soa->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 (answer) { + for (i = 0; i < answer->n_rrs; i++) { + r = dns_packet_append_rr(p, answer->rrs[i], NULL); + if (r < 0) + return r; + } + + DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs); + } + + if (soa) { + for (i = 0; i < soa->n_rrs; i++) { + r = dns_packet_append_rr(p, soa->rrs[i], NULL); + if (r < 0) + return r; + } + + DNS_PACKET_HEADER(p)->arcount = htobe16(soa->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, *soa = 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; + } + + r = dns_zone_lookup(&s->zone, p->question, &answer, &soa); + if (r < 0) { + log_debug("Failed to lookup key: %s", strerror(-r)); + return; + } + if (r == 0) + return; + + dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0); + + r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, &reply); + if (r < 0) { + log_debug("Failed to build reply packet: %s", strerror(-r)); + return; + } + + if (stream) + r = dns_stream_write_packet(stream, reply); + else { + if (p->family == AF_INET) + fd = manager_llmnr_ipv4_udp_fd(s->manager); + else if (p->family == AF_INET6) + fd = manager_llmnr_ipv6_udp_fd(s->manager); + else { + log_debug("Unknown protocol"); + return; + } + if (fd < 0) { + log_debug("Failed to get reply socket: %s", strerror(-fd)); + return; + } + + r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply); + } + + if (r < 0) { + log_debug("Failed to send reply packet: %s", strerror(-r)); + return; + } +}