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