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_close_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 /* Explicitly reset all error variables */
286 *h_errnop = NETDB_SUCCESS;
289 return NSS_STATUS_SUCCESS;
294 return NSS_STATUS_UNAVAIL;
297 enum nss_status _nss_resolve_gethostbyname3_r(
300 struct hostent *result,
301 char *buffer, size_t buflen,
302 int *errnop, int *h_errnop,
306 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
307 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
308 char *r_name, *r_aliases, *r_addr, *r_addr_list;
309 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
310 size_t l, idx, ms, alen;
311 const char *canonical;
323 if (af != AF_INET && af != AF_INET6) {
328 r = sd_bus_open_system(&bus);
332 r = sd_bus_message_new_method_call(
335 "org.freedesktop.resolve1",
336 "/org/freedesktop/resolve1",
337 "org.freedesktop.resolve1.Manager",
342 r = sd_bus_message_set_auto_start(req, false);
346 r = sd_bus_message_append(req, "si", name, af);
350 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
352 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
354 *h_errnop = HOST_NOT_FOUND;
355 return NSS_STATUS_NOTFOUND;
358 if (bus_error_shall_fallback(&error)) {
360 enum nss_status (*fallback)(
363 struct hostent *result,
364 char *buffer, size_t buflen,
365 int *errnop, int *h_errnop,
369 fallback = (enum nss_status (*)(const char *name,
371 struct hostent *result,
372 char *buffer, size_t buflen,
373 int *errnop, int *h_errnop,
376 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
378 return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp);
382 *h_errnop = NO_RECOVERY;
383 return NSS_STATUS_UNAVAIL;
386 c = count_addresses(reply, af, &canonical);
393 *h_errnop = HOST_NOT_FOUND;
394 return NSS_STATUS_NOTFOUND;
397 if (isempty(canonical))
400 alen = FAMILY_ADDRESS_SIZE(af);
401 l = strlen(canonical);
405 (c > 0 ? c : 1) * ALIGN(alen) +
406 (c > 0 ? c+1 : 2) * sizeof(char*);
410 *h_errnop = TRY_AGAIN;
411 return NSS_STATUS_TRYAGAIN;
414 /* First, append name */
416 memcpy(r_name, canonical, l+1);
419 /* Second, create empty aliases array */
420 r_aliases = buffer + idx;
421 ((char**) r_aliases)[0] = NULL;
422 idx += sizeof(char*);
424 /* Third, append addresses */
425 r_addr = buffer + idx;
427 r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
431 while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
436 r = sd_bus_message_read(reply, "i", &family);
440 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
444 r = sd_bus_message_read(reply, "i", &ifindex);
448 r = sd_bus_message_exit_container(reply);
465 memcpy(r_addr + i*ALIGN(alen), a, alen);
472 idx += c * ALIGN(alen);
474 /* Fourth, append address pointer array */
475 r_addr_list = buffer + idx;
476 for (i = 0; i < c; i++)
477 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
479 ((char**) r_addr_list)[i] = NULL;
480 idx += (c+1) * sizeof(char*);
484 result->h_name = r_name;
485 result->h_aliases = (char**) r_aliases;
486 result->h_addrtype = af;
487 result->h_length = alen;
488 result->h_addr_list = (char**) r_addr_list;
490 /* Explicitly reset all error variables */
492 *h_errnop = NETDB_SUCCESS;
501 return NSS_STATUS_SUCCESS;
506 return NSS_STATUS_UNAVAIL;
509 enum nss_status _nss_resolve_gethostbyaddr2_r(
510 const void* addr, socklen_t len,
512 struct hostent *result,
513 char *buffer, size_t buflen,
514 int *errnop, int *h_errnop,
517 _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
518 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
519 char *r_name, *r_aliases, *r_addr, *r_addr_list;
520 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
521 unsigned c = 0, i = 0;
532 if (!IN_SET(af, AF_INET, AF_INET6)) {
533 *errnop = EAFNOSUPPORT;
535 return NSS_STATUS_UNAVAIL;
538 if (len != FAMILY_ADDRESS_SIZE(af)) {
540 *h_errnop = NO_RECOVERY;
541 return NSS_STATUS_UNAVAIL;
544 r = sd_bus_open_system(&bus);
548 r = sd_bus_message_new_method_call(
551 "org.freedesktop.resolve1",
552 "/org/freedesktop/resolve1",
553 "org.freedesktop.resolve1.Manager",
558 r = sd_bus_message_set_auto_start(req, false);
562 r = sd_bus_message_append(req, "i", af);
566 r = sd_bus_message_append_array(req, 'y', addr, len);
570 r = sd_bus_message_append(req, "i", 0);
574 r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
576 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
578 *h_errnop = HOST_NOT_FOUND;
579 return NSS_STATUS_NOTFOUND;
582 if (bus_error_shall_fallback(&error)) {
584 enum nss_status (*fallback)(
585 const void* addr, socklen_t len,
587 struct hostent *result,
588 char *buffer, size_t buflen,
589 int *errnop, int *h_errnop,
592 fallback = (enum nss_status (*)(
593 const void* addr, socklen_t len,
595 struct hostent *result,
596 char *buffer, size_t buflen,
597 int *errnop, int *h_errnop,
599 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
602 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
606 *h_errnop = NO_RECOVERY;
607 return NSS_STATUS_UNAVAIL;
610 r = sd_bus_message_enter_container(reply, 'a', "s");
614 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
616 ms += ALIGN(strlen(n) + 1);
621 r = sd_bus_message_rewind(reply, false);
627 *h_errnop = HOST_NOT_FOUND;
628 return NSS_STATUS_NOTFOUND;
631 ms += ALIGN(len) + /* the address */
632 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
633 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
637 *h_errnop = TRY_AGAIN;
638 return NSS_STATUS_TRYAGAIN;
641 /* First, place address */
643 memcpy(r_addr, addr, len);
646 /* Second, place address list */
647 r_addr_list = buffer + idx;
648 ((char**) r_addr_list)[0] = r_addr;
649 ((char**) r_addr_list)[1] = NULL;
650 idx += sizeof(char*) * 2;
652 /* Third, reserve space for the aliases array */
653 r_aliases = buffer + idx;
654 idx += sizeof(char*) * c;
656 /* Fourth, place aliases */
658 r_name = buffer + idx;
659 while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
668 ((char**) r_aliases)[i-1] = p;
676 ((char**) r_aliases)[c-1] = NULL;
679 result->h_name = r_name;
680 result->h_aliases = (char**) r_aliases;
681 result->h_addrtype = af;
682 result->h_length = len;
683 result->h_addr_list = (char**) r_addr_list;
688 /* Explicitly reset all error variables */
690 *h_errnop = NETDB_SUCCESS;
693 return NSS_STATUS_SUCCESS;
698 return NSS_STATUS_UNAVAIL;
701 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
702 NSS_GETHOSTBYADDR_FALLBACKS(resolve);