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