chiark / gitweb /
bus-proxyd: explicitly address messages to unique and well-known name
[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(NULL, 0, &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         /* Explicitly reset all error variables */
169         *errnop = 0;
170         *h_errnop = NETDB_SUCCESS;
171         h_errno = 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 = FAMILY_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         /* Explicitly reset all error variables */
298         *errnop = 0;
299         *h_errnop = NETDB_SUCCESS;
300         h_errno = 0;
301
302         return NSS_STATUS_SUCCESS;
303 }
304
305 enum nss_status _nss_myhostname_gethostbyname3_r(
306                 const char *name,
307                 int af,
308                 struct hostent *host,
309                 char *buffer, size_t buflen,
310                 int *errnop, int *h_errnop,
311                 int32_t *ttlp,
312                 char **canonp) {
313
314         _cleanup_free_ struct local_address *addresses = NULL;
315         const char *canonical, *additional = NULL;
316         _cleanup_free_ char *hn = NULL;
317         uint32_t local_address_ipv4;
318         int n_addresses = 0;
319
320         assert(name);
321         assert(host);
322         assert(buffer);
323         assert(errnop);
324         assert(h_errnop);
325
326         if (af == AF_UNSPEC)
327                 af = AF_INET;
328
329         if (af != AF_INET && af != AF_INET6) {
330                 *errnop = EAFNOSUPPORT;
331                 *h_errnop = NO_DATA;
332                 return NSS_STATUS_UNAVAIL;
333         }
334
335         if (is_localhost(name)) {
336                 canonical = "localhost";
337                 local_address_ipv4 = htonl(INADDR_LOOPBACK);
338         } else {
339                 hn = gethostname_malloc();
340                 if (!hn) {
341                         *errnop = ENOMEM;
342                         *h_errnop = NO_RECOVERY;
343                         return NSS_STATUS_TRYAGAIN;
344                 }
345
346                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
347                         *errnop = ENOENT;
348                         *h_errnop = HOST_NOT_FOUND;
349                         return NSS_STATUS_NOTFOUND;
350                 }
351
352                 n_addresses = local_addresses(NULL, 0, &addresses);
353                 if (n_addresses < 0)
354                         n_addresses = 0;
355
356                 canonical = hn;
357                 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
358                 local_address_ipv4 = LOCALADDRESS_IPV4;
359         }
360
361         return fill_in_hostent(
362                         canonical, additional,
363                         af,
364                         addresses, n_addresses,
365                         local_address_ipv4,
366                         host,
367                         buffer, buflen,
368                         errnop, h_errnop,
369                         ttlp,
370                         canonp);
371 }
372
373 enum nss_status _nss_myhostname_gethostbyaddr2_r(
374                 const void* addr, socklen_t len,
375                 int af,
376                 struct hostent *host,
377                 char *buffer, size_t buflen,
378                 int *errnop, int *h_errnop,
379                 int32_t *ttlp) {
380
381         const char *canonical = NULL, *additional = NULL;
382         uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
383         _cleanup_free_ struct local_address *addresses = NULL;
384         _cleanup_free_ char *hn = NULL;
385         int n_addresses = 0;
386         struct local_address *a;
387         unsigned n;
388
389         assert(addr);
390         assert(host);
391         assert(buffer);
392         assert(errnop);
393         assert(h_errnop);
394
395         if (!IN_SET(af, AF_INET, AF_INET6)) {
396                 *errnop = EAFNOSUPPORT;
397                 *h_errnop = NO_DATA;
398                 return NSS_STATUS_UNAVAIL;
399         }
400
401         if (len != FAMILY_ADDRESS_SIZE(af)) {
402                 *errnop = EINVAL;
403                 *h_errnop = NO_RECOVERY;
404                 return NSS_STATUS_UNAVAIL;
405         }
406
407         if (af == AF_INET) {
408
409                 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
410                         goto found;
411
412                 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
413                         canonical = "localhost";
414                         local_address_ipv4 = htonl(INADDR_LOOPBACK);
415                         goto found;
416                 }
417
418         } else {
419                 assert(af == AF_INET6);
420
421                 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
422                         additional = "localhost";
423                         goto found;
424                 }
425
426         }
427
428         n_addresses = local_addresses(NULL, 0, &addresses);
429         if (n_addresses < 0)
430                 n_addresses = 0;
431
432         for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
433                 if (af != a->family)
434                         continue;
435
436                 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
437                         goto found;
438         }
439
440         *errnop = ENOENT;
441         *h_errnop = HOST_NOT_FOUND;
442
443         return NSS_STATUS_NOTFOUND;
444
445 found:
446         if (!canonical) {
447                 hn = gethostname_malloc();
448                 if (!hn) {
449                         *errnop = ENOMEM;
450                         *h_errnop = NO_RECOVERY;
451                         return NSS_STATUS_TRYAGAIN;
452                 }
453
454                 canonical = hn;
455         }
456
457         return fill_in_hostent(
458                         canonical, additional,
459                         af,
460                         addresses, n_addresses,
461                         local_address_ipv4,
462                         host,
463                         buffer, buflen,
464                         errnop, h_errnop,
465                         ttlp,
466                         NULL);
467
468 }
469
470 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
471 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);