]>
Commit | Line | Data |
---|---|---|
74b2466e LP |
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 | |
8ba9fd9c | 28 | #define CNAME_MAX 8 |
39762fdf | 29 | #define QUERIES_MAX 2048 |
8ba9fd9c LP |
30 | |
31 | static int dns_query_transaction_start(DnsQueryTransaction *t); | |
74b2466e LP |
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); | |
ad867662 LP |
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); | |
74b2466e LP |
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 | ||
ad867662 LP |
74 | t->tcp_fd = -1; |
75 | ||
74b2466e LP |
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 | ||
ad867662 LP |
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 | ||
74b2466e LP |
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 | ||
8ba9fd9c | 117 | if (state != DNS_QUERY_PENDING) { |
ad867662 LP |
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 | } | |
74b2466e | 245 | |
ad867662 | 246 | return 0; |
74b2466e LP |
247 | } |
248 | ||
ad867662 LP |
249 | void dns_query_transaction_reply(DnsQueryTransaction *t, DnsPacket *p) { |
250 | int r; | |
251 | ||
74b2466e LP |
252 | assert(t); |
253 | assert(p); | |
254 | ||
8ba9fd9c | 255 | if (t->state != DNS_QUERY_PENDING) |
ad867662 LP |
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); | |
8ba9fd9c LP |
280 | if (r == -ESRCH) { |
281 | /* No servers found? Damn! */ | |
282 | dns_query_transaction_set_state(t, DNS_QUERY_NO_SERVERS); | |
283 | return; | |
284 | } | |
ad867662 | 285 | if (r < 0) { |
8ba9fd9c LP |
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 | ||
ad867662 LP |
295 | return; |
296 | } | |
297 | } | |
74b2466e | 298 | |
b9d394ea LP |
299 | if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) |
300 | dns_query_transaction_set_state(t, DNS_QUERY_SUCCESS); | |
301 | else | |
74b2466e | 302 | dns_query_transaction_set_state(t, DNS_QUERY_FAILURE); |
74b2466e LP |
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) | |
ad867662 | 317 | dns_query_transaction_set_state(t, DNS_QUERY_RESOURCES); |
74b2466e LP |
318 | |
319 | return 0; | |
320 | } | |
321 | ||
ad867662 | 322 | static int dns_query_make_packet(DnsQueryTransaction *t) { |
74b2466e LP |
323 | _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; |
324 | unsigned n; | |
325 | int r; | |
326 | ||
327 | assert(t); | |
328 | ||
ad867662 | 329 | if (t->sent) |
74b2466e | 330 | return 0; |
74b2466e LP |
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 | ||
ad867662 LP |
345 | t->sent = p; |
346 | p = NULL; | |
347 | ||
348 | return 0; | |
349 | } | |
350 | ||
8ba9fd9c | 351 | static int dns_query_transaction_start(DnsQueryTransaction *t) { |
ad867662 LP |
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 | } | |
ad867662 LP |
362 | |
363 | r = dns_query_make_packet(t); | |
364 | if (r < 0) | |
365 | return r; | |
366 | ||
8ba9fd9c LP |
367 | t->n_attempts++; |
368 | t->received = dns_packet_unref(t->received); | |
369 | ||
ad867662 LP |
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 | } | |
74b2466e LP |
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 | ||
ad867662 LP |
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; | |
74b2466e | 389 | |
8ba9fd9c | 390 | dns_query_transaction_set_state(t, DNS_QUERY_PENDING); |
ad867662 | 391 | return 1; |
74b2466e LP |
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); | |
ad867662 | 401 | dns_packet_unref(q->received); |
74b2466e LP |
402 | sd_event_source_unref(q->timeout_event_source); |
403 | ||
404 | while (q->transactions) | |
405 | dns_query_transaction_free(q->transactions); | |
406 | ||
39762fdf | 407 | if (q->manager) { |
74b2466e | 408 | LIST_REMOVE(queries, q->manager->dns_queries, q); |
39762fdf LP |
409 | q->manager->n_dns_queries--; |
410 | } | |
74b2466e LP |
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; | |
74b2466e | 422 | const char *name = NULL; |
74b2466e LP |
423 | |
424 | assert(m); | |
425 | ||
426 | if (n_keys <= 0 || n_keys >= 65535) | |
427 | return -EINVAL; | |
428 | ||
39762fdf LP |
429 | if (m->n_dns_queries >= QUERIES_MAX) |
430 | return -EBUSY; | |
431 | ||
74b2466e LP |
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); | |
39762fdf | 456 | m->n_dns_queries++; |
74b2466e LP |
457 | q->manager = m; |
458 | ||
8ba9fd9c LP |
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) { | |
74b2466e LP |
517 | DnsScopeMatch match; |
518 | ||
8ba9fd9c | 519 | match = dns_scope_test(s, q->keys[0].name); |
74b2466e LP |
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) | |
8ba9fd9c | 540 | return -ESRCH; |
74b2466e LP |
541 | |
542 | r = dns_query_transaction_new(q, NULL, first); | |
543 | if (r < 0) | |
544 | return r; | |
545 | ||
74b2466e LP |
546 | LIST_FOREACH(scopes, s, first->scopes_next) { |
547 | DnsScopeMatch match; | |
548 | ||
8ba9fd9c | 549 | match = dns_scope_test(s, q->keys[0].name); |
74b2466e LP |
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; | |
74b2466e LP |
559 | } |
560 | ||
8ba9fd9c | 561 | q->received = dns_packet_unref(q->received); |
74b2466e LP |
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 | ||
8ba9fd9c | 567 | dns_query_set_state(q, DNS_QUERY_PENDING); |
74b2466e LP |
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 | ||
8ba9fd9c | 575 | if (q->state != DNS_QUERY_PENDING) |
74b2466e LP |
576 | break; |
577 | } | |
578 | ||
8ba9fd9c | 579 | return 1; |
74b2466e LP |
580 | |
581 | fail: | |
8ba9fd9c | 582 | dns_query_stop(q); |
74b2466e LP |
583 | return r; |
584 | } | |
585 | ||
586 | void dns_query_finish(DnsQuery *q) { | |
587 | DnsQueryTransaction *t; | |
ad867662 LP |
588 | DnsQueryState state = DNS_QUERY_NO_SERVERS; |
589 | DnsPacket *received = NULL; | |
74b2466e LP |
590 | |
591 | assert(q); | |
592 | ||
8ba9fd9c | 593 | if (q->state != DNS_QUERY_PENDING) |
74b2466e LP |
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 */ | |
8ba9fd9c | 599 | if (t->state == DNS_QUERY_PENDING || t->state == DNS_QUERY_NULL) |
74b2466e LP |
600 | return; |
601 | ||
ad867662 | 602 | /* One of the transactions is successful, let's use it */ |
74b2466e | 603 | if (t->state == DNS_QUERY_SUCCESS) { |
ad867662 | 604 | q->received = dns_packet_ref(t->received); |
74b2466e LP |
605 | dns_query_set_state(q, DNS_QUERY_SUCCESS); |
606 | return; | |
607 | } | |
608 | ||
ad867662 LP |
609 | /* One of the transactions has failed, let's see |
610 | * whether we find anything better, but if not, return | |
611 | * its response packet */ | |
74b2466e | 612 | if (t->state == DNS_QUERY_FAILURE) { |
ad867662 | 613 | received = t->received; |
74b2466e | 614 | state = DNS_QUERY_FAILURE; |
ad867662 LP |
615 | continue; |
616 | } | |
74b2466e | 617 | |
ad867662 | 618 | if (state == DNS_QUERY_NO_SERVERS && t->state != DNS_QUERY_NO_SERVERS) |
74b2466e LP |
619 | state = t->state; |
620 | } | |
621 | ||
622 | if (state == DNS_QUERY_FAILURE) | |
ad867662 | 623 | q->received = dns_packet_ref(received); |
74b2466e LP |
624 | |
625 | dns_query_set_state(q, state); | |
626 | } | |
8ba9fd9c LP |
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 | } |