Commit 9dd52e29 authored by Simon Morlat's avatar Simon Morlat

improve DNS resolver:

- supports IPv6 DNS servers
- supports nameserver lists with both IPv4 and IPv6 servers
parent 027d2dd3
......@@ -99,14 +99,14 @@ BELLESIP_EXPORT void belle_sip_resolver_context_cancel(belle_sip_resolver_contex
* Lookups the source address from local interface that can be used to connect to a destination address.
* local_port is only used to be assigned into the result source address.
**/
void belle_sip_get_src_addr_for(const struct sockaddr *dest, socklen_t destlen, struct sockaddr *src, socklen_t *srclen, int local_port);
BELLESIP_EXPORT void belle_sip_get_src_addr_for(const struct sockaddr *dest, socklen_t destlen, struct sockaddr *src, socklen_t *srclen, int local_port);
/**
* This function will transform a V4 to V6 mapped address to a pure V4 and write it into result, or will just copy it otherwise.
* The memory for v6 and result may be the same, in which case processing is done in place or no copy is done.
* The pointer to result must have sufficient storage, typically a struct sockaddr_storage.
**/
void belle_sip_address_remove_v4_mapping(const struct sockaddr *v6, struct sockaddr *result, socklen_t *result_len);
BELLESIP_EXPORT void belle_sip_address_remove_v4_mapping(const struct sockaddr *v6, struct sockaddr *result, socklen_t *result_len);
BELLE_SIP_END_DECLS
......
......@@ -93,6 +93,20 @@ BELLESIP_EXPORT const char * belle_sip_stack_get_dns_user_hosts_file(const belle
**/
BELLESIP_EXPORT void belle_sip_stack_set_dns_user_hosts_file(belle_sip_stack_t *stack, const char *hosts_file);
/**
* Get the overriding DNS resolv.conf file.
* @return The path to the overriding DNS resolv.conf file.
**/
BELLESIP_EXPORT const char * belle_sip_stack_get_dns_resolv_conf_file(const belle_sip_stack_t *stack);
/**
* Can be used to load an overriding DNS resolv.conf file for tests.
* @param stack
* @param hosts_file The path to the overriding DNS resolv.conf file to load.
**/
BELLESIP_EXPORT void belle_sip_stack_set_dns_resolv_conf_file(belle_sip_stack_t *stack, const char *hosts_file);
/**
* Returns the time interval in seconds after which a connection must be closed when inactive.
**/
......
......@@ -502,6 +502,7 @@ struct belle_sip_stack{
int resolver_send_error; /* used to simulate network error*/
int dscp;
char *dns_user_hosts_file; /* used to load additional hosts file for tests */
char *dns_resolv_conf; /*used to load custom resolv.conf, for tests*/
unsigned char dns_srv_enabled;
};
......
......@@ -152,10 +152,7 @@ void belle_sip_resolver_context_init(belle_sip_resolver_context_t *obj, belle_si
}
static struct dns_resolv_conf *resconf(belle_sip_simple_resolver_context_t *ctx) {
#if !_WIN32 && !HAVE_RESINIT
/*#if !_WIN32 && (!HAVE_RESINIT || !TARGET_OS_IPHONE)*/
const char *path;
#endif
int error;
if (ctx->resconf)
......@@ -165,44 +162,62 @@ static struct dns_resolv_conf *resconf(belle_sip_simple_resolver_context_t *ctx)
belle_sip_error("%s dns_resconf_open error: %s", __FUNCTION__, dns_strerror(error));
return NULL;
}
path=belle_sip_stack_get_dns_resolv_conf_file(ctx->base.stack);
if (!path){
#ifdef _WIN32
error = dns_resconf_loadwin(ctx->resconf);
if (error) {
belle_sip_error("%s dns_resconf_loadwin error", __FUNCTION__);
}
error = dns_resconf_loadwin(ctx->resconf);
if (error) {
belle_sip_error("%s dns_resconf_loadwin error", __FUNCTION__);
}
#elif ANDROID
error = dns_resconf_loadandroid(ctx->resconf);
if (error) {
belle_sip_error("%s dns_resconf_loadandroid error", __FUNCTION__);
}
error = dns_resconf_loadandroid(ctx->resconf);
if (error) {
belle_sip_error("%s dns_resconf_loadandroid error", __FUNCTION__);
}
#elif HAVE_RESINIT
/*#elif HAVE_RESINIT && TARGET_OS_IPHONE*/
error = dns_resconf_loadfromresolv(ctx->resconf);
if (error) {
belle_sip_error("%s dns_resconf_loadfromresolv error", __FUNCTION__);
}
error = dns_resconf_loadfromresolv(ctx->resconf);
if (error) {
belle_sip_error("%s dns_resconf_loadfromresolv error", __FUNCTION__);
}
#else
path = "/etc/resolv.conf";
error = dns_resconf_loadpath(ctx->resconf, path);
if (error) {
belle_sip_error("%s dns_resconf_loadpath error [%s]: %s", __FUNCTION__, path, dns_strerror(error));
return NULL;
}
path = "/etc/resolv.conf";
error = dns_resconf_loadpath(ctx->resconf, path);
if (error) {
belle_sip_error("%s dns_resconf_loadpath error [%s]: %s", __FUNCTION__, path, dns_strerror(error));
return NULL;
}
path = "/etc/nsswitch.conf";
error = dns_nssconf_loadpath(ctx->resconf, path);
if (error) {
belle_sip_message("%s dns_nssconf_loadpath error [%s]: %s", __FUNCTION__, path, dns_strerror(error));
}
path = "/etc/nsswitch.conf";
error = dns_nssconf_loadpath(ctx->resconf, path);
if (error) {
belle_sip_message("%s dns_nssconf_loadpath error [%s]: %s", __FUNCTION__, path, dns_strerror(error));
}
#endif
}else{
error = dns_resconf_loadpath(ctx->resconf, path);
if (error) {
belle_sip_error("%s dns_resconf_loadpath() of custom file error [%s]: %s", __FUNCTION__, path, dns_strerror(error));
return NULL;
}
}
if (error==0){
char ip[64];
char serv[10];
struct sockaddr *ns_addr=(struct sockaddr*)&ctx->resconf->nameserver[0];
getnameinfo(ns_addr,sizeof(struct sockaddr_storage),ip,sizeof(ip),serv,sizeof(serv),NI_NUMERICHOST|NI_NUMERICSERV);
belle_sip_message("Loaded DNS server: %s",ip);
ctx->resconf->iface.ss_family=ns_addr->sa_family;
int using_ipv6=FALSE;
int i;
belle_sip_message("Resolver is using DNS server(s):");
for(i=0;i<sizeof(ctx->resconf->nameserver)/sizeof(ctx->resconf->nameserver[0]);++i){
struct sockaddr *ns_addr=(struct sockaddr*)&ctx->resconf->nameserver[i];
if (ns_addr->sa_family==AF_UNSPEC) break;
getnameinfo(ns_addr,sizeof(struct sockaddr_storage),ip,sizeof(ip),serv,sizeof(serv),NI_NUMERICHOST|NI_NUMERICSERV);
belle_sip_message("\t%s",ip);
if (ns_addr->sa_family==AF_INET6) using_ipv6=TRUE;
}
ctx->resconf->iface.ss_family=using_ipv6 ? AF_INET6 : AF_INET;
}else{
belle_sip_error("Error loading dns server addresses.");
}
......
......@@ -115,6 +115,8 @@
#include <resolv.h>
#endif
//#define DNS_DEBUG 1
/*
* C O M P I L E R A N N O T A T I O N S
*
......@@ -184,7 +186,7 @@
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int dns_debug = 0;
int dns_debug = 2;
#if DNS_DEBUG
......@@ -5790,7 +5792,17 @@ int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *
if ((error = dns_so_newanswer(so, DNS_SO_MINBUF)))
goto syerr;
memcpy(&so->remote, host, dns_sa_len(host));
if (so->local.ss_family==AF_INET6 && host->sa_family==AF_INET){
uint32_t *addr=(uint32_t*)dns_sa_addr(AF_INET6,&so->remote);
/* add v4mapping*/
so->remote.ss_family=AF_INET6;
addr[0]=0;
addr[1]=0;
addr[2]=ntohl(0xffff);
addr[3]=((struct sockaddr_in*)host)->sin_addr.s_addr;
*dns_sa_port(AF_INET6,&so->remote)=((struct sockaddr_in*)host)->sin_port;
}else memcpy(&so->remote, host, dns_sa_len(host));
so->query = Q;
so->qout = 0;
......@@ -5916,31 +5928,47 @@ static int dns_so_tcp_recv(struct dns_socket *so) {
#pragma clang diagnostic pop
#endif
#define USE_CONNECT 0
int dns_so_check(struct dns_socket *so) {
int error;
long n;
#if !USE_CONNECT
struct sockaddr_storage saddr;
socklen_t slen=sizeof(saddr);
#endif
retry:
switch (so->state) {
case DNS_SO_UDP_INIT:
so->state++;
case DNS_SO_UDP_CONN:
#if USE_CONNECT
if (0 != connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote)))
goto soerr;
#endif
so->state++;
case DNS_SO_UDP_SEND:
#if USE_CONNECT
if (0 > (n = send(so->udp, (void *)so->query->data, so->query->end, 0)))
goto soerr;
#else
if (0 > (n = sendto(so->udp, (void *)so->query->data, so->query->end, 0, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))))
goto soerr;
#endif
so->stat.udp.sent.bytes += n;
so->stat.udp.sent.count++;
so->state++;
case DNS_SO_UDP_RECV:
#if USE_CONNECT
if (0 > (n = recv(so->udp, (void *)so->answer->data, so->answer->size, 0)))
goto soerr;
#else
if (0 > (n = recvfrom(so->udp, (void *)so->answer->data, so->answer->size, 0, (struct sockaddr*)&saddr,&slen)))
goto soerr;
/*TODO check if saddr matches one of the DNS server we previously sent a question, for security*/
#endif
so->stat.udp.rcvd.bytes += n;
so->stat.udp.rcvd.count++;
......@@ -6609,7 +6637,7 @@ static int dns_res_exec(struct dns_resolver *R) {
char host[DNS_D_MAXNAME + 1];
size_t len;
struct dns_rr rr;
struct sockaddr_in sin;
struct sockaddr_storage saddr={0};
int error;
struct dns_rr_i dns_rr_it;
int dns_grep_error;
......@@ -6882,7 +6910,7 @@ exec:
goto error;
F->hints_j.name = host;
F->hints_j.type = DNS_T_A;
F->hints_j.type = DNS_T_ALL;
F->hints_j.section = DNS_S_ALL & ~DNS_S_QD;
if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
......@@ -6892,23 +6920,28 @@ exec:
goto(R->sp, DNS_R_FOREACH_NS);
}
sin.sin_family = AF_INET;
saddr.ss_family = rr.type==DNS_T_AAAA ? AF_INET6 : AF_INET;
if ((error = dns_a_parse((struct dns_a *)&sin.sin_addr, &rr, F->hints)))
goto error;
if (saddr.ss_family==AF_INET){
if ((error = dns_a_parse((struct dns_a *)dns_sa_addr(saddr.ss_family, &saddr), &rr, F->hints)))
goto error;
}else{
if ((error = dns_aaaa_parse((struct dns_aaaa *)dns_sa_addr(saddr.ss_family, &saddr), &rr, F->hints)))
goto error;
}
if (R->sp == 0)
sin.sin_port = dns_hints_port(R->hints, AF_INET, (struct sockaddr *)&sin.sin_addr);
else
sin.sin_port = htons(53);
*dns_sa_port(saddr.ss_family, &saddr) = (R->sp == 0) ? dns_hints_port(R->hints, saddr.ss_family, (struct sockaddr *)&saddr) : htons(53);
if (DNS_DEBUG) {
char addr[INET_ADDRSTRLEN + 1];
dns_a_print(addr, sizeof addr, (struct dns_a *)&sin.sin_addr);
if (saddr.ss_family==AF_INET)
dns_a_print(addr, sizeof addr, (struct dns_a *)dns_sa_addr(saddr.ss_family, &saddr));
else dns_aaaa_print(addr, sizeof addr, (struct dns_aaaa *)dns_sa_addr(saddr.ss_family, &saddr));
DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", host, addr, R->sp);
}
if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin)))
if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&saddr)))
goto error;
F->state++;
......
......@@ -77,6 +77,7 @@ BELLE_SIP_INSTANCIATE_VPTR(belle_sip_hop_t,belle_sip_object_t,belle_sip_hop_dest
static void belle_sip_stack_destroy(belle_sip_stack_t *stack){
belle_sip_message("stack [%p] destroyed.",stack);
if (stack->dns_user_hosts_file) belle_sip_free(stack->dns_user_hosts_file);
if (stack->dns_resolv_conf) belle_sip_free(stack->dns_resolv_conf);
belle_sip_object_unref(stack->ml);
}
......@@ -197,6 +198,15 @@ void belle_sip_stack_set_dns_user_hosts_file(belle_sip_stack_t *stack, const cha
stack->dns_user_hosts_file = hosts_file?belle_sip_strdup(hosts_file):NULL;
}
const char * belle_sip_stack_get_dns_resolv_conf_file(const belle_sip_stack_t *stack){
return stack->dns_resolv_conf;
}
void belle_sip_stack_set_dns_resolv_conf_file(belle_sip_stack_t *stack, const char *resolv_conf_file){
if (stack->dns_resolv_conf) belle_sip_free(stack->dns_resolv_conf);
stack->dns_resolv_conf = resolv_conf_file?belle_sip_strdup(resolv_conf_file):NULL;
}
const char* belle_sip_version_to_string() {
#ifdef BELLESIP_VERSION
return BELLESIP_VERSION;
......
......@@ -407,7 +407,120 @@ static void no_query_needed(void) {
destroy_endpoint(client);
}
static void set_custom_resolv_conf(belle_sip_stack_t *stack, const char *ns[3]){
FILE *f=fopen("tmp_resolv.conf","w");
CU_ASSERT_PTR_NOT_NULL(f);
if (f){
int i;
for (i=0; i<3; ++i){
if (ns[i]!=NULL){
fprintf(f,"nameserver %s\n",ns[i]);
}
}
fclose(f);
}
belle_sip_stack_set_dns_resolv_conf_file(stack,"tmp_resolv.conf");
}
static void dns_fallback(void) {
struct addrinfo *ai;
int timeout;
endpoint_t *client = create_endpoint();
const char *nameservers[]={
"94.23.19.176", /*linphone.org ; this is not a name server, it will not respond*/
"8.8.8.8", /* public nameserver, should work*/
NULL
};
CU_ASSERT_PTR_NOT_NULL_FATAL(client);
timeout = belle_sip_stack_get_dns_timeout(client->stack);
set_custom_resolv_conf(client->stack,nameservers);
client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client);
CU_ASSERT_NOT_EQUAL(client->resolver_ctx, NULL);
CU_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout));
CU_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL);
if (client->ai_list) {
struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->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);
}
}
destroy_endpoint(client);
}
static void ipv6_dns_server(void) {
struct addrinfo *ai;
int timeout;
endpoint_t *client;
const char *nameservers[]={
"2a01:e00::2",
NULL
};
if (!belle_sip_tester_ipv6_available()){
belle_sip_warning("Test skipped, IPv6 connectivity not available.");
return;
}
client = create_endpoint();
CU_ASSERT_PTR_NOT_NULL_FATAL(client);
timeout = belle_sip_stack_get_dns_timeout(client->stack);
set_custom_resolv_conf(client->stack,nameservers);
client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client);
CU_ASSERT_NOT_EQUAL(client->resolver_ctx, NULL);
CU_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout));
CU_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL);
if (client->ai_list) {
struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->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);
}
}
destroy_endpoint(client);
}
static void ipv4_and_ipv6_dns_server(void) {
struct addrinfo *ai;
int timeout;
endpoint_t *client;
const char *nameservers[]={
"8.8.8.8",
"2a01:e00::2",
NULL
};
if (!belle_sip_tester_ipv6_available()){
belle_sip_warning("Test skipped, IPv6 connectivity not available.");
return;
}
client = create_endpoint();
CU_ASSERT_PTR_NOT_NULL_FATAL(client);
timeout = belle_sip_stack_get_dns_timeout(client->stack);
set_custom_resolv_conf(client->stack,nameservers);
client->resolver_ctx = belle_sip_stack_resolve_a(client->stack, IPV4_SIP_DOMAIN, SIP_PORT, AF_INET, a_resolve_done, client);
CU_ASSERT_NOT_EQUAL(client->resolver_ctx, NULL);
CU_ASSERT_TRUE(wait_for(client->stack, &client->resolve_done, 1, timeout));
CU_ASSERT_PTR_NOT_EQUAL(client->ai_list, NULL);
if (client->ai_list) {
struct sockaddr_in *sock_in = (struct sockaddr_in *)client->ai_list->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);
}
}
destroy_endpoint(client);
}
test_t resolver_tests[] = {
{ "A query (IPv4)", ipv4_a_query },
......@@ -424,6 +537,9 @@ test_t resolver_tests[] = {
{ "SRV + A query with no SRV result", srv_a_query_no_srv_result },
{ "Local SRV+A query", local_full_query },
{ "No query needed", no_query_needed },
{ "DNS fallback", dns_fallback },
{ "IPv6 DNS server", ipv6_dns_server },
{ "IPv4 and v6 DNS servers", ipv4_and_ipv6_dns_server }
};
test_suite_t resolver_test_suite = {
......
......@@ -99,8 +99,33 @@ const char * belle_sip_tester_test_name(const char *suite_name, int test_index)
return test_suite[suite_index]->tests[test_index].name;
}
static int _belle_sip_tester_ipv6_available(void){
struct addrinfo *ai=belle_sip_ip_address_to_addrinfo(AF_INET6,"2a01:e00::2",53);
if (ai){
struct sockaddr_storage ss;
struct addrinfo src;
socklen_t slen=sizeof(ss);
char localip[128];
int port=0;
belle_sip_get_src_addr_for(ai->ai_addr,ai->ai_addrlen,(struct sockaddr*) &ss,&slen,4444);
src.ai_addr=(struct sockaddr*) &ss;
src.ai_addrlen=slen;
belle_sip_addrinfo_to_ip(&src,localip, sizeof(localip),&port);
freeaddrinfo(ai);
return strcmp(localip,"::1")!=0;
}
return FALSE;
}
static int ipv6_available=0;
int belle_sip_tester_ipv6_available(void){
return ipv6_available;
}
void belle_sip_tester_init(void) {
belle_sip_object_enable_marshal_check(TRUE);
ipv6_available=_belle_sip_tester_ipv6_available();
add_test_suite(&cast_test_suite);
add_test_suite(&uri_test_suite);
add_test_suite(&headers_test_suite);
......@@ -114,6 +139,8 @@ void belle_sip_tester_init(void) {
add_test_suite(&refresher_test_suite);
}
void belle_sip_tester_uninit(void) {
if (test_suite != NULL) {
free(test_suite);
......
......@@ -56,7 +56,7 @@ extern test_suite_t register_test_suite;
extern test_suite_t dialog_test_suite;
extern test_suite_t refresher_test_suite;
extern int belle_sip_tester_ipv6_available(void);
extern int belle_sip_tester_nb_test_suites(void);
extern int belle_sip_tester_nb_tests(const char *suite_name);
extern const char * belle_sip_tester_test_suite_name(int suite_index);
......
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