diff options
Diffstat (limited to 'src/resolve/resolved-dns-transaction.c')
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 94 |
1 files changed, 93 insertions, 1 deletions
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 3260ded424..e468f245f7 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -39,6 +39,11 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { dns_packet_unref(t->received); dns_answer_unref(t->cached); + sd_event_source_unref(t->dns_ipv4_event_source); + sd_event_source_unref(t->dns_ipv6_event_source); + safe_close(t->dns_ipv4_fd); + safe_close(t->dns_ipv6_fd); + dns_server_unref(t->server); dns_stream_free(t->stream); @@ -89,6 +94,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) { if (!t) return -ENOMEM; + t->dns_ipv4_fd = t->dns_ipv6_fd = -1; + t->question = dns_question_ref(q); do @@ -590,7 +597,7 @@ int dns_transaction_go(DnsTransaction *t) { DnsServer *server; /* Try via UDP, and if that fails due to large size try via TCP */ - r = dns_scope_emit(t->scope, t->sent, &server); + r = dns_scope_emit(t->scope, t, t->sent, &server); if (r >= 0) t->server = dns_server_ref(server); else if (r == -EMSGSIZE) @@ -625,6 +632,91 @@ int dns_transaction_go(DnsTransaction *t) { 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; + DnsTransaction *t = userdata; + int r; + + assert(t); + assert(t->scope); + + r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p); + if (r <= 0) + return r; + + if (dns_packet_validate_reply(p) > 0 && + DNS_PACKET_ID(p) == t->id) { + dns_transaction_process_reply(t, p); + } else + log_debug("Invalid DNS packet."); + + return 0; +} + +int transaction_dns_ipv4_fd(DnsTransaction *t) { + const int one = 1; + int r; + + assert(t); + assert(t->scope); + assert(t->scope->manager); + + if (t->dns_ipv4_fd >= 0) + return t->dns_ipv4_fd; + + t->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (t->dns_ipv4_fd < 0) + return -errno; + + r = setsockopt(t->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv4_event_source, t->dns_ipv4_fd, EPOLLIN, on_dns_packet, t); + if (r < 0) + goto fail; + + return t->dns_ipv4_fd; + +fail: + t->dns_ipv4_fd = safe_close(t->dns_ipv4_fd); + return r; +} + +int transaction_dns_ipv6_fd(DnsTransaction *t) { + const int one = 1; + int r; + + assert(t); + assert(t->scope); + assert(t->scope->manager); + + if (t->dns_ipv6_fd >= 0) + return t->dns_ipv6_fd; + + t->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (t->dns_ipv6_fd < 0) + return -errno; + + r = setsockopt(t->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(t->scope->manager->event, &t->dns_ipv6_event_source, t->dns_ipv6_fd, EPOLLIN, on_dns_packet, t); + if (r < 0) + goto fail; + + return t->dns_ipv6_fd; + +fail: + t->dns_ipv6_fd = safe_close(t->dns_ipv6_fd); + return r; +} + static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = { [DNS_TRANSACTION_NULL] = "null", [DNS_TRANSACTION_PENDING] = "pending", |