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