chiark / gitweb /
build-sys: add a makefile target to run all tests through valgrind
[elogind.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 }