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