]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
nss-myhostname: resolve 'localhost' so that /etc/hosts becomes optional
[thirdparty/systemd.git] / src / nss-myhostname / nss-myhostname.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2008-2011 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <limits.h>
23 #include <nss.h>
24 #include <sys/types.h>
25 #include <netdb.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <net/if.h>
30 #include <stdlib.h>
31 #include <arpa/inet.h>
32
33 #include "ifconf.h"
34 #include "macro.h"
35
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() */
39 #include <assert.h>
40
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. */
45
46 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
47 #define LOCALADDRESS_IPV6 &in6addr_loopback
48 #define LOOPBACK_INTERFACE "lo"
49
50 enum nss_status _nss_myhostname_gethostbyname4_r(
51 const char *name,
52 struct gaih_addrtuple **pat,
53 char *buffer, size_t buflen,
54 int *errnop, int *h_errnop,
55 int32_t *ttlp) _public_;
56
57 enum nss_status _nss_myhostname_gethostbyname3_r(
58 const char *name,
59 int af,
60 struct hostent *host,
61 char *buffer, size_t buflen,
62 int *errnop, int *h_errnop,
63 int32_t *ttlp,
64 char **canonp) _public_;
65
66 enum nss_status _nss_myhostname_gethostbyname2_r(
67 const char *name,
68 int af,
69 struct hostent *host,
70 char *buffer, size_t buflen,
71 int *errnop, int *h_errnop) _public_;
72
73 enum nss_status _nss_myhostname_gethostbyname_r(
74 const char *name,
75 struct hostent *host,
76 char *buffer, size_t buflen,
77 int *errnop, int *h_errnop) _public_;
78
79 enum nss_status _nss_myhostname_gethostbyaddr2_r(
80 const void* addr, socklen_t len,
81 int af,
82 struct hostent *host,
83 char *buffer, size_t buflen,
84 int *errnop, int *h_errnop,
85 int32_t *ttlp) _public_;
86
87 enum nss_status _nss_myhostname_gethostbyaddr_r(
88 const void* addr, socklen_t len,
89 int af,
90 struct hostent *host,
91 char *buffer, size_t buflen,
92 int *errnop, int *h_errnop) _public_;
93
94 enum nss_status _nss_myhostname_gethostbyname4_r(
95 const char *name,
96 struct gaih_addrtuple **pat,
97 char *buffer, size_t buflen,
98 int *errnop, int *h_errnop,
99 int32_t *ttlp) {
100
101 unsigned lo_ifi;
102 char hn[HOST_NAME_MAX+1] = {};
103 const char *canonical = NULL;
104 size_t l, idx, ms;
105 char *r_name;
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;
110
111 if (strcasecmp(name, "localhost") == 0) {
112 /* We respond to 'localhost', so that /etc/hosts
113 * is optional */
114
115 canonical = "localhost";
116 local_address_ipv4 = htonl(INADDR_LOOPBACK);
117 } else {
118 /* We respond to our local host name */
119
120 if (gethostname(hn, sizeof(hn)-1) < 0) {
121 *errnop = errno;
122 *h_errnop = NO_RECOVERY;
123 return NSS_STATUS_UNAVAIL;
124 }
125
126 if (strcasecmp(name, hn) != 0) {
127 *errnop = ENOENT;
128 *h_errnop = HOST_NOT_FOUND;
129 return NSS_STATUS_NOTFOUND;
130 }
131
132 /* If this fails, n_addresses is 0. Which is fine */
133 ifconf_acquire_addresses(&addresses, &n_addresses);
134
135 canonical = hn;
136 local_address_ipv4 = LOCALADDRESS_IPV4;
137 }
138
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;
141
142 l = strlen(canonical);
143 ms = ALIGN(l+1)+ALIGN(sizeof(struct gaih_addrtuple))*(n_addresses > 0 ? n_addresses : 2);
144 if (buflen < ms) {
145 *errnop = ENOMEM;
146 *h_errnop = NO_RECOVERY;
147 free(addresses);
148 return NSS_STATUS_TRYAGAIN;
149 }
150
151 /* First, fill in hostname */
152 r_name = buffer;
153 memcpy(r_name, canonical, l+1);
154 idx = ALIGN(l+1);
155
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;
164
165 idx += ALIGN(sizeof(struct gaih_addrtuple));
166 r_tuple_prev = r_tuple;
167
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;
175
176 idx += ALIGN(sizeof(struct gaih_addrtuple));
177 r_tuple_prev = r_tuple;
178 }
179
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);
188
189 idx += ALIGN(sizeof(struct gaih_addrtuple));
190 r_tuple_prev = r_tuple;
191 }
192
193 /* Verify the size matches */
194 assert(idx == ms);
195
196 /* Nscd expects us to store the first record in **pat. */
197 if (*pat)
198 **pat = *r_tuple_prev;
199 else
200 *pat = r_tuple_prev;
201
202 if (ttlp)
203 *ttlp = 0;
204
205 free(addresses);
206
207 return NSS_STATUS_SUCCESS;
208 }
209
210 static enum nss_status fill_in_hostent(
211 const char *canonical, const char *additional,
212 int af,
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,
218 int32_t *ttlp,
219 char **canonp) {
220
221 size_t l_canonical, l_additional, idx, ms;
222 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
223 size_t alen;
224 struct address *a;
225 unsigned n, c;
226
227 alen = PROTO_ADDRESS_SIZE(af);
228
229 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
230 if (af == a->family)
231 c++;
232
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) +
237 sizeof(char*)+
238 (additional ? sizeof(char*) : 0) +
239 (c > 0 ? c : 1)*ALIGN(alen)+
240 (c > 0 ? c+1 : 2)*sizeof(char*);
241
242 if (buflen < ms) {
243 *errnop = ENOMEM;
244 *h_errnop = NO_RECOVERY;
245 free(addresses);
246 return NSS_STATUS_TRYAGAIN;
247 }
248
249 /* First, fill in hostnames */
250 r_name = buffer;
251 memcpy(r_name, canonical, l_canonical+1);
252 idx = ALIGN(l_canonical+1);
253
254 if (additional) {
255 r_alias = buffer + idx;
256 memcpy(r_alias, additional, l_additional+1);
257 idx += ALIGN(l_additional+1);
258 }
259
260 /* Second, create aliases array */
261 r_aliases = buffer + idx;
262 if (additional) {
263 ((char**) r_aliases)[0] = r_alias;
264 ((char**) r_aliases)[1] = NULL;
265 idx += 2*sizeof(char*);
266 } else {
267 ((char**) r_aliases)[0] = NULL;
268 idx += sizeof(char*);
269 }
270
271 /* Third, add addresses */
272 r_addr = buffer + idx;
273 if (c > 0) {
274 unsigned i = 0;
275
276 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
277 if (af != a->family)
278 continue;
279
280 memcpy(r_addr + i*ALIGN(alen), a->address, alen);
281 i++;
282 }
283
284 assert(i == c);
285 idx += c*ALIGN(alen);
286 } else {
287 if (af == AF_INET)
288 *(uint32_t*) r_addr = local_address_ipv4;
289 else
290 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
291
292 idx += ALIGN(alen);
293 }
294
295 /* Fourth, add address pointer array */
296 r_addr_list = buffer + idx;
297 if (c > 0) {
298 unsigned i = 0;
299
300 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
301 if (af != a->family)
302 continue;
303
304 ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
305 i++;
306 }
307
308 assert(i == c);
309 ((char**) r_addr_list)[c] = NULL;
310 idx += (c+1)*sizeof(char*);
311
312 } else {
313 ((char**) r_addr_list)[0] = r_addr;
314 ((char**) r_addr_list)[1] = NULL;
315 idx += 2*sizeof(char*);
316 }
317
318 /* Verify the size matches */
319 assert(idx == ms);
320
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;
326
327 if (ttlp)
328 *ttlp = 0;
329
330 if (canonp)
331 *canonp = r_name;
332
333 free(addresses);
334
335 return NSS_STATUS_SUCCESS;
336 }
337
338 enum nss_status _nss_myhostname_gethostbyname3_r(
339 const char *name,
340 int af,
341 struct hostent *host,
342 char *buffer, size_t buflen,
343 int *errnop, int *h_errnop,
344 int32_t *ttlp,
345 char **canonp) {
346
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;
352
353 if (af == AF_UNSPEC)
354 af = AF_INET;
355
356 if (af != AF_INET && af != AF_INET6) {
357 *errnop = EAFNOSUPPORT;
358 *h_errnop = NO_DATA;
359 return NSS_STATUS_UNAVAIL;
360 }
361
362 if (strcasecmp(name, "localhost") == 0) {
363 canonical = "localhost";
364 local_address_ipv4 = htonl(INADDR_LOOPBACK);
365 } else {
366 if (gethostname(hn, sizeof(hn)-1) < 0) {
367 *errnop = errno;
368 *h_errnop = NO_RECOVERY;
369 return NSS_STATUS_UNAVAIL;
370 }
371
372 if (strcasecmp(name, hn) != 0) {
373 *errnop = ENOENT;
374 *h_errnop = HOST_NOT_FOUND;
375 return NSS_STATUS_NOTFOUND;
376 }
377
378 ifconf_acquire_addresses(&addresses, &n_addresses);
379
380 canonical = hn;
381 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
382 local_address_ipv4 = LOCALADDRESS_IPV4;
383 }
384
385 return fill_in_hostent(
386 canonical, additional,
387 af,
388 addresses, n_addresses,
389 local_address_ipv4,
390 host,
391 buffer, buflen,
392 errnop, h_errnop,
393 ttlp,
394 canonp);
395 }
396
397 enum nss_status _nss_myhostname_gethostbyname2_r(
398 const char *name,
399 int af,
400 struct hostent *host,
401 char *buffer, size_t buflen,
402 int *errnop, int *h_errnop) {
403
404 return _nss_myhostname_gethostbyname3_r(
405 name,
406 af,
407 host,
408 buffer, buflen,
409 errnop, h_errnop,
410 NULL,
411 NULL);
412 }
413
414 enum nss_status _nss_myhostname_gethostbyname_r(
415 const char *name,
416 struct hostent *host,
417 char *buffer, size_t buflen,
418 int *errnop, int *h_errnop) {
419
420 return _nss_myhostname_gethostbyname3_r(
421 name,
422 AF_UNSPEC,
423 host,
424 buffer, buflen,
425 errnop, h_errnop,
426 NULL,
427 NULL);
428 }
429
430 enum nss_status _nss_myhostname_gethostbyaddr2_r(
431 const void* addr, socklen_t len,
432 int af,
433 struct hostent *host,
434 char *buffer, size_t buflen,
435 int *errnop, int *h_errnop,
436 int32_t *ttlp) {
437
438 char hn[HOST_NAME_MAX+1] = {};
439 struct address *addresses = NULL;
440 struct address *a;
441 unsigned n_addresses = 0, n;
442 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
443 const char *canonical = NULL, *additional = NULL;
444
445 if (len != PROTO_ADDRESS_SIZE(af)) {
446 *errnop = EINVAL;
447 *h_errnop = NO_RECOVERY;
448 return NSS_STATUS_UNAVAIL;
449 }
450
451 if (af == AF_INET) {
452
453 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
454 goto found;
455
456 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
457 canonical = "localhost";
458 local_address_ipv4 = htonl(INADDR_LOOPBACK);
459 goto found;
460 }
461
462 } else if (af == AF_INET6) {
463
464 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
465 additional = "localhost";
466 goto found;
467 }
468
469 } else {
470 *errnop = EAFNOSUPPORT;
471 *h_errnop = NO_DATA;
472 return NSS_STATUS_UNAVAIL;
473 }
474
475 ifconf_acquire_addresses(&addresses, &n_addresses);
476
477 for (a = addresses, n = 0; n < n_addresses; n++, a++) {
478 if (af != a->family)
479 continue;
480
481 if (memcmp(addr, a->address, PROTO_ADDRESS_SIZE(af)) == 0)
482 goto found;
483 }
484
485 *errnop = ENOENT;
486 *h_errnop = HOST_NOT_FOUND;
487
488 free(addresses);
489
490 return NSS_STATUS_NOTFOUND;
491
492 found:
493 if (!canonical) {
494 if (gethostname(hn, sizeof(hn)-1) < 0) {
495 *errnop = errno;
496 *h_errnop = NO_RECOVERY;
497
498 free(addresses);
499
500 return NSS_STATUS_UNAVAIL;
501 }
502
503 canonical = hn;
504 }
505
506 return fill_in_hostent(
507 canonical, additional,
508 af,
509 addresses, n_addresses,
510 local_address_ipv4,
511 host,
512 buffer, buflen,
513 errnop, h_errnop,
514 ttlp,
515 NULL);
516
517 }
518
519 enum nss_status _nss_myhostname_gethostbyaddr_r(
520 const void* addr, socklen_t len,
521 int af,
522 struct hostent *host,
523 char *buffer, size_t buflen,
524 int *errnop, int *h_errnop) {
525
526 return _nss_myhostname_gethostbyaddr2_r(
527 addr, len,
528 af,
529 host,
530 buffer, buflen,
531 errnop, h_errnop,
532 NULL);
533 }