]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2008-2011 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/>.
28 #include "alloc-util.h"
29 #include "hostname-util.h"
30 #include "local-addresses.h"
33 #include "signal-util.h"
34 #include "string-util.h"
37 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
38 * 127.0.0.1 that it can be translated back to the local hostname. For
39 * IPv6 we use ::1 which unfortunately will not translate back to the
40 * hostname but instead something like "localhost" or so. */
42 #define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
43 #define LOCALADDRESS_IPV6 &in6addr_loopback
45 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname
);
46 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname
);
48 enum nss_status
_nss_myhostname_gethostbyname4_r(
50 struct gaih_addrtuple
**pat
,
51 char *buffer
, size_t buflen
,
52 int *errnop
, int *h_errnop
,
55 struct gaih_addrtuple
*r_tuple
, *r_tuple_prev
= NULL
;
56 _cleanup_free_
struct local_address
*addresses
= NULL
;
57 _cleanup_free_
char *hn
= NULL
;
58 const char *canonical
= NULL
;
60 uint32_t local_address_ipv4
;
61 struct local_address
*a
;
66 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
74 if (is_localhost(name
)) {
75 /* We respond to 'localhost', so that /etc/hosts
78 canonical
= "localhost";
79 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
81 } else if (is_gateway_hostname(name
)) {
83 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
84 if (n_addresses
<= 0) {
86 *h_errnop
= HOST_NOT_FOUND
;
87 return NSS_STATUS_NOTFOUND
;
90 canonical
= "_gateway";
93 hn
= gethostname_malloc();
96 *h_errnop
= NO_RECOVERY
;
97 return NSS_STATUS_TRYAGAIN
;
100 /* We respond to our local host name, our hostname suffixed with a single dot. */
101 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), ".")) {
103 *h_errnop
= HOST_NOT_FOUND
;
104 return NSS_STATUS_NOTFOUND
;
107 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
112 local_address_ipv4
= LOCALADDRESS_IPV4
;
115 l
= strlen(canonical
);
116 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * (n_addresses
> 0 ? n_addresses
: 2);
119 *h_errnop
= NETDB_INTERNAL
;
120 return NSS_STATUS_TRYAGAIN
;
123 /* First, fill in hostname */
125 memcpy(r_name
, canonical
, l
+1);
128 assert(n_addresses
>= 0);
129 if (n_addresses
== 0) {
130 /* Second, fill in IPv6 tuple */
131 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
132 r_tuple
->next
= r_tuple_prev
;
133 r_tuple
->name
= r_name
;
134 r_tuple
->family
= AF_INET6
;
135 memcpy(r_tuple
->addr
, LOCALADDRESS_IPV6
, 16);
136 r_tuple
->scopeid
= 0;
138 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
139 r_tuple_prev
= r_tuple
;
141 /* Third, fill in IPv4 tuple */
142 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
143 r_tuple
->next
= r_tuple_prev
;
144 r_tuple
->name
= r_name
;
145 r_tuple
->family
= AF_INET
;
146 *(uint32_t*) r_tuple
->addr
= local_address_ipv4
;
147 r_tuple
->scopeid
= 0;
149 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
150 r_tuple_prev
= r_tuple
;
153 /* Fourth, fill actual addresses in, but in backwards order */
154 for (a
= addresses
+ n_addresses
- 1, n
= 0; (int) n
< n_addresses
; n
++, a
--) {
155 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
156 r_tuple
->next
= r_tuple_prev
;
157 r_tuple
->name
= r_name
;
158 r_tuple
->family
= a
->family
;
159 r_tuple
->scopeid
= a
->family
== AF_INET6
&& IN6_IS_ADDR_LINKLOCAL(&a
->address
.in6
) ? a
->ifindex
: 0;
160 memcpy(r_tuple
->addr
, &a
->address
, 16);
162 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
163 r_tuple_prev
= r_tuple
;
166 /* Verify the size matches */
169 /* Nscd expects us to store the first record in **pat. */
171 **pat
= *r_tuple_prev
;
178 /* Explicitly reset all error variables */
180 *h_errnop
= NETDB_SUCCESS
;
183 return NSS_STATUS_SUCCESS
;
186 static enum nss_status
fill_in_hostent(
187 const char *canonical
, const char *additional
,
189 struct local_address
*addresses
, unsigned n_addresses
,
190 uint32_t local_address_ipv4
,
191 struct hostent
*result
,
192 char *buffer
, size_t buflen
,
193 int *errnop
, int *h_errnop
,
197 size_t l_canonical
, l_additional
, idx
, ms
, alen
;
198 char *r_addr
, *r_name
, *r_aliases
, *r_alias
= NULL
, *r_addr_list
;
199 struct local_address
*a
;
208 alen
= FAMILY_ADDRESS_SIZE(af
);
210 for (a
= addresses
, n
= 0, c
= 0; n
< n_addresses
; a
++, n
++)
214 l_canonical
= strlen(canonical
);
215 l_additional
= strlen_ptr(additional
);
216 ms
= ALIGN(l_canonical
+1)+
217 (additional
? ALIGN(l_additional
+1) : 0) +
219 (additional
? sizeof(char*) : 0) +
220 (c
> 0 ? c
: 1) * ALIGN(alen
) +
221 (c
> 0 ? c
+1 : 2) * sizeof(char*);
225 *h_errnop
= NETDB_INTERNAL
;
226 return NSS_STATUS_TRYAGAIN
;
229 /* First, fill in hostnames */
231 memcpy(r_name
, canonical
, l_canonical
+1);
232 idx
= ALIGN(l_canonical
+1);
235 r_alias
= buffer
+ idx
;
236 memcpy(r_alias
, additional
, l_additional
+1);
237 idx
+= ALIGN(l_additional
+1);
240 /* Second, create aliases array */
241 r_aliases
= buffer
+ idx
;
243 ((char**) r_aliases
)[0] = r_alias
;
244 ((char**) r_aliases
)[1] = NULL
;
245 idx
+= 2*sizeof(char*);
247 ((char**) r_aliases
)[0] = NULL
;
248 idx
+= sizeof(char*);
251 /* Third, add addresses */
252 r_addr
= buffer
+ idx
;
256 for (a
= addresses
, n
= 0; n
< n_addresses
; a
++, n
++) {
260 memcpy(r_addr
+ i
*ALIGN(alen
), &a
->address
, alen
);
265 idx
+= c
*ALIGN(alen
);
268 *(uint32_t*) r_addr
= local_address_ipv4
;
270 memcpy(r_addr
, LOCALADDRESS_IPV6
, 16);
275 /* Fourth, add address pointer array */
276 r_addr_list
= buffer
+ idx
;
280 for (i
= 0; i
< c
; i
++)
281 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
283 ((char**) r_addr_list
)[i
] = NULL
;
284 idx
+= (c
+1) * sizeof(char*);
287 ((char**) r_addr_list
)[0] = r_addr
;
288 ((char**) r_addr_list
)[1] = NULL
;
289 idx
+= 2 * sizeof(char*);
292 /* Verify the size matches */
295 result
->h_name
= r_name
;
296 result
->h_aliases
= (char**) r_aliases
;
297 result
->h_addrtype
= af
;
298 result
->h_length
= alen
;
299 result
->h_addr_list
= (char**) r_addr_list
;
307 /* Explicitly reset all error variables */
309 *h_errnop
= NETDB_SUCCESS
;
312 return NSS_STATUS_SUCCESS
;
315 enum nss_status
_nss_myhostname_gethostbyname3_r(
318 struct hostent
*host
,
319 char *buffer
, size_t buflen
,
320 int *errnop
, int *h_errnop
,
324 _cleanup_free_
struct local_address
*addresses
= NULL
;
325 const char *canonical
, *additional
= NULL
;
326 _cleanup_free_
char *hn
= NULL
;
327 uint32_t local_address_ipv4
= 0;
330 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
341 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
342 *errnop
= EAFNOSUPPORT
;
344 return NSS_STATUS_UNAVAIL
;
347 if (is_localhost(name
)) {
348 canonical
= "localhost";
349 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
351 } else if (is_gateway_hostname(name
)) {
353 n_addresses
= local_gateways(NULL
, 0, af
, &addresses
);
354 if (n_addresses
<= 0) {
356 *h_errnop
= HOST_NOT_FOUND
;
357 return NSS_STATUS_NOTFOUND
;
360 canonical
= "_gateway";
363 hn
= gethostname_malloc();
366 *h_errnop
= NO_RECOVERY
;
367 return NSS_STATUS_TRYAGAIN
;
370 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), ".")) {
372 *h_errnop
= HOST_NOT_FOUND
;
373 return NSS_STATUS_NOTFOUND
;
376 n_addresses
= local_addresses(NULL
, 0, af
, &addresses
);
381 additional
= n_addresses
<= 0 && af
== AF_INET6
? "localhost" : NULL
;
382 local_address_ipv4
= LOCALADDRESS_IPV4
;
385 return fill_in_hostent(
386 canonical
, additional
,
388 addresses
, n_addresses
,
397 enum nss_status
_nss_myhostname_gethostbyaddr2_r(
398 const void* addr
, socklen_t len
,
400 struct hostent
*host
,
401 char *buffer
, size_t buflen
,
402 int *errnop
, int *h_errnop
,
405 const char *canonical
= NULL
, *additional
= NULL
;
406 uint32_t local_address_ipv4
= LOCALADDRESS_IPV4
;
407 _cleanup_free_
struct local_address
*addresses
= NULL
;
408 _cleanup_free_
char *hn
= NULL
;
410 struct local_address
*a
;
411 bool additional_from_hostname
= false;
414 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
422 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
423 *errnop
= EAFNOSUPPORT
;
425 return NSS_STATUS_UNAVAIL
;
428 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
430 *h_errnop
= NO_RECOVERY
;
431 return NSS_STATUS_UNAVAIL
;
435 if ((*(uint32_t*) addr
) == LOCALADDRESS_IPV4
)
438 if ((*(uint32_t*) addr
) == htobe32(INADDR_LOOPBACK
)) {
439 canonical
= "localhost";
440 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
445 assert(af
== AF_INET6
);
447 if (memcmp(addr
, LOCALADDRESS_IPV6
, 16) == 0) {
448 canonical
= "localhost";
449 additional_from_hostname
= true;
454 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
455 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++) {
459 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0)
463 addresses
= mfree(addresses
);
465 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
466 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++) {
470 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0) {
471 canonical
= "_gateway";
477 *h_errnop
= HOST_NOT_FOUND
;
478 return NSS_STATUS_NOTFOUND
;
481 if (!canonical
|| additional_from_hostname
) {
482 hn
= gethostname_malloc();
485 *h_errnop
= NO_RECOVERY
;
486 return NSS_STATUS_TRYAGAIN
;
495 return fill_in_hostent(
496 canonical
, additional
,
498 addresses
, n_addresses
,
507 NSS_GETHOSTBYNAME_FALLBACKS(myhostname
);
508 NSS_GETHOSTBYADDR_FALLBACKS(myhostname
);