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);
469 memcpy(r_addr + i*ALIGN(alen), a, alen);
476 idx += c * ALIGN(alen);
478 /* Fourth, append address pointer array */
479 r_addr_list = buffer + idx;
480 for (i = 0; i < c; i++)
481 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
483 ((char**) r_addr_list)[i] = NULL;
484 idx += (c+1) * sizeof(char*);
488 result->h_name = r_name;
489 result->h_aliases = (char**) r_aliases;
490 result->h_addrtype = af;
491 result->h_length = alen;
492 result->h_addr_list = (char**) r_addr_list;
494 /* Explicitly reset all error variables */
496 *h_errnop = NETDB_SUCCESS;
505 return NSS_STATUS_SUCCESS;
510 return NSS_STATUS_UNAVAIL;
513 enum nss_status _nss_resolve_gethostbyaddr2_r(
514 const void* addr, socklen_t len,
516 struct hostent *result,
517 char *buffer, size_t buflen,
518 int *errnop, int *h_errnop,
521 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
522 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
523 char *r_name, *r_aliases, *r_addr, *r_addr_list;
524 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
525 unsigned c = 0, i = 0;
536 if (!IN_SET(af, AF_INET, AF_INET6)) {
537 *errnop = EAFNOSUPPORT;
539 return NSS_STATUS_UNAVAIL;
542 if (len != FAMILY_ADDRESS_SIZE(af)) {
544 *h_errnop = NO_RECOVERY;
545 return NSS_STATUS_UNAVAIL;
548 r = sd_bus_open_system(&bus);
552 r = sd_bus_message_new_method_call(
555 "org.freedesktop.resolve1",
556 "/org/freedesktop/resolve1",
557 "org.freedesktop.resolve1.Manager",
562 r = sd_bus_message_set_auto_start(req, false);
566 r = sd_bus_message_append(req, "ii", 0, af);
570 r = sd_bus_message_append_array(req, 'y', addr, len);
574 r = sd_bus_message_append(req, "t", (uint64_t) 0);
578 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
580 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
582 *h_errnop = HOST_NOT_FOUND;
583 return NSS_STATUS_NOTFOUND;
586 if (bus_error_shall_fallback(&error)) {
588 enum nss_status (*fallback)(
589 const void* addr, socklen_t len,
591 struct hostent *result,
592 char *buffer, size_t buflen,
593 int *errnop, int *h_errnop,
596 fallback = (enum nss_status (*)(
597 const void* addr, socklen_t len,
599 struct hostent *result,
600 char *buffer, size_t buflen,
601 int *errnop, int *h_errnop,
603 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
606 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
610 *h_errnop = NO_RECOVERY;
611 return NSS_STATUS_UNAVAIL;
614 r = sd_bus_message_read(reply, "i", &ifindex);
623 r = sd_bus_message_enter_container(reply, 'a', "s");
627 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
629 ms += ALIGN(strlen(n) + 1);
634 r = sd_bus_message_rewind(reply, false);
640 *h_errnop = HOST_NOT_FOUND;
641 return NSS_STATUS_NOTFOUND;
644 ms += ALIGN(len) + /* the address */
645 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
646 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
650 *h_errnop = TRY_AGAIN;
651 return NSS_STATUS_TRYAGAIN;
654 /* First, place address */
656 memcpy(r_addr, addr, len);
659 /* Second, place address list */
660 r_addr_list = buffer + idx;
661 ((char**) r_addr_list)[0] = r_addr;
662 ((char**) r_addr_list)[1] = NULL;
663 idx += sizeof(char*) * 2;
665 /* Third, reserve space for the aliases array */
666 r_aliases = buffer + idx;
667 idx += sizeof(char*) * c;
669 /* Fourth, place aliases */
671 r_name = buffer + idx;
672 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
681 ((char**) r_aliases)[i-1] = p;
689 ((char**) r_aliases)[c-1] = NULL;
692 result->h_name = r_name;
693 result->h_aliases = (char**) r_aliases;
694 result->h_addrtype = af;
695 result->h_length = len;
696 result->h_addr_list = (char**) r_addr_list;
701 /* Explicitly reset all error variables */
703 *h_errnop = NETDB_SUCCESS;
706 return NSS_STATUS_SUCCESS;
711 return NSS_STATUS_UNAVAIL;
714 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
715 NSS_GETHOSTBYADDR_FALLBACKS(resolve);