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 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
39 * 127.0.0.1 that it can be translated back to the local hostname. For
40 * IPv6 we use ::1 which unfortunately will not translate back to the
41 * hostname but instead something like "localhost6" or so. */
43 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
44 #define LOCALADDRESS_IPV6 &in6addr_loopback
45 #define LOOPBACK_INTERFACE "lo"
47 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
48 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
50 enum nss_status _nss_myhostname_gethostbyname4_r(
52 struct gaih_addrtuple **pat,
53 char *buffer, size_t buflen,
54 int *errnop, int *h_errnop,
57 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
58 _cleanup_free_ struct local_address *addresses = NULL;
59 _cleanup_free_ char *hn = NULL;
60 const char *canonical = NULL;
61 int n_addresses = 0, lo_ifi;
62 uint32_t local_address_ipv4;
63 struct local_address *a;
74 if (is_localhost(name)) {
75 /* We respond to 'localhost', so that /etc/hosts
78 canonical = "localhost";
79 local_address_ipv4 = htonl(INADDR_LOOPBACK);
81 hn = gethostname_malloc();
84 *h_errnop = NO_RECOVERY;
85 return NSS_STATUS_TRYAGAIN;
88 /* We respond to our local host name, our our hostname suffixed with a single dot. */
89 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
91 *h_errnop = HOST_NOT_FOUND;
92 return NSS_STATUS_NOTFOUND;
95 n_addresses = local_addresses(&addresses);
100 local_address_ipv4 = LOCALADDRESS_IPV4;
103 /* If this call fails we fill in 0 as scope. Which is fine */
104 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
106 l = strlen(canonical);
107 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
110 *h_errnop = NO_RECOVERY;
111 return NSS_STATUS_TRYAGAIN;
114 /* First, fill in hostname */
116 memcpy(r_name, canonical, l+1);
119 if (n_addresses <= 0) {
120 /* Second, fill in IPv6 tuple */
121 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
122 r_tuple->next = r_tuple_prev;
123 r_tuple->name = r_name;
124 r_tuple->family = AF_INET6;
125 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
126 r_tuple->scopeid = (uint32_t) lo_ifi;
128 idx += ALIGN(sizeof(struct gaih_addrtuple));
129 r_tuple_prev = r_tuple;
131 /* Third, fill in IPv4 tuple */
132 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
133 r_tuple->next = r_tuple_prev;
134 r_tuple->name = r_name;
135 r_tuple->family = AF_INET;
136 *(uint32_t*) r_tuple->addr = local_address_ipv4;
137 r_tuple->scopeid = (uint32_t) lo_ifi;
139 idx += ALIGN(sizeof(struct gaih_addrtuple));
140 r_tuple_prev = r_tuple;
143 /* Fourth, fill actual addresses in, but in backwards order */
144 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
145 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
146 r_tuple->next = r_tuple_prev;
147 r_tuple->name = r_name;
148 r_tuple->family = a->family;
149 r_tuple->scopeid = a->ifindex;
150 memcpy(r_tuple->addr, &a->address, 16);
152 idx += ALIGN(sizeof(struct gaih_addrtuple));
153 r_tuple_prev = r_tuple;
156 /* Verify the size matches */
159 /* Nscd expects us to store the first record in **pat. */
161 **pat = *r_tuple_prev;
168 return NSS_STATUS_SUCCESS;
171 static enum nss_status fill_in_hostent(
172 const char *canonical, const char *additional,
174 struct local_address *addresses, unsigned n_addresses,
175 uint32_t local_address_ipv4,
176 struct hostent *result,
177 char *buffer, size_t buflen,
178 int *errnop, int *h_errnop,
182 size_t l_canonical, l_additional, idx, ms, alen;
183 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
184 struct local_address *a;
193 alen = FAMILY_ADDRESS_SIZE(af);
195 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
199 l_canonical = strlen(canonical);
200 l_additional = additional ? strlen(additional) : 0;
201 ms = ALIGN(l_canonical+1)+
202 (additional ? ALIGN(l_additional+1) : 0) +
204 (additional ? sizeof(char*) : 0) +
205 (c > 0 ? c : 1) * ALIGN(alen) +
206 (c > 0 ? c+1 : 2) * sizeof(char*);
210 *h_errnop = NO_RECOVERY;
211 return NSS_STATUS_TRYAGAIN;
214 /* First, fill in hostnames */
216 memcpy(r_name, canonical, l_canonical+1);
217 idx = ALIGN(l_canonical+1);
220 r_alias = buffer + idx;
221 memcpy(r_alias, additional, l_additional+1);
222 idx += ALIGN(l_additional+1);
225 /* Second, create aliases array */
226 r_aliases = buffer + idx;
228 ((char**) r_aliases)[0] = r_alias;
229 ((char**) r_aliases)[1] = NULL;
230 idx += 2*sizeof(char*);
232 ((char**) r_aliases)[0] = NULL;
233 idx += sizeof(char*);
236 /* Third, add addresses */
237 r_addr = buffer + idx;
241 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
245 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
250 idx += c*ALIGN(alen);
253 *(uint32_t*) r_addr = local_address_ipv4;
255 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
260 /* Fourth, add address pointer array */
261 r_addr_list = buffer + idx;
265 for (i = 0; i < c; i++)
266 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
268 ((char**) r_addr_list)[i] = NULL;
269 idx += (c+1) * sizeof(char*);
272 ((char**) r_addr_list)[0] = r_addr;
273 ((char**) r_addr_list)[1] = NULL;
274 idx += 2 * sizeof(char*);
277 /* Verify the size matches */
280 result->h_name = r_name;
281 result->h_aliases = (char**) r_aliases;
282 result->h_addrtype = af;
283 result->h_length = alen;
284 result->h_addr_list = (char**) r_addr_list;
292 return NSS_STATUS_SUCCESS;
295 enum nss_status _nss_myhostname_gethostbyname3_r(
298 struct hostent *host,
299 char *buffer, size_t buflen,
300 int *errnop, int *h_errnop,
304 _cleanup_free_ struct local_address *addresses = NULL;
305 const char *canonical, *additional = NULL;
306 _cleanup_free_ char *hn = NULL;
307 uint32_t local_address_ipv4;
319 if (af != AF_INET && af != AF_INET6) {
320 *errnop = EAFNOSUPPORT;
322 return NSS_STATUS_UNAVAIL;
325 if (is_localhost(name)) {
326 canonical = "localhost";
327 local_address_ipv4 = htonl(INADDR_LOOPBACK);
329 hn = gethostname_malloc();
332 *h_errnop = NO_RECOVERY;
333 return NSS_STATUS_TRYAGAIN;
336 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
338 *h_errnop = HOST_NOT_FOUND;
339 return NSS_STATUS_NOTFOUND;
342 n_addresses = local_addresses(&addresses);
347 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
348 local_address_ipv4 = LOCALADDRESS_IPV4;
351 return fill_in_hostent(
352 canonical, additional,
354 addresses, n_addresses,
363 enum nss_status _nss_myhostname_gethostbyaddr2_r(
364 const void* addr, socklen_t len,
366 struct hostent *host,
367 char *buffer, size_t buflen,
368 int *errnop, int *h_errnop,
371 const char *canonical = NULL, *additional = NULL;
372 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
373 _cleanup_free_ struct local_address *addresses = NULL;
374 _cleanup_free_ char *hn = NULL;
376 struct local_address *a;
385 if (!IN_SET(af, AF_INET, AF_INET6)) {
386 *errnop = EAFNOSUPPORT;
388 return NSS_STATUS_UNAVAIL;
391 if (len != FAMILY_ADDRESS_SIZE(af)) {
393 *h_errnop = NO_RECOVERY;
394 return NSS_STATUS_UNAVAIL;
399 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
402 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
403 canonical = "localhost";
404 local_address_ipv4 = htonl(INADDR_LOOPBACK);
409 assert(af == AF_INET6);
411 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
412 additional = "localhost";
418 n_addresses = local_addresses(&addresses);
422 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
426 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
431 *h_errnop = HOST_NOT_FOUND;
433 return NSS_STATUS_NOTFOUND;
437 hn = gethostname_malloc();
440 *h_errnop = NO_RECOVERY;
441 return NSS_STATUS_TRYAGAIN;
447 return fill_in_hostent(
448 canonical, additional,
450 addresses, n_addresses,
460 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
461 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);