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 } else if (streq(name, "gateway")) {
83 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
84 if (n_addresses <= 0) {
86 *h_errnop = HOST_NOT_FOUND;
87 return NSS_STATUS_NOTFOUND;
90 canonical = "gateway";
93 hn = gethostname_malloc();
96 *h_errnop = NO_RECOVERY;
97 return NSS_STATUS_TRYAGAIN;
100 /* We respond to our local host name, our our hostname suffixed with a single dot. */
101 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
103 *h_errnop = HOST_NOT_FOUND;
104 return NSS_STATUS_NOTFOUND;
107 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
112 local_address_ipv4 = LOCALADDRESS_IPV4;
115 /* If this call fails we fill in 0 as scope. Which is fine */
116 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
118 l = strlen(canonical);
119 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
122 *h_errnop = NO_RECOVERY;
123 return NSS_STATUS_TRYAGAIN;
126 /* First, fill in hostname */
128 memcpy(r_name, canonical, l+1);
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;
341 if (af != AF_INET && af != AF_INET6) {
342 *errnop = EAFNOSUPPORT;
344 return NSS_STATUS_UNAVAIL;
347 if (is_localhost(name)) {
348 canonical = "localhost";
349 local_address_ipv4 = htonl(INADDR_LOOPBACK);
351 } else if (streq(name, "gateway")) {
353 n_addresses = local_gateways(NULL, 0, af, &addresses);
354 if (n_addresses <= 0) {
356 *h_errnop = HOST_NOT_FOUND;
357 return NSS_STATUS_NOTFOUND;
360 canonical = "gateway";
363 hn = gethostname_malloc();
366 *h_errnop = NO_RECOVERY;
367 return NSS_STATUS_TRYAGAIN;
370 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
372 *h_errnop = HOST_NOT_FOUND;
373 return NSS_STATUS_NOTFOUND;
376 n_addresses = local_addresses(NULL, 0, af, &addresses);
381 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
382 local_address_ipv4 = LOCALADDRESS_IPV4;
385 return fill_in_hostent(
386 canonical, additional,
388 addresses, n_addresses,
397 enum nss_status _nss_myhostname_gethostbyaddr2_r(
398 const void* addr, socklen_t len,
400 struct hostent *host,
401 char *buffer, size_t buflen,
402 int *errnop, int *h_errnop,
405 const char *canonical = NULL, *additional = NULL;
406 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
407 _cleanup_free_ struct local_address *addresses = NULL;
408 _cleanup_free_ char *hn = NULL;
410 struct local_address *a;
419 if (!IN_SET(af, AF_INET, AF_INET6)) {
420 *errnop = EAFNOSUPPORT;
422 return NSS_STATUS_UNAVAIL;
425 if (len != FAMILY_ADDRESS_SIZE(af)) {
427 *h_errnop = NO_RECOVERY;
428 return NSS_STATUS_UNAVAIL;
433 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
436 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
437 canonical = "localhost";
438 local_address_ipv4 = htonl(INADDR_LOOPBACK);
443 assert(af == AF_INET6);
445 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
446 additional = "localhost";
452 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
453 if (n_addresses > 0) {
454 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
458 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
460 hn = gethostname_malloc();
463 *h_errnop = NO_RECOVERY;
464 return NSS_STATUS_TRYAGAIN;
476 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
477 if (n_addresses > 0) {
478 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
482 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
484 canonical = "gateway";
491 *h_errnop = HOST_NOT_FOUND;
493 return NSS_STATUS_NOTFOUND;
497 return fill_in_hostent(
498 canonical, additional,
500 addresses, n_addresses,
510 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
511 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);