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