1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
24 #include <sys/types.h>
31 #include <arpa/inet.h>
36 #include "bus-errors.h"
40 #include "in-addr-util.h"
42 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
43 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
45 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
47 typedef void (*voidfunc_t)(void);
49 static voidfunc_t find_fallback(const char *module, const char *symbol) {
52 /* Try to find a fallback NSS module symbol */
54 dl = dlopen(module, RTLD_LAZY|RTLD_NODELETE);
58 return dlsym(dl, symbol);
61 static bool bus_error_shall_fallback(sd_bus_error *e) {
62 return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
63 sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) ||
64 sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) ||
65 sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED);
68 static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
74 r = sd_bus_message_enter_container(m, 'a', "(iayi)");
78 while ((r = sd_bus_message_enter_container(m, 'r', "iayi")) > 0) {
81 r = sd_bus_message_read(m, "i", &family);
85 r = sd_bus_message_skip(m, "ayi");
89 r = sd_bus_message_exit_container(m);
93 if (af != AF_UNSPEC && family != af)
101 r = sd_bus_message_exit_container(m);
105 r = sd_bus_message_read(m, "s", canonical);
109 r = sd_bus_message_rewind(m, true);
116 enum nss_status _nss_resolve_gethostbyname4_r(
118 struct gaih_addrtuple **pat,
119 char *buffer, size_t buflen,
120 int *errnop, int *h_errnop,
123 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
124 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
125 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
126 _cleanup_bus_unref_ sd_bus *bus = NULL;
127 const char *canonical = NULL;
138 r = sd_bus_open_system(&bus);
142 r = sd_bus_message_new_method_call(
145 "org.freedesktop.resolve1",
146 "/org/freedesktop/resolve1",
147 "org.freedesktop.resolve1.Manager",
152 r = sd_bus_message_set_auto_start(req, false);
156 r = sd_bus_message_append(req, "si", name, AF_UNSPEC);
160 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
162 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
164 *h_errnop = HOST_NOT_FOUND;
165 return NSS_STATUS_NOTFOUND;
168 if (bus_error_shall_fallback(&error)) {
170 enum nss_status (*fallback)(
172 struct gaih_addrtuple **pat,
173 char *buffer, size_t buflen,
174 int *errnop, int *h_errnop,
177 fallback = (enum nss_status (*)(const char *name,
178 struct gaih_addrtuple **pat,
179 char *buffer, size_t buflen,
180 int *errnop, int *h_errnop,
182 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
184 return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
188 *h_errnop = NO_RECOVERY;
189 return NSS_STATUS_UNAVAIL;
192 c = count_addresses(reply, AF_UNSPEC, &canonical);
199 *h_errnop = HOST_NOT_FOUND;
200 return NSS_STATUS_NOTFOUND;
203 if (isempty(canonical))
206 l = strlen(canonical);
207 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
210 *h_errnop = TRY_AGAIN;
211 return NSS_STATUS_TRYAGAIN;
214 /* First, append name */
216 memcpy(r_name, canonical, l+1);
219 /* Second, append addresses */
220 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
222 r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
226 while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
231 r = sd_bus_message_read(reply, "i", &family);
235 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
239 r = sd_bus_message_read(reply, "i", &ifindex);
243 r = sd_bus_message_exit_container(reply);
247 if (!IN_SET(family, AF_INET, AF_INET6))
250 if (sz != FAMILY_ADDRESS_SIZE(family)) {
260 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
261 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
262 r_tuple->name = r_name;
263 r_tuple->family = family;
264 r_tuple->scopeid = ifindex;
265 memcpy(r_tuple->addr, a, sz);
267 idx += ALIGN(sizeof(struct gaih_addrtuple));
277 **pat = *r_tuple_first;
279 *pat = r_tuple_first;
284 return NSS_STATUS_SUCCESS;
289 return NSS_STATUS_UNAVAIL;
292 enum nss_status _nss_resolve_gethostbyname3_r(
295 struct hostent *result,
296 char *buffer, size_t buflen,
297 int *errnop, int *h_errnop,
301 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
302 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
303 char *r_name, *r_aliases, *r_addr, *r_addr_list;
304 _cleanup_bus_unref_ sd_bus *bus = NULL;
305 size_t l, idx, ms, alen;
306 const char *canonical;
318 if (af != AF_INET && af != AF_INET6) {
323 r = sd_bus_open_system(&bus);
327 r = sd_bus_message_new_method_call(
330 "org.freedesktop.resolve1",
331 "/org/freedesktop/resolve1",
332 "org.freedesktop.resolve1.Manager",
337 r = sd_bus_message_set_auto_start(req, false);
341 r = sd_bus_message_append(req, "si", name, af);
345 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
347 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
349 *h_errnop = HOST_NOT_FOUND;
350 return NSS_STATUS_NOTFOUND;
353 if (bus_error_shall_fallback(&error)) {
355 enum nss_status (*fallback)(
358 struct hostent *result,
359 char *buffer, size_t buflen,
360 int *errnop, int *h_errnop,
364 fallback = (enum nss_status (*)(const char *name,
366 struct hostent *result,
367 char *buffer, size_t buflen,
368 int *errnop, int *h_errnop,
371 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
373 return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
377 *h_errnop = NO_RECOVERY;
378 return NSS_STATUS_UNAVAIL;
381 c = count_addresses(reply, af, &canonical);
388 *h_errnop = HOST_NOT_FOUND;
389 return NSS_STATUS_NOTFOUND;
392 if (isempty(canonical))
395 alen = FAMILY_ADDRESS_SIZE(af);
396 l = strlen(canonical);
400 (c > 0 ? c : 1) * ALIGN(alen) +
401 (c > 0 ? c+1 : 2) * sizeof(char*);
405 *h_errnop = TRY_AGAIN;
406 return NSS_STATUS_TRYAGAIN;
409 /* First, append name */
411 memcpy(r_name, canonical, l+1);
414 /* Second, create empty aliases array */
415 r_aliases = buffer + idx;
416 ((char**) r_aliases)[0] = NULL;
417 idx += sizeof(char*);
419 /* Third, append addresses */
420 r_addr = buffer + idx;
422 r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
426 while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
431 r = sd_bus_message_read(reply, "i", &family);
435 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
439 r = sd_bus_message_read(reply, "i", &ifindex);
443 r = sd_bus_message_exit_container(reply);
460 memcpy(r_addr + i*ALIGN(alen), a, alen);
467 idx += c * ALIGN(alen);
469 /* Fourth, append address pointer array */
470 r_addr_list = buffer + idx;
471 for (i = 0; i < c; i++)
472 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
474 ((char**) r_addr_list)[i] = NULL;
475 idx += (c+1) * sizeof(char*);
479 result->h_name = r_name;
480 result->h_aliases = (char**) r_aliases;
481 result->h_addrtype = af;
482 result->h_length = alen;
483 result->h_addr_list = (char**) r_addr_list;
491 return NSS_STATUS_SUCCESS;
496 return NSS_STATUS_UNAVAIL;
499 enum nss_status _nss_resolve_gethostbyaddr2_r(
500 const void* addr, socklen_t len,
502 struct hostent *result,
503 char *buffer, size_t buflen,
504 int *errnop, int *h_errnop,
507 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
508 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
509 char *r_name, *r_aliases, *r_addr, *r_addr_list;
510 _cleanup_bus_unref_ sd_bus *bus = NULL;
511 unsigned c = 0, i = 0;
522 if (!IN_SET(af, AF_INET, AF_INET6)) {
523 *errnop = EAFNOSUPPORT;
525 return NSS_STATUS_UNAVAIL;
528 if (len != FAMILY_ADDRESS_SIZE(af)) {
530 *h_errnop = NO_RECOVERY;
531 return NSS_STATUS_UNAVAIL;
534 r = sd_bus_open_system(&bus);
538 r = sd_bus_message_new_method_call(
541 "org.freedesktop.resolve1",
542 "/org/freedesktop/resolve1",
543 "org.freedesktop.resolve1.Manager",
548 r = sd_bus_message_set_auto_start(req, false);
552 r = sd_bus_message_append(req, "i", af);
556 r = sd_bus_message_append_array(req, 'y', addr, len);
560 r = sd_bus_message_append(req, "i", 0);
564 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
566 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
568 *h_errnop = HOST_NOT_FOUND;
569 return NSS_STATUS_NOTFOUND;
572 if (bus_error_shall_fallback(&error)) {
574 enum nss_status (*fallback)(
575 const void* addr, socklen_t len,
577 struct hostent *result,
578 char *buffer, size_t buflen,
579 int *errnop, int *h_errnop,
582 fallback = (enum nss_status (*)(
583 const void* addr, socklen_t len,
585 struct hostent *result,
586 char *buffer, size_t buflen,
587 int *errnop, int *h_errnop,
589 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
592 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
596 *h_errnop = NO_RECOVERY;
597 return NSS_STATUS_UNAVAIL;
600 r = sd_bus_message_enter_container(reply, 'a', "s");
604 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
606 ms += ALIGN(strlen(n) + 1);
611 r = sd_bus_message_rewind(reply, false);
617 *h_errnop = HOST_NOT_FOUND;
618 return NSS_STATUS_NOTFOUND;
621 ms += ALIGN(len) + /* the address */
622 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
623 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
627 *h_errnop = TRY_AGAIN;
628 return NSS_STATUS_TRYAGAIN;
631 /* First, place address */
633 memcpy(r_addr, addr, len);
636 /* Second, place address list */
637 r_addr_list = buffer + idx;
638 ((char**) r_addr_list)[0] = r_addr;
639 ((char**) r_addr_list)[1] = NULL;
640 idx += sizeof(char*) * 2;
642 /* Third, reserve space for the aliases array */
643 r_aliases = buffer + idx;
644 idx += sizeof(char*) * c;
646 /* Fourth, place aliases */
648 r_name = buffer + idx;
649 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
658 ((char**) r_aliases)[i-1] = p;
666 ((char**) r_aliases)[c-1] = NULL;
669 result->h_name = r_name;
670 result->h_aliases = (char**) r_aliases;
671 result->h_addrtype = af;
672 result->h_length = len;
673 result->h_addr_list = (char**) r_addr_list;
678 return NSS_STATUS_SUCCESS;
683 return NSS_STATUS_UNAVAIL;
686 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
687 NSS_GETHOSTBYADDR_FALLBACKS(resolve);