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/>.
29 #include "local-addresses.h"
34 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
35 * 127.0.0.1 that it can be translated back to the local hostname. For
36 * IPv6 we use ::1 which unfortunately will not translate back to the
37 * hostname but instead something like "localhost" or so. */
39 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
40 #define LOCALADDRESS_IPV6 &in6addr_loopback
41 #define LOOPBACK_INTERFACE "lo"
43 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
44 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
46 static bool is_gateway(const char *hostname) {
49 return streq(hostname, "gateway") ||
50 streq(hostname, "gateway.");
53 enum nss_status _nss_myhostname_gethostbyname4_r(
55 struct gaih_addrtuple **pat,
56 char *buffer, size_t buflen,
57 int *errnop, int *h_errnop,
60 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
61 _cleanup_free_ struct local_address *addresses = NULL;
62 _cleanup_free_ char *hn = NULL;
63 const char *canonical = NULL;
64 int n_addresses = 0, lo_ifi;
65 uint32_t local_address_ipv4;
66 struct local_address *a;
77 if (is_localhost(name)) {
78 /* We respond to 'localhost', so that /etc/hosts
81 canonical = "localhost";
82 local_address_ipv4 = htonl(INADDR_LOOPBACK);
84 } else if (is_gateway(name)) {
86 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
87 if (n_addresses <= 0) {
89 *h_errnop = HOST_NOT_FOUND;
90 return NSS_STATUS_NOTFOUND;
93 canonical = "gateway";
96 hn = gethostname_malloc();
99 *h_errnop = NO_RECOVERY;
100 return NSS_STATUS_TRYAGAIN;
103 /* We respond to our local host name, our our hostname suffixed with a single dot. */
104 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
106 *h_errnop = HOST_NOT_FOUND;
107 return NSS_STATUS_NOTFOUND;
110 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
115 local_address_ipv4 = LOCALADDRESS_IPV4;
118 /* If this call fails we fill in 0 as scope. Which is fine */
119 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
121 l = strlen(canonical);
122 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
125 *h_errnop = NO_RECOVERY;
126 return NSS_STATUS_TRYAGAIN;
129 /* First, fill in hostname */
131 memcpy(r_name, canonical, l+1);
134 if (n_addresses <= 0) {
135 /* Second, fill in IPv6 tuple */
136 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
137 r_tuple->next = r_tuple_prev;
138 r_tuple->name = r_name;
139 r_tuple->family = AF_INET6;
140 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
141 r_tuple->scopeid = (uint32_t) lo_ifi;
143 idx += ALIGN(sizeof(struct gaih_addrtuple));
144 r_tuple_prev = r_tuple;
146 /* Third, fill in IPv4 tuple */
147 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
148 r_tuple->next = r_tuple_prev;
149 r_tuple->name = r_name;
150 r_tuple->family = AF_INET;
151 *(uint32_t*) r_tuple->addr = local_address_ipv4;
152 r_tuple->scopeid = (uint32_t) lo_ifi;
154 idx += ALIGN(sizeof(struct gaih_addrtuple));
155 r_tuple_prev = r_tuple;
158 /* Fourth, fill actual addresses in, but in backwards order */
159 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
160 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
161 r_tuple->next = r_tuple_prev;
162 r_tuple->name = r_name;
163 r_tuple->family = a->family;
164 r_tuple->scopeid = a->ifindex;
165 memcpy(r_tuple->addr, &a->address, 16);
167 idx += ALIGN(sizeof(struct gaih_addrtuple));
168 r_tuple_prev = r_tuple;
171 /* Verify the size matches */
174 /* Nscd expects us to store the first record in **pat. */
176 **pat = *r_tuple_prev;
183 /* Explicitly reset all error variables */
185 *h_errnop = NETDB_SUCCESS;
188 return NSS_STATUS_SUCCESS;
191 static enum nss_status fill_in_hostent(
192 const char *canonical, const char *additional,
194 struct local_address *addresses, unsigned n_addresses,
195 uint32_t local_address_ipv4,
196 struct hostent *result,
197 char *buffer, size_t buflen,
198 int *errnop, int *h_errnop,
202 size_t l_canonical, l_additional, idx, ms, alen;
203 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
204 struct local_address *a;
213 alen = FAMILY_ADDRESS_SIZE(af);
215 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
219 l_canonical = strlen(canonical);
220 l_additional = additional ? strlen(additional) : 0;
221 ms = ALIGN(l_canonical+1)+
222 (additional ? ALIGN(l_additional+1) : 0) +
224 (additional ? sizeof(char*) : 0) +
225 (c > 0 ? c : 1) * ALIGN(alen) +
226 (c > 0 ? c+1 : 2) * sizeof(char*);
230 *h_errnop = NO_RECOVERY;
231 return NSS_STATUS_TRYAGAIN;
234 /* First, fill in hostnames */
236 memcpy(r_name, canonical, l_canonical+1);
237 idx = ALIGN(l_canonical+1);
240 r_alias = buffer + idx;
241 memcpy(r_alias, additional, l_additional+1);
242 idx += ALIGN(l_additional+1);
245 /* Second, create aliases array */
246 r_aliases = buffer + idx;
248 ((char**) r_aliases)[0] = r_alias;
249 ((char**) r_aliases)[1] = NULL;
250 idx += 2*sizeof(char*);
252 ((char**) r_aliases)[0] = NULL;
253 idx += sizeof(char*);
256 /* Third, add addresses */
257 r_addr = buffer + idx;
261 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
265 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
270 idx += c*ALIGN(alen);
273 *(uint32_t*) r_addr = local_address_ipv4;
275 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
280 /* Fourth, add address pointer array */
281 r_addr_list = buffer + idx;
285 for (i = 0; i < c; i++)
286 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
288 ((char**) r_addr_list)[i] = NULL;
289 idx += (c+1) * sizeof(char*);
292 ((char**) r_addr_list)[0] = r_addr;
293 ((char**) r_addr_list)[1] = NULL;
294 idx += 2 * sizeof(char*);
297 /* Verify the size matches */
300 result->h_name = r_name;
301 result->h_aliases = (char**) r_aliases;
302 result->h_addrtype = af;
303 result->h_length = alen;
304 result->h_addr_list = (char**) r_addr_list;
312 /* Explicitly reset all error variables */
314 *h_errnop = NETDB_SUCCESS;
317 return NSS_STATUS_SUCCESS;
320 enum nss_status _nss_myhostname_gethostbyname3_r(
323 struct hostent *host,
324 char *buffer, size_t buflen,
325 int *errnop, int *h_errnop,
329 _cleanup_free_ struct local_address *addresses = NULL;
330 const char *canonical, *additional = NULL;
331 _cleanup_free_ char *hn = NULL;
332 uint32_t local_address_ipv4 = 0;
344 if (af != AF_INET && af != AF_INET6) {
345 *errnop = EAFNOSUPPORT;
347 return NSS_STATUS_UNAVAIL;
350 if (is_localhost(name)) {
351 canonical = "localhost";
352 local_address_ipv4 = htonl(INADDR_LOOPBACK);
354 } else if (is_gateway(name)) {
356 n_addresses = local_gateways(NULL, 0, af, &addresses);
357 if (n_addresses <= 0) {
359 *h_errnop = HOST_NOT_FOUND;
360 return NSS_STATUS_NOTFOUND;
363 canonical = "gateway";
366 hn = gethostname_malloc();
369 *h_errnop = NO_RECOVERY;
370 return NSS_STATUS_TRYAGAIN;
373 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
375 *h_errnop = HOST_NOT_FOUND;
376 return NSS_STATUS_NOTFOUND;
379 n_addresses = local_addresses(NULL, 0, af, &addresses);
384 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
385 local_address_ipv4 = LOCALADDRESS_IPV4;
388 return fill_in_hostent(
389 canonical, additional,
391 addresses, n_addresses,
400 enum nss_status _nss_myhostname_gethostbyaddr2_r(
401 const void* addr, socklen_t len,
403 struct hostent *host,
404 char *buffer, size_t buflen,
405 int *errnop, int *h_errnop,
408 const char *canonical = NULL, *additional = NULL;
409 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
410 _cleanup_free_ struct local_address *addresses = NULL;
411 _cleanup_free_ char *hn = NULL;
413 struct local_address *a;
414 bool additional_from_hostname = false;
423 if (!IN_SET(af, AF_INET, AF_INET6)) {
424 *errnop = EAFNOSUPPORT;
426 return NSS_STATUS_UNAVAIL;
429 if (len != FAMILY_ADDRESS_SIZE(af)) {
431 *h_errnop = NO_RECOVERY;
432 return NSS_STATUS_UNAVAIL;
436 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
439 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
440 canonical = "localhost";
441 local_address_ipv4 = htonl(INADDR_LOOPBACK);
446 assert(af == AF_INET6);
448 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
449 canonical = "localhost";
450 additional_from_hostname = true;
455 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
456 if (n_addresses > 0) {
457 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
461 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
469 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
470 if (n_addresses > 0) {
471 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
475 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
476 canonical = "gateway";
483 *h_errnop = HOST_NOT_FOUND;
485 return NSS_STATUS_NOTFOUND;
488 if (!canonical || (!additional && additional_from_hostname)) {
489 hn = gethostname_malloc();
492 *h_errnop = NO_RECOVERY;
493 return NSS_STATUS_TRYAGAIN;
499 if (!additional && additional_from_hostname)
503 return fill_in_hostent(
504 canonical, additional,
506 addresses, n_addresses,
515 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
516 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);