2 This file is part of systemd.
4 Copyright 2014 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "bus-common-errors.h"
29 #include "in-addr-util.h"
32 #include "string-util.h"
34 #include "signal-util.h"
36 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
37 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
39 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
41 static bool bus_error_shall_fallback(sd_bus_error
*e
) {
42 return sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
43 sd_bus_error_has_name(e
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
) ||
44 sd_bus_error_has_name(e
, SD_BUS_ERROR_NO_REPLY
) ||
45 sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
);
48 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
54 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
58 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
61 assert_cc(sizeof(int32_t) == sizeof(int));
63 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
67 r
= sd_bus_message_skip(m
, "ay");
71 r
= sd_bus_message_exit_container(m
);
75 if (af
!= AF_UNSPEC
&& family
!= af
)
83 r
= sd_bus_message_exit_container(m
);
87 r
= sd_bus_message_read(m
, "s", canonical
);
91 r
= sd_bus_message_rewind(m
, true);
98 static uint32_t ifindex_to_scopeid(int family
, const void *a
, int ifindex
) {
101 if (family
!= AF_INET6
)
104 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
106 assert(sizeof(in6
) == FAMILY_ADDRESS_SIZE(AF_INET6
));
107 memcpy(&in6
, a
, sizeof(struct in6_addr
));
109 return IN6_IS_ADDR_LINKLOCAL(&in6
) ? ifindex
: 0;
112 enum nss_status
_nss_resolve_gethostbyname4_r(
114 struct gaih_addrtuple
**pat
,
115 char *buffer
, size_t buflen
,
116 int *errnop
, int *h_errnop
,
119 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
120 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
121 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
122 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
123 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
124 const char *canonical
= NULL
;
129 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
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 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
168 allowing falling back to other nss modules. Treat all other error conditions as
169 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
170 case so that the nsswitch.conf configuration can distuingish such executed but
171 negative replies from complete failure to talk to resolved). */
172 if (!bus_error_shall_fallback(&error
))
173 ret
= NSS_STATUS_NOTFOUND
;
178 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
185 *h_errnop
= HOST_NOT_FOUND
;
186 return NSS_STATUS_NOTFOUND
;
189 if (isempty(canonical
))
192 l
= strlen(canonical
);
193 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
196 *h_errnop
= NETDB_INTERNAL
;
197 return NSS_STATUS_TRYAGAIN
;
200 /* First, append name */
202 memcpy(r_name
, canonical
, l
+1);
205 /* Second, append addresses */
206 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
208 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
212 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
217 assert_cc(sizeof(int32_t) == sizeof(int));
219 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
228 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
232 r
= sd_bus_message_exit_container(reply
);
236 if (!IN_SET(family
, AF_INET
, AF_INET6
))
239 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
244 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
245 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
246 r_tuple
->name
= r_name
;
247 r_tuple
->family
= family
;
248 r_tuple
->scopeid
= ifindex_to_scopeid(family
, a
, ifindex
);
249 memcpy(r_tuple
->addr
, a
, sz
);
251 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
261 **pat
= *r_tuple_first
;
263 *pat
= r_tuple_first
;
268 /* Explicitly reset all error variables */
270 *h_errnop
= NETDB_SUCCESS
;
273 return NSS_STATUS_SUCCESS
;
277 *h_errnop
= NO_RECOVERY
;
281 enum nss_status
_nss_resolve_gethostbyname3_r(
284 struct hostent
*result
,
285 char *buffer
, size_t buflen
,
286 int *errnop
, int *h_errnop
,
290 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
291 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
292 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
293 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
294 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
295 size_t l
, idx
, ms
, alen
;
296 const char *canonical
;
299 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
310 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
315 r
= sd_bus_open_system(&bus
);
319 r
= sd_bus_message_new_method_call(
322 "org.freedesktop.resolve1",
323 "/org/freedesktop/resolve1",
324 "org.freedesktop.resolve1.Manager",
329 r
= sd_bus_message_set_auto_start(req
, false);
333 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
337 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
339 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
341 *h_errnop
= HOST_NOT_FOUND
;
342 return NSS_STATUS_NOTFOUND
;
345 if (!bus_error_shall_fallback(&error
))
346 ret
= NSS_STATUS_NOTFOUND
;
351 c
= count_addresses(reply
, af
, &canonical
);
358 *h_errnop
= HOST_NOT_FOUND
;
359 return NSS_STATUS_NOTFOUND
;
362 if (isempty(canonical
))
365 alen
= FAMILY_ADDRESS_SIZE(af
);
366 l
= strlen(canonical
);
368 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
372 *h_errnop
= NETDB_INTERNAL
;
373 return NSS_STATUS_TRYAGAIN
;
376 /* First, append name */
378 memcpy(r_name
, canonical
, l
+1);
381 /* Second, create empty aliases array */
382 r_aliases
= buffer
+ idx
;
383 ((char**) r_aliases
)[0] = NULL
;
384 idx
+= sizeof(char*);
386 /* Third, append addresses */
387 r_addr
= buffer
+ idx
;
389 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
393 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
398 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
407 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
411 r
= sd_bus_message_exit_container(reply
);
423 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
430 idx
+= c
* ALIGN(alen
);
432 /* Fourth, append address pointer array */
433 r_addr_list
= buffer
+ idx
;
434 for (i
= 0; i
< c
; i
++)
435 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
437 ((char**) r_addr_list
)[i
] = NULL
;
438 idx
+= (c
+1) * sizeof(char*);
442 result
->h_name
= r_name
;
443 result
->h_aliases
= (char**) r_aliases
;
444 result
->h_addrtype
= af
;
445 result
->h_length
= alen
;
446 result
->h_addr_list
= (char**) r_addr_list
;
448 /* Explicitly reset all error variables */
450 *h_errnop
= NETDB_SUCCESS
;
459 return NSS_STATUS_SUCCESS
;
463 *h_errnop
= NO_RECOVERY
;
467 enum nss_status
_nss_resolve_gethostbyaddr2_r(
468 const void* addr
, socklen_t len
,
470 struct hostent
*result
,
471 char *buffer
, size_t buflen
,
472 int *errnop
, int *h_errnop
,
475 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
476 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
477 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
478 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
479 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
480 unsigned c
= 0, i
= 0;
485 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
493 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
494 *errnop
= EAFNOSUPPORT
;
496 return NSS_STATUS_UNAVAIL
;
499 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
501 *h_errnop
= NO_RECOVERY
;
502 return NSS_STATUS_UNAVAIL
;
505 r
= sd_bus_open_system(&bus
);
509 r
= sd_bus_message_new_method_call(
512 "org.freedesktop.resolve1",
513 "/org/freedesktop/resolve1",
514 "org.freedesktop.resolve1.Manager",
519 r
= sd_bus_message_set_auto_start(req
, false);
523 r
= sd_bus_message_append(req
, "ii", 0, af
);
527 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
531 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
535 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
537 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
539 *h_errnop
= HOST_NOT_FOUND
;
540 return NSS_STATUS_NOTFOUND
;
543 if (!bus_error_shall_fallback(&error
))
544 ret
= NSS_STATUS_NOTFOUND
;
549 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
553 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
561 ms
+= ALIGN(strlen(n
) + 1);
566 r
= sd_bus_message_rewind(reply
, false);
572 *h_errnop
= HOST_NOT_FOUND
;
573 return NSS_STATUS_NOTFOUND
;
576 ms
+= ALIGN(len
) + /* the address */
577 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
578 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
582 *h_errnop
= NETDB_INTERNAL
;
583 return NSS_STATUS_TRYAGAIN
;
586 /* First, place address */
588 memcpy(r_addr
, addr
, len
);
591 /* Second, place address list */
592 r_addr_list
= buffer
+ idx
;
593 ((char**) r_addr_list
)[0] = r_addr
;
594 ((char**) r_addr_list
)[1] = NULL
;
595 idx
+= sizeof(char*) * 2;
597 /* Third, reserve space for the aliases array */
598 r_aliases
= buffer
+ idx
;
599 idx
+= sizeof(char*) * c
;
601 /* Fourth, place aliases */
603 r_name
= buffer
+ idx
;
604 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
613 ((char**) r_aliases
)[i
-1] = p
;
621 ((char**) r_aliases
)[c
-1] = NULL
;
624 result
->h_name
= r_name
;
625 result
->h_aliases
= (char**) r_aliases
;
626 result
->h_addrtype
= af
;
627 result
->h_length
= len
;
628 result
->h_addr_list
= (char**) r_addr_list
;
633 /* Explicitly reset all error variables */
635 *h_errnop
= NETDB_SUCCESS
;
638 return NSS_STATUS_SUCCESS
;
642 *h_errnop
= NO_RECOVERY
;
646 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
647 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);