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