chiark / gitweb /
nss-myhostname: remove duplicate LICENCE
[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         *pat = r_tuple_prev;
180
181         if (ttlp)
182                 *ttlp = 0;
183
184         free(addresses);
185
186         return NSS_STATUS_SUCCESS;
187 }
188
189 static enum nss_status fill_in_hostent(
190                 const char *hn,
191                 int af,
192                 struct hostent *result,
193                 char *buffer, size_t buflen,
194                 int *errnop, int *h_errnop,
195                 int32_t *ttlp,
196                 char **canonp) {
197
198         size_t l, idx, ms;
199         char *r_addr, *r_name, *r_aliases, *r_addr_list;
200         size_t alen;
201         struct address *addresses = NULL, *a;
202         unsigned n_addresses = 0, n, c;
203
204         alen = PROTO_ADDRESS_SIZE(af);
205
206         ifconf_acquire_addresses(&addresses, &n_addresses);
207
208         for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
209                 if (af == a->family)
210                         c++;
211
212         l = strlen(hn);
213         ms = ALIGN(l+1)+
214                 sizeof(char*)+
215                 (c > 0 ? c : 1)*ALIGN(alen)+
216                 (c > 0 ? c+1 : 2)*sizeof(char*);
217
218         if (buflen < ms) {
219                 *errnop = ENOMEM;
220                 *h_errnop = NO_RECOVERY;
221                 free(addresses);
222                 return NSS_STATUS_TRYAGAIN;
223         }
224
225         /* First, fill in hostname */
226         r_name = buffer;
227         memcpy(r_name, hn, l+1);
228         idx = ALIGN(l+1);
229
230         /* Second, create (empty) aliases array */
231         r_aliases = buffer + idx;
232         *(char**) r_aliases = NULL;
233         idx += sizeof(char*);
234
235         /* Third, add addresses */
236         r_addr = buffer + idx;
237         if (c > 0) {
238                 unsigned i = 0;
239
240                 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
241                         if (af != a->family)
242                                 continue;
243
244                         memcpy(r_addr + i*ALIGN(alen), a->address, alen);
245                         i++;
246                 }
247
248                 assert(i == c);
249                 idx += c*ALIGN(alen);
250         } else {
251                 if (af == AF_INET)
252                         *(uint32_t*) r_addr = LOCALADDRESS_IPV4;
253                 else
254                         memcpy(r_addr, LOCALADDRESS_IPV6, 16);
255
256                 idx += ALIGN(alen);
257         }
258
259         /* Fourth, add address pointer array */
260         r_addr_list = buffer + idx;
261         if (c > 0) {
262                 unsigned i = 0;
263
264                 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
265                         if (af != a->family)
266                                 continue;
267
268                         ((char**) r_addr_list)[i] = (r_addr + i*ALIGN(alen));
269                         i++;
270                 }
271
272                 assert(i == c);
273                 ((char**) r_addr_list)[c] = 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         free(addresses);
298
299         return NSS_STATUS_SUCCESS;
300 }
301
302 enum nss_status _nss_myhostname_gethostbyname3_r(
303                 const char *name,
304                 int af,
305                 struct hostent *host,
306                 char *buffer, size_t buflen,
307                 int *errnop, int *h_errnop,
308                 int32_t *ttlp,
309                 char **canonp) {
310
311         char hn[HOST_NAME_MAX+1];
312
313         if (af == AF_UNSPEC)
314                 af = AF_INET;
315
316         if (af != AF_INET && af != AF_INET6) {
317                 *errnop = EAFNOSUPPORT;
318                 *h_errnop = NO_DATA;
319                 return NSS_STATUS_UNAVAIL;
320         }
321
322         memset(hn, 0, sizeof(hn));
323         if (gethostname(hn, sizeof(hn)-1) < 0) {
324                 *errnop = errno;
325                 *h_errnop = NO_RECOVERY;
326                 return NSS_STATUS_UNAVAIL;
327         }
328
329         if (strcasecmp(name, hn) != 0) {
330                 *errnop = ENOENT;
331                 *h_errnop = HOST_NOT_FOUND;
332                 return NSS_STATUS_NOTFOUND;
333         }
334
335         return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, canonp);
336 }
337
338 enum nss_status _nss_myhostname_gethostbyname2_r(
339                 const char *name,
340                 int af,
341                 struct hostent *host,
342                 char *buffer, size_t buflen,
343                 int *errnop, int *h_errnop) {
344
345         return _nss_myhostname_gethostbyname3_r(
346                         name,
347                         af,
348                         host,
349                         buffer, buflen,
350                         errnop, h_errnop,
351                         NULL,
352                         NULL);
353 }
354
355 enum nss_status _nss_myhostname_gethostbyname_r(
356                 const char *name,
357                 struct hostent *host,
358                 char *buffer, size_t buflen,
359                 int *errnop, int *h_errnop) {
360
361         return _nss_myhostname_gethostbyname3_r(
362                         name,
363                         AF_UNSPEC,
364                         host,
365                         buffer, buflen,
366                         errnop, h_errnop,
367                         NULL,
368                         NULL);
369 }
370
371 enum nss_status _nss_myhostname_gethostbyaddr2_r(
372                 const void* addr, socklen_t len,
373                 int af,
374                 struct hostent *host,
375                 char *buffer, size_t buflen,
376                 int *errnop, int *h_errnop,
377                 int32_t *ttlp) {
378
379         char hn[HOST_NAME_MAX+1];
380         struct address *addresses = NULL, *a;
381         unsigned n_addresses = 0, n;
382
383         if (len != PROTO_ADDRESS_SIZE(af)) {
384                 *errnop = EINVAL;
385                 *h_errnop = NO_RECOVERY;
386                 return NSS_STATUS_UNAVAIL;
387         }
388
389         if (af == AF_INET) {
390
391                 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
392                         goto found;
393
394         } else if (af == AF_INET6) {
395
396                 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0)
397                         goto found;
398
399         } else {
400                 *errnop = EAFNOSUPPORT;
401                 *h_errnop = NO_DATA;
402                 return NSS_STATUS_UNAVAIL;
403         }
404
405         ifconf_acquire_addresses(&addresses, &n_addresses);
406
407         for (a = addresses, n = 0; n < n_addresses; n++, a++) {
408                 if (af != a->family)
409                         continue;
410
411                 if (memcmp(addr, a->address, PROTO_ADDRESS_SIZE(af)) == 0)
412                         goto found;
413         }
414
415         *errnop = ENOENT;
416         *h_errnop = HOST_NOT_FOUND;
417
418         free(addresses);
419         return NSS_STATUS_NOTFOUND;
420
421 found:
422         free(addresses);
423
424         memset(hn, 0, sizeof(hn));
425         if (gethostname(hn, sizeof(hn)-1) < 0) {
426                 *errnop = errno;
427                 *h_errnop = NO_RECOVERY;
428
429                 return NSS_STATUS_UNAVAIL;
430         }
431
432         return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, NULL);
433
434 }
435
436 enum nss_status _nss_myhostname_gethostbyaddr_r(
437                 const void* addr, socklen_t len,
438                 int af,
439                 struct hostent *host,
440                 char *buffer, size_t buflen,
441                 int *errnop, int *h_errnop) {
442
443         return _nss_myhostname_gethostbyaddr2_r(
444                         addr, len,
445                         af,
446                         host,
447                         buffer, buflen,
448                         errnop, h_errnop,
449                         NULL);
450 }