diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 90083418d..7f58094e2 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -1154,6 +1154,10 @@ SalResolverContext * sal_resolve_a(Sal* sal, const char *name, int port, int fam return (SalResolverContext*)belle_sip_stack_resolve_a(sal->stack,name,port,family,(belle_sip_resolver_callback_t)cb,data); } +SalResolverContext * sal_resolve(Sal *sal, const char *service, const char *transport, const char *name, int port, int family, SalResolverCallback cb, void *data) { + return (SalResolverContext *)belle_sip_stack_resolve(sal->stack, service, transport, name, port, family, (belle_sip_resolver_callback_t)cb, data); +} + /* void sal_resolve_cancel(Sal *sal, SalResolverContext* ctx){ belle_sip_stack_resolve_cancel(sal->stack,ctx); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 9e44ac9dd..bb44d5da3 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -759,7 +759,7 @@ static void net_config_read (LinphoneCore *lc) nat_policy_ref = lp_config_get_string(lc->config, "net", "nat_policy_ref", NULL); if (nat_policy_ref != NULL) { - lc->nat_policy = linphone_nat_policy_new_from_config(lc->config, nat_policy_ref); + lc->nat_policy = linphone_core_create_nat_policy_from_config(lc, nat_policy_ref); } lc->net_conf.nat_address_ip = NULL; @@ -5073,31 +5073,18 @@ void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf) linphone_call_send_dtmf(call, dtmf); } -void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){ - if (lc->net_conf.stun_server!=NULL) - ms_free(lc->net_conf.stun_server); - if (server) - lc->net_conf.stun_server=ms_strdup(server); - else lc->net_conf.stun_server=NULL; - - /* each time the stun server is changed, we must clean the resolved cached addrinfo*/ - if (lc->net_conf.stun_addrinfo){ - freeaddrinfo(lc->net_conf.stun_addrinfo); - lc->net_conf.stun_addrinfo=NULL; - } - /*if a stun server is set, we must request asynchronous resolution immediately to be ready for call*/ - if (lc->net_conf.stun_server){ - linphone_core_resolve_stun_server(lc); - } - +void linphone_core_set_stun_server(LinphoneCore *lc, const char *server) { if (lc->nat_policy != NULL) - linphone_nat_policy_set_stun_server(lc->nat_policy, lc->net_conf.stun_server); - else if (linphone_core_ready(lc)) - lp_config_set_string(lc->config,"net","stun_server",lc->net_conf.stun_server); + linphone_nat_policy_set_stun_server(lc->nat_policy, server); + else + lp_config_set_string(lc->config, "net", "stun_server", server); } const char * linphone_core_get_stun_server(const LinphoneCore *lc){ - return lc->net_conf.stun_server; + if (lc->nat_policy != NULL) + return linphone_nat_policy_get_stun_server(lc->nat_policy); + else + return lp_config_get_string(lc->config, "net", "stun_server", NULL); } @@ -5173,7 +5160,7 @@ void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy nat_policy = linphone_nat_policy_ref(lc->nat_policy); linphone_nat_policy_clear(nat_policy); } else { - nat_policy = linphone_nat_policy_new(); + nat_policy = linphone_core_create_nat_policy(lc); } switch (pol) { @@ -6364,13 +6351,6 @@ void net_config_uninit(LinphoneCore *lc) { net_config_t *config=&lc->net_conf; - if (config->stun_server!=NULL){ - ms_free(config->stun_server); - } - if (config->stun_addrinfo){ - freeaddrinfo(config->stun_addrinfo); - config->stun_addrinfo=NULL; - } if (config->nat_address!=NULL){ lp_config_set_string(lc->config,"net","nat_address",config->nat_address); ms_free(lc->net_conf.nat_address); @@ -6381,7 +6361,7 @@ void net_config_uninit(LinphoneCore *lc) lp_config_set_int(lc->config,"net","mtu",config->mtu); if (lc->nat_policy != NULL) { lp_config_set_string(lc->config, "net", "nat_policy_ref", lc->nat_policy->ref); - linphone_nat_policy_save_to_config(lc->nat_policy, lc->config); + linphone_nat_policy_save_to_config(lc->nat_policy); } } diff --git a/coreapi/misc.c b/coreapi/misc.c index 65bf11ad5..33ef72146 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -584,16 +584,20 @@ static void stun_server_resolved(LinphoneCore *lc, const char *name, struct addr } void linphone_core_resolve_stun_server(LinphoneCore *lc){ - /* - * WARNING: stun server resolution only done in IPv4. - * TODO: use IPv6 resolution if linphone_core_ipv6_enabled()==TRUE and use V4Mapped addresses for ICE gathering. - */ - const char *server=lc->net_conf.stun_server; - if (lc->sal && server && !lc->net_conf.stun_res){ - char host[NI_MAXHOST]; - int port=3478; - linphone_parse_host_port(server,host,sizeof(host),&port); - lc->net_conf.stun_res=sal_resolve_a(lc->sal,host,port,AF_INET,(SalResolverCallback)stun_server_resolved,lc); + if (lc->nat_policy != NULL) { + linphone_nat_policy_resolve_stun_server(lc->nat_policy); + } else { + /* + * WARNING: stun server resolution only done in IPv4. + * TODO: use IPv6 resolution if linphone_core_ipv6_enabled()==TRUE and use V4Mapped addresses for ICE gathering. + */ + const char *server=linphone_core_get_stun_server(lc); + if (lc->sal && server && !lc->net_conf.stun_res){ + char host[NI_MAXHOST]; + int port=3478; + linphone_parse_host_port(server,host,sizeof(host),&port); + lc->net_conf.stun_res=sal_resolve_a(lc->sal,host,port,AF_INET,(SalResolverCallback)stun_server_resolved,lc); + } } } @@ -609,18 +613,22 @@ void linphone_core_resolve_stun_server(LinphoneCore *lc){ * changed. **/ const struct addrinfo *linphone_core_get_stun_server_addrinfo(LinphoneCore *lc){ - const char *server=linphone_core_get_stun_server(lc); - if (server){ - int wait_ms=0; - int wait_limit=1000; - linphone_core_resolve_stun_server(lc); - while (!lc->net_conf.stun_addrinfo && lc->net_conf.stun_res!=NULL && wait_mssal); - ms_usleep(50000); - wait_ms+=50; + if (lc->nat_policy != NULL) { + return linphone_nat_policy_get_stun_server_addrinfo(lc->nat_policy); + } else { + const char *server=linphone_core_get_stun_server(lc); + if (server){ + int wait_ms=0; + int wait_limit=1000; + linphone_core_resolve_stun_server(lc); + while (!lc->net_conf.stun_addrinfo && lc->net_conf.stun_res!=NULL && wait_mssal); + ms_usleep(50000); + wait_ms+=50; + } } + return lc->net_conf.stun_addrinfo; } - return lc->net_conf.stun_addrinfo; } void linphone_core_enable_forced_ice_relay(LinphoneCore *lc, bool_t enable) { diff --git a/coreapi/nat_policy.c b/coreapi/nat_policy.c index 926900461..f28bf7985 100644 --- a/coreapi/nat_policy.c +++ b/coreapi/nat_policy.c @@ -21,11 +21,33 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "private.h" +static LinphoneNatPolicy * _linphone_nat_policy_new_with_ref(LinphoneCore *lc, const char *ref) { + LinphoneNatPolicy *policy = belle_sip_object_new(LinphoneNatPolicy); + belle_sip_object_ref(policy); + policy->lc = lc; + policy->ref = belle_sip_strdup(ref); + return policy; +} + +static LinphoneNatPolicy * linphone_nat_policy_new(LinphoneCore *lc) { + char ref[17] = { 0 }; + belle_sip_random_token(ref, 16); + return _linphone_nat_policy_new_with_ref(lc, ref); +} + static void linphone_nat_policy_destroy(LinphoneNatPolicy *policy) { if (policy->ref) belle_sip_free(policy->ref); if (policy->stun_server) belle_sip_free(policy->stun_server); + if (policy->stun_addrinfo) freeaddrinfo(policy->stun_addrinfo); } +static bool_t linphone_nat_policy_stun_server_activated(LinphoneNatPolicy *policy) { + const char *server = linphone_nat_policy_get_stun_server(policy); + return (server != NULL) && (server[0] != '\0') + && ((linphone_nat_policy_stun_enabled(policy) == TRUE) || (linphone_nat_policy_turn_enabled(policy) == TRUE)); +} + + BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneNatPolicy); @@ -37,54 +59,6 @@ BELLE_SIP_INSTANCIATE_VPTR(LinphoneNatPolicy, belle_sip_object_t, ); -static LinphoneNatPolicy * _linphone_nat_policy_new_with_ref(const char *ref) { - LinphoneNatPolicy *policy = belle_sip_object_new(LinphoneNatPolicy); - belle_sip_object_ref(policy); - policy->ref = belle_sip_strdup(ref); - return policy; -} - -LinphoneNatPolicy * linphone_nat_policy_new(void) { - char ref[17] = { 0 }; - belle_sip_random_token(ref, 16); - return _linphone_nat_policy_new_with_ref(ref); -} - -LinphoneNatPolicy * linphone_nat_policy_new_from_config(LpConfig *config, const char *ref) { - LinphoneNatPolicy *policy = NULL; - char *section; - int index; - bool_t finished = FALSE; - - for (index = 0; finished != TRUE; index++) { - section = belle_sip_strdup_printf("nat_policy_%i", index); - if (lp_config_has_section(config, section)) { - const char *config_ref = lp_config_get_string(config, section, "ref", NULL); - if ((config_ref != NULL) && (strcmp(config_ref, ref) == 0)) { - const char *server = lp_config_get_string(config, section, "stun_server", NULL); - MSList *l = lp_config_get_string_list(config, section, "protocols", NULL); - policy = _linphone_nat_policy_new_with_ref(ref); - if (server != NULL) linphone_nat_policy_set_stun_server(policy, server); - if (l != NULL) { - bool_t upnp_enabled = FALSE; - MSList *elem; - for (elem = l; elem != NULL; elem = elem->next) { - const char *value = (const char *)elem->data; - if (strcmp(value, "stun") == 0) linphone_nat_policy_enable_stun(policy, TRUE); - else if (strcmp(value, "turn") == 0) linphone_nat_policy_enable_turn(policy, TRUE); - else if (strcmp(value, "ice") == 0) linphone_nat_policy_enable_ice(policy, TRUE); - else if (strcmp(value, "upnp") == 0) upnp_enabled = TRUE; - } - if (upnp_enabled) linphone_nat_policy_enable_upnp(policy, TRUE); - } - finished = TRUE; - } - } else finished = TRUE; - belle_sip_free(section); - } - return policy; -} - static void _linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy, LpConfig *config, int index) { char *section; MSList *l = NULL; @@ -103,7 +77,8 @@ static void _linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy, belle_sip_free(section); } -void linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy, LpConfig *config) { +void linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy) { + LpConfig *config = policy->lc->config; char *section; int index; bool_t finished = FALSE; @@ -195,11 +170,114 @@ const char * linphone_nat_policy_get_stun_server(const LinphoneNatPolicy *policy } void linphone_nat_policy_set_stun_server(LinphoneNatPolicy *policy, const char *stun_server) { + char *new_stun_server = NULL; + + if (stun_server != NULL) new_stun_server = belle_sip_strdup(stun_server); if (policy->stun_server != NULL) { belle_sip_free(policy->stun_server); policy->stun_server = NULL; } - if (stun_server != NULL) { - policy->stun_server = belle_sip_strdup(stun_server); + if (new_stun_server != NULL) { + policy->stun_server = new_stun_server; + linphone_nat_policy_resolve_stun_server(policy); } } + +static void stun_server_resolved(LinphoneNatPolicy *policy, const char *name, struct addrinfo *addrinfo) { + if (policy->stun_addrinfo) { + belle_sip_freeaddrinfo(policy->stun_addrinfo); + policy->stun_addrinfo = NULL; + } + if (addrinfo) { + ms_message("Stun server resolution successful."); + } else { + ms_warning("Stun server resolution failed."); + } + policy->stun_addrinfo = addrinfo; + policy->stun_resolver_context = NULL; +} + +void linphone_nat_policy_resolve_stun_server(LinphoneNatPolicy *policy) { + const char *service = NULL; + + /* + * WARNING: stun server resolution only done in IPv4. + * TODO: use IPv6 resolution if linphone_core_ipv6_enabled()==TRUE and use V4Mapped addresses for ICE gathering. + */ + if (linphone_nat_policy_stun_server_activated(policy) + && (policy->lc->sal != NULL) + && !policy->stun_resolver_context) { + char host[NI_MAXHOST]; + int port = 3478; + linphone_parse_host_port(policy->stun_server, host, sizeof(host), &port); + if (linphone_nat_policy_turn_enabled(policy)) service = "turn"; + else if (linphone_nat_policy_stun_enabled(policy)) service = "stun"; + if (service != NULL) { + policy->stun_resolver_context = sal_resolve(policy->lc->sal, service, "udp", host, port, AF_INET, (SalResolverCallback)stun_server_resolved, policy); + } + } +} + +const struct addrinfo * linphone_nat_policy_get_stun_server_addrinfo(LinphoneNatPolicy *policy) { + /* + * It is critical not to block for a long time if it can't be resolved, otherwise this stucks the main thread when making a call. + * On the contrary, a fully asynchronous call initiation is complex to develop. + * The compromise is then: + * - have a cache of the stun server addrinfo + * - this cached value is returned when it is non-null + * - an asynchronous resolution is asked each time this function is called to ensure frequent refreshes of the cached value. + * - if no cached value exists, block for a short time; this case must be unprobable because the resolution will be asked each + * time the stun server value is changed. + */ + if (linphone_nat_policy_stun_server_activated(policy)) { + int wait_ms = 0; + int wait_limit = 1000; + linphone_nat_policy_resolve_stun_server(policy); + while ((policy->stun_addrinfo == NULL) && (policy->stun_resolver_context != NULL) && (wait_ms < wait_limit)) { + sal_iterate(policy->lc->sal); + ms_usleep(50000); + wait_ms += 50; + } + } + return policy->stun_addrinfo; +} + +LinphoneNatPolicy * linphone_core_create_nat_policy(LinphoneCore *lc) { + return linphone_nat_policy_new(lc); +} + +LinphoneNatPolicy * linphone_core_create_nat_policy_from_config(LinphoneCore *lc, const char *ref) { + LpConfig *config = lc->config; + LinphoneNatPolicy *policy = NULL; + char *section; + int index; + bool_t finished = FALSE; + + for (index = 0; finished != TRUE; index++) { + section = belle_sip_strdup_printf("nat_policy_%i", index); + if (lp_config_has_section(config, section)) { + const char *config_ref = lp_config_get_string(config, section, "ref", NULL); + if ((config_ref != NULL) && (strcmp(config_ref, ref) == 0)) { + const char *server = lp_config_get_string(config, section, "stun_server", NULL); + MSList *l = lp_config_get_string_list(config, section, "protocols", NULL); + policy = _linphone_nat_policy_new_with_ref(lc, ref); + if (server != NULL) linphone_nat_policy_set_stun_server(policy, server); + if (l != NULL) { + bool_t upnp_enabled = FALSE; + MSList *elem; + for (elem = l; elem != NULL; elem = elem->next) { + const char *value = (const char *)elem->data; + if (strcmp(value, "stun") == 0) linphone_nat_policy_enable_stun(policy, TRUE); + else if (strcmp(value, "turn") == 0) linphone_nat_policy_enable_turn(policy, TRUE); + else if (strcmp(value, "ice") == 0) linphone_nat_policy_enable_ice(policy, TRUE); + else if (strcmp(value, "upnp") == 0) upnp_enabled = TRUE; + } + if (upnp_enabled) linphone_nat_policy_enable_upnp(policy, TRUE); + } + finished = TRUE; + } + } else finished = TRUE; + belle_sip_free(section); + } + return policy; +} diff --git a/coreapi/nat_policy.h b/coreapi/nat_policy.h index b46d7129a..96147ce67 100644 --- a/coreapi/nat_policy.h +++ b/coreapi/nat_policy.h @@ -37,12 +37,6 @@ extern "C" { typedef struct _LinphoneNatPolicy LinphoneNatPolicy; -/** - * Create a new LinphoneNatPolicy object with every policies being disabled. - * @return A new LinphoneNatPolicy object. - */ -LINPHONE_PUBLIC LinphoneNatPolicy * linphone_nat_policy_new(void); - /** * Acquire a reference to the LinphoneNatPolicy object. * @param[in] policy LinphoneNatPolicy object. @@ -152,6 +146,35 @@ LINPHONE_PUBLIC const char * linphone_nat_policy_get_stun_server(const LinphoneN */ LINPHONE_PUBLIC void linphone_nat_policy_set_stun_server(LinphoneNatPolicy *policy, const char *stun_server); +/** + * Start a STUN server DNS resolution. + * @param[in] policy LinphoneNatPolicy object + */ +LINPHONE_PUBLIC void linphone_nat_policy_resolve_stun_server(LinphoneNatPolicy *policy); + +/** + * Get the addrinfo representation of the STUN server address. + * WARNING: This function may block for up to 1 second. + * @param[in] policy LinphoneNatPolicy object + * @return addrinfo representation of the STUN server address. + */ +LINPHONE_PUBLIC const struct addrinfo * linphone_nat_policy_get_stun_server_addrinfo(LinphoneNatPolicy *policy); + +/** + * Create a new LinphoneNatPolicy object with every policies being disabled. + * @param[in] lc LinphoneCore object + * @return A new LinphoneNatPolicy object. + */ +LINPHONE_PUBLIC LinphoneNatPolicy * linphone_core_create_nat_policy(LinphoneCore *lc); + +/** + * Create a new LinphoneNatPolicy by reading the config of a LinphoneCore according to the passed ref. + * @param[in] lc LinphoneCore object + * @param[in] ref The reference of a NAT policy in the config of the LinphoneCore + * @return A new LinphoneNatPolicy object. + */ +LINPHONE_PUBLIC LinphoneNatPolicy * linphone_core_create_nat_policy_from_config(LinphoneCore *lc, const char *ref); + /** * @} */ diff --git a/coreapi/private.h b/coreapi/private.h index 881d5ce6a..6d36cf6ed 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -437,6 +437,7 @@ LINPHONE_PUBLIC MSList* linphone_core_fetch_friends_from_db(LinphoneCore *lc, Li LINPHONE_PUBLIC MSList* linphone_core_fetch_friends_lists_from_db(LinphoneCore *lc); LINPHONE_PUBLIC LinphoneFriendListStatus linphone_friend_list_import_friend(LinphoneFriendList *list, LinphoneFriend *lf, bool_t synchronize); +int linphone_parse_host_port(const char *input, char *host, size_t hostlen, int *port); int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, socklen_t *socklen, int default_port); bool_t host_has_ipv6_network(void); @@ -799,7 +800,6 @@ typedef struct net_config { char *nat_address; /* may be IP or host name */ char *nat_address_ip; /* ip translated from nat_address */ - char *stun_server; struct addrinfo *stun_addrinfo; SalResolverContext * stun_res; int download_bw; @@ -1190,6 +1190,9 @@ BELLE_SIP_DECLARE_VPTR(LinphoneBuffer); struct _LinphoneNatPolicy { belle_sip_object_t base; void *user_data; + LinphoneCore *lc; + SalResolverContext *stun_resolver_context; + struct addrinfo *stun_addrinfo; char *stun_server; char *ref; bool_t stun_enabled; @@ -1200,8 +1203,7 @@ struct _LinphoneNatPolicy { BELLE_SIP_DECLARE_VPTR(LinphoneNatPolicy); -LinphoneNatPolicy * linphone_nat_policy_new_from_config(LpConfig *config, const char *ref); -void linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy, LpConfig *config); +void linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy); /***************************************************************************** diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 2f0532455..0231d1ced 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -1358,7 +1358,7 @@ void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyC if (cfg->nat_policy != NULL) { lp_config_set_string(config, key, "nat_policy_ref", cfg->nat_policy->ref); - linphone_nat_policy_save_to_config(cfg->nat_policy, config); + linphone_nat_policy_save_to_config(cfg->nat_policy); } } @@ -1423,7 +1423,7 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LinphoneCore* lc nat_policy_ref = lp_config_get_string(config, key, "nat_policy_ref", NULL); if (nat_policy_ref != NULL) { - cfg->nat_policy = linphone_nat_policy_new_from_config(config, nat_policy_ref); + cfg->nat_policy = linphone_core_create_nat_policy_from_config(lc, nat_policy_ref); } return cfg; diff --git a/include/sal/sal.h b/include/sal/sal.h index 057c9d1b5..0fa288ea0 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -807,6 +807,7 @@ typedef void (*SalResolverCallback)(void *data, const char *name, struct addrinf typedef struct SalResolverContext SalResolverContext; LINPHONE_PUBLIC SalResolverContext * sal_resolve_a(Sal* sal, const char *name, int port, int family, SalResolverCallback cb, void *data); +LINPHONE_PUBLIC SalResolverContext * sal_resolve(Sal *sal, const char *service, const char *transport, const char *name, int port, int family, SalResolverCallback cb, void *data); //void sal_resolve_cancel(Sal *sal, SalResolverContext *ctx); SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value);