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