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