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