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>
38 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
39 * 127.0.0.1 that it can be translated back to the local hostname. For
40 * IPv6 we use ::1 which unfortunately will not translate back to the
41 * hostname but instead something like "localhost6" or so. */
43 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
44 #define LOCALADDRESS_IPV6 &in6addr_loopback
45 #define LOOPBACK_INTERFACE "lo"
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 if (gethostname(hn, sizeof(hn)-1) < 0) {
108 *h_errnop = NO_RECOVERY;
109 return NSS_STATUS_UNAVAIL;
112 if (strcasecmp(name, hn) != 0) {
114 *h_errnop = HOST_NOT_FOUND;
115 return NSS_STATUS_NOTFOUND;
118 /* If this fails, n_addresses is 0. Which is fine */
119 ifconf_acquire_addresses(&addresses, &n_addresses);
121 /* If this call fails we fill in 0 as scope. Which is fine */
122 lo_ifi = if_nametoindex(LOOPBACK_INTERFACE);
125 ms = ALIGN(l+1)+ALIGN(sizeof(struct gaih_addrtuple))*(n_addresses > 0 ? n_addresses : 2);
128 *h_errnop = NO_RECOVERY;
130 return NSS_STATUS_TRYAGAIN;
133 /* First, fill in hostname */
135 memcpy(r_name, hn, l+1);
138 if (n_addresses <= 0) {
139 /* Second, fill in IPv6 tuple */
140 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
141 r_tuple->next = r_tuple_prev;
142 r_tuple->name = r_name;
143 r_tuple->family = AF_INET6;
144 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
145 r_tuple->scopeid = (uint32_t) lo_ifi;
147 idx += ALIGN(sizeof(struct gaih_addrtuple));
148 r_tuple_prev = r_tuple;
150 /* Third, fill in IPv4 tuple */
151 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
152 r_tuple->next = r_tuple_prev;
153 r_tuple->name = r_name;
154 r_tuple->family = AF_INET;
155 *(uint32_t*) r_tuple->addr = LOCALADDRESS_IPV4;
156 r_tuple->scopeid = (uint32_t) lo_ifi;
158 idx += ALIGN(sizeof(struct gaih_addrtuple));
159 r_tuple_prev = r_tuple;
162 /* Fourth, fill actual addresses in, but in backwards order */
163 for (a = addresses + n_addresses - 1, n = 0; n < n_addresses; n++, a--) {
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 = a->family;
168 r_tuple->scopeid = a->ifindex;
169 memcpy(r_tuple->addr, a->address, 16);
171 idx += ALIGN(sizeof(struct gaih_addrtuple));
172 r_tuple_prev = r_tuple;
175 /* Verify the size matches */
178 /* Nscd expects us to store the first record in **pat. */
180 **pat = *r_tuple_prev;
189 return NSS_STATUS_SUCCESS;
192 static enum nss_status fill_in_hostent(
195 struct hostent *result,
196 char *buffer, size_t buflen,
197 int *errnop, int *h_errnop,
202 char *r_addr, *r_name, *r_aliases, *r_addr_list;
204 struct address *addresses = NULL, *a;
205 unsigned n_addresses = 0, n, c;
207 alen = PROTO_ADDRESS_SIZE(af);
209 ifconf_acquire_addresses(&addresses, &n_addresses);
211 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
218 (c > 0 ? c : 1)*ALIGN(alen)+
219 (c > 0 ? c+1 : 2)*sizeof(char*);
223 *h_errnop = NO_RECOVERY;
225 return NSS_STATUS_TRYAGAIN;
228 /* First, fill in hostname */
230 memcpy(r_name, hn, l+1);
233 /* Second, create (empty) aliases array */
234 r_aliases = buffer + idx;
235 *(char**) r_aliases = NULL;
236 idx += sizeof(char*);
238 /* Third, add addresses */
239 r_addr = buffer + idx;
243 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
247 memcpy(r_addr + i*ALIGN(alen), a->address, alen);
252 idx += c*ALIGN(alen);
255 *(uint32_t*) r_addr = LOCALADDRESS_IPV4;
257 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
262 /* Fourth, add address pointer array */
263 r_addr_list = buffer + idx;
267 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
271 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
276 ((char**) r_addr_list)[c] = NULL;
277 idx += (c+1)*sizeof(char*);
280 ((char**) r_addr_list)[0] = r_addr;
281 ((char**) r_addr_list)[1] = NULL;
282 idx += 2*sizeof(char*);
285 /* Verify the size matches */
288 result->h_name = r_name;
289 result->h_aliases = (char**) r_aliases;
290 result->h_addrtype = af;
291 result->h_length = alen;
292 result->h_addr_list = (char**) r_addr_list;
302 return NSS_STATUS_SUCCESS;
305 enum nss_status _nss_myhostname_gethostbyname3_r(
308 struct hostent *host,
309 char *buffer, size_t buflen,
310 int *errnop, int *h_errnop,
314 char hn[HOST_NAME_MAX+1] = {};
319 if (af != AF_INET && af != AF_INET6) {
320 *errnop = EAFNOSUPPORT;
322 return NSS_STATUS_UNAVAIL;
325 if (gethostname(hn, sizeof(hn)-1) < 0) {
327 *h_errnop = NO_RECOVERY;
328 return NSS_STATUS_UNAVAIL;
331 if (strcasecmp(name, hn) != 0) {
333 *h_errnop = HOST_NOT_FOUND;
334 return NSS_STATUS_NOTFOUND;
337 return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, canonp);
340 enum nss_status _nss_myhostname_gethostbyname2_r(
343 struct hostent *host,
344 char *buffer, size_t buflen,
345 int *errnop, int *h_errnop) {
347 return _nss_myhostname_gethostbyname3_r(
357 enum nss_status _nss_myhostname_gethostbyname_r(
359 struct hostent *host,
360 char *buffer, size_t buflen,
361 int *errnop, int *h_errnop) {
363 return _nss_myhostname_gethostbyname3_r(
373 enum nss_status _nss_myhostname_gethostbyaddr2_r(
374 const void* addr, socklen_t len,
376 struct hostent *host,
377 char *buffer, size_t buflen,
378 int *errnop, int *h_errnop,
381 char hn[HOST_NAME_MAX+1] = {};
382 struct address _cleanup_free_ *addresses = NULL;
384 unsigned n_addresses = 0, n;
386 if (len != PROTO_ADDRESS_SIZE(af)) {
388 *h_errnop = NO_RECOVERY;
389 return NSS_STATUS_UNAVAIL;
394 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
397 } else if (af == AF_INET6) {
399 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0)
403 *errnop = EAFNOSUPPORT;
405 return NSS_STATUS_UNAVAIL;
408 ifconf_acquire_addresses(&addresses, &n_addresses);
410 for (a = addresses, n = 0; n < n_addresses; n++, a++) {
414 if (memcmp(addr, a->address, PROTO_ADDRESS_SIZE(af)) == 0)
419 *h_errnop = HOST_NOT_FOUND;
421 return NSS_STATUS_NOTFOUND;
424 if (gethostname(hn, sizeof(hn)-1) < 0) {
426 *h_errnop = NO_RECOVERY;
428 return NSS_STATUS_UNAVAIL;
431 return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, NULL);
435 enum nss_status _nss_myhostname_gethostbyaddr_r(
436 const void* addr, socklen_t len,
438 struct hostent *host,
439 char *buffer, size_t buflen,
440 int *errnop, int *h_errnop) {
442 return _nss_myhostname_gethostbyaddr2_r(