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