chiark / gitweb /
autoconfization
[elogind.git] / nss-myhostname.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4     This file is part of nss-myhostname.
5
6     Copyright 2008 Lennart Poettering
7
8     nss-myhostname is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Lesser General Public License
10     as published by the Free Software Foundation, either version 2.1
11     of the License, or (at your option) any later version.
12
13     nss-myhostname is distributed in the hope that it will be useful,
14     but 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
19     License along with nss-myhostname. If not, If not, see
20     <http://www.gnu.org/licenses/>.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <limits.h>
28 #include <nss.h>
29 #include <sys/types.h>
30 #include <netdb.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <assert.h>
35 #include <unistd.h>
36 #include <net/if.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 #define ALIGN(a) (((a+sizeof(void*)-1)/sizeof(void*))*sizeof(void*))
48
49 enum nss_status _nss_myhostname_gethostbyname4_r(
50                 const char *name,
51                 struct gaih_addrtuple **pat,
52                 char *buffer, size_t buflen,
53                 int *errnop, int *h_errnop,
54                 int32_t *ttlp) {
55
56         unsigned ifi;
57         char hn[HOST_NAME_MAX+1];
58         size_t l, idx, ms;
59         char *r_name;
60         struct gaih_addrtuple *r_tuple1, *r_tuple2;
61
62         memset(hn, 0, sizeof(hn));
63         if (gethostname(hn, sizeof(hn)-1) < 0) {
64                 *errnop = errno;
65                 *h_errnop = NO_RECOVERY;
66                 return NSS_STATUS_UNAVAIL;
67         }
68
69         if (strcasecmp(name, hn) != 0) {
70                 *errnop = ENOENT;
71                 *h_errnop = HOST_NOT_FOUND;
72                 return NSS_STATUS_NOTFOUND;
73         }
74
75         /* If this call fails we fill in 0 as scope. Which is fine */
76         ifi = if_nametoindex(LOOPBACK_INTERFACE);
77
78         l = strlen(hn);
79         ms = ALIGN(l+1)+ALIGN(sizeof(struct gaih_addrtuple))*2;
80         if (buflen < ms) {
81                 *errnop = ENOMEM;
82                 *h_errnop = NO_RECOVERY;
83                 return NSS_STATUS_TRYAGAIN;
84         }
85
86         /* First, fill in hostname */
87         r_name = buffer;
88         memcpy(r_name, hn, l+1);
89         idx = ALIGN(l+1);
90
91         /* Second, fill in IPv4 tuple */
92         r_tuple1 = (struct gaih_addrtuple*) (buffer + idx);
93         r_tuple1->next = NULL;
94         r_tuple1->name = r_name;
95         r_tuple1->family = AF_INET;
96         *(uint32_t*) r_tuple1->addr = LOCALADDRESS_IPV4;
97         r_tuple1->scopeid = (uint32_t) ifi;
98         idx += ALIGN(sizeof(struct gaih_addrtuple));
99
100         /* Third, fill in IPv6 tuple */
101         r_tuple2 = (struct gaih_addrtuple*) (buffer + idx);
102         r_tuple2->next = r_tuple1;
103         r_tuple2->name = r_name;
104         r_tuple2->family = AF_INET6;
105         memcpy(r_tuple2->addr, LOCALADDRESS_IPV6, 16);
106         r_tuple1->scopeid = (uint32_t) ifi;
107         idx += ALIGN(sizeof(struct gaih_addrtuple));
108
109         /* Verify the size matches */
110         assert(idx == ms);
111
112         *pat = r_tuple2;
113
114         if (ttlp)
115                 *ttlp = 0;
116
117         return NSS_STATUS_SUCCESS;
118 }
119
120 static enum nss_status fill_in_hostent(
121                 const char *hn,
122                 int af,
123                 struct hostent *result,
124                 char *buffer, size_t buflen,
125                 int *errnop, int *h_errnop,
126                 int32_t *ttlp,
127                 char **canonp) {
128
129         size_t l, idx, ms;
130         char *r_addr, *r_name, *r_aliases, *r_addr_list;
131         size_t alen;
132
133         alen = af == AF_INET ? 4 : 16;
134
135         l = strlen(hn);
136         ms = ALIGN(l+1)+sizeof(char*)+ALIGN(alen)+sizeof(char*)*2;
137         if (buflen < ms) {
138                 *errnop = ENOMEM;
139                 *h_errnop = NO_RECOVERY;
140                 return NSS_STATUS_TRYAGAIN;
141         }
142
143         /* First, fill in hostname */
144         r_name = buffer;
145         memcpy(r_name, hn, l+1);
146         idx = ALIGN(l+1);
147
148         /* Second, create aliases array */
149         r_aliases = buffer + idx;
150         *(char**) r_aliases = NULL;
151         idx += sizeof(char*);
152
153         /* Third, add address */
154         r_addr = buffer + idx;
155         if (af == AF_INET)
156                 *(uint32_t*) r_addr = LOCALADDRESS_IPV4;
157         else
158                 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
159         idx += ALIGN(alen);
160
161         /* Fourth, add address pointer array */
162         r_addr_list = buffer + idx;
163         ((char**) r_addr_list)[0] = r_addr;
164         ((char**) r_addr_list)[1] = NULL;
165         idx += sizeof(char*)*2;
166
167         /* Verify the size matches */
168         assert(idx == ms);
169
170         result->h_name = r_name;
171         result->h_aliases = (char**) r_aliases;
172         result->h_addrtype = af;
173         result->h_length = alen;
174         result->h_addr_list = (char**) r_addr_list;
175
176         if (ttlp)
177                 *ttlp = 0;
178
179         if (canonp)
180                 *canonp = r_name;
181
182         return NSS_STATUS_SUCCESS;
183 }
184
185 enum nss_status _nss_myhostname_gethostbyname3_r(
186                 const char *name,
187                 int af,
188                 struct hostent *host,
189                 char *buffer, size_t buflen,
190                 int *errnop, int *h_errnop,
191                 int32_t *ttlp,
192                 char **canonp) {
193
194         char hn[HOST_NAME_MAX+1];
195
196         if (af == AF_UNSPEC)
197                 af = AF_INET;
198
199         if (af != AF_INET && af != AF_INET6) {
200                 *errnop = EAFNOSUPPORT;
201                 *h_errnop = NO_DATA;
202                 return NSS_STATUS_UNAVAIL;
203         }
204
205         memset(hn, 0, sizeof(hn));
206         if (gethostname(hn, sizeof(hn)-1) < 0) {
207                 *errnop = errno;
208                 *h_errnop = NO_RECOVERY;
209                 return NSS_STATUS_UNAVAIL;
210         }
211
212         if (strcasecmp(name, hn) != 0) {
213                 *errnop = ENOENT;
214                 *h_errnop = HOST_NOT_FOUND;
215                 return NSS_STATUS_NOTFOUND;
216         }
217
218         return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, canonp);
219 }
220
221 enum nss_status _nss_myhostname_gethostbyname2_r(
222                 const char *name,
223                 int af,
224                 struct hostent *host,
225                 char *buffer, size_t buflen,
226                 int *errnop, int *h_errnop) {
227
228         return _nss_myhostname_gethostbyname3_r(
229                         name,
230                         af,
231                         host,
232                         buffer, buflen,
233                         errnop, h_errnop,
234                         NULL,
235                         NULL);
236 }
237
238 enum nss_status _nss_myhostname_gethostbyname_r (
239                 const char *name,
240                 struct hostent *host,
241                 char *buffer, size_t buflen,
242                 int *errnop, int *h_errnop) {
243
244         return _nss_myhostname_gethostbyname3_r(
245                         name,
246                         AF_UNSPEC,
247                         host,
248                         buffer, buflen,
249                         errnop, h_errnop,
250                         NULL,
251                         NULL);
252 }
253
254 enum nss_status _nss_myhostname_gethostbyaddr2_r(
255                 const void* addr, socklen_t len,
256                 int af,
257                 struct hostent *host,
258                 char *buffer, size_t buflen,
259                 int *errnop, int *h_errnop,
260                 int32_t *ttlp) {
261
262         char hn[HOST_NAME_MAX+1];
263
264         if (af == AF_INET) {
265                 if (len != 4 ||
266                     (*(uint32_t*) addr) != LOCALADDRESS_IPV4)
267                         goto not_found;
268
269         } else if (af == AF_INET6) {
270                 if (len != 16 ||
271                     memcmp(addr, LOCALADDRESS_IPV6, 16) != 0)
272                         goto not_found;
273         } else {
274                 *errnop = EAFNOSUPPORT;
275                 *h_errnop = NO_DATA;
276                 return NSS_STATUS_UNAVAIL;
277         }
278
279         memset(hn, 0, sizeof(hn));
280         if (gethostname(hn, sizeof(hn)-1) < 0) {
281                 *errnop = errno;
282                 *h_errnop = NO_RECOVERY;
283                 return NSS_STATUS_UNAVAIL;
284         }
285
286         return fill_in_hostent(hn, af, host, buffer, buflen, errnop, h_errnop, ttlp, NULL);
287
288 not_found:
289         *errnop = ENOENT;
290         *h_errnop = HOST_NOT_FOUND;
291         return NSS_STATUS_NOTFOUND;
292 }
293
294 enum nss_status _nss_myhostname_gethostbyaddr_r(
295                 const void* addr, socklen_t len,
296                 int af,
297                 struct hostent *host,
298                 char *buffer, size_t buflen,
299                 int *errnop, int *h_errnop) {
300
301         return _nss_myhostname_gethostbyaddr2_r(
302                         addr, len,
303                         af,
304                         host,
305                         buffer, buflen,
306                         errnop, h_errnop,
307                         NULL);
308 }