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