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 /* Explicitly reset all error variables */
170 *h_errnop = NETDB_SUCCESS;
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, alen;
188 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
189 struct local_address *a;
198 alen = FAMILY_ADDRESS_SIZE(af);
200 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
204 l_canonical = strlen(canonical);
205 l_additional = additional ? strlen(additional) : 0;
206 ms = ALIGN(l_canonical+1)+
207 (additional ? ALIGN(l_additional+1) : 0) +
209 (additional ? sizeof(char*) : 0) +
210 (c > 0 ? c : 1) * ALIGN(alen) +
211 (c > 0 ? c+1 : 2) * sizeof(char*);
215 *h_errnop = NO_RECOVERY;
216 return NSS_STATUS_TRYAGAIN;
219 /* First, fill in hostnames */
221 memcpy(r_name, canonical, l_canonical+1);
222 idx = ALIGN(l_canonical+1);
225 r_alias = buffer + idx;
226 memcpy(r_alias, additional, l_additional+1);
227 idx += ALIGN(l_additional+1);
230 /* Second, create aliases array */
231 r_aliases = buffer + idx;
233 ((char**) r_aliases)[0] = r_alias;
234 ((char**) r_aliases)[1] = NULL;
235 idx += 2*sizeof(char*);
237 ((char**) r_aliases)[0] = NULL;
238 idx += sizeof(char*);
241 /* Third, add addresses */
242 r_addr = buffer + idx;
246 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
250 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
255 idx += c*ALIGN(alen);
258 *(uint32_t*) r_addr = local_address_ipv4;
260 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
265 /* Fourth, add address pointer array */
266 r_addr_list = buffer + idx;
270 for (i = 0; i < c; i++)
271 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
273 ((char**) r_addr_list)[i] = NULL;
274 idx += (c+1) * sizeof(char*);
277 ((char**) r_addr_list)[0] = r_addr;
278 ((char**) r_addr_list)[1] = NULL;
279 idx += 2 * sizeof(char*);
282 /* Verify the size matches */
285 result->h_name = r_name;
286 result->h_aliases = (char**) r_aliases;
287 result->h_addrtype = af;
288 result->h_length = alen;
289 result->h_addr_list = (char**) r_addr_list;
297 /* Explicitly reset all error variables */
299 *h_errnop = NETDB_SUCCESS;
302 return NSS_STATUS_SUCCESS;
305 enum nss_status _nss_myhostname_gethostbyname3_r(
308 struct hostent *host,
309 char *buffer, size_t buflen,
310 int *errnop, int *h_errnop,
314 _cleanup_free_ struct local_address *addresses = NULL;
315 const char *canonical, *additional = NULL;
316 _cleanup_free_ char *hn = NULL;
317 uint32_t local_address_ipv4;
329 if (af != AF_INET && af != AF_INET6) {
330 *errnop = EAFNOSUPPORT;
332 return NSS_STATUS_UNAVAIL;
335 if (is_localhost(name)) {
336 canonical = "localhost";
337 local_address_ipv4 = htonl(INADDR_LOOPBACK);
339 hn = gethostname_malloc();
342 *h_errnop = NO_RECOVERY;
343 return NSS_STATUS_TRYAGAIN;
346 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
348 *h_errnop = HOST_NOT_FOUND;
349 return NSS_STATUS_NOTFOUND;
352 n_addresses = local_addresses(&addresses);
357 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
358 local_address_ipv4 = LOCALADDRESS_IPV4;
361 return fill_in_hostent(
362 canonical, additional,
364 addresses, n_addresses,
373 enum nss_status _nss_myhostname_gethostbyaddr2_r(
374 const void* addr, socklen_t len,
376 struct hostent *host,
377 char *buffer, size_t buflen,
378 int *errnop, int *h_errnop,
381 const char *canonical = NULL, *additional = NULL;
382 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
383 _cleanup_free_ struct local_address *addresses = NULL;
384 _cleanup_free_ char *hn = NULL;
386 struct local_address *a;
395 if (!IN_SET(af, AF_INET, AF_INET6)) {
396 *errnop = EAFNOSUPPORT;
398 return NSS_STATUS_UNAVAIL;
401 if (len != FAMILY_ADDRESS_SIZE(af)) {
403 *h_errnop = NO_RECOVERY;
404 return NSS_STATUS_UNAVAIL;
409 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
412 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
413 canonical = "localhost";
414 local_address_ipv4 = htonl(INADDR_LOOPBACK);
419 assert(af == AF_INET6);
421 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
422 additional = "localhost";
428 n_addresses = local_addresses(&addresses);
432 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
436 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
441 *h_errnop = HOST_NOT_FOUND;
443 return NSS_STATUS_NOTFOUND;
447 hn = gethostname_malloc();
450 *h_errnop = NO_RECOVERY;
451 return NSS_STATUS_TRYAGAIN;
457 return fill_in_hostent(
458 canonical, additional,
460 addresses, n_addresses,
470 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
471 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);