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 "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 address *addresses = NULL;
103 _cleanup_free_ char *hn = NULL;
104 const char *canonical = NULL;
105 unsigned n_addresses = 0, n;
106 uint32_t local_address_ipv4;
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 /* If this fails, n_addresses is 0. Which is fine */
140 acquire_addresses(&addresses, &n_addresses);
143 local_address_ipv4 = LOCALADDRESS_IPV4;
146 /* If this call fails we fill in 0 as scope. Which is fine */
147 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
149 l = strlen(canonical);
150 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
153 *h_errnop = NO_RECOVERY;
154 return NSS_STATUS_TRYAGAIN;
157 /* First, fill in hostname */
159 memcpy(r_name, canonical, l+1);
162 if (n_addresses <= 0) {
163 /* Second, fill in IPv6 tuple */
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 = AF_INET6;
168 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
169 r_tuple->scopeid = (uint32_t) lo_ifi;
171 idx += ALIGN(sizeof(struct gaih_addrtuple));
172 r_tuple_prev = r_tuple;
174 /* Third, fill in IPv4 tuple */
175 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
176 r_tuple->next = r_tuple_prev;
177 r_tuple->name = r_name;
178 r_tuple->family = AF_INET;
179 *(uint32_t*) r_tuple->addr = local_address_ipv4;
180 r_tuple->scopeid = (uint32_t) lo_ifi;
182 idx += ALIGN(sizeof(struct gaih_addrtuple));
183 r_tuple_prev = r_tuple;
186 /* Fourth, fill actual addresses in, but in backwards order */
187 for (a = addresses + n_addresses - 1, n = 0; n < n_addresses; n++, a--) {
188 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
189 r_tuple->next = r_tuple_prev;
190 r_tuple->name = r_name;
191 r_tuple->family = a->family;
192 r_tuple->scopeid = a->ifindex;
193 memcpy(r_tuple->addr, &a->address, 16);
195 idx += ALIGN(sizeof(struct gaih_addrtuple));
196 r_tuple_prev = r_tuple;
199 /* Verify the size matches */
202 /* Nscd expects us to store the first record in **pat. */
204 **pat = *r_tuple_prev;
211 return NSS_STATUS_SUCCESS;
214 static enum nss_status fill_in_hostent(
215 const char *canonical, const char *additional,
217 struct address *addresses, unsigned n_addresses,
218 uint32_t local_address_ipv4,
219 struct hostent *result,
220 char *buffer, size_t buflen,
221 int *errnop, int *h_errnop,
225 size_t l_canonical, l_additional, idx, ms;
226 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
237 alen = PROTO_ADDRESS_SIZE(af);
239 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
243 l_canonical = strlen(canonical);
244 l_additional = additional ? strlen(additional) : 0;
245 ms = ALIGN(l_canonical+1)+
246 (additional ? ALIGN(l_additional+1) : 0) +
248 (additional ? sizeof(char*) : 0) +
249 (c > 0 ? c : 1) * ALIGN(alen)+
250 (c > 0 ? c+1 : 2) * sizeof(char*);
254 *h_errnop = NO_RECOVERY;
255 return NSS_STATUS_TRYAGAIN;
258 /* First, fill in hostnames */
260 memcpy(r_name, canonical, l_canonical+1);
261 idx = ALIGN(l_canonical+1);
264 r_alias = buffer + idx;
265 memcpy(r_alias, additional, l_additional+1);
266 idx += ALIGN(l_additional+1);
269 /* Second, create aliases array */
270 r_aliases = buffer + idx;
272 ((char**) r_aliases)[0] = r_alias;
273 ((char**) r_aliases)[1] = NULL;
274 idx += 2*sizeof(char*);
276 ((char**) r_aliases)[0] = NULL;
277 idx += sizeof(char*);
280 /* Third, add addresses */
281 r_addr = buffer + idx;
285 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
289 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
294 idx += c*ALIGN(alen);
297 *(uint32_t*) r_addr = local_address_ipv4;
299 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
304 /* Fourth, add address pointer array */
305 r_addr_list = buffer + idx;
309 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
313 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
318 ((char**) r_addr_list)[c] = NULL;
319 idx += (c+1)*sizeof(char*);
322 ((char**) r_addr_list)[0] = r_addr;
323 ((char**) r_addr_list)[1] = NULL;
324 idx += 2*sizeof(char*);
327 /* Verify the size matches */
330 result->h_name = r_name;
331 result->h_aliases = (char**) r_aliases;
332 result->h_addrtype = af;
333 result->h_length = alen;
334 result->h_addr_list = (char**) r_addr_list;
342 return NSS_STATUS_SUCCESS;
345 enum nss_status _nss_myhostname_gethostbyname3_r(
348 struct hostent *host,
349 char *buffer, size_t buflen,
350 int *errnop, int *h_errnop,
354 _cleanup_free_ struct address *addresses = NULL;
355 const char *canonical, *additional = NULL;
356 _cleanup_free_ char *hn = NULL;
357 uint32_t local_address_ipv4;
358 unsigned n_addresses = 0;
369 if (af != AF_INET && af != AF_INET6) {
370 *errnop = EAFNOSUPPORT;
372 return NSS_STATUS_UNAVAIL;
375 if (is_localhost(name)) {
376 canonical = "localhost";
377 local_address_ipv4 = htonl(INADDR_LOOPBACK);
379 hn = gethostname_malloc();
382 *h_errnop = NO_RECOVERY;
383 return NSS_STATUS_TRYAGAIN;
386 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
388 *h_errnop = HOST_NOT_FOUND;
389 return NSS_STATUS_NOTFOUND;
392 acquire_addresses(&addresses, &n_addresses);
395 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
396 local_address_ipv4 = LOCALADDRESS_IPV4;
399 return fill_in_hostent(
400 canonical, additional,
402 addresses, n_addresses,
411 enum nss_status _nss_myhostname_gethostbyname2_r(
414 struct hostent *host,
415 char *buffer, size_t buflen,
416 int *errnop, int *h_errnop) {
418 return _nss_myhostname_gethostbyname3_r(
428 enum nss_status _nss_myhostname_gethostbyname_r(
430 struct hostent *host,
431 char *buffer, size_t buflen,
432 int *errnop, int *h_errnop) {
434 return _nss_myhostname_gethostbyname3_r(
444 enum nss_status _nss_myhostname_gethostbyaddr2_r(
445 const void* addr, socklen_t len,
447 struct hostent *host,
448 char *buffer, size_t buflen,
449 int *errnop, int *h_errnop,
452 const char *canonical = NULL, *additional = NULL;
453 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
454 _cleanup_free_ struct address *addresses = NULL;
455 _cleanup_free_ char *hn = NULL;
456 unsigned n_addresses = 0, n;
465 if (len != PROTO_ADDRESS_SIZE(af)) {
467 *h_errnop = NO_RECOVERY;
468 return NSS_STATUS_UNAVAIL;
473 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
476 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
477 canonical = "localhost";
478 local_address_ipv4 = htonl(INADDR_LOOPBACK);
482 } else if (af == AF_INET6) {
484 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
485 additional = "localhost";
490 *errnop = EAFNOSUPPORT;
492 return NSS_STATUS_UNAVAIL;
495 acquire_addresses(&addresses, &n_addresses);
497 for (a = addresses, n = 0; n < n_addresses; n++, a++) {
501 if (memcmp(addr, &a->address, PROTO_ADDRESS_SIZE(af)) == 0)
506 *h_errnop = HOST_NOT_FOUND;
508 return NSS_STATUS_NOTFOUND;
512 hn = gethostname_malloc();
515 *h_errnop = NO_RECOVERY;
516 return NSS_STATUS_TRYAGAIN;
522 return fill_in_hostent(
523 canonical, additional,
525 addresses, n_addresses,
535 enum nss_status _nss_myhostname_gethostbyaddr_r(
536 const void* addr, socklen_t len,
538 struct hostent *host,
539 char *buffer, size_t buflen,
540 int *errnop, int *h_errnop) {
542 return _nss_myhostname_gethostbyaddr2_r(