]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-transaction.c
util-lib: move string table stuff into its own string-table.[ch]
[thirdparty/systemd.git] / src / resolve / resolved-dns-transaction.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "af-list.h"
23
24 #include "dns-domain.h"
25 #include "fd-util.h"
26 #include "random-util.h"
27 #include "resolved-dns-transaction.h"
28 #include "resolved-llmnr.h"
29 #include "string-table.h"
30
31 DnsTransaction* dns_transaction_free(DnsTransaction *t) {
32 DnsQuery *q;
33 DnsZoneItem *i;
34
35 if (!t)
36 return NULL;
37
38 sd_event_source_unref(t->timeout_event_source);
39
40 dns_packet_unref(t->sent);
41 dns_packet_unref(t->received);
42 dns_answer_unref(t->cached);
43
44 sd_event_source_unref(t->dns_udp_event_source);
45 safe_close(t->dns_udp_fd);
46
47 dns_server_unref(t->server);
48 dns_stream_free(t->stream);
49
50 if (t->scope) {
51 hashmap_remove(t->scope->transactions, t->key);
52
53 if (t->id != 0)
54 hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
55 }
56
57 dns_resource_key_unref(t->key);
58
59 while ((q = set_steal_first(t->queries)))
60 set_remove(q->transactions, t);
61 set_free(t->queries);
62
63 while ((i = set_steal_first(t->zone_items)))
64 i->probe_transaction = NULL;
65 set_free(t->zone_items);
66
67 free(t);
68 return NULL;
69 }
70
71 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
72
73 void dns_transaction_gc(DnsTransaction *t) {
74 assert(t);
75
76 if (t->block_gc > 0)
77 return;
78
79 if (set_isempty(t->queries) && set_isempty(t->zone_items))
80 dns_transaction_free(t);
81 }
82
83 int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) {
84 _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
85 int r;
86
87 assert(ret);
88 assert(s);
89 assert(key);
90
91 r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL);
92 if (r < 0)
93 return r;
94
95 r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops);
96 if (r < 0)
97 return r;
98
99 t = new0(DnsTransaction, 1);
100 if (!t)
101 return -ENOMEM;
102
103 t->dns_udp_fd = -1;
104 t->key = dns_resource_key_ref(key);
105
106 /* Find a fresh, unused transaction id */
107 do
108 random_bytes(&t->id, sizeof(t->id));
109 while (t->id == 0 ||
110 hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id)));
111
112 r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
113 if (r < 0) {
114 t->id = 0;
115 return r;
116 }
117
118 r = hashmap_put(s->transactions, t->key, t);
119 if (r < 0) {
120 hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id));
121 return r;
122 }
123
124 t->scope = s;
125
126 if (ret)
127 *ret = t;
128
129 t = NULL;
130
131 return 0;
132 }
133
134 static void dns_transaction_stop(DnsTransaction *t) {
135 assert(t);
136
137 t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
138 t->stream = dns_stream_free(t->stream);
139 }
140
141 static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
142 _cleanup_free_ char *pretty = NULL;
143 DnsZoneItem *z;
144
145 assert(t);
146 assert(p);
147
148 if (manager_our_packet(t->scope->manager, p) != 0)
149 return;
150
151 in_addr_to_string(p->family, &p->sender, &pretty);
152
153 log_debug("Transaction on scope %s on %s/%s got tentative packet from %s",
154 dns_protocol_to_string(t->scope->protocol),
155 t->scope->link ? t->scope->link->name : "*",
156 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
157 pretty);
158
159 /* RFC 4795, Section 4.1 says that the peer with the
160 * lexicographically smaller IP address loses */
161 if (memcmp(&p->sender, &p->destination, FAMILY_ADDRESS_SIZE(p->family)) >= 0) {
162 log_debug("Peer has lexicographically larger IP address and thus lost in the conflict.");
163 return;
164 }
165
166 log_debug("We have the lexicographically larger IP address and thus lost in the conflict.");
167
168 t->block_gc++;
169 while ((z = set_first(t->zone_items))) {
170 /* First, make sure the zone item drops the reference
171 * to us */
172 dns_zone_item_probe_stop(z);
173
174 /* Secondly, report this as conflict, so that we might
175 * look for a different hostname */
176 dns_zone_item_conflict(z);
177 }
178 t->block_gc--;
179
180 dns_transaction_gc(t);
181 }
182
183 void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
184 DnsQuery *q;
185 DnsZoneItem *z;
186 Iterator i;
187
188 assert(t);
189 assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
190
191 /* Note that this call might invalidate the query. Callers
192 * should hence not attempt to access the query or transaction
193 * after calling this function. */
194
195 log_debug("Transaction on scope %s on %s/%s now complete with <%s>",
196 dns_protocol_to_string(t->scope->protocol),
197 t->scope->link ? t->scope->link->name : "*",
198 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
199 dns_transaction_state_to_string(state));
200
201 t->state = state;
202
203 dns_transaction_stop(t);
204
205 /* Notify all queries that are interested, but make sure the
206 * transaction isn't freed while we are still looking at it */
207 t->block_gc++;
208 SET_FOREACH(q, t->queries, i)
209 dns_query_ready(q);
210 SET_FOREACH(z, t->zone_items, i)
211 dns_zone_item_ready(z);
212 t->block_gc--;
213
214 dns_transaction_gc(t);
215 }
216
217 static int on_stream_complete(DnsStream *s, int error) {
218 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
219 DnsTransaction *t;
220
221 assert(s);
222 assert(s->transaction);
223
224 /* Copy the data we care about out of the stream before we
225 * destroy it. */
226 t = s->transaction;
227 p = dns_packet_ref(s->read_packet);
228
229 t->stream = dns_stream_free(t->stream);
230
231 if (error != 0) {
232 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
233 return 0;
234 }
235
236 if (dns_packet_validate_reply(p) <= 0) {
237 log_debug("Invalid LLMNR TCP packet.");
238 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
239 return 0;
240 }
241
242 dns_scope_check_conflicts(t->scope, p);
243
244 t->block_gc++;
245 dns_transaction_process_reply(t, p);
246 t->block_gc--;
247
248 /* If the response wasn't useful, then complete the transition now */
249 if (t->state == DNS_TRANSACTION_PENDING)
250 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
251
252 return 0;
253 }
254
255 static int dns_transaction_open_tcp(DnsTransaction *t) {
256 DnsServer *server = NULL;
257 _cleanup_close_ int fd = -1;
258 int r;
259
260 assert(t);
261
262 if (t->stream)
263 return 0;
264
265 switch (t->scope->protocol) {
266 case DNS_PROTOCOL_DNS:
267 fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server);
268 break;
269
270 case DNS_PROTOCOL_LLMNR:
271 /* When we already received a reply to this (but it was truncated), send to its sender address */
272 if (t->received)
273 fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL);
274 else {
275 union in_addr_union address;
276 int family = AF_UNSPEC;
277
278 /* Otherwise, try to talk to the owner of a
279 * the IP address, in case this is a reverse
280 * PTR lookup */
281
282 r = dns_name_address(DNS_RESOURCE_KEY_NAME(t->key), &family, &address);
283 if (r < 0)
284 return r;
285 if (r == 0)
286 return -EINVAL;
287 if (family != t->scope->family)
288 return -ESRCH;
289
290 fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL);
291 }
292
293 break;
294
295 default:
296 return -EAFNOSUPPORT;
297 }
298
299 if (fd < 0)
300 return fd;
301
302 r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
303 if (r < 0)
304 return r;
305
306 fd = -1;
307
308 r = dns_stream_write_packet(t->stream, t->sent);
309 if (r < 0) {
310 t->stream = dns_stream_free(t->stream);
311 return r;
312 }
313
314 dns_server_unref(t->server);
315 t->server = dns_server_ref(server);
316 t->received = dns_packet_unref(t->received);
317 t->stream->complete = on_stream_complete;
318 t->stream->transaction = t;
319
320 /* The interface index is difficult to determine if we are
321 * connecting to the local host, hence fill this in right away
322 * instead of determining it from the socket */
323 if (t->scope->link)
324 t->stream->ifindex = t->scope->link->ifindex;
325
326 return 0;
327 }
328
329 static void dns_transaction_next_dns_server(DnsTransaction *t) {
330 assert(t);
331
332 t->server = dns_server_unref(t->server);
333 t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source);
334 t->dns_udp_fd = safe_close(t->dns_udp_fd);
335
336 dns_scope_next_dns_server(t->scope);
337 }
338
339 void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
340 usec_t ts;
341 int r;
342
343 assert(t);
344 assert(p);
345 assert(t->state == DNS_TRANSACTION_PENDING);
346 assert(t->scope);
347 assert(t->scope->manager);
348
349 /* Note that this call might invalidate the query. Callers
350 * should hence not attempt to access the query or transaction
351 * after calling this function. */
352
353 switch (t->scope->protocol) {
354 case DNS_PROTOCOL_LLMNR:
355 assert(t->scope->link);
356
357 /* For LLMNR we will not accept any packets from other
358 * interfaces */
359
360 if (p->ifindex != t->scope->link->ifindex)
361 return;
362
363 if (p->family != t->scope->family)
364 return;
365
366 /* Tentative packets are not full responses but still
367 * useful for identifying uniqueness conflicts during
368 * probing. */
369 if (DNS_PACKET_LLMNR_T(p)) {
370 dns_transaction_tentative(t, p);
371 return;
372 }
373
374 break;
375
376 case DNS_PROTOCOL_DNS:
377 break;
378
379 default:
380 assert_not_reached("Invalid DNS protocol.");
381 }
382
383 if (t->received != p) {
384 dns_packet_unref(t->received);
385 t->received = dns_packet_ref(p);
386 }
387
388 if (p->ipproto == IPPROTO_TCP) {
389 if (DNS_PACKET_TC(p)) {
390 /* Truncated via TCP? Somebody must be fucking with us */
391 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
392 return;
393 }
394
395 if (DNS_PACKET_ID(p) != t->id) {
396 /* Not the reply to our query? Somebody must be fucking with us */
397 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
398 return;
399 }
400 }
401
402 assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
403
404 switch (t->scope->protocol) {
405 case DNS_PROTOCOL_DNS:
406 assert(t->server);
407
408 dns_server_packet_received(t->server, ts - t->start_usec);
409
410 break;
411 case DNS_PROTOCOL_LLMNR:
412 case DNS_PROTOCOL_MDNS:
413 dns_scope_packet_received(t->scope, ts - t->start_usec);
414
415 break;
416 default:
417 break;
418 }
419
420 if (DNS_PACKET_TC(p)) {
421 /* Response was truncated, let's try again with good old TCP */
422 r = dns_transaction_open_tcp(t);
423 if (r == -ESRCH) {
424 /* No servers found? Damn! */
425 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
426 return;
427 }
428 if (r < 0) {
429 /* On LLMNR, if we cannot connect to the host,
430 * we immediately give up */
431 if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
432 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
433 return;
434 }
435
436 /* On DNS, couldn't send? Try immediately again, with a new server */
437 dns_transaction_next_dns_server(t);
438
439 r = dns_transaction_go(t);
440 if (r < 0) {
441 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
442 return;
443 }
444
445 return;
446 }
447 }
448
449 /* Parse and update the cache */
450 r = dns_packet_extract(p);
451 if (r < 0) {
452 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
453 return;
454 }
455
456 /* Only consider responses with equivalent query section to the request */
457 if (p->question->n_keys != 1 || dns_resource_key_equal(p->question->keys[0], t->key) <= 0) {
458 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
459 return;
460 }
461
462 /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
463 dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender);
464
465 if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
466 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
467 else
468 dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
469 }
470
471 static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
472 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
473 DnsTransaction *t = userdata;
474 int r;
475
476 assert(t);
477 assert(t->scope);
478
479 r = manager_recv(t->scope->manager, fd, DNS_PROTOCOL_DNS, &p);
480 if (r <= 0)
481 return r;
482
483 if (dns_packet_validate_reply(p) > 0 &&
484 DNS_PACKET_ID(p) == t->id)
485 dns_transaction_process_reply(t, p);
486 else
487 log_debug("Invalid DNS packet.");
488
489 return 0;
490 }
491
492 static int dns_transaction_emit(DnsTransaction *t) {
493 int r;
494
495 assert(t);
496
497 if (t->scope->protocol == DNS_PROTOCOL_DNS && !t->server) {
498 DnsServer *server = NULL;
499 _cleanup_close_ int fd = -1;
500
501 fd = dns_scope_udp_dns_socket(t->scope, &server);
502 if (fd < 0)
503 return fd;
504
505 r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t);
506 if (r < 0)
507 return r;
508
509 t->dns_udp_fd = fd;
510 fd = -1;
511 t->server = dns_server_ref(server);
512 }
513
514 r = dns_scope_emit(t->scope, t->dns_udp_fd, t->sent);
515 if (r < 0)
516 return r;
517
518 return 0;
519 }
520
521 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
522 DnsTransaction *t = userdata;
523 int r;
524
525 assert(s);
526 assert(t);
527
528 /* Timeout reached? Try again, with a new server */
529 dns_transaction_next_dns_server(t);
530
531 /* ... and possibly increased timeout */
532 if (t->server)
533 dns_server_packet_lost(t->server, usec - t->start_usec);
534 else
535 dns_scope_packet_lost(t->scope, usec - t->start_usec);
536
537 r = dns_transaction_go(t);
538 if (r < 0)
539 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
540
541 return 0;
542 }
543
544 static int dns_transaction_make_packet(DnsTransaction *t) {
545 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
546 int r;
547
548 assert(t);
549
550 if (t->sent)
551 return 0;
552
553 r = dns_packet_new_query(&p, t->scope->protocol, 0);
554 if (r < 0)
555 return r;
556
557 r = dns_scope_good_key(t->scope, t->key);
558 if (r < 0)
559 return r;
560 if (r == 0)
561 return -EDOM;
562
563 r = dns_packet_append_key(p, t->key, NULL);
564 if (r < 0)
565 return r;
566
567 DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
568 DNS_PACKET_HEADER(p)->id = t->id;
569
570 t->sent = p;
571 p = NULL;
572
573 return 0;
574 }
575
576 static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
577 assert(t);
578 assert(t->scope);
579
580 switch (t->scope->protocol) {
581 case DNS_PROTOCOL_DNS:
582 assert(t->server);
583
584 return t->server->resend_timeout;
585 case DNS_PROTOCOL_LLMNR:
586 case DNS_PROTOCOL_MDNS:
587 return t->scope->resend_timeout;
588 default:
589 assert_not_reached("Invalid DNS protocol.");
590 }
591 }
592
593 int dns_transaction_go(DnsTransaction *t) {
594 bool had_stream;
595 usec_t ts;
596 int r;
597
598 assert(t);
599
600 had_stream = !!t->stream;
601
602 dns_transaction_stop(t);
603
604 log_debug("Excercising transaction on scope %s on %s/%s",
605 dns_protocol_to_string(t->scope->protocol),
606 t->scope->link ? t->scope->link->name : "*",
607 t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
608
609 if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
610 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
611 return 0;
612 }
613
614 if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) {
615 /* If we already tried via a stream, then we don't
616 * retry on LLMNR. See RFC 4795, Section 2.7. */
617 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
618 return 0;
619 }
620
621 assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
622
623 t->n_attempts++;
624 t->start_usec = ts;
625 t->received = dns_packet_unref(t->received);
626 t->cached = dns_answer_unref(t->cached);
627 t->cached_rcode = 0;
628
629 /* Check the cache, but only if this transaction is not used
630 * for probing or verifying a zone item. */
631 if (set_isempty(t->zone_items)) {
632
633 /* Before trying the cache, let's make sure we figured out a
634 * server to use. Should this cause a change of server this
635 * might flush the cache. */
636 dns_scope_get_dns_server(t->scope);
637
638 /* Let's then prune all outdated entries */
639 dns_cache_prune(&t->scope->cache);
640
641 r = dns_cache_lookup(&t->scope->cache, t->key, &t->cached_rcode, &t->cached);
642 if (r < 0)
643 return r;
644 if (r > 0) {
645 if (t->cached_rcode == DNS_RCODE_SUCCESS)
646 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
647 else
648 dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
649 return 0;
650 }
651 }
652
653 if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) {
654 usec_t jitter;
655
656 /* RFC 4795 Section 2.7 suggests all queries should be
657 * delayed by a random time from 0 to JITTER_INTERVAL. */
658
659 t->initial_jitter = true;
660
661 random_bytes(&jitter, sizeof(jitter));
662 jitter %= LLMNR_JITTER_INTERVAL_USEC;
663
664 r = sd_event_add_time(
665 t->scope->manager->event,
666 &t->timeout_event_source,
667 clock_boottime_or_monotonic(),
668 ts + jitter,
669 LLMNR_JITTER_INTERVAL_USEC,
670 on_transaction_timeout, t);
671 if (r < 0)
672 return r;
673
674 t->n_attempts = 0;
675 t->state = DNS_TRANSACTION_PENDING;
676
677 log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter);
678 return 0;
679 }
680
681 /* Otherwise, we need to ask the network */
682 r = dns_transaction_make_packet(t);
683 if (r == -EDOM) {
684 /* Not the right request to make on this network?
685 * (i.e. an A request made on IPv6 or an AAAA request
686 * made on IPv4, on LLMNR or mDNS.) */
687 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
688 return 0;
689 }
690 if (r < 0)
691 return r;
692
693 if (t->scope->protocol == DNS_PROTOCOL_LLMNR &&
694 (dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "in-addr.arpa") > 0 ||
695 dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "ip6.arpa") > 0)) {
696
697 /* RFC 4795, Section 2.4. says reverse lookups shall
698 * always be made via TCP on LLMNR */
699 r = dns_transaction_open_tcp(t);
700 } else {
701 /* Try via UDP, and if that fails due to large size try via TCP */
702 r = dns_transaction_emit(t);
703 if (r == -EMSGSIZE)
704 r = dns_transaction_open_tcp(t);
705 }
706 if (r == -ESRCH) {
707 /* No servers to send this to? */
708 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
709 return 0;
710 } else if (r < 0) {
711 if (t->scope->protocol != DNS_PROTOCOL_DNS) {
712 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
713 return 0;
714 }
715
716 /* Couldn't send? Try immediately again, with a new server */
717 dns_transaction_next_dns_server(t);
718
719 return dns_transaction_go(t);
720 }
721
722 r = sd_event_add_time(
723 t->scope->manager->event,
724 &t->timeout_event_source,
725 clock_boottime_or_monotonic(),
726 ts + transaction_get_resend_timeout(t), 0,
727 on_transaction_timeout, t);
728 if (r < 0)
729 return r;
730
731 t->state = DNS_TRANSACTION_PENDING;
732 return 1;
733 }
734
735 static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
736 [DNS_TRANSACTION_NULL] = "null",
737 [DNS_TRANSACTION_PENDING] = "pending",
738 [DNS_TRANSACTION_FAILURE] = "failure",
739 [DNS_TRANSACTION_SUCCESS] = "success",
740 [DNS_TRANSACTION_NO_SERVERS] = "no-servers",
741 [DNS_TRANSACTION_TIMEOUT] = "timeout",
742 [DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
743 [DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
744 [DNS_TRANSACTION_RESOURCES] = "resources",
745 [DNS_TRANSACTION_ABORTED] = "aborted",
746 };
747 DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);