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