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