]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-resolve/nss-resolve.c
Merge pull request #20894 from andir/editorconfig
[thirdparty/systemd.git] / src / nss-resolve / nss-resolve.c
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
24 static void setup_logging(void) {
25 log_parse_environment();
26
27 if (DEBUG_LOGGING)
28 json_dispatch_flags = JSON_LOG;
29 }
30
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
43 static 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);
51 }
52
53 static int connect_to_resolved(Varlink **ret) {
54 _cleanup_(varlink_unrefp) Varlink *link = NULL;
55 int r;
56
57 r = varlink_connect_address(&link, "/run/systemd/resolve/io.systemd.Resolve");
58 if (r < 0)
59 return r;
60
61 r = varlink_set_relative_timeout(link, SD_RESOLVED_QUERY_TIMEOUT_USEC);
62 if (r < 0)
63 return r;
64
65 *ret = TAKE_PTR(link);
66 return 0;
67 }
68
69 static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
70 struct in6_addr in6;
71
72 if (family != AF_INET6 || ifindex == 0)
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
77 assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET6));
78 memcpy(&in6, a, sizeof(struct in6_addr));
79
80 return in6_addr_is_link_local(&in6) ? ifindex : 0;
81 }
82
83 static int json_dispatch_ifindex(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
84 int *ifi = userdata;
85 intmax_t t;
86
87 assert(variant);
88 assert(ifi);
89
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));
92
93 t = json_variant_integer(variant);
94 if (t > INT_MAX)
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
101 static 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
119 typedef struct ResolveHostnameReply {
120 JsonVariant *addresses;
121 char *name;
122 uint64_t flags;
123 } ResolveHostnameReply;
124
125 static void resolve_hostname_reply_destroy(ResolveHostnameReply *p) {
126 assert(p);
127
128 json_variant_unref(p->addresses);
129 free(p->name);
130 }
131
132 static 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
139 typedef struct AddressParameters {
140 int ifindex;
141 int family;
142 union in_addr_union address;
143 size_t address_size;
144 } AddressParameters;
145
146 static 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
181 static 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
188 static 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
203 enum 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
210 _cleanup_(varlink_unrefp) Varlink *link = NULL;
211 _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
212 _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
213 JsonVariant *rparams, *entry;
214 int r;
215
216 PROTECT_ERRNO;
217 NSS_ENTRYPOINT_BEGIN;
218
219 assert(name);
220 assert(pat);
221 assert(buffer);
222 assert(errnop);
223 assert(h_errnop);
224
225 r = connect_to_resolved(&link);
226 if (r < 0)
227 goto fail;
228
229 r = json_build(&cparams, JSON_BUILD_OBJECT(
230 JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
231 JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
232 if (r < 0)
233 goto fail;
234
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). */
240 const char *error_id;
241 r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
242 if (r < 0)
243 goto fail;
244 if (!isempty(error_id)) {
245 if (!error_shall_fallback(error_id))
246 goto not_found;
247 goto fail;
248 }
249
250 r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, json_dispatch_flags, &p);
251 if (r < 0)
252 goto fail;
253 if (json_variant_is_blank_object(p.addresses))
254 goto not_found;
255
256 size_t n_addresses = 0;
257 JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
258 AddressParameters q = {};
259
260 r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
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
272 n_addresses++;
273 }
274
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;
278
279 if (buflen < ms) {
280 UNPROTECT_ERRNO;
281 *errnop = ERANGE;
282 *h_errnop = NETDB_INTERNAL;
283 return NSS_STATUS_TRYAGAIN;
284 }
285
286 /* First, append name */
287 char *r_name = buffer;
288 memcpy(r_name, canonical, l + 1);
289 idx = ALIGN(l + 1);
290
291 /* Second, append addresses */
292 struct gaih_addrtuple *r_tuple = NULL,
293 *r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
294
295 JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
296 AddressParameters q = {};
297
298 r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
299 if (r < 0)
300 goto fail;
301
302 if (!IN_SET(q.family, AF_INET, AF_INET6))
303 continue;
304
305 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
306 r_tuple->next = (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
307 r_tuple->name = r_name;
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);
311
312 idx += ALIGN(sizeof(struct gaih_addrtuple));
313 }
314
315 assert(r_tuple); /* We had at least one address, so r_tuple must be set */
316 r_tuple->next = NULL; /* Override last next pointer */
317
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
328 /* Explicitly reset both *h_errnop and h_errno to work around
329 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
330 *h_errnop = NETDB_SUCCESS;
331 h_errno = 0;
332
333 return NSS_STATUS_SUCCESS;
334
335 fail:
336 UNPROTECT_ERRNO;
337 *errnop = -r;
338 *h_errnop = NO_RECOVERY;
339 return NSS_STATUS_UNAVAIL;
340
341 not_found:
342 *h_errnop = HOST_NOT_FOUND;
343 return NSS_STATUS_NOTFOUND;
344 }
345
346 enum 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
355 _cleanup_(varlink_unrefp) Varlink *link = NULL;
356 _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
357 _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
358 JsonVariant *rparams, *entry;
359 int r;
360
361 PROTECT_ERRNO;
362 NSS_ENTRYPOINT_BEGIN;
363
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
373 if (!IN_SET(af, AF_INET, AF_INET6)) {
374 r = -EAFNOSUPPORT;
375 goto fail;
376 }
377
378 r = connect_to_resolved(&link);
379 if (r < 0)
380 goto fail;
381
382 r = json_build(&cparams, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
383 JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af)),
384 JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
385 if (r < 0)
386 goto fail;
387
388 const char *error_id;
389 r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
390 if (r < 0)
391 goto fail;
392 if (!isempty(error_id)) {
393 if (!error_shall_fallback(error_id))
394 goto not_found;
395 goto fail;
396 }
397
398 r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, json_dispatch_flags, &p);
399 if (r < 0)
400 goto fail;
401 if (json_variant_is_blank_object(p.addresses))
402 goto not_found;
403
404 size_t n_addresses = 0;
405 JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
406 AddressParameters q = {};
407
408 r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
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
420 n_addresses++;
421 }
422
423 const char *canonical = p.name ?: name;
424
425 size_t alen = FAMILY_ADDRESS_SIZE(af);
426 size_t l = strlen(canonical);
427
428 size_t idx, ms = ALIGN(l + 1) + n_addresses * ALIGN(alen) + (n_addresses + 2) * sizeof(char*);
429
430 if (buflen < ms) {
431 UNPROTECT_ERRNO;
432 *errnop = ERANGE;
433 *h_errnop = NETDB_INTERNAL;
434 return NSS_STATUS_TRYAGAIN;
435 }
436
437 /* First, append name */
438 char *r_name = buffer;
439 memcpy(r_name, canonical, l+1);
440 idx = ALIGN(l+1);
441
442 /* Second, create empty aliases array */
443 char *r_aliases = buffer + idx;
444 ((char**) r_aliases)[0] = NULL;
445 idx += sizeof(char*);
446
447 /* Third, append addresses */
448 char *r_addr = buffer + idx;
449
450 size_t i = 0;
451 JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
452 AddressParameters q = {};
453
454 r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
455 if (r < 0)
456 goto fail;
457
458 if (q.family != af)
459 continue;
460
461 if (q.address_size != alen) {
462 r = -EINVAL;
463 goto fail;
464 }
465
466 memcpy(r_addr + i*ALIGN(alen), &q.address, alen);
467 i++;
468 }
469
470 assert(i == n_addresses);
471 idx += n_addresses * ALIGN(alen);
472
473 /* Fourth, append address pointer array */
474 char *r_addr_list = buffer + idx;
475 for (i = 0; i < n_addresses; i++)
476 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
477
478 ((char**) r_addr_list)[i] = NULL;
479 idx += (n_addresses + 1) * sizeof(char*);
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
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
500 return NSS_STATUS_SUCCESS;
501
502 fail:
503 UNPROTECT_ERRNO;
504 *errnop = -r;
505 *h_errnop = NO_RECOVERY;
506 return NSS_STATUS_UNAVAIL;
507
508 not_found:
509 *h_errnop = HOST_NOT_FOUND;
510 return NSS_STATUS_NOTFOUND;
511 }
512
513 typedef struct ResolveAddressReply {
514 JsonVariant *names;
515 uint64_t flags;
516 } ResolveAddressReply;
517
518 static void resolve_address_reply_destroy(ResolveAddressReply *p) {
519 assert(p);
520
521 json_variant_unref(p->names);
522 }
523
524 static 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
530 typedef struct NameParameters {
531 int ifindex;
532 char *name;
533 } NameParameters;
534
535 static void name_parameters_destroy(NameParameters *p) {
536 assert(p);
537
538 free(p->name);
539 }
540
541 static const JsonDispatch name_parameters_dispatch_table[] = {
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 },
544 {}
545 };
546
547 enum 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
555 _cleanup_(varlink_unrefp) Varlink *link = NULL;
556 _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
557 _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {};
558 JsonVariant *rparams, *entry;
559 int r;
560
561 PROTECT_ERRNO;
562 NSS_ENTRYPOINT_BEGIN;
563
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)) {
571 UNPROTECT_ERRNO;
572 *errnop = EAFNOSUPPORT;
573 *h_errnop = NO_DATA;
574 return NSS_STATUS_UNAVAIL;
575 }
576
577 if (len != FAMILY_ADDRESS_SIZE(af)) {
578 r = -EINVAL;
579 goto fail;
580 }
581
582 r = connect_to_resolved(&link);
583 if (r < 0)
584 goto fail;
585
586 r = json_build(&cparams, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("address", JSON_BUILD_BYTE_ARRAY(addr, len)),
587 JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af)),
588 JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
589 if (r < 0)
590 goto fail;
591
592 const char* error_id;
593 r = varlink_call(link, "io.systemd.Resolve.ResolveAddress", cparams, &rparams, &error_id, NULL);
594 if (r < 0)
595 goto fail;
596 if (!isempty(error_id)) {
597 if (!error_shall_fallback(error_id))
598 goto not_found;
599 goto fail;
600 }
601
602 r = json_dispatch(rparams, resolve_address_reply_dispatch_table, NULL, json_dispatch_flags, &p);
603 if (r < 0)
604 goto fail;
605 if (json_variant_is_blank_object(p.names))
606 goto not_found;
607
608 size_t ms = 0, idx;
609
610 JSON_VARIANT_ARRAY_FOREACH(entry, p.names) {
611 _cleanup_(name_parameters_destroy) NameParameters q = {};
612
613 r = json_dispatch(entry, name_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
614 if (r < 0)
615 goto fail;
616
617 ms += ALIGN(strlen(q.name) + 1);
618 }
619
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 */
624
625 if (buflen < ms) {
626 UNPROTECT_ERRNO;
627 *errnop = ERANGE;
628 *h_errnop = NETDB_INTERNAL;
629 return NSS_STATUS_TRYAGAIN;
630 }
631
632 /* First, place address */
633 char *r_addr = buffer;
634 memcpy(r_addr, addr, len);
635 idx = ALIGN(len);
636
637 /* Second, place address list */
638 char *r_addr_list = buffer + idx;
639 ((char**) r_addr_list)[0] = r_addr;
640 ((char**) r_addr_list)[1] = NULL;
641 idx += sizeof(char*) * 2;
642
643 /* Third, reserve space for the aliases array, plus trailing NULL */
644 char *r_aliases = buffer + idx;
645 idx += sizeof(char*) * n_names;
646
647 /* Fourth, place aliases */
648 char *r_name = buffer + idx;
649
650 size_t i = 0;
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 size_t l = strlen(q.name);
659 char *z = buffer + idx;
660 memcpy(z, q.name, l + 1);
661
662 if (i > 0)
663 ((char**) r_aliases)[i - 1] = z;
664 i++;
665
666 idx += ALIGN(l + 1);
667 }
668 ((char**) r_aliases)[n_names - 1] = NULL;
669
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
681 /* Explicitly reset both *h_errnop and h_errno to work around
682 * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
683 *h_errnop = NETDB_SUCCESS;
684 h_errno = 0;
685
686 return NSS_STATUS_SUCCESS;
687
688 fail:
689 UNPROTECT_ERRNO;
690 *errnop = -r;
691 *h_errnop = NO_RECOVERY;
692 return NSS_STATUS_UNAVAIL;
693
694 not_found:
695 *h_errnop = HOST_NOT_FOUND;
696 return NSS_STATUS_NOTFOUND;
697 }
698
699 NSS_GETHOSTBYNAME_FALLBACKS(resolve);
700 NSS_GETHOSTBYADDR_FALLBACKS(resolve);