162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Server address list management 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/ctype.h> 1062306a36Sopenharmony_ci#include <linux/dns_resolver.h> 1162306a36Sopenharmony_ci#include <linux/inet.h> 1262306a36Sopenharmony_ci#include <keys/rxrpc-type.h> 1362306a36Sopenharmony_ci#include "internal.h" 1462306a36Sopenharmony_ci#include "afs_fs.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * Release an address list. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_civoid afs_put_addrlist(struct afs_addr_list *alist) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci if (alist && refcount_dec_and_test(&alist->usage)) 2262306a36Sopenharmony_ci kfree_rcu(alist, rcu); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Allocate an address list. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistruct afs_addr_list *afs_alloc_addrlist(unsigned int nr, 2962306a36Sopenharmony_ci unsigned short service, 3062306a36Sopenharmony_ci unsigned short port) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct afs_addr_list *alist; 3362306a36Sopenharmony_ci unsigned int i; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci _enter("%u,%u,%u", nr, service, port); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (nr > AFS_MAX_ADDRESSES) 3862306a36Sopenharmony_ci nr = AFS_MAX_ADDRESSES; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci alist = kzalloc(struct_size(alist, addrs, nr), GFP_KERNEL); 4162306a36Sopenharmony_ci if (!alist) 4262306a36Sopenharmony_ci return NULL; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci refcount_set(&alist->usage, 1); 4562306a36Sopenharmony_ci alist->max_addrs = nr; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci for (i = 0; i < nr; i++) { 4862306a36Sopenharmony_ci struct sockaddr_rxrpc *srx = &alist->addrs[i]; 4962306a36Sopenharmony_ci srx->srx_family = AF_RXRPC; 5062306a36Sopenharmony_ci srx->srx_service = service; 5162306a36Sopenharmony_ci srx->transport_type = SOCK_DGRAM; 5262306a36Sopenharmony_ci srx->transport_len = sizeof(srx->transport.sin6); 5362306a36Sopenharmony_ci srx->transport.sin6.sin6_family = AF_INET6; 5462306a36Sopenharmony_ci srx->transport.sin6.sin6_port = htons(port); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return alist; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * Parse a text string consisting of delimited addresses. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistruct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *net, 6462306a36Sopenharmony_ci const char *text, size_t len, 6562306a36Sopenharmony_ci char delim, 6662306a36Sopenharmony_ci unsigned short service, 6762306a36Sopenharmony_ci unsigned short port) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct afs_vlserver_list *vllist; 7062306a36Sopenharmony_ci struct afs_addr_list *alist; 7162306a36Sopenharmony_ci const char *p, *end = text + len; 7262306a36Sopenharmony_ci const char *problem; 7362306a36Sopenharmony_ci unsigned int nr = 0; 7462306a36Sopenharmony_ci int ret = -ENOMEM; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci _enter("%*.*s,%c", (int)len, (int)len, text, delim); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!len) { 7962306a36Sopenharmony_ci _leave(" = -EDESTADDRREQ [empty]"); 8062306a36Sopenharmony_ci return ERR_PTR(-EDESTADDRREQ); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len))) 8462306a36Sopenharmony_ci delim = ','; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Count the addresses */ 8762306a36Sopenharmony_ci p = text; 8862306a36Sopenharmony_ci do { 8962306a36Sopenharmony_ci if (!*p) { 9062306a36Sopenharmony_ci problem = "nul"; 9162306a36Sopenharmony_ci goto inval; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci if (*p == delim) 9462306a36Sopenharmony_ci continue; 9562306a36Sopenharmony_ci nr++; 9662306a36Sopenharmony_ci if (*p == '[') { 9762306a36Sopenharmony_ci p++; 9862306a36Sopenharmony_ci if (p == end) { 9962306a36Sopenharmony_ci problem = "brace1"; 10062306a36Sopenharmony_ci goto inval; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci p = memchr(p, ']', end - p); 10362306a36Sopenharmony_ci if (!p) { 10462306a36Sopenharmony_ci problem = "brace2"; 10562306a36Sopenharmony_ci goto inval; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci p++; 10862306a36Sopenharmony_ci if (p >= end) 10962306a36Sopenharmony_ci break; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci p = memchr(p, delim, end - p); 11362306a36Sopenharmony_ci if (!p) 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci p++; 11662306a36Sopenharmony_ci } while (p < end); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci _debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci vllist = afs_alloc_vlserver_list(1); 12162306a36Sopenharmony_ci if (!vllist) 12262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci vllist->nr_servers = 1; 12562306a36Sopenharmony_ci vllist->servers[0].server = afs_alloc_vlserver("<dummy>", 7, AFS_VL_PORT); 12662306a36Sopenharmony_ci if (!vllist->servers[0].server) 12762306a36Sopenharmony_ci goto error_vl; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci alist = afs_alloc_addrlist(nr, service, AFS_VL_PORT); 13062306a36Sopenharmony_ci if (!alist) 13162306a36Sopenharmony_ci goto error; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Extract the addresses */ 13462306a36Sopenharmony_ci p = text; 13562306a36Sopenharmony_ci do { 13662306a36Sopenharmony_ci const char *q, *stop; 13762306a36Sopenharmony_ci unsigned int xport = port; 13862306a36Sopenharmony_ci __be32 x[4]; 13962306a36Sopenharmony_ci int family; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (*p == delim) { 14262306a36Sopenharmony_ci p++; 14362306a36Sopenharmony_ci continue; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (*p == '[') { 14762306a36Sopenharmony_ci p++; 14862306a36Sopenharmony_ci q = memchr(p, ']', end - p); 14962306a36Sopenharmony_ci } else { 15062306a36Sopenharmony_ci for (q = p; q < end; q++) 15162306a36Sopenharmony_ci if (*q == '+' || *q == delim) 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (in4_pton(p, q - p, (u8 *)&x[0], -1, &stop)) { 15662306a36Sopenharmony_ci family = AF_INET; 15762306a36Sopenharmony_ci } else if (in6_pton(p, q - p, (u8 *)x, -1, &stop)) { 15862306a36Sopenharmony_ci family = AF_INET6; 15962306a36Sopenharmony_ci } else { 16062306a36Sopenharmony_ci problem = "family"; 16162306a36Sopenharmony_ci goto bad_address; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci p = q; 16562306a36Sopenharmony_ci if (stop != p) { 16662306a36Sopenharmony_ci problem = "nostop"; 16762306a36Sopenharmony_ci goto bad_address; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (q < end && *q == ']') 17162306a36Sopenharmony_ci p++; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (p < end) { 17462306a36Sopenharmony_ci if (*p == '+') { 17562306a36Sopenharmony_ci /* Port number specification "+1234" */ 17662306a36Sopenharmony_ci xport = 0; 17762306a36Sopenharmony_ci p++; 17862306a36Sopenharmony_ci if (p >= end || !isdigit(*p)) { 17962306a36Sopenharmony_ci problem = "port"; 18062306a36Sopenharmony_ci goto bad_address; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci do { 18362306a36Sopenharmony_ci xport *= 10; 18462306a36Sopenharmony_ci xport += *p - '0'; 18562306a36Sopenharmony_ci if (xport > 65535) { 18662306a36Sopenharmony_ci problem = "pval"; 18762306a36Sopenharmony_ci goto bad_address; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci p++; 19062306a36Sopenharmony_ci } while (p < end && isdigit(*p)); 19162306a36Sopenharmony_ci } else if (*p == delim) { 19262306a36Sopenharmony_ci p++; 19362306a36Sopenharmony_ci } else { 19462306a36Sopenharmony_ci problem = "weird"; 19562306a36Sopenharmony_ci goto bad_address; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (family == AF_INET) 20062306a36Sopenharmony_ci afs_merge_fs_addr4(alist, x[0], xport); 20162306a36Sopenharmony_ci else 20262306a36Sopenharmony_ci afs_merge_fs_addr6(alist, x, xport); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci } while (p < end); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci rcu_assign_pointer(vllist->servers[0].server->addresses, alist); 20762306a36Sopenharmony_ci _leave(" = [nr %u]", alist->nr_addrs); 20862306a36Sopenharmony_ci return vllist; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciinval: 21162306a36Sopenharmony_ci _leave(" = -EINVAL [%s %zu %*.*s]", 21262306a36Sopenharmony_ci problem, p - text, (int)len, (int)len, text); 21362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 21462306a36Sopenharmony_cibad_address: 21562306a36Sopenharmony_ci _leave(" = -EINVAL [%s %zu %*.*s]", 21662306a36Sopenharmony_ci problem, p - text, (int)len, (int)len, text); 21762306a36Sopenharmony_ci ret = -EINVAL; 21862306a36Sopenharmony_cierror: 21962306a36Sopenharmony_ci afs_put_addrlist(alist); 22062306a36Sopenharmony_cierror_vl: 22162306a36Sopenharmony_ci afs_put_vlserverlist(net, vllist); 22262306a36Sopenharmony_ci return ERR_PTR(ret); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * Compare old and new address lists to see if there's been any change. 22762306a36Sopenharmony_ci * - How to do this in better than O(Nlog(N)) time? 22862306a36Sopenharmony_ci * - We don't really want to sort the address list, but would rather take the 22962306a36Sopenharmony_ci * list as we got it so as not to undo record rotation by the DNS server. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci#if 0 23262306a36Sopenharmony_cistatic int afs_cmp_addr_list(const struct afs_addr_list *a1, 23362306a36Sopenharmony_ci const struct afs_addr_list *a2) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci#endif 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * Perform a DNS query for VL servers and build a up an address list. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_cistruct afs_vlserver_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct afs_vlserver_list *vllist; 24462306a36Sopenharmony_ci char *result = NULL; 24562306a36Sopenharmony_ci int ret; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci _enter("%s", cell->name); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = dns_query(cell->net->net, "afsdb", cell->name, cell->name_len, 25062306a36Sopenharmony_ci "srv=1", &result, _expiry, true); 25162306a36Sopenharmony_ci if (ret < 0) { 25262306a36Sopenharmony_ci _leave(" = %d [dns]", ret); 25362306a36Sopenharmony_ci return ERR_PTR(ret); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (*_expiry == 0) 25762306a36Sopenharmony_ci *_expiry = ktime_get_real_seconds() + 60; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (ret > 1 && result[0] == 0) 26062306a36Sopenharmony_ci vllist = afs_extract_vlserver_list(cell, result, ret); 26162306a36Sopenharmony_ci else 26262306a36Sopenharmony_ci vllist = afs_parse_text_addrs(cell->net, result, ret, ',', 26362306a36Sopenharmony_ci VL_SERVICE, AFS_VL_PORT); 26462306a36Sopenharmony_ci kfree(result); 26562306a36Sopenharmony_ci if (IS_ERR(vllist) && vllist != ERR_PTR(-ENOMEM)) 26662306a36Sopenharmony_ci pr_err("Failed to parse DNS data %ld\n", PTR_ERR(vllist)); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return vllist; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * Merge an IPv4 entry into a fileserver address list. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_civoid afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct sockaddr_rxrpc *srx; 27762306a36Sopenharmony_ci u32 addr = ntohl(xdr); 27862306a36Sopenharmony_ci int i; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (alist->nr_addrs >= alist->max_addrs) 28162306a36Sopenharmony_ci return; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci for (i = 0; i < alist->nr_ipv4; i++) { 28462306a36Sopenharmony_ci struct sockaddr_in *a = &alist->addrs[i].transport.sin; 28562306a36Sopenharmony_ci u32 a_addr = ntohl(a->sin_addr.s_addr); 28662306a36Sopenharmony_ci u16 a_port = ntohs(a->sin_port); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (addr == a_addr && port == a_port) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci if (addr == a_addr && port < a_port) 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci if (addr < a_addr) 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (i < alist->nr_addrs) 29762306a36Sopenharmony_ci memmove(alist->addrs + i + 1, 29862306a36Sopenharmony_ci alist->addrs + i, 29962306a36Sopenharmony_ci sizeof(alist->addrs[0]) * (alist->nr_addrs - i)); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci srx = &alist->addrs[i]; 30262306a36Sopenharmony_ci srx->srx_family = AF_RXRPC; 30362306a36Sopenharmony_ci srx->transport_type = SOCK_DGRAM; 30462306a36Sopenharmony_ci srx->transport_len = sizeof(srx->transport.sin); 30562306a36Sopenharmony_ci srx->transport.sin.sin_family = AF_INET; 30662306a36Sopenharmony_ci srx->transport.sin.sin_port = htons(port); 30762306a36Sopenharmony_ci srx->transport.sin.sin_addr.s_addr = xdr; 30862306a36Sopenharmony_ci alist->nr_ipv4++; 30962306a36Sopenharmony_ci alist->nr_addrs++; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* 31362306a36Sopenharmony_ci * Merge an IPv6 entry into a fileserver address list. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_civoid afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct sockaddr_rxrpc *srx; 31862306a36Sopenharmony_ci int i, diff; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (alist->nr_addrs >= alist->max_addrs) 32162306a36Sopenharmony_ci return; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) { 32462306a36Sopenharmony_ci struct sockaddr_in6 *a = &alist->addrs[i].transport.sin6; 32562306a36Sopenharmony_ci u16 a_port = ntohs(a->sin6_port); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci diff = memcmp(xdr, &a->sin6_addr, 16); 32862306a36Sopenharmony_ci if (diff == 0 && port == a_port) 32962306a36Sopenharmony_ci return; 33062306a36Sopenharmony_ci if (diff == 0 && port < a_port) 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci if (diff < 0) 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (i < alist->nr_addrs) 33762306a36Sopenharmony_ci memmove(alist->addrs + i + 1, 33862306a36Sopenharmony_ci alist->addrs + i, 33962306a36Sopenharmony_ci sizeof(alist->addrs[0]) * (alist->nr_addrs - i)); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci srx = &alist->addrs[i]; 34262306a36Sopenharmony_ci srx->srx_family = AF_RXRPC; 34362306a36Sopenharmony_ci srx->transport_type = SOCK_DGRAM; 34462306a36Sopenharmony_ci srx->transport_len = sizeof(srx->transport.sin6); 34562306a36Sopenharmony_ci srx->transport.sin6.sin6_family = AF_INET6; 34662306a36Sopenharmony_ci srx->transport.sin6.sin6_port = htons(port); 34762306a36Sopenharmony_ci memcpy(&srx->transport.sin6.sin6_addr, xdr, 16); 34862306a36Sopenharmony_ci alist->nr_addrs++; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* 35262306a36Sopenharmony_ci * Get an address to try. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cibool afs_iterate_addresses(struct afs_addr_cursor *ac) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci unsigned long set, failed; 35762306a36Sopenharmony_ci int index; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!ac->alist) 36062306a36Sopenharmony_ci return false; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci set = ac->alist->responded; 36362306a36Sopenharmony_ci failed = ac->alist->failed; 36462306a36Sopenharmony_ci _enter("%lx-%lx-%lx,%d", set, failed, ac->tried, ac->index); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci ac->nr_iterations++; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci set &= ~(failed | ac->tried); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!set) 37162306a36Sopenharmony_ci return false; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci index = READ_ONCE(ac->alist->preferred); 37462306a36Sopenharmony_ci if (test_bit(index, &set)) 37562306a36Sopenharmony_ci goto selected; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci index = __ffs(set); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ciselected: 38062306a36Sopenharmony_ci ac->index = index; 38162306a36Sopenharmony_ci set_bit(index, &ac->tried); 38262306a36Sopenharmony_ci ac->responded = false; 38362306a36Sopenharmony_ci return true; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* 38762306a36Sopenharmony_ci * Release an address list cursor. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ciint afs_end_cursor(struct afs_addr_cursor *ac) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci struct afs_addr_list *alist; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci alist = ac->alist; 39462306a36Sopenharmony_ci if (alist) { 39562306a36Sopenharmony_ci if (ac->responded && 39662306a36Sopenharmony_ci ac->index != alist->preferred && 39762306a36Sopenharmony_ci test_bit(ac->alist->preferred, &ac->tried)) 39862306a36Sopenharmony_ci WRITE_ONCE(alist->preferred, ac->index); 39962306a36Sopenharmony_ci afs_put_addrlist(alist); 40062306a36Sopenharmony_ci ac->alist = NULL; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return ac->error; 40462306a36Sopenharmony_ci} 405