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"
36 /* Ensure that glibc's assert is used. We cannot use assert from macro.h, as
37 * libnss_myhostname will be linked into arbitrary programs which will, in turn
38 * attempt to write to the journal via log_dispatch() */
41 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
42 * 127.0.0.1 that it can be translated back to the local hostname. For
43 * IPv6 we use ::1 which unfortunately will not translate back to the
44 * hostname but instead something like "localhost6" or so. */
46 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
47 #define LOCALADDRESS_IPV6 &in6addr_loopback
48 #define LOOPBACK_INTERFACE "lo"
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,
55 int32_t *ttlp) _public_;
57 enum nss_status _nss_myhostname_gethostbyname3_r(
61 char *buffer, size_t buflen,
62 int *errnop, int *h_errnop,
64 char **canonp) _public_;
66 enum nss_status _nss_myhostname_gethostbyname2_r(
70 char *buffer, size_t buflen,
71 int *errnop, int *h_errnop) _public_;
73 enum nss_status _nss_myhostname_gethostbyname_r(
76 char *buffer, size_t buflen,
77 int *errnop, int *h_errnop) _public_;
79 enum nss_status _nss_myhostname_gethostbyaddr2_r(
80 const void* addr, socklen_t len,
83 char *buffer, size_t buflen,
84 int *errnop, int *h_errnop,
85 int32_t *ttlp) _public_;
87 enum nss_status _nss_myhostname_gethostbyaddr_r(
88 const void* addr, socklen_t len,
91 char *buffer, size_t buflen,
92 int *errnop, int *h_errnop) _public_;
94 enum nss_status _nss_myhostname_gethostbyname4_r(
96 struct gaih_addrtuple **pat,
97 char *buffer, size_t buflen,
98 int *errnop, int *h_errnop,
101 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
102 _cleanup_free_ struct local_address *addresses = NULL;
103 _cleanup_free_ char *hn = NULL;
104 const char *canonical = NULL;
105 int n_addresses = 0, lo_ifi;
106 uint32_t local_address_ipv4;
107 struct local_address *a;
118 if (is_localhost(name)) {
119 /* We respond to 'localhost', so that /etc/hosts
122 canonical = "localhost";
123 local_address_ipv4 = htonl(INADDR_LOOPBACK);
125 hn = gethostname_malloc();
128 *h_errnop = NO_RECOVERY;
129 return NSS_STATUS_TRYAGAIN;
132 /* We respond to our local host name, our our hostname suffixed with a single dot. */
133 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
135 *h_errnop = HOST_NOT_FOUND;
136 return NSS_STATUS_NOTFOUND;
139 n_addresses = local_addresses(&addresses);
144 local_address_ipv4 = LOCALADDRESS_IPV4;
147 /* If this call fails we fill in 0 as scope. Which is fine */
148 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
150 l = strlen(canonical);
151 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
154 *h_errnop = NO_RECOVERY;
155 return NSS_STATUS_TRYAGAIN;
158 /* First, fill in hostname */
160 memcpy(r_name, canonical, l+1);
163 if (n_addresses <= 0) {
164 /* Second, fill in IPv6 tuple */
165 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
166 r_tuple->next = r_tuple_prev;
167 r_tuple->name = r_name;
168 r_tuple->family = AF_INET6;
169 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
170 r_tuple->scopeid = (uint32_t) lo_ifi;
172 idx += ALIGN(sizeof(struct gaih_addrtuple));
173 r_tuple_prev = r_tuple;
175 /* Third, fill in IPv4 tuple */
176 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
177 r_tuple->next = r_tuple_prev;
178 r_tuple->name = r_name;
179 r_tuple->family = AF_INET;
180 *(uint32_t*) r_tuple->addr = local_address_ipv4;
181 r_tuple->scopeid = (uint32_t) lo_ifi;
183 idx += ALIGN(sizeof(struct gaih_addrtuple));
184 r_tuple_prev = r_tuple;
187 /* Fourth, fill actual addresses in, but in backwards order */
188 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
189 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
190 r_tuple->next = r_tuple_prev;
191 r_tuple->name = r_name;
192 r_tuple->family = a->family;
193 r_tuple->scopeid = a->ifindex;
194 memcpy(r_tuple->addr, &a->address, 16);
196 idx += ALIGN(sizeof(struct gaih_addrtuple));
197 r_tuple_prev = r_tuple;
200 /* Verify the size matches */
203 /* Nscd expects us to store the first record in **pat. */
205 **pat = *r_tuple_prev;
212 return NSS_STATUS_SUCCESS;
215 static enum nss_status fill_in_hostent(
216 const char *canonical, const char *additional,
218 struct local_address *addresses, unsigned n_addresses,
219 uint32_t local_address_ipv4,
220 struct hostent *result,
221 char *buffer, size_t buflen,
222 int *errnop, int *h_errnop,
226 size_t l_canonical, l_additional, idx, ms;
227 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
229 struct local_address *a;
238 alen = PROTO_ADDRESS_SIZE(af);
240 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
244 l_canonical = strlen(canonical);
245 l_additional = additional ? strlen(additional) : 0;
246 ms = ALIGN(l_canonical+1)+
247 (additional ? ALIGN(l_additional+1) : 0) +
249 (additional ? sizeof(char*) : 0) +
250 (c > 0 ? c : 1) * ALIGN(alen)+
251 (c > 0 ? c+1 : 2) * sizeof(char*);
255 *h_errnop = NO_RECOVERY;
256 return NSS_STATUS_TRYAGAIN;
259 /* First, fill in hostnames */
261 memcpy(r_name, canonical, l_canonical+1);
262 idx = ALIGN(l_canonical+1);
265 r_alias = buffer + idx;
266 memcpy(r_alias, additional, l_additional+1);
267 idx += ALIGN(l_additional+1);
270 /* Second, create aliases array */
271 r_aliases = buffer + idx;
273 ((char**) r_aliases)[0] = r_alias;
274 ((char**) r_aliases)[1] = NULL;
275 idx += 2*sizeof(char*);
277 ((char**) r_aliases)[0] = NULL;
278 idx += sizeof(char*);
281 /* Third, add addresses */
282 r_addr = buffer + idx;
286 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
290 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
295 idx += c*ALIGN(alen);
298 *(uint32_t*) r_addr = local_address_ipv4;
300 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
305 /* Fourth, add address pointer array */
306 r_addr_list = buffer + idx;
310 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
314 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
319 ((char**) r_addr_list)[c] = NULL;
320 idx += (c+1)*sizeof(char*);
323 ((char**) r_addr_list)[0] = r_addr;
324 ((char**) r_addr_list)[1] = NULL;
325 idx += 2*sizeof(char*);
328 /* Verify the size matches */
331 result->h_name = r_name;
332 result->h_aliases = (char**) r_aliases;
333 result->h_addrtype = af;
334 result->h_length = alen;
335 result->h_addr_list = (char**) r_addr_list;
343 return NSS_STATUS_SUCCESS;
346 enum nss_status _nss_myhostname_gethostbyname3_r(
349 struct hostent *host,
350 char *buffer, size_t buflen,
351 int *errnop, int *h_errnop,
355 _cleanup_free_ struct local_address *addresses = NULL;
356 const char *canonical, *additional = NULL;
357 _cleanup_free_ char *hn = NULL;
358 uint32_t local_address_ipv4;
370 if (af != AF_INET && af != AF_INET6) {
371 *errnop = EAFNOSUPPORT;
373 return NSS_STATUS_UNAVAIL;
376 if (is_localhost(name)) {
377 canonical = "localhost";
378 local_address_ipv4 = htonl(INADDR_LOOPBACK);
380 hn = gethostname_malloc();
383 *h_errnop = NO_RECOVERY;
384 return NSS_STATUS_TRYAGAIN;
387 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
389 *h_errnop = HOST_NOT_FOUND;
390 return NSS_STATUS_NOTFOUND;
393 n_addresses = local_addresses(&addresses);
398 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
399 local_address_ipv4 = LOCALADDRESS_IPV4;
402 return fill_in_hostent(
403 canonical, additional,
405 addresses, n_addresses,
414 enum nss_status _nss_myhostname_gethostbyname2_r(
417 struct hostent *host,
418 char *buffer, size_t buflen,
419 int *errnop, int *h_errnop) {
421 return _nss_myhostname_gethostbyname3_r(
431 enum nss_status _nss_myhostname_gethostbyname_r(
433 struct hostent *host,
434 char *buffer, size_t buflen,
435 int *errnop, int *h_errnop) {
437 return _nss_myhostname_gethostbyname3_r(
447 enum nss_status _nss_myhostname_gethostbyaddr2_r(
448 const void* addr, socklen_t len,
450 struct hostent *host,
451 char *buffer, size_t buflen,
452 int *errnop, int *h_errnop,
455 const char *canonical = NULL, *additional = NULL;
456 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
457 _cleanup_free_ struct local_address *addresses = NULL;
458 _cleanup_free_ char *hn = NULL;
460 struct local_address *a;
469 if (len != PROTO_ADDRESS_SIZE(af)) {
471 *h_errnop = NO_RECOVERY;
472 return NSS_STATUS_UNAVAIL;
477 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
480 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
481 canonical = "localhost";
482 local_address_ipv4 = htonl(INADDR_LOOPBACK);
486 } else if (af == AF_INET6) {
488 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
489 additional = "localhost";
494 *errnop = EAFNOSUPPORT;
496 return NSS_STATUS_UNAVAIL;
499 n_addresses = local_addresses(&addresses);
503 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
507 if (memcmp(addr, &a->address, PROTO_ADDRESS_SIZE(af)) == 0)
512 *h_errnop = HOST_NOT_FOUND;
514 return NSS_STATUS_NOTFOUND;
518 hn = gethostname_malloc();
521 *h_errnop = NO_RECOVERY;
522 return NSS_STATUS_TRYAGAIN;
528 return fill_in_hostent(
529 canonical, additional,
531 addresses, n_addresses,
541 enum nss_status _nss_myhostname_gethostbyaddr_r(
542 const void* addr, socklen_t len,
544 struct hostent *host,
545 char *buffer, size_t buflen,
546 int *errnop, int *h_errnop) {
548 return _nss_myhostname_gethostbyaddr2_r(