chiark / gitweb /
nss-resolve: fallback to the class NSS "dns" module if we cannot contact resolved
[elogind.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;
70
71         assert(m);
72         assert(canonical);
73
74         r = sd_bus_message_enter_container(m, 'a', "(iayi)");
75         if (r < 0)
76                 return r;
77
78         while ((r = sd_bus_message_enter_container(m, 'r', "iayi")) > 0) {
79                 int family;
80
81                 r = sd_bus_message_read(m, "i", &family);
82                 if (r < 0)
83                         return r;
84
85                 r = sd_bus_message_skip(m, "ayi");
86                 if (r < 0)
87                         return r;
88
89                 r = sd_bus_message_exit_container(m);
90                 if (r < 0)
91                         return r;
92
93                 if (af != AF_UNSPEC && family != af)
94                         continue;
95
96                 c ++;
97         }
98         if (r < 0)
99                 return r;
100
101         r = sd_bus_message_exit_container(m);
102         if (r < 0)
103                 return r;
104
105         r = sd_bus_message_read(m, "s", canonical);
106         if (r < 0)
107                 return r;
108
109         r = sd_bus_message_rewind(m, true);
110         if (r < 0)
111                 return r;
112
113         return c;
114 }
115
116 enum nss_status _nss_resolve_gethostbyname4_r(
117                 const char *name,
118                 struct gaih_addrtuple **pat,
119                 char *buffer, size_t buflen,
120                 int *errnop, int *h_errnop,
121                 int32_t *ttlp) {
122
123         _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
124         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
125         struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
126         _cleanup_bus_unref_ sd_bus *bus = NULL;
127         const char *canonical = NULL;
128         size_t l, ms, idx;
129         char *r_name;
130         int c, r, i = 0;
131
132         assert(name);
133         assert(pat);
134         assert(buffer);
135         assert(errnop);
136         assert(h_errnop);
137
138         r = sd_bus_open_system(&bus);
139         if (r < 0)
140                 goto fail;
141
142         r = sd_bus_message_new_method_call(
143                         bus,
144                         &req,
145                         "org.freedesktop.resolve1",
146                         "/org/freedesktop/resolve1",
147                         "org.freedesktop.resolve1.Manager",
148                         "ResolveHostname");
149         if (r < 0)
150                 goto fail;
151
152         r = sd_bus_message_set_auto_start(req, false);
153         if (r < 0)
154                 goto fail;
155
156         r = sd_bus_message_append(req, "si", name, AF_UNSPEC);
157         if (r < 0)
158                 goto fail;
159
160         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
161         if (r < 0) {
162                 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
163                         *errnop = ESRCH;
164                         *h_errnop = HOST_NOT_FOUND;
165                         return NSS_STATUS_NOTFOUND;
166                 }
167
168                 if (bus_error_shall_fallback(&error)) {
169
170                         enum nss_status (*fallback)(
171                                         const char *name,
172                                         struct gaih_addrtuple **pat,
173                                         char *buffer, size_t buflen,
174                                         int *errnop, int *h_errnop,
175                                         int32_t *ttlp);
176
177                         fallback = (enum nss_status (*)(const char *name,
178                                                         struct gaih_addrtuple **pat,
179                                                         char *buffer, size_t buflen,
180                                                         int *errnop, int *h_errnop,
181                                                         int32_t *ttlp))
182                                 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r");
183                         if (fallback)
184                                 return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp);
185                 }
186
187                 *errnop = -r;
188                 *h_errnop = NO_RECOVERY;
189                 return NSS_STATUS_UNAVAIL;
190         }
191
192         c = count_addresses(reply, AF_UNSPEC, &canonical);
193         if (c < 0) {
194                 r = c;
195                 goto fail;
196         }
197         if (c == 0) {
198                 *errnop = ESRCH;
199                 *h_errnop = HOST_NOT_FOUND;
200                 return NSS_STATUS_NOTFOUND;
201         }
202
203         if (isempty(canonical))
204                 canonical = name;
205
206         l = strlen(canonical);
207         ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
208         if (buflen < ms) {
209                 *errnop = ENOMEM;
210                 *h_errnop = TRY_AGAIN;
211                 return NSS_STATUS_TRYAGAIN;
212         }
213
214         /* First, append name */
215         r_name = buffer;
216         memcpy(r_name, canonical, l+1);
217         idx = ALIGN(l+1);
218
219         /* Second, append addresses */
220         r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
221
222         r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
223         if (r < 0)
224                 goto fail;
225
226         while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
227                 int family, ifindex;
228                 const void *a;
229                 size_t sz;
230
231                 r = sd_bus_message_read(reply, "i", &family);
232                 if (r < 0)
233                         goto fail;
234
235                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
236                 if (r < 0)
237                         goto fail;
238
239                 r = sd_bus_message_read(reply, "i", &ifindex);
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                 if (ifindex < 0) {
256                         r = -EINVAL;
257                         goto fail;
258                 }
259
260                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
261                 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
262                 r_tuple->name = r_name;
263                 r_tuple->family = family;
264                 r_tuple->scopeid = ifindex;
265                 memcpy(r_tuple->addr, a, sz);
266
267                 idx += ALIGN(sizeof(struct gaih_addrtuple));
268                 i++;
269         }
270         if (r < 0)
271                 goto fail;
272
273         assert(i == c);
274         assert(idx == ms);
275
276         if (*pat)
277                 **pat = *r_tuple_first;
278         else
279                 *pat = r_tuple_first;
280
281         if (ttlp)
282                 *ttlp = 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_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, "si", name, af);
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) +
399                 sizeof(char*) +
400                 (c > 0 ? c : 1) * ALIGN(alen) +
401                 (c > 0 ? c+1 : 2) * sizeof(char*);
402
403         if (buflen < ms) {
404                 *errnop = ENOMEM;
405                 *h_errnop = TRY_AGAIN;
406                 return NSS_STATUS_TRYAGAIN;
407         }
408
409         /* First, append name */
410         r_name = buffer;
411         memcpy(r_name, canonical, l+1);
412         idx = ALIGN(l+1);
413
414         /* Second, create empty aliases array */
415         r_aliases = buffer + idx;
416         ((char**) r_aliases)[0] = NULL;
417         idx += sizeof(char*);
418
419         /* Third, append addresses */
420         r_addr = buffer + idx;
421
422         r = sd_bus_message_enter_container(reply, 'a', "(iayi)");
423         if (r < 0)
424                 goto fail;
425
426         while ((r = sd_bus_message_enter_container(reply, 'r', "iayi")) > 0) {
427                 int family, ifindex;
428                 const void *a;
429                 size_t sz;
430
431                 r = sd_bus_message_read(reply, "i", &family);
432                 if (r < 0)
433                         goto fail;
434
435                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
436                 if (r < 0)
437                         goto fail;
438
439                 r = sd_bus_message_read(reply, "i", &ifindex);
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                 if (ifindex < 0) {
456                         r = -EINVAL;
457                         goto fail;
458                 }
459
460                 memcpy(r_addr + i*ALIGN(alen), a, alen);
461                 i++;
462         }
463         if (r < 0)
464                 goto fail;
465
466         assert(i == c);
467         idx += c * ALIGN(alen);
468
469         /* Fourth, append address pointer array */
470         r_addr_list = buffer + idx;
471         for (i = 0; i < c; i++)
472                 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
473
474         ((char**) r_addr_list)[i] = NULL;
475         idx += (c+1) * sizeof(char*);
476
477         assert(idx == ms);
478
479         result->h_name = r_name;
480         result->h_aliases = (char**) r_aliases;
481         result->h_addrtype = af;
482         result->h_length = alen;
483         result->h_addr_list = (char**) r_addr_list;
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_unref_ sd_bus *bus = NULL;
511         unsigned c = 0, i = 0;
512         size_t ms = 0, idx;
513         const char *n;
514         int r;
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, "i", 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, "i", 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', "s");
601         if (r < 0)
602                 goto fail;
603
604         while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
605                 c++;
606                 ms += ALIGN(strlen(n) + 1);
607         }
608         if (r < 0)
609                 goto fail;
610
611         r = sd_bus_message_rewind(reply, false);
612         if (r < 0)
613                 return r;
614
615         if (c <= 0) {
616                 *errnop = ESRCH;
617                 *h_errnop = HOST_NOT_FOUND;
618                 return NSS_STATUS_NOTFOUND;
619         }
620
621         ms += ALIGN(len) +              /* the address */
622               2 * sizeof(char*) +       /* pointers to the address, plus trailing NULL */
623               c * sizeof(char*);        /* pointers to aliases, plus trailing NULL */
624
625         if (buflen < ms) {
626                 *errnop = ENOMEM;
627                 *h_errnop = TRY_AGAIN;
628                 return NSS_STATUS_TRYAGAIN;
629         }
630
631         /* First, place address */
632         r_addr = buffer;
633         memcpy(r_addr, addr, len);
634         idx = ALIGN(len);
635
636         /* Second, place address list */
637         r_addr_list = buffer + idx;
638         ((char**) r_addr_list)[0] = r_addr;
639         ((char**) r_addr_list)[1] = NULL;
640         idx += sizeof(char*) * 2;
641
642         /* Third, reserve space for the aliases array */
643         r_aliases = buffer + idx;
644         idx += sizeof(char*) * c;
645
646         /* Fourth, place aliases */
647         i = 0;
648         r_name = buffer + idx;
649         while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
650                 char *p;
651                 size_t l;
652
653                 l = strlen(n);
654                 p = buffer + idx;
655                 memcpy(p, n, l+1);
656
657                 if (i > 1)
658                         ((char**) r_aliases)[i-1] = p;
659                 i++;
660
661                 idx += ALIGN(l+1);
662         }
663         if (r < 0)
664                 goto fail;
665
666         ((char**) r_aliases)[c-1] = NULL;
667         assert(idx == ms);
668
669         result->h_name = r_name;
670         result->h_aliases = (char**) r_aliases;
671         result->h_addrtype = af;
672         result->h_length = len;
673         result->h_addr_list = (char**) r_addr_list;
674
675         if (ttlp)
676                 *ttlp = 0;
677
678         return NSS_STATUS_SUCCESS;
679
680 fail:
681         *errnop = -r;
682         *h_errnop = NO_DATA;
683         return NSS_STATUS_UNAVAIL;
684 }
685
686 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
687 NSS_GETHOSTBYADDR_FALLBACKS(resolve);