]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
2 This file is part of systemd.
4 Copyright 2008-2011 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "alloc-util.h"
28 #include "hostname-util.h"
29 #include "local-addresses.h"
32 #include "signal-util.h"
33 #include "string-util.h"
36 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
37 * 127.0.0.1 that it can be translated back to the local hostname. For
38 * IPv6 we use ::1 which unfortunately will not translate back to the
39 * hostname but instead something like "localhost" or so. */
41 #define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
42 #define LOCALADDRESS_IPV6 &in6addr_loopback
44 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname
);
45 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname
);
47 enum nss_status
_nss_myhostname_gethostbyname4_r(
49 struct gaih_addrtuple
**pat
,
50 char *buffer
, size_t buflen
,
51 int *errnop
, int *h_errnop
,
54 struct gaih_addrtuple
*r_tuple
, *r_tuple_prev
= NULL
;
55 _cleanup_free_
struct local_address
*addresses
= NULL
;
56 _cleanup_free_
char *hn
= NULL
;
57 const char *canonical
= NULL
;
58 int n_addresses
= 0, lo_ifi
;
59 uint32_t local_address_ipv4
;
60 struct local_address
*a
;
65 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
73 if (is_localhost(name
)) {
74 /* We respond to 'localhost', so that /etc/hosts
77 canonical
= "localhost";
78 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
80 } else if (is_gateway_hostname(name
)) {
82 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
83 if (n_addresses
<= 0) {
85 *h_errnop
= HOST_NOT_FOUND
;
86 return NSS_STATUS_NOTFOUND
;
89 canonical
= "gateway";
92 hn
= gethostname_malloc();
95 *h_errnop
= NO_RECOVERY
;
96 return NSS_STATUS_TRYAGAIN
;
99 /* We respond to our local host name, our hostname suffixed with a single dot. */
100 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), ".")) {
102 *h_errnop
= HOST_NOT_FOUND
;
103 return NSS_STATUS_NOTFOUND
;
106 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
111 local_address_ipv4
= LOCALADDRESS_IPV4
;
114 /* If this call fails we fill in 0 as scope. Which is fine */
115 lo_ifi
= n_addresses
<= 0 ? LOOPBACK_IFINDEX
: 0;
117 l
= strlen(canonical
);
118 ms
= ALIGN(l
+1) + ALIGN(sizeof(struct gaih_addrtuple
)) * (n_addresses
> 0 ? n_addresses
: 2);
121 *h_errnop
= NO_RECOVERY
;
122 return NSS_STATUS_TRYAGAIN
;
125 /* First, fill in hostname */
127 memcpy(r_name
, canonical
, l
+1);
130 assert(n_addresses
>= 0);
131 if (n_addresses
== 0) {
132 /* Second, fill in IPv6 tuple */
133 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
134 r_tuple
->next
= r_tuple_prev
;
135 r_tuple
->name
= r_name
;
136 r_tuple
->family
= AF_INET6
;
137 memcpy(r_tuple
->addr
, LOCALADDRESS_IPV6
, 16);
138 r_tuple
->scopeid
= (uint32_t) lo_ifi
;
140 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
141 r_tuple_prev
= r_tuple
;
143 /* Third, fill in IPv4 tuple */
144 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
145 r_tuple
->next
= r_tuple_prev
;
146 r_tuple
->name
= r_name
;
147 r_tuple
->family
= AF_INET
;
148 *(uint32_t*) r_tuple
->addr
= local_address_ipv4
;
149 r_tuple
->scopeid
= (uint32_t) lo_ifi
;
151 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
152 r_tuple_prev
= r_tuple
;
155 /* Fourth, fill actual addresses in, but in backwards order */
156 for (a
= addresses
+ n_addresses
- 1, n
= 0; (int) n
< n_addresses
; n
++, a
--) {
157 r_tuple
= (struct gaih_addrtuple
*) (buffer
+ idx
);
158 r_tuple
->next
= r_tuple_prev
;
159 r_tuple
->name
= r_name
;
160 r_tuple
->family
= a
->family
;
161 r_tuple
->scopeid
= a
->ifindex
;
162 memcpy(r_tuple
->addr
, &a
->address
, 16);
164 idx
+= ALIGN(sizeof(struct gaih_addrtuple
));
165 r_tuple_prev
= r_tuple
;
168 /* Verify the size matches */
171 /* Nscd expects us to store the first record in **pat. */
173 **pat
= *r_tuple_prev
;
180 /* Explicitly reset all error variables */
182 *h_errnop
= NETDB_SUCCESS
;
185 return NSS_STATUS_SUCCESS
;
188 static enum nss_status
fill_in_hostent(
189 const char *canonical
, const char *additional
,
191 struct local_address
*addresses
, unsigned n_addresses
,
192 uint32_t local_address_ipv4
,
193 struct hostent
*result
,
194 char *buffer
, size_t buflen
,
195 int *errnop
, int *h_errnop
,
199 size_t l_canonical
, l_additional
, idx
, ms
, alen
;
200 char *r_addr
, *r_name
, *r_aliases
, *r_alias
= NULL
, *r_addr_list
;
201 struct local_address
*a
;
210 alen
= FAMILY_ADDRESS_SIZE(af
);
212 for (a
= addresses
, n
= 0, c
= 0; n
< n_addresses
; a
++, n
++)
216 l_canonical
= strlen(canonical
);
217 l_additional
= additional
? strlen(additional
) : 0;
218 ms
= ALIGN(l_canonical
+1)+
219 (additional
? ALIGN(l_additional
+1) : 0) +
221 (additional
? sizeof(char*) : 0) +
222 (c
> 0 ? c
: 1) * ALIGN(alen
) +
223 (c
> 0 ? c
+1 : 2) * sizeof(char*);
227 *h_errnop
= NO_RECOVERY
;
228 return NSS_STATUS_TRYAGAIN
;
231 /* First, fill in hostnames */
233 memcpy(r_name
, canonical
, l_canonical
+1);
234 idx
= ALIGN(l_canonical
+1);
237 r_alias
= buffer
+ idx
;
238 memcpy(r_alias
, additional
, l_additional
+1);
239 idx
+= ALIGN(l_additional
+1);
242 /* Second, create aliases array */
243 r_aliases
= buffer
+ idx
;
245 ((char**) r_aliases
)[0] = r_alias
;
246 ((char**) r_aliases
)[1] = NULL
;
247 idx
+= 2*sizeof(char*);
249 ((char**) r_aliases
)[0] = NULL
;
250 idx
+= sizeof(char*);
253 /* Third, add addresses */
254 r_addr
= buffer
+ idx
;
258 for (a
= addresses
, n
= 0; n
< n_addresses
; a
++, n
++) {
262 memcpy(r_addr
+ i
*ALIGN(alen
), &a
->address
, alen
);
267 idx
+= c
*ALIGN(alen
);
270 *(uint32_t*) r_addr
= local_address_ipv4
;
272 memcpy(r_addr
, LOCALADDRESS_IPV6
, 16);
277 /* Fourth, add address pointer array */
278 r_addr_list
= buffer
+ idx
;
282 for (i
= 0; i
< c
; i
++)
283 ((char**) r_addr_list
)[i
] = r_addr
+ i
*ALIGN(alen
);
285 ((char**) r_addr_list
)[i
] = NULL
;
286 idx
+= (c
+1) * sizeof(char*);
289 ((char**) r_addr_list
)[0] = r_addr
;
290 ((char**) r_addr_list
)[1] = NULL
;
291 idx
+= 2 * sizeof(char*);
294 /* Verify the size matches */
297 result
->h_name
= r_name
;
298 result
->h_aliases
= (char**) r_aliases
;
299 result
->h_addrtype
= af
;
300 result
->h_length
= alen
;
301 result
->h_addr_list
= (char**) r_addr_list
;
309 /* Explicitly reset all error variables */
311 *h_errnop
= NETDB_SUCCESS
;
314 return NSS_STATUS_SUCCESS
;
317 enum nss_status
_nss_myhostname_gethostbyname3_r(
320 struct hostent
*host
,
321 char *buffer
, size_t buflen
,
322 int *errnop
, int *h_errnop
,
326 _cleanup_free_
struct local_address
*addresses
= NULL
;
327 const char *canonical
, *additional
= NULL
;
328 _cleanup_free_
char *hn
= NULL
;
329 uint32_t local_address_ipv4
= 0;
332 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
343 if (af
!= AF_INET
&& af
!= AF_INET6
) {
344 *errnop
= EAFNOSUPPORT
;
346 return NSS_STATUS_UNAVAIL
;
349 if (is_localhost(name
)) {
350 canonical
= "localhost";
351 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
353 } else if (is_gateway_hostname(name
)) {
355 n_addresses
= local_gateways(NULL
, 0, af
, &addresses
);
356 if (n_addresses
<= 0) {
358 *h_errnop
= HOST_NOT_FOUND
;
359 return NSS_STATUS_NOTFOUND
;
362 canonical
= "gateway";
365 hn
= gethostname_malloc();
368 *h_errnop
= NO_RECOVERY
;
369 return NSS_STATUS_TRYAGAIN
;
372 if (!streq(name
, hn
) && !streq_ptr(startswith(name
, hn
), ".")) {
374 *h_errnop
= HOST_NOT_FOUND
;
375 return NSS_STATUS_NOTFOUND
;
378 n_addresses
= local_addresses(NULL
, 0, af
, &addresses
);
383 additional
= n_addresses
<= 0 && af
== AF_INET6
? "localhost" : NULL
;
384 local_address_ipv4
= LOCALADDRESS_IPV4
;
387 return fill_in_hostent(
388 canonical
, additional
,
390 addresses
, n_addresses
,
399 enum nss_status
_nss_myhostname_gethostbyaddr2_r(
400 const void* addr
, socklen_t len
,
402 struct hostent
*host
,
403 char *buffer
, size_t buflen
,
404 int *errnop
, int *h_errnop
,
407 const char *canonical
= NULL
, *additional
= NULL
;
408 uint32_t local_address_ipv4
= LOCALADDRESS_IPV4
;
409 _cleanup_free_
struct local_address
*addresses
= NULL
;
410 _cleanup_free_
char *hn
= NULL
;
412 struct local_address
*a
;
413 bool additional_from_hostname
= false;
416 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
424 if (!IN_SET(af
, AF_INET
, AF_INET6
)) {
425 *errnop
= EAFNOSUPPORT
;
427 return NSS_STATUS_UNAVAIL
;
430 if (len
!= FAMILY_ADDRESS_SIZE(af
)) {
432 *h_errnop
= NO_RECOVERY
;
433 return NSS_STATUS_UNAVAIL
;
437 if ((*(uint32_t*) addr
) == LOCALADDRESS_IPV4
)
440 if ((*(uint32_t*) addr
) == htobe32(INADDR_LOOPBACK
)) {
441 canonical
= "localhost";
442 local_address_ipv4
= htobe32(INADDR_LOOPBACK
);
447 assert(af
== AF_INET6
);
449 if (memcmp(addr
, LOCALADDRESS_IPV6
, 16) == 0) {
450 canonical
= "localhost";
451 additional_from_hostname
= true;
456 n_addresses
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
457 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++) {
461 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0)
465 addresses
= mfree(addresses
);
467 n_addresses
= local_gateways(NULL
, 0, AF_UNSPEC
, &addresses
);
468 for (a
= addresses
, n
= 0; (int) n
< n_addresses
; n
++, a
++) {
472 if (memcmp(addr
, &a
->address
, FAMILY_ADDRESS_SIZE(af
)) == 0) {
473 canonical
= "gateway";
479 *h_errnop
= HOST_NOT_FOUND
;
480 return NSS_STATUS_NOTFOUND
;
483 if (!canonical
|| additional_from_hostname
) {
484 hn
= gethostname_malloc();
487 *h_errnop
= NO_RECOVERY
;
488 return NSS_STATUS_TRYAGAIN
;
497 return fill_in_hostent(
498 canonical
, additional
,
500 addresses
, n_addresses
,
509 NSS_GETHOSTBYNAME_FALLBACKS(myhostname
);
510 NSS_GETHOSTBYADDR_FALLBACKS(myhostname
);