autofs-5.0.4 - ipv6 name and address support From: Ian Kent For ipv6 we need to convert all ipv4 specific function calls to ipv6 compatible function calls and update the rpc code to deal with ipv6 addresses. The host proximity calculation also needes to be updated. I'm sure this isn't completely correct yet and it will need more work as I become more familiar with how ipv6 and subneting with it is used. The changes so far function correctly for the current autofs connectathon test suite maps so I hope there aren't any regressions with current map configurations. --- include/replicated.h | 2 include/rpc_subs.h | 4 lib/rpc_subs.c | 484 ++++++++++++++++++++++++++++++++------------------ modules/cyrus-sasl.c | 17 +- modules/replicated.c | 235 +++++++++++++++++------- 5 files changed, 499 insertions(+), 243 deletions(-) diff --git a/include/replicated.h b/include/replicated.h index 88cd08a..e0133ff 100644 --- a/include/replicated.h +++ b/include/replicated.h @@ -51,7 +51,7 @@ struct host { char *name; - char *addr; + struct sockaddr *addr; size_t addr_len; char *path; unsigned int version; diff --git a/include/rpc_subs.h b/include/rpc_subs.h index e20a89d..87fd568 100644 --- a/include/rpc_subs.h +++ b/include/rpc_subs.h @@ -46,7 +46,7 @@ struct conn_info { const char *host; - const char *addr; + struct sockaddr *addr; size_t addr_len; unsigned short port; unsigned long program; @@ -63,7 +63,7 @@ int rpc_udp_getclient(struct conn_info *, unsigned int, unsigned int); void rpc_destroy_udp_client(struct conn_info *); int rpc_tcp_getclient(struct conn_info *, unsigned int, unsigned int); void rpc_destroy_tcp_client(struct conn_info *); -int rpc_portmap_getclient(struct conn_info *, const char *, const char *, size_t, const char *, unsigned int); +int rpc_portmap_getclient(struct conn_info *, const char *, struct sockaddr *, size_t, const char *, unsigned int); unsigned short rpc_portmap_getport(struct conn_info *, struct pmap *); int rpc_ping_proto(struct conn_info *); int rpc_ping(const char *, long, long, unsigned int); diff --git a/lib/rpc_subs.c b/lib/rpc_subs.c index 7b347a7..a822f1f 100644 --- a/lib/rpc_subs.c +++ b/lib/rpc_subs.c @@ -17,10 +17,11 @@ #define _GNU_SOURCE #endif +#include "config.h" + #include #include #include - #include #include #include @@ -54,145 +55,76 @@ inline void dump_core(void); -/* - * Create a UDP RPC client - */ -static CLIENT *create_udp_client(struct conn_info *info) +static CLIENT *rpc_clntudp_create(struct sockaddr *addr, struct conn_info *info, int *fd) { - int fd, ret, ghn_errno; - CLIENT *client; - struct sockaddr_in laddr, raddr; - struct hostent hp; - struct hostent *php = &hp; - struct hostent *result; - char buf[HOST_ENT_BUF_SIZE]; - size_t len; - - if (info->proto->p_proto != IPPROTO_UDP) - return NULL; - - if (info->client) { - if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) { - fd = -1; - clnt_destroy(info->client); - info->client = NULL; - } else { - clnt_control(info->client, CLSET_FD_NCLOSE, NULL); - clnt_destroy(info->client); - } - } - - memset(&laddr, 0, sizeof(laddr)); - memset(&raddr, 0, sizeof(raddr)); - - raddr.sin_family = AF_INET; - if (info->addr) { - memcpy(&raddr.sin_addr.s_addr, info->addr, info->addr_len); - goto got_addr; - } - - if (inet_aton(info->host, &raddr.sin_addr)) - goto got_addr; - - memset(&hp, 0, sizeof(struct hostent)); - - ret = gethostbyname_r(info->host, php, - buf, HOST_ENT_BUF_SIZE, &result, &ghn_errno); - if (ret || !result) { - int err = ghn_errno == -1 ? errno : ghn_errno; - char *estr = strerror_r(err, buf, HOST_ENT_BUF_SIZE); - logerr("hostname lookup failed: %s", estr); - goto out_close; - } - memcpy(&raddr.sin_addr.s_addr, php->h_addr, php->h_length); - -got_addr: - raddr.sin_port = htons(info->port); - - if (!info->client) { - /* - * bind to any unused port. If we left this up to the rpc - * layer, it would bind to a reserved port, which has been shown - * to exhaust the reserved port range in some situations. - */ - fd = open_sock(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) - return NULL; - - laddr.sin_family = AF_INET; - laddr.sin_port = 0; - laddr.sin_addr.s_addr = htonl(INADDR_ANY); - - len = sizeof(struct sockaddr_in); - if (bind(fd, (struct sockaddr *)&laddr, len) < 0) { - close(fd); - fd = RPC_ANYSOCK; - /* FALLTHROUGH */ - } - } - - client = clntudp_bufcreate(&raddr, - info->program, info->version, - info->timeout, &fd, - info->send_sz, info->recv_sz); + struct sockaddr_in *in4_raddr; + struct sockaddr_in6 *in6_raddr; + CLIENT *client = NULL; + + switch (addr->sa_family) { + case AF_INET: + in4_raddr = (struct sockaddr_in *) addr; + in4_raddr->sin_port = htons(info->port); + client = clntudp_bufcreate(in4_raddr, + info->program, info->version, + info->timeout, fd, + info->send_sz, info->recv_sz); + break; - if (!client) { - info->client = NULL; - goto out_close; - } + case AF_INET6: +#ifndef INET6 + /* Quiet compile warning */ + in6_raddr = NULL; +#else + in6_raddr = (struct sockaddr_in6 *) addr; + in6_raddr->sin6_port = htons(info->port); + client = clntudp6_bufcreate(in6_raddr, + info->program, info->version, + info->timeout, fd, + info->send_sz, info->recv_sz); +#endif + break; - /* Close socket fd on destroy, as is default for rpcowned fds */ - if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) { - clnt_destroy(client); - info->client = NULL; - goto out_close; + default: + break; } return client; - -out_close: - if (fd != -1) - close(fd); - return NULL; } -int rpc_udp_getclient(struct conn_info *info, - unsigned int program, unsigned int version) +static CLIENT *rpc_clnttcp_create(struct sockaddr *addr, struct conn_info *info, int *fd) { - struct protoent *pe_proto; - CLIENT *client; + struct sockaddr_in *in4_raddr; + struct sockaddr_in6 *in6_raddr; + CLIENT *client = NULL; + + switch (addr->sa_family) { + case AF_INET: + in4_raddr = (struct sockaddr_in *) addr; + in4_raddr->sin_port = htons(info->port); + client = clnttcp_create(in4_raddr, + info->program, info->version, fd, + info->send_sz, info->recv_sz); + break; - if (!info->client) { - pe_proto = getprotobyname("udp"); - if (!pe_proto) - return 0; + case AF_INET6: +#ifndef INET6 + /* Quiet compile warning */ + in6_raddr = NULL; +#else + in6_raddr = (struct sockaddr_in6 *) addr; + in6_raddr->sin6_port = htons(info->port); + client = clnttcp6_create(in6_raddr, + info->program, info->version, fd, + info->send_sz, info->recv_sz); +#endif + break; - info->proto = pe_proto; - info->send_sz = UDPMSGSIZE; - info->recv_sz = UDPMSGSIZE; + default: + break; } - info->program = program; - info->version = version; - - client = create_udp_client(info); - - if (!client) - return 0; - - info->client = client; - - return 1; -} - -void rpc_destroy_udp_client(struct conn_info *info) -{ - if (!info->client) - return; - - clnt_destroy(info->client); - info->client = NULL; - return; + return client; } /* @@ -201,12 +133,11 @@ void rpc_destroy_udp_client(struct conn_info *info) * The input struct timeval always has tv_nsec set to zero, * we only ever use tv_sec for timeouts. */ -static int connect_nb(int fd, struct sockaddr_in *addr, struct timeval *tout) +static int connect_nb(int fd, struct sockaddr *addr, socklen_t len, struct timeval *tout) { struct pollfd pfd[1]; int timeout = tout->tv_sec; int flags, ret; - socklen_t len; flags = fcntl(fd, F_GETFL, 0); if (flags < 0) @@ -221,8 +152,7 @@ static int connect_nb(int fd, struct sockaddr_in *addr, struct timeval *tout) * we set ret = -errno to capture it in case we decide to * use it later. */ - len = sizeof(struct sockaddr); - ret = connect(fd, (struct sockaddr *)addr, len); + ret = connect(fd, addr, len); if (ret < 0 && errno != EINPROGRESS) { ret = -errno; goto done; @@ -277,26 +207,241 @@ done: return ret; } +static CLIENT *rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, int *fd) +{ + CLIENT *client = NULL; + struct sockaddr *laddr; + struct sockaddr_in in4_laddr; + struct sockaddr_in6 in6_laddr; + int type, proto; + socklen_t slen; + + proto = info->proto->p_proto; + if (proto == IPPROTO_UDP) + type = SOCK_DGRAM; + else + type = SOCK_STREAM; + + /* + * bind to any unused port. If we left this up to the rpc + * layer, it would bind to a reserved port, which has been shown + * to exhaust the reserved port range in some situations. + */ + switch (addr->sa_family) { + case AF_INET: + in4_laddr.sin_family = AF_INET; + in4_laddr.sin_port = htons(0); + in4_laddr.sin_addr.s_addr = htonl(INADDR_ANY); + slen = sizeof(struct sockaddr_in); + laddr = (struct sockaddr *) &in4_laddr; + break; + + case AF_INET6: +#ifndef INET6 + /* Quiet compiler */ + in6_laddr.sin6_family = AF_INET6; + return NULL; +#else + in6_laddr.sin6_family = AF_INET6; + in6_laddr.sin6_port = htons(0); + in6_laddr.sin6_addr = in6addr_any; + slen = sizeof(struct sockaddr_in6); + laddr = (struct sockaddr *) &in6_laddr; + break; +#endif + default: + return NULL; + } + + if (!info->client) { + *fd = open_sock(addr->sa_family, type, proto); + if (*fd < 0) + return NULL; + + if (bind(*fd, laddr, slen) < 0) { + close(*fd); + return NULL; + } + } + + switch (info->proto->p_proto) { + case IPPROTO_UDP: + if (!info->client) { + *fd = open_sock(addr->sa_family, type, proto); + if (*fd < 0) + return NULL; + + if (bind(*fd, laddr, slen) < 0) { + close(*fd); + return NULL; + } + } + client = rpc_clntudp_create(addr, info, fd); + break; + + case IPPROTO_TCP: + if (!info->client) { + *fd = open_sock(addr->sa_family, type, proto); + if (*fd < 0) + return NULL; + + if (connect_nb(*fd, laddr, slen, &info->timeout) < 0) { + close(*fd); + return NULL; + } + } + client = rpc_clnttcp_create(addr, info, fd); + break; + + default: + break; + } + + return client; +} + +/* + * Create a UDP RPC client + */ +static CLIENT *create_udp_client(struct conn_info *info) +{ + CLIENT *client = NULL; + struct addrinfo *ai, *haddr; + struct addrinfo hints; + int fd, ret; + + if (info->proto->p_proto != IPPROTO_UDP) + return NULL; + + fd = RPC_ANYSOCK; + + if (info->client) { + if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) { + fd = RPC_ANYSOCK; + clnt_destroy(info->client); + info->client = NULL; + } else { + clnt_control(info->client, CLSET_FD_NCLOSE, NULL); + clnt_destroy(info->client); + } + } + + if (info->addr) { + client = rpc_do_create_client(info->addr, info, &fd); + if (client) + goto done; + + if (!info->client) { + close(fd); + fd = RPC_ANYSOCK; + } + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(info->host, NULL, &hints, &ai); + if (ret) { + error(LOGOPT_ANY, + "hostname lookup failed: %s", gai_strerror(ret)); + goto out_close; + } + + haddr = ai; + while (haddr) { + client = rpc_do_create_client(haddr->ai_addr, info, &fd); + if (client) + break; + + if (!info->client) { + close(fd); + fd = RPC_ANYSOCK; + } + + haddr = haddr->ai_next; + } + + freeaddrinfo(ai); + + if (!client) { + info->client = NULL; + goto out_close; + } +done: + /* Close socket fd on destroy, as is default for rpcowned fds */ + if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) { + clnt_destroy(client); + info->client = NULL; + goto out_close; + } + + return client; + +out_close: + if (fd != -1) + close(fd); + return NULL; +} + +int rpc_udp_getclient(struct conn_info *info, + unsigned int program, unsigned int version) +{ + struct protoent *pe_proto; + CLIENT *client; + + if (!info->client) { + pe_proto = getprotobyname("udp"); + if (!pe_proto) + return 0; + + info->proto = pe_proto; + info->send_sz = UDPMSGSIZE; + info->recv_sz = UDPMSGSIZE; + } + + info->program = program; + info->version = version; + + client = create_udp_client(info); + + if (!client) + return 0; + + info->client = client; + + return 1; +} + +void rpc_destroy_udp_client(struct conn_info *info) +{ + if (!info->client) + return; + + clnt_destroy(info->client); + info->client = NULL; + return; +} + /* * Create a TCP RPC client using non-blocking connect */ static CLIENT *create_tcp_client(struct conn_info *info) { - int fd, ghn_errno; - CLIENT *client; - struct sockaddr_in addr; - struct hostent hp; - struct hostent *php = &hp; - struct hostent *result; - char buf[HOST_ENT_BUF_SIZE]; - int ret; + CLIENT *client = NULL; + struct addrinfo *ai, *haddr; + struct addrinfo hints; + int fd, ret; if (info->proto->p_proto != IPPROTO_TCP) return NULL; + fd = RPC_ANYSOCK; + if (info->client) { if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) { - fd = -1; + fd = RPC_ANYSOCK; clnt_destroy(info->client); info->client = NULL; } else { @@ -305,51 +450,50 @@ static CLIENT *create_tcp_client(struct conn_info *info) } } - memset(&addr, 0, sizeof(addr)); - - addr.sin_family = AF_INET; if (info->addr) { - memcpy(&addr.sin_addr.s_addr, info->addr, info->addr_len); - goto got_addr; - } + client = rpc_do_create_client(info->addr, info, &fd); + if (client) + goto done; - if (inet_aton(info->host, &addr.sin_addr)) - goto got_addr; + if (!info->client) { + close(fd); + fd = RPC_ANYSOCK; + } + } - memset(&hp, 0, sizeof(struct hostent)); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; - ret = gethostbyname_r(info->host, php, - buf, HOST_ENT_BUF_SIZE, &result, &ghn_errno); - if (ret || !result) { - int err = ghn_errno == -1 ? errno : ghn_errno; - char *estr = strerror_r(err, buf, HOST_ENT_BUF_SIZE); - logerr("hostname lookup failed: %s", estr); + ret = getaddrinfo(info->host, NULL, &hints, &ai); + if (ret) { + error(LOGOPT_ANY, + "hostname lookup failed: %s", gai_strerror(ret)); goto out_close; } - memcpy(&addr.sin_addr.s_addr, php->h_addr, php->h_length); -got_addr: - addr.sin_port = htons(info->port); + haddr = ai; + while (haddr) { + client = rpc_do_create_client(haddr->ai_addr, info, &fd); + if (client) + break; - if (!info->client) { - fd = open_sock(PF_INET, SOCK_STREAM, info->proto->p_proto); - if (fd < 0) - return NULL; + if (!info->client) { + close(fd); + fd = RPC_ANYSOCK; + } - ret = connect_nb(fd, &addr, &info->timeout); - if (ret < 0) - goto out_close; + haddr = haddr->ai_next; } - client = clnttcp_create(&addr, - info->program, info->version, &fd, - info->send_sz, info->recv_sz); + freeaddrinfo(ai); if (!client) { info->client = NULL; goto out_close; } - +done: /* Close socket fd on destroy, as is default for rpcowned fds */ if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) { clnt_destroy(client); @@ -420,7 +564,7 @@ void rpc_destroy_tcp_client(struct conn_info *info) } int rpc_portmap_getclient(struct conn_info *info, - const char *host, const char *addr, size_t addr_len, + const char *host, struct sockaddr *addr, size_t addr_len, const char *proto, unsigned int option) { struct protoent *pe_proto; diff --git a/modules/cyrus-sasl.c b/modules/cyrus-sasl.c index 286af15..ec2ab0c 100644 --- a/modules/cyrus-sasl.c +++ b/modules/cyrus-sasl.c @@ -732,16 +732,25 @@ sasl_bind_mech(unsigned logopt, LDAP *ldap, struct lookup_context *ctxt, const c debug(logopt, "Attempting sasl bind with mechanism %s", mech); result = ldap_get_option(ldap, LDAP_OPT_HOST_NAME, &host); - if (result != LDAP_SUCCESS || !host) { + if (result != LDAP_OPT_SUCCESS || !host) { debug(logopt, "failed to get hostname for connection"); return NULL; } - if ((tmp = strchr(host, ':'))) - *tmp = '\0'; + if ((tmp = strrchr(host, ':'))) { + if (*(tmp - 1) != ']') { + *tmp = '\0'; + tmp = host; + } else { + *(tmp - 1) = '\0'; + tmp = host; + if (*tmp == '[') + tmp++; + } + } /* Create a new authentication context for the service. */ - result = sasl_client_new("ldap", host, NULL, NULL, NULL, 0, &conn); + result = sasl_client_new("ldap", tmp, NULL, NULL, NULL, 0, &conn); if (result != SASL_OK) { error(logopt, "sasl_client_new failed with error %d", result); diff --git a/modules/replicated.c b/modules/replicated.c index 835af97..9e85c00 100644 --- a/modules/replicated.c +++ b/modules/replicated.c @@ -45,7 +45,7 @@ #include #include #include -#include +#include #include #include #include @@ -75,6 +75,20 @@ static int volatile ifc_last_len = 0; #define max(x, y) (x >= y ? x : y) #define mmax(x, y, z) (max(x, y) == x ? max(x, z) : max(y, z)) +unsigned int ipv6_mask_cmp(uint32_t *host, uint32_t *iface, uint32_t *mask) +{ + unsigned int ret = 1; + unsigned int i; + + for (i = 0; i < 4; i++) { + if ((host[i] & mask[i]) != (iface[i] & mask[i])) { + ret = 0; + break; + } + } + return ret; +} + void seed_random(void) { int fd; @@ -136,20 +150,49 @@ static int alloc_ifreq(struct ifconf *ifc, int sock) return 1; } -static unsigned int get_proximity(const char *host_addr, int addr_len) +static unsigned int get_proximity(struct sockaddr *host_addr) { - struct sockaddr_in *msk_addr, *if_addr; + struct sockaddr_in *addr, *msk_addr, *if_addr; + struct sockaddr_in6 *addr6, *msk6_addr, *if6_addr; struct in_addr *hst_addr; - char tmp[20], buf[MAX_ERR_BUF], *ptr; + struct in6_addr *hst6_addr; + int addr_len; + char buf[MAX_ERR_BUF], *ptr; struct ifconf ifc; struct ifreq *ifr, nmptr; int sock, ret, i; - uint32_t mask, ha, ia; + uint32_t mask, ha, ia, *mask6, *ha6, *ia6; + + addr = NULL; + addr6 = NULL; + hst_addr = NULL; + hst6_addr = NULL; + mask6 = NULL; + ha6 = NULL; + ia6 = NULL; + + switch (host_addr->sa_family) { + case AF_INET: + addr = (struct sockaddr_in *) host_addr; + hst_addr = (struct in_addr *) &addr->sin_addr; + ha = ntohl((uint32_t) hst_addr->s_addr); + addr_len = sizeof(hst_addr); + break; - memcpy(tmp, host_addr, addr_len); - hst_addr = (struct in_addr *) tmp; + case AF_INET6: +#ifndef INET6 + return PROXIMITY_ERROR; +#else + addr6 = (struct sockaddr_in6 *) host_addr; + hst6_addr = (struct in6_addr *) &addr6->sin6_addr; + ha6 = &hst6_addr->s6_addr32[0]; + addr_len = sizeof(hst6_addr); + break; +#endif - ha = ntohl((uint32_t) hst_addr->s_addr); + default: + return PROXIMITY_ERROR; + } sock = open_sock(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { @@ -174,6 +217,10 @@ static unsigned int get_proximity(const char *host_addr, int addr_len) switch (ifr->ifr_addr.sa_family) { case AF_INET: +#ifndef INET6 + if (host_addr->sa_family == AF_INET6) + break; +#endif if_addr = (struct sockaddr_in *) &ifr->ifr_addr; ret = memcmp(&if_addr->sin_addr, hst_addr, addr_len); if (!ret) { @@ -183,6 +230,20 @@ static unsigned int get_proximity(const char *host_addr, int addr_len) } break; + case AF_INET6: +#ifndef INET6 + if (host_addr->sa_family == AF_INET) + break; + + if6_addr = (struct sockaddr_in6 *) &ifr->ifr_addr; + ret = memcmp(&if6_addr->sin6_addr, hst6_addr, addr_len); + if (!ret) { + close(sock); + free(ifc.ifc_req); + return PROXIMITY_LOCAL; + } +#endif + default: break; } @@ -197,23 +258,27 @@ static unsigned int get_proximity(const char *host_addr, int addr_len) while (ptr < (char *) ifc.ifc_req + ifc.ifc_len) { ifr = (struct ifreq *) ptr; + nmptr = *ifr; + ret = ioctl(sock, SIOCGIFNETMASK, &nmptr); + if (ret == -1) { + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); + logerr("ioctl: %s", estr); + close(sock); + free(ifc.ifc_req); + return PROXIMITY_ERROR; + } + switch (ifr->ifr_addr.sa_family) { case AF_INET: +#ifndef INET6 + if (host_addr->sa_family == AF_INET6) + break; +#endif if_addr = (struct sockaddr_in *) &ifr->ifr_addr; ia = ntohl((uint32_t) if_addr->sin_addr.s_addr); /* Is the address within a localiy attached subnet */ - nmptr = *ifr; - ret = ioctl(sock, SIOCGIFNETMASK, &nmptr); - if (ret == -1) { - char *estr = strerror_r(errno, buf, MAX_ERR_BUF); - logerr("ioctl: %s", estr); - close(sock); - free(ifc.ifc_req); - return PROXIMITY_ERROR; - } - msk_addr = (struct sockaddr_in *) &nmptr.ifr_netmask; mask = ntohl((uint32_t) msk_addr->sin_addr.s_addr); @@ -247,6 +312,29 @@ static unsigned int get_proximity(const char *host_addr, int addr_len) } break; + case AF_INET6: +#ifndef INET6 + if (host_addr->sa_family == AF_INET) + break; + + if6_addr = (struct sockaddr_in6 *) &ifr->ifr_addr; + ia6 = &if6_addr->sin6_addr.s6_addr32[0]; + + /* Is the address within the network of the interface */ + + msk6_addr = (struct sockaddr_in6 *) &nmptr.ifr_netmask; + mask6 = &msk6_addr->sin6_addr.s6_addr32[0]; + + if (ipv6_mask_cmp(ha6, ia6, mask6)) { + close(sock); + free(ifc.ifc_req); + return PROXIMITY_SUBNET; + } + + /* How do we define "local network" in ipv6? */ +#endif + break; + default: break; } @@ -262,11 +350,12 @@ static unsigned int get_proximity(const char *host_addr, int addr_len) } static struct host *new_host(const char *name, - const char *addr, size_t addr_len, + struct sockaddr *addr, size_t addr_len, unsigned int proximity, unsigned int weight) { struct host *new; - char *tmp1, *tmp2; + struct sockaddr *tmp2; + char *tmp1; if (!name || !addr) return NULL; @@ -950,65 +1039,78 @@ int prune_host_list(unsigned logopt, struct host **list, return 1; } -static int add_host_addrs(struct host **list, const char *host, unsigned int weight) +static int add_new_host(struct host **list, + const char *host, unsigned int weight, + struct addrinfo *host_addr) { - struct hostent he; - struct hostent *phe = &he; - struct hostent *result; - struct sockaddr_in saddr; - char buf[MAX_IFC_BUF], **haddr; - int ghn_errno, ret; struct host *new; unsigned int prx; + int addr_len; - saddr.sin_family = AF_INET; - if (inet_aton(host, &saddr.sin_addr)) { - const char *thost = (const char *) &saddr.sin_addr; - - prx = get_proximity(thost, sizeof(saddr.sin_addr)); - if (prx == PROXIMITY_ERROR) - return 0; - - if (!(new = new_host(host, thost, sizeof(saddr.sin_addr), prx, weight))) - return 0; - - if (!add_host(list, new)) - free_host(new); - - return 1; - } + prx = get_proximity(host_addr->ai_addr); + if (prx == PROXIMITY_ERROR) + return 0; - memset(buf, 0, MAX_IFC_BUF); - memset(&he, 0, sizeof(struct hostent)); + addr_len = sizeof(struct sockaddr); + new = new_host(host, host_addr->ai_addr, addr_len, prx, weight); + if (!new) + return 0; - ret = gethostbyname_r(host, phe, - buf, MAX_IFC_BUF, &result, &ghn_errno); - if (ret || !result) { - if (ghn_errno == -1) - logmsg("host %s: lookup failure %d", host, errno); - else - logmsg("host %s: lookup failure %d", host, ghn_errno); + if (!add_host(list, new)) { + free_host(new); return 0; } - for (haddr = phe->h_addr_list; *haddr; haddr++) { - struct in_addr tt; + return 1; +} - prx = get_proximity(*haddr, phe->h_length); - if (prx == PROXIMITY_ERROR) - return 0; +static int add_host_addrs(struct host **list, const char *host, unsigned int weight) +{ + struct addrinfo hints, *ni, *this; + int ret; - memcpy(&tt, *haddr, sizeof(struct in_addr)); - if (!(new = new_host(host, *haddr, phe->h_length, prx, weight))) - return 0; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; - if (!add_host(list, new)) { - free_host(new); - continue; - } + ret = getaddrinfo(host, NULL, &hints, &ni); + if (ret) + goto try_name; + + this = ni; + while (this) { + ret = add_new_host(list, host, weight, this); + if (!ret) + break; + this = this->ai_next; + } + freeaddrinfo(ni); + goto done; + +try_name: + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(host, NULL, &hints, &ni); + if (ret) { + error(LOGOPT_ANY, "hostname lookup failed: %s", + gai_strerror(ret)); + return 0; } - return 1; + this = ni; + while (this) { + ret = add_new_host(list, host, weight, this); + if (!ret) + break; + this = this->ai_next; + } + freeaddrinfo(ni); +done: + return ret; } static int add_path(struct host *hosts, const char *path, int len) @@ -1057,7 +1159,8 @@ static int add_local_path(struct host **hosts, const char *path) new->path = tmp; new->proximity = PROXIMITY_LOCAL; new->version = NFS_VERS_MASK; - new->name = new->addr = NULL; + new->name = NULL; + new->addr = NULL; new->weight = new->cost = 0; add_host(hosts, new);