]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-resolve/nss-resolve.c
Merge pull request #4832 from rojkov/mdns
[thirdparty/systemd.git] / src / nss-resolve / nss-resolve.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2014 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <dlfcn.h>
21 #include <errno.h>
22 #include <netdb.h>
23 #include <nss.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "sd-bus.h"
28
29 #include "bus-common-errors.h"
30 #include "in-addr-util.h"
31 #include "macro.h"
32 #include "nss-util.h"
33 #include "string-util.h"
34 #include "util.h"
35 #include "signal-util.h"
36
37 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
38 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
39
40 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
41
42 typedef void (*voidfunc_t)(void);
43
44 static voidfunc_t find_fallback(const char *module, const char *symbol) {
45 void *dl;
46
47 /* Try to find a fallback NSS module symbol */
48
49 dl = dlopen(module, RTLD_LAZY|RTLD_NODELETE);
50 if (!dl)
51 return NULL;
52
53 return dlsym(dl, symbol);
54 }
55
56 static bool bus_error_shall_fallback(sd_bus_error *e) {
57 return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
58 sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) ||
59 sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) ||
60 sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED);
61 }
62
63 static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
64 int c = 0, r;
65
66 assert(m);
67 assert(canonical);
68
69 r = sd_bus_message_enter_container(m, 'a', "(iiay)");
70 if (r < 0)
71 return r;
72
73 while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) {
74 int family, ifindex;
75
76 assert_cc(sizeof(int32_t) == sizeof(int));
77
78 r = sd_bus_message_read(m, "ii", &ifindex, &family);
79 if (r < 0)
80 return r;
81
82 r = sd_bus_message_skip(m, "ay");
83 if (r < 0)
84 return r;
85
86 r = sd_bus_message_exit_container(m);
87 if (r < 0)
88 return r;
89
90 if (af != AF_UNSPEC && family != af)
91 continue;
92
93 c++;
94 }
95 if (r < 0)
96 return r;
97
98 r = sd_bus_message_exit_container(m);
99 if (r < 0)
100 return r;
101
102 r = sd_bus_message_read(m, "s", canonical);
103 if (r < 0)
104 return r;
105
106 r = sd_bus_message_rewind(m, true);
107 if (r < 0)
108 return r;
109
110 return c;
111 }
112
113 static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
114 struct in6_addr in6;
115
116 if (family != AF_INET6)
117 return 0;
118
119 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
120
121 assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET));
122 memcpy(&in6, a, sizeof(struct in6_addr));
123
124 return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0;
125 }
126
127 enum nss_status _nss_resolve_gethostbyname4_r(
128 const char *name,
129 struct gaih_addrtuple **pat,
130 char *buffer, size_t buflen,
131 int *errnop, int *h_errnop,
132 int32_t *ttlp) {
133
134 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
135 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
136 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
137 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
138 enum nss_status ret = NSS_STATUS_UNAVAIL;
139 const char *canonical = NULL;
140 size_t l, ms, idx;
141 char *r_name;
142 int c, r, i = 0;
143
144 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
145
146 assert(name);
147 assert(pat);
148 assert(buffer);
149 assert(errnop);
150 assert(h_errnop);
151
152 r = sd_bus_open_system(&bus);
153 if (r < 0)
154 goto fallback;
155
156 r = sd_bus_message_new_method_call(
157 bus,
158 &req,
159 "org.freedesktop.resolve1",
160 "/org/freedesktop/resolve1",
161 "org.freedesktop.resolve1.Manager",
162 "ResolveHostname");
163 if (r < 0)
164 goto fail;
165
166 r = sd_bus_message_set_auto_start(req, false);
167 if (r < 0)
168 goto fail;
169
170 r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0);
171 if (r < 0)
172 goto fail;
173
174 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
175 if (r < 0) {
176 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
177 *errnop = ESRCH;
178 *h_errnop = HOST_NOT_FOUND;
179 return NSS_STATUS_NOTFOUND;
180 }
181
182 if (bus_error_shall_fallback(&error))
183 goto fallback;
184
185 /* Treat all other error conditions as NOTFOUND, and fail. This includes DNSSEC errors and
186 suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf configuration can distuingish
187 such executed but negative replies from complete failure to talk to resolved. */
188 ret = NSS_STATUS_NOTFOUND;
189 goto fail;
190 }
191
192 c = count_addresses(reply, AF_UNSPEC, &canonical);
193 if (c < 0) {
194 r = c;
195 goto fail;
196 }
197 if (c == 0) {
198 *errnop = ESRCH;
199 *h_errnop = HOST_NOT_FOUND;
200 return NSS_STATUS_NOTFOUND;
201 }
202
203 if (isempty(canonical))
204 canonical = name;
205
206 l = strlen(canonical);
207 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
208 if (buflen < ms) {
209 *errnop = ENOMEM;
210 *h_errnop = TRY_AGAIN;
211 return NSS_STATUS_TRYAGAIN;
212 }
213
214 /* First, append name */
215 r_name = buffer;
216 memcpy(r_name, canonical, l+1);
217 idx = ALIGN(l+1);
218
219 /* Second, append addresses */
220 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
221
222 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
223 if (r < 0)
224 goto fail;
225
226 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
227 int family, ifindex;
228 const void *a;
229 size_t sz;
230
231 assert_cc(sizeof(int32_t) == sizeof(int));
232
233 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
234 if (r < 0)
235 goto fail;
236
237 if (ifindex < 0) {
238 r = -EINVAL;
239 goto fail;
240 }
241
242 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
243 if (r < 0)
244 goto fail;
245
246 r = sd_bus_message_exit_container(reply);
247 if (r < 0)
248 goto fail;
249
250 if (!IN_SET(family, AF_INET, AF_INET6))
251 continue;
252
253 if (sz != FAMILY_ADDRESS_SIZE(family)) {
254 r = -EINVAL;
255 goto fail;
256 }
257
258 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
259 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
260 r_tuple->name = r_name;
261 r_tuple->family = family;
262 r_tuple->scopeid = ifindex_to_scopeid(family, a, ifindex);
263 memcpy(r_tuple->addr, a, sz);
264
265 idx += ALIGN(sizeof(struct gaih_addrtuple));
266 i++;
267 }
268 if (r < 0)
269 goto fail;
270
271 assert(i == c);
272 assert(idx == ms);
273
274 if (*pat)
275 **pat = *r_tuple_first;
276 else
277 *pat = r_tuple_first;
278
279 if (ttlp)
280 *ttlp = 0;
281
282 /* Explicitly reset all error variables */
283 *errnop = 0;
284 *h_errnop = NETDB_SUCCESS;
285 h_errno = 0;
286
287 return NSS_STATUS_SUCCESS;
288
289 fallback:
290 {
291 _nss_gethostbyname4_r_t fallback;
292
293 fallback = (_nss_gethostbyname4_r_t)
294 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
295
296 if (fallback)
297 return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
298 }
299
300 fail:
301 *errnop = -r;
302 *h_errnop = NO_RECOVERY;
303 return ret;
304 }
305
306 enum nss_status _nss_resolve_gethostbyname3_r(
307 const char *name,
308 int af,
309 struct hostent *result,
310 char *buffer, size_t buflen,
311 int *errnop, int *h_errnop,
312 int32_t *ttlp,
313 char **canonp) {
314
315 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
316 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
317 char *r_name, *r_aliases, *r_addr, *r_addr_list;
318 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
319 enum nss_status ret = NSS_STATUS_UNAVAIL;
320 size_t l, idx, ms, alen;
321 const char *canonical;
322 int c, r, i = 0;
323
324 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
325
326 assert(name);
327 assert(result);
328 assert(buffer);
329 assert(errnop);
330 assert(h_errnop);
331
332 if (af == AF_UNSPEC)
333 af = AF_INET;
334
335 if (af != AF_INET && af != AF_INET6) {
336 r = -EAFNOSUPPORT;
337 goto fail;
338 }
339
340 r = sd_bus_open_system(&bus);
341 if (r < 0)
342 goto fallback;
343
344 r = sd_bus_message_new_method_call(
345 bus,
346 &req,
347 "org.freedesktop.resolve1",
348 "/org/freedesktop/resolve1",
349 "org.freedesktop.resolve1.Manager",
350 "ResolveHostname");
351 if (r < 0)
352 goto fail;
353
354 r = sd_bus_message_set_auto_start(req, false);
355 if (r < 0)
356 goto fail;
357
358 r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0);
359 if (r < 0)
360 goto fail;
361
362 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
363 if (r < 0) {
364 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
365 *errnop = ESRCH;
366 *h_errnop = HOST_NOT_FOUND;
367 return NSS_STATUS_NOTFOUND;
368 }
369
370 if (bus_error_shall_fallback(&error))
371 goto fallback;
372
373 ret = NSS_STATUS_NOTFOUND;
374 goto fail;
375 }
376
377 c = count_addresses(reply, af, &canonical);
378 if (c < 0) {
379 r = c;
380 goto fail;
381 }
382 if (c == 0) {
383 *errnop = ESRCH;
384 *h_errnop = HOST_NOT_FOUND;
385 return NSS_STATUS_NOTFOUND;
386 }
387
388 if (isempty(canonical))
389 canonical = name;
390
391 alen = FAMILY_ADDRESS_SIZE(af);
392 l = strlen(canonical);
393
394 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
395
396 if (buflen < ms) {
397 *errnop = ENOMEM;
398 *h_errnop = TRY_AGAIN;
399 return NSS_STATUS_TRYAGAIN;
400 }
401
402 /* First, append name */
403 r_name = buffer;
404 memcpy(r_name, canonical, l+1);
405 idx = ALIGN(l+1);
406
407 /* Second, create empty aliases array */
408 r_aliases = buffer + idx;
409 ((char**) r_aliases)[0] = NULL;
410 idx += sizeof(char*);
411
412 /* Third, append addresses */
413 r_addr = buffer + idx;
414
415 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
416 if (r < 0)
417 goto fail;
418
419 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
420 int ifindex, family;
421 const void *a;
422 size_t sz;
423
424 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
425 if (r < 0)
426 goto fail;
427
428 if (ifindex < 0) {
429 r = -EINVAL;
430 goto fail;
431 }
432
433 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
434 if (r < 0)
435 goto fail;
436
437 r = sd_bus_message_exit_container(reply);
438 if (r < 0)
439 goto fail;
440
441 if (family != af)
442 continue;
443
444 if (sz != alen) {
445 r = -EINVAL;
446 goto fail;
447 }
448
449 memcpy(r_addr + i*ALIGN(alen), a, alen);
450 i++;
451 }
452 if (r < 0)
453 goto fail;
454
455 assert(i == c);
456 idx += c * ALIGN(alen);
457
458 /* Fourth, append address pointer array */
459 r_addr_list = buffer + idx;
460 for (i = 0; i < c; i++)
461 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
462
463 ((char**) r_addr_list)[i] = NULL;
464 idx += (c+1) * sizeof(char*);
465
466 assert(idx == ms);
467
468 result->h_name = r_name;
469 result->h_aliases = (char**) r_aliases;
470 result->h_addrtype = af;
471 result->h_length = alen;
472 result->h_addr_list = (char**) r_addr_list;
473
474 /* Explicitly reset all error variables */
475 *errnop = 0;
476 *h_errnop = NETDB_SUCCESS;
477 h_errno = 0;
478
479 if (ttlp)
480 *ttlp = 0;
481
482 if (canonp)
483 *canonp = r_name;
484
485 return NSS_STATUS_SUCCESS;
486
487 fallback:
488 {
489 _nss_gethostbyname3_r_t fallback;
490
491 fallback = (_nss_gethostbyname3_r_t)
492 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
493 if (fallback)
494 return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
495 }
496
497 fail:
498 *errnop = -r;
499 *h_errnop = NO_RECOVERY;
500 return ret;
501 }
502
503 enum nss_status _nss_resolve_gethostbyaddr2_r(
504 const void* addr, socklen_t len,
505 int af,
506 struct hostent *result,
507 char *buffer, size_t buflen,
508 int *errnop, int *h_errnop,
509 int32_t *ttlp) {
510
511 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
512 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
513 char *r_name, *r_aliases, *r_addr, *r_addr_list;
514 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
515 enum nss_status ret = NSS_STATUS_UNAVAIL;
516 unsigned c = 0, i = 0;
517 size_t ms = 0, idx;
518 const char *n;
519 int r, ifindex;
520
521 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
522
523 assert(addr);
524 assert(result);
525 assert(buffer);
526 assert(errnop);
527 assert(h_errnop);
528
529 if (!IN_SET(af, AF_INET, AF_INET6)) {
530 *errnop = EAFNOSUPPORT;
531 *h_errnop = NO_DATA;
532 return NSS_STATUS_UNAVAIL;
533 }
534
535 if (len != FAMILY_ADDRESS_SIZE(af)) {
536 *errnop = EINVAL;
537 *h_errnop = NO_RECOVERY;
538 return NSS_STATUS_UNAVAIL;
539 }
540
541 r = sd_bus_open_system(&bus);
542 if (r < 0)
543 goto fallback;
544
545 r = sd_bus_message_new_method_call(
546 bus,
547 &req,
548 "org.freedesktop.resolve1",
549 "/org/freedesktop/resolve1",
550 "org.freedesktop.resolve1.Manager",
551 "ResolveAddress");
552 if (r < 0)
553 goto fail;
554
555 r = sd_bus_message_set_auto_start(req, false);
556 if (r < 0)
557 goto fail;
558
559 r = sd_bus_message_append(req, "ii", 0, af);
560 if (r < 0)
561 goto fail;
562
563 r = sd_bus_message_append_array(req, 'y', addr, len);
564 if (r < 0)
565 goto fail;
566
567 r = sd_bus_message_append(req, "t", (uint64_t) 0);
568 if (r < 0)
569 goto fail;
570
571 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
572 if (r < 0) {
573 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
574 *errnop = ESRCH;
575 *h_errnop = HOST_NOT_FOUND;
576 return NSS_STATUS_NOTFOUND;
577 }
578
579 if (bus_error_shall_fallback(&error))
580 goto fallback;
581
582 ret = NSS_STATUS_NOTFOUND;
583 goto fail;
584 }
585
586 r = sd_bus_message_enter_container(reply, 'a', "(is)");
587 if (r < 0)
588 goto fail;
589
590 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
591
592 if (ifindex < 0) {
593 r = -EINVAL;
594 goto fail;
595 }
596
597 c++;
598 ms += ALIGN(strlen(n) + 1);
599 }
600 if (r < 0)
601 goto fail;
602
603 r = sd_bus_message_rewind(reply, false);
604 if (r < 0)
605 return r;
606
607 if (c <= 0) {
608 *errnop = ESRCH;
609 *h_errnop = HOST_NOT_FOUND;
610 return NSS_STATUS_NOTFOUND;
611 }
612
613 ms += ALIGN(len) + /* the address */
614 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
615 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
616
617 if (buflen < ms) {
618 *errnop = ENOMEM;
619 *h_errnop = TRY_AGAIN;
620 return NSS_STATUS_TRYAGAIN;
621 }
622
623 /* First, place address */
624 r_addr = buffer;
625 memcpy(r_addr, addr, len);
626 idx = ALIGN(len);
627
628 /* Second, place address list */
629 r_addr_list = buffer + idx;
630 ((char**) r_addr_list)[0] = r_addr;
631 ((char**) r_addr_list)[1] = NULL;
632 idx += sizeof(char*) * 2;
633
634 /* Third, reserve space for the aliases array */
635 r_aliases = buffer + idx;
636 idx += sizeof(char*) * c;
637
638 /* Fourth, place aliases */
639 i = 0;
640 r_name = buffer + idx;
641 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
642 char *p;
643 size_t l;
644
645 l = strlen(n);
646 p = buffer + idx;
647 memcpy(p, n, l+1);
648
649 if (i > 0)
650 ((char**) r_aliases)[i-1] = p;
651 i++;
652
653 idx += ALIGN(l+1);
654 }
655 if (r < 0)
656 goto fail;
657
658 ((char**) r_aliases)[c-1] = NULL;
659 assert(idx == ms);
660
661 result->h_name = r_name;
662 result->h_aliases = (char**) r_aliases;
663 result->h_addrtype = af;
664 result->h_length = len;
665 result->h_addr_list = (char**) r_addr_list;
666
667 if (ttlp)
668 *ttlp = 0;
669
670 /* Explicitly reset all error variables */
671 *errnop = 0;
672 *h_errnop = NETDB_SUCCESS;
673 h_errno = 0;
674
675 return NSS_STATUS_SUCCESS;
676
677 fallback:
678 {
679 _nss_gethostbyaddr2_r_t fallback;
680
681 fallback = (_nss_gethostbyaddr2_r_t)
682 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
683
684 if (fallback)
685 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
686 }
687
688 fail:
689 *errnop = -r;
690 *h_errnop = NO_RECOVERY;
691 return ret;
692 }
693
694 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
695 NSS_GETHOSTBYADDR_FALLBACKS(resolve);