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