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);
407 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
411 *h_errnop = TRY_AGAIN;
412 return NSS_STATUS_TRYAGAIN;
415 /* First, append name */
417 memcpy(r_name, canonical, l+1);
420 /* Second, create empty aliases array */
421 r_aliases = buffer + idx;
422 ((char**) r_aliases)[0] = NULL;
423 idx += sizeof(char*);
425 /* Third, append addresses */
426 r_addr = buffer + idx;
428 r = sd_bus_message_read(reply, "i", &ifindex);
437 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
441 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
446 r = sd_bus_message_read(reply, "i", &family);
450 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
454 r = sd_bus_message_exit_container(reply);
466 memcpy(r_addr + i*ALIGN(alen), a, alen);
473 idx += c * ALIGN(alen);
475 /* Fourth, append address pointer array */
476 r_addr_list = buffer + idx;
477 for (i = 0; i < c; i++)
478 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
480 ((char**) r_addr_list)[i] = NULL;
481 idx += (c+1) * sizeof(char*);
485 result->h_name = r_name;
486 result->h_aliases = (char**) r_aliases;
487 result->h_addrtype = af;
488 result->h_length = alen;
489 result->h_addr_list = (char**) r_addr_list;
491 /* Explicitly reset all error variables */
493 *h_errnop = NETDB_SUCCESS;
502 return NSS_STATUS_SUCCESS;
507 return NSS_STATUS_UNAVAIL;
510 enum nss_status _nss_resolve_gethostbyaddr2_r(
511 const void* addr, socklen_t len,
513 struct hostent *result,
514 char *buffer, size_t buflen,
515 int *errnop, int *h_errnop,
518 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
519 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
520 char *r_name, *r_aliases, *r_addr, *r_addr_list;
521 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
522 unsigned c = 0, i = 0;
533 if (!IN_SET(af, AF_INET, AF_INET6)) {
534 *errnop = EAFNOSUPPORT;
536 return NSS_STATUS_UNAVAIL;
539 if (len != FAMILY_ADDRESS_SIZE(af)) {
541 *h_errnop = NO_RECOVERY;
542 return NSS_STATUS_UNAVAIL;
545 r = sd_bus_open_system(&bus);
549 r = sd_bus_message_new_method_call(
552 "org.freedesktop.resolve1",
553 "/org/freedesktop/resolve1",
554 "org.freedesktop.resolve1.Manager",
559 r = sd_bus_message_set_auto_start(req, false);
563 r = sd_bus_message_append(req, "ii", 0, af);
567 r = sd_bus_message_append_array(req, 'y', addr, len);
571 r = sd_bus_message_append(req, "t", (uint64_t) 0);
575 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
577 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
579 *h_errnop = HOST_NOT_FOUND;
580 return NSS_STATUS_NOTFOUND;
583 if (bus_error_shall_fallback(&error)) {
585 enum nss_status (*fallback)(
586 const void* addr, socklen_t len,
588 struct hostent *result,
589 char *buffer, size_t buflen,
590 int *errnop, int *h_errnop,
593 fallback = (enum nss_status (*)(
594 const void* addr, socklen_t len,
596 struct hostent *result,
597 char *buffer, size_t buflen,
598 int *errnop, int *h_errnop,
600 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
603 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
607 *h_errnop = NO_RECOVERY;
608 return NSS_STATUS_UNAVAIL;
611 r = sd_bus_message_read(reply, "i", &ifindex);
620 r = sd_bus_message_enter_container(reply, 'a', "s");
624 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
626 ms += ALIGN(strlen(n) + 1);
631 r = sd_bus_message_rewind(reply, false);
637 *h_errnop = HOST_NOT_FOUND;
638 return NSS_STATUS_NOTFOUND;
641 ms += ALIGN(len) + /* the address */
642 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
643 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
647 *h_errnop = TRY_AGAIN;
648 return NSS_STATUS_TRYAGAIN;
651 /* First, place address */
653 memcpy(r_addr, addr, len);
656 /* Second, place address list */
657 r_addr_list = buffer + idx;
658 ((char**) r_addr_list)[0] = r_addr;
659 ((char**) r_addr_list)[1] = NULL;
660 idx += sizeof(char*) * 2;
662 /* Third, reserve space for the aliases array */
663 r_aliases = buffer + idx;
664 idx += sizeof(char*) * c;
666 /* Fourth, place aliases */
668 r_name = buffer + idx;
669 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
678 ((char**) r_aliases)[i-1] = p;
686 ((char**) r_aliases)[c-1] = NULL;
689 result->h_name = r_name;
690 result->h_aliases = (char**) r_aliases;
691 result->h_addrtype = af;
692 result->h_length = len;
693 result->h_addr_list = (char**) r_addr_list;
698 /* Explicitly reset all error variables */
700 *h_errnop = NETDB_SUCCESS;
703 return NSS_STATUS_SUCCESS;
708 return NSS_STATUS_UNAVAIL;
711 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
712 NSS_GETHOSTBYADDR_FALLBACKS(resolve);