chiark / gitweb /
resolved: add more const
[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, t->scope->protocol, 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->scope->protocol == DNS_PROTOCOL_DNS)
233                 return -ENOTSUP;
234
235         if (t->tcp_fd >= 0)
236                 return 0;
237
238         t->tcp_written = 0;
239         t->tcp_read = 0;
240         t->received = dns_packet_unref(t->received);
241
242         t->tcp_fd = dns_scope_tcp_socket(t->scope);
243         if (t->tcp_fd < 0)
244                 return t->tcp_fd;
245
246         r = sd_event_add_io(t->query->manager->event, &t->tcp_event_source, t->tcp_fd, EPOLLIN|EPOLLOUT, on_tcp_ready, t);
247         if (r < 0) {
248                 t->tcp_fd = safe_close(t->tcp_fd);
249                 return r;
250         }
251
252         return 0;
253 }
254
255 void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) {
256         int r;
257
258         assert(t);
259         assert(p);
260         assert(t->state == DNS_QUERY_PENDING);
261
262         /* Note that this call might invalidate the query. Callers
263          * should hence not attempt to access the query or transaction
264          * after calling this function. */
265
266         if (t->received != p) {
267                 dns_packet_unref(t->received);
268                 t->received = dns_packet_ref(p);
269         }
270
271         if (t->tcp_fd >= 0) {
272                 if (DNS_PACKET_TC(p)) {
273                         /* Truncated via TCP? Somebody must be fucking with us */
274                         dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
275                         return;
276                 }
277
278                 if (DNS_PACKET_ID(p) != t->id) {
279                         /* Not the reply to our query? Somebody must be fucking with us */
280                         dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
281                         return;
282                 }
283         }
284
285         if (DNS_PACKET_TC(p)) {
286                 /* Response was truncated, let's try again with good old TCP */
287                 r = dns_query_transaction_open_tcp(t);
288                 if (r == -ESRCH) {
289                         /* No servers found? Damn! */
290                         dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
291                         return;
292                 }
293                 if (r < 0) {
294                         /* Couldn't send? Try immediately again, with a new server */
295                         dns_scope_next_dns_server(t->scope);
296
297                         r = dns_query_transaction_go(t);
298                         if (r < 0) {
299                                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
300                                 return;
301                         }
302
303                         return;
304                 }
305         }
306
307         /* Parse and update the cache */
308         r = dns_packet_extract_rrs(p);
309         if (r < 0) {
310                 dns_query_transaction_complete(t, DNS_QUERY_INVALID_REPLY);
311                 return;
312         } else if (r > 0)
313                 dns_cache_put_rrs(&t->scope->cache, p->rrs, r, 0);
314
315         if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
316                 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
317         else
318                 dns_query_transaction_complete(t, DNS_QUERY_FAILURE);
319 }
320
321 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
322         DnsQueryTransaction *t = userdata;
323         int r;
324
325         assert(s);
326         assert(t);
327
328         /* Timeout reached? Try again, with a new server */
329         dns_scope_next_dns_server(t->scope);
330
331         r = dns_query_transaction_go(t);
332         if (r < 0)
333                 dns_query_transaction_complete(t, DNS_QUERY_RESOURCES);
334
335         return 0;
336 }
337
338 static int dns_query_make_packet(DnsQueryTransaction *t) {
339         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
340         unsigned n, added = 0;
341         int r;
342
343         assert(t);
344
345         if (t->sent)
346                 return 0;
347
348         r = dns_packet_new_query(&p, t->scope->protocol, 0);
349         if (r < 0)
350                 return r;
351
352         for (n = 0; n < t->query->n_keys; n++) {
353                 r = dns_scope_good_key(t->scope, &t->query->keys[n]);
354                 if (r < 0)
355                         return r;
356                 if (r == 0)
357                         continue;
358
359                 r = dns_packet_append_key(p, &t->query->keys[n], NULL);
360                 if (r < 0)
361                         return r;
362
363                 added++;
364         }
365
366         if (added <= 0)
367                 return -EDOM;
368
369         DNS_PACKET_HEADER(p)->qdcount = htobe16(added);
370         DNS_PACKET_HEADER(p)->id = t->id;
371
372         t->sent = p;
373         p = NULL;
374
375         return 0;
376 }
377
378 static int dns_query_transaction_go(DnsQueryTransaction *t) {
379         int r;
380
381         assert(t);
382
383         dns_query_transaction_stop(t);
384
385         if (t->n_attempts >= ATTEMPTS_MAX) {
386                 dns_query_transaction_complete(t, DNS_QUERY_ATTEMPTS_MAX);
387                 return 0;
388         }
389
390         t->n_attempts++;
391         t->received = dns_packet_unref(t->received);
392         t->cached_rrs = dns_resource_record_freev(t->cached_rrs, t->n_cached_rrs);
393         t->n_cached_rrs = 0;
394
395         /* First, let's try the cache */
396         dns_cache_prune(&t->scope->cache);
397         r = dns_cache_lookup_many(&t->scope->cache, t->query->keys, t->query->n_keys, &t->cached_rrs);
398         if (r < 0)
399                 return r;
400         if (r > 0) {
401                 t->n_cached_rrs = r;
402                 dns_query_transaction_complete(t, DNS_QUERY_SUCCESS);
403                 return 0;
404         }
405
406         /* Otherwise, we need to ask the network */
407         r = dns_query_make_packet(t);
408         if (r == -EDOM) {
409                 /* Not the right request to make on this network?
410                  * (i.e. an A request made on IPv6 or an AAAA request
411                  * made on IPv4, on LLMNR or mDNS.) */
412                 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
413                 return 0;
414         }
415         if (r < 0)
416                 return r;
417
418         /* Try via UDP, and if that fails due to large size try via TCP */
419         r = dns_scope_send(t->scope, t->sent);
420         if (r == -EMSGSIZE)
421                 r = dns_query_transaction_open_tcp(t);
422         if (r == -ESRCH) {
423                 /* No servers to send this to? */
424                 dns_query_transaction_complete(t, DNS_QUERY_NO_SERVERS);
425                 return 0;
426         }
427         if (r < 0) {
428                 /* Couldn't send? Try immediately again, with a new server */
429                 dns_scope_next_dns_server(t->scope);
430
431                 return dns_query_transaction_go(t);
432         }
433
434         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);
435         if (r < 0)
436                 return r;
437
438         t->state = DNS_QUERY_PENDING;
439         return 1;
440 }
441
442 DnsQuery *dns_query_free(DnsQuery *q) {
443         unsigned n;
444
445         if (!q)
446                 return NULL;
447
448         sd_bus_message_unref(q->request);
449         dns_packet_unref(q->received);
450
451         dns_resource_record_freev(q->cached_rrs, q->n_cached_rrs);
452
453         sd_event_source_unref(q->timeout_event_source);
454
455         while (q->transactions)
456                 dns_query_transaction_free(q->transactions);
457
458         if (q->manager) {
459                 LIST_REMOVE(queries, q->manager->dns_queries, q);
460                 q->manager->n_dns_queries--;
461         }
462
463         for (n = 0; n < q->n_keys; n++)
464                 free(q->keys[n].name);
465         free(q->keys);
466         free(q);
467
468         return NULL;
469 }
470
471 int dns_query_new(Manager *m, DnsQuery **ret, DnsResourceKey *keys, unsigned n_keys) {
472         _cleanup_(dns_query_freep) DnsQuery *q = NULL;
473         const char *name = NULL;
474
475         assert(m);
476
477         if (n_keys <= 0 || n_keys >= 65535)
478                 return -EINVAL;
479
480         if (m->n_dns_queries >= QUERIES_MAX)
481                 return -EBUSY;
482
483         assert(keys);
484
485         q = new0(DnsQuery, 1);
486         if (!q)
487                 return -ENOMEM;
488
489         q->keys = new(DnsResourceKey, n_keys);
490         if (!q->keys)
491                 return -ENOMEM;
492
493         for (q->n_keys = 0; q->n_keys < n_keys; q->n_keys++) {
494                 q->keys[q->n_keys].class = keys[q->n_keys].class;
495                 q->keys[q->n_keys].type = keys[q->n_keys].type;
496                 q->keys[q->n_keys].name = strdup(keys[q->n_keys].name);
497                 if (!q->keys[q->n_keys].name)
498                         return -ENOMEM;
499
500                 if (!name)
501                         name = q->keys[q->n_keys].name;
502                 else if (!dns_name_equal(name, q->keys[q->n_keys].name))
503                         return -EINVAL;
504
505                 log_debug("Looking up RR for %s %s %s",
506                           strna(dns_class_to_string(keys[q->n_keys].class)),
507                           strna(dns_type_to_string(keys[q->n_keys].type)),
508                           keys[q->n_keys].name);
509         }
510
511         LIST_PREPEND(queries, m->dns_queries, q);
512         m->n_dns_queries++;
513         q->manager = m;
514
515         if (ret)
516                 *ret = q;
517         q = NULL;
518
519         return 0;
520 }
521
522 static void dns_query_stop(DnsQuery *q) {
523         assert(q);
524
525         q->timeout_event_source = sd_event_source_unref(q->timeout_event_source);
526
527         while (q->transactions)
528                 dns_query_transaction_free(q->transactions);
529 }
530
531 static void dns_query_complete(DnsQuery *q, DnsQueryState state) {
532         assert(q);
533         assert(!IN_SET(state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
534         assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
535
536         /* Note that this call might invalidate the query. Callers
537          * should hence not attempt to access the query or transaction
538          * after calling this function. */
539
540         q->state = state;
541
542         dns_query_stop(q);
543         if (q->complete)
544                 q->complete(q);
545 }
546
547 static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
548         DnsQuery *q = userdata;
549
550         assert(s);
551         assert(q);
552
553         dns_query_complete(q, DNS_QUERY_TIMEOUT);
554         return 0;
555 }
556
557 int dns_query_go(DnsQuery *q) {
558         DnsScopeMatch found = DNS_SCOPE_NO;
559         DnsScope *s, *first = NULL;
560         DnsQueryTransaction *t;
561         int r;
562
563         assert(q);
564
565         if (q->state != DNS_QUERY_NULL)
566                 return 0;
567
568         assert(q->n_keys > 0);
569
570         LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
571                 DnsScopeMatch match;
572
573                 match = dns_scope_good_domain(s, q->keys[0].name);
574                 if (match < 0)
575                         return match;
576
577                 if (match == DNS_SCOPE_NO)
578                         continue;
579
580                 found = match;
581
582                 if (match == DNS_SCOPE_YES) {
583                         first = s;
584                         break;
585                 } else {
586                         assert(match == DNS_SCOPE_MAYBE);
587
588                         if (!first)
589                                 first = s;
590                 }
591         }
592
593         if (found == DNS_SCOPE_NO)
594                 return -ESRCH;
595
596         r = dns_query_transaction_new(q, NULL, first);
597         if (r < 0)
598                 return r;
599
600         LIST_FOREACH(scopes, s, first->scopes_next) {
601                 DnsScopeMatch match;
602
603                 match = dns_scope_good_domain(s, q->keys[0].name);
604                 if (match < 0)
605                         return match;
606
607                 if (match != found)
608                         continue;
609
610                 r = dns_query_transaction_new(q, NULL, s);
611                 if (r < 0)
612                         return r;
613         }
614
615         q->received = dns_packet_unref(q->received);
616
617         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);
618         if (r < 0)
619                 goto fail;
620
621         q->state = DNS_QUERY_PENDING;
622         q->block_finish++;
623
624         LIST_FOREACH(transactions_by_query, t, q->transactions) {
625                 r = dns_query_transaction_go(t);
626                 if (r < 0)
627                         goto fail;
628         }
629
630         q->block_finish--;
631         dns_query_finish(q);
632
633         return 1;
634
635 fail:
636         dns_query_stop(q);
637         return r;
638 }
639
640 void dns_query_finish(DnsQuery *q) {
641         DnsQueryTransaction *t;
642         DnsQueryState state = DNS_QUERY_NO_SERVERS;
643         DnsPacket *received = NULL;
644
645         assert(q);
646         assert(IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING));
647
648         /* Note that this call might invalidate the query. Callers
649          * should hence not attempt to access the query or transaction
650          * after calling this function, unless the block_finish
651          * counter was explicitly bumped before doing so. */
652
653         if (q->block_finish > 0)
654                 return;
655
656         LIST_FOREACH(transactions_by_query, t, q->transactions) {
657
658                 /* One of the transactions is still going on, let's wait for it */
659                 if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL)
660                         return;
661
662                 /* One of the transactions is successful, let's use
663                  * it, and copy its data out */
664                 if (t->state == DNS_QUERY_SUCCESS) {
665                         q->received = dns_packet_ref(t->received);
666
667                         /* We simply steal the cached RRs array */
668                         q->cached_rrs = t->cached_rrs;
669                         q->n_cached_rrs = t->n_cached_rrs;
670                         t->cached_rrs = NULL;
671                         t->n_cached_rrs = 0;
672
673                         dns_query_complete(q, DNS_QUERY_SUCCESS);
674                         return;
675                 }
676
677                 /* One of the transactions has failed, let's see
678                  * whether we find anything better, but if not, return
679                  * its response packet */
680                 if (t->state == DNS_QUERY_FAILURE) {
681                         received = t->received;
682                         state = DNS_QUERY_FAILURE;
683                         continue;
684                 }
685
686                 if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS)
687                         state = t->state;
688         }
689
690         if (state == DNS_QUERY_FAILURE)
691                 q->received = dns_packet_ref(received);
692
693         dns_query_complete(q, state);
694 }
695
696 int dns_query_cname_redirect(DnsQuery *q, const char *name) {
697         DnsResourceKey *keys;
698         unsigned i;
699
700         assert(q);
701
702         if (q->n_cname > CNAME_MAX)
703                 return -ELOOP;
704
705         keys = new(DnsResourceKey, q->n_keys);
706         if (!keys)
707                 return -ENOMEM;
708
709         for (i = 0; i < q->n_keys; i++) {
710                 keys[i].class = q->keys[i].class;
711                 keys[i].type = q->keys[i].type;
712                 keys[i].name = strdup(name);
713                 if (!keys[i].name) {
714
715                         for (; i > 0; i--)
716                                 free(keys[i-1].name);
717                         free(keys);
718                         return -ENOMEM;
719                 }
720         }
721
722         for (i = 0; i < q->n_keys; i++)
723                 free(q->keys[i].name);
724         free(q->keys);
725
726         q->keys = keys;
727
728         q->n_cname++;
729
730         dns_query_stop(q);
731         q->state = DNS_QUERY_NULL;
732
733         return 0;
734 }
735
736 int dns_query_matches_rr(DnsQuery *q, DnsResourceRecord *rr) {
737         unsigned i;
738         int r;
739
740         assert(q);
741         assert(rr);
742
743         for (i = 0; i < q->n_keys; i++) {
744
745                 if (rr->key.class != q->keys[i].class)
746                         continue;
747
748                 if (rr->key.type != q->keys[i].type &&
749                     q->keys[i].type != DNS_TYPE_ANY)
750                         continue;
751
752                 r = dns_name_equal(rr->key.name, q->keys[i].name);
753                 if (r != 0)
754                         return r;
755         }
756
757         return 0;
758 }
759
760 int dns_query_matches_cname(DnsQuery *q, DnsResourceRecord *rr) {
761         unsigned i;
762         int r;
763
764         assert(q);
765         assert(rr);
766
767         for (i = 0; i < q->n_keys; i++) {
768
769                 if (rr->key.class != q->keys[i].class)
770                         continue;
771
772                 if (rr->key.type != DNS_TYPE_CNAME)
773                         continue;
774
775                 r = dns_name_equal(rr->key.name, q->keys[i].name);
776                 if (r != 0)
777                         return r;
778         }
779
780         return 0;
781 }
782
783 int dns_query_get_rrs(DnsQuery *q, DnsResourceRecord ***rrs) {
784         int r;
785
786         assert(q);
787         assert(rrs);
788
789         if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
790                 return -EBUSY;
791
792         if (q->received) {
793                 r = dns_packet_extract_rrs(q->received);
794                 if (r < 0)
795                         return r;
796                 if (r == 0) {
797                         *rrs = NULL;
798                         return r;
799                 }
800
801                 *rrs = q->received->rrs;
802                 return r;
803         }
804
805         if (q->cached_rrs) {
806                 *rrs = q->cached_rrs;
807                 return q->n_cached_rrs;
808         }
809
810         return -ESRCH;
811 }
812
813 int dns_query_get_rcode(DnsQuery *q) {
814         assert(q);
815
816         if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
817                 return -EBUSY;
818
819         if (!q->received)
820                 return -ESRCH;
821
822         return DNS_PACKET_RCODE(q->received);
823 }
824
825 int dns_query_get_ifindex(DnsQuery *q) {
826         assert(q);
827
828         if (IN_SET(q->state, DNS_QUERY_NULL, DNS_QUERY_PENDING))
829                 return -EBUSY;
830
831         if (!q->received)
832                 return -ESRCH;
833
834         return q->received->ifindex;
835 }