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