]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
10 #include "errno-util.h"
11 #include "hostname-util.h"
12 #include "local-addresses.h"
15 #include "signal-util.h"
16 #include "string-util.h"
18 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
19 * 127.0.0.1 that it can be translated back to the local hostname. For
20 * IPv6 we use ::1 which unfortunately will not translate back to the
21 * hostname but instead something like "localhost" or so. */
23 #define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
24 #define LOCALADDRESS_IPV6 &in6addr_loopback
26 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname
);
27 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname
);
29 enum nss_status
_nss_myhostname_gethostbyname4_r(
31 struct gaih_addrtuple
**pat
,
32 char *buffer
, size_t buflen
,
33 int *errnop
, int *h_errnop
,
36 struct gaih_addrtuple
*r_tuple
, *r_tuple_prev
= NULL
;
37 _cleanup_free_
struct local_address
*addresses
= NULL
;
38 _cleanup_free_
char *hn
= NULL
;
39 const char *canonical
= NULL
;
41 uint32_t local_address_ipv4
;
42 struct local_address
*a
;
48 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
56 if (is_localhost(name
)) {
57 /* We respond to 'localhost', so that /etc/hosts is optional */
59 canonical
= "localhost";
60 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
62 } else if (is_gateway_hostname(name
)) {
64 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
68 canonical
= "_gateway";
70 } else if (is_outbound_hostname(name
)) {
72 n_addresses
= local_outbounds(NULL
, 0, AF_UNSPEC
, &addresses
);
76 canonical
= "_outbound";
79 hn
= gethostname_malloc();
83 *h_errnop
= NO_RECOVERY
;
84 return NSS_STATUS_TRYAGAIN
;
87 /* We respond to our local hostname, our hostname suffixed with a single dot. */
88 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), "."))
91 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
96 local_address_ipv4
= LOCALADDRESS_IPV4
;
99 l
= strlen(canonical
);
100 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * (n_addresses
> 0 ? n_addresses
: 2);
104 *h_errnop
= NETDB_INTERNAL
;
105 return NSS_STATUS_TRYAGAIN
;
108 /* First, fill in hostname */
110 memcpy(r_name
, canonical
, l
+1);
113 assert(n_addresses
>= 0);
114 if (n_addresses
== 0) {
115 /* Second, fill in IPv6 tuple */
116 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
117 r_tuple
->next
= r_tuple_prev
;
118 r_tuple
->name
= r_name
;
119 r_tuple
->family
= AF_INET6
;
120 memcpy(r_tuple
->addr
, LOCALADDRESS_IPV6
, 16);
121 r_tuple
->scopeid
= 0;
123 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
124 r_tuple_prev
= r_tuple
;
126 /* Third, fill in IPv4 tuple */
127 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
128 r_tuple
->next
= r_tuple_prev
;
129 r_tuple
->name
= r_name
;
130 r_tuple
->family
= AF_INET
;
131 *(uint32_t*) r_tuple
->addr
= local_address_ipv4
;
132 r_tuple
->scopeid
= 0;
134 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
135 r_tuple_prev
= r_tuple
;
138 /* Fourth, fill actual addresses in, but in backwards order */
139 for (a
= addresses
+ n_addresses
- 1, n
= 0; (int) n
< n_addresses
; n
++, a
--) {
140 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
141 r_tuple
->next
= r_tuple_prev
;
142 r_tuple
->name
= r_name
;
143 r_tuple
->family
= a
->family
;
144 r_tuple
->scopeid
= a
->family
== AF_INET6
&& in6_addr_is_link_local(&a
->address
.in6
) ? a
->ifindex
: 0;
145 memcpy(r_tuple
->addr
, &a
->address
, 16);
147 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
148 r_tuple_prev
= r_tuple
;
151 /* Verify the size matches */
154 /* Nscd expects us to store the first record in **pat. */
156 **pat
= *r_tuple_prev
;
163 /* Explicitly reset both *h_errnop and h_errno to work around
164 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
165 *h_errnop
= NETDB_SUCCESS
;
168 return NSS_STATUS_SUCCESS
;
171 *h_errnop
= HOST_NOT_FOUND
;
172 return NSS_STATUS_NOTFOUND
;
175 static enum nss_status
fill_in_hostent(
176 const char *canonical
, const char *additional
,
178 struct local_address
*addresses
, unsigned n_addresses
,
179 uint32_t local_address_ipv4
,
180 struct hostent
*result
,
181 char *buffer
, size_t buflen
,
182 int *errnop
, int *h_errnop
,
186 size_t l_canonical
, l_additional
, idx
, ms
, alen
;
187 char *r_addr
, *r_name
, *r_aliases
, *r_alias
= NULL
, *r_addr_list
;
188 struct local_address
*a
;
199 alen
= FAMILY_ADDRESS_SIZE(af
);
201 for (a
= addresses
, n
= 0, c
= 0; n
< n_addresses
; a
++, n
++)
205 l_canonical
= strlen(canonical
);
206 l_additional
= strlen_ptr(additional
);
207 ms
= ALIGN(l_canonical
+1)+
208 (additional
? ALIGN(l_additional
+1) : 0) +
210 (additional
? sizeof(char*) : 0) +
211 (c
> 0 ? c
: 1) * ALIGN(alen
) +
212 (c
> 0 ? c
+1 : 2) * sizeof(char*);
217 *h_errnop
= NETDB_INTERNAL
;
218 return NSS_STATUS_TRYAGAIN
;
221 /* First, fill in hostnames */
223 memcpy(r_name
, canonical
, l_canonical
+1);
224 idx
= ALIGN(l_canonical
+1);
227 r_alias
= buffer
+ idx
;
228 memcpy(r_alias
, additional
, l_additional
+1);
229 idx
+= ALIGN(l_additional
+1);
232 /* Second, create aliases array */
233 r_aliases
= buffer
+ idx
;
235 ((char**) r_aliases
)[0] = r_alias
;
236 ((char**) r_aliases
)[1] = NULL
;
237 idx
+= 2*sizeof(char*);
239 ((char**) r_aliases
)[0] = NULL
;
240 idx
+= sizeof(char*);
243 /* Third, add addresses */
244 r_addr
= buffer
+ idx
;
248 for (a
= addresses
, n
= 0; n
< n_addresses
; a
++, n
++) {
252 memcpy(r_addr
+ i
*ALIGN(alen
), &a
->address
, alen
);
257 idx
+= c
*ALIGN(alen
);
260 *(uint32_t*) r_addr
= local_address_ipv4
;
262 memcpy(r_addr
, LOCALADDRESS_IPV6
, 16);
267 /* Fourth, add address pointer array */
268 r_addr_list
= buffer
+ idx
;
272 for (i
= 0; i
< c
; i
++)
273 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
275 ((char**) r_addr_list
)[i
] = NULL
;
276 idx
+= (c
+1) * sizeof(char*);
279 ((char**) r_addr_list
)[0] = r_addr
;
280 ((char**) r_addr_list
)[1] = NULL
;
281 idx
+= 2 * sizeof(char*);
284 /* Verify the size matches */
287 result
->h_name
= r_name
;
288 result
->h_aliases
= (char**) r_aliases
;
289 result
->h_addrtype
= af
;
290 result
->h_length
= alen
;
291 result
->h_addr_list
= (char**) r_addr_list
;
299 /* Explicitly reset both *h_errnop and h_errno to work around
300 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
301 *h_errnop
= NETDB_SUCCESS
;
304 return NSS_STATUS_SUCCESS
;
307 enum nss_status
_nss_myhostname_gethostbyname3_r(
310 struct hostent
*host
,
311 char *buffer
, size_t buflen
,
312 int *errnop
, int *h_errnop
,
316 _cleanup_free_
struct local_address
*addresses
= NULL
;
317 const char *canonical
, *additional
= NULL
;
318 _cleanup_free_
char *hn
= NULL
;
319 uint32_t local_address_ipv4
= 0;
323 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
334 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
336 *errnop
= EAFNOSUPPORT
;
338 return NSS_STATUS_UNAVAIL
;
341 if (is_localhost(name
)) {
342 canonical
= "localhost";
343 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
345 } else if (is_gateway_hostname(name
)) {
347 n_addresses
= local_gateways(NULL
, 0, af
, &addresses
);
348 if (n_addresses
<= 0)
351 canonical
= "_gateway";
353 } else if (is_outbound_hostname(name
)) {
355 n_addresses
= local_outbounds(NULL
, 0, af
, &addresses
);
356 if (n_addresses
<= 0)
359 canonical
= "_outbound";
362 hn
= gethostname_malloc();
366 *h_errnop
= NO_RECOVERY
;
367 return NSS_STATUS_TRYAGAIN
;
370 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), "."))
373 n_addresses
= local_addresses(NULL
, 0, af
, &addresses
);
378 additional
= n_addresses
<= 0 && af
== AF_INET6
? "localhost" : NULL
;
379 local_address_ipv4
= LOCALADDRESS_IPV4
;
384 return fill_in_hostent(
385 canonical
, additional
,
387 addresses
, n_addresses
,
396 *h_errnop
= HOST_NOT_FOUND
;
397 return NSS_STATUS_NOTFOUND
;
400 enum nss_status
_nss_myhostname_gethostbyaddr2_r(
401 const void* addr
, socklen_t len
,
403 struct hostent
*host
,
404 char *buffer
, size_t buflen
,
405 int *errnop
, int *h_errnop
,
408 const char *canonical
= NULL
, *additional
= NULL
;
409 uint32_t local_address_ipv4
= LOCALADDRESS_IPV4
;
410 _cleanup_free_
struct local_address
*addresses
= NULL
;
411 _cleanup_free_
char *hn
= NULL
;
413 struct local_address
*a
;
414 bool additional_from_hostname
= false;
418 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
426 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
428 *errnop
= EAFNOSUPPORT
;
430 return NSS_STATUS_UNAVAIL
;
433 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
436 *h_errnop
= NO_RECOVERY
;
437 return NSS_STATUS_UNAVAIL
;
441 if ((*(uint32_t*) addr
) == LOCALADDRESS_IPV4
)
444 if ((*(uint32_t*) addr
) == htobe32(INADDR_LOOPBACK
)) {
445 canonical
= "localhost";
446 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
451 assert(af
== AF_INET6
);
453 if (memcmp(addr
, LOCALADDRESS_IPV6
, 16) == 0) {
454 canonical
= "localhost";
455 additional_from_hostname
= true;
460 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
461 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++) {
465 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0)
469 addresses
= mfree(addresses
);
471 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
472 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++) {
476 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0) {
477 canonical
= "_gateway";
482 *h_errnop
= HOST_NOT_FOUND
;
483 return NSS_STATUS_NOTFOUND
;
486 if (!canonical
|| additional_from_hostname
) {
487 hn
= gethostname_malloc();
491 *h_errnop
= NO_RECOVERY
;
492 return NSS_STATUS_TRYAGAIN
;
502 return fill_in_hostent(
503 canonical
, additional
,
505 addresses
, n_addresses
,
514 NSS_GETHOSTBYNAME_FALLBACKS(myhostname
);
515 NSS_GETHOSTBYADDR_FALLBACKS(myhostname
);