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