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