Commit f34a77dc authored by Ghislain MARY's avatar Ghislain MARY

Handle TTL value in DNS responses and reconnect if the address changed.

parent cea1e814
......@@ -31,14 +31,14 @@ typedef struct belle_sip_resolver_context belle_sip_resolver_context_t;
* Callback prototype for asynchronous DNS SRV resolution.
* The srv_list contains struct dns_srv elements that must be taken and (possibly later) freed by the callee, using belle_sip_free().
*/
typedef void (*belle_sip_resolver_srv_callback_t)(void *data, const char *name, belle_sip_list_t *srv_list);
typedef void (*belle_sip_resolver_srv_callback_t)(void *data, const char *name, belle_sip_list_t *srv_list, uint32_t ttl);
/**
* Callback prototype for asynchronous DNS A and AAAA resolution.
* The ai_list contains addrinfo elements that must be taken and (possibly later) freed by the callee, using freeaddrinfo().
* These elements are linked by their ai_next field.
**/
typedef void (*belle_sip_resolver_callback_t)(void *data, const char *name, struct addrinfo *ai_list);
typedef void (*belle_sip_resolver_callback_t)(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl);
BELLE_SIP_BEGIN_DECLS
......
......@@ -103,6 +103,7 @@ BELLE_SIP_INSTANCIATE_VPTR(belle_sip_dns_srv_t, belle_sip_object_t,belle_sip_dns
struct belle_sip_resolver_context{
belle_sip_source_t source;
belle_sip_stack_t *stack;
uint32_t min_ttl;
uint8_t notified;
uint8_t cancelled;
uint8_t pad[2];
......@@ -168,6 +169,7 @@ struct belle_sip_dual_resolver_context{
void belle_sip_resolver_context_init(belle_sip_resolver_context_t *obj, belle_sip_stack_t *stack){
obj->stack=stack;
obj->min_ttl = UINT32_MAX;
belle_sip_init_sockets(); /* Need to be called for DNS resolution to work on Windows platform. */
}
......@@ -418,11 +420,11 @@ static void simple_resolver_context_notify(belle_sip_resolver_context_t *obj) {
#if USE_GETADDRINFO_FALLBACK
if (ctx->getaddrinfo_ai_list != NULL) ai_list = &ctx->getaddrinfo_ai_list;
#endif
ctx->cb(ctx->cb_data, ctx->name, *ai_list);
ctx->cb(ctx->cb_data, ctx->name, *ai_list, BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl);
*ai_list = NULL;
} else if (ctx->type == DNS_T_SRV) {
ctx->srv_list = srv_select_by_weight(ctx->srv_list);
ctx->srv_cb(ctx->srv_cb_data, ctx->name, ctx->srv_list);
ctx->srv_cb(ctx->srv_cb_data, ctx->name, ctx->srv_list, BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl);
}
}
......@@ -432,7 +434,7 @@ static void dual_resolver_context_notify(belle_sip_resolver_context_t *obj) {
results = ai_list_append(results, ctx->a_results);
ctx->a_results = NULL;
ctx->aaaa_results = NULL;
ctx->cb(ctx->cb_data, ctx->name, results);
ctx->cb(ctx->cb_data, ctx->name, results, BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl);
}
static void combined_resolver_context_cleanup(belle_sip_combined_resolver_context_t *ctx) {
......@@ -450,7 +452,7 @@ static void combined_resolver_context_cleanup(belle_sip_combined_resolver_contex
static void combined_resolver_context_notify(belle_sip_resolver_context_t *obj) {
belle_sip_combined_resolver_context_t *ctx = BELLE_SIP_COMBINED_RESOLVER_CONTEXT(obj);
ctx->cb(ctx->cb_data, ctx->name, ctx->final_results);
ctx->cb(ctx->cb_data, ctx->name, ctx->final_results, BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl);
ctx->final_results = NULL;
combined_resolver_context_cleanup(ctx);
}
......@@ -515,6 +517,7 @@ static int resolver_process_data(belle_sip_simple_resolver_context_t *ctx, unsig
sin6.sin6_family = AF_INET6;
sin6.sin6_port = ctx->port;
append_dns_result(ctx,&ctx->ai_list,(struct sockaddr*)&sin6,sizeof(sin6));
if (rr.ttl < BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl = rr.ttl;
} else if ((ctx->type == DNS_T_A) && (rr.class == DNS_C_IN) && (rr.type == DNS_T_A)) {
struct dns_a *a = &any.a;
struct sockaddr_in sin;
......@@ -523,6 +526,7 @@ static int resolver_process_data(belle_sip_simple_resolver_context_t *ctx, unsig
sin.sin_family = AF_INET;
sin.sin_port = ctx->port;
append_dns_result(ctx,&ctx->ai_list,(struct sockaddr*)&sin,sizeof(sin));
if (rr.ttl < BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl = rr.ttl;
} else if ((ctx->type == DNS_T_SRV) && (rr.class == DNS_C_IN) && (rr.type == DNS_T_SRV)) {
char host[NI_MAXHOST + 1];
struct dns_srv *srv = &any.srv;
......@@ -530,6 +534,7 @@ static int resolver_process_data(belle_sip_simple_resolver_context_t *ctx, unsig
snprintf(host, sizeof(host), "[target:%s port:%d prio:%d weight:%d]", srv->target, srv->port, srv->priority, srv->weight);
ctx->srv_list = belle_sip_list_insert_sorted(ctx->srv_list, belle_sip_object_ref(b_srv), srv_compare_prio);
belle_sip_message("SRV %s resolved to %s", ctx->name, host);
if (rr.ttl < BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(ctx)->min_ttl = rr.ttl;
}
}
}
......@@ -917,17 +922,18 @@ static char * srv_prefix_from_service_and_transport(const char *service, const c
return belle_sip_strdup_printf("_%s._udp.", service);
}
static void process_a_fallback_result(void *data, const char *name, struct addrinfo *ai_list){
static void process_a_fallback_result(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl){
belle_sip_combined_resolver_context_t *ctx=(belle_sip_combined_resolver_context_t *)data;
ctx->final_results=ai_list;
belle_sip_resolver_context_notify(BELLE_SIP_RESOLVER_CONTEXT(ctx));
}
static void combined_resolver_context_check_finished(belle_sip_combined_resolver_context_t *obj){
static void combined_resolver_context_check_finished(belle_sip_combined_resolver_context_t *obj, uint32_t ttl){
belle_sip_list_t *elem;
struct addrinfo *final=NULL;
unsigned char finished=TRUE;
if (ttl < BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(obj)->min_ttl = ttl;
for(elem=obj->srv_results;elem!=NULL;elem=elem->next){
belle_sip_dns_srv_t *srv=(belle_sip_dns_srv_t*)elem->data;
if (!srv->a_done) {
......@@ -949,12 +955,13 @@ static void combined_resolver_context_check_finished(belle_sip_combined_resolver
}
}
static void process_a_from_srv(void *data, const char *name, struct addrinfo *ai_list){
static void process_a_from_srv(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl){
belle_sip_dns_srv_t *srv=(belle_sip_dns_srv_t*)data;
srv->a_results=ai_list;
srv->a_done=TRUE;
belle_sip_message("A query finished for srv result [%s]",srv->target);
combined_resolver_context_check_finished(srv->root_resolver);
if (ttl < BELLE_SIP_RESOLVER_CONTEXT(srv->root_resolver)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(srv->root_resolver)->min_ttl = ttl;
combined_resolver_context_check_finished(srv->root_resolver, ttl);
}
static void srv_resolve_a(belle_sip_combined_resolver_context_t *obj, belle_sip_dns_srv_t *srv){
......@@ -970,10 +977,11 @@ static void srv_resolve_a(belle_sip_combined_resolver_context_t *obj, belle_sip_
belle_sip_object_unref(srv);
}
static void process_srv_results(void *data, const char *name, belle_sip_list_t *srv_results){
static void process_srv_results(void *data, const char *name, belle_sip_list_t *srv_results, uint32_t ttl){
belle_sip_combined_resolver_context_t *ctx=(belle_sip_combined_resolver_context_t *)data;
/*take a ref here, because the A resolution might succeed synchronously and terminate the context before exiting this function*/
belle_sip_object_ref(ctx);
if (ttl < BELLE_SIP_RESOLVER_CONTEXT(data)->min_ttl) BELLE_SIP_RESOLVER_CONTEXT(data)->min_ttl = ttl;
if (srv_results){
belle_sip_list_t *elem;
/* take a ref of each srv_results because the last A resolution may terminate synchronously
......@@ -1023,7 +1031,7 @@ belle_sip_resolver_context_t * belle_sip_stack_resolve(belle_sip_stack_t *stack,
return BELLE_SIP_RESOLVER_CONTEXT(ctx);
} else {
/* There is no resolve to be done */
cb(data, name, res);
cb(data, name, res, UINT32_MAX);
return NULL;
}
}
......@@ -1061,14 +1069,14 @@ static void dual_resolver_context_check_finished(belle_sip_dual_resolver_context
}
}
static void on_ipv4_results(void *data, const char *name, struct addrinfo *ai_list) {
static void on_ipv4_results(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl) {
belle_sip_dual_resolver_context_t *ctx = BELLE_SIP_DUAL_RESOLVER_CONTEXT(data);
ctx->a_results = ai_list;
ctx->a_notified = TRUE;
dual_resolver_context_check_finished(ctx);
}
static void on_ipv6_results(void *data, const char *name, struct addrinfo *ai_list) {
static void on_ipv6_results(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl) {
belle_sip_dual_resolver_context_t *ctx = BELLE_SIP_DUAL_RESOLVER_CONTEXT(data);
ctx->aaaa_results = ai_list;
ctx->aaaa_notified = TRUE;
......@@ -1116,7 +1124,7 @@ belle_sip_resolver_context_t * belle_sip_stack_resolve_a(belle_sip_stack_t *stac
}
} else {
/* There is no resolve to be done */
cb(data, name, res);
cb(data, name, res, UINT32_MAX);
}
return NULL;
}
......
......@@ -126,6 +126,10 @@ static void belle_sip_channel_destroy(belle_sip_channel_t *obj){
belle_sip_main_loop_remove_source(obj->stack->ml,obj->inactivity_timer);
belle_sip_object_unref(obj->inactivity_timer);
}
if (obj->dns_ttl_timer) {
belle_sip_main_loop_remove_source(obj->stack->ml, obj->dns_ttl_timer);
belle_sip_object_unref(obj->dns_ttl_timer);
}
if (obj->public_ip) belle_sip_free(obj->public_ip);
if (obj->outgoing_messages) belle_sip_list_free_with_data(obj->outgoing_messages,belle_sip_object_unref);
if (obj->incoming_messages) belle_sip_list_free_with_data(obj->incoming_messages,belle_sip_object_unref);
......@@ -1418,16 +1422,57 @@ 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 *ai_list){
static int channel_dns_ttl_timeout(void *data, unsigned int event) {
belle_sip_channel_t *obj = (belle_sip_channel_t *)data;
belle_sip_message("Channel [%p]: DNS TTL timeout reached.", obj);
obj->dns_ttl_timedout = TRUE;
return BELLE_SIP_STOP;
}
static bool_t addrinfo_in_list(const struct addrinfo *ai, const struct addrinfo *list) {
const struct addrinfo *item = list;
bool_t in_list = FALSE;
while (item != NULL) {
if ((ai->ai_family == item->ai_family) && (bctbx_sockaddr_equals(ai->ai_addr, item->ai_addr))) {
in_list = TRUE;
break;
}
item = item->ai_next;
}
return in_list;
}
static void channel_res_done(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl){
belle_sip_channel_t *obj=(belle_sip_channel_t*)data;
if (obj->resolver_ctx){
belle_sip_object_unref(obj->resolver_ctx);
obj->resolver_ctx=NULL;
}
if (ai_list){
obj->peer_list=obj->current_peer=ai_list;
channel_set_state(obj,BELLE_SIP_CHANNEL_RES_DONE);
if (!obj->current_peer) {
obj->peer_list=obj->current_peer=ai_list;
channel_set_state(obj,BELLE_SIP_CHANNEL_RES_DONE);
} else {
if (addrinfo_in_list(obj->current_peer, ai_list)) {
belle_sip_message("channel[%p]: DNS resolution returned the currently used address, continue using it", obj);
obj->peer_list = ai_list;
channel_set_state(obj, BELLE_SIP_CHANNEL_READY);
} else {
belle_sip_message("channel[%p]: DNS resolution returned an address different than the one being used, reconnect to the new address", obj);
obj->peer_list = obj->current_peer = ai_list;
belle_sip_channel_close(obj);
belle_sip_main_loop_do_later(obj->stack->ml, (belle_sip_callback_t)channel_connect_next, belle_sip_object_ref(obj));
channel_set_state(obj, BELLE_SIP_CHANNEL_RETRY);
}
}
channel_prepare_continue(obj);
if (!obj->dns_ttl_timer ) {
obj->dns_ttl_timer = belle_sip_main_loop_create_timeout(obj->stack->ml, channel_dns_ttl_timeout, obj, ttl * 1000, "Channel DNS TTL timer");
} else {
/* Restart the timer for new period. */
belle_sip_source_set_timeout(obj->dns_ttl_timer, ttl * 1000);
belle_sip_main_loop_add_source(obj->stack->ml, obj->dns_ttl_timer);
}
}else{
belle_sip_error("%s: DNS resolution failed for %s", __FUNCTION__, name);
channel_set_state(obj,BELLE_SIP_CHANNEL_ERROR);
......@@ -1538,6 +1583,13 @@ belle_sip_channel_t *belle_sip_channel_find_from_list(belle_sip_list_t *l, int a
return chan;
}
void belle_sip_channel_check_dns_reusability(belle_sip_channel_t *obj) {
if (obj->dns_ttl_timedout) {
obj->dns_ttl_timedout = FALSE;
belle_sip_channel_resolve(obj);
}
}
#ifdef __ANDROID__
unsigned long belle_sip_begin_background_task(const char *name, belle_sip_background_task_end_callback_t cb, void *data){
......
......@@ -112,6 +112,7 @@ struct belle_sip_channel{
belle_sip_channel_input_stream_t input_stream;
belle_sip_list_t* incoming_messages;
belle_sip_source_t *inactivity_timer;
belle_sip_source_t *dns_ttl_timer;
uint64_t last_recv_time;
int simulated_recv_return; /* used to simulate network error. 0= no data (disconnected) >0= do nothing -1= network error, 1500 special number to silently discard incoming buffer*/
unsigned long bg_task_id;
......@@ -124,6 +125,7 @@ struct belle_sip_channel{
unsigned char soft_error; /*set when this channel enters ERROR state because of error detected in upper layer */
int stop_logging_buffer; /*log buffer content only if this is non binary data, and stop it at the first occurence*/
bool_t closed_by_remote; /*If the channel has been remotely closed*/
bool_t dns_ttl_timedout;
};
#define BELLE_SIP_CHANNEL(obj) BELLE_SIP_CAST(obj,belle_sip_channel_t)
......@@ -207,6 +209,11 @@ int belle_sip_channel_notify_timeout(belle_sip_channel_t *obj);
/*Used by transaction layer to report a server having internal errors, so that we can retry with another IP (in case of DNS SRV)*/
BELLESIP_EXPORT void belle_sip_channel_notify_server_error(belle_sip_channel_t *obj);
/*
* Check if the DNS TTL has expired. If this is the case, set the channel status to RES_IN_PROGRESS.
*/
void belle_sip_channel_check_dns_reusability(belle_sip_channel_t *obj);
BELLE_SIP_END_DECLS
......
......@@ -833,7 +833,10 @@ belle_sip_channel_t * belle_sip_provider_get_channel(belle_sip_provider_t *p, co
lp=(belle_sip_listening_point_t*)l->data;
if (strcasecmp(belle_sip_listening_point_get_transport(lp),hop->transport)==0){
chan=belle_sip_listening_point_get_channel(lp,hop);
if (chan) return chan;
if (chan) {
belle_sip_channel_check_dns_reusability(chan);
return chan;
}
candidate=lp;
}
}
......
......@@ -413,7 +413,7 @@ static int tls_channel_connect_to(belle_sip_channel_t *obj, const struct addrinf
return -1;
}
static void http_proxy_res_done(void *data, const char *name, struct addrinfo *ai_list){
static void http_proxy_res_done(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl){
belle_sip_tls_channel_t *obj=(belle_sip_tls_channel_t*)data;
if (obj->http_proxy_resolver_ctx){
belle_sip_object_unref(obj->http_proxy_resolver_ctx);
......
......@@ -85,7 +85,7 @@ static void destroy_endpoint(endpoint_t *endpoint) {
belle_sip_uninit_sockets();
}
static void a_resolve_done(void *data, const char *name, struct addrinfo *ai_list) {
static void a_resolve_done(void *data, const char *name, struct addrinfo *ai_list, uint32_t ttl) {
endpoint_t *client = (endpoint_t *)data;
BELLESIP_UNUSED(name);
client->resolve_done = 1;
......@@ -96,7 +96,7 @@ static void a_resolve_done(void *data, const char *name, struct addrinfo *ai_lis
client->resolve_ko = 1;
}
static void srv_resolve_done(void *data, const char *name, belle_sip_list_t *srv_list) {
static void srv_resolve_done(void *data, const char *name, belle_sip_list_t *srv_list, uint32_t ttl) {
endpoint_t *client = (endpoint_t *)data;
BELLESIP_UNUSED(name);
client->resolve_done = 1;
......
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