]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
Merge pull request #57 from pwithnall/wip/pwithnall/udev-virtualbox-rules
[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 <nss.h>
23 #include <netdb.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <net/if.h>
27 #include <stdlib.h>
28
29 #include "local-addresses.h"
30 #include "macro.h"
31 #include "nss-util.h"
32 #include "hostname-util.h"
33 #include "util.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 "localhost" or so. */
39
40 #define LOCALADDRESS_IPV4 (htonl(0x7F000002))
41 #define LOCALADDRESS_IPV6 &in6addr_loopback
42 #define LOOPBACK_INTERFACE "lo"
43
44 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
45 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
46
47 static bool is_gateway(const char *hostname) {
48 assert(hostname);
49
50 return streq(hostname, "gateway") ||
51 streq(hostname, "gateway.");
52 }
53
54 enum nss_status _nss_myhostname_gethostbyname4_r(
55 const char *name,
56 struct gaih_addrtuple **pat,
57 char *buffer, size_t buflen,
58 int *errnop, int *h_errnop,
59 int32_t *ttlp) {
60
61 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
62 _cleanup_free_ struct local_address *addresses = NULL;
63 _cleanup_free_ char *hn = NULL;
64 const char *canonical = NULL;
65 int n_addresses = 0, lo_ifi;
66 uint32_t local_address_ipv4;
67 struct local_address *a;
68 size_t l, idx, ms;
69 char *r_name;
70 unsigned n;
71
72 assert(name);
73 assert(pat);
74 assert(buffer);
75 assert(errnop);
76 assert(h_errnop);
77
78 if (is_localhost(name)) {
79 /* We respond to 'localhost', so that /etc/hosts
80 * is optional */
81
82 canonical = "localhost";
83 local_address_ipv4 = htonl(INADDR_LOOPBACK);
84
85 } else if (is_gateway(name)) {
86
87 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
88 if (n_addresses <= 0) {
89 *errnop = ENOENT;
90 *h_errnop = HOST_NOT_FOUND;
91 return NSS_STATUS_NOTFOUND;
92 }
93
94 canonical = "gateway";
95
96 } else {
97 hn = gethostname_malloc();
98 if (!hn) {
99 *errnop = ENOMEM;
100 *h_errnop = NO_RECOVERY;
101 return NSS_STATUS_TRYAGAIN;
102 }
103
104 /* We respond to our local host name, our our hostname suffixed with a single dot. */
105 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
106 *errnop = ENOENT;
107 *h_errnop = HOST_NOT_FOUND;
108 return NSS_STATUS_NOTFOUND;
109 }
110
111 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
112 if (n_addresses < 0)
113 n_addresses = 0;
114
115 canonical = hn;
116 local_address_ipv4 = LOCALADDRESS_IPV4;
117 }
118
119 /* If this call fails we fill in 0 as scope. Which is fine */
120 lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
121
122 l = strlen(canonical);
123 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
124 if (buflen < ms) {
125 *errnop = ENOMEM;
126 *h_errnop = NO_RECOVERY;
127 return NSS_STATUS_TRYAGAIN;
128 }
129
130 /* First, fill in hostname */
131 r_name = buffer;
132 memcpy(r_name, canonical, l+1);
133 idx = ALIGN(l+1);
134
135 if (n_addresses <= 0) {
136 /* Second, fill in IPv6 tuple */
137 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
138 r_tuple->next = r_tuple_prev;
139 r_tuple->name = r_name;
140 r_tuple->family = AF_INET6;
141 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
142 r_tuple->scopeid = (uint32_t) lo_ifi;
143
144 idx += ALIGN(sizeof(struct gaih_addrtuple));
145 r_tuple_prev = r_tuple;
146
147 /* Third, fill in IPv4 tuple */
148 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
149 r_tuple->next = r_tuple_prev;
150 r_tuple->name = r_name;
151 r_tuple->family = AF_INET;
152 *(uint32_t*) r_tuple->addr = local_address_ipv4;
153 r_tuple->scopeid = (uint32_t) lo_ifi;
154
155 idx += ALIGN(sizeof(struct gaih_addrtuple));
156 r_tuple_prev = r_tuple;
157 }
158
159 /* Fourth, fill actual addresses in, but in backwards order */
160 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
161 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
162 r_tuple->next = r_tuple_prev;
163 r_tuple->name = r_name;
164 r_tuple->family = a->family;
165 r_tuple->scopeid = a->ifindex;
166 memcpy(r_tuple->addr, &a->address, 16);
167
168 idx += ALIGN(sizeof(struct gaih_addrtuple));
169 r_tuple_prev = r_tuple;
170 }
171
172 /* Verify the size matches */
173 assert(idx == ms);
174
175 /* Nscd expects us to store the first record in **pat. */
176 if (*pat)
177 **pat = *r_tuple_prev;
178 else
179 *pat = r_tuple_prev;
180
181 if (ttlp)
182 *ttlp = 0;
183
184 /* Explicitly reset all error variables */
185 *errnop = 0;
186 *h_errnop = NETDB_SUCCESS;
187 h_errno = 0;
188
189 return NSS_STATUS_SUCCESS;
190 }
191
192 static enum nss_status fill_in_hostent(
193 const char *canonical, const char *additional,
194 int af,
195 struct local_address *addresses, unsigned n_addresses,
196 uint32_t local_address_ipv4,
197 struct hostent *result,
198 char *buffer, size_t buflen,
199 int *errnop, int *h_errnop,
200 int32_t *ttlp,
201 char **canonp) {
202
203 size_t l_canonical, l_additional, idx, ms, alen;
204 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
205 struct local_address *a;
206 unsigned n, c;
207
208 assert(canonical);
209 assert(result);
210 assert(buffer);
211 assert(errnop);
212 assert(h_errnop);
213
214 alen = FAMILY_ADDRESS_SIZE(af);
215
216 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
217 if (af == a->family)
218 c++;
219
220 l_canonical = strlen(canonical);
221 l_additional = additional ? strlen(additional) : 0;
222 ms = ALIGN(l_canonical+1)+
223 (additional ? ALIGN(l_additional+1) : 0) +
224 sizeof(char*) +
225 (additional ? sizeof(char*) : 0) +
226 (c > 0 ? c : 1) * ALIGN(alen) +
227 (c > 0 ? c+1 : 2) * sizeof(char*);
228
229 if (buflen < ms) {
230 *errnop = ENOMEM;
231 *h_errnop = NO_RECOVERY;
232 return NSS_STATUS_TRYAGAIN;
233 }
234
235 /* First, fill in hostnames */
236 r_name = buffer;
237 memcpy(r_name, canonical, l_canonical+1);
238 idx = ALIGN(l_canonical+1);
239
240 if (additional) {
241 r_alias = buffer + idx;
242 memcpy(r_alias, additional, l_additional+1);
243 idx += ALIGN(l_additional+1);
244 }
245
246 /* Second, create aliases array */
247 r_aliases = buffer + idx;
248 if (additional) {
249 ((char**) r_aliases)[0] = r_alias;
250 ((char**) r_aliases)[1] = NULL;
251 idx += 2*sizeof(char*);
252 } else {
253 ((char**) r_aliases)[0] = NULL;
254 idx += sizeof(char*);
255 }
256
257 /* Third, add addresses */
258 r_addr = buffer + idx;
259 if (c > 0) {
260 unsigned i = 0;
261
262 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
263 if (af != a->family)
264 continue;
265
266 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
267 i++;
268 }
269
270 assert(i == c);
271 idx += c*ALIGN(alen);
272 } else {
273 if (af == AF_INET)
274 *(uint32_t*) r_addr = local_address_ipv4;
275 else
276 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
277
278 idx += ALIGN(alen);
279 }
280
281 /* Fourth, add address pointer array */
282 r_addr_list = buffer + idx;
283 if (c > 0) {
284 unsigned i;
285
286 for (i = 0; i < c; i++)
287 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
288
289 ((char**) r_addr_list)[i] = NULL;
290 idx += (c+1) * sizeof(char*);
291
292 } else {
293 ((char**) r_addr_list)[0] = r_addr;
294 ((char**) r_addr_list)[1] = NULL;
295 idx += 2 * sizeof(char*);
296 }
297
298 /* Verify the size matches */
299 assert(idx == ms);
300
301 result->h_name = r_name;
302 result->h_aliases = (char**) r_aliases;
303 result->h_addrtype = af;
304 result->h_length = alen;
305 result->h_addr_list = (char**) r_addr_list;
306
307 if (ttlp)
308 *ttlp = 0;
309
310 if (canonp)
311 *canonp = r_name;
312
313 /* Explicitly reset all error variables */
314 *errnop = 0;
315 *h_errnop = NETDB_SUCCESS;
316 h_errno = 0;
317
318 return NSS_STATUS_SUCCESS;
319 }
320
321 enum nss_status _nss_myhostname_gethostbyname3_r(
322 const char *name,
323 int af,
324 struct hostent *host,
325 char *buffer, size_t buflen,
326 int *errnop, int *h_errnop,
327 int32_t *ttlp,
328 char **canonp) {
329
330 _cleanup_free_ struct local_address *addresses = NULL;
331 const char *canonical, *additional = NULL;
332 _cleanup_free_ char *hn = NULL;
333 uint32_t local_address_ipv4 = 0;
334 int n_addresses = 0;
335
336 assert(name);
337 assert(host);
338 assert(buffer);
339 assert(errnop);
340 assert(h_errnop);
341
342 if (af == AF_UNSPEC)
343 af = AF_INET;
344
345 if (af != AF_INET && af != AF_INET6) {
346 *errnop = EAFNOSUPPORT;
347 *h_errnop = NO_DATA;
348 return NSS_STATUS_UNAVAIL;
349 }
350
351 if (is_localhost(name)) {
352 canonical = "localhost";
353 local_address_ipv4 = htonl(INADDR_LOOPBACK);
354
355 } else if (is_gateway(name)) {
356
357 n_addresses = local_gateways(NULL, 0, af, &addresses);
358 if (n_addresses <= 0) {
359 *errnop = ENOENT;
360 *h_errnop = HOST_NOT_FOUND;
361 return NSS_STATUS_NOTFOUND;
362 }
363
364 canonical = "gateway";
365
366 } else {
367 hn = gethostname_malloc();
368 if (!hn) {
369 *errnop = ENOMEM;
370 *h_errnop = NO_RECOVERY;
371 return NSS_STATUS_TRYAGAIN;
372 }
373
374 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
375 *errnop = ENOENT;
376 *h_errnop = HOST_NOT_FOUND;
377 return NSS_STATUS_NOTFOUND;
378 }
379
380 n_addresses = local_addresses(NULL, 0, af, &addresses);
381 if (n_addresses < 0)
382 n_addresses = 0;
383
384 canonical = hn;
385 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
386 local_address_ipv4 = LOCALADDRESS_IPV4;
387 }
388
389 return fill_in_hostent(
390 canonical, additional,
391 af,
392 addresses, n_addresses,
393 local_address_ipv4,
394 host,
395 buffer, buflen,
396 errnop, h_errnop,
397 ttlp,
398 canonp);
399 }
400
401 enum nss_status _nss_myhostname_gethostbyaddr2_r(
402 const void* addr, socklen_t len,
403 int af,
404 struct hostent *host,
405 char *buffer, size_t buflen,
406 int *errnop, int *h_errnop,
407 int32_t *ttlp) {
408
409 const char *canonical = NULL, *additional = NULL;
410 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
411 _cleanup_free_ struct local_address *addresses = NULL;
412 _cleanup_free_ char *hn = NULL;
413 int n_addresses = 0;
414 struct local_address *a;
415 bool additional_from_hostname = false;
416 unsigned n;
417
418 assert(addr);
419 assert(host);
420 assert(buffer);
421 assert(errnop);
422 assert(h_errnop);
423
424 if (!IN_SET(af, AF_INET, AF_INET6)) {
425 *errnop = EAFNOSUPPORT;
426 *h_errnop = NO_DATA;
427 return NSS_STATUS_UNAVAIL;
428 }
429
430 if (len != FAMILY_ADDRESS_SIZE(af)) {
431 *errnop = EINVAL;
432 *h_errnop = NO_RECOVERY;
433 return NSS_STATUS_UNAVAIL;
434 }
435
436 if (af == AF_INET) {
437 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
438 goto found;
439
440 if ((*(uint32_t*) addr) == htonl(INADDR_LOOPBACK)) {
441 canonical = "localhost";
442 local_address_ipv4 = htonl(INADDR_LOOPBACK);
443 goto found;
444 }
445
446 } else {
447 assert(af == AF_INET6);
448
449 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
450 canonical = "localhost";
451 additional_from_hostname = true;
452 goto found;
453 }
454 }
455
456 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
457 if (n_addresses > 0) {
458 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
459 if (af != a->family)
460 continue;
461
462 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
463 goto found;
464 }
465 }
466
467 free(addresses);
468 addresses = NULL;
469
470 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
471 if (n_addresses > 0) {
472 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
473 if (af != a->family)
474 continue;
475
476 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
477 canonical = "gateway";
478 goto found;
479 }
480 }
481 }
482
483 *errnop = ENOENT;
484 *h_errnop = HOST_NOT_FOUND;
485
486 return NSS_STATUS_NOTFOUND;
487
488 found:
489 if (!canonical || (!additional && additional_from_hostname)) {
490 hn = gethostname_malloc();
491 if (!hn) {
492 *errnop = ENOMEM;
493 *h_errnop = NO_RECOVERY;
494 return NSS_STATUS_TRYAGAIN;
495 }
496
497 if (!canonical)
498 canonical = hn;
499
500 if (!additional && additional_from_hostname)
501 additional = hn;
502 }
503
504 return fill_in_hostent(
505 canonical, additional,
506 af,
507 addresses, n_addresses,
508 local_address_ipv4,
509 host,
510 buffer, buflen,
511 errnop, h_errnop,
512 ttlp,
513 NULL);
514 }
515
516 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
517 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);