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