]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nss-resolve/nss-resolve.c
resolved: add small NSS module that uses resolved to resolve DNS names
[thirdparty/systemd.git] / src / nss-resolve / nss-resolve.c
CommitLineData
4d1cf1e2
LP
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
41NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
42NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
43
44#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC)
45
46static 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
84enum 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
234fail:
235 *errnop = -r;
236 *h_errnop = NO_DATA;
237 return NSS_STATUS_UNAVAIL;
238}
239
240enum 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
416fail:
417 *errnop = -r;
418 *h_errnop = NO_DATA;
419 return NSS_STATUS_UNAVAIL;
420}
421
422enum 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
578fail:
579 *errnop = -r;
580 *h_errnop = NO_DATA;
581 return NSS_STATUS_UNAVAIL;
582}
583
584NSS_GETHOSTBYNAME_FALLBACKS(resolve);
585NSS_GETHOSTBYADDR_FALLBACKS(resolve);