chiark / gitweb /
resolved: add DNS cache
[elogind.git] / src / resolve / resolved-dns-query.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 "resolved-dns-query.h"
23 #include "resolved-dns-domain.h"
24
25 #define TRANSACTION_TIMEOUT_USEC (5 * USEC_PER_SEC)
26 #define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC)
27 #define ATTEMPTS_MAX 8
28 #define CNAME_MAX 8
29 #define QUERIES_MAX 2048
30
31 static int dns_query_transaction_go(DnsQueryTransaction *t);
32
33 DnsQueryTransaction* dns_query_transaction_free(DnsQueryTransaction *t) {
34         if (!t)
35                 return NULL;
36
37         sd_event_source_unref(t->timeout_event_source);
38
39         dns_packet_unref(t->sent);
40         dns_packet_unref(t->received);
41
42         dns_resource_record_freev(t->cached_rrs, t->n_cached_rrs);
43
44         sd_event_source_unref(t->tcp_event_source);
45         safe_close(t->tcp_fd);
46
47         if (t->query) {
48                 LIST_REMOVE(transactions_by_query, t->query->transactions, t);
49                 hashmap_remove(t->query->manager->dns_query_transactions, UINT_TO_PTR(t->id));
50         }
51
52         if (t->scope)
53                 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
54
55         free(t);
56         return NULL;
57 }
58
59 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryTransaction*, dns_query_transaction_free);
60
61 static int dns_query_transaction_new(DnsQuery *q, DnsQueryTransaction **ret, DnsScope *s) {
62         _cleanup_(dns_query_transaction_freep) DnsQueryTransaction *t = NULL;
63         int r;
64
65         assert(q);
66         assert(s);
67
68         r = hashmap_ensure_allocated(&q->manager->dns_query_transactions, NULL, NULL);
69         if (r < 0)
70                 return r;
71
72         t = new0(DnsQueryTransaction, 1);
73         if (!t)
74                 return -ENOMEM;
75
76         t->tcp_fd = -1;
77
78         do
79                 random_bytes(&t->id, sizeof(t->id));
80         while (t->id == 0 ||
81                hashmap_get(q->manager->dns_query_transactions, UINT_TO_PTR(t->id)));
82
83         r = hashmap_put(q->manager->dns_query_transactions, UINT_TO_PTR(t->id), t);
84         if (r < 0) {
85                 t->id = 0;
86                 return r;
87         }
88
89         LIST_PREPEND(transactions_by_query, q->transactions, t);
90         t->query = q;
91
92         LIST_PREPEND(transactions_by_scope, s->transactions, t);
93         t->scope = s;
94
95         if (ret)
96                 *ret = t;
97
98         t = NULL;
99
100         return 0;
101 }
102
103 static void dns_query_transaction_stop(DnsQueryTransaction *t) {
104         assert(t);
105
106         t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
107         t->tcp_event_source = sd_event_source_unref(t->tcp_event_source);
108         t->tcp_fd = safe_close(t->tcp_fd);
109 }
110
111 static void dns_query_transaction_complete(DnsQueryTransaction *t, DnsQueryState state) {
112         assert(t);
113         assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
114         assert(IN_SET(t->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
115
116         /* Note that this call might invalidate the query. Callers
117          * should hence not attempt to access the query or transaction
118          * after calling this function. */
119
120         t->state = state;
121
122         dns_query_transaction_stop(t);
123         dns_query_finish(t->query);
124 }
125
126 static int on_tcp_ready(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
127         DnsQueryTransaction *t = userdata;
128         int r;
129
130         assert(t);
131
132         if (revents & EPOLLOUT) {
133                 struct iovec iov[2];
134                 be16_t sz;
135                 ssize_t ss;
136
137                 sz = htobe16(t->sent->size);
138
139                 iov[0].iov_base = &sz;
140                 iov[0].iov_len = sizeof(sz);
141                 iov[1].iov_base = DNS_PACKET_DATA(t->sent);
142                 iov[1].iov_len = t->sent->size;
143
144                 IOVEC_INCREMENT(iov, 2, t->tcp_written);
145
146                 ss = writev(fd, iov, 2);
147                 if (ss < 0) {
148                         if (errno != EINTR && errno != EAGAIN) {
149                                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
150                                 return -errno;
151                         }
152                 } else
153                         t->tcp_written += ss;
154
155                 /* Are we done? If so, disable the event source for EPOLLOUT */
156                 if (t->tcp_written >= sizeof(sz) + t->sent->size) {
157                         r = sd_event_source_set_io_events(s, EPOLLIN);
158                         if (r < 0) {
159                                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
160                                 return r;
161                         }
162                 }
163         }
164
165         if (revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) {
166
167                 if (t->tcp_read < sizeof(t->tcp_read_size)) {
168                         ssize_t ss;
169
170                         ss = read(fd, (uint8_t*) &t->tcp_read_size + t->tcp_read, sizeof(t->tcp_read_size) - t->tcp_read);
171                         if (ss < 0) {
172                                 if (errno != EINTR && errno != EAGAIN) {
173                                         dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
174                                         return -errno;
175                                 }
176                         } else if (ss == 0) {
177                                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
178                                 return -EIO;
179                         } else
180                                 t->tcp_read += ss;
181                 }
182
183                 if (t->tcp_read >= sizeof(t->tcp_read_size)) {
184
185                         if (be16toh(t->tcp_read_size) < DNS_PACKET_HEADER_SIZE) {
186                                 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
187                                 return -EBADMSG;
188                         }
189
190                         if (t->tcp_read < sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
191                                 ssize_t ss;
192
193                                 if (!t->received) {
194                                         r = dns_packet_new(&t->received, be16toh(t->tcp_read_size));
195                                         if (r < 0) {
196                                                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
197                                                 return r;
198                                         }
199                                 }
200
201                                 ss = read(fd,
202                                           (uint8_t*) DNS_PACKET_DATA(t->received) + t->tcp_read - sizeof(t->tcp_read_size),
203                                           sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size) - t->tcp_read);
204                                 if (ss < 0) {
205                                         if (errno != EINTR && errno != EAGAIN) {
206                                                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
207                                                 return -errno;
208                                         }
209                                 } else if (ss == 0) {
210                                         dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
211                                         return -EIO;
212                                 }  else
213                                         t->tcp_read += ss;
214                         }
215
216                         if (t->tcp_read >= sizeof(t->tcp_read_size) + be16toh(t->tcp_read_size)) {
217                                 t->received->size = be16toh(t->tcp_read_size);
218                                 dns_query_transaction_reply(t, t->received);
219                                 return 0;
220                         }
221                 }
222         }
223
224         return 0;
225 }
226
227 static int dns_query_transaction_open_tcp(DnsQueryTransaction *t) {
228         int r;
229
230         assert(t);
231
232         if (t->tcp_fd >= 0)
233                 return 0;
234
235         t->tcp_written = 0;
236         t->tcp_read = 0;
237         t->received = dns_packet_unref(t->received);
238
239         t->tcp_fd = dns_scope_tcp_socket(t->scope);
240         if (t->tcp_fd < 0)
241                 return t->tcp_fd;
242
243         r = sd_event_add_io(t->query->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
244         if (r < 0) {
245                 t->tcp_fd = safe_close(t->tcp_fd);
246                 return r;
247         }
248
249         return 0;
250 }
251
252 void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
253         int r;
254
255         assert(t);
256         assert(p);
257         assert(t->state == DNS_QUERY_PENDING);
258
259         /* Note that this call might invalidate the query. Callers
260          * should hence not attempt to access the query or transaction
261          * after calling this function. */
262
263         if (t->received != p) {
264                 dns_packet_unref(t->received);
265                 t->received = dns_packet_ref(p);
266         }
267
268         if (t->tcp_fd >= 0) {
269                 if (DNS_PACKET_TC(p)) {
270                         /* Truncated via TCP? Somebody must be fucking with us */
271                         dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
272                         return;
273                 }
274
275                 if (DNS_PACKET_ID(p) != t->id) {
276                         /* Not the reply to our query? Somebody must be fucking with us */
277                         dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
278                         return;
279                 }
280         }
281
282         if (DNS_PACKET_TC(p)) {
283                 /* Response was truncated, let's try again with good old TCP */
284                 r = dns_query_transaction_open_tcp(t);
285                 if (r == -ESRCH) {
286                         /* No servers found? Damn! */
287                         dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
288                         return;
289                 }
290                 if (r < 0) {
291                         /* Couldn't send? Try immediately again, with a new server */
292                         dns_scope_next_dns_server(t->scope);
293
294                         r = dns_query_transaction_go(t);
295                         if (r < 0) {
296                                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
297                                 return;
298                         }
299
300                         return;
301                 }
302         }
303
304         /* Parse and update the cache */
305         r = dns_packet_extract_rrs(p);
306         if (r < 0) {
307                 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
308                 return;
309         } else if (r > 0)
310                 dns_cache_put_rrs(&t->scope->cache, p->rrs, r, 0);
311
312         if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
313                 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
314         else
315                 dns_query_transaction_complete(t, DNS_QUERY_FAILURE);
316 }
317
318 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
319         DnsQueryTransaction *t = userdata;
320         int r;
321
322         assert(s);
323         assert(t);
324
325         /* Timeout reached? Try again, with a new server */
326         dns_scope_next_dns_server(t->scope);
327
328         r = dns_query_transaction_go(t);
329         if (r < 0)
330                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
331
332         return 0;
333 }
334
335 static int dns_query_make_packet(DnsQueryTransaction *t) {
336         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
337         unsigned n;
338         int r;
339
340         assert(t);
341
342         if (t->sent)
343                 return 0;
344
345         r = dns_packet_new_query(&p, 0);
346         if (r < 0)
347                 return r;
348
349         for (n = 0; n < t->query->n_keys; n++) {
350                 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
351                 if (r < 0)
352                         return r;
353         }
354
355         DNS_PACKET_HEADER(p)->qdcount = htobe16(t->query->n_keys);
356         DNS_PACKET_HEADER(p)->id = t->id;
357
358         t->sent = p;
359         p = NULL;
360
361         return 0;
362 }
363
364 static int dns_query_transaction_go(DnsQueryTransaction *t) {
365         int r;
366
367         assert(t);
368
369         dns_query_transaction_stop(t);
370
371         if (t->n_attempts >= ATTEMPTS_MAX) {
372                 dns_query_transaction_complete(t, DNS_QUERY_ATTEMPTS_MAX);
373                 return 0;
374         }
375
376         t->n_attempts++;
377         t->received = dns_packet_unref(t->received);
378         t->cached_rrs = dns_resource_record_freev(t->cached_rrs, t->n_cached_rrs);
379         t->n_cached_rrs = 0;
380
381         /* First, let's try the cache */
382         dns_cache_prune(&t->scope->cache);
383         r = dns_cache_lookup_many(&t->scope->cache, t->query->keys, t->query->n_keys, &t->cached_rrs);
384         if (r < 0)
385                 return r;
386         if (r > 0) {
387                 t->n_cached_rrs = r;
388                 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
389                 return 0;
390         }
391
392         /* Otherwise, we need to ask the network */
393         r = dns_query_make_packet(t);
394         if (r < 0)
395                 return r;
396
397         /* Try via UDP, and if that fails due to large size try via TCP */
398         r = dns_scope_send(t->scope, t->sent);
399         if (r == -EMSGSIZE)
400                 r = dns_query_transaction_open_tcp(t);
401         if (r == -ESRCH) {
402                 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
403                 return 0;
404         }
405         if (r < 0) {
406                 /* Couldn't send? Try immediately again, with a new server */
407                 dns_scope_next_dns_server(t->scope);
408
409                 return dns_query_transaction_go(t);
410         }
411
412         r = sd_event_add_time(t->query->manager->event, &t->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + TRANSACTION_TIMEOUT_USEC, 0, on_transaction_timeout, t);
413         if (r < 0)
414                 return r;
415
416         t->state = DNS_QUERY_PENDING;
417         return 1;
418 }
419
420 DnsQuery *dns_query_free(DnsQuery *q) {
421         unsigned n;
422
423         if (!q)
424                 return NULL;
425
426         sd_bus_message_unref(q->request);
427         dns_packet_unref(q->received);
428
429         dns_resource_record_freev(q->cached_rrs, q->n_cached_rrs);
430
431         sd_event_source_unref(q->timeout_event_source);
432
433         while (q->transactions)
434                 dns_query_transaction_free(q->transactions);
435
436         if (q->manager) {
437                 LIST_REMOVE(queries, q->manager->dns_queries, q);
438                 q->manager->n_dns_queries--;
439         }
440
441         for (n = 0; n < q->n_keys; n++)
442                 free(q->keys[n].name);
443         free(q->keys);
444         free(q);
445
446         return NULL;
447 }
448
449 int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
450         _cleanup_(dns_query_freep) DnsQuery *q = NULL;
451         const char *name = NULL;
452
453         assert(m);
454
455         if (n_keys <= 0 || n_keys >= 65535)
456                 return -EINVAL;
457
458         if (m->n_dns_queries >= QUERIES_MAX)
459                 return -EBUSY;
460
461         assert(keys);
462
463         q = new0(DnsQuery, 1);
464         if (!q)
465                 return -ENOMEM;
466
467         q->keys = new(DnsResourceKey, n_keys);
468         if (!q->keys)
469                 return -ENOMEM;
470
471         for (q->n_keys = 0; q->n_keys < n_keys; q->n_keys++) {
472                 q->keys[q->n_keys].class = keys[q->n_keys].class;
473                 q->keys[q->n_keys].type = keys[q->n_keys].type;
474                 q->keys[q->n_keys].name = strdup(keys[q->n_keys].name);
475                 if (!q->keys[q->n_keys].name)
476                         return -ENOMEM;
477
478                 if (!name)
479                         name = q->keys[q->n_keys].name;
480                 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
481                         return -EINVAL;
482
483                 log_debug("Looking up RR for %s %s %s",
484                           strna(dns_class_to_string(keys[q->n_keys].class)),
485                           strna(dns_type_to_string(keys[q->n_keys].type)),
486                           keys[q->n_keys].name);
487         }
488
489         LIST_PREPEND(queries, m->dns_queries, q);
490         m->n_dns_queries++;
491         q->manager = m;
492
493         if (ret)
494                 *ret = q;
495         q = NULL;
496
497         return 0;
498 }
499
500 static void dns_query_stop(DnsQuery *q) {
501         assert(q);
502
503         q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
504
505         while (q->transactions)
506                 dns_query_transaction_free(q->transactions);
507 }
508
509 static void dns_query_complete(DnsQuery *q, DnsQueryState state) {
510         assert(q);
511         assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
512         assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
513
514         /* Note that this call might invalidate the query. Callers
515          * should hence not attempt to access the query or transaction
516          * after calling this function. */
517
518         q->state = state;
519
520         dns_query_stop(q);
521         if (q->complete)
522                 q->complete(q);
523 }
524
525 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
526         DnsQuery *q = userdata;
527
528         assert(s);
529         assert(q);
530
531         dns_query_complete(q, DNS_QUERY_TIMEOUT);
532         return 0;
533 }
534
535 int dns_query_go(DnsQuery *q) {
536         DnsScopeMatch found = DNS_SCOPE_NO;
537         DnsScope *s, *first = NULL;
538         DnsQueryTransaction *t;
539         int r;
540
541         assert(q);
542
543         if (q->state != DNS_QUERY_NULL)
544                 return 0;
545
546         assert(q->n_keys > 0);
547
548         LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
549                 DnsScopeMatch match;
550
551                 match = dns_scope_test(s, q->keys[0].name);
552                 if (match < 0)
553                         return match;
554
555                 if (match == DNS_SCOPE_NO)
556                         continue;
557
558                 found = match;
559
560                 if (match == DNS_SCOPE_YES) {
561                         first = s;
562                         break;
563                 } else {
564                         assert(match == DNS_SCOPE_MAYBE);
565
566                         if (!first)
567                                 first = s;
568                 }
569         }
570
571         if (found == DNS_SCOPE_NO)
572                 return -ESRCH;
573
574         r = dns_query_transaction_new(q, NULL, first);
575         if (r < 0)
576                 return r;
577
578         LIST_FOREACH(scopes, s, first->scopes_next) {
579                 DnsScopeMatch match;
580
581                 match = dns_scope_test(s, q->keys[0].name);
582                 if (match < 0)
583                         return match;
584
585                 if (match != found)
586                         continue;
587
588                 r = dns_query_transaction_new(q, NULL, s);
589                 if (r < 0)
590                         return r;
591         }
592
593         q->received = dns_packet_unref(q->received);
594
595         r = sd_event_add_time(q->manager->event, &q->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + QUERY_TIMEOUT_USEC, 0, on_query_timeout, q);
596         if (r < 0)
597                 goto fail;
598
599         q->state = DNS_QUERY_PENDING;
600         q->block_finish++;
601
602         LIST_FOREACH(transactions_by_query, t, q->transactions) {
603                 r = dns_query_transaction_go(t);
604                 if (r < 0)
605                         goto fail;
606         }
607
608         q->block_finish--;
609         dns_query_finish(q);
610
611         return 1;
612
613 fail:
614         dns_query_stop(q);
615         return r;
616 }
617
618 void dns_query_finish(DnsQuery *q) {
619         DnsQueryTransaction *t;
620         DnsQueryState state = DNS_QUERY_NO_SERVERS;
621         DnsPacket *received = NULL;
622
623         assert(q);
624         assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
625
626         /* Note that this call might invalidate the query. Callers
627          * should hence not attempt to access the query or transaction
628          * after calling this function, unless the block_finish
629          * counter was explicitly bumped before doing so. */
630
631         if (q->block_finish > 0)
632                 return;
633
634         LIST_FOREACH(transactions_by_query, t, q->transactions) {
635
636                 /* One of the transactions is still going on, let's wait for it */
637                 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
638                         return;
639
640                 /* One of the transactions is successful, let's use
641                  * it, and copy its data out */
642                 if (t->state == DNS_QUERY_SUCCESS) {
643                         q->received = dns_packet_ref(t->received);
644
645                         /* We simply steal the cached RRs array */
646                         q->cached_rrs = t->cached_rrs;
647                         q->n_cached_rrs = t->n_cached_rrs;
648                         t->cached_rrs = NULL;
649                         t->n_cached_rrs = 0;
650
651                         dns_query_complete(q, DNS_QUERY_SUCCESS);
652                         return;
653                 }
654
655                 /* One of the transactions has failed, let's see
656                  * whether we find anything better, but if not, return
657                  * its response packet */
658                 if (t->state == DNS_QUERY_FAILURE) {
659                         received = t->received;
660                         state = DNS_QUERY_FAILURE;
661                         continue;
662                 }
663
664                 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
665                         state = t->state;
666         }
667
668         if (state == DNS_QUERY_FAILURE)
669                 q->received = dns_packet_ref(received);
670
671         dns_query_complete(q, state);
672 }
673
674 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
675         DnsResourceKey *keys;
676         unsigned i;
677
678         assert(q);
679
680         if (q->n_cname > CNAME_MAX)
681                 return -ELOOP;
682
683         keys = new(DnsResourceKey, q->n_keys);
684         if (!keys)
685                 return -ENOMEM;
686
687         for (i = 0; i < q->n_keys; i++) {
688                 keys[i].class = q->keys[i].class;
689                 keys[i].type = q->keys[i].type;
690                 keys[i].name = strdup(name);
691                 if (!keys[i].name) {
692
693                         for (; i > 0; i--)
694                                 free(keys[i-1].name);
695                         free(keys);
696                         return -ENOMEM;
697                 }
698         }
699
700         for (i = 0; i < q->n_keys; i++)
701                 free(q->keys[i].name);
702         free(q->keys);
703
704         q->keys = keys;
705
706         q->n_cname++;
707
708         dns_query_stop(q);
709         q->state = DNS_QUERY_NULL;
710
711         return 0;
712 }
713
714 int dns_query_matches_rr(DnsQuery *q, DnsResourceRecord *rr) {
715         unsigned i;
716         int r;
717
718         assert(q);
719         assert(rr);
720
721         for (i = 0; i < q->n_keys; i++) {
722
723                 if (rr->key.class != q->keys[i].class)
724                         continue;
725
726                 if (rr->key.type != q->keys[i].type &&
727                     q->keys[i].type != DNS_TYPE_ANY)
728                         continue;
729
730                 r = dns_name_equal(rr->key.name, q->keys[i].name);
731                 if (r != 0)
732                         return r;
733         }
734
735         return 0;
736 }
737
738 int dns_query_matches_cname(DnsQuery *q, DnsResourceRecord *rr) {
739         unsigned i;
740         int r;
741
742         assert(q);
743         assert(rr);
744
745         for (i = 0; i < q->n_keys; i++) {
746
747                 if (rr->key.class != q->keys[i].class)
748                         continue;
749
750                 if (rr->key.type != DNS_TYPE_CNAME)
751                         continue;
752
753                 r = dns_name_equal(rr->key.name, q->keys[i].name);
754                 if (r != 0)
755                         return r;
756         }
757
758         return 0;
759 }
760
761 int dns_query_get_rrs(DnsQuery *q, DnsResourceRecord ***rrs) {
762         int r;
763
764         assert(q);
765         assert(rrs);
766
767         if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
768                 return -EBUSY;
769
770         if (q->received) {
771                 r = dns_packet_extract_rrs(q->received);
772                 if (r < 0)
773                         return r;
774                 if (r == 0) {
775                         *rrs = NULL;
776                         return r;
777                 }
778
779                 *rrs = q->received->rrs;
780                 return r;
781         }
782
783         if (q->cached_rrs) {
784                 *rrs = q->cached_rrs;
785                 return q->n_cached_rrs;
786         }
787
788         return -ESRCH;
789 }
790
791 int dns_query_get_rcode(DnsQuery *q) {
792         assert(q);
793
794         if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
795                 return -EBUSY;
796
797         if (!q->received)
798                 return -ESRCH;
799
800         return DNS_PACKET_RCODE(q->received);
801 }
802
803 int dns_query_get_ifindex(DnsQuery *q) {
804         assert(q);
805
806         if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
807                 return -EBUSY;
808
809         if (!q->received)
810                 return -ESRCH;
811
812         return q->received->ifindex;
813 }