]>
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 "socket-util.h"
17 #include "string-util.h"
19 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
20 * 127.0.0.1 that it can be translated back to the local hostname. For
21 * IPv6 we use ::1 which unfortunately will not translate back to the
22 * hostname but instead something like "localhost" or so. */
24 #define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
25 #define LOCALADDRESS_IPV6 &in6addr_loopback
27 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname
);
28 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname
);
30 enum nss_status
_nss_myhostname_gethostbyname4_r(
32 struct gaih_addrtuple
**pat
,
33 char *buffer
, size_t buflen
,
34 int *errnop
, int *h_errnop
,
37 struct gaih_addrtuple
*r_tuple
, *r_tuple_prev
= NULL
;
38 _cleanup_free_
struct local_address
*addresses
= NULL
;
39 _cleanup_free_
char *hn
= NULL
;
40 const char *canonical
= NULL
;
42 uint32_t local_address_ipv4
;
47 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
55 if (is_localhost(name
)) {
56 /* We respond to 'localhost', so that /etc/hosts is optional */
58 canonical
= "localhost";
59 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
61 } else if (is_gateway_hostname(name
)) {
63 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
67 canonical
= "_gateway";
69 } else if (is_outbound_hostname(name
)) {
71 n_addresses
= local_outbounds(NULL
, 0, AF_UNSPEC
, &addresses
);
75 canonical
= "_outbound";
78 hn
= gethostname_malloc();
82 *h_errnop
= NO_RECOVERY
;
83 return NSS_STATUS_TRYAGAIN
;
86 /* We respond to our local hostname, our hostname suffixed with a single dot. */
87 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), "."))
90 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
95 local_address_ipv4
= LOCALADDRESS_IPV4
;
98 l
= strlen(canonical
);
99 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * (n_addresses
> 0 ? n_addresses
: 1 + socket_ipv6_is_enabled());
103 *h_errnop
= NETDB_INTERNAL
;
104 return NSS_STATUS_TRYAGAIN
;
107 /* First, fill in hostname */
109 memcpy(r_name
, canonical
, l
+1);
112 assert(n_addresses
>= 0);
113 if (n_addresses
== 0) {
114 /* Second, fill in IPv6 tuple */
115 if (socket_ipv6_is_enabled()) {
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
;
127 /* Third, fill in IPv4 tuple */
128 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
129 r_tuple
->next
= r_tuple_prev
;
130 r_tuple
->name
= r_name
;
131 r_tuple
->family
= AF_INET
;
132 *(uint32_t*) r_tuple
->addr
= local_address_ipv4
;
133 r_tuple
->scopeid
= 0;
135 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
136 r_tuple_prev
= r_tuple
;
139 /* Fourth, fill actual addresses in, but in backwards order */
140 for (int i
= n_addresses
; i
> 0; i
--) {
141 struct local_address
*a
= addresses
+ i
- 1;
143 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
144 r_tuple
->next
= r_tuple_prev
;
145 r_tuple
->name
= r_name
;
146 r_tuple
->family
= a
->family
;
147 r_tuple
->scopeid
= a
->family
== AF_INET6
&& in6_addr_is_link_local(&a
->address
.in6
) ? a
->ifindex
: 0;
148 memcpy(r_tuple
->addr
, &a
->address
, 16);
150 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
151 r_tuple_prev
= r_tuple
;
154 /* Verify the size matches */
157 /* Nscd expects us to store the first record in **pat. */
159 **pat
= *r_tuple_prev
;
166 /* Explicitly reset both *h_errnop and h_errno to work around
167 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
168 *h_errnop
= NETDB_SUCCESS
;
171 return NSS_STATUS_SUCCESS
;
174 *h_errnop
= HOST_NOT_FOUND
;
175 return NSS_STATUS_NOTFOUND
;
178 static enum nss_status
fill_in_hostent(
179 const char *canonical
, const char *additional
,
181 struct local_address
*addresses
, unsigned n_addresses
,
182 uint32_t local_address_ipv4
,
183 struct hostent
*result
,
184 char *buffer
, size_t buflen
,
185 int *errnop
, int *h_errnop
,
189 size_t l_canonical
, l_additional
, idx
, ms
, alen
;
190 char *r_addr
, *r_name
, *r_aliases
, *r_alias
= NULL
, *r_addr_list
;
191 struct local_address
*a
;
195 assert(IN_SET(af
, AF_INET
, AF_INET6
));
203 alen
= FAMILY_ADDRESS_SIZE(af
);
205 for (a
= addresses
, n
= 0, c
= 0; n
< n_addresses
; a
++, n
++)
209 l_canonical
= strlen(canonical
);
210 l_additional
= strlen_ptr(additional
);
211 ms
= ALIGN(l_canonical
+1)+
212 (additional
? ALIGN(l_additional
+1) : 0) +
214 (additional
? sizeof(char*) : 0) +
215 (c
> 0 ? c
: af
== AF_INET
? 1 : socket_ipv6_is_enabled()) * ALIGN(alen
) +
216 (c
> 0 ? c
+1 : af
== AF_INET
? 2 : (unsigned) socket_ipv6_is_enabled() + 1) * sizeof(char*);
221 *h_errnop
= NETDB_INTERNAL
;
222 return NSS_STATUS_TRYAGAIN
;
225 /* First, fill in hostnames */
227 memcpy(r_name
, canonical
, l_canonical
+1);
228 idx
= ALIGN(l_canonical
+1);
231 r_alias
= buffer
+ idx
;
232 memcpy(r_alias
, additional
, l_additional
+1);
233 idx
+= ALIGN(l_additional
+1);
236 /* Second, create aliases array */
237 r_aliases
= buffer
+ idx
;
239 ((char**) r_aliases
)[0] = r_alias
;
240 ((char**) r_aliases
)[1] = NULL
;
241 idx
+= 2*sizeof(char*);
243 ((char**) r_aliases
)[0] = NULL
;
244 idx
+= sizeof(char*);
247 /* Third, add addresses */
248 r_addr
= buffer
+ idx
;
252 for (a
= addresses
, n
= 0; n
< n_addresses
; a
++, n
++) {
256 memcpy(r_addr
+ i
*ALIGN(alen
), &a
->address
, alen
);
261 idx
+= c
*ALIGN(alen
);
263 } else if (af
== AF_INET
) {
264 *(uint32_t*) r_addr
= local_address_ipv4
;
266 } else if (socket_ipv6_is_enabled()) {
267 memcpy(r_addr
, LOCALADDRESS_IPV6
, 16);
271 /* Fourth, add address pointer array */
272 r_addr_list
= buffer
+ idx
;
276 for (i
= 0; i
< c
; i
++)
277 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
279 ((char**) r_addr_list
)[i
] = NULL
;
280 idx
+= (c
+1) * sizeof(char*);
282 } else if (af
== AF_INET
|| socket_ipv6_is_enabled()) {
283 ((char**) r_addr_list
)[0] = r_addr
;
284 ((char**) r_addr_list
)[1] = NULL
;
285 idx
+= 2 * sizeof(char*);
287 ((char**) r_addr_list
)[0] = NULL
;
288 idx
+= sizeof(char*);
291 /* Verify the size matches */
294 result
->h_name
= r_name
;
295 result
->h_aliases
= (char**) r_aliases
;
296 result
->h_addrtype
= af
;
297 result
->h_length
= alen
;
298 result
->h_addr_list
= (char**) r_addr_list
;
306 /* Explicitly reset both *h_errnop and h_errno to work around
307 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
308 *h_errnop
= NETDB_SUCCESS
;
311 return NSS_STATUS_SUCCESS
;
314 enum nss_status
_nss_myhostname_gethostbyname3_r(
317 struct hostent
*host
,
318 char *buffer
, size_t buflen
,
319 int *errnop
, int *h_errnop
,
323 _cleanup_free_
struct local_address
*addresses
= NULL
;
324 const char *canonical
, *additional
= NULL
;
325 _cleanup_free_
char *hn
= NULL
;
326 uint32_t local_address_ipv4
= 0;
330 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
341 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
343 *errnop
= EAFNOSUPPORT
;
345 return NSS_STATUS_UNAVAIL
;
348 if (is_localhost(name
)) {
349 if (af
== AF_INET6
&& !socket_ipv6_is_enabled())
352 canonical
= "localhost";
353 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
355 } else if (is_gateway_hostname(name
)) {
357 n_addresses
= local_gateways(NULL
, 0, af
, &addresses
);
358 if (n_addresses
<= 0)
361 canonical
= "_gateway";
363 } else if (is_outbound_hostname(name
)) {
365 n_addresses
= local_outbounds(NULL
, 0, af
, &addresses
);
366 if (n_addresses
<= 0)
369 canonical
= "_outbound";
372 hn
= gethostname_malloc();
376 *h_errnop
= NO_RECOVERY
;
377 return NSS_STATUS_TRYAGAIN
;
380 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), "."))
383 n_addresses
= local_addresses(NULL
, 0, af
, &addresses
);
388 additional
= n_addresses
<= 0 && af
== AF_INET6
? "localhost" : NULL
;
389 local_address_ipv4
= LOCALADDRESS_IPV4
;
394 return fill_in_hostent(
395 canonical
, additional
,
397 addresses
, n_addresses
,
406 *h_errnop
= HOST_NOT_FOUND
;
407 return NSS_STATUS_NOTFOUND
;
410 enum nss_status
_nss_myhostname_gethostbyaddr2_r(
411 const void* addr
, socklen_t len
,
413 struct hostent
*host
,
414 char *buffer
, size_t buflen
,
415 int *errnop
, int *h_errnop
,
418 const char *canonical
= NULL
, *additional
= NULL
;
419 uint32_t local_address_ipv4
= LOCALADDRESS_IPV4
;
420 _cleanup_free_
struct local_address
*addresses
= NULL
;
421 _cleanup_free_
char *hn
= NULL
;
423 struct local_address
*a
;
424 bool additional_from_hostname
= false;
428 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
436 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
438 *errnop
= EAFNOSUPPORT
;
440 return NSS_STATUS_UNAVAIL
;
443 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
446 *h_errnop
= NO_RECOVERY
;
447 return NSS_STATUS_UNAVAIL
;
451 if ((*(uint32_t*) addr
) == LOCALADDRESS_IPV4
)
454 if ((*(uint32_t*) addr
) == htobe32(INADDR_LOOPBACK
)) {
455 canonical
= "localhost";
456 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
461 assert(af
== AF_INET6
);
463 if (socket_ipv6_is_enabled())
466 if (memcmp(addr
, LOCALADDRESS_IPV6
, 16) == 0) {
467 canonical
= "localhost";
468 additional_from_hostname
= true;
473 n_addresses
= local_addresses(NULL
, 0, af
, &addresses
);
474 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++)
475 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0)
478 addresses
= mfree(addresses
);
480 n_addresses
= local_gateways(NULL
, 0, af
, &addresses
);
481 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++)
482 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0) {
483 canonical
= "_gateway";
488 *h_errnop
= HOST_NOT_FOUND
;
489 return NSS_STATUS_NOTFOUND
;
492 if (!canonical
|| additional_from_hostname
) {
493 hn
= gethostname_malloc();
497 *h_errnop
= NO_RECOVERY
;
498 return NSS_STATUS_TRYAGAIN
;
508 return fill_in_hostent(
509 canonical
, additional
,
511 addresses
, n_addresses
,
520 NSS_GETHOSTBYNAME_FALLBACKS(myhostname
);
521 NSS_GETHOSTBYADDR_FALLBACKS(myhostname
);