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 "localhost" 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 static bool is_gateway(const char *hostname) {
53 return streq(hostname, "gateway") ||
54 streq(hostname, "gateway.");
57 enum nss_status _nss_myhostname_gethostbyname4_r(
59 struct gaih_addrtuple **pat,
60 char *buffer, size_t buflen,
61 int *errnop, int *h_errnop,
64 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
65 _cleanup_free_ struct local_address *addresses = NULL;
66 _cleanup_free_ char *hn = NULL;
67 const char *canonical = NULL;
68 int n_addresses = 0, lo_ifi;
69 uint32_t local_address_ipv4;
70 struct local_address *a;
81 if (is_localhost(name)) {
82 /* We respond to 'localhost', so that /etc/hosts
85 canonical = "localhost";
86 local_address_ipv4 = htonl(INADDR_LOOPBACK);
88 } else if (is_gateway(name)) {
90 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
91 if (n_addresses <= 0) {
93 *h_errnop = HOST_NOT_FOUND;
94 return NSS_STATUS_NOTFOUND;
97 canonical = "gateway";
100 hn = gethostname_malloc();
103 *h_errnop = NO_RECOVERY;
104 return NSS_STATUS_TRYAGAIN;
107 /* We respond to our local host name, our our hostname suffixed with a single dot. */
108 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
110 *h_errnop = HOST_NOT_FOUND;
111 return NSS_STATUS_NOTFOUND;
114 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
119 local_address_ipv4 = LOCALADDRESS_IPV4;
122 /* If this call fails we fill in 0 as scope. Which is fine */
123 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
125 l = strlen(canonical);
126 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
129 *h_errnop = NO_RECOVERY;
130 return NSS_STATUS_TRYAGAIN;
133 /* First, fill in hostname */
135 memcpy(r_name, canonical, l+1);
138 if (n_addresses <= 0) {
139 /* Second, fill in IPv6 tuple */
140 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
141 r_tuple->next = r_tuple_prev;
142 r_tuple->name = r_name;
143 r_tuple->family = AF_INET6;
144 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
145 r_tuple->scopeid = (uint32_t) lo_ifi;
147 idx += ALIGN(sizeof(struct gaih_addrtuple));
148 r_tuple_prev = r_tuple;
150 /* Third, fill in IPv4 tuple */
151 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
152 r_tuple->next = r_tuple_prev;
153 r_tuple->name = r_name;
154 r_tuple->family = AF_INET;
155 *(uint32_t*) r_tuple->addr = local_address_ipv4;
156 r_tuple->scopeid = (uint32_t) lo_ifi;
158 idx += ALIGN(sizeof(struct gaih_addrtuple));
159 r_tuple_prev = r_tuple;
162 /* Fourth, fill actual addresses in, but in backwards order */
163 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
164 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
165 r_tuple->next = r_tuple_prev;
166 r_tuple->name = r_name;
167 r_tuple->family = a->family;
168 r_tuple->scopeid = a->ifindex;
169 memcpy(r_tuple->addr, &a->address, 16);
171 idx += ALIGN(sizeof(struct gaih_addrtuple));
172 r_tuple_prev = r_tuple;
175 /* Verify the size matches */
178 /* Nscd expects us to store the first record in **pat. */
180 **pat = *r_tuple_prev;
187 /* Explicitly reset all error variables */
189 *h_errnop = NETDB_SUCCESS;
192 return NSS_STATUS_SUCCESS;
195 static enum nss_status fill_in_hostent(
196 const char *canonical, const char *additional,
198 struct local_address *addresses, unsigned n_addresses,
199 uint32_t local_address_ipv4,
200 struct hostent *result,
201 char *buffer, size_t buflen,
202 int *errnop, int *h_errnop,
206 size_t l_canonical, l_additional, idx, ms, alen;
207 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
208 struct local_address *a;
217 alen = FAMILY_ADDRESS_SIZE(af);
219 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
223 l_canonical = strlen(canonical);
224 l_additional = additional ? strlen(additional) : 0;
225 ms = ALIGN(l_canonical+1)+
226 (additional ? ALIGN(l_additional+1) : 0) +
228 (additional ? sizeof(char*) : 0) +
229 (c > 0 ? c : 1) * ALIGN(alen) +
230 (c > 0 ? c+1 : 2) * sizeof(char*);
234 *h_errnop = NO_RECOVERY;
235 return NSS_STATUS_TRYAGAIN;
238 /* First, fill in hostnames */
240 memcpy(r_name, canonical, l_canonical+1);
241 idx = ALIGN(l_canonical+1);
244 r_alias = buffer + idx;
245 memcpy(r_alias, additional, l_additional+1);
246 idx += ALIGN(l_additional+1);
249 /* Second, create aliases array */
250 r_aliases = buffer + idx;
252 ((char**) r_aliases)[0] = r_alias;
253 ((char**) r_aliases)[1] = NULL;
254 idx += 2*sizeof(char*);
256 ((char**) r_aliases)[0] = NULL;
257 idx += sizeof(char*);
260 /* Third, add addresses */
261 r_addr = buffer + idx;
265 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
269 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
274 idx += c*ALIGN(alen);
277 *(uint32_t*) r_addr = local_address_ipv4;
279 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
284 /* Fourth, add address pointer array */
285 r_addr_list = buffer + idx;
289 for (i = 0; i < c; i++)
290 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
292 ((char**) r_addr_list)[i] = NULL;
293 idx += (c+1) * sizeof(char*);
296 ((char**) r_addr_list)[0] = r_addr;
297 ((char**) r_addr_list)[1] = NULL;
298 idx += 2 * sizeof(char*);
301 /* Verify the size matches */
304 result->h_name = r_name;
305 result->h_aliases = (char**) r_aliases;
306 result->h_addrtype = af;
307 result->h_length = alen;
308 result->h_addr_list = (char**) r_addr_list;
316 /* Explicitly reset all error variables */
318 *h_errnop = NETDB_SUCCESS;
321 return NSS_STATUS_SUCCESS;
324 enum nss_status _nss_myhostname_gethostbyname3_r(
327 struct hostent *host,
328 char *buffer, size_t buflen,
329 int *errnop, int *h_errnop,
333 _cleanup_free_ struct local_address *addresses = NULL;
334 const char *canonical, *additional = NULL;
335 _cleanup_free_ char *hn = NULL;
336 uint32_t local_address_ipv4 = 0;
348 if (af != AF_INET && af != AF_INET6) {
349 *errnop = EAFNOSUPPORT;
351 return NSS_STATUS_UNAVAIL;
354 if (is_localhost(name)) {
355 canonical = "localhost";
356 local_address_ipv4 = htonl(INADDR_LOOPBACK);
358 } else if (is_gateway(name)) {
360 n_addresses = local_gateways(NULL, 0, af, &addresses);
361 if (n_addresses <= 0) {
363 *h_errnop = HOST_NOT_FOUND;
364 return NSS_STATUS_NOTFOUND;
367 canonical = "gateway";
370 hn = gethostname_malloc();
373 *h_errnop = NO_RECOVERY;
374 return NSS_STATUS_TRYAGAIN;
377 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
379 *h_errnop = HOST_NOT_FOUND;
380 return NSS_STATUS_NOTFOUND;
383 n_addresses = local_addresses(NULL, 0, af, &addresses);
388 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
389 local_address_ipv4 = LOCALADDRESS_IPV4;
392 return fill_in_hostent(
393 canonical, additional,
395 addresses, n_addresses,
404 enum nss_status _nss_myhostname_gethostbyaddr2_r(
405 const void* addr, socklen_t len,
407 struct hostent *host,
408 char *buffer, size_t buflen,
409 int *errnop, int *h_errnop,
412 const char *canonical = NULL, *additional = NULL;
413 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
414 _cleanup_free_ struct local_address *addresses = NULL;
415 _cleanup_free_ char *hn = NULL;
417 struct local_address *a;
418 bool additional_from_hostname = false;
427 if (!IN_SET(af, AF_INET, AF_INET6)) {
428 *errnop = EAFNOSUPPORT;
430 return NSS_STATUS_UNAVAIL;
433 if (len != FAMILY_ADDRESS_SIZE(af)) {
435 *h_errnop = NO_RECOVERY;
436 return NSS_STATUS_UNAVAIL;
440 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
443 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
444 canonical = "localhost";
445 local_address_ipv4 = htonl(INADDR_LOOPBACK);
450 assert(af == AF_INET6);
452 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
453 canonical = "localhost";
454 additional_from_hostname = true;
459 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
460 if (n_addresses > 0) {
461 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
465 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
473 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
474 if (n_addresses > 0) {
475 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
479 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
480 canonical = "gateway";
487 *h_errnop = HOST_NOT_FOUND;
489 return NSS_STATUS_NOTFOUND;
492 if (!canonical || (!additional && additional_from_hostname)) {
493 hn = gethostname_malloc();
496 *h_errnop = NO_RECOVERY;
497 return NSS_STATUS_TRYAGAIN;
503 if (!additional && additional_from_hostname)
507 return fill_in_hostent(
508 canonical, additional,
510 addresses, n_addresses,
519 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
520 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);