1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "bus-common-errors.h"
30 #include "in-addr-util.h"
33 #include "string-util.h"
35 #include "signal-util.h"
37 NSS_GETHOSTBYNAME_PROTOTYPES(resolve
);
38 NSS_GETHOSTBYADDR_PROTOTYPES(resolve
);
40 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
42 static bool bus_error_shall_fallback(sd_bus_error
*e
) {
43 return sd_bus_error_has_name(e
, SD_BUS_ERROR_SERVICE_UNKNOWN
) ||
44 sd_bus_error_has_name(e
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
) ||
45 sd_bus_error_has_name(e
, SD_BUS_ERROR_NO_REPLY
) ||
46 sd_bus_error_has_name(e
, SD_BUS_ERROR_ACCESS_DENIED
);
49 static int count_addresses(sd_bus_message
*m
, int af
, const char **canonical
) {
55 r
= sd_bus_message_enter_container(m
, 'a', "(iiay)");
59 while ((r
= sd_bus_message_enter_container(m
, 'r', "iiay")) > 0) {
62 assert_cc(sizeof(int32_t) == sizeof(int));
64 r
= sd_bus_message_read(m
, "ii", &ifindex
, &family
);
68 r
= sd_bus_message_skip(m
, "ay");
72 r
= sd_bus_message_exit_container(m
);
76 if (af
!= AF_UNSPEC
&& family
!= af
)
84 r
= sd_bus_message_exit_container(m
);
88 r
= sd_bus_message_read(m
, "s", canonical
);
92 r
= sd_bus_message_rewind(m
, true);
99 static uint32_t ifindex_to_scopeid(int family
, const void *a
, int ifindex
) {
102 if (family
!= AF_INET6
)
105 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
107 assert(sizeof(in6
) == FAMILY_ADDRESS_SIZE(AF_INET6
));
108 memcpy(&in6
, a
, sizeof(struct in6_addr
));
110 return IN6_IS_ADDR_LINKLOCAL(&in6
) ? ifindex
: 0;
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_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
121 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
122 struct gaih_addrtuple
*r_tuple
, *r_tuple_first
= NULL
;
123 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
124 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
125 const char *canonical
= NULL
;
130 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
138 r
= sd_bus_open_system(&bus
);
142 r
= sd_bus_message_new_method_call(
145 "org.freedesktop.resolve1",
146 "/org/freedesktop/resolve1",
147 "org.freedesktop.resolve1.Manager",
152 r
= sd_bus_message_set_auto_start(req
, false);
156 r
= sd_bus_message_append(req
, "isit", 0, name
, AF_UNSPEC
, (uint64_t) 0);
160 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
162 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
164 *h_errnop
= HOST_NOT_FOUND
;
165 return NSS_STATUS_NOTFOUND
;
168 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
169 allowing falling back to other nss modules. Treat all other error conditions as
170 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
171 case so that the nsswitch.conf configuration can distuingish such executed but
172 negative replies from complete failure to talk to resolved). */
173 if (!bus_error_shall_fallback(&error
))
174 ret
= NSS_STATUS_NOTFOUND
;
179 c
= count_addresses(reply
, AF_UNSPEC
, &canonical
);
186 *h_errnop
= HOST_NOT_FOUND
;
187 return NSS_STATUS_NOTFOUND
;
190 if (isempty(canonical
))
193 l
= strlen(canonical
);
194 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * c
;
197 *h_errnop
= NETDB_INTERNAL
;
198 return NSS_STATUS_TRYAGAIN
;
201 /* First, append name */
203 memcpy(r_name
, canonical
, l
+1);
206 /* Second, append addresses */
207 r_tuple_first
= (struct gaih_addrtuple
*) (buffer
+ idx
);
209 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
213 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
218 assert_cc(sizeof(int32_t) == sizeof(int));
220 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
229 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
233 r
= sd_bus_message_exit_container(reply
);
237 if (!IN_SET(family
, AF_INET
, AF_INET6
))
240 if (sz
!= FAMILY_ADDRESS_SIZE(family
)) {
245 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
246 r_tuple
->next
= i
== c
-1 ? NULL
: (struct gaih_addrtuple
*) ((char*) r_tuple
+ ALIGN(sizeof(struct gaih_addrtuple
)));
247 r_tuple
->name
= r_name
;
248 r_tuple
->family
= family
;
249 r_tuple
->scopeid
= ifindex_to_scopeid(family
, a
, ifindex
);
250 memcpy(r_tuple
->addr
, a
, sz
);
252 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
262 **pat
= *r_tuple_first
;
264 *pat
= r_tuple_first
;
269 /* Explicitly reset all error variables */
271 *h_errnop
= NETDB_SUCCESS
;
274 return NSS_STATUS_SUCCESS
;
278 *h_errnop
= NO_RECOVERY
;
282 enum nss_status
_nss_resolve_gethostbyname3_r(
285 struct hostent
*result
,
286 char *buffer
, size_t buflen
,
287 int *errnop
, int *h_errnop
,
291 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
292 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
293 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
294 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
295 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
296 size_t l
, idx
, ms
, alen
;
297 const char *canonical
;
300 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
311 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
316 r
= sd_bus_open_system(&bus
);
320 r
= sd_bus_message_new_method_call(
323 "org.freedesktop.resolve1",
324 "/org/freedesktop/resolve1",
325 "org.freedesktop.resolve1.Manager",
330 r
= sd_bus_message_set_auto_start(req
, false);
334 r
= sd_bus_message_append(req
, "isit", 0, name
, af
, (uint64_t) 0);
338 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
340 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
342 *h_errnop
= HOST_NOT_FOUND
;
343 return NSS_STATUS_NOTFOUND
;
346 if (!bus_error_shall_fallback(&error
))
347 ret
= NSS_STATUS_NOTFOUND
;
352 c
= count_addresses(reply
, af
, &canonical
);
359 *h_errnop
= HOST_NOT_FOUND
;
360 return NSS_STATUS_NOTFOUND
;
363 if (isempty(canonical
))
366 alen
= FAMILY_ADDRESS_SIZE(af
);
367 l
= strlen(canonical
);
369 ms
= ALIGN(l
+1) + c
* ALIGN(alen
) + (c
+2) * sizeof(char*);
373 *h_errnop
= NETDB_INTERNAL
;
374 return NSS_STATUS_TRYAGAIN
;
377 /* First, append name */
379 memcpy(r_name
, canonical
, l
+1);
382 /* Second, create empty aliases array */
383 r_aliases
= buffer
+ idx
;
384 ((char**) r_aliases
)[0] = NULL
;
385 idx
+= sizeof(char*);
387 /* Third, append addresses */
388 r_addr
= buffer
+ idx
;
390 r
= sd_bus_message_enter_container(reply
, 'a', "(iiay)");
394 while ((r
= sd_bus_message_enter_container(reply
, 'r', "iiay")) > 0) {
399 r
= sd_bus_message_read(reply
, "ii", &ifindex
, &family
);
408 r
= sd_bus_message_read_array(reply
, 'y', &a
, &sz
);
412 r
= sd_bus_message_exit_container(reply
);
424 memcpy(r_addr
+ i
*ALIGN(alen
), a
, alen
);
431 idx
+= c
* ALIGN(alen
);
433 /* Fourth, append address pointer array */
434 r_addr_list
= buffer
+ idx
;
435 for (i
= 0; i
< c
; i
++)
436 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
438 ((char**) r_addr_list
)[i
] = NULL
;
439 idx
+= (c
+1) * sizeof(char*);
443 result
->h_name
= r_name
;
444 result
->h_aliases
= (char**) r_aliases
;
445 result
->h_addrtype
= af
;
446 result
->h_length
= alen
;
447 result
->h_addr_list
= (char**) r_addr_list
;
449 /* Explicitly reset all error variables */
451 *h_errnop
= NETDB_SUCCESS
;
460 return NSS_STATUS_SUCCESS
;
464 *h_errnop
= NO_RECOVERY
;
468 enum nss_status
_nss_resolve_gethostbyaddr2_r(
469 const void* addr
, socklen_t len
,
471 struct hostent
*result
,
472 char *buffer
, size_t buflen
,
473 int *errnop
, int *h_errnop
,
476 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*req
= NULL
, *reply
= NULL
;
477 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
478 char *r_name
, *r_aliases
, *r_addr
, *r_addr_list
;
479 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
480 enum nss_status ret
= NSS_STATUS_UNAVAIL
;
481 unsigned c
= 0, i
= 0;
486 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
494 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
495 *errnop
= EAFNOSUPPORT
;
497 return NSS_STATUS_UNAVAIL
;
500 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
502 *h_errnop
= NO_RECOVERY
;
503 return NSS_STATUS_UNAVAIL
;
506 r
= sd_bus_open_system(&bus
);
510 r
= sd_bus_message_new_method_call(
513 "org.freedesktop.resolve1",
514 "/org/freedesktop/resolve1",
515 "org.freedesktop.resolve1.Manager",
520 r
= sd_bus_message_set_auto_start(req
, false);
524 r
= sd_bus_message_append(req
, "ii", 0, af
);
528 r
= sd_bus_message_append_array(req
, 'y', addr
, len
);
532 r
= sd_bus_message_append(req
, "t", (uint64_t) 0);
536 r
= sd_bus_call(bus
, req
, DNS_CALL_TIMEOUT_USEC
, &error
, &reply
);
538 if (sd_bus_error_has_name(&error
, _BUS_ERROR_DNS
"NXDOMAIN")) {
540 *h_errnop
= HOST_NOT_FOUND
;
541 return NSS_STATUS_NOTFOUND
;
544 if (!bus_error_shall_fallback(&error
))
545 ret
= NSS_STATUS_NOTFOUND
;
550 r
= sd_bus_message_enter_container(reply
, 'a', "(is)");
554 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
562 ms
+= ALIGN(strlen(n
) + 1);
567 r
= sd_bus_message_rewind(reply
, false);
573 *h_errnop
= HOST_NOT_FOUND
;
574 return NSS_STATUS_NOTFOUND
;
577 ms
+= ALIGN(len
) + /* the address */
578 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
579 c
* sizeof(char*); /* pointers to aliases, plus trailing NULL */
583 *h_errnop
= NETDB_INTERNAL
;
584 return NSS_STATUS_TRYAGAIN
;
587 /* First, place address */
589 memcpy(r_addr
, addr
, len
);
592 /* Second, place address list */
593 r_addr_list
= buffer
+ idx
;
594 ((char**) r_addr_list
)[0] = r_addr
;
595 ((char**) r_addr_list
)[1] = NULL
;
596 idx
+= sizeof(char*) * 2;
598 /* Third, reserve space for the aliases array */
599 r_aliases
= buffer
+ idx
;
600 idx
+= sizeof(char*) * c
;
602 /* Fourth, place aliases */
604 r_name
= buffer
+ idx
;
605 while ((r
= sd_bus_message_read(reply
, "(is)", &ifindex
, &n
)) > 0) {
614 ((char**) r_aliases
)[i
-1] = p
;
622 ((char**) r_aliases
)[c
-1] = NULL
;
625 result
->h_name
= r_name
;
626 result
->h_aliases
= (char**) r_aliases
;
627 result
->h_addrtype
= af
;
628 result
->h_length
= len
;
629 result
->h_addr_list
= (char**) r_addr_list
;
634 /* Explicitly reset all error variables */
636 *h_errnop
= NETDB_SUCCESS
;
639 return NSS_STATUS_SUCCESS
;
643 *h_errnop
= NO_RECOVERY
;
647 NSS_GETHOSTBYNAME_FALLBACKS(resolve
);
648 NSS_GETHOSTBYADDR_FALLBACKS(resolve
);