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