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