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