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