chiark / gitweb /
resolved: fix serialization of LOC records, check correctness
[elogind.git] / src / resolve / resolved-dns-transaction.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 "af-list.h"
23
24 #include "resolved-dns-transaction.h"
25
26 DnsTransaction* dns_transaction_free(DnsTransaction *t) {
27         DnsQuery *q;
28         DnsZoneItem *i;
29
30         if (!t)
31                 return NULL;
32
33         sd_event_source_unref(t->timeout_event_source);
34
35         dns_question_unref(t->question);
36         dns_packet_unref(t->sent);
37         dns_packet_unref(t->received);
38         dns_answer_unref(t->cached);
39
40         dns_stream_free(t->stream);
41
42         if (t->scope) {
43                 LIST_REMOVE(transactions_by_scope, t->scope->transactions, t);
44
45                 if (t->id != 0)
46                         hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id));
47         }
48
49         while ((q = set_steal_first(t->queries)))
50                 set_remove(q->transactions, t);
51         set_free(t->queries);
52
53         while ((i = set_steal_first(t->zone_items)))
54                 i->probe_transaction = NULL;
55         set_free(t->zone_items);
56
57         free(t);
58         return NULL;
59 }
60
61 DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_free);
62
63 void dns_transaction_gc(DnsTransaction *t) {
64         assert(t);
65
66         if (t->block_gc > 0)
67                 return;
68
69         if (set_isempty(t->queries) && set_isempty(t->zone_items))
70                 dns_transaction_free(t);
71 }
72
73 int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsQuestion *q) {
74         _cleanup_(dns_transaction_freep) DnsTransaction *t = NULL;
75         int r;
76
77         assert(ret);
78         assert(s);
79         assert(q);
80
81         r = hashmap_ensure_allocated(&s->manager->dns_transactions, NULL, NULL);
82         if (r < 0)
83                 return r;
84
85         t = new0(DnsTransaction, 1);
86         if (!t)
87                 return -ENOMEM;
88
89         t->question = dns_question_ref(q);
90
91         do
92                 random_bytes(&t->id, sizeof(t->id));
93         while (t->id == 0 ||
94                hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(t->id)));
95
96         r = hashmap_put(s->manager->dns_transactions, UINT_TO_PTR(t->id), t);
97         if (r < 0) {
98                 t->id = 0;
99                 return r;
100         }
101
102         LIST_PREPEND(transactions_by_scope, s->transactions, t);
103         t->scope = s;
104
105         if (ret)
106                 *ret = t;
107
108         t = NULL;
109
110         return 0;
111 }
112
113 static void dns_transaction_stop(DnsTransaction *t) {
114         assert(t);
115
116         t->timeout_event_source = sd_event_source_unref(t->timeout_event_source);
117         t->stream = dns_stream_free(t->stream);
118 }
119
120 static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
121         DnsZoneItem *z;
122         Iterator i;
123
124         assert(t);
125         assert(p);
126
127         if (manager_our_packet(t->scope->manager, p) != 0)
128                 return;
129
130         log_debug("Transaction on scope %s on %s/%s got tentative packet",
131                   dns_protocol_to_string(t->scope->protocol),
132                   t->scope->link ? t->scope->link->name : "*",
133                   t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
134
135         t->block_gc++;
136         SET_FOREACH(z, t->zone_items, i)
137                 dns_zone_item_conflict(z);
138         t->block_gc--;
139
140         dns_transaction_gc(t);
141 }
142
143 void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
144         DnsQuery *q;
145         DnsZoneItem *z;
146         Iterator i;
147
148         assert(t);
149         assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
150         assert(IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
151
152         /* Note that this call might invalidate the query. Callers
153          * should hence not attempt to access the query or transaction
154          * after calling this function. */
155
156         log_debug("Transaction on scope %s on %s/%s now complete with <%s>",
157                   dns_protocol_to_string(t->scope->protocol),
158                   t->scope->link ? t->scope->link->name : "*",
159                   t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
160                   dns_transaction_state_to_string(state));
161
162         t->state = state;
163
164         dns_transaction_stop(t);
165
166         /* Notify all queries that are interested, but make sure the
167          * transaction isn't freed while we are still looking at it */
168         t->block_gc++;
169         SET_FOREACH(q, t->queries, i)
170                 dns_query_ready(q);
171         SET_FOREACH(z, t->zone_items, i)
172                 dns_zone_item_ready(z);
173         t->block_gc--;
174
175         dns_transaction_gc(t);
176 }
177
178 static int on_stream_complete(DnsStream *s, int error) {
179         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
180         DnsTransaction *t;
181
182         assert(s);
183         assert(s->transaction);
184
185         /* Copy the data we care about out of the stream before we
186          * destroy it. */
187         t = s->transaction;
188         p = dns_packet_ref(s->read_packet);
189
190         t->stream = dns_stream_free(t->stream);
191
192         if (error != 0) {
193                 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
194                 return 0;
195         }
196
197         t->block_gc++;
198         dns_transaction_process_reply(t, p);
199         t->block_gc--;
200
201         /* If the response wasn't useful, then complete the transition now */
202         if (t->state == DNS_TRANSACTION_PENDING)
203                 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
204
205         return 0;
206 }
207
208 static int dns_transaction_open_tcp(DnsTransaction *t) {
209         _cleanup_close_ int fd = -1;
210         int r;
211
212         assert(t);
213
214         if (t->stream)
215                 return 0;
216
217         if (t->scope->protocol == DNS_PROTOCOL_DNS)
218                 fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53);
219         else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
220
221                 /* When we already received a query to this (but it was truncated), send to its sender address */
222                 if (t->received)
223                         fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port);
224                 else {
225                         union in_addr_union address;
226                         int family;
227
228                         /* Otherwise, try to talk to the owner of a
229                          * the IP address, in case this is a reverse
230                          * PTR lookup */
231                         r = dns_question_extract_reverse_address(t->question, &family, &address);
232                         if (r < 0)
233                                 return r;
234                         if (r == 0)
235                                 return -EINVAL;
236
237                         fd = dns_scope_tcp_socket(t->scope, family, &address, 5355);
238                 }
239         } else
240                 return -EAFNOSUPPORT;
241
242         if (fd < 0)
243                 return fd;
244
245         r = dns_stream_new(t->scope->manager, &t->stream, t->scope->protocol, fd);
246         if (r < 0)
247                 return r;
248
249         fd = -1;
250
251         r = dns_stream_write_packet(t->stream, t->sent);
252         if (r < 0) {
253                 t->stream = dns_stream_free(t->stream);
254                 return r;
255         }
256
257         t->received = dns_packet_unref(t->received);
258         t->stream->complete = on_stream_complete;
259         t->stream->transaction = t;
260
261         /* The interface index is difficult to determine if we are
262          * connecting to the local host, hence fill this in right away
263          * instead of determining it from the socket */
264         if (t->scope->link)
265                 t->stream->ifindex = t->scope->link->ifindex;
266
267         return 0;
268 }
269
270 void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
271         int r;
272
273         assert(t);
274         assert(p);
275         assert(t->state == DNS_TRANSACTION_PENDING);
276
277         /* Note that this call might invalidate the query. Callers
278          * should hence not attempt to access the query or transaction
279          * after calling this function. */
280
281         if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
282                 assert(t->scope->link);
283
284                 /* For LLMNR we will not accept any packets from other
285                  * interfaces */
286
287                 if (p->ifindex != t->scope->link->ifindex)
288                         return;
289
290                 if (p->family != t->scope->family)
291                         return;
292
293                 /* Tentative packets are not full responses but still
294                  * useful for identifying uniqueness conflicts during
295                  * probing. */
296                 if (DNS_PACKET_T(p)) {
297                         dns_transaction_tentative(t, p);
298                         return;
299                 }
300         }
301
302         if (t->scope->protocol == DNS_PROTOCOL_DNS) {
303
304                 /* For DNS we are fine with accepting packets on any
305                  * interface, but the source IP address must be one of
306                  * a valid DNS server */
307
308                 if (!dns_scope_good_dns_server(t->scope, p->family, &p->sender))
309                         return;
310
311                 if (p->sender_port != 53)
312                         return;
313         }
314
315         if (t->received != p) {
316                 dns_packet_unref(t->received);
317                 t->received = dns_packet_ref(p);
318         }
319
320         if (p->ipproto == IPPROTO_TCP) {
321                 if (DNS_PACKET_TC(p)) {
322                         /* Truncated via TCP? Somebody must be fucking with us */
323                         dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
324                         return;
325                 }
326
327                 if (DNS_PACKET_ID(p) != t->id) {
328                         /* Not the reply to our query? Somebody must be fucking with us */
329                         dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
330                         return;
331                 }
332         }
333
334         if (DNS_PACKET_TC(p)) {
335                 /* Response was truncated, let's try again with good old TCP */
336                 r = dns_transaction_open_tcp(t);
337                 if (r == -ESRCH) {
338                         /* No servers found? Damn! */
339                         dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
340                         return;
341                 }
342                 if (r < 0) {
343                         /* On LLMNR, if we cannot connect to the host,
344                          * we immediately give up */
345                         if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
346                                 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
347                                 return;
348                         }
349
350                         /* On DNS, couldn't send? Try immediately again, with a new server */
351                         dns_scope_next_dns_server(t->scope);
352
353                         r = dns_transaction_go(t);
354                         if (r < 0) {
355                                 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
356                                 return;
357                         }
358
359                         return;
360                 }
361         }
362
363         /* Parse and update the cache */
364         r = dns_packet_extract(p);
365         if (r < 0) {
366                 dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
367                 return;
368         }
369
370         /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */
371         dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0);
372
373         if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS)
374                 dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
375         else
376                 dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
377 }
378
379 static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdata) {
380         DnsTransaction *t = userdata;
381         int r;
382
383         assert(s);
384         assert(t);
385
386         /* Timeout reached? Try again, with a new server */
387         dns_scope_next_dns_server(t->scope);
388
389         r = dns_transaction_go(t);
390         if (r < 0)
391                 dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
392
393         return 0;
394 }
395
396 static int dns_transaction_make_packet(DnsTransaction *t) {
397         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
398         unsigned n, added = 0;
399         int r;
400
401         assert(t);
402
403         if (t->sent)
404                 return 0;
405
406         r = dns_packet_new_query(&p, t->scope->protocol, 0);
407         if (r < 0)
408                 return r;
409
410         for (n = 0; n < t->question->n_keys; n++) {
411                 r = dns_scope_good_key(t->scope, t->question->keys[n]);
412                 if (r < 0)
413                         return r;
414                 if (r == 0)
415                         continue;
416
417                 r = dns_packet_append_key(p, t->question->keys[n], NULL);
418                 if (r < 0)
419                         return r;
420
421                 added++;
422         }
423
424         if (added <= 0)
425                 return -EDOM;
426
427         DNS_PACKET_HEADER(p)->qdcount = htobe16(added);
428         DNS_PACKET_HEADER(p)->id = t->id;
429
430         t->sent = p;
431         p = NULL;
432
433         return 0;
434 }
435
436 int dns_transaction_go(DnsTransaction *t) {
437         bool had_stream;
438         int r;
439
440         assert(t);
441
442         had_stream = !!t->stream;
443
444         dns_transaction_stop(t);
445
446         log_debug("Beginning transaction on scope %s on %s/%s",
447                   dns_protocol_to_string(t->scope->protocol),
448                   t->scope->link ? t->scope->link->name : "*",
449                   t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family));
450
451         if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
452                 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
453                 return 0;
454         }
455
456         if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) {
457                 /* If we already tried via a stream, then we don't
458                  * retry on LLMNR. See RFC 4795, Section 2.7. */
459                 dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
460                 return 0;
461         }
462
463         t->n_attempts++;
464         t->received = dns_packet_unref(t->received);
465         t->cached = dns_answer_unref(t->cached);
466         t->cached_rcode = 0;
467
468         /* First, let's try the cache */
469         dns_cache_prune(&t->scope->cache);
470         r = dns_cache_lookup(&t->scope->cache, t->question, &t->cached_rcode, &t->cached);
471         if (r < 0)
472                 return r;
473         if (r > 0) {
474                 if (t->cached_rcode == DNS_RCODE_SUCCESS)
475                         dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
476                 else
477                         dns_transaction_complete(t, DNS_TRANSACTION_FAILURE);
478                 return 0;
479         }
480
481         /* Otherwise, we need to ask the network */
482         r = dns_transaction_make_packet(t);
483         if (r == -EDOM) {
484                 /* Not the right request to make on this network?
485                  * (i.e. an A request made on IPv6 or an AAAA request
486                  * made on IPv4, on LLMNR or mDNS.) */
487                 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
488                 return 0;
489         }
490         if (r < 0)
491                 return r;
492
493         if (t->scope->protocol == DNS_PROTOCOL_LLMNR &&
494             (dns_question_endswith(t->question, "in-addr.arpa") > 0 ||
495              dns_question_endswith(t->question, "ip6.arpa") > 0)) {
496
497                 /* RFC 4795, Section 2.4. says reverse lookups shall
498                  * always be made via TCP on LLMNR */
499                 r = dns_transaction_open_tcp(t);
500         } else {
501                 /* Try via UDP, and if that fails due to large size try via TCP */
502                 r = dns_scope_send(t->scope, t->sent);
503                 if (r == -EMSGSIZE)
504                         r = dns_transaction_open_tcp(t);
505         }
506         if (r == -ESRCH) {
507                 /* No servers to send this to? */
508                 dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
509                 return 0;
510         }
511         if (r < 0) {
512                 /* Couldn't send? Try immediately again, with a new server */
513                 dns_scope_next_dns_server(t->scope);
514
515                 return dns_transaction_go(t);
516         }
517
518         r = sd_event_add_time(t->scope->manager->event, &t->timeout_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + TRANSACTION_TIMEOUT_USEC(t->scope->protocol), 0, on_transaction_timeout, t);
519         if (r < 0)
520                 return r;
521
522         t->state = DNS_TRANSACTION_PENDING;
523         return 1;
524 }
525
526 static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = {
527         [DNS_TRANSACTION_NULL] = "null",
528         [DNS_TRANSACTION_PENDING] = "pending",
529         [DNS_TRANSACTION_FAILURE] = "failure",
530         [DNS_TRANSACTION_SUCCESS] = "success",
531         [DNS_TRANSACTION_NO_SERVERS] = "no-servers",
532         [DNS_TRANSACTION_TIMEOUT] = "timeout",
533         [DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
534         [DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
535         [DNS_TRANSACTION_RESOURCES] = "resources",
536         [DNS_TRANSACTION_ABORTED] = "aborted",
537 };
538 DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);