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;
188 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
190 struct local_address *a;
199 alen = PROTO_ADDRESS_SIZE(af);
201 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
205 l_canonical = strlen(canonical);
206 l_additional = additional ? strlen(additional) : 0;
207 ms = ALIGN(l_canonical+1)+
208 (additional ? ALIGN(l_additional+1) : 0) +
210 (additional ? sizeof(char*) : 0) +
211 (c > 0 ? c : 1) * ALIGN(alen)+
212 (c > 0 ? c+1 : 2) * sizeof(char*);
216 *h_errnop = NO_RECOVERY;
217 return NSS_STATUS_TRYAGAIN;
220 /* First, fill in hostnames */
222 memcpy(r_name, canonical, l_canonical+1);
223 idx = ALIGN(l_canonical+1);
226 r_alias = buffer + idx;
227 memcpy(r_alias, additional, l_additional+1);
228 idx += ALIGN(l_additional+1);
231 /* Second, create aliases array */
232 r_aliases = buffer + idx;
234 ((char**) r_aliases)[0] = r_alias;
235 ((char**) r_aliases)[1] = NULL;
236 idx += 2*sizeof(char*);
238 ((char**) r_aliases)[0] = NULL;
239 idx += sizeof(char*);
242 /* Third, add addresses */
243 r_addr = buffer + idx;
247 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
251 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
256 idx += c*ALIGN(alen);
259 *(uint32_t*) r_addr = local_address_ipv4;
261 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
266 /* Fourth, add address pointer array */
267 r_addr_list = buffer + idx;
271 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
275 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
280 ((char**) r_addr_list)[c] = NULL;
281 idx += (c+1)*sizeof(char*);
284 ((char**) r_addr_list)[0] = r_addr;
285 ((char**) r_addr_list)[1] = NULL;
286 idx += 2*sizeof(char*);
289 /* Verify the size matches */
292 result->h_name = r_name;
293 result->h_aliases = (char**) r_aliases;
294 result->h_addrtype = af;
295 result->h_length = alen;
296 result->h_addr_list = (char**) r_addr_list;
304 return NSS_STATUS_SUCCESS;
307 enum nss_status _nss_myhostname_gethostbyname3_r(
310 struct hostent *host,
311 char *buffer, size_t buflen,
312 int *errnop, int *h_errnop,
316 _cleanup_free_ struct local_address *addresses = NULL;
317 const char *canonical, *additional = NULL;
318 _cleanup_free_ char *hn = NULL;
319 uint32_t local_address_ipv4;
331 if (af != AF_INET && af != AF_INET6) {
332 *errnop = EAFNOSUPPORT;
334 return NSS_STATUS_UNAVAIL;
337 if (is_localhost(name)) {
338 canonical = "localhost";
339 local_address_ipv4 = htonl(INADDR_LOOPBACK);
341 hn = gethostname_malloc();
344 *h_errnop = NO_RECOVERY;
345 return NSS_STATUS_TRYAGAIN;
348 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
350 *h_errnop = HOST_NOT_FOUND;
351 return NSS_STATUS_NOTFOUND;
354 n_addresses = local_addresses(&addresses);
359 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
360 local_address_ipv4 = LOCALADDRESS_IPV4;
363 return fill_in_hostent(
364 canonical, additional,
366 addresses, n_addresses,
375 enum nss_status _nss_myhostname_gethostbyaddr2_r(
376 const void* addr, socklen_t len,
378 struct hostent *host,
379 char *buffer, size_t buflen,
380 int *errnop, int *h_errnop,
383 const char *canonical = NULL, *additional = NULL;
384 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
385 _cleanup_free_ struct local_address *addresses = NULL;
386 _cleanup_free_ char *hn = NULL;
388 struct local_address *a;
397 if (len != PROTO_ADDRESS_SIZE(af)) {
399 *h_errnop = NO_RECOVERY;
400 return NSS_STATUS_UNAVAIL;
405 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
408 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
409 canonical = "localhost";
410 local_address_ipv4 = htonl(INADDR_LOOPBACK);
414 } else if (af == AF_INET6) {
416 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
417 additional = "localhost";
422 *errnop = EAFNOSUPPORT;
424 return NSS_STATUS_UNAVAIL;
427 n_addresses = local_addresses(&addresses);
431 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
435 if (memcmp(addr, &a->address, PROTO_ADDRESS_SIZE(af)) == 0)
440 *h_errnop = HOST_NOT_FOUND;
442 return NSS_STATUS_NOTFOUND;
446 hn = gethostname_malloc();
449 *h_errnop = NO_RECOVERY;
450 return NSS_STATUS_TRYAGAIN;
456 return fill_in_hostent(
457 canonical, additional,
459 addresses, n_addresses,
469 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
470 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);