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
) {
69 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
73 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
76 assert_cc(sizeof(int32_t) == sizeof(int));
78 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
82 r
= sd_bus_message_skip(m
, "ay");
86 r
= sd_bus_message_exit_container(m
);
90 if (af
!= AF_UNSPEC
&& family
!= af
)
98 r
= sd_bus_message_exit_container(m
);
102 r
= sd_bus_message_read(m
, "s", canonical
);
106 r
= sd_bus_message_rewind(m
, true);
113 enum nss_status
_nss_resolve_gethostbyname4_r(
115 struct gaih_addrtuple
**pat
,
116 char *buffer
, size_t buflen
,
117 int *errnop
, int *h_errnop
,
120 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
121 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
122 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
123 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
124 const char *canonical
= NULL
;
135 r
= sd_bus_open_system(&bus
);
139 r
= sd_bus_message_new_method_call(
142 "org.freedesktop.resolve1",
143 "/org/freedesktop/resolve1",
144 "org.freedesktop.resolve1.Manager",
149 r
= sd_bus_message_set_auto_start(req
, false);
153 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
157 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
159 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
161 *h_errnop
= HOST_NOT_FOUND
;
162 return NSS_STATUS_NOTFOUND
;
165 if (bus_error_shall_fallback(&error
)) {
167 enum nss_status (*fallback
)(
169 struct gaih_addrtuple
**pat
,
170 char *buffer
, size_t buflen
,
171 int *errnop
, int *h_errnop
,
174 fallback
= (enum nss_status (*)(const char *name
,
175 struct gaih_addrtuple
**pat
,
176 char *buffer
, size_t buflen
,
177 int *errnop
, int *h_errnop
,
179 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
181 return fallback(name
, pat
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
185 *h_errnop
= NO_RECOVERY
;
186 return NSS_STATUS_UNAVAIL
;
189 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
196 *h_errnop
= HOST_NOT_FOUND
;
197 return NSS_STATUS_NOTFOUND
;
200 if (isempty(canonical
))
203 l
= strlen(canonical
);
204 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
207 *h_errnop
= TRY_AGAIN
;
208 return NSS_STATUS_TRYAGAIN
;
211 /* First, append name */
213 memcpy(r_name
, canonical
, l
+1);
216 /* Second, append addresses */
217 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
219 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
223 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
228 assert_cc(sizeof(int32_t) == sizeof(int));
230 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
239 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
243 r
= sd_bus_message_exit_container(reply
);
247 if (!IN_SET(family
, AF_INET
, AF_INET6
))
250 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
255 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
256 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
257 r_tuple
->name
= r_name
;
258 r_tuple
->family
= family
;
259 r_tuple
->scopeid
= ifindex
;
260 memcpy(r_tuple
->addr
, a
, sz
);
262 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
272 **pat
= *r_tuple_first
;
274 *pat
= r_tuple_first
;
279 /* Explicitly reset all error variables */
281 *h_errnop
= NETDB_SUCCESS
;
284 return NSS_STATUS_SUCCESS
;
289 return NSS_STATUS_UNAVAIL
;
292 enum nss_status
_nss_resolve_gethostbyname3_r(
295 struct hostent
*result
,
296 char *buffer
, size_t buflen
,
297 int *errnop
, int *h_errnop
,
301 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
302 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
303 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
304 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
305 size_t l
, idx
, ms
, alen
;
306 const char *canonical
;
318 if (af
!= AF_INET
&& af
!= AF_INET6
) {
323 r
= sd_bus_open_system(&bus
);
327 r
= sd_bus_message_new_method_call(
330 "org.freedesktop.resolve1",
331 "/org/freedesktop/resolve1",
332 "org.freedesktop.resolve1.Manager",
337 r
= sd_bus_message_set_auto_start(req
, false);
341 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
345 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
347 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
349 *h_errnop
= HOST_NOT_FOUND
;
350 return NSS_STATUS_NOTFOUND
;
353 if (bus_error_shall_fallback(&error
)) {
355 enum nss_status (*fallback
)(
358 struct hostent
*result
,
359 char *buffer
, size_t buflen
,
360 int *errnop
, int *h_errnop
,
364 fallback
= (enum nss_status (*)(const char *name
,
366 struct hostent
*result
,
367 char *buffer
, size_t buflen
,
368 int *errnop
, int *h_errnop
,
371 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
373 return fallback(name
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
, canonp
);
377 *h_errnop
= NO_RECOVERY
;
378 return NSS_STATUS_UNAVAIL
;
381 c
= count_addresses(reply
, af
, &canonical
);
388 *h_errnop
= HOST_NOT_FOUND
;
389 return NSS_STATUS_NOTFOUND
;
392 if (isempty(canonical
))
395 alen
= FAMILY_ADDRESS_SIZE(af
);
396 l
= strlen(canonical
);
398 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
402 *h_errnop
= TRY_AGAIN
;
403 return NSS_STATUS_TRYAGAIN
;
406 /* First, append name */
408 memcpy(r_name
, canonical
, l
+1);
411 /* Second, create empty aliases array */
412 r_aliases
= buffer
+ idx
;
413 ((char**) r_aliases
)[0] = NULL
;
414 idx
+= sizeof(char*);
416 /* Third, append addresses */
417 r_addr
= buffer
+ idx
;
419 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
423 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
428 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
437 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
441 r
= sd_bus_message_exit_container(reply
);
453 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
460 idx
+= c
* ALIGN(alen
);
462 /* Fourth, append address pointer array */
463 r_addr_list
= buffer
+ idx
;
464 for (i
= 0; i
< c
; i
++)
465 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
467 ((char**) r_addr_list
)[i
] = NULL
;
468 idx
+= (c
+1) * sizeof(char*);
472 result
->h_name
= r_name
;
473 result
->h_aliases
= (char**) r_aliases
;
474 result
->h_addrtype
= af
;
475 result
->h_length
= alen
;
476 result
->h_addr_list
= (char**) r_addr_list
;
478 /* Explicitly reset all error variables */
480 *h_errnop
= NETDB_SUCCESS
;
489 return NSS_STATUS_SUCCESS
;
494 return NSS_STATUS_UNAVAIL
;
497 enum nss_status
_nss_resolve_gethostbyaddr2_r(
498 const void* addr
, socklen_t len
,
500 struct hostent
*result
,
501 char *buffer
, size_t buflen
,
502 int *errnop
, int *h_errnop
,
505 _cleanup_bus_message_unref_ sd_bus_message
*req
= NULL
, *reply
= NULL
;
506 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
507 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
508 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
509 unsigned c
= 0, i
= 0;
520 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
521 *errnop
= EAFNOSUPPORT
;
523 return NSS_STATUS_UNAVAIL
;
526 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
528 *h_errnop
= NO_RECOVERY
;
529 return NSS_STATUS_UNAVAIL
;
532 r
= sd_bus_open_system(&bus
);
536 r
= sd_bus_message_new_method_call(
539 "org.freedesktop.resolve1",
540 "/org/freedesktop/resolve1",
541 "org.freedesktop.resolve1.Manager",
546 r
= sd_bus_message_set_auto_start(req
, false);
550 r
= sd_bus_message_append(req
, "ii", 0, af
);
554 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
558 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
562 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
564 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
566 *h_errnop
= HOST_NOT_FOUND
;
567 return NSS_STATUS_NOTFOUND
;
570 if (bus_error_shall_fallback(&error
)) {
572 enum nss_status (*fallback
)(
573 const void* addr
, socklen_t len
,
575 struct hostent
*result
,
576 char *buffer
, size_t buflen
,
577 int *errnop
, int *h_errnop
,
580 fallback
= (enum nss_status (*)(
581 const void* addr
, socklen_t len
,
583 struct hostent
*result
,
584 char *buffer
, size_t buflen
,
585 int *errnop
, int *h_errnop
,
587 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
590 return fallback(addr
, len
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
594 *h_errnop
= NO_RECOVERY
;
595 return NSS_STATUS_UNAVAIL
;
598 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
602 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
610 ms
+= ALIGN(strlen(n
) + 1);
615 r
= sd_bus_message_rewind(reply
, false);
621 *h_errnop
= HOST_NOT_FOUND
;
622 return NSS_STATUS_NOTFOUND
;
625 ms
+= ALIGN(len
) + /* the address */
626 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
627 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
631 *h_errnop
= TRY_AGAIN
;
632 return NSS_STATUS_TRYAGAIN
;
635 /* First, place address */
637 memcpy(r_addr
, addr
, len
);
640 /* Second, place address list */
641 r_addr_list
= buffer
+ idx
;
642 ((char**) r_addr_list
)[0] = r_addr
;
643 ((char**) r_addr_list
)[1] = NULL
;
644 idx
+= sizeof(char*) * 2;
646 /* Third, reserve space for the aliases array */
647 r_aliases
= buffer
+ idx
;
648 idx
+= sizeof(char*) * c
;
650 /* Fourth, place aliases */
652 r_name
= buffer
+ idx
;
653 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
662 ((char**) r_aliases
)[i
-1] = p
;
670 ((char**) r_aliases
)[c
-1] = NULL
;
673 result
->h_name
= r_name
;
674 result
->h_aliases
= (char**) r_aliases
;
675 result
->h_addrtype
= af
;
676 result
->h_length
= len
;
677 result
->h_addr_list
= (char**) r_addr_list
;
682 /* Explicitly reset all error variables */
684 *h_errnop
= NETDB_SUCCESS
;
687 return NSS_STATUS_SUCCESS
;
692 return NSS_STATUS_UNAVAIL
;
695 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
696 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);