]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
5abc0c91bf29a5f568bcc4d8741af00428326f54
1 /* SPDX-License-Identifier: LGPL-2.1+ */
10 #include "alloc-util.h"
11 #include "hostname-util.h"
12 #include "local-addresses.h"
15 #include "signal-util.h"
16 #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
;
43 struct local_address
*a
;
49 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
57 if (is_localhost(name
)) {
58 /* We respond to 'localhost', so that /etc/hosts
61 canonical
= "localhost";
62 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
64 } else if (is_gateway_hostname(name
)) {
66 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
67 if (n_addresses
<= 0) {
68 *h_errnop
= HOST_NOT_FOUND
;
69 return NSS_STATUS_NOTFOUND
;
72 canonical
= "_gateway";
75 hn
= gethostname_malloc();
78 *h_errnop
= NO_RECOVERY
;
79 return NSS_STATUS_TRYAGAIN
;
82 /* We respond to our local host name, our hostname suffixed with a single dot. */
83 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), ".")) {
84 *h_errnop
= HOST_NOT_FOUND
;
85 return NSS_STATUS_NOTFOUND
;
88 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
93 local_address_ipv4
= LOCALADDRESS_IPV4
;
96 l
= strlen(canonical
);
97 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * (n_addresses
> 0 ? n_addresses
: 2);
100 *h_errnop
= NETDB_INTERNAL
;
101 return NSS_STATUS_TRYAGAIN
;
104 /* First, fill in hostname */
106 memcpy(r_name
, canonical
, l
+1);
109 assert(n_addresses
>= 0);
110 if (n_addresses
== 0) {
111 /* Second, fill in IPv6 tuple */
112 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
113 r_tuple
->next
= r_tuple_prev
;
114 r_tuple
->name
= r_name
;
115 r_tuple
->family
= AF_INET6
;
116 memcpy(r_tuple
->addr
, LOCALADDRESS_IPV6
, 16);
117 r_tuple
->scopeid
= 0;
119 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
120 r_tuple_prev
= r_tuple
;
122 /* Third, fill in IPv4 tuple */
123 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
124 r_tuple
->next
= r_tuple_prev
;
125 r_tuple
->name
= r_name
;
126 r_tuple
->family
= AF_INET
;
127 *(uint32_t*) r_tuple
->addr
= local_address_ipv4
;
128 r_tuple
->scopeid
= 0;
130 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
131 r_tuple_prev
= r_tuple
;
134 /* Fourth, fill actual addresses in, but in backwards order */
135 for (a
= addresses
+ n_addresses
- 1, n
= 0; (int) n
< n_addresses
; n
++, a
--) {
136 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
137 r_tuple
->next
= r_tuple_prev
;
138 r_tuple
->name
= r_name
;
139 r_tuple
->family
= a
->family
;
140 r_tuple
->scopeid
= a
->family
== AF_INET6
&& IN6_IS_ADDR_LINKLOCAL(&a
->address
.in6
) ? a
->ifindex
: 0;
141 memcpy(r_tuple
->addr
, &a
->address
, 16);
143 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
144 r_tuple_prev
= r_tuple
;
147 /* Verify the size matches */
150 /* Nscd expects us to store the first record in **pat. */
152 **pat
= *r_tuple_prev
;
159 /* Explicitly reset both *h_errnop and h_errno to work around
160 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
161 *h_errnop
= NETDB_SUCCESS
;
164 return NSS_STATUS_SUCCESS
;
167 static enum nss_status
fill_in_hostent(
168 const char *canonical
, const char *additional
,
170 struct local_address
*addresses
, unsigned n_addresses
,
171 uint32_t local_address_ipv4
,
172 struct hostent
*result
,
173 char *buffer
, size_t buflen
,
174 int *errnop
, int *h_errnop
,
178 size_t l_canonical
, l_additional
, idx
, ms
, alen
;
179 char *r_addr
, *r_name
, *r_aliases
, *r_alias
= NULL
, *r_addr_list
;
180 struct local_address
*a
;
189 alen
= FAMILY_ADDRESS_SIZE(af
);
191 for (a
= addresses
, n
= 0, c
= 0; n
< n_addresses
; a
++, n
++)
195 l_canonical
= strlen(canonical
);
196 l_additional
= strlen_ptr(additional
);
197 ms
= ALIGN(l_canonical
+1)+
198 (additional
? ALIGN(l_additional
+1) : 0) +
200 (additional
? sizeof(char*) : 0) +
201 (c
> 0 ? c
: 1) * ALIGN(alen
) +
202 (c
> 0 ? c
+1 : 2) * sizeof(char*);
206 *h_errnop
= NETDB_INTERNAL
;
207 return NSS_STATUS_TRYAGAIN
;
210 /* First, fill in hostnames */
212 memcpy(r_name
, canonical
, l_canonical
+1);
213 idx
= ALIGN(l_canonical
+1);
216 r_alias
= buffer
+ idx
;
217 memcpy(r_alias
, additional
, l_additional
+1);
218 idx
+= ALIGN(l_additional
+1);
221 /* Second, create aliases array */
222 r_aliases
= buffer
+ idx
;
224 ((char**) r_aliases
)[0] = r_alias
;
225 ((char**) r_aliases
)[1] = NULL
;
226 idx
+= 2*sizeof(char*);
228 ((char**) r_aliases
)[0] = NULL
;
229 idx
+= sizeof(char*);
232 /* Third, add addresses */
233 r_addr
= buffer
+ idx
;
237 for (a
= addresses
, n
= 0; n
< n_addresses
; a
++, n
++) {
241 memcpy(r_addr
+ i
*ALIGN(alen
), &a
->address
, alen
);
246 idx
+= c
*ALIGN(alen
);
249 *(uint32_t*) r_addr
= local_address_ipv4
;
251 memcpy(r_addr
, LOCALADDRESS_IPV6
, 16);
256 /* Fourth, add address pointer array */
257 r_addr_list
= buffer
+ idx
;
261 for (i
= 0; i
< c
; i
++)
262 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
264 ((char**) r_addr_list
)[i
] = NULL
;
265 idx
+= (c
+1) * sizeof(char*);
268 ((char**) r_addr_list
)[0] = r_addr
;
269 ((char**) r_addr_list
)[1] = NULL
;
270 idx
+= 2 * sizeof(char*);
273 /* Verify the size matches */
276 result
->h_name
= r_name
;
277 result
->h_aliases
= (char**) r_aliases
;
278 result
->h_addrtype
= af
;
279 result
->h_length
= alen
;
280 result
->h_addr_list
= (char**) r_addr_list
;
288 /* Explicitly reset both *h_errnop and h_errno to work around
289 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
290 *h_errnop
= NETDB_SUCCESS
;
293 return NSS_STATUS_SUCCESS
;
296 enum nss_status
_nss_myhostname_gethostbyname3_r(
299 struct hostent
*host
,
300 char *buffer
, size_t buflen
,
301 int *errnop
, int *h_errnop
,
305 _cleanup_free_
struct local_address
*addresses
= NULL
;
306 const char *canonical
, *additional
= NULL
;
307 _cleanup_free_
char *hn
= NULL
;
308 uint32_t local_address_ipv4
= 0;
312 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
323 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
324 *errnop
= EAFNOSUPPORT
;
326 return NSS_STATUS_UNAVAIL
;
329 if (is_localhost(name
)) {
330 canonical
= "localhost";
331 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
333 } else if (is_gateway_hostname(name
)) {
335 n_addresses
= local_gateways(NULL
, 0, af
, &addresses
);
336 if (n_addresses
<= 0) {
337 *h_errnop
= HOST_NOT_FOUND
;
338 return NSS_STATUS_NOTFOUND
;
341 canonical
= "_gateway";
344 hn
= gethostname_malloc();
347 *h_errnop
= NO_RECOVERY
;
348 return NSS_STATUS_TRYAGAIN
;
351 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), ".")) {
352 *h_errnop
= HOST_NOT_FOUND
;
353 return NSS_STATUS_NOTFOUND
;
356 n_addresses
= local_addresses(NULL
, 0, af
, &addresses
);
361 additional
= n_addresses
<= 0 && af
== AF_INET6
? "localhost" : NULL
;
362 local_address_ipv4
= LOCALADDRESS_IPV4
;
365 return fill_in_hostent(
366 canonical
, additional
,
368 addresses
, n_addresses
,
377 enum nss_status
_nss_myhostname_gethostbyaddr2_r(
378 const void* addr
, socklen_t len
,
380 struct hostent
*host
,
381 char *buffer
, size_t buflen
,
382 int *errnop
, int *h_errnop
,
385 const char *canonical
= NULL
, *additional
= NULL
;
386 uint32_t local_address_ipv4
= LOCALADDRESS_IPV4
;
387 _cleanup_free_
struct local_address
*addresses
= NULL
;
388 _cleanup_free_
char *hn
= NULL
;
390 struct local_address
*a
;
391 bool additional_from_hostname
= false;
395 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
403 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
404 *errnop
= EAFNOSUPPORT
;
406 return NSS_STATUS_UNAVAIL
;
409 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
411 *h_errnop
= NO_RECOVERY
;
412 return NSS_STATUS_UNAVAIL
;
416 if ((*(uint32_t*) addr
) == LOCALADDRESS_IPV4
)
419 if ((*(uint32_t*) addr
) == htobe32(INADDR_LOOPBACK
)) {
420 canonical
= "localhost";
421 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
426 assert(af
== AF_INET6
);
428 if (memcmp(addr
, LOCALADDRESS_IPV6
, 16) == 0) {
429 canonical
= "localhost";
430 additional_from_hostname
= true;
435 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
436 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++) {
440 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0)
444 addresses
= mfree(addresses
);
446 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
447 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++) {
451 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0) {
452 canonical
= "_gateway";
457 *h_errnop
= HOST_NOT_FOUND
;
458 return NSS_STATUS_NOTFOUND
;
461 if (!canonical
|| additional_from_hostname
) {
462 hn
= gethostname_malloc();
465 *h_errnop
= NO_RECOVERY
;
466 return NSS_STATUS_TRYAGAIN
;
475 return fill_in_hostent(
476 canonical
, additional
,
478 addresses
, n_addresses
,
487 NSS_GETHOSTBYNAME_FALLBACKS(myhostname
);
488 NSS_GETHOSTBYADDR_FALLBACKS(myhostname
);