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"
33 #include "in-addr-util.h"
36 #include "string-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 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
123 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
124 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
125 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
126 const char *canonical
= NULL
;
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_enter_container(reply
, 'a', "(iiay)");
225 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
230 assert_cc(sizeof(int32_t) == sizeof(int));
232 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
241 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
245 r
= sd_bus_message_exit_container(reply
);
249 if (!IN_SET(family
, AF_INET
, AF_INET6
))
252 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
257 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
258 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
259 r_tuple
->name
= r_name
;
260 r_tuple
->family
= family
;
261 r_tuple
->scopeid
= ifindex
;
262 memcpy(r_tuple
->addr
, a
, sz
);
264 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
274 **pat
= *r_tuple_first
;
276 *pat
= r_tuple_first
;
281 /* Explicitly reset all error variables */
283 *h_errnop
= NETDB_SUCCESS
;
286 return NSS_STATUS_SUCCESS
;
291 return NSS_STATUS_UNAVAIL
;
294 enum nss_status
_nss_resolve_gethostbyname3_r(
297 struct hostent
*result
,
298 char *buffer
, size_t buflen
,
299 int *errnop
, int *h_errnop
,
303 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
304 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
305 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
306 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
307 size_t l
, idx
, ms
, alen
;
308 const char *canonical
;
320 if (af
!= AF_INET
&& af
!= AF_INET6
) {
325 r
= sd_bus_open_system(&bus
);
329 r
= sd_bus_message_new_method_call(
332 "org.freedesktop.resolve1",
333 "/org/freedesktop/resolve1",
334 "org.freedesktop.resolve1.Manager",
339 r
= sd_bus_message_set_auto_start(req
, false);
343 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
347 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
349 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
351 *h_errnop
= HOST_NOT_FOUND
;
352 return NSS_STATUS_NOTFOUND
;
355 if (bus_error_shall_fallback(&error
)) {
357 enum nss_status (*fallback
)(
360 struct hostent
*result
,
361 char *buffer
, size_t buflen
,
362 int *errnop
, int *h_errnop
,
366 fallback
= (enum nss_status (*)(const char *name
,
368 struct hostent
*result
,
369 char *buffer
, size_t buflen
,
370 int *errnop
, int *h_errnop
,
373 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r");
375 return fallback(name
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
, canonp
);
379 *h_errnop
= NO_RECOVERY
;
380 return NSS_STATUS_UNAVAIL
;
383 c
= count_addresses(reply
, af
, &canonical
);
390 *h_errnop
= HOST_NOT_FOUND
;
391 return NSS_STATUS_NOTFOUND
;
394 if (isempty(canonical
))
397 alen
= FAMILY_ADDRESS_SIZE(af
);
398 l
= strlen(canonical
);
400 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
404 *h_errnop
= TRY_AGAIN
;
405 return NSS_STATUS_TRYAGAIN
;
408 /* First, append name */
410 memcpy(r_name
, canonical
, l
+1);
413 /* Second, create empty aliases array */
414 r_aliases
= buffer
+ idx
;
415 ((char**) r_aliases
)[0] = NULL
;
416 idx
+= sizeof(char*);
418 /* Third, append addresses */
419 r_addr
= buffer
+ idx
;
421 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
425 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
430 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
439 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
443 r
= sd_bus_message_exit_container(reply
);
455 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
462 idx
+= c
* ALIGN(alen
);
464 /* Fourth, append address pointer array */
465 r_addr_list
= buffer
+ idx
;
466 for (i
= 0; i
< c
; i
++)
467 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
469 ((char**) r_addr_list
)[i
] = NULL
;
470 idx
+= (c
+1) * sizeof(char*);
474 result
->h_name
= r_name
;
475 result
->h_aliases
= (char**) r_aliases
;
476 result
->h_addrtype
= af
;
477 result
->h_length
= alen
;
478 result
->h_addr_list
= (char**) r_addr_list
;
480 /* Explicitly reset all error variables */
482 *h_errnop
= NETDB_SUCCESS
;
491 return NSS_STATUS_SUCCESS
;
496 return NSS_STATUS_UNAVAIL
;
499 enum nss_status
_nss_resolve_gethostbyaddr2_r(
500 const void* addr
, socklen_t len
,
502 struct hostent
*result
,
503 char *buffer
, size_t buflen
,
504 int *errnop
, int *h_errnop
,
507 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
508 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
509 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
510 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
511 unsigned c
= 0, i
= 0;
522 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
523 *errnop
= EAFNOSUPPORT
;
525 return NSS_STATUS_UNAVAIL
;
528 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
530 *h_errnop
= NO_RECOVERY
;
531 return NSS_STATUS_UNAVAIL
;
534 r
= sd_bus_open_system(&bus
);
538 r
= sd_bus_message_new_method_call(
541 "org.freedesktop.resolve1",
542 "/org/freedesktop/resolve1",
543 "org.freedesktop.resolve1.Manager",
548 r
= sd_bus_message_set_auto_start(req
, false);
552 r
= sd_bus_message_append(req
, "ii", 0, af
);
556 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
560 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
564 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
566 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
568 *h_errnop
= HOST_NOT_FOUND
;
569 return NSS_STATUS_NOTFOUND
;
572 if (bus_error_shall_fallback(&error
)) {
574 enum nss_status (*fallback
)(
575 const void* addr
, socklen_t len
,
577 struct hostent
*result
,
578 char *buffer
, size_t buflen
,
579 int *errnop
, int *h_errnop
,
582 fallback
= (enum nss_status (*)(
583 const void* addr
, socklen_t len
,
585 struct hostent
*result
,
586 char *buffer
, size_t buflen
,
587 int *errnop
, int *h_errnop
,
589 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
592 return fallback(addr
, len
, af
, result
, buffer
, buflen
, errnop
, h_errnop
, ttlp
);
596 *h_errnop
= NO_RECOVERY
;
597 return NSS_STATUS_UNAVAIL
;
600 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
604 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
612 ms
+= ALIGN(strlen(n
) + 1);
617 r
= sd_bus_message_rewind(reply
, false);
623 *h_errnop
= HOST_NOT_FOUND
;
624 return NSS_STATUS_NOTFOUND
;
627 ms
+= ALIGN(len
) + /* the address */
628 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
629 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
633 *h_errnop
= TRY_AGAIN
;
634 return NSS_STATUS_TRYAGAIN
;
637 /* First, place address */
639 memcpy(r_addr
, addr
, len
);
642 /* Second, place address list */
643 r_addr_list
= buffer
+ idx
;
644 ((char**) r_addr_list
)[0] = r_addr
;
645 ((char**) r_addr_list
)[1] = NULL
;
646 idx
+= sizeof(char*) * 2;
648 /* Third, reserve space for the aliases array */
649 r_aliases
= buffer
+ idx
;
650 idx
+= sizeof(char*) * c
;
652 /* Fourth, place aliases */
654 r_name
= buffer
+ idx
;
655 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
664 ((char**) r_aliases
)[i
-1] = p
;
672 ((char**) r_aliases
)[c
-1] = NULL
;
675 result
->h_name
= r_name
;
676 result
->h_aliases
= (char**) r_aliases
;
677 result
->h_addrtype
= af
;
678 result
->h_length
= len
;
679 result
->h_addr_list
= (char**) r_addr_list
;
684 /* Explicitly reset all error variables */
686 *h_errnop
= NETDB_SUCCESS
;
689 return NSS_STATUS_SUCCESS
;
694 return NSS_STATUS_UNAVAIL
;
697 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
698 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);