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