chiark / gitweb /
7b0c24fbeacb1dc073aa95df8ef1ae132ea86ba8
[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
33 #include "sd-bus.h"
34 #include "bus-util.h"
35 #include "bus-errors.h"
36 #include "macro.h"
37 #include "nss-util.h"
38 #include "util.h"
39 #include "in-addr-util.h"
40
41 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
42 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
43
44 #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
45
46 static int count_addresses(sd_bus_message *m, unsigned af, unsigned *ret) {
47         unsigned c = 0;
48         int r;
49
50         assert(m);
51         assert(ret);
52
53         while ((r = sd_bus_message_enter_container(m, 'r', "yayi")) > 0) {
54                 unsigned char family;
55
56                 r = sd_bus_message_read(m, "y", &family);
57                 if (r < 0)
58                         return r;
59
60                 r = sd_bus_message_skip(m, "ayi");
61                 if (r < 0)
62                         return r;
63
64                 r = sd_bus_message_exit_container(m);
65                 if (r < 0)
66                         return r;
67
68                 if (af != AF_UNSPEC && family != af)
69                         continue;
70
71                 c ++;
72         }
73         if (r < 0)
74                 return r;
75
76         r = sd_bus_message_rewind(m, false);
77         if (r < 0)
78                 return r;
79
80         *ret = c;
81         return 0;
82 }
83
84 enum nss_status _nss_resolve_gethostbyname4_r(
85                 const char *name,
86                 struct gaih_addrtuple **pat,
87                 char *buffer, size_t buflen,
88                 int *errnop, int *h_errnop,
89                 int32_t *ttlp) {
90
91         _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
92         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
93         struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
94         _cleanup_bus_unref_ sd_bus *bus = NULL;
95         size_t l, ms, idx;
96         char *r_name;
97         unsigned i = 0, c = 0;
98         int r;
99
100         assert(name);
101         assert(pat);
102         assert(buffer);
103         assert(errnop);
104         assert(h_errnop);
105
106         r = sd_bus_open_system(&bus);
107         if (r < 0)
108                 goto fail;
109
110         r = sd_bus_message_new_method_call(
111                         bus,
112                         &req,
113                         "org.freedesktop.resolve1",
114                         "/org/freedesktop/resolve1",
115                         "org.freedesktop.resolve1.Manager",
116                         "ResolveHostname");
117         if (r < 0)
118                 goto fail;
119
120         r = sd_bus_message_set_auto_start(req, false);
121         if (r < 0)
122                 goto fail;
123
124         r = sd_bus_message_append(req, "sy", name, AF_UNSPEC);
125         if (r < 0)
126                 goto fail;
127
128         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
129         if (r < 0) {
130                 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
131                         *errnop = ESRCH;
132                         *h_errnop = HOST_NOT_FOUND;
133                         return NSS_STATUS_NOTFOUND;
134                 }
135
136                 *errnop = -r;
137                 *h_errnop = NO_RECOVERY;
138                 return NSS_STATUS_UNAVAIL;
139         }
140
141         r = sd_bus_message_enter_container(reply, 'a', "(yayi)");
142         if (r < 0)
143                 goto fail;
144
145         r = count_addresses(reply, AF_UNSPEC, &c);
146         if (r < 0)
147                 goto fail;
148
149         if (c <= 0) {
150                 *errnop = ESRCH;
151                 *h_errnop = HOST_NOT_FOUND;
152                 return NSS_STATUS_NOTFOUND;
153         }
154
155         l = strlen(name);
156         ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
157         if (buflen < ms) {
158                 *errnop = ENOMEM;
159                 *h_errnop = TRY_AGAIN;
160                 return NSS_STATUS_TRYAGAIN;
161         }
162
163         /* First, append name */
164         r_name = buffer;
165         memcpy(r_name, name, l+1);
166         idx = ALIGN(l+1);
167
168         /* Second, append addresses */
169         r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
170         while ((r = sd_bus_message_enter_container(reply, 'r', "yayi")) > 0) {
171                 unsigned char family;
172                 const void *a;
173                 int ifindex;
174                 size_t sz;
175
176                 r = sd_bus_message_read(reply, "y", &family);
177                 if (r < 0)
178                         goto fail;
179
180                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
181                 if (r < 0)
182                         goto fail;
183
184                 r = sd_bus_message_read(reply, "i", &ifindex);
185                 if (r < 0)
186                         goto fail;
187
188                 r = sd_bus_message_exit_container(reply);
189                 if (r < 0)
190                         goto fail;
191
192                 if (!IN_SET(family, AF_INET, AF_INET6))
193                         continue;
194
195                 if (sz != PROTO_ADDRESS_SIZE(family)) {
196                         r = -EINVAL;
197                         goto fail;
198                 }
199
200                 if (ifindex < 0) {
201                         r = -EINVAL;
202                         goto fail;
203                 }
204
205                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
206                 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
207                 r_tuple->name = r_name;
208                 r_tuple->family = family;
209                 r_tuple->scopeid = ifindex;
210                 memcpy(r_tuple->addr, a, sz);
211
212                 idx += ALIGN(sizeof(struct gaih_addrtuple));
213                 i++;
214         }
215
216         assert(i == c);
217
218         r = sd_bus_message_exit_container(reply);
219         if (r < 0)
220                 goto fail;
221
222         assert(idx == ms);
223
224         if (*pat)
225                 **pat = *r_tuple_first;
226         else
227                 *pat = r_tuple_first;
228
229         if (ttlp)
230                 *ttlp = 0;
231
232         return NSS_STATUS_SUCCESS;
233
234 fail:
235         *errnop = -r;
236         *h_errnop = NO_DATA;
237         return NSS_STATUS_UNAVAIL;
238 }
239
240 enum nss_status _nss_resolve_gethostbyname3_r(
241                 const char *name,
242                 int af,
243                 struct hostent *result,
244                 char *buffer, size_t buflen,
245                 int *errnop, int *h_errnop,
246                 int32_t *ttlp,
247                 char **canonp) {
248
249         _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
250         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
251         char *r_name, *r_aliases, *r_addr, *r_addr_list;
252         _cleanup_bus_unref_ sd_bus *bus = NULL;
253         unsigned c = 0, i = 0;
254         size_t l, idx, ms, alen;
255         int r;
256
257         assert(name);
258         assert(result);
259         assert(buffer);
260         assert(errnop);
261         assert(h_errnop);
262
263         if (af == AF_UNSPEC)
264                 af = AF_INET;
265
266         if (af != AF_INET && af != AF_INET6) {
267                 r = -EAFNOSUPPORT;
268                 goto fail;
269         }
270
271         r = sd_bus_open_system(&bus);
272         if (r < 0)
273                 goto fail;
274
275         r = sd_bus_message_new_method_call(
276                         bus,
277                         &req,
278                         "org.freedesktop.resolve1",
279                         "/org/freedesktop/resolve1",
280                         "org.freedesktop.resolve1.Manager",
281                         "ResolveHostname");
282         if (r < 0)
283                 goto fail;
284
285         r = sd_bus_message_set_auto_start(req, false);
286         if (r < 0)
287                 goto fail;
288
289         r = sd_bus_message_append(req, "sy", name, af);
290         if (r < 0)
291                 goto fail;
292
293         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
294         if (r < 0) {
295                 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
296                         *errnop = ESRCH;
297                         *h_errnop = HOST_NOT_FOUND;
298                         return NSS_STATUS_NOTFOUND;
299                 }
300
301                 *errnop = -r;
302                 *h_errnop = NO_RECOVERY;
303                 return NSS_STATUS_UNAVAIL;
304         }
305
306         r = sd_bus_message_enter_container(reply, 'a', "(yayi)");
307         if (r < 0)
308                 goto fail;
309
310         r = count_addresses(reply, af, &c);
311         if (r < 0)
312                 goto fail;
313
314         if (c <= 0) {
315                 *errnop = ESRCH;
316                 *h_errnop = HOST_NOT_FOUND;
317                 return NSS_STATUS_NOTFOUND;
318         }
319
320         alen = PROTO_ADDRESS_SIZE(af);
321         l = strlen(name);
322
323         ms = ALIGN(l+1) +
324                 sizeof(char*) +
325                 (c > 0 ? c : 1) * ALIGN(alen) +
326                 (c > 0 ? c+1 : 2) * sizeof(char*);
327
328         if (buflen < ms) {
329                 *errnop = ENOMEM;
330                 *h_errnop = TRY_AGAIN;
331                 return NSS_STATUS_TRYAGAIN;
332         }
333
334         /* First, append name */
335         r_name = buffer;
336         memcpy(r_name, name, l+1);
337         idx = ALIGN(l+1);
338
339         /* Second, create aliases array */
340         r_aliases = buffer + idx;
341         ((char**) r_aliases)[0] = NULL;
342         idx += sizeof(char*);
343
344         /* Third, append addresses */
345         r_addr = buffer + idx;
346         while ((r = sd_bus_message_enter_container(reply, 'r', "yayi")) > 0) {
347                 unsigned char family;
348                 const void *a;
349                 int ifindex;
350                 size_t sz;
351
352                 r = sd_bus_message_read(reply, "y", &family);
353                 if (r < 0)
354                         goto fail;
355
356                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
357                 if (r < 0)
358                         goto fail;
359
360                 r = sd_bus_message_read(reply, "i", &ifindex);
361                 if (r < 0)
362                         goto fail;
363
364                 r = sd_bus_message_exit_container(reply);
365                 if (r < 0)
366                         goto fail;
367
368                 if (family != af)
369                         continue;
370
371                 if (sz != alen) {
372                         r = -EINVAL;
373                         goto fail;
374                 }
375
376                 if (ifindex < 0) {
377                         r = -EINVAL;
378                         goto fail;
379                 }
380
381                 memcpy(r_addr + i*ALIGN(alen), a, alen);
382                 i++;
383         }
384
385         assert(i == c);
386         idx += c * ALIGN(alen);
387
388         r = sd_bus_message_exit_container(reply);
389         if (r < 0)
390                 goto fail;
391
392         /* Third, append address pointer array */
393         r_addr_list = buffer + idx;
394         for (i = 0; i < c; i++)
395                 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
396
397         ((char**) r_addr_list)[i] = NULL;
398         idx += (c+1) * sizeof(char*);
399
400         assert(idx == ms);
401
402         result->h_name = r_name;
403         result->h_aliases = (char**) r_aliases;
404         result->h_addrtype = af;
405         result->h_length = alen;
406         result->h_addr_list = (char**) r_addr_list;
407
408         if (ttlp)
409                 *ttlp = 0;
410
411         if (canonp)
412                 *canonp = r_name;
413
414         return NSS_STATUS_SUCCESS;
415
416 fail:
417         *errnop = -r;
418         *h_errnop = NO_DATA;
419         return NSS_STATUS_UNAVAIL;
420 }
421
422 enum nss_status _nss_resolve_gethostbyaddr2_r(
423                 const void* addr, socklen_t len,
424                 int af,
425                 struct hostent *result,
426                 char *buffer, size_t buflen,
427                 int *errnop, int *h_errnop,
428                 int32_t *ttlp) {
429
430         _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
431         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
432         char *r_name, *r_aliases, *r_addr, *r_addr_list;
433         _cleanup_bus_unref_ sd_bus *bus = NULL;
434         unsigned c = 0, i = 0;
435         size_t ms = 0, idx;
436         const char *n;
437         int r;
438
439         assert(addr);
440         assert(result);
441         assert(buffer);
442         assert(errnop);
443         assert(h_errnop);
444
445         if (!IN_SET(af, AF_INET, AF_INET6)) {
446                 *errnop = EAFNOSUPPORT;
447                 *h_errnop = NO_DATA;
448                 return NSS_STATUS_UNAVAIL;
449         }
450
451         if (len != PROTO_ADDRESS_SIZE(af)) {
452                 *errnop = EINVAL;
453                 *h_errnop = NO_RECOVERY;
454                 return NSS_STATUS_UNAVAIL;
455         }
456
457         r = sd_bus_open_system(&bus);
458         if (r < 0)
459                 goto fail;
460
461         r = sd_bus_message_new_method_call(
462                         bus,
463                         &req,
464                         "org.freedesktop.resolve1",
465                         "/org/freedesktop/resolve1",
466                         "org.freedesktop.resolve1.Manager",
467                         "ResolveAddress");
468         if (r < 0)
469                 goto fail;
470
471         r = sd_bus_message_set_auto_start(req, false);
472         if (r < 0)
473                 goto fail;
474
475         r = sd_bus_message_append(req, "y", af);
476         if (r < 0)
477                 goto fail;
478
479         r = sd_bus_message_append_array(req, 'y', addr, len);
480         if (r < 0)
481                 goto fail;
482
483         r = sd_bus_message_append(req, "i", 0);
484         if (r < 0)
485                 goto fail;
486
487         r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
488         if (r < 0) {
489                 if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) {
490                         *errnop = ESRCH;
491                         *h_errnop = HOST_NOT_FOUND;
492                         return NSS_STATUS_NOTFOUND;
493                 }
494
495                 *errnop = -r;
496                 *h_errnop = NO_RECOVERY;
497                 return NSS_STATUS_UNAVAIL;
498         }
499
500         r = sd_bus_message_enter_container(reply, 'a', "s");
501         if (r < 0)
502                 goto fail;
503
504         while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
505                 c++;
506                 ms += ALIGN(strlen(n) + 1);
507         }
508         if (r < 0)
509                 goto fail;
510
511         r = sd_bus_message_rewind(reply, false);
512         if (r < 0)
513                 return r;
514
515         if (c <= 0) {
516                 *errnop = ESRCH;
517                 *h_errnop = HOST_NOT_FOUND;
518                 return NSS_STATUS_NOTFOUND;
519         }
520
521         ms += ALIGN(len) +              /* the address */
522               2 * sizeof(char*) +       /* pointers to the address, plus trailing NULL */
523               c * sizeof(char*);        /* pointers to aliases, plus trailing NULL */
524
525         if (buflen < ms) {
526                 *errnop = ENOMEM;
527                 *h_errnop = TRY_AGAIN;
528                 return NSS_STATUS_TRYAGAIN;
529         }
530
531         /* First, place address */
532         r_addr = buffer;
533         memcpy(r_addr, addr, len);
534         idx = ALIGN(len);
535
536         /* Second, place address list */
537         r_addr_list = buffer + idx;
538         ((char**) r_addr_list)[0] = r_addr;
539         ((char**) r_addr_list)[1] = NULL;
540         idx += sizeof(char*) * 2;
541
542         /* Third, reserve space for the aliases array */
543         r_aliases = buffer + idx;
544         idx += sizeof(char*) * c;
545
546         /* Fourth, place aliases */
547         i = 0;
548         r_name = buffer + idx;
549         while ((r = sd_bus_message_read(reply, "s", &n)) > 0) {
550                 char *p;
551                 size_t l;
552
553                 l = strlen(n);
554                 p = buffer + idx;
555                 memcpy(p, n, l+1);
556
557                 if (i > 1)
558                         ((char**) r_aliases)[i-1] = p;
559                 i++;
560
561                 idx += ALIGN(l+1);
562         }
563
564         ((char**) r_aliases)[c-1] = NULL;
565         assert(idx == ms);
566
567         result->h_name = r_name;
568         result->h_aliases = (char**) r_aliases;
569         result->h_addrtype = af;
570         result->h_length = len;
571         result->h_addr_list = (char**) r_addr_list;
572
573         if (ttlp)
574                 *ttlp = 0;
575
576         return NSS_STATUS_SUCCESS;
577
578 fail:
579         *errnop = -r;
580         *h_errnop = NO_DATA;
581         return NSS_STATUS_UNAVAIL;
582 }
583
584 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
585 NSS_GETHOSTBYADDR_FALLBACKS(resolve);