chiark / gitweb /
Update TODO
[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-common-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) + c * ALIGN(alen) + (c+2) * sizeof(char*);
408
409         if (buflen < ms) {
410                 *errnop = ENOMEM;
411                 *h_errnop = TRY_AGAIN;
412                 return NSS_STATUS_TRYAGAIN;
413         }
414
415         /* First, append name */
416         r_name = buffer;
417         memcpy(r_name, canonical, l+1);
418         idx = ALIGN(l+1);
419
420         /* Second, create empty aliases array */
421         r_aliases = buffer + idx;
422         ((char**) r_aliases)[0] = NULL;
423         idx += sizeof(char*);
424
425         /* Third, append addresses */
426         r_addr = buffer + idx;
427
428         r = sd_bus_message_read(reply, "i", &ifindex);
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_enter_container(reply, 'a', "(iay)");
438         if (r < 0)
439                 goto fail;
440
441         while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
442                 int family;
443                 const void *a;
444                 size_t sz;
445
446                 r = sd_bus_message_read(reply, "i", &family);
447                 if (r < 0)
448                         goto fail;
449
450                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
451                 if (r < 0)
452                         goto fail;
453
454                 r = sd_bus_message_exit_container(reply);
455                 if (r < 0)
456                         goto fail;
457
458                 if (family != af)
459                         continue;
460
461                 if (sz != alen) {
462                         r = -EINVAL;
463                         goto fail;
464                 }
465
466                 memcpy(r_addr + i*ALIGN(alen), a, alen);
467                 i++;
468         }
469         if (r < 0)
470                 goto fail;
471
472         assert(i == c);
473         idx += c * ALIGN(alen);
474
475         /* Fourth, append address pointer array */
476         r_addr_list = buffer + idx;
477         for (i = 0; i < c; i++)
478                 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
479
480         ((char**) r_addr_list)[i] = NULL;
481         idx += (c+1) * sizeof(char*);
482
483         assert(idx == ms);
484
485         result->h_name = r_name;
486         result->h_aliases = (char**) r_aliases;
487         result->h_addrtype = af;
488         result->h_length = alen;
489         result->h_addr_list = (char**) r_addr_list;
490
491         /* Explicitly reset all error variables */
492         *errnop = 0;
493         *h_errnop = NETDB_SUCCESS;
494         h_errno = 0;
495
496         if (ttlp)
497                 *ttlp = 0;
498
499         if (canonp)
500                 *canonp = r_name;
501
502         return NSS_STATUS_SUCCESS;
503
504 fail:
505         *errnop = -r;
506         *h_errnop = NO_DATA;
507         return NSS_STATUS_UNAVAIL;
508 }
509
510 enum nss_status _nss_resolve_gethostbyaddr2_r(
511                 const void* addr, socklen_t len,
512                 int af,
513                 struct hostent *result,
514                 char *buffer, size_t buflen,
515                 int *errnop, int *h_errnop,
516                 int32_t *ttlp) {
517
518         _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
519         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
520         char *r_name, *r_aliases, *r_addr, *r_addr_list;
521         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
522         unsigned c = 0, i = 0;
523         size_t ms = 0, idx;
524         const char *n;
525         int r, ifindex;
526
527         assert(addr);
528         assert(result);
529         assert(buffer);
530         assert(errnop);
531         assert(h_errnop);
532
533         if (!IN_SET(af, AF_INET, AF_INET6)) {
534                 *errnop = EAFNOSUPPORT;
535                 *h_errnop = NO_DATA;
536                 return NSS_STATUS_UNAVAIL;
537         }
538
539         if (len != FAMILY_ADDRESS_SIZE(af)) {
540                 *errnop = EINVAL;
541                 *h_errnop = NO_RECOVERY;
542                 return NSS_STATUS_UNAVAIL;
543         }
544
545         r = sd_bus_open_system(&bus);
546         if (r < 0)
547                 goto fail;
548
549         r = sd_bus_message_new_method_call(
550                         bus,
551                         &req,
552                         "org.freedesktop.resolve1",
553                         "/org/freedesktop/resolve1",
554                         "org.freedesktop.resolve1.Manager",
555                         "ResolveAddress");
556         if (r < 0)
557                 goto fail;
558
559         r = sd_bus_message_set_auto_start(req, false);
560         if (r < 0)
561                 goto fail;
562
563         r = sd_bus_message_append(req, "ii", 0, af);
564         if (r < 0)
565                 goto fail;
566
567         r = sd_bus_message_append_array(req, 'y', addr, len);
568         if (r < 0)
569                 goto fail;
570
571         r = sd_bus_message_append(req, "t", (uint64_t) 0);
572         if (r < 0)
573                 goto fail;
574
575         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
576         if (r < 0) {
577                 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
578                         *errnop = ESRCH;
579                         *h_errnop = HOST_NOT_FOUND;
580                         return NSS_STATUS_NOTFOUND;
581                 }
582
583                 if (bus_error_shall_fallback(&error)) {
584
585                         enum nss_status (*fallback)(
586                                         const void* addr, socklen_t len,
587                                         int af,
588                                         struct hostent *result,
589                                         char *buffer, size_t buflen,
590                                         int *errnop, int *h_errnop,
591                                         int32_t *ttlp);
592
593                         fallback = (enum nss_status (*)(
594                                         const void* addr, socklen_t len,
595                                         int af,
596                                         struct hostent *result,
597                                         char *buffer, size_t buflen,
598                                         int *errnop, int *h_errnop,
599                                         int32_t *ttlp))
600                                 find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r");
601
602                         if (fallback)
603                                 return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp);
604                 }
605
606                 *errnop = -r;
607                 *h_errnop = NO_RECOVERY;
608                 return NSS_STATUS_UNAVAIL;
609         }
610
611         r = sd_bus_message_read(reply, "i", &ifindex);
612         if (r < 0)
613                 goto fail;
614
615         if (ifindex < 0) {
616                 r = -EINVAL;
617                 goto fail;
618         }
619
620         r = sd_bus_message_enter_container(reply, 'a', "s");
621         if (r < 0)
622                 goto fail;
623
624         while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
625                 c++;
626                 ms += ALIGN(strlen(n) + 1);
627         }
628         if (r < 0)
629                 goto fail;
630
631         r = sd_bus_message_rewind(reply, false);
632         if (r < 0)
633                 return r;
634
635         if (c <= 0) {
636                 *errnop = ESRCH;
637                 *h_errnop = HOST_NOT_FOUND;
638                 return NSS_STATUS_NOTFOUND;
639         }
640
641         ms += ALIGN(len) +              /* the address */
642               2 * sizeof(char*) +       /* pointers to the address, plus trailing NULL */
643               c * sizeof(char*);        /* pointers to aliases, plus trailing NULL */
644
645         if (buflen < ms) {
646                 *errnop = ENOMEM;
647                 *h_errnop = TRY_AGAIN;
648                 return NSS_STATUS_TRYAGAIN;
649         }
650
651         /* First, place address */
652         r_addr = buffer;
653         memcpy(r_addr, addr, len);
654         idx = ALIGN(len);
655
656         /* Second, place address list */
657         r_addr_list = buffer + idx;
658         ((char**) r_addr_list)[0] = r_addr;
659         ((char**) r_addr_list)[1] = NULL;
660         idx += sizeof(char*) * 2;
661
662         /* Third, reserve space for the aliases array */
663         r_aliases = buffer + idx;
664         idx += sizeof(char*) * c;
665
666         /* Fourth, place aliases */
667         i = 0;
668         r_name = buffer + idx;
669         while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
670                 char *p;
671                 size_t l;
672
673                 l = strlen(n);
674                 p = buffer + idx;
675                 memcpy(p, n, l+1);
676
677                 if (i > 1)
678                         ((char**) r_aliases)[i-1] = p;
679                 i++;
680
681                 idx += ALIGN(l+1);
682         }
683         if (r < 0)
684                 goto fail;
685
686         ((char**) r_aliases)[c-1] = NULL;
687         assert(idx == ms);
688
689         result->h_name = r_name;
690         result->h_aliases = (char**) r_aliases;
691         result->h_addrtype = af;
692         result->h_length = len;
693         result->h_addr_list = (char**) r_addr_list;
694
695         if (ttlp)
696                 *ttlp = 0;
697
698         /* Explicitly reset all error variables */
699         *errnop = 0;
700         *h_errnop = NETDB_SUCCESS;
701         h_errno = 0;
702
703         return NSS_STATUS_SUCCESS;
704
705 fail:
706         *errnop = -r;
707         *h_errnop = NO_DATA;
708         return NSS_STATUS_UNAVAIL;
709 }
710
711 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
712 NSS_GETHOSTBYADDR_FALLBACKS(resolve);