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>
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,
102 char hn[HOST_NAME_MAX+1] = {};
103 const char *canonical = NULL;
106 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
107 struct address *addresses = NULL, *a;
108 unsigned n_addresses = 0, n;
109 uint32_t local_address_ipv4;
111 if (strcasecmp(name, "localhost") == 0) {
112 /* We respond to 'localhost', so that /etc/hosts
115 canonical = "localhost";
116 local_address_ipv4 = htonl(INADDR_LOOPBACK);
118 /* We respond to our local host name */
120 if (gethostname(hn, sizeof(hn)-1) < 0) {
122 *h_errnop = NO_RECOVERY;
123 return NSS_STATUS_UNAVAIL;
126 if (strcasecmp(name, hn) != 0) {
128 *h_errnop = HOST_NOT_FOUND;
129 return NSS_STATUS_NOTFOUND;
132 /* If this fails, n_addresses is 0. Which is fine */
133 ifconf_acquire_addresses(&addresses, &n_addresses);
136 local_address_ipv4 = LOCALADDRESS_IPV4;
139 /* If this call fails we fill in 0 as scope. Which is fine */
140 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
142 l = strlen(canonical);
143 ms = ALIGN(l+1)+ALIGN(sizeof(struct gaih_addrtuple))*(n_addresses > 0 ? n_addresses : 2);
146 *h_errnop = NO_RECOVERY;
148 return NSS_STATUS_TRYAGAIN;
151 /* First, fill in hostname */
153 memcpy(r_name, canonical, l+1);
156 if (n_addresses <= 0) {
157 /* Second, fill in IPv6 tuple */
158 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
159 r_tuple->next = r_tuple_prev;
160 r_tuple->name = r_name;
161 r_tuple->family = AF_INET6;
162 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
163 r_tuple->scopeid = (uint32_t) lo_ifi;
165 idx += ALIGN(sizeof(struct gaih_addrtuple));
166 r_tuple_prev = r_tuple;
168 /* Third, fill in IPv4 tuple */
169 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
170 r_tuple->next = r_tuple_prev;
171 r_tuple->name = r_name;
172 r_tuple->family = AF_INET;
173 *(uint32_t*) r_tuple->addr = local_address_ipv4;
174 r_tuple->scopeid = (uint32_t) lo_ifi;
176 idx += ALIGN(sizeof(struct gaih_addrtuple));
177 r_tuple_prev = r_tuple;
180 /* Fourth, fill actual addresses in, but in backwards order */
181 for (a = addresses + n_addresses - 1, n = 0; n < n_addresses; n++, a--) {
182 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
183 r_tuple->next = r_tuple_prev;
184 r_tuple->name = r_name;
185 r_tuple->family = a->family;
186 r_tuple->scopeid = a->ifindex;
187 memcpy(r_tuple->addr, a->address, 16);
189 idx += ALIGN(sizeof(struct gaih_addrtuple));
190 r_tuple_prev = r_tuple;
193 /* Verify the size matches */
196 /* Nscd expects us to store the first record in **pat. */
198 **pat = *r_tuple_prev;
207 return NSS_STATUS_SUCCESS;
210 static enum nss_status fill_in_hostent(
211 const char *canonical, const char *additional,
213 struct address *addresses, unsigned n_addresses,
214 uint32_t local_address_ipv4,
215 struct hostent *result,
216 char *buffer, size_t buflen,
217 int *errnop, int *h_errnop,
221 size_t l_canonical, l_additional, idx, ms;
222 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
227 alen = PROTO_ADDRESS_SIZE(af);
229 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
233 l_canonical = strlen(canonical);
234 l_additional = additional ? strlen(additional) : 0;
235 ms = ALIGN(l_canonical+1)+
236 (additional ? ALIGN(l_additional+1) : 0) +
238 (additional ? sizeof(char*) : 0) +
239 (c > 0 ? c : 1)*ALIGN(alen)+
240 (c > 0 ? c+1 : 2)*sizeof(char*);
244 *h_errnop = NO_RECOVERY;
246 return NSS_STATUS_TRYAGAIN;
249 /* First, fill in hostnames */
251 memcpy(r_name, canonical, l_canonical+1);
252 idx = ALIGN(l_canonical+1);
255 r_alias = buffer + idx;
256 memcpy(r_alias, additional, l_additional+1);
257 idx += ALIGN(l_additional+1);
260 /* Second, create aliases array */
261 r_aliases = buffer + idx;
263 ((char**) r_aliases)[0] = r_alias;
264 ((char**) r_aliases)[1] = NULL;
265 idx += 2*sizeof(char*);
267 ((char**) r_aliases)[0] = NULL;
268 idx += sizeof(char*);
271 /* Third, add addresses */
272 r_addr = buffer + idx;
276 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
280 memcpy(r_addr + i*ALIGN(alen), a->address, alen);
285 idx += c*ALIGN(alen);
288 *(uint32_t*) r_addr = local_address_ipv4;
290 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
295 /* Fourth, add address pointer array */
296 r_addr_list = buffer + idx;
300 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
304 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
309 ((char**) r_addr_list)[c] = NULL;
310 idx += (c+1)*sizeof(char*);
313 ((char**) r_addr_list)[0] = r_addr;
314 ((char**) r_addr_list)[1] = NULL;
315 idx += 2*sizeof(char*);
318 /* Verify the size matches */
321 result->h_name = r_name;
322 result->h_aliases = (char**) r_aliases;
323 result->h_addrtype = af;
324 result->h_length = alen;
325 result->h_addr_list = (char**) r_addr_list;
335 return NSS_STATUS_SUCCESS;
338 enum nss_status _nss_myhostname_gethostbyname3_r(
341 struct hostent *host,
342 char *buffer, size_t buflen,
343 int *errnop, int *h_errnop,
347 char hn[HOST_NAME_MAX+1] = {};
348 struct address *addresses = NULL;
349 unsigned n_addresses = 0;
350 const char *canonical, *additional = NULL;
351 uint32_t local_address_ipv4;
356 if (af != AF_INET && af != AF_INET6) {
357 *errnop = EAFNOSUPPORT;
359 return NSS_STATUS_UNAVAIL;
362 if (strcasecmp(name, "localhost") == 0) {
363 canonical = "localhost";
364 local_address_ipv4 = htonl(INADDR_LOOPBACK);
366 if (gethostname(hn, sizeof(hn)-1) < 0) {
368 *h_errnop = NO_RECOVERY;
369 return NSS_STATUS_UNAVAIL;
372 if (strcasecmp(name, hn) != 0) {
374 *h_errnop = HOST_NOT_FOUND;
375 return NSS_STATUS_NOTFOUND;
378 ifconf_acquire_addresses(&addresses, &n_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_gethostbyname2_r(
400 struct hostent *host,
401 char *buffer, size_t buflen,
402 int *errnop, int *h_errnop) {
404 return _nss_myhostname_gethostbyname3_r(
414 enum nss_status _nss_myhostname_gethostbyname_r(
416 struct hostent *host,
417 char *buffer, size_t buflen,
418 int *errnop, int *h_errnop) {
420 return _nss_myhostname_gethostbyname3_r(
430 enum nss_status _nss_myhostname_gethostbyaddr2_r(
431 const void* addr, socklen_t len,
433 struct hostent *host,
434 char *buffer, size_t buflen,
435 int *errnop, int *h_errnop,
438 char hn[HOST_NAME_MAX+1] = {};
439 struct address *addresses = NULL;
441 unsigned n_addresses = 0, n;
442 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
443 const char *canonical = NULL, *additional = NULL;
445 if (len != PROTO_ADDRESS_SIZE(af)) {
447 *h_errnop = NO_RECOVERY;
448 return NSS_STATUS_UNAVAIL;
453 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
456 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
457 canonical = "localhost";
458 local_address_ipv4 = htonl(INADDR_LOOPBACK);
462 } else if (af == AF_INET6) {
464 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
465 additional = "localhost";
470 *errnop = EAFNOSUPPORT;
472 return NSS_STATUS_UNAVAIL;
475 ifconf_acquire_addresses(&addresses, &n_addresses);
477 for (a = addresses, n = 0; n < n_addresses; n++, a++) {
481 if (memcmp(addr, a->address, PROTO_ADDRESS_SIZE(af)) == 0)
486 *h_errnop = HOST_NOT_FOUND;
490 return NSS_STATUS_NOTFOUND;
494 if (gethostname(hn, sizeof(hn)-1) < 0) {
496 *h_errnop = NO_RECOVERY;
500 return NSS_STATUS_UNAVAIL;
506 return fill_in_hostent(
507 canonical, additional,
509 addresses, n_addresses,
519 enum nss_status _nss_myhostname_gethostbyaddr_r(
520 const void* addr, socklen_t len,
522 struct hostent *host,
523 char *buffer, size_t buflen,
524 int *errnop, int *h_errnop) {
526 return _nss_myhostname_gethostbyaddr2_r(