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>
32 #include <arpa/inet.h>
36 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
37 * 127.0.0.1 that it can be translated back to the local hostname. For
38 * IPv6 we use ::1 which unfortunately will not translate back to the
39 * hostname but instead something like "localhost6" or so. */
41 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
42 #define LOCALADDRESS_IPV6 &in6addr_loopback
43 #define LOOPBACK_INTERFACE "lo"
45 #define ALIGN(a) (((a+sizeof(void*)-1)/sizeof(void*))*sizeof(void*))
47 enum nss_status _nss_myhostname_gethostbyname4_r(
49 struct gaih_addrtuple **pat,
50 char *buffer, size_t buflen,
51 int *errnop, int *h_errnop,
52 int32_t *ttlp) _public_;
54 enum nss_status _nss_myhostname_gethostbyname3_r(
58 char *buffer, size_t buflen,
59 int *errnop, int *h_errnop,
61 char **canonp) _public_;
63 enum nss_status _nss_myhostname_gethostbyname2_r(
67 char *buffer, size_t buflen,
68 int *errnop, int *h_errnop) _public_;
70 enum nss_status _nss_myhostname_gethostbyname_r(
73 char *buffer, size_t buflen,
74 int *errnop, int *h_errnop) _public_;
76 enum nss_status _nss_myhostname_gethostbyaddr2_r(
77 const void* addr, socklen_t len,
80 char *buffer, size_t buflen,
81 int *errnop, int *h_errnop,
82 int32_t *ttlp) _public_;
84 enum nss_status _nss_myhostname_gethostbyaddr_r(
85 const void* addr, socklen_t len,
88 char *buffer, size_t buflen,
89 int *errnop, int *h_errnop) _public_;
91 enum nss_status _nss_myhostname_gethostbyname4_r(
93 struct gaih_addrtuple **pat,
94 char *buffer, size_t buflen,
95 int *errnop, int *h_errnop,
99 char hn[HOST_NAME_MAX+1];
102 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
103 struct address *addresses = NULL, *a;
104 unsigned n_addresses = 0, n;
106 memset(hn, 0, sizeof(hn));
107 if (gethostname(hn, sizeof(hn)-1) < 0) {
109 *h_errnop = NO_RECOVERY;
110 return NSS_STATUS_UNAVAIL;
113 if (strcasecmp(name, hn) != 0) {
115 *h_errnop = HOST_NOT_FOUND;
116 return NSS_STATUS_NOTFOUND;
119 /* If this fails, n_addresses is 0. Which is fine */
120 ifconf_acquire_addresses(&addresses, &n_addresses);
122 /* If this call fails we fill in 0 as scope. Which is fine */
123 lo_ifi = if_nametoindex(LOOPBACK_INTERFACE);
126 ms = ALIGN(l+1)+ALIGN(sizeof(struct gaih_addrtuple))*(n_addresses > 0 ? n_addresses : 2);
129 *h_errnop = NO_RECOVERY;
131 return NSS_STATUS_TRYAGAIN;
134 /* First, fill in hostname */
136 memcpy(r_name, hn, l+1);
139 if (n_addresses <= 0) {
140 /* Second, fill in IPv6 tuple */
141 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
142 r_tuple->next = r_tuple_prev;
143 r_tuple->name = r_name;
144 r_tuple->family = AF_INET6;
145 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
146 r_tuple->scopeid = (uint32_t) lo_ifi;
148 idx += ALIGN(sizeof(struct gaih_addrtuple));
149 r_tuple_prev = r_tuple;
151 /* Third, fill in IPv4 tuple */
152 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
153 r_tuple->next = r_tuple_prev;
154 r_tuple->name = r_name;
155 r_tuple->family = AF_INET;
156 *(uint32_t*) r_tuple->addr = LOCALADDRESS_IPV4;
157 r_tuple->scopeid = (uint32_t) lo_ifi;
159 idx += ALIGN(sizeof(struct gaih_addrtuple));
160 r_tuple_prev = r_tuple;
163 /* Fourth, fill actual addresses in, but in backwards order */
164 for (a = addresses + n_addresses - 1, n = 0; n < n_addresses; n++, a--) {
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 = a->family;
169 r_tuple->scopeid = a->ifindex;
170 memcpy(r_tuple->addr, a->address, 16);
172 idx += ALIGN(sizeof(struct gaih_addrtuple));
173 r_tuple_prev = r_tuple;
176 /* Verify the size matches */
186 return NSS_STATUS_SUCCESS;
189 static enum nss_status fill_in_hostent(
192 struct hostent *result,
193 char *buffer, size_t buflen,
194 int *errnop, int *h_errnop,
199 char *r_addr, *r_name, *r_aliases, *r_addr_list;
201 struct address *addresses = NULL, *a;
202 unsigned n_addresses = 0, n, c;
204 alen = PROTO_ADDRESS_SIZE(af);
206 ifconf_acquire_addresses(&addresses, &n_addresses);
208 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
215 (c > 0 ? c : 1)*ALIGN(alen)+
216 (c > 0 ? c+1 : 2)*sizeof(char*);
220 *h_errnop = NO_RECOVERY;
222 return NSS_STATUS_TRYAGAIN;
225 /* First, fill in hostname */
227 memcpy(r_name, hn, l+1);
230 /* Second, create (empty) aliases array */
231 r_aliases = buffer + idx;
232 *(char**) r_aliases = NULL;
233 idx += sizeof(char*);
235 /* Third, add addresses */
236 r_addr = buffer + idx;
240 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
244 memcpy(r_addr + i*ALIGN(alen), a->address, alen);
249 idx += c*ALIGN(alen);
252 *(uint32_t*) r_addr = LOCALADDRESS_IPV4;
254 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
259 /* Fourth, add address pointer array */
260 r_addr_list = buffer + idx;
264 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
268 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
273 ((char**) r_addr_list)[c] = 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;
299 return NSS_STATUS_SUCCESS;
302 enum nss_status _nss_myhostname_gethostbyname3_r(
305 struct hostent *host,
306 char *buffer, size_t buflen,
307 int *errnop, int *h_errnop,
311 char hn[HOST_NAME_MAX+1];
316 if (af != AF_INET && af != AF_INET6) {
317 *errnop = EAFNOSUPPORT;
319 return NSS_STATUS_UNAVAIL;
322 memset(hn, 0, sizeof(hn));
323 if (gethostname(hn, sizeof(hn)-1) < 0) {
325 *h_errnop = NO_RECOVERY;
326 return NSS_STATUS_UNAVAIL;
329 if (strcasecmp(name, hn) != 0) {
331 *h_errnop = HOST_NOT_FOUND;
332 return NSS_STATUS_NOTFOUND;
335 return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, canonp);
338 enum nss_status _nss_myhostname_gethostbyname2_r(
341 struct hostent *host,
342 char *buffer, size_t buflen,
343 int *errnop, int *h_errnop) {
345 return _nss_myhostname_gethostbyname3_r(
355 enum nss_status _nss_myhostname_gethostbyname_r(
357 struct hostent *host,
358 char *buffer, size_t buflen,
359 int *errnop, int *h_errnop) {
361 return _nss_myhostname_gethostbyname3_r(
371 enum nss_status _nss_myhostname_gethostbyaddr2_r(
372 const void* addr, socklen_t len,
374 struct hostent *host,
375 char *buffer, size_t buflen,
376 int *errnop, int *h_errnop,
379 char hn[HOST_NAME_MAX+1];
380 struct address *addresses = NULL, *a;
381 unsigned n_addresses = 0, n;
383 if (len != PROTO_ADDRESS_SIZE(af)) {
385 *h_errnop = NO_RECOVERY;
386 return NSS_STATUS_UNAVAIL;
391 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
394 } else if (af == AF_INET6) {
396 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0)
400 *errnop = EAFNOSUPPORT;
402 return NSS_STATUS_UNAVAIL;
405 ifconf_acquire_addresses(&addresses, &n_addresses);
407 for (a = addresses, n = 0; n < n_addresses; n++, a++) {
411 if (memcmp(addr, a->address, PROTO_ADDRESS_SIZE(af)) == 0)
416 *h_errnop = HOST_NOT_FOUND;
419 return NSS_STATUS_NOTFOUND;
424 memset(hn, 0, sizeof(hn));
425 if (gethostname(hn, sizeof(hn)-1) < 0) {
427 *h_errnop = NO_RECOVERY;
429 return NSS_STATUS_UNAVAIL;
432 return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, NULL);
436 enum nss_status _nss_myhostname_gethostbyaddr_r(
437 const void* addr, socklen_t len,
439 struct hostent *host,
440 char *buffer, size_t buflen,
441 int *errnop, int *h_errnop) {
443 return _nss_myhostname_gethostbyaddr2_r(