From c53d65f18f32bf39a3b2211698245be16fa8fec6 Mon Sep 17 00:00:00 2001 From: Marcel Poul Date: Mon, 5 Sep 2011 11:31:41 +0000 Subject: [PATCH] LB/GSS rutines now try connecting to alternate addresses. Both Ip versions could be used on server. RT ticket #26307. --- org.glite.lbjp-common.gss/src/glite_gss.c | 195 ++++++++++++++++++------------ 1 file changed, 117 insertions(+), 78 deletions(-) diff --git a/org.glite.lbjp-common.gss/src/glite_gss.c b/org.glite.lbjp-common.gss/src/glite_gss.c index e6b1369..bae3169 100644 --- a/org.glite.lbjp-common.gss/src/glite_gss.c +++ b/org.glite.lbjp-common.gss/src/glite_gss.c @@ -61,6 +61,9 @@ struct asyn_result { static int globus_common_activated = 0; +static void free_hostent(struct hostent *h); +static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *addr, int addrtype, int port, struct timeval *timeout, edg_wll_GssConnection *connection, + edg_wll_GssStatus* gss_code); static int decrement_timeout(struct timeval *timeout, struct timeval before, struct timeval after) { (*timeout).tv_sec = (*timeout).tv_sec - (after.tv_sec - before.tv_sec); @@ -81,31 +84,40 @@ static void callback_ares_gethostbyname(void *arg, int status, struct hostent *h #endif { struct asyn_result *arp = (struct asyn_result *) arg; + int n_addr = 0; + int i = 0; switch (status) { case ARES_SUCCESS: - if (h && h->h_addr_list[0]) { - arp->ent->h_addr_list = - (char **) malloc(2 * sizeof(char *)); - if (arp->ent->h_addr_list == NULL) { - arp->err = NETDB_INTERNAL; - break; - } - arp->ent->h_addr_list[0] = - malloc(h->h_length); - if (arp->ent->h_addr_list[0] == NULL) { - free(arp->ent->h_addr_list); + if (h == NULL || h->h_addr_list[0] == NULL){ + arp->err = NO_DATA; + break; + } + /*how many addresses are there in h->h_addr_list*/ + while (h->h_addr_list[n_addr]) + n_addr++; + + arp->ent->h_addr_list = (char **) calloc((n_addr+1), sizeof(char *)); + if (arp->ent->h_addr_list == NULL) { + arp->err = NETDB_INTERNAL; + break; + } + for (i = 0; i < n_addr; i++) { + arp->ent->h_addr_list[i] = malloc(h->h_length); + if (arp->ent->h_addr_list[i] == NULL) { + free_hostent (arp->ent); + arp->ent = NULL; arp->err = NETDB_INTERNAL; break; } - memcpy(arp->ent->h_addr_list[0], h->h_addr_list[0], + memcpy(arp->ent->h_addr_list[i], h->h_addr_list[i], h->h_length); - arp->ent->h_addr_list[1] = NULL; + } + /* rest of h members might be assigned here(name,aliases), not necessery now */ + arp->ent->h_addr_list[n_addr] = NULL; arp->ent->h_addrtype = h->h_addrtype; + arp->ent->h_length = h->h_length; arp->err = NETDB_SUCCESS; - } else { - arp->err = NO_DATA; - } break; case ARES_EBADNAME: case ARES_ENOTFOUND: @@ -139,8 +151,7 @@ static void free_hostent(struct hostent *h){ } } -static int asyn_getservbyname2(int af, struct sockaddr_storage *addrOut, socklen_t *a_len,char const *name, int port, struct timeval *timeout) { - struct asyn_result ar; +static int asyn_getservbyname(int af, struct asyn_result *ar,char const *name, int port, struct timeval *timeout) { ares_channel channel; int nfds; fd_set readers, writers; @@ -168,11 +179,10 @@ static int asyn_getservbyname2(int af, struct sockaddr_storage *addrOut, socklen /* ares init */ if ( ares_init(&channel) != ARES_SUCCESS ) return(NETDB_INTERNAL); - ar.ent = (struct hostent *) calloc (sizeof(*ar.ent),1); /* query DNS server asynchronously */ ares_gethostbyname(channel, name2, af, callback_ares_gethostbyname, - (void *) &ar); + (void *) ar); /* wait for result */ while (1) { @@ -185,7 +195,6 @@ static int asyn_getservbyname2(int af, struct sockaddr_storage *addrOut, socklen gettimeofday(&check_time,0); if (timeout && decrement_timeout(timeout, start_time, check_time)) { ares_destroy(channel); - free_hostent(ar.ent); return(TRY_AGAIN); } start_time = check_time; @@ -195,7 +204,6 @@ static int asyn_getservbyname2(int af, struct sockaddr_storage *addrOut, socklen switch ( select(nfds, &readers, &writers, NULL, tvp) ) { case -1: if (errno != EINTR) { ares_destroy(channel); - free_hostent(ar.ent); return NETDB_INTERNAL; } else continue; @@ -206,31 +214,7 @@ static int asyn_getservbyname2(int af, struct sockaddr_storage *addrOut, socklen default: ares_process(channel, &readers, &writers); } } - - if (ar.err == NETDB_SUCCESS) { - struct sockaddr_in *p4 = (struct sockaddr_in *)addrOut; - struct sockaddr_in6 *p6 = (struct sockaddr_in6 *)addrOut; - - memset(addrOut, 0, sizeof *addrOut); - addrOut->ss_family = ar.ent->h_addrtype; - switch (ar.ent->h_addrtype) { - case AF_INET: - memcpy(&p4->sin_addr,ar.ent->h_addr_list[0], sizeof(struct in_addr)); - p4->sin_port = htons(port); - *a_len = sizeof (struct sockaddr_in); - break; - case AF_INET6: - memcpy(&p6->sin6_addr,ar.ent->h_addr_list[0], sizeof(struct in6_addr)); - p6->sin6_port = htons(port); - *a_len = sizeof (struct sockaddr_in6); - break; - default: - return NETDB_INTERNAL; - break; - } - } - free_hostent(ar.ent); ar.ent = NULL; - err = ar.err; + err = ar->err; /* literal conversion should always succeed */ if (name2 != name) free(name2-1); @@ -240,42 +224,38 @@ static int asyn_getservbyname2(int af, struct sockaddr_storage *addrOut, socklen return err; } -static int asyn_getservbyname(struct sockaddr_storage *addrOut, socklen_t *a_len,char const *name, int port, struct timeval *timeout) { - int res; - - res = asyn_getservbyname2(AF_INET6, addrOut, a_len, name, port, timeout); - if (res != HOST_NOT_FOUND) return res; - res = asyn_getservbyname2(AF_INET, addrOut, a_len, name, port, timeout); - return res; -} - static int -do_connect(int *s, char const *hostname, int port, struct timeval *timeout) +do_connect(int *s, char *addr, int addrtype, int port, struct timeval *timeout) { int sock; struct timeval before,after,to; struct sockaddr_storage a; + struct sockaddr_storage *p_a=&a; socklen_t a_len; int sock_err; socklen_t err_len; - int h_errno; int opt; - /* XXX todo: try multiple addresses */ - switch (h_errno = asyn_getservbyname(&a, &a_len, hostname, port, timeout)) { - case NETDB_SUCCESS: - break; - case TRY_AGAIN: - return EDG_WLL_GSS_ERROR_TIMEOUT; - case NETDB_INTERNAL: - /* fall through */ - default: - /* h_errno may be thread safe with Linux pthread libs, - * but such an assumption is not portable - */ - errno = h_errno; - return EDG_WLL_GSS_ERROR_HERRNO; - } + struct sockaddr_in *p4 = (struct sockaddr_in *)p_a; + struct sockaddr_in6 *p6 = (struct sockaddr_in6 *)p_a; + + memset(p_a, 0, sizeof *p_a); + p_a->ss_family = addrtype; + switch (addrtype) { + case AF_INET: + memcpy(&p4->sin_addr, addr, sizeof(struct in_addr)); + p4->sin_port = htons(port); + a_len = sizeof (struct sockaddr_in); + break; + case AF_INET6: + memcpy(&p6->sin6_addr, addr, sizeof(struct in6_addr)); + p6->sin6_port = htons(port); + a_len = sizeof (struct sockaddr_in6); + break; + default: + return NETDB_INTERNAL; + break; + } sock = socket(a.ss_family, SOCK_STREAM, 0); if (sock < 0) return EDG_WLL_GSS_ERROR_ERRNO; @@ -301,6 +281,7 @@ do_connect(int *s, char const *hostname, int port, struct timeval *timeout) case -1: close(sock); return EDG_WLL_GSS_ERROR_ERRNO; case 0: close(sock); + tv_sub(*timeout, *timeout); return EDG_WLL_GSS_ERROR_TIMEOUT; } gettimeofday(&after,NULL); @@ -674,7 +655,66 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port, struct timeval *timeout, edg_wll_GssConnection *connection, edg_wll_GssStatus* gss_code) { - int sock, ret; + int ret; + struct asyn_result ar; + int h_errno; + int addr_types[] = {AF_INET6, AF_INET}; + memset(connection, 0, sizeof(*connection)); + + + int ipver = AF_INET6; //def value; try IPv6 first + int j; + for (j = 0; j< sizeof(addr_types)/sizeof(*addr_types); j++) { + ipver = addr_types[j]; + ar.ent = (struct hostent *) calloc (1, sizeof(struct hostent)); + switch (h_errno = asyn_getservbyname(ipver, &ar, hostname, port, timeout)) { + case NETDB_SUCCESS: + break; + case TRY_AGAIN: + ret = EDG_WLL_GSS_ERROR_TIMEOUT; + goto end; + case NETDB_INTERNAL: + errno = h_errno; + ret = EDG_WLL_GSS_ERROR_HERRNO; + goto end; + default: + /* h_errno may be thread safe with Linux pthread libs, + * but such an assumption is not portable + */ + errno = h_errno; + ret = EDG_WLL_GSS_ERROR_HERRNO; + continue; + } + + int i = 0; + while (ar.ent->h_addr_list[i]) + { + ret = try_conn_and_auth (cred, hostname, ar.ent->h_addr_list[i], + ar.ent->h_addrtype, port, timeout, connection, gss_code); + if (ret == 0) + goto end; + if (timeout->tv_sec < 0 ||(timeout->tv_sec == 0 && timeout->tv_usec <= 0)) + goto end; + i++; + } + free_hostent(ar.ent); + ar.ent = NULL; + } + + end: + if (ar.ent != NULL){ + free_hostent(ar.ent); + ar.ent = NULL; + } + return ret; +} + +/* try connection and authentication for the given addr*/ +static int try_conn_and_auth (edg_wll_GssCred cred, char const *hostname, char *addr, int addrtype, int port, struct timeval *timeout, edg_wll_GssConnection *connection, + edg_wll_GssStatus* gss_code) +{ + int sock; + int ret = 0; OM_uint32 maj_stat, min_stat, min_stat2, req_flags; int context_established = 0; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; @@ -685,12 +725,11 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port, int retry = _EXPIRED_ALERT_RETRY_COUNT; maj_stat = min_stat = min_stat2 = req_flags = 0; - memset(connection, 0, sizeof(*connection)); /* GSI specific */ req_flags = GSS_C_GLOBUS_SSL_COMPATIBLE; - ret = do_connect(&sock, hostname, port, timeout); + ret = do_connect(&sock, addr, addrtype, port, timeout); if (ret) return ret; @@ -712,6 +751,7 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port, } free(servername); + servername = NULL; memset(&input_token, 0, sizeof(input_token)); /* XXX if cred == GSS_C_NO_CREDENTIAL set the ANONYMOUS flag */ @@ -785,7 +825,6 @@ edg_wll_gss_connect(edg_wll_GssCred cred, char const *hostname, int port, connection->sock = sock; connection->context = context; - servername = NULL; ret = 0; end: @@ -795,7 +834,7 @@ end: } if (server != GSS_C_NO_NAME) gss_release_name(&min_stat2, &server); - if (servername == NULL) + if (servername != NULL) free(servername); if (ret) close(sock); -- 1.8.2.3