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