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/>.
31 #include "bus-common-errors.h"
35 #include "in-addr-util.h"
37 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
38 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
40 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
42 typedef void (*voidfunc_t)(void);
44 static voidfunc_t find_fallback(const char *module, const char *symbol) {
47 /* Try to find a fallback NSS module symbol */
49 dl = dlopen(module, RTLD_LAZY|RTLD_NODELETE);
53 return dlsym(dl, symbol);
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);
63 static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
64 int c = 0, r, ifindex;
69 r = sd_bus_message_read(m, "i", &ifindex);
73 r = sd_bus_message_enter_container(m, 'a', "(iay)");
77 while ((r = sd_bus_message_enter_container(m, 'r', "iay")) > 0) {
80 r = sd_bus_message_read(m, "i", &family);
84 r = sd_bus_message_skip(m, "ay");
88 r = sd_bus_message_exit_container(m);
92 if (af != AF_UNSPEC && family != af)
100 r = sd_bus_message_exit_container(m);
104 r = sd_bus_message_read(m, "s", canonical);
108 r = sd_bus_message_rewind(m, true);
115 enum nss_status _nss_resolve_gethostbyname4_r(
117 struct gaih_addrtuple **pat,
118 char *buffer, size_t buflen,
119 int *errnop, int *h_errnop,
122 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
123 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
124 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
125 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
126 const char *canonical = NULL;
129 int c, r, i = 0, ifindex;
137 r = sd_bus_open_system(&bus);
141 r = sd_bus_message_new_method_call(
144 "org.freedesktop.resolve1",
145 "/org/freedesktop/resolve1",
146 "org.freedesktop.resolve1.Manager",
151 r = sd_bus_message_set_auto_start(req, false);
155 r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0);
159 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
161 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
163 *h_errnop = HOST_NOT_FOUND;
164 return NSS_STATUS_NOTFOUND;
167 if (bus_error_shall_fallback(&error)) {
169 enum nss_status (*fallback)(
171 struct gaih_addrtuple **pat,
172 char *buffer, size_t buflen,
173 int *errnop, int *h_errnop,
176 fallback = (enum nss_status (*)(const char *name,
177 struct gaih_addrtuple **pat,
178 char *buffer, size_t buflen,
179 int *errnop, int *h_errnop,
181 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
183 return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
187 *h_errnop = NO_RECOVERY;
188 return NSS_STATUS_UNAVAIL;
191 c = count_addresses(reply, AF_UNSPEC, &canonical);
198 *h_errnop = HOST_NOT_FOUND;
199 return NSS_STATUS_NOTFOUND;
202 if (isempty(canonical))
205 l = strlen(canonical);
206 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
209 *h_errnop = TRY_AGAIN;
210 return NSS_STATUS_TRYAGAIN;
213 /* First, append name */
215 memcpy(r_name, canonical, l+1);
218 /* Second, append addresses */
219 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
221 r = sd_bus_message_read(reply, "i", &ifindex);
230 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
234 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
239 r = sd_bus_message_read(reply, "i", &family);
243 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
247 r = sd_bus_message_exit_container(reply);
251 if (!IN_SET(family, AF_INET, AF_INET6))
254 if (sz != FAMILY_ADDRESS_SIZE(family)) {
259 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
260 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
261 r_tuple->name = r_name;
262 r_tuple->family = family;
263 r_tuple->scopeid = ifindex;
264 memcpy(r_tuple->addr, a, sz);
266 idx += ALIGN(sizeof(struct gaih_addrtuple));
276 **pat = *r_tuple_first;
278 *pat = r_tuple_first;
283 /* Explicitly reset all error variables */
285 *h_errnop = NETDB_SUCCESS;
288 return NSS_STATUS_SUCCESS;
293 return NSS_STATUS_UNAVAIL;
296 enum nss_status _nss_resolve_gethostbyname3_r(
299 struct hostent *result,
300 char *buffer, size_t buflen,
301 int *errnop, int *h_errnop,
305 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
306 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
307 char *r_name, *r_aliases, *r_addr, *r_addr_list;
308 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
309 size_t l, idx, ms, alen;
310 const char *canonical;
311 int c, r, i = 0, ifindex;
322 if (af != AF_INET && af != AF_INET6) {
327 r = sd_bus_open_system(&bus);
331 r = sd_bus_message_new_method_call(
334 "org.freedesktop.resolve1",
335 "/org/freedesktop/resolve1",
336 "org.freedesktop.resolve1.Manager",
341 r = sd_bus_message_set_auto_start(req, false);
345 r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0);
349 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
351 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
353 *h_errnop = HOST_NOT_FOUND;
354 return NSS_STATUS_NOTFOUND;
357 if (bus_error_shall_fallback(&error)) {
359 enum nss_status (*fallback)(
362 struct hostent *result,
363 char *buffer, size_t buflen,
364 int *errnop, int *h_errnop,
368 fallback = (enum nss_status (*)(const char *name,
370 struct hostent *result,
371 char *buffer, size_t buflen,
372 int *errnop, int *h_errnop,
375 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
377 return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
381 *h_errnop = NO_RECOVERY;
382 return NSS_STATUS_UNAVAIL;
385 c = count_addresses(reply, af, &canonical);
392 *h_errnop = HOST_NOT_FOUND;
393 return NSS_STATUS_NOTFOUND;
396 if (isempty(canonical))
399 alen = FAMILY_ADDRESS_SIZE(af);
400 l = strlen(canonical);
402 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
406 *h_errnop = TRY_AGAIN;
407 return NSS_STATUS_TRYAGAIN;
410 /* First, append name */
412 memcpy(r_name, canonical, l+1);
415 /* Second, create empty aliases array */
416 r_aliases = buffer + idx;
417 ((char**) r_aliases)[0] = NULL;
418 idx += sizeof(char*);
420 /* Third, append addresses */
421 r_addr = buffer + idx;
423 r = sd_bus_message_read(reply, "i", &ifindex);
432 r = sd_bus_message_enter_container(reply, 'a', "(iay)");
436 while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
441 r = sd_bus_message_read(reply, "i", &family);
445 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
449 r = sd_bus_message_exit_container(reply);
461 memcpy(r_addr + i*ALIGN(alen), a, alen);
468 idx += c * ALIGN(alen);
470 /* Fourth, append address pointer array */
471 r_addr_list = buffer + idx;
472 for (i = 0; i < c; i++)
473 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
475 ((char**) r_addr_list)[i] = NULL;
476 idx += (c+1) * sizeof(char*);
480 result->h_name = r_name;
481 result->h_aliases = (char**) r_aliases;
482 result->h_addrtype = af;
483 result->h_length = alen;
484 result->h_addr_list = (char**) r_addr_list;
486 /* Explicitly reset all error variables */
488 *h_errnop = NETDB_SUCCESS;
497 return NSS_STATUS_SUCCESS;
502 return NSS_STATUS_UNAVAIL;
505 enum nss_status _nss_resolve_gethostbyaddr2_r(
506 const void* addr, socklen_t len,
508 struct hostent *result,
509 char *buffer, size_t buflen,
510 int *errnop, int *h_errnop,
513 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
514 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
515 char *r_name, *r_aliases, *r_addr, *r_addr_list;
516 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
517 unsigned c = 0, i = 0;
528 if (!IN_SET(af, AF_INET, AF_INET6)) {
529 *errnop = EAFNOSUPPORT;
531 return NSS_STATUS_UNAVAIL;
534 if (len != FAMILY_ADDRESS_SIZE(af)) {
536 *h_errnop = NO_RECOVERY;
537 return NSS_STATUS_UNAVAIL;
540 r = sd_bus_open_system(&bus);
544 r = sd_bus_message_new_method_call(
547 "org.freedesktop.resolve1",
548 "/org/freedesktop/resolve1",
549 "org.freedesktop.resolve1.Manager",
554 r = sd_bus_message_set_auto_start(req, false);
558 r = sd_bus_message_append(req, "ii", 0, af);
562 r = sd_bus_message_append_array(req, 'y', addr, len);
566 r = sd_bus_message_append(req, "t", (uint64_t) 0);
570 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
572 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
574 *h_errnop = HOST_NOT_FOUND;
575 return NSS_STATUS_NOTFOUND;
578 if (bus_error_shall_fallback(&error)) {
580 enum nss_status (*fallback)(
581 const void* addr, socklen_t len,
583 struct hostent *result,
584 char *buffer, size_t buflen,
585 int *errnop, int *h_errnop,
588 fallback = (enum nss_status (*)(
589 const void* addr, socklen_t len,
591 struct hostent *result,
592 char *buffer, size_t buflen,
593 int *errnop, int *h_errnop,
595 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
598 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
602 *h_errnop = NO_RECOVERY;
603 return NSS_STATUS_UNAVAIL;
606 r = sd_bus_message_read(reply, "i", &ifindex);
615 r = sd_bus_message_enter_container(reply, 'a', "s");
619 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
621 ms += ALIGN(strlen(n) + 1);
626 r = sd_bus_message_rewind(reply, false);
632 *h_errnop = HOST_NOT_FOUND;
633 return NSS_STATUS_NOTFOUND;
636 ms += ALIGN(len) + /* the address */
637 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
638 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
642 *h_errnop = TRY_AGAIN;
643 return NSS_STATUS_TRYAGAIN;
646 /* First, place address */
648 memcpy(r_addr, addr, len);
651 /* Second, place address list */
652 r_addr_list = buffer + idx;
653 ((char**) r_addr_list)[0] = r_addr;
654 ((char**) r_addr_list)[1] = NULL;
655 idx += sizeof(char*) * 2;
657 /* Third, reserve space for the aliases array */
658 r_aliases = buffer + idx;
659 idx += sizeof(char*) * c;
661 /* Fourth, place aliases */
663 r_name = buffer + idx;
664 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
673 ((char**) r_aliases)[i-1] = p;
681 ((char**) r_aliases)[c-1] = NULL;
684 result->h_name = r_name;
685 result->h_aliases = (char**) r_aliases;
686 result->h_addrtype = af;
687 result->h_length = len;
688 result->h_addr_list = (char**) r_addr_list;
693 /* Explicitly reset all error variables */
695 *h_errnop = NETDB_SUCCESS;
698 return NSS_STATUS_SUCCESS;
703 return NSS_STATUS_UNAVAIL;
706 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
707 NSS_GETHOSTBYADDR_FALLBACKS(resolve);