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