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 */
179 /* Nscd expects us to store the first record in **pat. */
181 **pat = *r_tuple_prev;
190 return NSS_STATUS_SUCCESS;
193 static enum nss_status fill_in_hostent(
196 struct hostent *result,
197 char *buffer, size_t buflen,
198 int *errnop, int *h_errnop,
203 char *r_addr, *r_name, *r_aliases, *r_addr_list;
205 struct address *addresses = NULL, *a;
206 unsigned n_addresses = 0, n, c;
208 alen = PROTO_ADDRESS_SIZE(af);
210 ifconf_acquire_addresses(&addresses, &n_addresses);
212 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
219 (c > 0 ? c : 1)*ALIGN(alen)+
220 (c > 0 ? c+1 : 2)*sizeof(char*);
224 *h_errnop = NO_RECOVERY;
226 return NSS_STATUS_TRYAGAIN;
229 /* First, fill in hostname */
231 memcpy(r_name, hn, l+1);
234 /* Second, create (empty) aliases array */
235 r_aliases = buffer + idx;
236 *(char**) r_aliases = NULL;
237 idx += sizeof(char*);
239 /* Third, add addresses */
240 r_addr = buffer + idx;
244 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
248 memcpy(r_addr + i*ALIGN(alen), a->address, alen);
253 idx += c*ALIGN(alen);
256 *(uint32_t*) r_addr = LOCALADDRESS_IPV4;
258 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
263 /* Fourth, add address pointer array */
264 r_addr_list = buffer + idx;
268 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
272 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
277 ((char**) r_addr_list)[c] = NULL;
278 idx += (c+1)*sizeof(char*);
281 ((char**) r_addr_list)[0] = r_addr;
282 ((char**) r_addr_list)[1] = NULL;
283 idx += 2*sizeof(char*);
286 /* Verify the size matches */
289 result->h_name = r_name;
290 result->h_aliases = (char**) r_aliases;
291 result->h_addrtype = af;
292 result->h_length = alen;
293 result->h_addr_list = (char**) r_addr_list;
303 return NSS_STATUS_SUCCESS;
306 enum nss_status _nss_myhostname_gethostbyname3_r(
309 struct hostent *host,
310 char *buffer, size_t buflen,
311 int *errnop, int *h_errnop,
315 char hn[HOST_NAME_MAX+1];
320 if (af != AF_INET && af != AF_INET6) {
321 *errnop = EAFNOSUPPORT;
323 return NSS_STATUS_UNAVAIL;
326 memset(hn, 0, sizeof(hn));
327 if (gethostname(hn, sizeof(hn)-1) < 0) {
329 *h_errnop = NO_RECOVERY;
330 return NSS_STATUS_UNAVAIL;
333 if (strcasecmp(name, hn) != 0) {
335 *h_errnop = HOST_NOT_FOUND;
336 return NSS_STATUS_NOTFOUND;
339 return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, canonp);
342 enum nss_status _nss_myhostname_gethostbyname2_r(
345 struct hostent *host,
346 char *buffer, size_t buflen,
347 int *errnop, int *h_errnop) {
349 return _nss_myhostname_gethostbyname3_r(
359 enum nss_status _nss_myhostname_gethostbyname_r(
361 struct hostent *host,
362 char *buffer, size_t buflen,
363 int *errnop, int *h_errnop) {
365 return _nss_myhostname_gethostbyname3_r(
375 enum nss_status _nss_myhostname_gethostbyaddr2_r(
376 const void* addr, socklen_t len,
378 struct hostent *host,
379 char *buffer, size_t buflen,
380 int *errnop, int *h_errnop,
383 char hn[HOST_NAME_MAX+1];
384 struct address *addresses = NULL, *a;
385 unsigned n_addresses = 0, n;
387 if (len != PROTO_ADDRESS_SIZE(af)) {
389 *h_errnop = NO_RECOVERY;
390 return NSS_STATUS_UNAVAIL;
395 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
398 } else if (af == AF_INET6) {
400 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0)
404 *errnop = EAFNOSUPPORT;
406 return NSS_STATUS_UNAVAIL;
409 ifconf_acquire_addresses(&addresses, &n_addresses);
411 for (a = addresses, n = 0; n < n_addresses; n++, a++) {
415 if (memcmp(addr, a->address, PROTO_ADDRESS_SIZE(af)) == 0)
420 *h_errnop = HOST_NOT_FOUND;
423 return NSS_STATUS_NOTFOUND;
428 memset(hn, 0, sizeof(hn));
429 if (gethostname(hn, sizeof(hn)-1) < 0) {
431 *h_errnop = NO_RECOVERY;
433 return NSS_STATUS_UNAVAIL;
436 return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, NULL);
440 enum nss_status _nss_myhostname_gethostbyaddr_r(
441 const void* addr, socklen_t len,
443 struct hostent *host,
444 char *buffer, size_t buflen,
445 int *errnop, int *h_errnop) {
447 return _nss_myhostname_gethostbyaddr2_r(