chiark / gitweb /
06bd842fc61bfd70b7b9dc225af8f2ae604b5aec
[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;
188         char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
189         size_t alen;
190         struct local_address *a;
191         unsigned n, c;
192
193         assert(canonical);
194         assert(result);
195         assert(buffer);
196         assert(errnop);
197         assert(h_errnop);
198
199         alen = PROTO_ADDRESS_SIZE(af);
200
201         for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
202                 if (af == a->family)
203                         c++;
204
205         l_canonical = strlen(canonical);
206         l_additional = additional ? strlen(additional) : 0;
207         ms = ALIGN(l_canonical+1)+
208                 (additional ? ALIGN(l_additional+1) : 0) +
209                 sizeof(char*) +
210                 (additional ? sizeof(char*) : 0) +
211                 (c > 0 ? c : 1) * ALIGN(alen)+
212                 (c > 0 ? c+1 : 2) * sizeof(char*);
213
214         if (buflen < ms) {
215                 *errnop = ENOMEM;
216                 *h_errnop = NO_RECOVERY;
217                 return NSS_STATUS_TRYAGAIN;
218         }
219
220         /* First, fill in hostnames */
221         r_name = buffer;
222         memcpy(r_name, canonical, l_canonical+1);
223         idx = ALIGN(l_canonical+1);
224
225         if (additional) {
226                 r_alias = buffer + idx;
227                 memcpy(r_alias, additional, l_additional+1);
228                 idx += ALIGN(l_additional+1);
229         }
230
231         /* Second, create aliases array */
232         r_aliases = buffer + idx;
233         if (additional) {
234                 ((char**) r_aliases)[0] = r_alias;
235                 ((char**) r_aliases)[1] = NULL;
236                 idx += 2*sizeof(char*);
237         } else {
238                 ((char**) r_aliases)[0] = NULL;
239                 idx += sizeof(char*);
240         }
241
242         /* Third, add addresses */
243         r_addr = buffer + idx;
244         if (c > 0) {
245                 unsigned i = 0;
246
247                 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
248                         if (af != a->family)
249                                 continue;
250
251                         memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
252                         i++;
253                 }
254
255                 assert(i == c);
256                 idx += c*ALIGN(alen);
257         } else {
258                 if (af == AF_INET)
259                         *(uint32_t*) r_addr = local_address_ipv4;
260                 else
261                         memcpy(r_addr, LOCALADDRESS_IPV6, 16);
262
263                 idx += ALIGN(alen);
264         }
265
266         /* Fourth, add address pointer array */
267         r_addr_list = buffer + idx;
268         if (c > 0) {
269                 unsigned i = 0;
270
271                 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
272                         if (af != a->family)
273                                 continue;
274
275                         ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
276                         i++;
277                 }
278
279                 assert(i == c);
280                 ((char**) r_addr_list)[c] = NULL;
281                 idx += (c+1)*sizeof(char*);
282
283         } else {
284                 ((char**) r_addr_list)[0] = r_addr;
285                 ((char**) r_addr_list)[1] = NULL;
286                 idx += 2*sizeof(char*);
287         }
288
289         /* Verify the size matches */
290         assert(idx == ms);
291
292         result->h_name = r_name;
293         result->h_aliases = (char**) r_aliases;
294         result->h_addrtype = af;
295         result->h_length = alen;
296         result->h_addr_list = (char**) r_addr_list;
297
298         if (ttlp)
299                 *ttlp = 0;
300
301         if (canonp)
302                 *canonp = r_name;
303
304         return NSS_STATUS_SUCCESS;
305 }
306
307 enum nss_status _nss_myhostname_gethostbyname3_r(
308                 const char *name,
309                 int af,
310                 struct hostent *host,
311                 char *buffer, size_t buflen,
312                 int *errnop, int *h_errnop,
313                 int32_t *ttlp,
314                 char **canonp) {
315
316         _cleanup_free_ struct local_address *addresses = NULL;
317         const char *canonical, *additional = NULL;
318         _cleanup_free_ char *hn = NULL;
319         uint32_t local_address_ipv4;
320         int n_addresses = 0;
321
322         assert(name);
323         assert(host);
324         assert(buffer);
325         assert(errnop);
326         assert(h_errnop);
327
328         if (af == AF_UNSPEC)
329                 af = AF_INET;
330
331         if (af != AF_INET && af != AF_INET6) {
332                 *errnop = EAFNOSUPPORT;
333                 *h_errnop = NO_DATA;
334                 return NSS_STATUS_UNAVAIL;
335         }
336
337         if (is_localhost(name)) {
338                 canonical = "localhost";
339                 local_address_ipv4 = htonl(INADDR_LOOPBACK);
340         } else {
341                 hn = gethostname_malloc();
342                 if (!hn) {
343                         *errnop = ENOMEM;
344                         *h_errnop = NO_RECOVERY;
345                         return NSS_STATUS_TRYAGAIN;
346                 }
347
348                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
349                         *errnop = ENOENT;
350                         *h_errnop = HOST_NOT_FOUND;
351                         return NSS_STATUS_NOTFOUND;
352                 }
353
354                 n_addresses = local_addresses(&addresses);
355                 if (n_addresses < 0)
356                         n_addresses = 0;
357
358                 canonical = hn;
359                 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
360                 local_address_ipv4 = LOCALADDRESS_IPV4;
361         }
362
363         return fill_in_hostent(
364                         canonical, additional,
365                         af,
366                         addresses, n_addresses,
367                         local_address_ipv4,
368                         host,
369                         buffer, buflen,
370                         errnop, h_errnop,
371                         ttlp,
372                         canonp);
373 }
374
375 enum nss_status _nss_myhostname_gethostbyaddr2_r(
376                 const void* addr, socklen_t len,
377                 int af,
378                 struct hostent *host,
379                 char *buffer, size_t buflen,
380                 int *errnop, int *h_errnop,
381                 int32_t *ttlp) {
382
383         const char *canonical = NULL, *additional = NULL;
384         uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
385         _cleanup_free_ struct local_address *addresses = NULL;
386         _cleanup_free_ char *hn = NULL;
387         int n_addresses = 0;
388         struct local_address *a;
389         unsigned n;
390
391         assert(addr);
392         assert(host);
393         assert(buffer);
394         assert(errnop);
395         assert(h_errnop);
396
397         if (len != PROTO_ADDRESS_SIZE(af)) {
398                 *errnop = EINVAL;
399                 *h_errnop = NO_RECOVERY;
400                 return NSS_STATUS_UNAVAIL;
401         }
402
403         if (af == AF_INET) {
404
405                 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
406                         goto found;
407
408                 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
409                         canonical = "localhost";
410                         local_address_ipv4 = htonl(INADDR_LOOPBACK);
411                         goto found;
412                 }
413
414         } else if (af == AF_INET6) {
415
416                 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
417                         additional = "localhost";
418                         goto found;
419                 }
420
421         } else {
422                 *errnop = EAFNOSUPPORT;
423                 *h_errnop = NO_DATA;
424                 return NSS_STATUS_UNAVAIL;
425         }
426
427         n_addresses = local_addresses(&addresses);
428         if (n_addresses < 0)
429                 n_addresses = 0;
430
431         for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
432                 if (af != a->family)
433                         continue;
434
435                 if (memcmp(addr, &a->address, PROTO_ADDRESS_SIZE(af)) == 0)
436                         goto found;
437         }
438
439         *errnop = ENOENT;
440         *h_errnop = HOST_NOT_FOUND;
441
442         return NSS_STATUS_NOTFOUND;
443
444 found:
445         if (!canonical) {
446                 hn = gethostname_malloc();
447                 if (!hn) {
448                         *errnop = ENOMEM;
449                         *h_errnop = NO_RECOVERY;
450                         return NSS_STATUS_TRYAGAIN;
451                 }
452
453                 canonical = hn;
454         }
455
456         return fill_in_hostent(
457                         canonical, additional,
458                         af,
459                         addresses, n_addresses,
460                         local_address_ipv4,
461                         host,
462                         buffer, buflen,
463                         errnop, h_errnop,
464                         ttlp,
465                         NULL);
466
467 }
468
469 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
470 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);