]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-myhostname/nss-myhostname.c
Merge pull request #653 from dvdhrm/bus-gold
[thirdparty/systemd.git] / src / nss-myhostname / nss-myhostname.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2008-2011 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <net/if.h>
22 #include <netdb.h>
23 #include <nss.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "alloc-util.h"
28 #include "hostname-util.h"
29 #include "local-addresses.h"
30 #include "macro.h"
31 #include "nss-util.h"
32 #include "signal-util.h"
33 #include "string-util.h"
34 #include "util.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 "localhost" or so. */
40
41 #define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
42 #define LOCALADDRESS_IPV6 &in6addr_loopback
43
44 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
45 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
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) {
53
54 struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
55 _cleanup_free_ struct local_address *addresses = NULL;
56 _cleanup_free_ char *hn = NULL;
57 const char *canonical = NULL;
58 int n_addresses = 0, lo_ifi;
59 uint32_t local_address_ipv4;
60 struct local_address *a;
61 size_t l, idx, ms;
62 char *r_name;
63 unsigned n;
64
65 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
66
67 assert(name);
68 assert(pat);
69 assert(buffer);
70 assert(errnop);
71 assert(h_errnop);
72
73 if (is_localhost(name)) {
74 /* We respond to 'localhost', so that /etc/hosts
75 * is optional */
76
77 canonical = "localhost";
78 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
79
80 } else if (is_gateway_hostname(name)) {
81
82 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
83 if (n_addresses <= 0) {
84 *errnop = ENOENT;
85 *h_errnop = HOST_NOT_FOUND;
86 return NSS_STATUS_NOTFOUND;
87 }
88
89 canonical = "gateway";
90
91 } else {
92 hn = gethostname_malloc();
93 if (!hn) {
94 *errnop = ENOMEM;
95 *h_errnop = NO_RECOVERY;
96 return NSS_STATUS_TRYAGAIN;
97 }
98
99 /* We respond to our local host name, our hostname suffixed with a single dot. */
100 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
101 *errnop = ENOENT;
102 *h_errnop = HOST_NOT_FOUND;
103 return NSS_STATUS_NOTFOUND;
104 }
105
106 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
107 if (n_addresses < 0)
108 n_addresses = 0;
109
110 canonical = hn;
111 local_address_ipv4 = LOCALADDRESS_IPV4;
112 }
113
114 /* If this call fails we fill in 0 as scope. Which is fine */
115 lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0;
116
117 l = strlen(canonical);
118 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
119 if (buflen < ms) {
120 *errnop = ENOMEM;
121 *h_errnop = NO_RECOVERY;
122 return NSS_STATUS_TRYAGAIN;
123 }
124
125 /* First, fill in hostname */
126 r_name = buffer;
127 memcpy(r_name, canonical, l+1);
128 idx = ALIGN(l+1);
129
130 assert(n_addresses >= 0);
131 if (n_addresses == 0) {
132 /* Second, fill in IPv6 tuple */
133 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
134 r_tuple->next = r_tuple_prev;
135 r_tuple->name = r_name;
136 r_tuple->family = AF_INET6;
137 memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
138 r_tuple->scopeid = (uint32_t) lo_ifi;
139
140 idx += ALIGN(sizeof(struct gaih_addrtuple));
141 r_tuple_prev = r_tuple;
142
143 /* Third, fill in IPv4 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_INET;
148 *(uint32_t*) r_tuple->addr = local_address_ipv4;
149 r_tuple->scopeid = (uint32_t) lo_ifi;
150
151 idx += ALIGN(sizeof(struct gaih_addrtuple));
152 r_tuple_prev = r_tuple;
153 }
154
155 /* Fourth, fill actual addresses in, but in backwards order */
156 for (a = addresses + n_addresses - 1, n = 0; (int) n < n_addresses; n++, a--) {
157 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
158 r_tuple->next = r_tuple_prev;
159 r_tuple->name = r_name;
160 r_tuple->family = a->family;
161 r_tuple->scopeid = a->ifindex;
162 memcpy(r_tuple->addr, &a->address, 16);
163
164 idx += ALIGN(sizeof(struct gaih_addrtuple));
165 r_tuple_prev = r_tuple;
166 }
167
168 /* Verify the size matches */
169 assert(idx == ms);
170
171 /* Nscd expects us to store the first record in **pat. */
172 if (*pat)
173 **pat = *r_tuple_prev;
174 else
175 *pat = r_tuple_prev;
176
177 if (ttlp)
178 *ttlp = 0;
179
180 /* Explicitly reset all error variables */
181 *errnop = 0;
182 *h_errnop = NETDB_SUCCESS;
183 h_errno = 0;
184
185 return NSS_STATUS_SUCCESS;
186 }
187
188 static enum nss_status fill_in_hostent(
189 const char *canonical, const char *additional,
190 int af,
191 struct local_address *addresses, unsigned n_addresses,
192 uint32_t local_address_ipv4,
193 struct hostent *result,
194 char *buffer, size_t buflen,
195 int *errnop, int *h_errnop,
196 int32_t *ttlp,
197 char **canonp) {
198
199 size_t l_canonical, l_additional, idx, ms, alen;
200 char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
201 struct local_address *a;
202 unsigned n, c;
203
204 assert(canonical);
205 assert(result);
206 assert(buffer);
207 assert(errnop);
208 assert(h_errnop);
209
210 alen = FAMILY_ADDRESS_SIZE(af);
211
212 for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
213 if (af == a->family)
214 c++;
215
216 l_canonical = strlen(canonical);
217 l_additional = additional ? strlen(additional) : 0;
218 ms = ALIGN(l_canonical+1)+
219 (additional ? ALIGN(l_additional+1) : 0) +
220 sizeof(char*) +
221 (additional ? sizeof(char*) : 0) +
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 return NSS_STATUS_TRYAGAIN;
229 }
230
231 /* First, fill in hostnames */
232 r_name = buffer;
233 memcpy(r_name, canonical, l_canonical+1);
234 idx = ALIGN(l_canonical+1);
235
236 if (additional) {
237 r_alias = buffer + idx;
238 memcpy(r_alias, additional, l_additional+1);
239 idx += ALIGN(l_additional+1);
240 }
241
242 /* Second, create aliases array */
243 r_aliases = buffer + idx;
244 if (additional) {
245 ((char**) r_aliases)[0] = r_alias;
246 ((char**) r_aliases)[1] = NULL;
247 idx += 2*sizeof(char*);
248 } else {
249 ((char**) r_aliases)[0] = NULL;
250 idx += sizeof(char*);
251 }
252
253 /* Third, add addresses */
254 r_addr = buffer + idx;
255 if (c > 0) {
256 unsigned i = 0;
257
258 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
259 if (af != a->family)
260 continue;
261
262 memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
263 i++;
264 }
265
266 assert(i == c);
267 idx += c*ALIGN(alen);
268 } else {
269 if (af == AF_INET)
270 *(uint32_t*) r_addr = local_address_ipv4;
271 else
272 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
273
274 idx += ALIGN(alen);
275 }
276
277 /* Fourth, add address pointer array */
278 r_addr_list = buffer + idx;
279 if (c > 0) {
280 unsigned i;
281
282 for (i = 0; i < c; i++)
283 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
284
285 ((char**) r_addr_list)[i] = NULL;
286 idx += (c+1) * sizeof(char*);
287
288 } else {
289 ((char**) r_addr_list)[0] = r_addr;
290 ((char**) r_addr_list)[1] = NULL;
291 idx += 2 * sizeof(char*);
292 }
293
294 /* Verify the size matches */
295 assert(idx == ms);
296
297 result->h_name = r_name;
298 result->h_aliases = (char**) r_aliases;
299 result->h_addrtype = af;
300 result->h_length = alen;
301 result->h_addr_list = (char**) r_addr_list;
302
303 if (ttlp)
304 *ttlp = 0;
305
306 if (canonp)
307 *canonp = r_name;
308
309 /* Explicitly reset all error variables */
310 *errnop = 0;
311 *h_errnop = NETDB_SUCCESS;
312 h_errno = 0;
313
314 return NSS_STATUS_SUCCESS;
315 }
316
317 enum nss_status _nss_myhostname_gethostbyname3_r(
318 const char *name,
319 int af,
320 struct hostent *host,
321 char *buffer, size_t buflen,
322 int *errnop, int *h_errnop,
323 int32_t *ttlp,
324 char **canonp) {
325
326 _cleanup_free_ struct local_address *addresses = NULL;
327 const char *canonical, *additional = NULL;
328 _cleanup_free_ char *hn = NULL;
329 uint32_t local_address_ipv4 = 0;
330 int n_addresses = 0;
331
332 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
333
334 assert(name);
335 assert(host);
336 assert(buffer);
337 assert(errnop);
338 assert(h_errnop);
339
340 if (af == AF_UNSPEC)
341 af = AF_INET;
342
343 if (af != AF_INET && af != AF_INET6) {
344 *errnop = EAFNOSUPPORT;
345 *h_errnop = NO_DATA;
346 return NSS_STATUS_UNAVAIL;
347 }
348
349 if (is_localhost(name)) {
350 canonical = "localhost";
351 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
352
353 } else if (is_gateway_hostname(name)) {
354
355 n_addresses = local_gateways(NULL, 0, af, &addresses);
356 if (n_addresses <= 0) {
357 *errnop = ENOENT;
358 *h_errnop = HOST_NOT_FOUND;
359 return NSS_STATUS_NOTFOUND;
360 }
361
362 canonical = "gateway";
363
364 } else {
365 hn = gethostname_malloc();
366 if (!hn) {
367 *errnop = ENOMEM;
368 *h_errnop = NO_RECOVERY;
369 return NSS_STATUS_TRYAGAIN;
370 }
371
372 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) {
373 *errnop = ENOENT;
374 *h_errnop = HOST_NOT_FOUND;
375 return NSS_STATUS_NOTFOUND;
376 }
377
378 n_addresses = local_addresses(NULL, 0, af, &addresses);
379 if (n_addresses < 0)
380 n_addresses = 0;
381
382 canonical = hn;
383 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
384 local_address_ipv4 = LOCALADDRESS_IPV4;
385 }
386
387 return fill_in_hostent(
388 canonical, additional,
389 af,
390 addresses, n_addresses,
391 local_address_ipv4,
392 host,
393 buffer, buflen,
394 errnop, h_errnop,
395 ttlp,
396 canonp);
397 }
398
399 enum nss_status _nss_myhostname_gethostbyaddr2_r(
400 const void* addr, socklen_t len,
401 int af,
402 struct hostent *host,
403 char *buffer, size_t buflen,
404 int *errnop, int *h_errnop,
405 int32_t *ttlp) {
406
407 const char *canonical = NULL, *additional = NULL;
408 uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
409 _cleanup_free_ struct local_address *addresses = NULL;
410 _cleanup_free_ char *hn = NULL;
411 int n_addresses = 0;
412 struct local_address *a;
413 bool additional_from_hostname = false;
414 unsigned n;
415
416 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
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) == htobe32(INADDR_LOOPBACK)) {
441 canonical = "localhost";
442 local_address_ipv4 = htobe32(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 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
458 if (af != a->family)
459 continue;
460
461 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
462 goto found;
463 }
464
465 addresses = mfree(addresses);
466
467 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
468 for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) {
469 if (af != a->family)
470 continue;
471
472 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
473 canonical = "gateway";
474 goto found;
475 }
476 }
477
478 *errnop = ENOENT;
479 *h_errnop = HOST_NOT_FOUND;
480 return NSS_STATUS_NOTFOUND;
481
482 found:
483 if (!canonical || additional_from_hostname) {
484 hn = gethostname_malloc();
485 if (!hn) {
486 *errnop = ENOMEM;
487 *h_errnop = NO_RECOVERY;
488 return NSS_STATUS_TRYAGAIN;
489 }
490
491 if (!canonical)
492 canonical = hn;
493 else
494 additional = hn;
495 }
496
497 return fill_in_hostent(
498 canonical, additional,
499 af,
500 addresses, n_addresses,
501 local_address_ipv4,
502 host,
503 buffer, buflen,
504 errnop, h_errnop,
505 ttlp,
506 NULL);
507 }
508
509 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
510 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);