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"
38 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
39 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
41 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
43 typedef void (*voidfunc_t
)(void);
45 static voidfunc_t
find_fallback(const char *module
, const char *symbol
) {
48 /* Try to find a fallback NSS module symbol */
50 dl
= dlopen(module
, RTLD_LAZY
|RTLD_NODELETE
);
54 return dlsym(dl
, symbol
);
57 static bool bus_error_shall_fallback(sd_bus_error
*e
) {
58 return sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
59 sd_bus_error_has_name(e
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
) ||
60 sd_bus_error_has_name(e
, SD_BUS_ERROR_NO_REPLY
) ||
61 sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
);
64 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
70 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
74 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
77 assert_cc(sizeof(int32_t) == sizeof(int));
79 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
83 r
= sd_bus_message_skip(m
, "ay");
87 r
= sd_bus_message_exit_container(m
);
91 if (af
!= AF_UNSPEC
&& family
!= af
)
99 r
= sd_bus_message_exit_container(m
);
103 r
= sd_bus_message_read(m
, "s", canonical
);
107 r
= sd_bus_message_rewind(m
, true);
114 enum nss_status
_nss_resolve_gethostbyname4_r(
116 struct gaih_addrtuple
**pat
,
117 char *buffer
, size_t buflen
,
118 int *errnop
, int *h_errnop
,
121 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
122 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
123 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
124 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
125 const char *canonical
= NULL
;
136 r
= sd_bus_open_system(&bus
);
140 r
= sd_bus_message_new_method_call(
143 "org.freedesktop.resolve1",
144 "/org/freedesktop/resolve1",
145 "org.freedesktop.resolve1.Manager",
150 r
= sd_bus_message_set_auto_start(req
, false);
154 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
158 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
160 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
162 *h_errnop
= HOST_NOT_FOUND
;
163 return NSS_STATUS_NOTFOUND
;
166 if (bus_error_shall_fallback(&error
)) {
168 enum nss_status (*fallback
)(
170 struct gaih_addrtuple
**pat
,
171 char *buffer
, size_t buflen
,
172 int *errnop
, int *h_errnop
,
175 fallback
= (enum nss_status (*)(const char *name
,
176 struct gaih_addrtuple
**pat
,
177 char *buffer
, size_t buflen
,
178 int *errnop
, int *h_errnop
,
180 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
182 return fallback(name
, pat
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
186 *h_errnop
= NO_RECOVERY
;
187 return NSS_STATUS_UNAVAIL
;
190 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
197 *h_errnop
= HOST_NOT_FOUND
;
198 return NSS_STATUS_NOTFOUND
;
201 if (isempty(canonical
))
204 l
= strlen(canonical
);
205 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
208 *h_errnop
= TRY_AGAIN
;
209 return NSS_STATUS_TRYAGAIN
;
212 /* First, append name */
214 memcpy(r_name
, canonical
, l
+1);
217 /* Second, append addresses */
218 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
220 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
224 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
229 assert_cc(sizeof(int32_t) == sizeof(int));
231 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
240 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
244 r
= sd_bus_message_exit_container(reply
);
248 if (!IN_SET(family
, AF_INET
, AF_INET6
))
251 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
256 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
257 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
258 r_tuple
->name
= r_name
;
259 r_tuple
->family
= family
;
260 r_tuple
->scopeid
= ifindex
;
261 memcpy(r_tuple
->addr
, a
, sz
);
263 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
273 **pat
= *r_tuple_first
;
275 *pat
= r_tuple_first
;
280 /* Explicitly reset all error variables */
282 *h_errnop
= NETDB_SUCCESS
;
285 return NSS_STATUS_SUCCESS
;
290 return NSS_STATUS_UNAVAIL
;
293 enum nss_status
_nss_resolve_gethostbyname3_r(
296 struct hostent
*result
,
297 char *buffer
, size_t buflen
,
298 int *errnop
, int *h_errnop
,
302 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
303 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
304 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
305 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
306 size_t l
, idx
, ms
, alen
;
307 const char *canonical
;
319 if (af
!= AF_INET
&& af
!= AF_INET6
) {
324 r
= sd_bus_open_system(&bus
);
328 r
= sd_bus_message_new_method_call(
331 "org.freedesktop.resolve1",
332 "/org/freedesktop/resolve1",
333 "org.freedesktop.resolve1.Manager",
338 r
= sd_bus_message_set_auto_start(req
, false);
342 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
346 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
348 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
350 *h_errnop
= HOST_NOT_FOUND
;
351 return NSS_STATUS_NOTFOUND
;
354 if (bus_error_shall_fallback(&error
)) {
356 enum nss_status (*fallback
)(
359 struct hostent
*result
,
360 char *buffer
, size_t buflen
,
361 int *errnop
, int *h_errnop
,
365 fallback
= (enum nss_status (*)(const char *name
,
367 struct hostent
*result
,
368 char *buffer
, size_t buflen
,
369 int *errnop
, int *h_errnop
,
372 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
374 return fallback(name
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
, canonp
);
378 *h_errnop
= NO_RECOVERY
;
379 return NSS_STATUS_UNAVAIL
;
382 c
= count_addresses(reply
, af
, &canonical
);
389 *h_errnop
= HOST_NOT_FOUND
;
390 return NSS_STATUS_NOTFOUND
;
393 if (isempty(canonical
))
396 alen
= FAMILY_ADDRESS_SIZE(af
);
397 l
= strlen(canonical
);
399 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
403 *h_errnop
= TRY_AGAIN
;
404 return NSS_STATUS_TRYAGAIN
;
407 /* First, append name */
409 memcpy(r_name
, canonical
, l
+1);
412 /* Second, create empty aliases array */
413 r_aliases
= buffer
+ idx
;
414 ((char**) r_aliases
)[0] = NULL
;
415 idx
+= sizeof(char*);
417 /* Third, append addresses */
418 r_addr
= buffer
+ idx
;
420 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
424 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
429 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
438 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
442 r
= sd_bus_message_exit_container(reply
);
454 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
461 idx
+= c
* ALIGN(alen
);
463 /* Fourth, append address pointer array */
464 r_addr_list
= buffer
+ idx
;
465 for (i
= 0; i
< c
; i
++)
466 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
468 ((char**) r_addr_list
)[i
] = NULL
;
469 idx
+= (c
+1) * sizeof(char*);
473 result
->h_name
= r_name
;
474 result
->h_aliases
= (char**) r_aliases
;
475 result
->h_addrtype
= af
;
476 result
->h_length
= alen
;
477 result
->h_addr_list
= (char**) r_addr_list
;
479 /* Explicitly reset all error variables */
481 *h_errnop
= NETDB_SUCCESS
;
490 return NSS_STATUS_SUCCESS
;
495 return NSS_STATUS_UNAVAIL
;
498 enum nss_status
_nss_resolve_gethostbyaddr2_r(
499 const void* addr
, socklen_t len
,
501 struct hostent
*result
,
502 char *buffer
, size_t buflen
,
503 int *errnop
, int *h_errnop
,
506 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
507 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
508 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
509 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
510 unsigned c
= 0, i
= 0;
521 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
522 *errnop
= EAFNOSUPPORT
;
524 return NSS_STATUS_UNAVAIL
;
527 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
529 *h_errnop
= NO_RECOVERY
;
530 return NSS_STATUS_UNAVAIL
;
533 r
= sd_bus_open_system(&bus
);
537 r
= sd_bus_message_new_method_call(
540 "org.freedesktop.resolve1",
541 "/org/freedesktop/resolve1",
542 "org.freedesktop.resolve1.Manager",
547 r
= sd_bus_message_set_auto_start(req
, false);
551 r
= sd_bus_message_append(req
, "ii", 0, af
);
555 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
559 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
563 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
565 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
567 *h_errnop
= HOST_NOT_FOUND
;
568 return NSS_STATUS_NOTFOUND
;
571 if (bus_error_shall_fallback(&error
)) {
573 enum nss_status (*fallback
)(
574 const void* addr
, socklen_t len
,
576 struct hostent
*result
,
577 char *buffer
, size_t buflen
,
578 int *errnop
, int *h_errnop
,
581 fallback
= (enum nss_status (*)(
582 const void* addr
, socklen_t len
,
584 struct hostent
*result
,
585 char *buffer
, size_t buflen
,
586 int *errnop
, int *h_errnop
,
588 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
591 return fallback(addr
, len
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
595 *h_errnop
= NO_RECOVERY
;
596 return NSS_STATUS_UNAVAIL
;
599 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
603 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
611 ms
+= ALIGN(strlen(n
) + 1);
616 r
= sd_bus_message_rewind(reply
, false);
622 *h_errnop
= HOST_NOT_FOUND
;
623 return NSS_STATUS_NOTFOUND
;
626 ms
+= ALIGN(len
) + /* the address */
627 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
628 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
632 *h_errnop
= TRY_AGAIN
;
633 return NSS_STATUS_TRYAGAIN
;
636 /* First, place address */
638 memcpy(r_addr
, addr
, len
);
641 /* Second, place address list */
642 r_addr_list
= buffer
+ idx
;
643 ((char**) r_addr_list
)[0] = r_addr
;
644 ((char**) r_addr_list
)[1] = NULL
;
645 idx
+= sizeof(char*) * 2;
647 /* Third, reserve space for the aliases array */
648 r_aliases
= buffer
+ idx
;
649 idx
+= sizeof(char*) * c
;
651 /* Fourth, place aliases */
653 r_name
= buffer
+ idx
;
654 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
663 ((char**) r_aliases
)[i
-1] = p
;
671 ((char**) r_aliases
)[c
-1] = NULL
;
674 result
->h_name
= r_name
;
675 result
->h_aliases
= (char**) r_aliases
;
676 result
->h_addrtype
= af
;
677 result
->h_length
= len
;
678 result
->h_addr_list
= (char**) r_addr_list
;
683 /* Explicitly reset all error variables */
685 *h_errnop
= NETDB_SUCCESS
;
688 return NSS_STATUS_SUCCESS
;
693 return NSS_STATUS_UNAVAIL
;
696 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
697 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);