Commit 567fd8f8 authored by Ghislain MARY's avatar Ghislain MARY

Handle multiple results for DNS A and AAAA queries.

parent 8dc44745
......@@ -137,9 +137,8 @@ static int resolver_process_a_data(belle_sip_resolver_context_t *ctx, unsigned i
sin6.sin6_port = ctx->port;
if (getnameinfo((struct sockaddr *)&sin6, sizeof(sin6), host, sizeof(host), service, sizeof(service), NI_NUMERICHOST) != 0)
continue;
ctx->ai = belle_sip_ip_address_to_addrinfo(ctx->family, host, ctx->port);
belle_sip_message("%s has address %s", ctx->name, host);
break;
ctx->ai_list = belle_sip_list_append(ctx->ai_list, belle_sip_ip_address_to_addrinfo(ctx->family, host, ctx->port));
belle_sip_message("%s resolved to %s", ctx->name, host);
} else {
if ((rr.class == DNS_C_IN) && (rr.type == DNS_T_A)) {
struct dns_a *a = &any.a;
......@@ -150,15 +149,14 @@ static int resolver_process_a_data(belle_sip_resolver_context_t *ctx, unsigned i
sin.sin_port = ctx->port;
if (getnameinfo((struct sockaddr *)&sin, sizeof(sin), host, sizeof(host), service, sizeof(service), NI_NUMERICHOST) != 0)
continue;
ctx->ai = belle_sip_ip_address_to_addrinfo(ctx->family, host, ctx->port);
belle_sip_message("%s has address %s", ctx->name, host);
break;
ctx->ai_list = belle_sip_list_append(ctx->ai_list, belle_sip_ip_address_to_addrinfo(ctx->family, host, ctx->port));
belle_sip_message("%s resolved to %s", ctx->name, host);
}
}
}
}
free(ans);
ctx->cb(ctx->cb_data, ctx->name, ctx->ai);
ctx->cb(ctx->cb_data, ctx->name, ctx->ai_list);
ctx->done=TRUE;
return BELLE_SIP_STOP;
}
......@@ -285,8 +283,8 @@ struct addrinfo * belle_sip_ip_address_to_addrinfo(int family, const char *ipadd
static void belle_sip_resolver_context_destroy(belle_sip_resolver_context_t *ctx){
/* Do not free ctx->ai with freeaddrinfo(). Let the caller do it, otherwise
it will not be able to use it after the resolver has been destroyed. */
/* Do not free elements of ctx->ai_list with freeaddrinfo(). Let the caller do it, otherwise
it will not be able to use them after the resolver has been destroyed. */
if (ctx->name)
belle_sip_free(ctx->name);
if (ctx->R)
......@@ -311,7 +309,7 @@ unsigned long belle_sip_resolve(belle_sip_stack_t *stack, const char *name, int
ctx->cb = cb;
ctx->name = belle_sip_strdup(name);
ctx->port = port;
ctx->ai = NULL;
ctx->ai_list = NULL;
if (family == 0) family = AF_UNSPEC;
ctx->family = family;
if (resolver_start_query(ctx,
......@@ -330,7 +328,8 @@ unsigned long belle_sip_resolve(belle_sip_stack_t *stack, const char *name, int
return 0; /*resolution done synchronously*/
}
} else {
cb(data, name, res);
belle_sip_list_t *ai_list = belle_sip_list_append(NULL, res);
cb(data, name, ai_list);
return 0;
}
}
......
......@@ -29,10 +29,10 @@ typedef struct belle_sip_resolver_context belle_sip_resolver_context_t;
#define BELLE_SIP_RESOLVER_CONTEXT(obj) BELLE_SIP_CAST(obj,belle_sip_resolver_context_t)
/**
* Callback prototype for asynchronous DNS resolution. The result addrinfo must be taken and (possibly later) freed by
* the callee, using freeaddrinfo().
* Callback prototype for asynchronous DNS resolution. The results_list contains addrinfo elements that must be
* taken and (possibly later) freed by the callee, using freeaddrinfo().
**/
typedef void (*belle_sip_resolver_callback_t)(void *data, const char *name, struct addrinfo *result);
typedef void (*belle_sip_resolver_callback_t)(void *data, const char *name, belle_sip_list_t *results_list);
struct belle_sip_resolver_context{
......@@ -45,7 +45,7 @@ struct belle_sip_resolver_context{
struct dns_resolver *R;
char *name;
int port;
struct addrinfo *ai;
belle_sip_list_t *ai_list;
int family;
uint8_t cancelled;
uint8_t done;
......
......@@ -54,8 +54,14 @@ static belle_sip_list_t * for_each_weak_unref_free(belle_sip_list_t *l, belle_si
return NULL;
}
static void belle_sip_channel_free_peer(void *ptr) {
struct addrinfo *ai = (struct addrinfo *)ptr;
freeaddrinfo(ai);
}
static void belle_sip_channel_destroy(belle_sip_channel_t *obj){
if (obj->peer_list) freeaddrinfo(obj->peer_list);
belle_sip_list_for_each(obj->peer_list,belle_sip_channel_free_peer);
belle_sip_list_free(obj->peer_list);
belle_sip_free(obj->peer_cname);
belle_sip_free(obj->peer_name);
if (obj->local_ip) belle_sip_free(obj->local_ip);
......@@ -302,7 +308,8 @@ void belle_sip_channel_init_with_addr(belle_sip_channel_t *obj, belle_sip_stack_
ai.ai_addrlen=addrlen;
belle_sip_addrinfo_to_ip(&ai,remoteip,sizeof(remoteip),&peer_port);
belle_sip_channel_init(obj,stack,NULL,0,NULL,remoteip,peer_port);
obj->peer_list=obj->current_peer=belle_sip_ip_address_to_addrinfo(ai.ai_family, obj->peer_name,obj->peer_port);
obj->current_peer=belle_sip_ip_address_to_addrinfo(ai.ai_family, obj->peer_name,obj->peer_port);
obj->peer_list=belle_sip_list_prepend(obj->peer_list,obj->current_peer);
}
void belle_sip_channel_set_socket(belle_sip_channel_t *obj, belle_sip_socket_t sock, belle_sip_source_func_t datafunc){
......@@ -564,12 +571,12 @@ void belle_sip_channel_set_ready(belle_sip_channel_t *obj, const struct sockaddr
channel_process_queue(obj);
}
static void channel_res_done(void *data, const char *name, struct addrinfo *res){
static void channel_res_done(void *data, const char *name, belle_sip_list_t *results_list){
belle_sip_channel_t *obj=(belle_sip_channel_t*)data;
obj->resolver_id=0;
if (res){
obj->peer_list=res;
obj->current_peer=res;
if (results_list && (belle_sip_list_size(results_list)>0)){
obj->peer_list=results_list;
obj->current_peer=(struct addrinfo *)belle_sip_list_nth_data(obj->peer_list,0);
channel_set_state(obj,BELLE_SIP_CHANNEL_RES_DONE);
channel_prepare_continue(obj);
}else{
......
......@@ -85,7 +85,7 @@ struct belle_sip_channel{
char *local_ip;
int local_port;
unsigned long resolver_id;
struct addrinfo *peer_list;
belle_sip_list_t *peer_list;
struct addrinfo *current_peer;
belle_sip_list_t *outgoing_messages;
belle_sip_list_t* incoming_messages;
......
......@@ -117,7 +117,7 @@ belle_sip_channel_t * belle_sip_channel_new_udp_with_addr(belle_sip_stack_t *sta
if (err!=0){
belle_sip_error("getaddrinfo() failed for udp channel [%p] error [%s]",obj,gai_strerror(err));
}
obj->base.peer_list=obj->base.current_peer;
obj->base.peer_list=belle_sip_list_prepend(obj->base.peer_list,obj->base.current_peer);
return (belle_sip_channel_t*)obj;
}
......@@ -26,6 +26,7 @@
#define IPV4_SIP_DOMAIN "sip.linphone.org"
#define IPV4_SIP_IP "37.59.129.73"
#define IPV4_SIP_BAD_DOMAIN "dummy.linphone.org"
#define IPV4_MULTIRES_DOMAIN "google.fr"
#define IPV6_SIP_DOMAIN "videolan.org"
#define IPV6_SIP_IP "2a01:e0d:1:3:58bf:fa02:0:1"
#define SIP_PORT 5060
......@@ -36,7 +37,7 @@ typedef struct endpoint {
long unsigned int resolver_id;
int resolve_done;
int resolve_ko;
struct addrinfo *result;
belle_sip_list_t *results_list; /**< List of struct addrinfo pointers */
} endpoint_t;
static unsigned int wait_for(belle_sip_stack_t *stack, int *current_value, int expected_value, int timeout) {
......@@ -59,14 +60,18 @@ static endpoint_t* create_endpoint(void) {
return endpoint;
}
static void free_result(void *ptr) {
struct addrinfo *ai = (struct addrinfo *)ptr;
freeaddrinfo(ai);
}
static void reset_endpoint(endpoint_t *endpoint) {
endpoint->resolver_id = 0;
endpoint->resolve_done = 0;
endpoint->resolve_ko = 0;
if (endpoint->result) {
freeaddrinfo(endpoint->result);
endpoint->result = NULL;
}
belle_sip_list_for_each(endpoint->results_list, free_result);
belle_sip_list_free(endpoint->results_list);
endpoint->results_list = NULL;
}
static void destroy_endpoint(endpoint_t *endpoint) {
......@@ -76,12 +81,12 @@ static void destroy_endpoint(endpoint_t *endpoint) {
belle_sip_uninit_sockets();
}
static void resolve_done(void *data, const char *name, struct addrinfo *res) {
static void resolve_done(void *data, const char *name, belle_sip_list_t *results_list) {
endpoint_t *client = (endpoint_t *)data;
BELLESIP_UNUSED(name);
client->resolve_done = 1;
if (res) {
client->result = res;
if (results_list && (belle_sip_list_size(results_list) > 0)) {
client->results_list = results_list;
client->resolve_done = 1;
} else
client->resolve_ko = 1;
......@@ -98,13 +103,15 @@ static void ipv4_a_query(void) {
client->resolver_id = belle_sip_resolve(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, resolve_done, client, belle_sip_stack_get_main_loop(client->stack));
CU_ASSERT_NOT_EQUAL(client->resolver_id, 0);
CU_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout));
CU_ASSERT_PTR_NOT_EQUAL(client->result, NULL);
if (client->result) {
struct sockaddr_in *sock_in = (struct sockaddr_in *)client->result->ai_addr;
CU_ASSERT_PTR_NOT_EQUAL(client->results_list, NULL);
if (client->results_list && (belle_sip_list_size(client->results_list) > 0)) {
struct addrinfo *result_ai = belle_sip_list_nth_data(client->results_list, 0);
struct sockaddr_in *sock_in = (struct sockaddr_in *)result_ai->ai_addr;
CU_ASSERT_EQUAL(ntohs(sock_in->sin_port), SIP_PORT);
ai = belle_sip_ip_address_to_addrinfo(AF_INET, IPV4_SIP_IP, SIP_PORT);
if (ai) {
CU_ASSERT_EQUAL(sock_in->sin_addr.s_addr, ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr);
freeaddrinfo(ai);
}
}
......@@ -121,7 +128,7 @@ static void ipv4_a_query_no_result(void) {
client->resolver_id = belle_sip_resolve(client->stack, IPV4_SIP_BAD_DOMAIN, SIP_PORT, AF_INET, resolve_done, client, belle_sip_stack_get_main_loop(client->stack));
CU_ASSERT_NOT_EQUAL(client->resolver_id, 0);
CU_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout));
CU_ASSERT_PTR_EQUAL(client->result, NULL);
CU_ASSERT_PTR_EQUAL(client->results_list, NULL);
destroy_endpoint(client);
}
......@@ -149,11 +156,30 @@ static void ipv4_a_query_timeout(void) {
client->resolver_id = belle_sip_resolve(client->stack, "toto.com", SIP_PORT, AF_INET, resolve_done, client, belle_sip_stack_get_main_loop(client->stack));
CU_ASSERT_NOT_EQUAL(client->resolver_id, 0);
CU_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, 200));
CU_ASSERT_PTR_EQUAL(client->result, NULL);
CU_ASSERT_PTR_EQUAL(client->results_list, NULL);
CU_ASSERT_EQUAL(client->resolve_ko,1);
destroy_endpoint(client);
}
/* Successful IPv4 A query with multiple results */
static void ipv4_a_query_multiple_results(void) {
int timeout;
endpoint_t *client = create_endpoint();
CU_ASSERT_PTR_NOT_NULL_FATAL(client);
timeout = belle_sip_stack_get_dns_timeout(client->stack);
client->resolver_id = belle_sip_resolve(client->stack, IPV4_MULTIRES_DOMAIN, SIP_PORT, AF_INET, resolve_done, client, belle_sip_stack_get_main_loop(client->stack));
CU_ASSERT_NOT_EQUAL(client->resolver_id, 0);
CU_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout));
CU_ASSERT_PTR_NOT_EQUAL(client->results_list, NULL);
if (client->results_list) {
CU_ASSERT_NOT_EQUAL(belle_sip_list_size(client->results_list), 0);
CU_ASSERT_NOT_EQUAL(belle_sip_list_size(client->results_list), 1);
}
destroy_endpoint(client);
}
/* Successful IPv6 AAAA query */
static void ipv6_aaaa_query(void) {
struct addrinfo *ai;
......@@ -165,9 +191,10 @@ static void ipv6_aaaa_query(void) {
client->resolver_id = belle_sip_resolve(client->stack, IPV6_SIP_DOMAIN, SIP_PORT, AF_INET6, resolve_done, client, belle_sip_stack_get_main_loop(client->stack));
CU_ASSERT_NOT_EQUAL(client->resolver_id, 0);
CU_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout));
CU_ASSERT_PTR_NOT_EQUAL(client->result, NULL);
if (client->result) {
struct sockaddr_in6 *sock_in6 = (struct sockaddr_in6 *)client->result->ai_addr;
CU_ASSERT_PTR_NOT_EQUAL(client->results_list, NULL);
if (client->results_list && (belle_sip_list_size(client->results_list) > 0)) {
struct addrinfo *result_ai = belle_sip_list_nth_data(client->results_list, 0);
struct sockaddr_in6 *sock_in6 = (struct sockaddr_in6 *)result_ai->ai_addr;
CU_ASSERT_EQUAL(ntohs(sock_in6->sin6_port), SIP_PORT);
ai = belle_sip_ip_address_to_addrinfo(AF_INET6, IPV6_SIP_IP, SIP_PORT);
if (ai) {
......@@ -176,6 +203,7 @@ static void ipv6_aaaa_query(void) {
for (i = 0; i < 8; i++) {
CU_ASSERT_EQUAL(sock_in6->sin6_addr.s6_addr[i], ipv6_address->s6_addr[i]);
}
freeaddrinfo(ai);
}
}
......@@ -188,6 +216,7 @@ test_t resolver_tests[] = {
{ "A query (IPv4) with no result", ipv4_a_query_no_result },
{ "A query (IPv4) with send failure", ipv4_a_query_send_failure },
{ "A query (IPv4) with timeout", ipv4_a_query_timeout },
{ "A query (IPv4) with multiple results", ipv4_a_query_multiple_results },
{ "AAAA query (IPv6)", ipv6_aaaa_query },
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment