]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/nss-resolve/nss-resolve.c
Merge pull request #31524 from poettering/secure-getenv-naming-fix
[thirdparty/systemd.git] / src / nss-resolve / nss-resolve.c
index dfc0977c849c3692479bdc1428fa0bd20da716dd..3cefb6394c8336419e6aa843aba3bb85dc86ce47 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "env-util.h"
 #include "errno-util.h"
+#include "glyph-util.h"
 #include "in-addr-util.h"
 #include "macro.h"
 #include "nss-util.h"
 #include "strv.h"
 #include "varlink.h"
 
-static JsonDispatchFlags json_dispatch_flags = 0;
+static JsonDispatchFlags json_dispatch_flags = JSON_ALLOW_EXTENSIONS;
 
 static void setup_logging(void) {
-        log_parse_environment();
+        log_parse_environment_variables();
 
         if (DEBUG_LOGGING)
                 json_dispatch_flags = JSON_LOG;
@@ -41,6 +42,9 @@ NSS_GETHOSTBYNAME_PROTOTYPES(resolve);
 NSS_GETHOSTBYADDR_PROTOTYPES(resolve);
 
 static bool error_shall_fallback(const char *error_id) {
+        /* The Varlink errors where we shall signal "please fallback" back to the NSS stack, so that some
+         * fallback module can be loaded. (These are mostly all Varlink-internal errors, as apparently we
+         * then were unable to even do IPC with systemd-resolved.) */
         return STR_IN_SET(error_id,
                           VARLINK_ERROR_DISCONNECTED,
                           VARLINK_ERROR_TIMEOUT,
@@ -50,6 +54,16 @@ static bool error_shall_fallback(const char *error_id) {
                           VARLINK_ERROR_METHOD_NOT_IMPLEMENTED);
 }
 
+static bool error_shall_try_again(const char *error_id) {
+        /* The Varlink errors where we shall signal "can't answer now but might be able to later" back to the
+         * NSS stack. These are all errors that indicate lack of configuration or network problems. */
+        return STR_IN_SET(error_id,
+                          "io.systemd.Resolve.NoNameServers",
+                          "io.systemd.Resolve.QueryTimedOut",
+                          "io.systemd.Resolve.MaxAttemptsReached",
+                          "io.systemd.Resolve.NetworkDown");
+}
+
 static int connect_to_resolved(Varlink **ret) {
         _cleanup_(varlink_unrefp) Varlink *link = NULL;
         int r;
@@ -81,11 +95,10 @@ static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) {
 }
 
 static int json_dispatch_ifindex(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
-        int *ifi = userdata;
-        intmax_t t;
+        int *ifi = ASSERT_PTR(userdata);
+        int64_t t;
 
         assert(variant);
-        assert(ifi);
 
         if (!json_variant_is_integer(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
@@ -99,11 +112,10 @@ static int json_dispatch_ifindex(const char *name, JsonVariant *variant, JsonDis
 }
 
 static int json_dispatch_family(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
-        int *family = userdata;
-        intmax_t t;
+        int *family = ASSERT_PTR(userdata);
+        int64_t t;
 
         assert(variant);
-        assert(family);
 
         if (!json_variant_is_integer(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
@@ -130,9 +142,9 @@ static void resolve_hostname_reply_destroy(ResolveHostnameReply *p) {
 }
 
 static const JsonDispatch resolve_hostname_reply_dispatch_table[] = {
-        { "addresses", JSON_VARIANT_ARRAY,    json_dispatch_variant, offsetof(ResolveHostnameReply, addresses), JSON_MANDATORY },
-        { "name",      JSON_VARIANT_STRING,   json_dispatch_string,  offsetof(ResolveHostnameReply, name),      0              },
-        { "flags",     JSON_VARIANT_UNSIGNED, json_dispatch_uint64,  offsetof(ResolveHostnameReply, flags),     0              },
+        { "addresses", JSON_VARIANT_ARRAY,         json_dispatch_variant, offsetof(ResolveHostnameReply, addresses), JSON_MANDATORY },
+        { "name",      JSON_VARIANT_STRING,        json_dispatch_string,  offsetof(ResolveHostnameReply, name),      0              },
+        { "flags",     _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,  offsetof(ResolveHostnameReply, flags),     0              },
         {}
 };
 
@@ -144,13 +156,12 @@ typedef struct AddressParameters {
 } AddressParameters;
 
 static int json_dispatch_address(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
-        AddressParameters *p = userdata;
+        AddressParameters *p = ASSERT_PTR(userdata);
         union in_addr_union buf = {};
         JsonVariant *i;
         size_t n, k = 0;
 
         assert(variant);
-        assert(p);
 
         if (!json_variant_is_array(variant))
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
@@ -160,14 +171,16 @@ static int json_dispatch_address(const char *name, JsonVariant *variant, JsonDis
                 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name));
 
         JSON_VARIANT_ARRAY_FOREACH(i, variant) {
-                intmax_t b;
+                int64_t b;
 
                 if (!json_variant_is_integer(i))
                         return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an integer.", k, strna(name));
 
                 b = json_variant_integer(i);
                 if (b < 0 || b > 0xff)
-                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is out of range 0…255.", k, strna(name));
+                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
+                                        "Element %zu of JSON field '%s' is out of range 0%s255.",
+                                        k, strna(name), special_glyph(SPECIAL_GLYPH_ELLIPSIS));
 
                 buf.bytes[k++] = (uint8_t) b;
         }
@@ -185,19 +198,30 @@ static const JsonDispatch address_parameters_dispatch_table[] = {
         {}
 };
 
-static uint64_t query_flags(void) {
-        uint64_t f = 0;
-        int r;
+static uint64_t query_flag(
+                const char *name,
+                const int value,
+                uint64_t flag) {
 
-        /* Allow callers to turn off validation, when we resolve via nss-resolve */
+        int r;
 
-        r = getenv_bool_secure("SYSTEMD_NSS_RESOLVE_VALIDATE");
-        if (r < 0 && r != -ENXIO)
-                log_debug_errno(r, "Failed to parse $SYSTEMD_NSS_RESOLVE_VALIDATE value, ignoring.");
-        else if (r == 0)
-                f |= SD_RESOLVED_NO_VALIDATE;
+        r = secure_getenv_bool(name);
+        if (r >= 0)
+                return r == value ? flag : 0;
+        if (r != -ENXIO)
+                log_debug_errno(r, "Failed to parse $%s, ignoring.", name);
+        return 0;
+}
 
-        return f;
+static uint64_t query_flags(void) {
+        /* Allow callers to turn off validation, synthetization, caching, etc., when we resolve via
+         * nss-resolve. */
+        return  query_flag("SYSTEMD_NSS_RESOLVE_VALIDATE", 0, SD_RESOLVED_NO_VALIDATE) |
+                query_flag("SYSTEMD_NSS_RESOLVE_SYNTHESIZE", 0, SD_RESOLVED_NO_SYNTHESIZE) |
+                query_flag("SYSTEMD_NSS_RESOLVE_CACHE", 0, SD_RESOLVED_NO_CACHE) |
+                query_flag("SYSTEMD_NSS_RESOLVE_ZONE", 0, SD_RESOLVED_NO_ZONE) |
+                query_flag("SYSTEMD_NSS_RESOLVE_TRUST_ANCHOR", 0, SD_RESOLVED_NO_TRUST_ANCHOR) |
+                query_flag("SYSTEMD_NSS_RESOLVE_NETWORK", 0, SD_RESOLVED_NO_NETWORK);
 }
 
 enum nss_status _nss_resolve_gethostbyname4_r(
@@ -207,14 +231,10 @@ enum nss_status _nss_resolve_gethostbyname4_r(
                 int *errnop, int *h_errnop,
                 int32_t *ttlp) {
 
-        _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
-        _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
-        struct gaih_addrtuple *r_tuple = NULL, *r_tuple_first = NULL;
         _cleanup_(varlink_unrefp) Varlink *link = NULL;
-        const char *canonical = NULL, *error_id = NULL;
-        JsonVariant *entry, *rparams;
-        size_t l, ms, idx, c = 0;
-        char *r_name;
+        _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
+        _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
+        JsonVariant *rparams, *entry;
         int r;
 
         PROTECT_ERRNO;
@@ -241,25 +261,31 @@ enum nss_status _nss_resolve_gethostbyname4_r(
          * DNSSEC errors and suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf
          * configuration can distinguish such executed but negative replies from complete failure to
          * talk to resolved). */
-        r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
+        const char *error_id;
+        r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id);
         if (r < 0)
                 goto fail;
         if (!isempty(error_id)) {
-                if (!error_shall_fallback(error_id))
-                        goto not_found;
-                goto fail;
+                if (error_shall_try_again(error_id))
+                        goto try_again;
+                if (error_shall_fallback(error_id))
+                        goto fail;
+                if (streq(error_id, "io.systemd.Resolve.NoSuchResourceRecord"))
+                        goto no_data;
+                goto not_found;
         }
 
-        r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, json_dispatch_flags, &p);
+        r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, json_dispatch_flags, &p);
         if (r < 0)
                 goto fail;
         if (json_variant_is_blank_object(p.addresses))
                 goto not_found;
 
+        size_t n_addresses = 0;
         JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
                 AddressParameters q = {};
 
-                r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
+                r = json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q);
                 if (r < 0)
                         goto fail;
 
@@ -271,13 +297,13 @@ enum nss_status _nss_resolve_gethostbyname4_r(
                         goto fail;
                 }
 
-                c++;
+                n_addresses++;
         }
 
-        canonical = p.name ?: name;
+        const char *canonical = p.name ?: name;
+        size_t l = strlen(canonical);
+        size_t idx, ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * n_addresses;
 
-        l = strlen(canonical);
-        ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
         if (buflen < ms) {
                 UNPROTECT_ERRNO;
                 *errnop = ERANGE;
@@ -286,17 +312,18 @@ enum nss_status _nss_resolve_gethostbyname4_r(
         }
 
         /* First, append name */
-        r_name = buffer;
-        memcpy(r_name, canonical, l+1);
-        idx = ALIGN(l+1);
+        char *r_name = buffer;
+        memcpy(r_name, canonical, l + 1);
+        idx = ALIGN(l + 1);
 
         /* Second, append addresses */
-        r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
+        struct gaih_addrtuple *r_tuple = NULL,
+                *r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
 
         JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
                 AddressParameters q = {};
 
-                r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
+                r = json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q);
                 if (r < 0)
                         goto fail;
 
@@ -313,7 +340,7 @@ enum nss_status _nss_resolve_gethostbyname4_r(
                 idx += ALIGN(sizeof(struct gaih_addrtuple));
         }
 
-        assert(r_tuple);
+        assert(r_tuple);  /* We had at least one address, so r_tuple must be set */
         r_tuple->next = NULL;  /* Override last next pointer */
 
         assert(idx == ms);
@@ -342,6 +369,16 @@ fail:
 not_found:
         *h_errnop = HOST_NOT_FOUND;
         return NSS_STATUS_NOTFOUND;
+
+no_data:
+        *h_errnop = NO_DATA;
+        return NSS_STATUS_NOTFOUND;
+
+try_again:
+        UNPROTECT_ERRNO;
+        *errnop = -r;
+        *h_errnop = TRY_AGAIN;
+        return NSS_STATUS_TRYAGAIN;
 }
 
 enum nss_status _nss_resolve_gethostbyname3_r(
@@ -353,13 +390,10 @@ enum nss_status _nss_resolve_gethostbyname3_r(
                 int32_t *ttlp,
                 char **canonp) {
 
-        _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
-        _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
-        char *r_name, *r_aliases, *r_addr, *r_addr_list;
         _cleanup_(varlink_unrefp) Varlink *link = NULL;
-        const char *canonical, *error_id = NULL;
-        size_t l, idx, ms, alen, i = 0, c = 0;
-        JsonVariant *entry, *rparams;
+        _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
+        _cleanup_(resolve_hostname_reply_destroy) ResolveHostnameReply p = {};
+        JsonVariant *rparams, *entry;
         int r;
 
         PROTECT_ERRNO;
@@ -389,25 +423,31 @@ enum nss_status _nss_resolve_gethostbyname3_r(
         if (r < 0)
                 goto fail;
 
-        r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id, NULL);
+        const char *error_id;
+        r = varlink_call(link, "io.systemd.Resolve.ResolveHostname", cparams, &rparams, &error_id);
         if (r < 0)
                 goto fail;
         if (!isempty(error_id)) {
-                if (!error_shall_fallback(error_id))
-                        goto not_found;
-                goto fail;
+                if (error_shall_try_again(error_id))
+                        goto try_again;
+                if (error_shall_fallback(error_id))
+                        goto fail;
+                if (streq(error_id, "io.systemd.Resolve.NoSuchResourceRecord"))
+                        goto no_data;
+                goto not_found;
         }
 
-        r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, NULL, json_dispatch_flags, &p);
+        r = json_dispatch(rparams, resolve_hostname_reply_dispatch_table, json_dispatch_flags, &p);
         if (r < 0)
                 goto fail;
         if (json_variant_is_blank_object(p.addresses))
                 goto not_found;
 
+        size_t n_addresses = 0;
         JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
                 AddressParameters q = {};
 
-                r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
+                r = json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q);
                 if (r < 0)
                         goto fail;
 
@@ -419,15 +459,15 @@ enum nss_status _nss_resolve_gethostbyname3_r(
                         goto fail;
                 }
 
-                c++;
+                n_addresses++;
         }
 
-        canonical = p.name ?: name;
+        const char *canonical = p.name ?: name;
 
-        alen = FAMILY_ADDRESS_SIZE(af);
-        l = strlen(canonical);
+        size_t alen = FAMILY_ADDRESS_SIZE(af);
+        size_t l = strlen(canonical);
 
-        ms = ALIGN(l+1) + c*ALIGN(alen) + (c+2) * sizeof(char*);
+        size_t idx, ms = ALIGN(l + 1) + n_addresses * ALIGN(alen) + (n_addresses + 2) * sizeof(char*);
 
         if (buflen < ms) {
                 UNPROTECT_ERRNO;
@@ -437,22 +477,23 @@ enum nss_status _nss_resolve_gethostbyname3_r(
         }
 
         /* First, append name */
-        r_name = buffer;
+        char *r_name = buffer;
         memcpy(r_name, canonical, l+1);
         idx = ALIGN(l+1);
 
         /* Second, create empty aliases array */
-        r_aliases = buffer + idx;
+        char *r_aliases = buffer + idx;
         ((char**) r_aliases)[0] = NULL;
         idx += sizeof(char*);
 
         /* Third, append addresses */
-        r_addr = buffer + idx;
+        char *r_addr = buffer + idx;
 
+        size_t i = 0;
         JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) {
                 AddressParameters q = {};
 
-                r = json_dispatch(entry, address_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
+                r = json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q);
                 if (r < 0)
                         goto fail;
 
@@ -468,16 +509,16 @@ enum nss_status _nss_resolve_gethostbyname3_r(
                 i++;
         }
 
-        assert(i == c);
-        idx += c * ALIGN(alen);
+        assert(i == n_addresses);
+        idx += n_addresses * ALIGN(alen);
 
         /* Fourth, append address pointer array */
-        r_addr_list = buffer + idx;
-        for (i = 0; i < c; i++)
+        char *r_addr_list = buffer + idx;
+        for (i = 0; i < n_addresses; i++)
                 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
 
         ((char**) r_addr_list)[i] = NULL;
-        idx += (c+1) * sizeof(char*);
+        idx += (n_addresses + 1) * sizeof(char*);
 
         assert(idx == ms);
 
@@ -509,6 +550,16 @@ fail:
 not_found:
         *h_errnop = HOST_NOT_FOUND;
         return NSS_STATUS_NOTFOUND;
+
+no_data:
+        *h_errnop = NO_DATA;
+        return NSS_STATUS_NOTFOUND;
+
+try_again:
+        UNPROTECT_ERRNO;
+        *errnop = -r;
+        *h_errnop = TRY_AGAIN;
+        return NSS_STATUS_TRYAGAIN;
 }
 
 typedef struct ResolveAddressReply {
@@ -523,8 +574,8 @@ static void resolve_address_reply_destroy(ResolveAddressReply *p) {
 }
 
 static const JsonDispatch resolve_address_reply_dispatch_table[] = {
-        { "names", JSON_VARIANT_ARRAY,    json_dispatch_variant, offsetof(ResolveAddressReply, names), JSON_MANDATORY },
-        { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64,  offsetof(ResolveAddressReply, flags), 0              },
+        { "names", JSON_VARIANT_ARRAY,         json_dispatch_variant, offsetof(ResolveAddressReply, names), JSON_MANDATORY },
+        { "flags", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64,  offsetof(ResolveAddressReply, flags), 0              },
         {}
 };
 
@@ -540,8 +591,8 @@ static void name_parameters_destroy(NameParameters *p) {
 }
 
 static const JsonDispatch name_parameters_dispatch_table[] = {
-        { "ifindex", JSON_VARIANT_INTEGER,  json_dispatch_ifindex, offsetof(NameParameters, ifindex), 0              },
-        { "name",    JSON_VARIANT_UNSIGNED, json_dispatch_string,  offsetof(NameParameters, name),    JSON_MANDATORY },
+        { "ifindex", JSON_VARIANT_INTEGER, json_dispatch_ifindex, offsetof(NameParameters, ifindex), 0              },
+        { "name",    JSON_VARIANT_STRING,  json_dispatch_string,  offsetof(NameParameters, name),    JSON_MANDATORY },
         {}
 };
 
@@ -553,14 +604,10 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
                 int *errnop, int *h_errnop,
                 int32_t *ttlp) {
 
-        _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {};
-        _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
-        char *r_name, *r_aliases, *r_addr, *r_addr_list;
         _cleanup_(varlink_unrefp) Varlink *link = NULL;
-        JsonVariant *entry, *rparams;
-        const char *n, *error_id;
-        unsigned c = 0, i = 0;
-        size_t ms = 0, idx;
+        _cleanup_(json_variant_unrefp) JsonVariant *cparams = NULL;
+        _cleanup_(resolve_address_reply_destroy) ResolveAddressReply p = {};
+        JsonVariant *rparams, *entry;
         int r;
 
         PROTECT_ERRNO;
@@ -594,34 +641,40 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
         if (r < 0)
                 goto fail;
 
-        r = varlink_call(link, "io.systemd.Resolve.ResolveAddress", cparams, &rparams, &error_id, NULL);
+        const char* error_id;
+        r = varlink_call(link, "io.systemd.Resolve.ResolveAddress", cparams, &rparams, &error_id);
         if (r < 0)
                 goto fail;
         if (!isempty(error_id)) {
-                if (!error_shall_fallback(error_id))
-                        goto not_found;
-                goto fail;
+                if (error_shall_try_again(error_id))
+                        goto try_again;
+                if (error_shall_fallback(error_id))
+                        goto fail;
+                goto not_found;
         }
 
-        r = json_dispatch(rparams, resolve_address_reply_dispatch_table, NULL, json_dispatch_flags, &p);
+        r = json_dispatch(rparams, resolve_address_reply_dispatch_table, json_dispatch_flags, &p);
         if (r < 0)
                 goto fail;
         if (json_variant_is_blank_object(p.names))
                 goto not_found;
 
+        size_t ms = 0, idx;
+
         JSON_VARIANT_ARRAY_FOREACH(entry, p.names) {
                 _cleanup_(name_parameters_destroy) NameParameters q = {};
 
-                r = json_dispatch(entry, name_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
+                r = json_dispatch(entry, name_parameters_dispatch_table, json_dispatch_flags, &q);
                 if (r < 0)
                         goto fail;
 
                 ms += ALIGN(strlen(q.name) + 1);
         }
 
-        ms += ALIGN(len) +                                           /* the address */
-              2 * sizeof(char*) +                                    /* pointers to the address, plus trailing NULL */
-              json_variant_elements(p.names) * sizeof(char*);        /* pointers to aliases, plus trailing NULL */
+        size_t n_names = json_variant_elements(p.names);
+        ms += ALIGN(len) +                    /* the address */
+              2 * sizeof(char*) +             /* pointer to the address, plus trailing NULL */
+              n_names * sizeof(char*);        /* pointers to aliases, plus trailing NULL */
 
         if (buflen < ms) {
                 UNPROTECT_ERRNO;
@@ -631,44 +684,43 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
         }
 
         /* First, place address */
-        r_addr = buffer;
+        char *r_addr = buffer;
         memcpy(r_addr, addr, len);
         idx = ALIGN(len);
 
         /* Second, place address list */
-        r_addr_list = buffer + idx;
+        char *r_addr_list = buffer + idx;
         ((char**) r_addr_list)[0] = r_addr;
         ((char**) r_addr_list)[1] = NULL;
         idx += sizeof(char*) * 2;
 
-        /* Third, reserve space for the aliases array */
-        r_aliases = buffer + idx;
-        idx += sizeof(char*) * c;
+        /* Third, reserve space for the aliases array, plus trailing NULL */
+        char *r_aliases = buffer + idx;
+        idx += sizeof(char*) * n_names;
 
         /* Fourth, place aliases */
-        i = 0;
-        r_name = buffer + idx;
+        char *r_name = buffer + idx;
+
+        size_t i = 0;
         JSON_VARIANT_ARRAY_FOREACH(entry, p.names) {
                 _cleanup_(name_parameters_destroy) NameParameters q = {};
-                size_t l;
-                char *z;
 
-                r = json_dispatch(entry, name_parameters_dispatch_table, NULL, json_dispatch_flags, &q);
+                r = json_dispatch(entry, name_parameters_dispatch_table, json_dispatch_flags, &q);
                 if (r < 0)
                         goto fail;
 
-                l = strlen(q.name);
-                z = buffer + idx;
-                memcpy(z, n, l+1);
+                size_t l = strlen(q.name);
+                char *z = buffer + idx;
+                memcpy(z, q.name, l + 1);
 
                 if (i > 0)
-                        ((char**) r_aliases)[i-1] = z;
+                        ((char**) r_aliases)[i - 1] = z;
                 i++;
 
-                idx += ALIGN(l+1);
+                idx += ALIGN(l + 1);
         }
+        ((char**) r_aliases)[n_names - 1] = NULL;
 
-        ((char**) r_aliases)[c-1] = NULL;
         assert(idx == ms);
 
         result->h_name = r_name;
@@ -696,6 +748,12 @@ fail:
 not_found:
         *h_errnop = HOST_NOT_FOUND;
         return NSS_STATUS_NOTFOUND;
+
+try_again:
+        UNPROTECT_ERRNO;
+        *errnop = -r;
+        *h_errnop = TRY_AGAIN;
+        return NSS_STATUS_TRYAGAIN;
 }
 
 NSS_GETHOSTBYNAME_FALLBACKS(resolve);