1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2008-2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/types.h>
31 #include <arpa/inet.h>
33 #include "local-addresses.h"
38 /* Ensure that glibc's assert is used. We cannot use assert from macro.h, as
39 * libnss_myhostname will be linked into arbitrary programs which will, in turn
40 * attempt to write to the journal via log_dispatch() */
43 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
44 * 127.0.0.1 that it can be translated back to the local hostname. For
45 * IPv6 we use ::1 which unfortunately will not translate back to the
46 * hostname but instead something like "localhost6" or so. */
48 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
49 #define LOCALADDRESS_IPV6 &in6addr_loopback
50 #define LOOPBACK_INTERFACE "lo"
52 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
53 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
55 enum nss_status _nss_myhostname_gethostbyname4_r(
57 struct gaih_addrtuple **pat,
58 char *buffer, size_t buflen,
59 int *errnop, int *h_errnop,
62 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
63 _cleanup_free_ struct local_address *addresses = NULL;
64 _cleanup_free_ char *hn = NULL;
65 const char *canonical = NULL;
66 int n_addresses = 0, lo_ifi;
67 uint32_t local_address_ipv4;
68 struct local_address *a;
79 if (is_localhost(name)) {
80 /* We respond to 'localhost', so that /etc/hosts
83 canonical = "localhost";
84 local_address_ipv4 = htonl(INADDR_LOOPBACK);
86 hn = gethostname_malloc();
89 *h_errnop = NO_RECOVERY;
90 return NSS_STATUS_TRYAGAIN;
93 /* We respond to our local host name, our our hostname suffixed with a single dot. */
94 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
96 *h_errnop = HOST_NOT_FOUND;
97 return NSS_STATUS_NOTFOUND;
100 n_addresses = local_addresses(&addresses);
105 local_address_ipv4 = LOCALADDRESS_IPV4;
108 /* If this call fails we fill in 0 as scope. Which is fine */
109 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
111 l = strlen(canonical);
112 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
115 *h_errnop = NO_RECOVERY;
116 return NSS_STATUS_TRYAGAIN;
119 /* First, fill in hostname */
121 memcpy(r_name, canonical, l+1);
124 if (n_addresses <= 0) {
125 /* Second, fill in IPv6 tuple */
126 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
127 r_tuple->next = r_tuple_prev;
128 r_tuple->name = r_name;
129 r_tuple->family = AF_INET6;
130 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
131 r_tuple->scopeid = (uint32_t) lo_ifi;
133 idx += ALIGN(sizeof(struct gaih_addrtuple));
134 r_tuple_prev = r_tuple;
136 /* Third, fill in IPv4 tuple */
137 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
138 r_tuple->next = r_tuple_prev;
139 r_tuple->name = r_name;
140 r_tuple->family = AF_INET;
141 *(uint32_t*) r_tuple->addr = local_address_ipv4;
142 r_tuple->scopeid = (uint32_t) lo_ifi;
144 idx += ALIGN(sizeof(struct gaih_addrtuple));
145 r_tuple_prev = r_tuple;
148 /* Fourth, fill actual addresses in, but in backwards order */
149 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
150 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
151 r_tuple->next = r_tuple_prev;
152 r_tuple->name = r_name;
153 r_tuple->family = a->family;
154 r_tuple->scopeid = a->ifindex;
155 memcpy(r_tuple->addr, &a->address, 16);
157 idx += ALIGN(sizeof(struct gaih_addrtuple));
158 r_tuple_prev = r_tuple;
161 /* Verify the size matches */
164 /* Nscd expects us to store the first record in **pat. */
166 **pat = *r_tuple_prev;
173 return NSS_STATUS_SUCCESS;
176 static enum nss_status fill_in_hostent(
177 const char *canonical, const char *additional,
179 struct local_address *addresses, unsigned n_addresses,
180 uint32_t local_address_ipv4,
181 struct hostent *result,
182 char *buffer, size_t buflen,
183 int *errnop, int *h_errnop,
187 size_t l_canonical, l_additional, idx, ms, alen;
188 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
189 struct local_address *a;
198 alen = PROTO_ADDRESS_SIZE(af);
200 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
204 l_canonical = strlen(canonical);
205 l_additional = additional ? strlen(additional) : 0;
206 ms = ALIGN(l_canonical+1)+
207 (additional ? ALIGN(l_additional+1) : 0) +
209 (additional ? sizeof(char*) : 0) +
210 (c > 0 ? c : 1) * ALIGN(alen) +
211 (c > 0 ? c+1 : 2) * sizeof(char*);
215 *h_errnop = NO_RECOVERY;
216 return NSS_STATUS_TRYAGAIN;
219 /* First, fill in hostnames */
221 memcpy(r_name, canonical, l_canonical+1);
222 idx = ALIGN(l_canonical+1);
225 r_alias = buffer + idx;
226 memcpy(r_alias, additional, l_additional+1);
227 idx += ALIGN(l_additional+1);
230 /* Second, create aliases array */
231 r_aliases = buffer + idx;
233 ((char**) r_aliases)[0] = r_alias;
234 ((char**) r_aliases)[1] = NULL;
235 idx += 2*sizeof(char*);
237 ((char**) r_aliases)[0] = NULL;
238 idx += sizeof(char*);
241 /* Third, add addresses */
242 r_addr = buffer + idx;
246 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
250 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
255 idx += c*ALIGN(alen);
258 *(uint32_t*) r_addr = local_address_ipv4;
260 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
265 /* Fourth, add address pointer array */
266 r_addr_list = buffer + idx;
270 for (i = 0; i < c; i++)
271 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
273 ((char**) r_addr_list)[i] = NULL;
274 idx += (c+1) * sizeof(char*);
277 ((char**) r_addr_list)[0] = r_addr;
278 ((char**) r_addr_list)[1] = NULL;
279 idx += 2 * sizeof(char*);
282 /* Verify the size matches */
285 result->h_name = r_name;
286 result->h_aliases = (char**) r_aliases;
287 result->h_addrtype = af;
288 result->h_length = alen;
289 result->h_addr_list = (char**) r_addr_list;
297 return NSS_STATUS_SUCCESS;
300 enum nss_status _nss_myhostname_gethostbyname3_r(
303 struct hostent *host,
304 char *buffer, size_t buflen,
305 int *errnop, int *h_errnop,
309 _cleanup_free_ struct local_address *addresses = NULL;
310 const char *canonical, *additional = NULL;
311 _cleanup_free_ char *hn = NULL;
312 uint32_t local_address_ipv4;
324 if (af != AF_INET && af != AF_INET6) {
325 *errnop = EAFNOSUPPORT;
327 return NSS_STATUS_UNAVAIL;
330 if (is_localhost(name)) {
331 canonical = "localhost";
332 local_address_ipv4 = htonl(INADDR_LOOPBACK);
334 hn = gethostname_malloc();
337 *h_errnop = NO_RECOVERY;
338 return NSS_STATUS_TRYAGAIN;
341 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
343 *h_errnop = HOST_NOT_FOUND;
344 return NSS_STATUS_NOTFOUND;
347 n_addresses = local_addresses(&addresses);
352 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
353 local_address_ipv4 = LOCALADDRESS_IPV4;
356 return fill_in_hostent(
357 canonical, additional,
359 addresses, n_addresses,
368 enum nss_status _nss_myhostname_gethostbyaddr2_r(
369 const void* addr, socklen_t len,
371 struct hostent *host,
372 char *buffer, size_t buflen,
373 int *errnop, int *h_errnop,
376 const char *canonical = NULL, *additional = NULL;
377 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
378 _cleanup_free_ struct local_address *addresses = NULL;
379 _cleanup_free_ char *hn = NULL;
381 struct local_address *a;
390 if (len != PROTO_ADDRESS_SIZE(af)) {
392 *h_errnop = NO_RECOVERY;
393 return NSS_STATUS_UNAVAIL;
398 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
401 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
402 canonical = "localhost";
403 local_address_ipv4 = htonl(INADDR_LOOPBACK);
407 } else if (af == AF_INET6) {
409 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
410 additional = "localhost";
415 *errnop = EAFNOSUPPORT;
417 return NSS_STATUS_UNAVAIL;
420 n_addresses = local_addresses(&addresses);
424 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
428 if (memcmp(addr, &a->address, PROTO_ADDRESS_SIZE(af)) == 0)
433 *h_errnop = HOST_NOT_FOUND;
435 return NSS_STATUS_NOTFOUND;
439 hn = gethostname_malloc();
442 *h_errnop = NO_RECOVERY;
443 return NSS_STATUS_TRYAGAIN;
449 return fill_in_hostent(
450 canonical, additional,
452 addresses, n_addresses,
462 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
463 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);