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) {
69 int c = 0, r, ifindex;
74 r = sd_bus_message_read(m, "i", &ifindex);
78 r = sd_bus_message_enter_container(m, 'a', "(iay)");
82 while ((r = sd_bus_message_enter_container(m, 'r', "iay")) > 0) {
85 r = sd_bus_message_read(m, "i", &family);
89 r = sd_bus_message_skip(m, "ay");
93 r = sd_bus_message_exit_container(m);
97 if (af != AF_UNSPEC && family != af)
105 r = sd_bus_message_exit_container(m);
109 r = sd_bus_message_read(m, "s", canonical);
113 r = sd_bus_message_rewind(m, true);
120 enum nss_status _nss_resolve_gethostbyname4_r(
122 struct gaih_addrtuple **pat,
123 char *buffer, size_t buflen,
124 int *errnop, int *h_errnop,
127 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
128 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
129 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
130 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
131 const char *canonical = NULL;
134 int c, r, i = 0, ifindex;
142 r = sd_bus_open_system(&bus);
146 r = sd_bus_message_new_method_call(
149 "org.freedesktop.resolve1",
150 "/org/freedesktop/resolve1",
151 "org.freedesktop.resolve1.Manager",
156 r = sd_bus_message_set_auto_start(req, false);
160 r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0);
164 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
166 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
168 *h_errnop = HOST_NOT_FOUND;
169 return NSS_STATUS_NOTFOUND;
172 if (bus_error_shall_fallback(&error)) {
174 enum nss_status (*fallback)(
176 struct gaih_addrtuple **pat,
177 char *buffer, size_t buflen,
178 int *errnop, int *h_errnop,
181 fallback = (enum nss_status (*)(const char *name,
182 struct gaih_addrtuple **pat,
183 char *buffer, size_t buflen,
184 int *errnop, int *h_errnop,
186 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
188 return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
192 *h_errnop = NO_RECOVERY;
193 return NSS_STATUS_UNAVAIL;
196 c = count_addresses(reply, AF_UNSPEC, &canonical);
203 *h_errnop = HOST_NOT_FOUND;
204 return NSS_STATUS_NOTFOUND;
207 if (isempty(canonical))
210 l = strlen(canonical);
211 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
214 *h_errnop = TRY_AGAIN;
215 return NSS_STATUS_TRYAGAIN;
218 /* First, append name */
220 memcpy(r_name, canonical, l+1);
223 /* Second, append addresses */
224 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
226 r = sd_bus_message_read(reply, "i", &ifindex);
235 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
239 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
244 r = sd_bus_message_read(reply, "i", &family);
248 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
252 r = sd_bus_message_exit_container(reply);
256 if (!IN_SET(family, AF_INET, AF_INET6))
259 if (sz != FAMILY_ADDRESS_SIZE(family)) {
264 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
265 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
266 r_tuple->name = r_name;
267 r_tuple->family = family;
268 r_tuple->scopeid = ifindex;
269 memcpy(r_tuple->addr, a, sz);
271 idx += ALIGN(sizeof(struct gaih_addrtuple));
281 **pat = *r_tuple_first;
283 *pat = r_tuple_first;
288 /* Explicitly reset all error variables */
290 *h_errnop = NETDB_SUCCESS;
293 return NSS_STATUS_SUCCESS;
298 return NSS_STATUS_UNAVAIL;
301 enum nss_status _nss_resolve_gethostbyname3_r(
304 struct hostent *result,
305 char *buffer, size_t buflen,
306 int *errnop, int *h_errnop,
310 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
311 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
312 char *r_name, *r_aliases, *r_addr, *r_addr_list;
313 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
314 size_t l, idx, ms, alen;
315 const char *canonical;
316 int c, r, i = 0, ifindex;
327 if (af != AF_INET && af != AF_INET6) {
332 r = sd_bus_open_system(&bus);
336 r = sd_bus_message_new_method_call(
339 "org.freedesktop.resolve1",
340 "/org/freedesktop/resolve1",
341 "org.freedesktop.resolve1.Manager",
346 r = sd_bus_message_set_auto_start(req, false);
350 r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0);
354 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
356 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
358 *h_errnop = HOST_NOT_FOUND;
359 return NSS_STATUS_NOTFOUND;
362 if (bus_error_shall_fallback(&error)) {
364 enum nss_status (*fallback)(
367 struct hostent *result,
368 char *buffer, size_t buflen,
369 int *errnop, int *h_errnop,
373 fallback = (enum nss_status (*)(const char *name,
375 struct hostent *result,
376 char *buffer, size_t buflen,
377 int *errnop, int *h_errnop,
380 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
382 return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
386 *h_errnop = NO_RECOVERY;
387 return NSS_STATUS_UNAVAIL;
390 c = count_addresses(reply, af, &canonical);
397 *h_errnop = HOST_NOT_FOUND;
398 return NSS_STATUS_NOTFOUND;
401 if (isempty(canonical))
404 alen = FAMILY_ADDRESS_SIZE(af);
405 l = strlen(canonical);
409 (c > 0 ? c : 1) * ALIGN(alen) +
410 (c > 0 ? c+1 : 2) * sizeof(char*);
414 *h_errnop = TRY_AGAIN;
415 return NSS_STATUS_TRYAGAIN;
418 /* First, append name */
420 memcpy(r_name, canonical, l+1);
423 /* Second, create empty aliases array */
424 r_aliases = buffer + idx;
425 ((char**) r_aliases)[0] = NULL;
426 idx += sizeof(char*);
428 /* Third, append addresses */
429 r_addr = buffer + idx;
431 r = sd_bus_message_read(reply, "i", &ifindex);
440 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
444 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
449 r = sd_bus_message_read(reply, "i", &family);
453 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
457 r = sd_bus_message_exit_container(reply);
474 memcpy(r_addr + i*ALIGN(alen), a, alen);
481 idx += c * ALIGN(alen);
483 /* Fourth, append address pointer array */
484 r_addr_list = buffer + idx;
485 for (i = 0; i < c; i++)
486 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
488 ((char**) r_addr_list)[i] = NULL;
489 idx += (c+1) * sizeof(char*);
493 result->h_name = r_name;
494 result->h_aliases = (char**) r_aliases;
495 result->h_addrtype = af;
496 result->h_length = alen;
497 result->h_addr_list = (char**) r_addr_list;
499 /* Explicitly reset all error variables */
501 *h_errnop = NETDB_SUCCESS;
510 return NSS_STATUS_SUCCESS;
515 return NSS_STATUS_UNAVAIL;
518 enum nss_status _nss_resolve_gethostbyaddr2_r(
519 const void* addr, socklen_t len,
521 struct hostent *result,
522 char *buffer, size_t buflen,
523 int *errnop, int *h_errnop,
526 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
527 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
528 char *r_name, *r_aliases, *r_addr, *r_addr_list;
529 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
530 unsigned c = 0, i = 0;
541 if (!IN_SET(af, AF_INET, AF_INET6)) {
542 *errnop = EAFNOSUPPORT;
544 return NSS_STATUS_UNAVAIL;
547 if (len != FAMILY_ADDRESS_SIZE(af)) {
549 *h_errnop = NO_RECOVERY;
550 return NSS_STATUS_UNAVAIL;
553 r = sd_bus_open_system(&bus);
557 r = sd_bus_message_new_method_call(
560 "org.freedesktop.resolve1",
561 "/org/freedesktop/resolve1",
562 "org.freedesktop.resolve1.Manager",
567 r = sd_bus_message_set_auto_start(req, false);
571 r = sd_bus_message_append(req, "ii", 0, af);
575 r = sd_bus_message_append_array(req, 'y', addr, len);
579 r = sd_bus_message_append(req, "t", (uint64_t) 0);
583 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
585 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
587 *h_errnop = HOST_NOT_FOUND;
588 return NSS_STATUS_NOTFOUND;
591 if (bus_error_shall_fallback(&error)) {
593 enum nss_status (*fallback)(
594 const void* addr, socklen_t len,
596 struct hostent *result,
597 char *buffer, size_t buflen,
598 int *errnop, int *h_errnop,
601 fallback = (enum nss_status (*)(
602 const void* addr, socklen_t len,
604 struct hostent *result,
605 char *buffer, size_t buflen,
606 int *errnop, int *h_errnop,
608 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
611 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
615 *h_errnop = NO_RECOVERY;
616 return NSS_STATUS_UNAVAIL;
619 r = sd_bus_message_read(reply, "i", &ifindex);
628 r = sd_bus_message_enter_container(reply, 'a', "s");
632 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
634 ms += ALIGN(strlen(n) + 1);
639 r = sd_bus_message_rewind(reply, false);
645 *h_errnop = HOST_NOT_FOUND;
646 return NSS_STATUS_NOTFOUND;
649 ms += ALIGN(len) + /* the address */
650 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
651 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
655 *h_errnop = TRY_AGAIN;
656 return NSS_STATUS_TRYAGAIN;
659 /* First, place address */
661 memcpy(r_addr, addr, len);
664 /* Second, place address list */
665 r_addr_list = buffer + idx;
666 ((char**) r_addr_list)[0] = r_addr;
667 ((char**) r_addr_list)[1] = NULL;
668 idx += sizeof(char*) * 2;
670 /* Third, reserve space for the aliases array */
671 r_aliases = buffer + idx;
672 idx += sizeof(char*) * c;
674 /* Fourth, place aliases */
676 r_name = buffer + idx;
677 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
686 ((char**) r_aliases)[i-1] = p;
694 ((char**) r_aliases)[c-1] = NULL;
697 result->h_name = r_name;
698 result->h_aliases = (char**) r_aliases;
699 result->h_addrtype = af;
700 result->h_length = len;
701 result->h_addr_list = (char**) r_addr_list;
706 /* Explicitly reset all error variables */
708 *h_errnop = NETDB_SUCCESS;
711 return NSS_STATUS_SUCCESS;
716 return NSS_STATUS_UNAVAIL;
719 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
720 NSS_GETHOSTBYADDR_FALLBACKS(resolve);