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