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