summaryrefslogtreecommitdiff
path: root/src/resolve/resolved-dns-transaction.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve/resolved-dns-transaction.c')
-rw-r--r--src/resolve/resolved-dns-transaction.c94
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",