]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-resolve/nss-resolve.c
Merge pull request #9668 from poettering/open-parent
[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 enum nss_status _nss_resolve_gethostbyname4_r(
95 const char *name,
96 struct gaih_addrtuple **pat,
97 char *buffer, size_t buflen,
98 int *errnop, int *h_errnop,
99 int32_t *ttlp) {
100
101 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
102 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
103 struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
104 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
105 enum nss_status ret = NSS_STATUS_UNAVAIL;
106 const char *canonical = NULL;
107 size_t l, ms, idx;
108 char *r_name;
109 int c, r, i = 0;
110
111 PROTECT_ERRNO;
112 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
113
114 assert(name);
115 assert(pat);
116 assert(buffer);
117 assert(errnop);
118 assert(h_errnop);
119
120 r = sd_bus_open_system(&bus);
121 if (r < 0)
122 goto fail;
123
124 r = sd_bus_message_new_method_call(
125 bus,
126 &req,
127 "org.freedesktop.resolve1",
128 "/org/freedesktop/resolve1",
129 "org.freedesktop.resolve1.Manager",
130 "ResolveHostname");
131 if (r < 0)
132 goto fail;
133
134 r = sd_bus_message_set_auto_start(req, false);
135 if (r < 0)
136 goto fail;
137
138 r = sd_bus_message_append(req, "isit", 0, name, AF_UNSPEC, (uint64_t) 0);
139 if (r < 0)
140 goto fail;
141
142 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
143 if (r < 0) {
144 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
145 !bus_error_shall_fallback(&error))
146 goto not_found;
147
148 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails,
149 allowing falling back to other nss modules. Treat all other error conditions as
150 NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this
151 case so that the nsswitch.conf configuration can distuingish such executed but
152 negative replies from complete failure to talk to resolved). */
153 goto fail;
154 }
155
156 c = count_addresses(reply, AF_UNSPEC, &canonical);
157 if (c < 0) {
158 r = c;
159 goto fail;
160 }
161 if (c == 0)
162 goto not_found;
163
164 if (isempty(canonical))
165 canonical = name;
166
167 l = strlen(canonical);
168 ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
169 if (buflen < ms) {
170 *errnop = ERANGE;
171 *h_errnop = NETDB_INTERNAL;
172 return NSS_STATUS_TRYAGAIN;
173 }
174
175 /* First, append name */
176 r_name = buffer;
177 memcpy(r_name, canonical, l+1);
178 idx = ALIGN(l+1);
179
180 /* Second, append addresses */
181 r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
182
183 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
184 if (r < 0)
185 goto fail;
186
187 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
188 int family, ifindex;
189 const void *a;
190 size_t sz;
191
192 assert_cc(sizeof(int32_t) == sizeof(int));
193
194 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
195 if (r < 0)
196 goto fail;
197
198 if (ifindex < 0) {
199 r = -EINVAL;
200 goto fail;
201 }
202
203 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
204 if (r < 0)
205 goto fail;
206
207 r = sd_bus_message_exit_container(reply);
208 if (r < 0)
209 goto fail;
210
211 if (!IN_SET(family, AF_INET, AF_INET6))
212 continue;
213
214 if (sz != FAMILY_ADDRESS_SIZE(family)) {
215 r = -EINVAL;
216 goto fail;
217 }
218
219 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
220 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
221 r_tuple->name = r_name;
222 r_tuple->family = family;
223 r_tuple->scopeid = ifindex_to_scopeid(family, a, ifindex);
224 memcpy(r_tuple->addr, a, sz);
225
226 idx += ALIGN(sizeof(struct gaih_addrtuple));
227 i++;
228 }
229 if (r < 0)
230 goto fail;
231
232 assert(i == c);
233 assert(idx == ms);
234
235 if (*pat)
236 **pat = *r_tuple_first;
237 else
238 *pat = r_tuple_first;
239
240 if (ttlp)
241 *ttlp = 0;
242
243 /* Explicitly reset both *h_errnop and h_errno to work around
244 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
245 *h_errnop = NETDB_SUCCESS;
246 h_errno = 0;
247
248 return NSS_STATUS_SUCCESS;
249
250 fail:
251 *errnop = -r;
252 *h_errnop = NO_RECOVERY;
253 return ret;
254
255 not_found:
256 *h_errnop = HOST_NOT_FOUND;
257 return NSS_STATUS_NOTFOUND;
258 }
259
260 enum nss_status _nss_resolve_gethostbyname3_r(
261 const char *name,
262 int af,
263 struct hostent *result,
264 char *buffer, size_t buflen,
265 int *errnop, int *h_errnop,
266 int32_t *ttlp,
267 char **canonp) {
268
269 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
270 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
271 char *r_name, *r_aliases, *r_addr, *r_addr_list;
272 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
273 enum nss_status ret = NSS_STATUS_UNAVAIL;
274 size_t l, idx, ms, alen;
275 const char *canonical;
276 int c, r, i = 0;
277
278 PROTECT_ERRNO;
279 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
280
281 assert(name);
282 assert(result);
283 assert(buffer);
284 assert(errnop);
285 assert(h_errnop);
286
287 if (af == AF_UNSPEC)
288 af = AF_INET;
289
290 if (!IN_SET(af, AF_INET, AF_INET6)) {
291 r = -EAFNOSUPPORT;
292 goto fail;
293 }
294
295 r = sd_bus_open_system(&bus);
296 if (r < 0)
297 goto fail;
298
299 r = sd_bus_message_new_method_call(
300 bus,
301 &req,
302 "org.freedesktop.resolve1",
303 "/org/freedesktop/resolve1",
304 "org.freedesktop.resolve1.Manager",
305 "ResolveHostname");
306 if (r < 0)
307 goto fail;
308
309 r = sd_bus_message_set_auto_start(req, false);
310 if (r < 0)
311 goto fail;
312
313 r = sd_bus_message_append(req, "isit", 0, name, af, (uint64_t) 0);
314 if (r < 0)
315 goto fail;
316
317 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
318 if (r < 0) {
319 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
320 !bus_error_shall_fallback(&error))
321 goto not_found;
322
323 goto fail;
324 }
325
326 c = count_addresses(reply, af, &canonical);
327 if (c < 0) {
328 r = c;
329 goto fail;
330 }
331 if (c == 0)
332 goto not_found;
333
334 if (isempty(canonical))
335 canonical = name;
336
337 alen = FAMILY_ADDRESS_SIZE(af);
338 l = strlen(canonical);
339
340 ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*);
341
342 if (buflen < ms) {
343 *errnop = ERANGE;
344 *h_errnop = NETDB_INTERNAL;
345 return NSS_STATUS_TRYAGAIN;
346 }
347
348 /* First, append name */
349 r_name = buffer;
350 memcpy(r_name, canonical, l+1);
351 idx = ALIGN(l+1);
352
353 /* Second, create empty aliases array */
354 r_aliases = buffer + idx;
355 ((char**) r_aliases)[0] = NULL;
356 idx += sizeof(char*);
357
358 /* Third, append addresses */
359 r_addr = buffer + idx;
360
361 r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
362 if (r < 0)
363 goto fail;
364
365 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
366 int ifindex, family;
367 const void *a;
368 size_t sz;
369
370 r = sd_bus_message_read(reply, "ii", &ifindex, &family);
371 if (r < 0)
372 goto fail;
373
374 if (ifindex < 0) {
375 r = -EINVAL;
376 goto fail;
377 }
378
379 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
380 if (r < 0)
381 goto fail;
382
383 r = sd_bus_message_exit_container(reply);
384 if (r < 0)
385 goto fail;
386
387 if (family != af)
388 continue;
389
390 if (sz != alen) {
391 r = -EINVAL;
392 goto fail;
393 }
394
395 memcpy(r_addr + i*ALIGN(alen), a, alen);
396 i++;
397 }
398 if (r < 0)
399 goto fail;
400
401 assert(i == c);
402 idx += c * ALIGN(alen);
403
404 /* Fourth, append address pointer array */
405 r_addr_list = buffer + idx;
406 for (i = 0; i < c; i++)
407 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
408
409 ((char**) r_addr_list)[i] = NULL;
410 idx += (c+1) * sizeof(char*);
411
412 assert(idx == ms);
413
414 result->h_name = r_name;
415 result->h_aliases = (char**) r_aliases;
416 result->h_addrtype = af;
417 result->h_length = alen;
418 result->h_addr_list = (char**) r_addr_list;
419
420 if (ttlp)
421 *ttlp = 0;
422
423 if (canonp)
424 *canonp = r_name;
425
426 /* Explicitly reset both *h_errnop and h_errno to work around
427 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
428 *h_errnop = NETDB_SUCCESS;
429 h_errno = 0;
430
431 return NSS_STATUS_SUCCESS;
432
433 fail:
434 *errnop = -r;
435 *h_errnop = NO_RECOVERY;
436 return ret;
437
438 not_found:
439 *h_errnop = HOST_NOT_FOUND;
440 return NSS_STATUS_NOTFOUND;
441 }
442
443 enum nss_status _nss_resolve_gethostbyaddr2_r(
444 const void* addr, socklen_t len,
445 int af,
446 struct hostent *result,
447 char *buffer, size_t buflen,
448 int *errnop, int *h_errnop,
449 int32_t *ttlp) {
450
451 _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL;
452 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
453 char *r_name, *r_aliases, *r_addr, *r_addr_list;
454 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
455 enum nss_status ret = NSS_STATUS_UNAVAIL;
456 unsigned c = 0, i = 0;
457 size_t ms = 0, idx;
458 const char *n;
459 int r, ifindex;
460
461 PROTECT_ERRNO;
462 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
463
464 assert(addr);
465 assert(result);
466 assert(buffer);
467 assert(errnop);
468 assert(h_errnop);
469
470 if (!IN_SET(af, AF_INET, AF_INET6)) {
471 *errnop = EAFNOSUPPORT;
472 *h_errnop = NO_DATA;
473 return NSS_STATUS_UNAVAIL;
474 }
475
476 if (len != FAMILY_ADDRESS_SIZE(af)) {
477 *errnop = EINVAL;
478 *h_errnop = NO_RECOVERY;
479 return NSS_STATUS_UNAVAIL;
480 }
481
482 r = sd_bus_open_system(&bus);
483 if (r < 0)
484 goto fail;
485
486 r = sd_bus_message_new_method_call(
487 bus,
488 &req,
489 "org.freedesktop.resolve1",
490 "/org/freedesktop/resolve1",
491 "org.freedesktop.resolve1.Manager",
492 "ResolveAddress");
493 if (r < 0)
494 goto fail;
495
496 r = sd_bus_message_set_auto_start(req, false);
497 if (r < 0)
498 goto fail;
499
500 r = sd_bus_message_append(req, "ii", 0, af);
501 if (r < 0)
502 goto fail;
503
504 r = sd_bus_message_append_array(req, 'y', addr, len);
505 if (r < 0)
506 goto fail;
507
508 r = sd_bus_message_append(req, "t", (uint64_t) 0);
509 if (r < 0)
510 goto fail;
511
512 r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply);
513 if (r < 0) {
514 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN") ||
515 !bus_error_shall_fallback(&error))
516 goto not_found;
517
518 goto fail;
519 }
520
521 r = sd_bus_message_enter_container(reply, 'a', "(is)");
522 if (r < 0)
523 goto fail;
524
525 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
526
527 if (ifindex < 0) {
528 r = -EINVAL;
529 goto fail;
530 }
531
532 c++;
533 ms += ALIGN(strlen(n) + 1);
534 }
535 if (r < 0)
536 goto fail;
537
538 r = sd_bus_message_rewind(reply, false);
539 if (r < 0)
540 return r;
541
542 if (c <= 0)
543 goto not_found;
544
545 ms += ALIGN(len) + /* the address */
546 2 * sizeof(char*) + /* pointers to the address, plus trailing NULL */
547 c * sizeof(char*); /* pointers to aliases, plus trailing NULL */
548
549 if (buflen < ms) {
550 *errnop = ERANGE;
551 *h_errnop = NETDB_INTERNAL;
552 return NSS_STATUS_TRYAGAIN;
553 }
554
555 /* First, place address */
556 r_addr = buffer;
557 memcpy(r_addr, addr, len);
558 idx = ALIGN(len);
559
560 /* Second, place address list */
561 r_addr_list = buffer + idx;
562 ((char**) r_addr_list)[0] = r_addr;
563 ((char**) r_addr_list)[1] = NULL;
564 idx += sizeof(char*) * 2;
565
566 /* Third, reserve space for the aliases array */
567 r_aliases = buffer + idx;
568 idx += sizeof(char*) * c;
569
570 /* Fourth, place aliases */
571 i = 0;
572 r_name = buffer + idx;
573 while ((r = sd_bus_message_read(reply, "(is)", &ifindex, &n)) > 0) {
574 char *p;
575 size_t l;
576
577 l = strlen(n);
578 p = buffer + idx;
579 memcpy(p, n, l+1);
580
581 if (i > 0)
582 ((char**) r_aliases)[i-1] = p;
583 i++;
584
585 idx += ALIGN(l+1);
586 }
587 if (r < 0)
588 goto fail;
589
590 ((char**) r_aliases)[c-1] = NULL;
591 assert(idx == ms);
592
593 result->h_name = r_name;
594 result->h_aliases = (char**) r_aliases;
595 result->h_addrtype = af;
596 result->h_length = len;
597 result->h_addr_list = (char**) r_addr_list;
598
599 if (ttlp)
600 *ttlp = 0;
601
602 /* Explicitly reset both *h_errnop and h_errno to work around
603 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
604 *h_errnop = NETDB_SUCCESS;
605 h_errno = 0;
606
607 return NSS_STATUS_SUCCESS;
608
609 fail:
610 *errnop = -r;
611 *h_errnop = NO_RECOVERY;
612 return ret;
613
614 not_found:
615 *h_errnop = HOST_NOT_FOUND;
616 return NSS_STATUS_NOTFOUND;
617 }
618
619 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
620 NSS_GETHOSTBYADDR_FALLBACKS(resolve);