1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <netdb.h>
5 #include <nss.h>
6 #include <pthread.h>
7 #include <stdlib.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10
11 #include "env-util.h"
12 #include "errno-util.h"
13 #include "in-addr-util.h"
14 #include "macro.h"
15 #include "nss-util.h"
16 #include "resolved-def.h"
17 #include "signal-util.h"
18 #include "string-util.h"
19 #include "strv.h"
20 #include "varlink.h"
21
22 static JsonDispatchFlags json_dispatch_flags = 0;
23
setup_logging(void)24 static void setup_logging(void) {
25 log_parse_environment_variables();
26
27 if (DEBUG_LOGGING)
28 json_dispatch_flags = JSON_LOG;
29 }
30
setup_logging_once(void)31 static void setup_logging_once(void) {
32 static pthread_once_t once = PTHREAD_ONCE_INIT;
33 assert_se(pthread_once(&once, setup_logging) == 0);
34 }
35
36 #define NSS_ENTRYPOINT_BEGIN \
37 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \
38 setup_logging_once()
39
40 NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
41 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
42
error_shall_fallback(const char * error_id)43 static bool error_shall_fallback(const char *error_id) {
44 /* The Varlink errors where we shall signal "please fallback" back to the NSS stack, so that some
45 * fallback module can be loaded. (These are mostly all Varlink-internal errors, as apparently we
46 * then were unable to even do IPC with systemd-resolved.) */
47 return STR_IN_SET(error_id,
48 VARLINK_ERROR_DISCONNECTED,
49 VARLINK_ERROR_TIMEOUT,
50 VARLINK_ERROR_PROTOCOL,
51 VARLINK_ERROR_INTERFACE_NOT_FOUND,
52 VARLINK_ERROR_METHOD_NOT_FOUND,
53 VARLINK_ERROR_METHOD_NOT_IMPLEMENTED);
54 }
55
error_shall_try_again(const char * error_id)56 static bool error_shall_try_again(const char *error_id) {
57 /* The Varlink errors where we shall signal "can't answer now but might be able to later" back to the
58 * NSS stack. These are all errors that indicate lack of configuration or network problems. */
59 return STR_IN_SET(error_id,
60 "io.systemd.Resolve.NoNameServers",
61 "io.systemd.Resolve.QueryTimedOut",
62 "io.systemd.Resolve.MaxAttemptsReached",
63 "io.systemd.Resolve.NetworkDown");
64 }
65
connect_to_resolved(Varlink ** ret)66 static int connect_to_resolved(Varlink **ret) {
67 _cleanup_(varlink_unrefp) Varlink *link = NULL;
68 int r;
69
70 r = varlink_connect_address(&link, "/run/systemd/resolve/io.systemd.Resolve");
71 if (r < 0)
72 return r;
73
74 r = varlink_set_relative_timeout(link, SD_RESOLVED_QUERY_TIMEOUT_USEC);
75 if (r < 0)
76 return r;
77
78 *ret = TAKE_PTR(link);
79 return 0;
80 }
81
ifindex_to_scopeid(int family,const void * a,int ifindex)82 static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
83 struct in6_addr in6;
84
85 if (family != AF_INET6 || ifindex == 0)
86 return 0;
87
88 /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */
89
90 assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET6));
91 memcpy(&in6, a, sizeof(struct in6_addr));
92
93 return in6_addr_is_link_local(&in6) ? ifindex : 0;
94 }
95
json_dispatch_ifindex(const char * name,JsonVariant * variant,JsonDispatchFlags flags,void * userdata)96 static int json_dispatch_ifindex(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
97 int *ifi = userdata;
98 int64_t t;
99
100 assert(variant);
101 assert(ifi);
102
103 if (!json_variant_is_integer(variant))
104 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
105
106 t = json_variant_integer(variant);
107 if (t > INT_MAX)
108 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is out of bounds for an interface index.", strna(name));
109
110 *ifi = (int) t;
111 return 0;
112 }
113
json_dispatch_family(const char * name,JsonVariant * variant,JsonDispatchFlags flags,void * userdata)114 static int json_dispatch_family(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
115 int *family = userdata;
116 int64_t t;
117
118 assert(variant);
119 assert(family);
120
121 if (!json_variant_is_integer(variant))
122 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
123
124 t = json_variant_integer(variant);
125 if (t < 0 || t > INT_MAX)
126 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid family.", strna(name));
127
128 *family = (int) t;
129 return 0;
130 }
131
132 typedef struct ResolveHostnameReply {
133 JsonVariant *addresses;
134 char *name;
135 uint64_t flags;
136 } ResolveHostnameReply;
137
resolve_hostname_reply_destroy(ResolveHostnameReply * p)138 static void resolve_hostname_reply_destroy(ResolveHostnameReply *p) {
139 assert(p);
140
141 json_variant_unref(p->addresses);
142 free(p->name);
143 }
144
145 static const JsonDispatch resolve_hostname_reply_dispatch_table[] = {
146 { "addresses", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(ResolveHostnameReply, addresses), JSON_MANDATORY },
147 { "name", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ResolveHostnameReply, name), 0 },
148 { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(ResolveHostnameReply, flags), 0 },
149 {}
150 };
151
152 typedef struct AddressParameters {
153 int ifindex;
154 int family;
155 union in_addr_union address;
156 size_t address_size;
157 } AddressParameters;
158
json_dispatch_address(const char * name,JsonVariant * variant,JsonDispatchFlags flags,void * userdata)159 static int json_dispatch_address(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
160 AddressParameters *p = userdata;
161 union in_addr_union buf = {};
162 JsonVariant *i;
163 size_t n, k = 0;
164
165 assert(variant);
166 assert(p);
167
168 if (!json_variant_is_array(variant))
169 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
170
171 n = json_variant_elements(variant);
172 if (!IN_SET(n, 4, 16))
173 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name));
174
175 JSON_VARIANT_ARRAY_FOREACH(i, variant) {
176 int64_t b;
177
178 if (!json_variant_is_integer(i))
179 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an integer.", k, strna(name));
180
181 b = json_variant_integer(i);
182 if (b < 0 || b > 0xff)
183 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is out of range 0…255.", k, strna(name));
184
185 buf.bytes[k++] = (uint8_t) b;
186 }
187
188 p->address = buf;
189 p->address_size = k;
190
191 return 0;
192 }
193
194 static const JsonDispatch address_parameters_dispatch_table[] = {
195 { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(AddressParameters, ifindex), 0 },
196 { "family", JSON_VARIANT_INTEGER, json_dispatch_family, offsetof(AddressParameters, family), JSON_MANDATORY },
197 { "address", JSON_VARIANT_ARRAY, json_dispatch_address, 0, JSON_MANDATORY },
198 {}
199 };
200
query_flag(const char * name,const int value,uint64_t flag)201 static uint64_t query_flag(
202 const char *name,
203 const int value,
204 uint64_t flag) {
205 int r;
206
207 r = getenv_bool_secure(name);
208 if (r >= 0)
209 return r == value ? flag : 0;
210 if (r != -ENXIO)
211 log_debug_errno(r, "Failed to parse $%s, ignoring.", name);
212 return 0;
213 }
214
query_flags(void)215 static uint64_t query_flags(void) {
216 /* Allow callers to turn off validation, synthetization, caching, etc., when we resolve via
217 * nss-resolve. */
218 return query_flag("SYSTEMD_NSS_RESOLVE_VALIDATE", 0, SD_RESOLVED_NO_VALIDATE) |
219 query_flag("SYSTEMD_NSS_RESOLVE_SYNTHESIZE", 0, SD_RESOLVED_NO_SYNTHESIZE) |
220 query_flag("SYSTEMD_NSS_RESOLVE_CACHE", 0, SD_RESOLVED_NO_CACHE) |
221 query_flag("SYSTEMD_NSS_RESOLVE_ZONE", 0, SD_RESOLVED_NO_ZONE) |
222 query_flag("SYSTEMD_NSS_RESOLVE_TRUST_ANCHOR", 0, SD_RESOLVED_NO_TRUST_ANCHOR) |
223 query_flag("SYSTEMD_NSS_RESOLVE_NETWORK", 0, SD_RESOLVED_NO_NETWORK);
224 }
225
_nss_resolve_gethostbyname4_r(const char * name,struct gaih_addrtuple ** pat,char * buffer,size_t buflen,int * errnop,int * h_errnop,int32_t * ttlp)226 enum nss_status _nss_resolve_gethostbyname4_r(
227 const char *name,
228 struct gaih_addrtuple **pat,
229 char *buffer, size_t buflen,
230 int *errnop, int *h_errnop,
231 int32_t *ttlp) {
232
233 _cleanup_(varlink_unrefp) Varlink *link = NULL;
234 _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
235 _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
236 JsonVariant *rparams, *entry;
237 int r;
238
239 PROTECT_ERRNO;
240 NSS_ENTRYPOINT_BEGIN;
241
242 assert(name);
243 assert(pat);
244 assert(buffer);
245 assert(errnop);
246 assert(h_errnop);
247
248 r = connect_to_resolved(&link);
249 if (r < 0)
250 goto fail;
251
252 r = json_build(&cparams, JSON_BUILD_OBJECT(
253 JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
254 JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
255 if (r < 0)
256 goto fail;
257
258 /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, allowing falling
259 * back to other nss modules. Treat all other error conditions as NOTFOUND. This includes
260 * DNSSEC errors and suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf
261 * configuration can distinguish such executed but negative replies from complete failure to
262 * talk to resolved). */
263 const char *error_id;
264 r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
265 if (r < 0)
266 goto fail;
267 if (!isempty(error_id)) {
268 if (error_shall_try_again(error_id))
269 goto try_again;
270 if (error_shall_fallback(error_id))
271 goto fail;
272 goto not_found;
273 }
274
275 r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, json_dispatch_flags, &p);
276 if (r < 0)
277 goto fail;
278 if (json_variant_is_blank_object(p.addresses))
279 goto not_found;
280
281 size_t n_addresses = 0;
282 JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
283 AddressParameters q = {};
284
285 r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
286 if (r < 0)
287 goto fail;
288
289 if (!IN_SET(q.family, AF_INET, AF_INET6))
290 continue;
291
292 if (q.address_size != FAMILY_ADDRESS_SIZE(q.family)) {
293 r = -EINVAL;
294 goto fail;
295 }
296
297 n_addresses++;
298 }
299
300 const char *canonical = p.name ?: name;
301 size_t l = strlen(canonical);
302 size_t idx, ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * n_addresses;
303
304 if (buflen < ms) {
305 UNPROTECT_ERRNO;
306 *errnop = ERANGE;
307 *h_errnop = NETDB_INTERNAL;
308 return NSS_STATUS_TRYAGAIN;
309 }
310
311 /* First, append name */
312 char *r_name = buffer;
313 memcpy(r_name, canonical, l + 1);
314 idx = ALIGN(l + 1);
315
316 /* Second, append addresses */
317 struct gaih_addrtuple *r_tuple = NULL,
318 *r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
319
320 JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
321 AddressParameters q = {};
322
323 r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
324 if (r < 0)
325 goto fail;
326
327 if (!IN_SET(q.family, AF_INET, AF_INET6))
328 continue;
329
330 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
331 r_tuple->next = (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
332 r_tuple->name = r_name;
333 r_tuple->family = q.family;
334 r_tuple->scopeid = ifindex_to_scopeid(q.family, &q.address, q.ifindex);
335 memcpy(r_tuple->addr, &q.address, q.address_size);
336
337 idx += ALIGN(sizeof(struct gaih_addrtuple));
338 }
339
340 assert(r_tuple); /* We had at least one address, so r_tuple must be set */
341 r_tuple->next = NULL; /* Override last next pointer */
342
343 assert(idx == ms);
344
345 if (*pat)
346 **pat = *r_tuple_first;
347 else
348 *pat = r_tuple_first;
349
350 if (ttlp)
351 *ttlp = 0;
352
353 /* Explicitly reset both *h_errnop and h_errno to work around
354 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
355 *h_errnop = NETDB_SUCCESS;
356 h_errno = 0;
357
358 return NSS_STATUS_SUCCESS;
359
360 fail:
361 UNPROTECT_ERRNO;
362 *errnop = -r;
363 *h_errnop = NO_RECOVERY;
364 return NSS_STATUS_UNAVAIL;
365
366 not_found:
367 *h_errnop = HOST_NOT_FOUND;
368 return NSS_STATUS_NOTFOUND;
369
370 try_again:
371 UNPROTECT_ERRNO;
372 *errnop = -r;
373 *h_errnop = TRY_AGAIN;
374 return NSS_STATUS_TRYAGAIN;
375 }
376
_nss_resolve_gethostbyname3_r(const char * name,int af,struct hostent * result,char * buffer,size_t buflen,int * errnop,int * h_errnop,int32_t * ttlp,char ** canonp)377 enum nss_status _nss_resolve_gethostbyname3_r(
378 const char *name,
379 int af,
380 struct hostent *result,
381 char *buffer, size_t buflen,
382 int *errnop, int *h_errnop,
383 int32_t *ttlp,
384 char **canonp) {
385
386 _cleanup_(varlink_unrefp) Varlink *link = NULL;
387 _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
388 _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
389 JsonVariant *rparams, *entry;
390 int r;
391
392 PROTECT_ERRNO;
393 NSS_ENTRYPOINT_BEGIN;
394
395 assert(name);
396 assert(result);
397 assert(buffer);
398 assert(errnop);
399 assert(h_errnop);
400
401 if (af == AF_UNSPEC)
402 af = AF_INET;
403
404 if (!IN_SET(af, AF_INET, AF_INET6)) {
405 r = -EAFNOSUPPORT;
406 goto fail;
407 }
408
409 r = connect_to_resolved(&link);
410 if (r < 0)
411 goto fail;
412
413 r = json_build(&cparams, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
414 JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af)),
415 JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
416 if (r < 0)
417 goto fail;
418
419 const char *error_id;
420 r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
421 if (r < 0)
422 goto fail;
423 if (!isempty(error_id)) {
424 if (error_shall_try_again(error_id))
425 goto try_again;
426 if (error_shall_fallback(error_id))
427 goto fail;
428 goto not_found;
429 }
430
431 r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, json_dispatch_flags, &p);
432 if (r < 0)
433 goto fail;
434 if (json_variant_is_blank_object(p.addresses))
435 goto not_found;
436
437 size_t n_addresses = 0;
438 JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
439 AddressParameters q = {};
440
441 r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
442 if (r < 0)
443 goto fail;
444
445 if (!IN_SET(q.family, AF_INET, AF_INET6))
446 continue;
447
448 if (q.address_size != FAMILY_ADDRESS_SIZE(q.family)) {
449 r = -EINVAL;
450 goto fail;
451 }
452
453 n_addresses++;
454 }
455
456 const char *canonical = p.name ?: name;
457
458 size_t alen = FAMILY_ADDRESS_SIZE(af);
459 size_t l = strlen(canonical);
460
461 size_t idx, ms = ALIGN(l + 1) + n_addresses * ALIGN(alen) + (n_addresses + 2) * sizeof(char*);
462
463 if (buflen < ms) {
464 UNPROTECT_ERRNO;
465 *errnop = ERANGE;
466 *h_errnop = NETDB_INTERNAL;
467 return NSS_STATUS_TRYAGAIN;
468 }
469
470 /* First, append name */
471 char *r_name = buffer;
472 memcpy(r_name, canonical, l+1);
473 idx = ALIGN(l+1);
474
475 /* Second, create empty aliases array */
476 char *r_aliases = buffer + idx;
477 ((char**) r_aliases)[0] = NULL;
478 idx += sizeof(char*);
479
480 /* Third, append addresses */
481 char *r_addr = buffer + idx;
482
483 size_t i = 0;
484 JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
485 AddressParameters q = {};
486
487 r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
488 if (r < 0)
489 goto fail;
490
491 if (q.family != af)
492 continue;
493
494 if (q.address_size != alen) {
495 r = -EINVAL;
496 goto fail;
497 }
498
499 memcpy(r_addr + i*ALIGN(alen), &q.address, alen);
500 i++;
501 }
502
503 assert(i == n_addresses);
504 idx += n_addresses * ALIGN(alen);
505
506 /* Fourth, append address pointer array */
507 char *r_addr_list = buffer + idx;
508 for (i = 0; i < n_addresses; i++)
509 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
510
511 ((char**) r_addr_list)[i] = NULL;
512 idx += (n_addresses + 1) * sizeof(char*);
513
514 assert(idx == ms);
515
516 result->h_name = r_name;
517 result->h_aliases = (char**) r_aliases;
518 result->h_addrtype = af;
519 result->h_length = alen;
520 result->h_addr_list = (char**) r_addr_list;
521
522 if (ttlp)
523 *ttlp = 0;
524
525 if (canonp)
526 *canonp = r_name;
527
528 /* Explicitly reset both *h_errnop and h_errno to work around
529 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
530 *h_errnop = NETDB_SUCCESS;
531 h_errno = 0;
532
533 return NSS_STATUS_SUCCESS;
534
535 fail:
536 UNPROTECT_ERRNO;
537 *errnop = -r;
538 *h_errnop = NO_RECOVERY;
539 return NSS_STATUS_UNAVAIL;
540
541 not_found:
542 *h_errnop = HOST_NOT_FOUND;
543 return NSS_STATUS_NOTFOUND;
544
545 try_again:
546 UNPROTECT_ERRNO;
547 *errnop = -r;
548 *h_errnop = TRY_AGAIN;
549 return NSS_STATUS_TRYAGAIN;
550 }
551
552 typedef struct ResolveAddressReply {
553 JsonVariant *names;
554 uint64_t flags;
555 } ResolveAddressReply;
556
resolve_address_reply_destroy(ResolveAddressReply * p)557 static void resolve_address_reply_destroy(ResolveAddressReply *p) {
558 assert(p);
559
560 json_variant_unref(p->names);
561 }
562
563 static const JsonDispatch resolve_address_reply_dispatch_table[] = {
564 { "names", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(ResolveAddressReply, names), JSON_MANDATORY },
565 { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(ResolveAddressReply, flags), 0 },
566 {}
567 };
568
569 typedef struct NameParameters {
570 int ifindex;
571 char *name;
572 } NameParameters;
573
name_parameters_destroy(NameParameters * p)574 static void name_parameters_destroy(NameParameters *p) {
575 assert(p);
576
577 free(p->name);
578 }
579
580 static const JsonDispatch name_parameters_dispatch_table[] = {
581 { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(NameParameters, ifindex), 0 },
582 { "name", JSON_VARIANT_STRING, json_dispatch_string, offsetof(NameParameters, name), JSON_MANDATORY },
583 {}
584 };
585
_nss_resolve_gethostbyaddr2_r(const void * addr,socklen_t len,int af,struct hostent * result,char * buffer,size_t buflen,int * errnop,int * h_errnop,int32_t * ttlp)586 enum nss_status _nss_resolve_gethostbyaddr2_r(
587 const void* addr, socklen_t len,
588 int af,
589 struct hostent *result,
590 char *buffer, size_t buflen,
591 int *errnop, int *h_errnop,
592 int32_t *ttlp) {
593
594 _cleanup_(varlink_unrefp) Varlink *link = NULL;
595 _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
596 _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {};
597 JsonVariant *rparams, *entry;
598 int r;
599
600 PROTECT_ERRNO;
601 NSS_ENTRYPOINT_BEGIN;
602
603 assert(addr);
604 assert(result);
605 assert(buffer);
606 assert(errnop);
607 assert(h_errnop);
608
609 if (!IN_SET(af, AF_INET, AF_INET6)) {
610 UNPROTECT_ERRNO;
611 *errnop = EAFNOSUPPORT;
612 *h_errnop = NO_DATA;
613 return NSS_STATUS_UNAVAIL;
614 }
615
616 if (len != FAMILY_ADDRESS_SIZE(af)) {
617 r = -EINVAL;
618 goto fail;
619 }
620
621 r = connect_to_resolved(&link);
622 if (r < 0)
623 goto fail;
624
625 r = json_build(&cparams, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("address", JSON_BUILD_BYTE_ARRAY(addr, len)),
626 JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af)),
627 JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
628 if (r < 0)
629 goto fail;
630
631 const char* error_id;
632 r = varlink_call(link, "io.systemd.Resolve.ResolveAddress", cparams, &rparams, &error_id, NULL);
633 if (r < 0)
634 goto fail;
635 if (!isempty(error_id)) {
636 if (error_shall_try_again(error_id))
637 goto try_again;
638 if (error_shall_fallback(error_id))
639 goto fail;
640 goto not_found;
641 }
642
643 r = json_dispatch(rparams, resolve_address_reply_dispatch_table, NULL, json_dispatch_flags, &p);
644 if (r < 0)
645 goto fail;
646 if (json_variant_is_blank_object(p.names))
647 goto not_found;
648
649 size_t ms = 0, idx;
650
651 JSON_VARIANT_ARRAY_FOREACH(entry, p.names) {
652 _cleanup_(name_parameters_destroy) NameParameters q = {};
653
654 r = json_dispatch(entry, name_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
655 if (r < 0)
656 goto fail;
657
658 ms += ALIGN(strlen(q.name) + 1);
659 }
660
661 size_t n_names = json_variant_elements(p.names);
662 ms += ALIGN(len) + /* the address */
663 2 * sizeof(char*) + /* pointer to the address, plus trailing NULL */
664 n_names * sizeof(char*); /* pointers to aliases, plus trailing NULL */
665
666 if (buflen < ms) {
667 UNPROTECT_ERRNO;
668 *errnop = ERANGE;
669 *h_errnop = NETDB_INTERNAL;
670 return NSS_STATUS_TRYAGAIN;
671 }
672
673 /* First, place address */
674 char *r_addr = buffer;
675 memcpy(r_addr, addr, len);
676 idx = ALIGN(len);
677
678 /* Second, place address list */
679 char *r_addr_list = buffer + idx;
680 ((char**) r_addr_list)[0] = r_addr;
681 ((char**) r_addr_list)[1] = NULL;
682 idx += sizeof(char*) * 2;
683
684 /* Third, reserve space for the aliases array, plus trailing NULL */
685 char *r_aliases = buffer + idx;
686 idx += sizeof(char*) * n_names;
687
688 /* Fourth, place aliases */
689 char *r_name = buffer + idx;
690
691 size_t i = 0;
692 JSON_VARIANT_ARRAY_FOREACH(entry, p.names) {
693 _cleanup_(name_parameters_destroy) NameParameters q = {};
694
695 r = json_dispatch(entry, name_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
696 if (r < 0)
697 goto fail;
698
699 size_t l = strlen(q.name);
700 char *z = buffer + idx;
701 memcpy(z, q.name, l + 1);
702
703 if (i > 0)
704 ((char**) r_aliases)[i - 1] = z;
705 i++;
706
707 idx += ALIGN(l + 1);
708 }
709 ((char**) r_aliases)[n_names - 1] = NULL;
710
711 assert(idx == ms);
712
713 result->h_name = r_name;
714 result->h_aliases = (char**) r_aliases;
715 result->h_addrtype = af;
716 result->h_length = len;
717 result->h_addr_list = (char**) r_addr_list;
718
719 if (ttlp)
720 *ttlp = 0;
721
722 /* Explicitly reset both *h_errnop and h_errno to work around
723 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
724 *h_errnop = NETDB_SUCCESS;
725 h_errno = 0;
726
727 return NSS_STATUS_SUCCESS;
728
729 fail:
730 UNPROTECT_ERRNO;
731 *errnop = -r;
732 *h_errnop = NO_RECOVERY;
733 return NSS_STATUS_UNAVAIL;
734
735 not_found:
736 *h_errnop = HOST_NOT_FOUND;
737 return NSS_STATUS_NOTFOUND;
738
739 try_again:
740 UNPROTECT_ERRNO;
741 *errnop = -r;
742 *h_errnop = TRY_AGAIN;
743 return NSS_STATUS_TRYAGAIN;
744 }
745
746 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
747 NSS_GETHOSTBYADDR_FALLBACKS(resolve);
748