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