]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-resolve/nss-resolve.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / nss-resolve / nss-resolve.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <netdb.h>
5 #include <nss.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "sd-bus.h"
10
11 #include "bus-common-errors.h"
12 #include "in-addr-util.h"
13 #include "macro.h"
14 #include "nss-util.h"
15 #include "resolved-def.h"
16 #include "string-util.h"
17 #include "util.h"
18 #include "signal-util.h"
19
20 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
21 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
22
23 static bool bus_error_shall_fallback(sd_bus_error *e) {
24 return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
25 sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) ||
26 sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) ||
27 sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED);
28 }
29
30 static int count_addresses(sd_bus_message *m, int af, const char **canonical) {
31 int c = 0, r;
32
33 assert(m);
34 assert(canonical);
35
36 r = sd_bus_message_enter_container(m, 'a', "(iiay)");
37 if (r < 0)
38 return r;
39
40 while ((r = sd_bus_message_enter_container(m, 'r', "iiay")) > 0) {
41 int family, ifindex;
42
43 assert_cc(sizeof(int32_t) == sizeof(int));
44
45 r = sd_bus_message_read(m, "ii", &ifindex, &family);
46 if (r < 0)
47 return r;
48
49 r = sd_bus_message_skip(m, "ay");
50 if (r < 0)
51 return r;
52
53 r = sd_bus_message_exit_container(m);
54 if (r < 0)
55 return r;
56
57 if (af != AF_UNSPEC && family != af)
58 continue;
59
60 c++;
61 }
62 if (r < 0)
63 return r;
64
65 r = sd_bus_message_exit_container(m);
66 if (r < 0)
67 return r;
68
69 r = sd_bus_message_read(m, "s", canonical);
70 if (r < 0)
71 return r;
72
73 r = sd_bus_message_rewind(m, true);
74 if (r < 0)
75 return r;
76
77 return c;
78 }
79
80 static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
81 struct in6_addr in6;
82
83 if (family != AF_INET6)
84 return 0;
85
86 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
87
88 assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET6));
89 memcpy(&in6, a, sizeof(struct in6_addr));
90
91 return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0;
92 }
93
94 static bool avoid_deadlock(void) {
95
96 /* Check whether this lookup might have a chance of deadlocking because we are called from the service manager
97 * code activating systemd-resolved.service. After all, we shouldn't synchronously do lookups to
98 * systemd-resolved if we are required to finish before it can be started. This of course won't detect all
99 * possible dead locks of this kind, but it should work for the most obvious cases. */
100
101 if (geteuid() != 0) /* Ignore the env vars unless we are privileged. */
102 return false;
103
104 return streq_ptr(getenv("SYSTEMD_ACTIVATION_UNIT"), "systemd-resolved.service") &&
105 streq_ptr(getenv("SYSTEMD_ACTIVATION_SCOPE"), "system");
106 }
107
108 enum nss_status _nss_resolve_gethostbyname4_r(
109 const char *name,
110 struct gaih_addrtuple **pat,
111 char *buffer, size_t buflen,
112 int *errnop, int *h_errnop,
113 int32_t *ttlp) {
114
115 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
116 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
117 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
118 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
119 enum nss_status ret = NSS_STATUS_UNAVAIL;
120 const char *canonical = NULL;
121 size_t l, ms, idx;
122 char *r_name;
123 int c, r, i = 0;
124
125 PROTECT_ERRNO;
126 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
127
128 assert(name);
129 assert(pat);
130 assert(buffer);
131 assert(errnop);
132 assert(h_errnop);
133
134 if (avoid_deadlock()) {
135 r = -EDEADLK;
136 goto fail;
137 }
138
139 r = sd_bus_open_system(&bus);
140 if (r < 0)
141 goto fail;
142
143 r = sd_bus_message_new_method_call(
144 bus,
145 &req,
146 "org.freedesktop.resolve1",
147 "/org/freedesktop/resolve1",
148 "org.freedesktop.resolve1.Manager",
149 "ResolveHostname");
150 if (r < 0)
151 goto fail;
152
153 r = sd_bus_message_set_auto_start(req, false);
154 if (r < 0)
155 goto fail;
156
157 r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0);
158 if (r < 0)
159 goto fail;
160
161 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
162 if (r < 0) {
163 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
164 !bus_error_shall_fallback(&error))
165 goto not_found;
166
167 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
168 allowing falling back to other nss modules. Treat all other error conditions as
169 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
170 case so that the nsswitch.conf configuration can distuingish such executed but
171 negative replies from complete failure to talk to resolved). */
172 goto fail;
173 }
174
175 c = count_addresses(reply, AF_UNSPEC, &canonical);
176 if (c < 0) {
177 r = c;
178 goto fail;
179 }
180 if (c == 0)
181 goto not_found;
182
183 if (isempty(canonical))
184 canonical = name;
185
186 l = strlen(canonical);
187 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
188 if (buflen < ms) {
189 UNPROTECT_ERRNO;
190 *errnop = ERANGE;
191 *h_errnop = NETDB_INTERNAL;
192 return NSS_STATUS_TRYAGAIN;
193 }
194
195 /* First, append name */
196 r_name = buffer;
197 memcpy(r_name, canonical, l+1);
198 idx = ALIGN(l+1);
199
200 /* Second, append addresses */
201 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
202
203 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
204 if (r < 0)
205 goto fail;
206
207 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
208 int family, ifindex;
209 const void *a;
210 size_t sz;
211
212 assert_cc(sizeof(int32_t) == sizeof(int));
213
214 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
215 if (r < 0)
216 goto fail;
217
218 if (ifindex < 0) {
219 r = -EINVAL;
220 goto fail;
221 }
222
223 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
224 if (r < 0)
225 goto fail;
226
227 r = sd_bus_message_exit_container(reply);
228 if (r < 0)
229 goto fail;
230
231 if (!IN_SET(family, AF_INET, AF_INET6))
232 continue;
233
234 if (sz != FAMILY_ADDRESS_SIZE(family)) {
235 r = -EINVAL;
236 goto fail;
237 }
238
239 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
240 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
241 r_tuple->name = r_name;
242 r_tuple->family = family;
243 r_tuple->scopeid = ifindex_to_scopeid(family, a, ifindex);
244 memcpy(r_tuple->addr, a, sz);
245
246 idx += ALIGN(sizeof(struct gaih_addrtuple));
247 i++;
248 }
249 if (r < 0)
250 goto fail;
251
252 assert(i == c);
253 assert(idx == ms);
254
255 if (*pat)
256 **pat = *r_tuple_first;
257 else
258 *pat = r_tuple_first;
259
260 if (ttlp)
261 *ttlp = 0;
262
263 /* Explicitly reset both *h_errnop and h_errno to work around
264 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
265 *h_errnop = NETDB_SUCCESS;
266 h_errno = 0;
267
268 return NSS_STATUS_SUCCESS;
269
270 fail:
271 UNPROTECT_ERRNO;
272 *errnop = -r;
273 *h_errnop = NO_RECOVERY;
274 return ret;
275
276 not_found:
277 *h_errnop = HOST_NOT_FOUND;
278 return NSS_STATUS_NOTFOUND;
279 }
280
281 enum nss_status _nss_resolve_gethostbyname3_r(
282 const char *name,
283 int af,
284 struct hostent *result,
285 char *buffer, size_t buflen,
286 int *errnop, int *h_errnop,
287 int32_t *ttlp,
288 char **canonp) {
289
290 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
291 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
292 char *r_name, *r_aliases, *r_addr, *r_addr_list;
293 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
294 enum nss_status ret = NSS_STATUS_UNAVAIL;
295 size_t l, idx, ms, alen;
296 const char *canonical;
297 int c, r, i = 0;
298
299 PROTECT_ERRNO;
300 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
301
302 assert(name);
303 assert(result);
304 assert(buffer);
305 assert(errnop);
306 assert(h_errnop);
307
308 if (af == AF_UNSPEC)
309 af = AF_INET;
310
311 if (!IN_SET(af, AF_INET, AF_INET6)) {
312 r = -EAFNOSUPPORT;
313 goto fail;
314 }
315
316 if (avoid_deadlock()) {
317 r = -EDEADLK;
318 goto fail;
319 }
320
321 r = sd_bus_open_system(&bus);
322 if (r < 0)
323 goto fail;
324
325 r = sd_bus_message_new_method_call(
326 bus,
327 &req,
328 "org.freedesktop.resolve1",
329 "/org/freedesktop/resolve1",
330 "org.freedesktop.resolve1.Manager",
331 "ResolveHostname");
332 if (r < 0)
333 goto fail;
334
335 r = sd_bus_message_set_auto_start(req, false);
336 if (r < 0)
337 goto fail;
338
339 r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0);
340 if (r < 0)
341 goto fail;
342
343 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
344 if (r < 0) {
345 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
346 !bus_error_shall_fallback(&error))
347 goto not_found;
348
349 goto fail;
350 }
351
352 c = count_addresses(reply, af, &canonical);
353 if (c < 0) {
354 r = c;
355 goto fail;
356 }
357 if (c == 0)
358 goto not_found;
359
360 if (isempty(canonical))
361 canonical = name;
362
363 alen = FAMILY_ADDRESS_SIZE(af);
364 l = strlen(canonical);
365
366 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
367
368 if (buflen < ms) {
369 UNPROTECT_ERRNO;
370 *errnop = ERANGE;
371 *h_errnop = NETDB_INTERNAL;
372 return NSS_STATUS_TRYAGAIN;
373 }
374
375 /* First, append name */
376 r_name = buffer;
377 memcpy(r_name, canonical, l+1);
378 idx = ALIGN(l+1);
379
380 /* Second, create empty aliases array */
381 r_aliases = buffer + idx;
382 ((char**) r_aliases)[0] = NULL;
383 idx += sizeof(char*);
384
385 /* Third, append addresses */
386 r_addr = buffer + idx;
387
388 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
389 if (r < 0)
390 goto fail;
391
392 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
393 int ifindex, family;
394 const void *a;
395 size_t sz;
396
397 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
398 if (r < 0)
399 goto fail;
400
401 if (ifindex < 0) {
402 r = -EINVAL;
403 goto fail;
404 }
405
406 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
407 if (r < 0)
408 goto fail;
409
410 r = sd_bus_message_exit_container(reply);
411 if (r < 0)
412 goto fail;
413
414 if (family != af)
415 continue;
416
417 if (sz != alen) {
418 r = -EINVAL;
419 goto fail;
420 }
421
422 memcpy(r_addr + i*ALIGN(alen), a, alen);
423 i++;
424 }
425 if (r < 0)
426 goto fail;
427
428 assert(i == c);
429 idx += c * ALIGN(alen);
430
431 /* Fourth, append address pointer array */
432 r_addr_list = buffer + idx;
433 for (i = 0; i < c; i++)
434 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
435
436 ((char**) r_addr_list)[i] = NULL;
437 idx += (c+1) * sizeof(char*);
438
439 assert(idx == ms);
440
441 result->h_name = r_name;
442 result->h_aliases = (char**) r_aliases;
443 result->h_addrtype = af;
444 result->h_length = alen;
445 result->h_addr_list = (char**) r_addr_list;
446
447 if (ttlp)
448 *ttlp = 0;
449
450 if (canonp)
451 *canonp = r_name;
452
453 /* Explicitly reset both *h_errnop and h_errno to work around
454 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
455 *h_errnop = NETDB_SUCCESS;
456 h_errno = 0;
457
458 return NSS_STATUS_SUCCESS;
459
460 fail:
461 UNPROTECT_ERRNO;
462 *errnop = -r;
463 *h_errnop = NO_RECOVERY;
464 return ret;
465
466 not_found:
467 *h_errnop = HOST_NOT_FOUND;
468 return NSS_STATUS_NOTFOUND;
469 }
470
471 enum nss_status _nss_resolve_gethostbyaddr2_r(
472 const void* addr, socklen_t len,
473 int af,
474 struct hostent *result,
475 char *buffer, size_t buflen,
476 int *errnop, int *h_errnop,
477 int32_t *ttlp) {
478
479 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
480 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
481 char *r_name, *r_aliases, *r_addr, *r_addr_list;
482 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
483 enum nss_status ret = NSS_STATUS_UNAVAIL;
484 unsigned c = 0, i = 0;
485 size_t ms = 0, idx;
486 const char *n;
487 int r, ifindex;
488
489 PROTECT_ERRNO;
490 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
491
492 assert(addr);
493 assert(result);
494 assert(buffer);
495 assert(errnop);
496 assert(h_errnop);
497
498 if (!IN_SET(af, AF_INET, AF_INET6)) {
499 UNPROTECT_ERRNO;
500 *errnop = EAFNOSUPPORT;
501 *h_errnop = NO_DATA;
502 return NSS_STATUS_UNAVAIL;
503 }
504
505 if (len != FAMILY_ADDRESS_SIZE(af)) {
506 UNPROTECT_ERRNO;
507 *errnop = EINVAL;
508 *h_errnop = NO_RECOVERY;
509 return NSS_STATUS_UNAVAIL;
510 }
511
512 if (avoid_deadlock()) {
513 r = -EDEADLK;
514 goto fail;
515 }
516
517 r = sd_bus_open_system(&bus);
518 if (r < 0)
519 goto fail;
520
521 r = sd_bus_message_new_method_call(
522 bus,
523 &req,
524 "org.freedesktop.resolve1",
525 "/org/freedesktop/resolve1",
526 "org.freedesktop.resolve1.Manager",
527 "ResolveAddress");
528 if (r < 0)
529 goto fail;
530
531 r = sd_bus_message_set_auto_start(req, false);
532 if (r < 0)
533 goto fail;
534
535 r = sd_bus_message_append(req, "ii", 0, af);
536 if (r < 0)
537 goto fail;
538
539 r = sd_bus_message_append_array(req, 'y', addr, len);
540 if (r < 0)
541 goto fail;
542
543 r = sd_bus_message_append(req, "t", (uint64_t) 0);
544 if (r < 0)
545 goto fail;
546
547 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
548 if (r < 0) {
549 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
550 !bus_error_shall_fallback(&error))
551 goto not_found;
552
553 goto fail;
554 }
555
556 r = sd_bus_message_enter_container(reply, 'a', "(is)");
557 if (r < 0)
558 goto fail;
559
560 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
561
562 if (ifindex < 0) {
563 r = -EINVAL;
564 goto fail;
565 }
566
567 c++;
568 ms += ALIGN(strlen(n) + 1);
569 }
570 if (r < 0)
571 goto fail;
572
573 r = sd_bus_message_rewind(reply, false);
574 if (r < 0)
575 return r;
576
577 if (c <= 0)
578 goto not_found;
579
580 ms += ALIGN(len) + /* the address */
581 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
582 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
583
584 if (buflen < ms) {
585 UNPROTECT_ERRNO;
586 *errnop = ERANGE;
587 *h_errnop = NETDB_INTERNAL;
588 return NSS_STATUS_TRYAGAIN;
589 }
590
591 /* First, place address */
592 r_addr = buffer;
593 memcpy(r_addr, addr, len);
594 idx = ALIGN(len);
595
596 /* Second, place address list */
597 r_addr_list = buffer + idx;
598 ((char**) r_addr_list)[0] = r_addr;
599 ((char**) r_addr_list)[1] = NULL;
600 idx += sizeof(char*) * 2;
601
602 /* Third, reserve space for the aliases array */
603 r_aliases = buffer + idx;
604 idx += sizeof(char*) * c;
605
606 /* Fourth, place aliases */
607 i = 0;
608 r_name = buffer + idx;
609 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
610 char *p;
611 size_t l;
612
613 l = strlen(n);
614 p = buffer + idx;
615 memcpy(p, n, l+1);
616
617 if (i > 0)
618 ((char**) r_aliases)[i-1] = p;
619 i++;
620
621 idx += ALIGN(l+1);
622 }
623 if (r < 0)
624 goto fail;
625
626 ((char**) r_aliases)[c-1] = NULL;
627 assert(idx == ms);
628
629 result->h_name = r_name;
630 result->h_aliases = (char**) r_aliases;
631 result->h_addrtype = af;
632 result->h_length = len;
633 result->h_addr_list = (char**) r_addr_list;
634
635 if (ttlp)
636 *ttlp = 0;
637
638 /* Explicitly reset both *h_errnop and h_errno to work around
639 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
640 *h_errnop = NETDB_SUCCESS;
641 h_errno = 0;
642
643 return NSS_STATUS_SUCCESS;
644
645 fail:
646 UNPROTECT_ERRNO;
647 *errnop = -r;
648 *h_errnop = NO_RECOVERY;
649 return ret;
650
651 not_found:
652 *h_errnop = HOST_NOT_FOUND;
653 return NSS_STATUS_NOTFOUND;
654 }
655
656 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
657 NSS_GETHOSTBYADDR_FALLBACKS(resolve);