chiark / gitweb /
tmpfiles: let's always use DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING() instead...
[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 #include "nss-util.h"
36 #include "util.h"
37
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 "localhost" or so. */
42
43 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
44 #define LOCALADDRESS_IPV6 &in6addr_loopback
45 #define LOOPBACK_INTERFACE "lo"
46
47 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
48 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
49
50 static bool is_gateway(const char *hostname) {
51         assert(hostname);
52
53         return streq(hostname, "gateway") ||
54                streq(hostname, "gateway.");
55 }
56
57 enum nss_status _nss_myhostname_gethostbyname4_r(
58                 const char *name,
59                 struct gaih_addrtuple **pat,
60                 char *buffer, size_t buflen,
61                 int *errnop, int *h_errnop,
62                 int32_t *ttlp) {
63
64         struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
65         _cleanup_free_ struct local_address *addresses = NULL;
66         _cleanup_free_ char *hn = NULL;
67         const char *canonical = NULL;
68         int n_addresses = 0, lo_ifi;
69         uint32_t local_address_ipv4;
70         struct local_address *a;
71         size_t l, idx, ms;
72         char *r_name;
73         unsigned n;
74
75         assert(name);
76         assert(pat);
77         assert(buffer);
78         assert(errnop);
79         assert(h_errnop);
80
81         if (is_localhost(name)) {
82                 /* We respond to 'localhost', so that /etc/hosts
83                  * is optional */
84
85                 canonical = "localhost";
86                 local_address_ipv4 = htonl(INADDR_LOOPBACK);
87
88         } else if (is_gateway(name)) {
89
90                 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
91                 if (n_addresses <= 0) {
92                         *errnop = ENOENT;
93                         *h_errnop = HOST_NOT_FOUND;
94                         return NSS_STATUS_NOTFOUND;
95                 }
96
97                 canonical = "gateway";
98
99         } else {
100                 hn = gethostname_malloc();
101                 if (!hn) {
102                         *errnop = ENOMEM;
103                         *h_errnop = NO_RECOVERY;
104                         return NSS_STATUS_TRYAGAIN;
105                 }
106
107                 /* We respond to our local host name, our our hostname suffixed with a single dot. */
108                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
109                         *errnop = ENOENT;
110                         *h_errnop = HOST_NOT_FOUND;
111                         return NSS_STATUS_NOTFOUND;
112                 }
113
114                 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
115                 if (n_addresses < 0)
116                         n_addresses = 0;
117
118                 canonical = hn;
119                 local_address_ipv4 = LOCALADDRESS_IPV4;
120         }
121
122         /* If this call fails we fill in 0 as scope. Which is fine */
123         lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
124
125         l = strlen(canonical);
126         ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
127         if (buflen < ms) {
128                 *errnop = ENOMEM;
129                 *h_errnop = NO_RECOVERY;
130                 return NSS_STATUS_TRYAGAIN;
131         }
132
133         /* First, fill in hostname */
134         r_name = buffer;
135         memcpy(r_name, canonical, l+1);
136         idx = ALIGN(l+1);
137
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;
146
147                 idx += ALIGN(sizeof(struct gaih_addrtuple));
148                 r_tuple_prev = r_tuple;
149
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 = local_address_ipv4;
156                 r_tuple->scopeid = (uint32_t) lo_ifi;
157
158                 idx += ALIGN(sizeof(struct gaih_addrtuple));
159                 r_tuple_prev = r_tuple;
160         }
161
162         /* Fourth, fill actual addresses in, but in backwards order */
163         for (a = addresses + n_addresses - 1, n = 0; (int) 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);
170
171                 idx += ALIGN(sizeof(struct gaih_addrtuple));
172                 r_tuple_prev = r_tuple;
173         }
174
175         /* Verify the size matches */
176         assert(idx == ms);
177
178         /* Nscd expects us to store the first record in **pat. */
179         if (*pat)
180                 **pat = *r_tuple_prev;
181         else
182                 *pat = r_tuple_prev;
183
184         if (ttlp)
185                 *ttlp = 0;
186
187         /* Explicitly reset all error variables */
188         *errnop = 0;
189         *h_errnop = NETDB_SUCCESS;
190         h_errno = 0;
191
192         return NSS_STATUS_SUCCESS;
193 }
194
195 static enum nss_status fill_in_hostent(
196                 const char *canonical, const char *additional,
197                 int af,
198                 struct local_address *addresses, unsigned n_addresses,
199                 uint32_t local_address_ipv4,
200                 struct hostent *result,
201                 char *buffer, size_t buflen,
202                 int *errnop, int *h_errnop,
203                 int32_t *ttlp,
204                 char **canonp) {
205
206         size_t l_canonical, l_additional, idx, ms, alen;
207         char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
208         struct local_address *a;
209         unsigned n, c;
210
211         assert(canonical);
212         assert(result);
213         assert(buffer);
214         assert(errnop);
215         assert(h_errnop);
216
217         alen = FAMILY_ADDRESS_SIZE(af);
218
219         for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
220                 if (af == a->family)
221                         c++;
222
223         l_canonical = strlen(canonical);
224         l_additional = additional ? strlen(additional) : 0;
225         ms = ALIGN(l_canonical+1)+
226                 (additional ? ALIGN(l_additional+1) : 0) +
227                 sizeof(char*) +
228                 (additional ? sizeof(char*) : 0) +
229                 (c > 0 ? c : 1) * ALIGN(alen) +
230                 (c > 0 ? c+1 : 2) * sizeof(char*);
231
232         if (buflen < ms) {
233                 *errnop = ENOMEM;
234                 *h_errnop = NO_RECOVERY;
235                 return NSS_STATUS_TRYAGAIN;
236         }
237
238         /* First, fill in hostnames */
239         r_name = buffer;
240         memcpy(r_name, canonical, l_canonical+1);
241         idx = ALIGN(l_canonical+1);
242
243         if (additional) {
244                 r_alias = buffer + idx;
245                 memcpy(r_alias, additional, l_additional+1);
246                 idx += ALIGN(l_additional+1);
247         }
248
249         /* Second, create aliases array */
250         r_aliases = buffer + idx;
251         if (additional) {
252                 ((char**) r_aliases)[0] = r_alias;
253                 ((char**) r_aliases)[1] = NULL;
254                 idx += 2*sizeof(char*);
255         } else {
256                 ((char**) r_aliases)[0] = NULL;
257                 idx += sizeof(char*);
258         }
259
260         /* Third, add addresses */
261         r_addr = buffer + idx;
262         if (c > 0) {
263                 unsigned i = 0;
264
265                 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
266                         if (af != a->family)
267                                 continue;
268
269                         memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
270                         i++;
271                 }
272
273                 assert(i == c);
274                 idx += c*ALIGN(alen);
275         } else {
276                 if (af == AF_INET)
277                         *(uint32_t*) r_addr = local_address_ipv4;
278                 else
279                         memcpy(r_addr, LOCALADDRESS_IPV6, 16);
280
281                 idx += ALIGN(alen);
282         }
283
284         /* Fourth, add address pointer array */
285         r_addr_list = buffer + idx;
286         if (c > 0) {
287                 unsigned i;
288
289                 for (i = 0; i < c; i++)
290                         ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
291
292                 ((char**) r_addr_list)[i] = NULL;
293                 idx += (c+1) * sizeof(char*);
294
295         } else {
296                 ((char**) r_addr_list)[0] = r_addr;
297                 ((char**) r_addr_list)[1] = NULL;
298                 idx += 2 * sizeof(char*);
299         }
300
301         /* Verify the size matches */
302         assert(idx == ms);
303
304         result->h_name = r_name;
305         result->h_aliases = (char**) r_aliases;
306         result->h_addrtype = af;
307         result->h_length = alen;
308         result->h_addr_list = (char**) r_addr_list;
309
310         if (ttlp)
311                 *ttlp = 0;
312
313         if (canonp)
314                 *canonp = r_name;
315
316         /* Explicitly reset all error variables */
317         *errnop = 0;
318         *h_errnop = NETDB_SUCCESS;
319         h_errno = 0;
320
321         return NSS_STATUS_SUCCESS;
322 }
323
324 enum nss_status _nss_myhostname_gethostbyname3_r(
325                 const char *name,
326                 int af,
327                 struct hostent *host,
328                 char *buffer, size_t buflen,
329                 int *errnop, int *h_errnop,
330                 int32_t *ttlp,
331                 char **canonp) {
332
333         _cleanup_free_ struct local_address *addresses = NULL;
334         const char *canonical, *additional = NULL;
335         _cleanup_free_ char *hn = NULL;
336         uint32_t local_address_ipv4 = 0;
337         int n_addresses = 0;
338
339         assert(name);
340         assert(host);
341         assert(buffer);
342         assert(errnop);
343         assert(h_errnop);
344
345         if (af == AF_UNSPEC)
346                 af = AF_INET;
347
348         if (af != AF_INET && af != AF_INET6) {
349                 *errnop = EAFNOSUPPORT;
350                 *h_errnop = NO_DATA;
351                 return NSS_STATUS_UNAVAIL;
352         }
353
354         if (is_localhost(name)) {
355                 canonical = "localhost";
356                 local_address_ipv4 = htonl(INADDR_LOOPBACK);
357
358         } else if (is_gateway(name)) {
359
360                 n_addresses = local_gateways(NULL, 0, af, &addresses);
361                 if (n_addresses <= 0) {
362                         *errnop = ENOENT;
363                         *h_errnop = HOST_NOT_FOUND;
364                         return NSS_STATUS_NOTFOUND;
365                 }
366
367                 canonical = "gateway";
368
369         } else {
370                 hn = gethostname_malloc();
371                 if (!hn) {
372                         *errnop = ENOMEM;
373                         *h_errnop = NO_RECOVERY;
374                         return NSS_STATUS_TRYAGAIN;
375                 }
376
377                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
378                         *errnop = ENOENT;
379                         *h_errnop = HOST_NOT_FOUND;
380                         return NSS_STATUS_NOTFOUND;
381                 }
382
383                 n_addresses = local_addresses(NULL, 0, af, &addresses);
384                 if (n_addresses < 0)
385                         n_addresses = 0;
386
387                 canonical = hn;
388                 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
389                 local_address_ipv4 = LOCALADDRESS_IPV4;
390         }
391
392         return fill_in_hostent(
393                         canonical, additional,
394                         af,
395                         addresses, n_addresses,
396                         local_address_ipv4,
397                         host,
398                         buffer, buflen,
399                         errnop, h_errnop,
400                         ttlp,
401                         canonp);
402 }
403
404 enum nss_status _nss_myhostname_gethostbyaddr2_r(
405                 const void* addr, socklen_t len,
406                 int af,
407                 struct hostent *host,
408                 char *buffer, size_t buflen,
409                 int *errnop, int *h_errnop,
410                 int32_t *ttlp) {
411
412         const char *canonical = NULL, *additional = NULL;
413         uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
414         _cleanup_free_ struct local_address *addresses = NULL;
415         _cleanup_free_ char *hn = NULL;
416         int n_addresses = 0;
417         struct local_address *a;
418         bool additional_from_hostname = false;
419         unsigned n;
420
421         assert(addr);
422         assert(host);
423         assert(buffer);
424         assert(errnop);
425         assert(h_errnop);
426
427         if (!IN_SET(af, AF_INET, AF_INET6)) {
428                 *errnop = EAFNOSUPPORT;
429                 *h_errnop = NO_DATA;
430                 return NSS_STATUS_UNAVAIL;
431         }
432
433         if (len != FAMILY_ADDRESS_SIZE(af)) {
434                 *errnop = EINVAL;
435                 *h_errnop = NO_RECOVERY;
436                 return NSS_STATUS_UNAVAIL;
437         }
438
439         if (af == AF_INET) {
440                 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
441                         goto found;
442
443                 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
444                         canonical = "localhost";
445                         local_address_ipv4 = htonl(INADDR_LOOPBACK);
446                         goto found;
447                 }
448
449         } else {
450                 assert(af == AF_INET6);
451
452                 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
453                         canonical = "localhost";
454                         additional_from_hostname = true;
455                         goto found;
456                 }
457         }
458
459         n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
460         if (n_addresses > 0) {
461                 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
462                         if (af != a->family)
463                                 continue;
464
465                         if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
466                                 goto found;
467                 }
468         }
469
470         free(addresses);
471         addresses = NULL;
472
473         n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
474         if (n_addresses > 0) {
475                 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
476                         if (af != a->family)
477                                 continue;
478
479                         if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
480                                 canonical = "gateway";
481                                 goto found;
482                         }
483                 }
484         }
485
486         *errnop = ENOENT;
487         *h_errnop = HOST_NOT_FOUND;
488
489         return NSS_STATUS_NOTFOUND;
490
491 found:
492         if (!canonical || (!additional && additional_from_hostname)) {
493                 hn = gethostname_malloc();
494                 if (!hn) {
495                         *errnop = ENOMEM;
496                         *h_errnop = NO_RECOVERY;
497                         return NSS_STATUS_TRYAGAIN;
498                 }
499
500                 if (!canonical)
501                         canonical = hn;
502
503                 if (!additional && additional_from_hostname)
504                         additional = hn;
505         }
506
507         return fill_in_hostent(
508                         canonical, additional,
509                         af,
510                         addresses, n_addresses,
511                         local_address_ipv4,
512                         host,
513                         buffer, buflen,
514                         errnop, h_errnop,
515                         ttlp,
516                         NULL);
517 }
518
519 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
520 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);