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"
32 #include "in-addr-util.h"
35 #include "string-util.h"
37 #include "signal-util.h"
39 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
40 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
42 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
44 typedef void (*voidfunc_t
)(void);
46 static voidfunc_t
find_fallback(const char *module
, const char *symbol
) {
49 /* Try to find a fallback NSS module symbol */
51 dl
= dlopen(module
, RTLD_LAZY
|RTLD_NODELETE
);
55 return dlsym(dl
, symbol
);
58 static bool bus_error_shall_fallback(sd_bus_error
*e
) {
59 return sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
60 sd_bus_error_has_name(e
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
) ||
61 sd_bus_error_has_name(e
, SD_BUS_ERROR_NO_REPLY
) ||
62 sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
);
65 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
71 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
75 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
78 assert_cc(sizeof(int32_t) == sizeof(int));
80 r
= sd_bus_message_read(m
, "ii", &ifindex
, &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 enum nss_status (*fallback
)(
124 struct gaih_addrtuple
**pat
,
125 char *buffer
, size_t buflen
,
126 int *errnop
, int *h_errnop
,
129 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
130 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
131 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
132 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
133 const char *canonical
= NULL
;
138 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
146 r
= sd_bus_open_system(&bus
);
150 r
= sd_bus_message_new_method_call(
153 "org.freedesktop.resolve1",
154 "/org/freedesktop/resolve1",
155 "org.freedesktop.resolve1.Manager",
160 r
= sd_bus_message_set_auto_start(req
, false);
164 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
168 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
170 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
172 *h_errnop
= HOST_NOT_FOUND
;
173 return NSS_STATUS_NOTFOUND
;
176 if (bus_error_shall_fallback(&error
))
182 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
189 *h_errnop
= HOST_NOT_FOUND
;
190 return NSS_STATUS_NOTFOUND
;
193 if (isempty(canonical
))
196 l
= strlen(canonical
);
197 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
200 *h_errnop
= TRY_AGAIN
;
201 return NSS_STATUS_TRYAGAIN
;
204 /* First, append name */
206 memcpy(r_name
, canonical
, l
+1);
209 /* Second, append addresses */
210 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
212 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
216 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
221 assert_cc(sizeof(int32_t) == sizeof(int));
223 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
232 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
236 r
= sd_bus_message_exit_container(reply
);
240 if (!IN_SET(family
, AF_INET
, AF_INET6
))
243 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
248 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
249 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
250 r_tuple
->name
= r_name
;
251 r_tuple
->family
= family
;
252 r_tuple
->scopeid
= ifindex
;
253 memcpy(r_tuple
->addr
, a
, sz
);
255 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
265 **pat
= *r_tuple_first
;
267 *pat
= r_tuple_first
;
272 /* Explicitly reset all error variables */
274 *h_errnop
= NETDB_SUCCESS
;
277 return NSS_STATUS_SUCCESS
;
280 fallback
= (enum nss_status (*)(const char *name
,
281 struct gaih_addrtuple
**pat
,
282 char *buffer
, size_t buflen
,
283 int *errnop
, int *h_errnop
,
285 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
288 return fallback(name
, pat
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
292 *h_errnop
= NO_RECOVERY
;
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 enum nss_status (*fallback
)(
308 struct hostent
*result
,
309 char *buffer
, size_t buflen
,
310 int *errnop
, int *h_errnop
,
314 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
315 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
316 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
317 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
318 size_t l
, idx
, ms
, alen
;
319 const char *canonical
;
322 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
333 if (af
!= AF_INET
&& af
!= AF_INET6
) {
338 r
= sd_bus_open_system(&bus
);
342 r
= sd_bus_message_new_method_call(
345 "org.freedesktop.resolve1",
346 "/org/freedesktop/resolve1",
347 "org.freedesktop.resolve1.Manager",
352 r
= sd_bus_message_set_auto_start(req
, false);
356 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
360 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
362 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
364 *h_errnop
= HOST_NOT_FOUND
;
365 return NSS_STATUS_NOTFOUND
;
368 if (bus_error_shall_fallback(&error
))
374 c
= count_addresses(reply
, af
, &canonical
);
381 *h_errnop
= HOST_NOT_FOUND
;
382 return NSS_STATUS_NOTFOUND
;
385 if (isempty(canonical
))
388 alen
= FAMILY_ADDRESS_SIZE(af
);
389 l
= strlen(canonical
);
391 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
395 *h_errnop
= TRY_AGAIN
;
396 return NSS_STATUS_TRYAGAIN
;
399 /* First, append name */
401 memcpy(r_name
, canonical
, l
+1);
404 /* Second, create empty aliases array */
405 r_aliases
= buffer
+ idx
;
406 ((char**) r_aliases
)[0] = NULL
;
407 idx
+= sizeof(char*);
409 /* Third, append addresses */
410 r_addr
= buffer
+ idx
;
412 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
416 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
421 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
430 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
434 r
= sd_bus_message_exit_container(reply
);
446 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
453 idx
+= c
* ALIGN(alen
);
455 /* Fourth, append address pointer array */
456 r_addr_list
= buffer
+ idx
;
457 for (i
= 0; i
< c
; i
++)
458 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
460 ((char**) r_addr_list
)[i
] = NULL
;
461 idx
+= (c
+1) * sizeof(char*);
465 result
->h_name
= r_name
;
466 result
->h_aliases
= (char**) r_aliases
;
467 result
->h_addrtype
= af
;
468 result
->h_length
= alen
;
469 result
->h_addr_list
= (char**) r_addr_list
;
471 /* Explicitly reset all error variables */
473 *h_errnop
= NETDB_SUCCESS
;
482 return NSS_STATUS_SUCCESS
;
485 fallback
= (enum nss_status (*)(const char *name
,
487 struct hostent
*result
,
488 char *buffer
, size_t buflen
,
489 int *errnop
, int *h_errnop
,
492 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
494 return fallback(name
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
, canonp
);
498 *h_errnop
= NO_RECOVERY
;
499 return NSS_STATUS_UNAVAIL
;
502 enum nss_status
_nss_resolve_gethostbyaddr2_r(
503 const void* addr
, socklen_t len
,
505 struct hostent
*result
,
506 char *buffer
, size_t buflen
,
507 int *errnop
, int *h_errnop
,
510 enum nss_status (*fallback
)(
511 const void* addr
, socklen_t len
,
513 struct hostent
*result
,
514 char *buffer
, size_t buflen
,
515 int *errnop
, int *h_errnop
,
519 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
520 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
521 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
522 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
523 unsigned c
= 0, i
= 0;
528 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
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
))
591 *h_errnop
= NO_RECOVERY
;
592 return NSS_STATUS_UNAVAIL
;
595 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
599 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
607 ms
+= ALIGN(strlen(n
) + 1);
612 r
= sd_bus_message_rewind(reply
, false);
618 *h_errnop
= HOST_NOT_FOUND
;
619 return NSS_STATUS_NOTFOUND
;
622 ms
+= ALIGN(len
) + /* the address */
623 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
624 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
628 *h_errnop
= TRY_AGAIN
;
629 return NSS_STATUS_TRYAGAIN
;
632 /* First, place address */
634 memcpy(r_addr
, addr
, len
);
637 /* Second, place address list */
638 r_addr_list
= buffer
+ idx
;
639 ((char**) r_addr_list
)[0] = r_addr
;
640 ((char**) r_addr_list
)[1] = NULL
;
641 idx
+= sizeof(char*) * 2;
643 /* Third, reserve space for the aliases array */
644 r_aliases
= buffer
+ idx
;
645 idx
+= sizeof(char*) * c
;
647 /* Fourth, place aliases */
649 r_name
= buffer
+ idx
;
650 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
659 ((char**) r_aliases
)[i
-1] = p
;
667 ((char**) r_aliases
)[c
-1] = NULL
;
670 result
->h_name
= r_name
;
671 result
->h_aliases
= (char**) r_aliases
;
672 result
->h_addrtype
= af
;
673 result
->h_length
= len
;
674 result
->h_addr_list
= (char**) r_addr_list
;
679 /* Explicitly reset all error variables */
681 *h_errnop
= NETDB_SUCCESS
;
684 return NSS_STATUS_SUCCESS
;
687 fallback
= (enum nss_status (*)(
688 const void* addr
, socklen_t len
,
690 struct hostent
*result
,
691 char *buffer
, size_t buflen
,
692 int *errnop
, int *h_errnop
,
694 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
697 return fallback(addr
, len
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
701 *h_errnop
= NO_RECOVERY
;
702 return NSS_STATUS_UNAVAIL
;
705 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
706 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);