]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
nss-myhostname: copy first result to preallocated buffer
[thirdparty/systemd.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 }