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