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