chiark / gitweb /
6551ac9f320159697df9515f44629dce8521afb4
[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 "addresses.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         struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
102         _cleanup_free_ struct address *addresses = NULL;
103         _cleanup_free_ char *hn = NULL;
104         const char *canonical = NULL;
105         unsigned n_addresses = 0, n;
106         uint32_t local_address_ipv4;
107         struct address *a;
108         size_t l, idx, ms;
109         char *r_name;
110         int lo_ifi;
111
112         assert(name);
113         assert(pat);
114         assert(buffer);
115         assert(errnop);
116         assert(h_errnop);
117
118         if (is_localhost(name)) {
119                 /* We respond to 'localhost', so that /etc/hosts
120                  * is optional */
121
122                 canonical = "localhost";
123                 local_address_ipv4 = htonl(INADDR_LOOPBACK);
124         } else {
125                 hn = gethostname_malloc();
126                 if (!hn) {
127                         *errnop = ENOMEM;
128                         *h_errnop = NO_RECOVERY;
129                         return NSS_STATUS_TRYAGAIN;
130                 }
131
132                 /* We respond to our local host name, our our hostname suffixed with a single dot. */
133                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
134                         *errnop = ENOENT;
135                         *h_errnop = HOST_NOT_FOUND;
136                         return NSS_STATUS_NOTFOUND;
137                 }
138
139                 /* If this fails, n_addresses is 0. Which is fine */
140                 acquire_addresses(&addresses, &n_addresses);
141
142                 canonical = hn;
143                 local_address_ipv4 = LOCALADDRESS_IPV4;
144         }
145
146         /* If this call fails we fill in 0 as scope. Which is fine */
147         lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
148
149         l = strlen(canonical);
150         ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
151         if (buflen < ms) {
152                 *errnop = ENOMEM;
153                 *h_errnop = NO_RECOVERY;
154                 return NSS_STATUS_TRYAGAIN;
155         }
156
157         /* First, fill in hostname */
158         r_name = buffer;
159         memcpy(r_name, canonical, l+1);
160         idx = ALIGN(l+1);
161
162         if (n_addresses <= 0) {
163                 /* Second, fill in IPv6 tuple */
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 = AF_INET6;
168                 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
169                 r_tuple->scopeid = (uint32_t) lo_ifi;
170
171                 idx += ALIGN(sizeof(struct gaih_addrtuple));
172                 r_tuple_prev = r_tuple;
173
174                 /* Third, fill in IPv4 tuple */
175                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
176                 r_tuple->next = r_tuple_prev;
177                 r_tuple->name = r_name;
178                 r_tuple->family = AF_INET;
179                 *(uint32_t*) r_tuple->addr = local_address_ipv4;
180                 r_tuple->scopeid = (uint32_t) lo_ifi;
181
182                 idx += ALIGN(sizeof(struct gaih_addrtuple));
183                 r_tuple_prev = r_tuple;
184         }
185
186         /* Fourth, fill actual addresses in, but in backwards order */
187         for (a = addresses + n_addresses - 1, n = 0; n < n_addresses; n++, a--) {
188                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
189                 r_tuple->next = r_tuple_prev;
190                 r_tuple->name = r_name;
191                 r_tuple->family = a->family;
192                 r_tuple->scopeid = a->ifindex;
193                 memcpy(r_tuple->addr, &a->address, 16);
194
195                 idx += ALIGN(sizeof(struct gaih_addrtuple));
196                 r_tuple_prev = r_tuple;
197         }
198
199         /* Verify the size matches */
200         assert(idx == ms);
201
202         /* Nscd expects us to store the first record in **pat. */
203         if (*pat)
204                 **pat = *r_tuple_prev;
205         else
206                 *pat = r_tuple_prev;
207
208         if (ttlp)
209                 *ttlp = 0;
210
211         return NSS_STATUS_SUCCESS;
212 }
213
214 static enum nss_status fill_in_hostent(
215                 const char *canonical, const char *additional,
216                 int af,
217                 struct address *addresses, unsigned n_addresses,
218                 uint32_t local_address_ipv4,
219                 struct hostent *result,
220                 char *buffer, size_t buflen,
221                 int *errnop, int *h_errnop,
222                 int32_t *ttlp,
223                 char **canonp) {
224
225         size_t l_canonical, l_additional, idx, ms;
226         char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
227         size_t alen;
228         struct address *a;
229         unsigned n, c;
230
231         assert(canonical);
232         assert(result);
233         assert(buffer);
234         assert(errnop);
235         assert(h_errnop);
236
237         alen = PROTO_ADDRESS_SIZE(af);
238
239         for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
240                 if (af == a->family)
241                         c++;
242
243         l_canonical = strlen(canonical);
244         l_additional = additional ? strlen(additional) : 0;
245         ms = ALIGN(l_canonical+1)+
246                 (additional ? ALIGN(l_additional+1) : 0) +
247                 sizeof(char*) +
248                 (additional ? sizeof(char*) : 0) +
249                 (c > 0 ? c : 1) * ALIGN(alen)+
250                 (c > 0 ? c+1 : 2) * sizeof(char*);
251
252         if (buflen < ms) {
253                 *errnop = ENOMEM;
254                 *h_errnop = NO_RECOVERY;
255                 return NSS_STATUS_TRYAGAIN;
256         }
257
258         /* First, fill in hostnames */
259         r_name = buffer;
260         memcpy(r_name, canonical, l_canonical+1);
261         idx = ALIGN(l_canonical+1);
262
263         if (additional) {
264                 r_alias = buffer + idx;
265                 memcpy(r_alias, additional, l_additional+1);
266                 idx += ALIGN(l_additional+1);
267         }
268
269         /* Second, create aliases array */
270         r_aliases = buffer + idx;
271         if (additional) {
272                 ((char**) r_aliases)[0] = r_alias;
273                 ((char**) r_aliases)[1] = NULL;
274                 idx += 2*sizeof(char*);
275         } else {
276                 ((char**) r_aliases)[0] = NULL;
277                 idx += sizeof(char*);
278         }
279
280         /* Third, add addresses */
281         r_addr = buffer + idx;
282         if (c > 0) {
283                 unsigned i = 0;
284
285                 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
286                         if (af != a->family)
287                                 continue;
288
289                         memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
290                         i++;
291                 }
292
293                 assert(i == c);
294                 idx += c*ALIGN(alen);
295         } else {
296                 if (af == AF_INET)
297                         *(uint32_t*) r_addr = local_address_ipv4;
298                 else
299                         memcpy(r_addr, LOCALADDRESS_IPV6, 16);
300
301                 idx += ALIGN(alen);
302         }
303
304         /* Fourth, add address pointer array */
305         r_addr_list = buffer + idx;
306         if (c > 0) {
307                 unsigned i = 0;
308
309                 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
310                         if (af != a->family)
311                                 continue;
312
313                         ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
314                         i++;
315                 }
316
317                 assert(i == c);
318                 ((char**) r_addr_list)[c] = NULL;
319                 idx += (c+1)*sizeof(char*);
320
321         } else {
322                 ((char**) r_addr_list)[0] = r_addr;
323                 ((char**) r_addr_list)[1] = NULL;
324                 idx += 2*sizeof(char*);
325         }
326
327         /* Verify the size matches */
328         assert(idx == ms);
329
330         result->h_name = r_name;
331         result->h_aliases = (char**) r_aliases;
332         result->h_addrtype = af;
333         result->h_length = alen;
334         result->h_addr_list = (char**) r_addr_list;
335
336         if (ttlp)
337                 *ttlp = 0;
338
339         if (canonp)
340                 *canonp = r_name;
341
342         return NSS_STATUS_SUCCESS;
343 }
344
345 enum nss_status _nss_myhostname_gethostbyname3_r(
346                 const char *name,
347                 int af,
348                 struct hostent *host,
349                 char *buffer, size_t buflen,
350                 int *errnop, int *h_errnop,
351                 int32_t *ttlp,
352                 char **canonp) {
353
354         _cleanup_free_ struct address *addresses = NULL;
355         const char *canonical, *additional = NULL;
356         _cleanup_free_ char *hn = NULL;
357         uint32_t local_address_ipv4;
358         unsigned n_addresses = 0;
359
360         assert(name);
361         assert(host);
362         assert(buffer);
363         assert(errnop);
364         assert(h_errnop);
365
366         if (af == AF_UNSPEC)
367                 af = AF_INET;
368
369         if (af != AF_INET && af != AF_INET6) {
370                 *errnop = EAFNOSUPPORT;
371                 *h_errnop = NO_DATA;
372                 return NSS_STATUS_UNAVAIL;
373         }
374
375         if (is_localhost(name)) {
376                 canonical = "localhost";
377                 local_address_ipv4 = htonl(INADDR_LOOPBACK);
378         } else {
379                 hn = gethostname_malloc();
380                 if (!hn) {
381                         *errnop = ENOMEM;
382                         *h_errnop = NO_RECOVERY;
383                         return NSS_STATUS_TRYAGAIN;
384                 }
385
386                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
387                         *errnop = ENOENT;
388                         *h_errnop = HOST_NOT_FOUND;
389                         return NSS_STATUS_NOTFOUND;
390                 }
391
392                 acquire_addresses(&addresses, &n_addresses);
393
394                 canonical = hn;
395                 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
396                 local_address_ipv4 = LOCALADDRESS_IPV4;
397         }
398
399         return fill_in_hostent(
400                         canonical, additional,
401                         af,
402                         addresses, n_addresses,
403                         local_address_ipv4,
404                         host,
405                         buffer, buflen,
406                         errnop, h_errnop,
407                         ttlp,
408                         canonp);
409 }
410
411 enum nss_status _nss_myhostname_gethostbyname2_r(
412                 const char *name,
413                 int af,
414                 struct hostent *host,
415                 char *buffer, size_t buflen,
416                 int *errnop, int *h_errnop) {
417
418         return _nss_myhostname_gethostbyname3_r(
419                         name,
420                         af,
421                         host,
422                         buffer, buflen,
423                         errnop, h_errnop,
424                         NULL,
425                         NULL);
426 }
427
428 enum nss_status _nss_myhostname_gethostbyname_r(
429                 const char *name,
430                 struct hostent *host,
431                 char *buffer, size_t buflen,
432                 int *errnop, int *h_errnop) {
433
434         return _nss_myhostname_gethostbyname3_r(
435                         name,
436                         AF_UNSPEC,
437                         host,
438                         buffer, buflen,
439                         errnop, h_errnop,
440                         NULL,
441                         NULL);
442 }
443
444 enum nss_status _nss_myhostname_gethostbyaddr2_r(
445                 const void* addr, socklen_t len,
446                 int af,
447                 struct hostent *host,
448                 char *buffer, size_t buflen,
449                 int *errnop, int *h_errnop,
450                 int32_t *ttlp) {
451
452         const char *canonical = NULL, *additional = NULL;
453         uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
454         _cleanup_free_ struct address *addresses = NULL;
455         _cleanup_free_ char *hn = NULL;
456         unsigned n_addresses = 0, n;
457         struct address *a;
458
459         assert(addr);
460         assert(host);
461         assert(buffer);
462         assert(errnop);
463         assert(h_errnop);
464
465         if (len != PROTO_ADDRESS_SIZE(af)) {
466                 *errnop = EINVAL;
467                 *h_errnop = NO_RECOVERY;
468                 return NSS_STATUS_UNAVAIL;
469         }
470
471         if (af == AF_INET) {
472
473                 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
474                         goto found;
475
476                 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
477                         canonical = "localhost";
478                         local_address_ipv4 = htonl(INADDR_LOOPBACK);
479                         goto found;
480                 }
481
482         } else if (af == AF_INET6) {
483
484                 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
485                         additional = "localhost";
486                         goto found;
487                 }
488
489         } else {
490                 *errnop = EAFNOSUPPORT;
491                 *h_errnop = NO_DATA;
492                 return NSS_STATUS_UNAVAIL;
493         }
494
495         acquire_addresses(&addresses, &n_addresses);
496
497         for (a = addresses, n = 0; n < n_addresses; n++, a++) {
498                 if (af != a->family)
499                         continue;
500
501                 if (memcmp(addr, &a->address, PROTO_ADDRESS_SIZE(af)) == 0)
502                         goto found;
503         }
504
505         *errnop = ENOENT;
506         *h_errnop = HOST_NOT_FOUND;
507
508         return NSS_STATUS_NOTFOUND;
509
510 found:
511         if (!canonical) {
512                 hn = gethostname_malloc();
513                 if (!hn) {
514                         *errnop = ENOMEM;
515                         *h_errnop = NO_RECOVERY;
516                         return NSS_STATUS_TRYAGAIN;
517                 }
518
519                 canonical = hn;
520         }
521
522         return fill_in_hostent(
523                         canonical, additional,
524                         af,
525                         addresses, n_addresses,
526                         local_address_ipv4,
527                         host,
528                         buffer, buflen,
529                         errnop, h_errnop,
530                         ttlp,
531                         NULL);
532
533 }
534
535 enum nss_status _nss_myhostname_gethostbyaddr_r(
536                 const void* addr, socklen_t len,
537                 int af,
538                 struct hostent *host,
539                 char *buffer, size_t buflen,
540                 int *errnop, int *h_errnop) {
541
542         return _nss_myhostname_gethostbyaddr2_r(
543                         addr, len,
544                         af,
545                         host,
546                         buffer, buflen,
547                         errnop, h_errnop,
548                         NULL);
549 }