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