diff --git a/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt index 564d80d51..ea97ad5f3 100644 --- a/coreapi/CMakeLists.txt +++ b/coreapi/CMakeLists.txt @@ -48,6 +48,7 @@ set(LINPHONE_HEADER_FILES linphone_tunnel.h lpc2xml.h lpconfig.h + nat_policy.h ringtoneplayer.h sipsetup.h sqlite3_bctbx_vfs.h @@ -102,6 +103,7 @@ set(LINPHONE_SOURCE_FILES_C lsd.c message_storage.c misc.c + nat_policy.c offeranswer.c offeranswer.h player.c diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index ae407244b..97ba75460 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -42,6 +42,7 @@ linphone_include_HEADERS=\ linphone_tunnel.h \ lpc2xml.h \ lpconfig.h \ + nat_policy.h \ sipsetup.h \ sqlite3_bctbx_vfs.h \ xml2lpc.h \ @@ -81,6 +82,7 @@ liblinphone_la_SOURCES=\ lsd.c \ message_storage.c \ misc.c \ + nat_policy.c \ offeranswer.c offeranswer.h\ player.c \ presence.c \ diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 75071f1bc..acaaf6c0a 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/linphone_proxy_config.h b/coreapi/linphone_proxy_config.h index 9484589df..722cae051 100644 --- a/coreapi/linphone_proxy_config.h +++ b/coreapi/linphone_proxy_config.h @@ -547,6 +547,24 @@ LINPHONE_PUBLIC const char * linphone_proxy_config_get_ref_key(const LinphonePro **/ LINPHONE_PUBLIC void linphone_proxy_config_set_ref_key(LinphoneProxyConfig *cfg, const char *refkey); +/** + * Get The policy that is used to pass through NATs/firewalls when using this proxy config. + * If it is set to NULL, the default NAT policy from the core will be used instead. + * @param[in] cfg #LinphoneProxyConfig object + * @return LinphoneNatPolicy object in use. + * @see linphone_core_get_nat_policy() + */ +LINPHONE_PUBLIC LinphoneNatPolicy * linphone_proxy_config_get_nat_policy(const LinphoneProxyConfig *cfg); + +/** + * Set the policy to use to pass through NATs/firewalls when using this proxy config. + * If it is set to NULL, the default NAT policy from the core will be used instead. + * @param[in] cfg #LinphoneProxyConfig object + * @param[in] policy LinphoneNatPolicy object + * @see linphone_core_set_nat_policy() + */ +LINPHONE_PUBLIC void linphone_proxy_config_set_nat_policy(LinphoneProxyConfig *cfg, LinphoneNatPolicy *policy); + /** * @} */ diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 1cc34c2a6..21ba67ff9 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -1311,7 +1311,7 @@ static void linphone_call_compute_streams_indexes(LinphoneCall *call, const SalM LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ LinphoneCall *call = belle_sip_object_new(LinphoneCall); SalMediaDescription *md; - LinphoneFirewallPolicy fpol; + LinphoneNatPolicy *nat_policy = NULL; int i; call->dir=LinphoneCallIncoming; @@ -1386,28 +1386,26 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro } } - fpol=linphone_core_get_firewall_policy(call->core); - /*create the ice session now if ICE is required*/ - if (fpol==LinphonePolicyUseIce){ + if (call->dest_proxy != NULL) nat_policy = linphone_proxy_config_get_nat_policy(call->dest_proxy); + if (nat_policy == NULL) nat_policy = linphone_core_get_nat_policy(call->core); + if ((nat_policy != NULL) && linphone_nat_policy_ice_enabled(nat_policy)) { + /* Create the ice session now if ICE is required */ if (md){ linphone_call_create_ice_session(call, IR_Controlled); }else{ - fpol=LinphonePolicyNoFirewall; + nat_policy = NULL; ms_warning("ICE not supported for incoming INVITE without SDP."); } } /*reserve the sockets immediately*/ linphone_call_init_media_streams(call); - switch (fpol) { - case LinphonePolicyUseIce: + if (nat_policy != NULL) { + if (linphone_nat_policy_ice_enabled(nat_policy)) { call->defer_notify_incoming = linphone_call_prepare_ice(call,TRUE) == 1; - break; - case LinphonePolicyUseStun: + } else if (linphone_nat_policy_stun_enabled(nat_policy)) { call->ping_time=linphone_core_run_stun_tests(call->core,call); - /* No break to also destroy ice session in this case. */ - break; - case LinphonePolicyUseUpnp: + } else if (linphone_nat_policy_upnp_enabled(nat_policy)) { #ifdef BUILD_UPNP if(!lc->rtp_conf.disable_upnp) { call->upnp_session = linphone_upnp_session_new(call); @@ -1419,9 +1417,7 @@ LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *fro } } #endif //BUILD_UPNP - break; - default: - break; + } } discover_mtu(lc,linphone_address_get_domain(from)); @@ -1438,11 +1434,11 @@ void linphone_call_free_media_resources(LinphoneCall *call){ int i; linphone_call_stop_media_streams(call); + linphone_call_delete_upnp_session(call); + linphone_call_delete_ice_session(call); for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; ++i){ ms_media_stream_sessions_uninit(&call->sessions[i]); } - linphone_call_delete_upnp_session(call); - linphone_call_delete_ice_session(call); linphone_call_stats_uninit(&call->stats[LINPHONE_CALL_STATS_AUDIO]); linphone_call_stats_uninit(&call->stats[LINPHONE_CALL_STATS_VIDEO]); linphone_call_stats_uninit(&call->stats[LINPHONE_CALL_STATS_TEXT]); @@ -2466,13 +2462,15 @@ void linphone_call_init_audio_stream(LinphoneCall *call){ /* init zrtp even if we didn't explicitely set it, just in case peer offers it */ if (ms_zrtp_available()) { + char *uri = linphone_address_as_string_uri_only((call->dir==LinphoneCallIncoming) ? call->log->from : call->log->to); MSZrtpParams params; memset(¶ms,0,sizeof(MSZrtpParams)); /*call->current_params.media_encryption will be set later when zrtp is activated*/ params.zid_file=lc->zrtp_secrets_cache; - params.uri= linphone_address_as_string_uri_only((call->dir==LinphoneCallIncoming) ? call->log->from : call->log->to); + params.uri=uri; setZrtpCryptoTypesParameters(¶ms,call->core); audio_stream_enable_zrtp(call->audiostream,¶ms); + if (uri != NULL) ms_free(uri); } media_stream_reclaim_sessions(&audiostream->ms, &call->sessions[call->main_audio_stream_index]); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 52090f148..12f9b87cc 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -759,13 +759,22 @@ static void net_config_read (LinphoneCore *lc) int tmp; const char *tmpstr; LpConfig *config=lc->config; + const char *nat_policy_ref; + + nat_policy_ref = lp_config_get_string(lc->config, "net", "nat_policy_ref", NULL); + if (nat_policy_ref != NULL) { + lc->nat_policy = linphone_core_create_nat_policy_from_config(lc, nat_policy_ref); + } lc->net_conf.nat_address_ip = NULL; tmp=lp_config_get_int(config,"net","download_bw",0); linphone_core_set_download_bandwidth(lc,tmp); tmp=lp_config_get_int(config,"net","upload_bw",0); linphone_core_set_upload_bandwidth(lc,tmp); - linphone_core_set_stun_server(lc,lp_config_get_string(config,"net","stun_server",NULL)); + if (lc->nat_policy == NULL) /* For compatibility, now the STUN server is stored in the NAT policy. */ + linphone_core_set_stun_server(lc,lp_config_get_string(config,"net","stun_server",NULL)); + else + linphone_core_set_stun_server(lc, linphone_nat_policy_get_stun_server(lc->nat_policy)); tmpstr=lp_config_get_string(lc->config,"net","nat_address",NULL); if (tmpstr!=NULL && (strlen(tmpstr)<1)) tmpstr=NULL; linphone_core_set_nat_address(lc,tmpstr); @@ -782,7 +791,8 @@ static void net_config_read (LinphoneCore *lc) linphone_core_enable_dns_srv(lc, tmp); /* This is to filter out unsupported firewall policies */ - linphone_core_set_firewall_policy(lc, linphone_core_get_firewall_policy(lc)); + if (nat_policy_ref == NULL) + linphone_core_set_firewall_policy(lc, linphone_core_get_firewall_policy(lc)); } static void build_sound_devices_table(LinphoneCore *lc){ @@ -5103,29 +5113,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); - } - - if (linphone_core_ready(lc)) - lp_config_set_string(lc->config,"net","stun_server",lc->net_conf.stun_server); +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, 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); } @@ -5194,66 +5193,71 @@ const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc) return lc->net_conf.nat_address_ip; } -void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol){ - const char *policy = "none"; +void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol) { + LinphoneNatPolicy *nat_policy; + char *stun_server = NULL; + char *stun_server_username = NULL; + + if (lc->nat_policy != NULL) { + nat_policy = linphone_nat_policy_ref(lc->nat_policy); + stun_server = ms_strdup(linphone_nat_policy_get_stun_server(nat_policy)); + stun_server_username = ms_strdup(linphone_nat_policy_get_stun_server_username(nat_policy)); + linphone_nat_policy_clear(nat_policy); + } else { + nat_policy = linphone_core_create_nat_policy(lc); + stun_server = ms_strdup(linphone_core_get_stun_server(lc)); + } switch (pol) { default: case LinphonePolicyNoFirewall: - policy = "none"; - break; case LinphonePolicyUseNatAddress: - policy = "nat_address"; break; case LinphonePolicyUseStun: - policy = "stun"; + linphone_nat_policy_enable_stun(nat_policy, TRUE); break; case LinphonePolicyUseIce: - policy = "ice"; + linphone_nat_policy_enable_ice(nat_policy, TRUE); + linphone_nat_policy_enable_stun(nat_policy, TRUE); break; case LinphonePolicyUseUpnp: #ifdef BUILD_UPNP - policy = "upnp"; + linphone_nat_policy_enable_upnp(nat_policy); #else ms_warning("UPNP is not available, reset firewall policy to no firewall"); - pol = LinphonePolicyNoFirewall; - policy = "none"; #endif //BUILD_UPNP break; } -#ifdef BUILD_UPNP - if(pol == LinphonePolicyUseUpnp) { - if(lc->upnp == NULL) { - lc->upnp = linphone_upnp_context_new(lc); - } - } else { - if(lc->upnp != NULL) { - linphone_upnp_context_destroy(lc->upnp); - lc->upnp = NULL; - } + + if (stun_server_username != NULL) { + linphone_nat_policy_set_stun_server_username(nat_policy, stun_server_username); + ms_free(stun_server_username); } - linphone_core_enable_keep_alive(lc, (lc->sip_conf.keepalive_period > 0)); -#endif //BUILD_UPNP - switch(pol) { - case LinphonePolicyUseUpnp: - sal_nat_helper_enable(lc->sal, FALSE); - sal_enable_auto_contacts(lc->sal,FALSE); - sal_use_rport(lc->sal, FALSE); - break; - default: - sal_nat_helper_enable(lc->sal, lp_config_get_int(lc->config,"net","enable_nat_helper",1)); - sal_enable_auto_contacts(lc->sal,TRUE); - sal_use_rport(lc->sal, lp_config_get_int(lc->config,"sip","use_rport",1)); - break; + if (stun_server != NULL) { + linphone_nat_policy_set_stun_server(nat_policy, stun_server); + ms_free(stun_server); } - if (lc->sip_conf.contact) update_primary_contact(lc); - if (linphone_core_ready(lc)) - lp_config_set_string(lc->config,"net","firewall_policy",policy); + linphone_core_set_nat_policy(lc, nat_policy); + linphone_nat_policy_unref(nat_policy); } -LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc){ + +LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc) { const char *policy; + policy = lp_config_get_string(lc->config, "net", "firewall_policy", NULL); - if ((policy == NULL) || (strcmp(policy, "0") == 0)) + if (policy == NULL) { + LinphoneNatPolicy *nat_policy = linphone_core_get_nat_policy(lc); + if (nat_policy == NULL) { + return LinphonePolicyNoFirewall; + } else if (linphone_nat_policy_upnp_enabled(nat_policy)) + return LinphonePolicyUseUpnp; + else if (linphone_nat_policy_ice_enabled(nat_policy)) + return LinphonePolicyUseIce; + else if (linphone_nat_policy_stun_enabled(nat_policy)) + return LinphonePolicyUseStun; + else + return LinphonePolicyNoFirewall; + } else if (strcmp(policy, "0") == 0) return LinphonePolicyNoFirewall; else if ((strcmp(policy, "nat_address") == 0) || (strcmp(policy, "1") == 0)) return LinphonePolicyUseNatAddress; @@ -5267,6 +5271,38 @@ LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc) return LinphonePolicyNoFirewall; } +void linphone_core_set_nat_policy(LinphoneCore *lc, LinphoneNatPolicy *policy) { + if (policy != NULL) policy = linphone_nat_policy_ref(policy); /* Prevent object destruction if the same policy is used */ + if (lc->nat_policy != NULL) linphone_nat_policy_unref(lc->nat_policy); + if (policy != NULL) lc->nat_policy = policy; + +#ifdef BUILD_UPNP + linphone_core_enable_keep_alive(lc, (lc->sip_conf.keepalive_period > 0)); + if (linphone_nat_policy_upnp_enabled(policy)) { + if (lc->upnp == NULL) { + lc->upnp = linphone_upnp_context_new(lc); + } + sal_nat_helper_enable(lc->sal, FALSE); + sal_enable_auto_contacts(lc->sal, FALSE); + sal_use_rport(lc->sal, FALSE); + } else { + if (lc->upnp != NULL) { + linphone_upnp_context_destroy(lc->upnp); + lc->upnp = NULL; + } +#endif + sal_nat_helper_enable(lc->sal, lp_config_get_int(lc->config, "net", "enable_nat_helper", 1)); + sal_enable_auto_contacts(lc->sal, TRUE); + sal_use_rport(lc->sal, lp_config_get_int(lc->config, "sip", "use_rport", 1)); + if (lc->sip_conf.contact) update_primary_contact(lc); +#ifdef BUILD_UPNP + } +#endif +} + +LinphoneNatPolicy * linphone_core_get_nat_policy(const LinphoneCore *lc) { + return lc->nat_policy; +} /******************************************************************************* @@ -6367,13 +6403,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); @@ -6382,6 +6411,12 @@ void net_config_uninit(LinphoneCore *lc) ms_free(lc->net_conf.nat_address_ip); } 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); + linphone_nat_policy_unref(lc->nat_policy); + lc->nat_policy = NULL; + } } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 9661b5d92..e43c25c9f 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -406,6 +406,7 @@ LINPHONE_PUBLIC const char* linphone_privacy_to_string(LinphonePrivacy privacy); #include "content.h" #include "event.h" #include "linphonefriend.h" +#include "nat_policy.h" #include "xmlrpc.h" #include "conference.h" #else @@ -415,6 +416,7 @@ LINPHONE_PUBLIC const char* linphone_privacy_to_string(LinphonePrivacy privacy); #include "linphone/content.h" #include "linphone/event.h" #include "linphone/linphonefriend.h" +#include "linphone/nat_policy.h" #include "linphone/xmlrpc.h" #include "linphone/conference.h" #endif @@ -2188,6 +2190,7 @@ typedef struct _LCCallbackObj /** * Policy to use to pass through firewalls. * @ingroup network_parameters + * @deprecated Use LinphoneNatPolicy instead **/ typedef enum _LinphoneFirewallPolicy { LinphonePolicyNoFirewall, /**< Do not use any mechanism to pass through firewalls */ @@ -3186,6 +3189,7 @@ LINPHONE_PUBLIC const char *linphone_core_get_nat_address(const LinphoneCore *lc * @param[in] lc #LinphoneCore object. * @param[in] pol The #LinphoneFirewallPolicy to use. * @ingroup network_parameters + * @deprecated Use linphone_core_set_nat_policy() instead. */ LINPHONE_PUBLIC void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol); @@ -3194,9 +3198,30 @@ LINPHONE_PUBLIC void linphone_core_set_firewall_policy(LinphoneCore *lc, Linphon * @param[in] lc #LinphoneCore object. * @return The #LinphoneFirewallPolicy that is being used. * @ingroup network_parameters + * @deprecated Use linphone_core_get_nat_policy() instead. */ LINPHONE_PUBLIC LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc); +/** + * Set the policy to use to pass through NATs/firewalls. + * It may be overridden by a NAT policy for a specific proxy config. + * @param[in] lc #LinphoneCore object + * @param[in] policy LinphoneNatPolicy object + * @ingroup network_parameters + * @see linphone_proxy_config_set_nat_policy() + */ +LINPHONE_PUBLIC void linphone_core_set_nat_policy(LinphoneCore *lc, LinphoneNatPolicy *policy); + +/** + * Get The policy that is used to pass through NATs/firewalls. + * It may be overridden by a NAT policy for a specific proxy config. + * @param[in] lc #LinphoneCore object + * @return LinphoneNatPolicy object in use. + * @ingroup network_parameters + * @see linphone_proxy_config_get_nat_policy() + */ +LINPHONE_PUBLIC LinphoneNatPolicy * linphone_core_get_nat_policy(const LinphoneCore *lc); + /* sound functions */ /* returns a null terminated static array of string describing the sound devices */ LINPHONE_PUBLIC const char** linphone_core_get_sound_devices(LinphoneCore *lc); diff --git a/coreapi/lpconfig.c b/coreapi/lpconfig.c index b5c547b1f..2f410e7b3 100644 --- a/coreapi/lpconfig.c +++ b/coreapi/lpconfig.c @@ -530,6 +530,31 @@ const char *lp_config_get_string(const LpConfig *lpconfig, const char *section, return default_string; } +MSList * lp_config_get_string_list(const LpConfig *lpconfig, const char *section, const char *key, MSList *default_list) { + LpItem *item; + LpSection *sec = lp_config_find_section(lpconfig, section); + if (sec != NULL) { + item = lp_section_find_item(sec, key); + if (item != NULL) { + MSList *l = NULL; + char *str; + char *ptr; + str = ptr = ms_strdup(item->value); + while (ptr != NULL) { + char *next = strstr(ptr, ","); + if (next != NULL) { + *(next++) = '\0'; + } + l = ms_list_append(l, ms_strdup(ptr)); + ptr = next; + } + ms_free(str); + return l; + } + } + return default_list; +} + bool_t lp_config_get_range(const LpConfig *lpconfig, const char *section, const char *key, int *min, int *max, int default_min, int default_max) { const char *str = lp_config_get_string(lpconfig, section, key, NULL); if (str != NULL) { @@ -645,6 +670,22 @@ void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *ke lpconfig->modified++; } +void lp_config_set_string_list(LpConfig *lpconfig, const char *section, const char *key, const MSList *value) { + char *strvalue = NULL; + char *tmp = NULL; + const MSList *elem; + for (elem = value; elem != NULL; elem = elem->next) { + if (strvalue) { + tmp = ms_strdup_printf("%s,%s", strvalue, (const char *)elem->data); + ms_free(strvalue); + strvalue = tmp; + } + else strvalue = ms_strdup((const char *)elem->data); + } + lp_config_set_string(lpconfig, section, key, strvalue); + if (strvalue) ms_free(strvalue); +} + void lp_config_set_range(LpConfig *lpconfig, const char *section, const char *key, int min_value, int max_value) { char tmp[30]; snprintf(tmp, sizeof(tmp), "%i-%i", min_value, max_value); diff --git a/coreapi/lpconfig.h b/coreapi/lpconfig.h index 86c1089a9..2e3a99d60 100644 --- a/coreapi/lpconfig.h +++ b/coreapi/lpconfig.h @@ -104,6 +104,13 @@ LINPHONE_PUBLIC int lp_config_read_file(LpConfig *lpconfig, const char *filename **/ LINPHONE_PUBLIC const char *lp_config_get_string(const LpConfig *lpconfig, const char *section, const char *key, const char *default_string); +/** + * Retrieves a configuration item as a list of strings, given its section, key, and default value. + * @ingroup misc + * The default value is returned if the config item isn't found. + */ +LINPHONE_PUBLIC MSList * lp_config_get_string_list(const LpConfig *lpconfig, const char *section, const char *key, MSList *default_list); + /** * Retrieves a configuration item as a range, given its section, key, and default min and max values. * @@ -144,6 +151,12 @@ LINPHONE_PUBLIC float lp_config_get_float(const LpConfig *lpconfig,const char *s **/ LINPHONE_PUBLIC void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value); +/** + * Sets a string list config item + * @ingroup misc + */ +LINPHONE_PUBLIC void lp_config_set_string_list(LpConfig *lpconfig, const char *section, const char *key, const MSList *value); + /** * Sets a range config item * diff --git a/coreapi/misc.c b/coreapi/misc.c index 835acb6e9..6c989b066 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -317,28 +317,30 @@ static ortp_socket_t create_socket(int local_port){ return sock; } -static int sendStunRequest(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t changeAddr){ - char buf[STUN_MAX_MESSAGE_SIZE]; - int len = STUN_MAX_MESSAGE_SIZE; - StunAtrString username; - StunAtrString password; - StunMessage req; - int err; - memset(&req, 0, sizeof(StunMessage)); - memset(&username,0,sizeof(username)); - memset(&password,0,sizeof(password)); - stunBuildReqSimple( &req, &username, changeAddr , changeAddr , id); - len = stunEncodeMessage( &req, buf, len, &password); - if (len<=0){ +static int send_stun_request(int sock, const struct sockaddr *server, socklen_t addrlen, int id, bool_t change_addr){ + char *buf = NULL; + int len; + int err = 0; + MSStunMessage *req = ms_stun_binding_request_create(); + UInt96 tr_id = ms_stun_message_get_tr_id(req); + tr_id.octet[0] = id; + ms_stun_message_set_tr_id(req, tr_id); + ms_stun_message_enable_change_ip(req, change_addr); + ms_stun_message_enable_change_port(req, change_addr); + len = ms_stun_message_encode(req, &buf); + if (len <= 0) { ms_error("Fail to encode stun message."); - return -1; + err = -1; + } else { + err = sendto(sock, buf, len, 0, server, addrlen); + if (err < 0) { + ms_error("sendto failed: %s",strerror(errno)); + err = -1; + } } - err=sendto(sock,buf,len,0,server,addrlen); - if (err<0){ - ms_error("sendto failed: %s",strerror(errno)); - return -1; - } - return 0; + if (buf != NULL) ms_free(buf); + ms_free(req); + return err; } int linphone_parse_host_port(const char *input, char *host, size_t hostlen, int *port){ @@ -387,23 +389,32 @@ int parse_hostname_to_addr(const char *server, struct sockaddr_storage *ss, sock return 0; } -static int recvStunResponse(ortp_socket_t sock, char *ipaddr, int *port, int *id){ - char buf[STUN_MAX_MESSAGE_SIZE]; - int len = STUN_MAX_MESSAGE_SIZE; - StunMessage resp; - len=recv(sock,buf,len,0); - if (len>0){ +static int recv_stun_response(ortp_socket_t sock, char *ipaddr, int *port, int *id) { + char buf[MS_STUN_MAX_MESSAGE_SIZE]; + int len = MS_STUN_MAX_MESSAGE_SIZE; + MSStunMessage *resp; + + len = recv(sock, buf, len, 0); + if (len > 0) { struct in_addr ia; - stunParseMessage(buf,len, &resp ); - *id=resp.msgHdr.tr_id.octet[0]; - if (resp.hasXorMappedAddress){ - *port = resp.xorMappedAddress.ipv4.port; - ia.s_addr=htonl(resp.xorMappedAddress.ipv4.addr); - }else if (resp.hasMappedAddress){ - *port = resp.mappedAddress.ipv4.port; - ia.s_addr=htonl(resp.mappedAddress.ipv4.addr); - }else return -1; - strncpy(ipaddr,inet_ntoa(ia),LINPHONE_IPADDR_SIZE); + resp = ms_stun_message_create_from_buffer_parsing((uint8_t *)buf, len); + if (resp != NULL) { + const MSStunAddress *stun_addr; + UInt96 tr_id = ms_stun_message_get_tr_id(resp); + *id = tr_id.octet[0]; + stun_addr = ms_stun_message_get_xor_mapped_address(resp); + if (stun_addr != NULL) { + *port = stun_addr->ip.v4.port; + ia.s_addr = htonl(stun_addr->ip.v4.addr); + } else { + stun_addr = ms_stun_message_get_mapped_address(resp); + if (stun_addr != NULL) { + *port = stun_addr->ip.v4.port; + ia.s_addr = htonl(stun_addr->ip.v4.addr); + } else len = -1; + } + if (len > 0) strncpy(ipaddr, inet_ntoa(ia), LINPHONE_IPADDR_SIZE); + } } return len; } @@ -459,44 +470,32 @@ int linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ int id; if (loops%20==0){ ms_message("Sending stun requests..."); - sendStunRequest((int)sock1,ai->ai_addr,(socklen_t)ai->ai_addrlen,11,TRUE); - sendStunRequest((int)sock1,ai->ai_addr,(socklen_t)ai->ai_addrlen,1,FALSE); + send_stun_request((int)sock1,ai->ai_addr,(socklen_t)ai->ai_addrlen,11,TRUE); + send_stun_request((int)sock1,ai->ai_addr,(socklen_t)ai->ai_addrlen,1,FALSE); if (sock2!=-1){ - sendStunRequest((int)sock2,ai->ai_addr,(socklen_t)ai->ai_addrlen,22,TRUE); - sendStunRequest((int)sock2,ai->ai_addr,(socklen_t)ai->ai_addrlen,2,FALSE); + send_stun_request((int)sock2,ai->ai_addr,(socklen_t)ai->ai_addrlen,22,TRUE); + send_stun_request((int)sock2,ai->ai_addr,(socklen_t)ai->ai_addrlen,2,FALSE); } if (sock3!=-1){ - sendStunRequest((int)sock3,ai->ai_addr,(socklen_t)ai->ai_addrlen,33,TRUE); - sendStunRequest((int)sock3,ai->ai_addr,(socklen_t)ai->ai_addrlen,3,FALSE); + send_stun_request((int)sock3,ai->ai_addr,(socklen_t)ai->ai_addrlen,33,TRUE); + send_stun_request((int)sock3,ai->ai_addr,(socklen_t)ai->ai_addrlen,3,FALSE); } } ms_usleep(10000); - if (recvStunResponse(sock1,ac->addr, - &ac->port,&id)>0){ - ms_message("STUN test result: local audio port maps to %s:%i", - ac->addr, - ac->port); - if (id==11) - cone_audio=TRUE; + if (recv_stun_response(sock1, ac->addr, &ac->port, &id) > 0) { + ms_message("STUN test result: local audio port maps to %s:%i", ac->addr, ac->port); + if (id==11) cone_audio=TRUE; got_audio=TRUE; } - if (recvStunResponse(sock2,vc->addr, - &vc->port,&id)>0){ - ms_message("STUN test result: local video port maps to %s:%i", - vc->addr, - vc->port); - if (id==22) - cone_video=TRUE; + if (recv_stun_response(sock2, vc->addr, &vc->port, &id) > 0) { + ms_message("STUN test result: local video port maps to %s:%i", vc->addr, vc->port); + if (id==22) cone_video=TRUE; got_video=TRUE; } - if (recvStunResponse(sock3,tc->addr, - &tc->port,&id)>0){ - ms_message("STUN test result: local text port maps to %s:%i", - tc->addr, - tc->port); - if (id==33) - cone_text=TRUE; + if (recv_stun_response(sock3, tc->addr, &tc->port, &id)>0) { + ms_message("STUN test result: local text port maps to %s:%i", tc->addr, tc->port); + if (id==33) cone_text=TRUE; got_text=TRUE; } ortp_gettimeofday(&cur,NULL); @@ -585,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); + } } } @@ -610,31 +613,83 @@ 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) { lc->forced_ice_relay = enable; } +static void stun_auth_requested_cb(LinphoneCall *call, const char *realm, const char *nonce, const char **username, const char **password, const char **ha1) { + LinphoneProxyConfig *proxy = NULL; + const LinphoneNatPolicy *nat_policy = NULL; + const LinphoneAddress *addr = NULL; + const LinphoneAuthInfo *auth_info = NULL; + LinphoneCore *lc = call->core; + const char *user = NULL; + + // Get the username from the nat policy or the proxy config + if (call->dest_proxy != NULL) proxy = call->dest_proxy; + else proxy = linphone_core_get_default_proxy_config(call->core); + if (proxy == NULL) return; + nat_policy = linphone_proxy_config_get_nat_policy(proxy); + if (nat_policy != NULL) { + user = linphone_nat_policy_get_stun_server_username(nat_policy); + } else { + nat_policy = linphone_core_get_nat_policy(call->core); + if (nat_policy != NULL) { + user = linphone_nat_policy_get_stun_server_username(nat_policy); + } + } + if (user == NULL) { + /* If the username has not been found in the nat_policy, take the username from the currently used proxy config. */ + addr = linphone_proxy_config_get_identity_address(proxy); + if (addr == NULL) return; + user = linphone_address_get_username(addr); + } + if (user == NULL) return; + + auth_info = linphone_core_find_auth_info(lc, realm, user, NULL); + if (auth_info != NULL) { + const char *hash = linphone_auth_info_get_ha1(auth_info); + if (hash != NULL) { + *ha1 = hash; + } else { + *password = linphone_auth_info_get_passwd(auth_info); + } + *username = user; + } else { + ms_warning("No auth info found for STUN auth request"); + } +} + int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call){ char local_addr[64]; const struct addrinfo *ai = NULL; IceCheckList *audio_check_list; IceCheckList *video_check_list; IceCheckList *text_check_list; - const char *server = linphone_core_get_stun_server(lc); + LinphoneNatPolicy *nat_policy = NULL; + const char *server = NULL; + + if (call->dest_proxy != NULL) nat_policy = linphone_proxy_config_get_nat_policy(call->dest_proxy); + if (nat_policy == NULL) nat_policy = linphone_core_get_nat_policy(lc); + if (nat_policy != NULL) server = linphone_nat_policy_get_stun_server(nat_policy); if (call->ice_session == NULL) return -1; audio_check_list = ice_session_check_list(call->ice_session, call->main_audio_stream_index); @@ -646,8 +701,8 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call){ ms_warning("Ice gathering is not implemented for ipv6"); return -1; } - if (server){ - ai=linphone_core_get_stun_server_addrinfo(lc); + if ((nat_policy != NULL) && (server != NULL) && (server[0] != '\0')) { + ai=linphone_nat_policy_get_stun_server_addrinfo(nat_policy); if (ai==NULL){ ms_warning("Fail to resolve STUN server for ICE gathering, continuing without stun."); } @@ -658,31 +713,35 @@ int linphone_core_gather_ice_candidates(LinphoneCore *lc, LinphoneCall *call){ ice_session_enable_forced_relay(call->ice_session, lc->forced_ice_relay); + // TODO: Handle IPv6 /* Gather local host candidates. */ if (linphone_core_get_local_ip_for(AF_INET, NULL, local_addr) < 0) { ms_error("Fail to get local ip"); return -1; } if ((ice_check_list_state(audio_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(audio_check_list) == FALSE)) { - ice_add_local_candidate(audio_check_list, "host", local_addr, call->media_ports[call->main_audio_stream_index].rtp_port, 1, NULL); - ice_add_local_candidate(audio_check_list, "host", local_addr, call->media_ports[call->main_audio_stream_index].rtcp_port, 2, NULL); + ice_add_local_candidate(audio_check_list, "host", AF_INET, local_addr, call->media_ports[call->main_audio_stream_index].rtp_port, 1, NULL); + ice_add_local_candidate(audio_check_list, "host", AF_INET, local_addr, call->media_ports[call->main_audio_stream_index].rtcp_port, 2, NULL); call->stats[LINPHONE_CALL_STATS_AUDIO].ice_state = LinphoneIceStateInProgress; } if (linphone_core_video_enabled(lc) && (video_check_list != NULL) && (ice_check_list_state(video_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(video_check_list) == FALSE)) { - ice_add_local_candidate(video_check_list, "host", local_addr, call->media_ports[call->main_video_stream_index].rtp_port, 1, NULL); - ice_add_local_candidate(video_check_list, "host", local_addr, call->media_ports[call->main_video_stream_index].rtcp_port, 2, NULL); + ice_add_local_candidate(video_check_list, "host", AF_INET, local_addr, call->media_ports[call->main_video_stream_index].rtp_port, 1, NULL); + ice_add_local_candidate(video_check_list, "host", AF_INET, local_addr, call->media_ports[call->main_video_stream_index].rtcp_port, 2, NULL); call->stats[LINPHONE_CALL_STATS_VIDEO].ice_state = LinphoneIceStateInProgress; } if (call->params->realtimetext_enabled && (text_check_list != NULL) && (ice_check_list_state(text_check_list) != ICL_Completed) && (ice_check_list_candidates_gathered(text_check_list) == FALSE)) { - ice_add_local_candidate(text_check_list, "host", local_addr, call->media_ports[call->main_text_stream_index].rtp_port, 1, NULL); - ice_add_local_candidate(text_check_list, "host", local_addr, call->media_ports[call->main_text_stream_index].rtcp_port, 2, NULL); + ice_add_local_candidate(text_check_list, "host", AF_INET, local_addr, call->media_ports[call->main_text_stream_index].rtp_port, 1, NULL); + ice_add_local_candidate(text_check_list, "host", AF_INET, local_addr, call->media_ports[call->main_text_stream_index].rtcp_port, 2, NULL); call->stats[LINPHONE_CALL_STATS_TEXT].ice_state = LinphoneIceStateInProgress; } - if (ai){ - ms_message("ICE: gathering candidate from [%s]",server); + if ((ai != NULL) && (nat_policy != NULL) + && (linphone_nat_policy_stun_enabled(nat_policy) || linphone_nat_policy_turn_enabled(nat_policy))) { + ms_message("ICE: gathering candidate from [%s] using %s", server, linphone_nat_policy_turn_enabled(nat_policy) ? "TURN" : "STUN"); /* Gather local srflx candidates. */ + ice_session_enable_turn(call->ice_session, linphone_nat_policy_turn_enabled(nat_policy)); + ice_session_set_stun_auth_requested_cb(call->ice_session, (MSStunAuthRequestedCb)stun_auth_requested_cb, call); ice_session_gather_candidates(call->ice_session, ai->ai_addr, (socklen_t)ai->ai_addrlen); return 1; } else { @@ -835,7 +894,8 @@ void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDes } void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session, bool_t use_nortpproxy) { - const char *rtp_addr, *rtcp_addr; + IceCandidate *rtp_candidate = NULL; + IceCandidate *rtcp_candidate = NULL; IceSessionState session_state = ice_session_state(session); int nb_candidates; int i, j; @@ -843,9 +903,9 @@ void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSess if (session_state == IS_Completed) { if (use_nortpproxy) desc->set_nortpproxy = TRUE; - result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_addr, NULL, NULL, NULL); + result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, 0), &rtp_candidate, NULL); if (result == TRUE) { - strncpy(desc->addr, rtp_addr, sizeof(desc->addr)); + strncpy(desc->addr, rtp_candidate->taddr.ip, sizeof(desc->addr)); } else { ms_warning("If ICE has completed successfully, rtp_addr should be set!"); } @@ -862,14 +922,16 @@ void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSess if (!sal_stream_description_active(stream) || (cl == NULL)) continue; if (ice_check_list_state(cl) == ICL_Completed) { if (use_nortpproxy) stream->set_nortpproxy = TRUE; - result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port); + result = ice_check_list_selected_valid_local_candidate(ice_session_check_list(session, i), &rtp_candidate, &rtcp_candidate); } else { stream->set_nortpproxy = FALSE; - result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_addr, &stream->rtp_port, &rtcp_addr, &stream->rtcp_port); + result = ice_check_list_default_local_candidate(ice_session_check_list(session, i), &rtp_candidate, &rtcp_candidate); } if (result == TRUE) { - strncpy(stream->rtp_addr, rtp_addr, sizeof(stream->rtp_addr)); - strncpy(stream->rtcp_addr, rtcp_addr, sizeof(stream->rtcp_addr)); + strncpy(stream->rtp_addr, rtp_candidate->taddr.ip, sizeof(stream->rtp_addr)); + strncpy(stream->rtcp_addr, rtcp_candidate->taddr.ip, sizeof(stream->rtcp_addr)); + stream->rtp_port = rtp_candidate->taddr.port; + stream->rtcp_port = rtcp_candidate->taddr.port; } else { memset(stream->rtp_addr, 0, sizeof(stream->rtp_addr)); memset(stream->rtcp_addr, 0, sizeof(stream->rtcp_addr)); @@ -916,13 +978,12 @@ void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSess } } if ((ice_check_list_state(cl) == ICL_Completed) && (ice_session_role(session) == IR_Controlling)) { - int rtp_port, rtcp_port; memset(stream->ice_remote_candidates, 0, sizeof(stream->ice_remote_candidates)); - if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_addr, &rtp_port, &rtcp_addr, &rtcp_port) == TRUE) { - strncpy(stream->ice_remote_candidates[0].addr, rtp_addr, sizeof(stream->ice_remote_candidates[0].addr)); - stream->ice_remote_candidates[0].port = rtp_port; - strncpy(stream->ice_remote_candidates[1].addr, rtcp_addr, sizeof(stream->ice_remote_candidates[1].addr)); - stream->ice_remote_candidates[1].port = rtcp_port; + if (ice_check_list_selected_valid_remote_candidate(cl, &rtp_candidate, &rtcp_candidate) == TRUE) { + strncpy(stream->ice_remote_candidates[0].addr, rtp_candidate->taddr.ip, sizeof(stream->ice_remote_candidates[0].addr)); + stream->ice_remote_candidates[0].port = rtp_candidate->taddr.port; + strncpy(stream->ice_remote_candidates[1].addr, rtcp_candidate->taddr.ip, sizeof(stream->ice_remote_candidates[1].addr)); + stream->ice_remote_candidates[1].port = rtcp_candidate->taddr.port; } else { ms_error("ice: Selected valid remote candidates should be present if the check list is in the Completed state"); } @@ -1066,7 +1127,8 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, get_default_addr_and_port(candidate->componentID, md, stream, &addr, &port); if (addr && (candidate->port == port) && (strlen(candidate->addr) == strlen(addr)) && (strcmp(candidate->addr, addr) == 0)) default_candidate = TRUE; - ice_add_remote_candidate(cl, candidate->type, candidate->addr, candidate->port, candidate->componentID, + // TODO: Handle IPv6 + ice_add_remote_candidate(cl, candidate->type, AF_INET, candidate->addr, candidate->port, candidate->componentID, candidate->priority, candidate->foundation, default_candidate); } if (ice_restarted == FALSE) { @@ -1082,7 +1144,8 @@ void linphone_call_update_ice_from_remote_media_description(LinphoneCall *call, /* If we receive a re-invite and we finished ICE processing on our side, use the candidates given by the remote. */ ice_check_list_unselect_valid_pairs(cl); } - ice_add_losing_pair(cl, j + 1, remote_candidate->addr, remote_candidate->port, addr, port); + // TODO: Handle IPv6 + ice_add_losing_pair(cl, j + 1, AF_INET, remote_candidate->addr, remote_candidate->port, addr, port); losing_pairs_added = TRUE; } if (losing_pairs_added == TRUE) ice_check_list_check_completed(cl); diff --git a/coreapi/nat_policy.c b/coreapi/nat_policy.c new file mode 100644 index 000000000..5df7e3c24 --- /dev/null +++ b/coreapi/nat_policy.c @@ -0,0 +1,303 @@ +/* +linphone +Copyright (C) 2010-2016 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "linphonecore.h" +#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_server_username) belle_sip_free(policy->stun_server_username); + if (policy->stun_addrinfo) bctbx_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); + +BELLE_SIP_INSTANCIATE_VPTR(LinphoneNatPolicy, belle_sip_object_t, + (belle_sip_object_destroy_t)linphone_nat_policy_destroy, + NULL, // clone + NULL, // marshal + TRUE +); + + +static void _linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy, LpConfig *config, int index) { + char *section; + MSList *l = NULL; + + section = belle_sip_strdup_printf("nat_policy_%i", index); + lp_config_set_string(config, section, "ref", policy->ref); + lp_config_set_string(config, section, "stun_server", policy->stun_server); + lp_config_set_string(config, section, "stun_server_username", policy->stun_server_username); + if (linphone_nat_policy_upnp_enabled(policy)) { + l = ms_list_append(l, "upnp"); + } else { + if (linphone_nat_policy_stun_enabled(policy)) l = ms_list_append(l, "stun"); + if (linphone_nat_policy_turn_enabled(policy)) l = ms_list_append(l, "turn"); + if (linphone_nat_policy_ice_enabled(policy)) l = ms_list_append(l, "ice"); + } + lp_config_set_string_list(config, section, "protocols", l); + belle_sip_free(section); + ms_list_free(l); +} + +void linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy) { + LpConfig *config = policy->lc->config; + 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, policy->ref) == 0)) { + _linphone_nat_policy_save_to_config(policy, config, index); + finished = TRUE; + } + } else { + _linphone_nat_policy_save_to_config(policy, config, index); + finished = TRUE; + } + belle_sip_free(section); + } +} + +LinphoneNatPolicy * linphone_nat_policy_ref(LinphoneNatPolicy *policy) { + belle_sip_object_ref(policy); + return policy; +} + +void linphone_nat_policy_unref(LinphoneNatPolicy *policy) { + belle_sip_object_unref(policy); +} + +void *linphone_nat_policy_get_user_data(const LinphoneNatPolicy *policy) { + return policy->user_data; +} + +void linphone_nat_policy_set_user_data(LinphoneNatPolicy *policy, void *ud) { + policy->user_data = ud; +} + + +void linphone_nat_policy_clear(LinphoneNatPolicy *policy) { + linphone_nat_policy_enable_stun(policy, FALSE); + linphone_nat_policy_enable_turn(policy, FALSE); + linphone_nat_policy_enable_ice(policy, FALSE); + linphone_nat_policy_enable_upnp(policy, FALSE); + linphone_nat_policy_set_stun_server(policy, NULL); +} + +bool_t linphone_nat_policy_stun_enabled(const LinphoneNatPolicy *policy) { + return policy->stun_enabled; +} + +void linphone_nat_policy_enable_stun(LinphoneNatPolicy *policy, bool_t enable) { + policy->stun_enabled = enable; +} + +bool_t linphone_nat_policy_turn_enabled(const LinphoneNatPolicy *policy) { + return policy->turn_enabled; +} + +void linphone_nat_policy_enable_turn(LinphoneNatPolicy *policy, bool_t enable) { + policy->turn_enabled = enable; +} + +bool_t linphone_nat_policy_ice_enabled(const LinphoneNatPolicy *policy) { + return policy->ice_enabled; +} + +void linphone_nat_policy_enable_ice(LinphoneNatPolicy *policy, bool_t enable) { + policy->ice_enabled = enable; +} + +bool_t linphone_nat_policy_upnp_enabled(const LinphoneNatPolicy *policy) { + return policy->upnp_enabled; +} + +void linphone_nat_policy_enable_upnp(LinphoneNatPolicy *policy, bool_t enable) { + policy->upnp_enabled = enable; + if (enable) { +#ifdef BUILD_UPNP + policy->stun_enabled = policy->turn_enabled = policy->ice_enabled = FALSE; + ms_warning("Enabling uPnP NAT policy has disabled any other previously enabled policies"); +#else + ms_warning("Cannot enable the uPnP NAT policy because the uPnP support is not compiled in"); +#endif + } +} + +const char * linphone_nat_policy_get_stun_server(const LinphoneNatPolicy *policy) { + return policy->stun_server; +} + +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 (new_stun_server != NULL) { + policy->stun_server = new_stun_server; + linphone_nat_policy_resolve_stun_server(policy); + } +} + +const char * linphone_nat_policy_get_stun_server_username(const LinphoneNatPolicy *policy) { + return policy->stun_server_username; +} + +void linphone_nat_policy_set_stun_server_username(LinphoneNatPolicy *policy, const char *username) { + char *new_username = NULL; + + if (username != NULL) new_username = belle_sip_strdup(username); + if (policy->stun_server_username != NULL) { + belle_sip_free(policy->stun_server_username); + policy->stun_server_username = NULL; + } + if (new_username != NULL) policy->stun_server_username = new_username; +} + +static void stun_server_resolved(LinphoneNatPolicy *policy, const char *name, struct addrinfo *addrinfo) { + if (policy->stun_addrinfo) { + bctbx_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); + const char *username = lp_config_get_string(config, section, "stun_server_username", 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 (username != NULL) linphone_nat_policy_set_stun_server_username(policy, username); + 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 new file mode 100644 index 000000000..e122508aa --- /dev/null +++ b/coreapi/nat_policy.h @@ -0,0 +1,205 @@ +/* +nat_policy.h +Copyright (C) 2010-2016 Belledonne Communications SARL + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef LINPHONE_NAT_POLICY_H_ +#define LINPHONE_NAT_POLICY_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @addtogroup network_parameters + * @{ + */ + +/** + * Policy to use to pass through NATs/firewalls. + */ +typedef struct _LinphoneNatPolicy LinphoneNatPolicy; + + +/** + * Acquire a reference to the LinphoneNatPolicy object. + * @param[in] policy LinphoneNatPolicy object. + * @return The same LinphoneNatPolicy object. +**/ +LINPHONE_PUBLIC LinphoneNatPolicy * linphone_nat_policy_ref(LinphoneNatPolicy *policy); + +/** + * Release reference to the LinphoneNatPolicy object. + * @param[in] policy LinphoneNatPolicy object. +**/ +LINPHONE_PUBLIC void linphone_nat_policy_unref(LinphoneNatPolicy *policy); + +/** + * Retrieve the user pointer associated with the LinphoneNatPolicy object. + * @param[in] policy LinphoneNatPolicy object. + * @return The user pointer associated with the LinphoneNatPolicy object. +**/ +LINPHONE_PUBLIC void *linphone_nat_policy_get_user_data(const LinphoneNatPolicy *policy); + +/** + * Assign a user pointer to the LinphoneNatPolicy object. + * @param[in] policy LinphoneNatPolicy object. + * @param[in] ud The user pointer to associate with the LinphoneNatPolicy object. +**/ +LINPHONE_PUBLIC void linphone_nat_policy_set_user_data(LinphoneNatPolicy *policy, void *ud); + +/** + * Clear a NAT policy (deactivate all protocols and unset the STUN server). + * @param[in] policy LinphoneNatPolicy object. + */ +LINPHONE_PUBLIC void linphone_nat_policy_clear(LinphoneNatPolicy *policy); + +/** + * Tell whether STUN is enabled. + * @param[in] policy LinphoneNatPolicy object + * @return Boolean value telling whether STUN is enabled. + */ +LINPHONE_PUBLIC bool_t linphone_nat_policy_stun_enabled(const LinphoneNatPolicy *policy); + +/** + * Enable STUN. + * If TURN is also enabled, TURN will be used instead of STUN. + * @param[in] policy LinphoneNatPolicy object + * @param[in] enable Boolean value telling whether to enable STUN. + */ +LINPHONE_PUBLIC void linphone_nat_policy_enable_stun(LinphoneNatPolicy *policy, bool_t enable); + +/** + * Tell whether TURN is enabled. + * @param[in] policy LinphoneNatPolicy object + * @return Boolean value telling whether TURN is enabled. + */ +LINPHONE_PUBLIC bool_t linphone_nat_policy_turn_enabled(const LinphoneNatPolicy *policy); + +/** + * Enable TURN. + * If STUN is also enabled, it is ignored and TURN is used. + * @param[in] policy LinphoneNatPolicy object + * @param[in] enable Boolean value telling whether to enable TURN. + */ +LINPHONE_PUBLIC void linphone_nat_policy_enable_turn(LinphoneNatPolicy *policy, bool_t enable); + +/** + * Tell whether ICE is enabled. + * @param[in] policy LinphoneNatPolicy object + * @return Boolean value telling whether ICE is enabled. + */ +LINPHONE_PUBLIC bool_t linphone_nat_policy_ice_enabled(const LinphoneNatPolicy *policy); + +/** + * Enable ICE. + * ICE can be enabled without STUN/TURN, in which case only the local candidates will be used. + * @param[in] policy LinphoneNatPolicy object + * @param[in] enable Boolean value telling whether to enable ICE. + */ +LINPHONE_PUBLIC void linphone_nat_policy_enable_ice(LinphoneNatPolicy *policy, bool_t enable); + +/** + * Tell whether uPnP is enabled. + * @param[in] policy LinphoneNatPolicy object + * @return Boolean value telling whether uPnP is enabled. + */ +LINPHONE_PUBLIC bool_t linphone_nat_policy_upnp_enabled(const LinphoneNatPolicy *policy); + +/** + * Enable uPnP. + * This has the effect to disable every other policies (ICE, STUN and TURN). + * @param[in] policy LinphoneNatPolicy object + * @param[in] enable Boolean value telling whether to enable uPnP. + */ +LINPHONE_PUBLIC void linphone_nat_policy_enable_upnp(LinphoneNatPolicy *policy, bool_t enable); + +/** + * Get the STUN/TURN server to use with this NAT policy. + * Used when STUN or TURN are enabled. + * @param[in] policy LinphoneNatPolicy object + * @return The STUN server used by this NAT policy. + */ +LINPHONE_PUBLIC const char * linphone_nat_policy_get_stun_server(const LinphoneNatPolicy *policy); + +/** + * Set the STUN/TURN server to use with this NAT policy. + * Used when STUN or TURN are enabled. + * @param[in] policy LinphoneNatPolicy object + * @param[in] stun_server The STUN server to use with this NAT policy. + */ +LINPHONE_PUBLIC void linphone_nat_policy_set_stun_server(LinphoneNatPolicy *policy, const char *stun_server); + +/** + * Get the username used to authenticate with the STUN/TURN server. + * The authentication will search for a LinphoneAuthInfo with this username. + * If it is not set the username of the currently used LinphoneProxyConfig is used to search for a LinphoneAuthInfo. + * @param[in] policy LinphoneNatPolicy object + * @return The username used to authenticate with the STUN/TURN server. + */ +LINPHONE_PUBLIC const char * linphone_nat_policy_get_stun_server_username(const LinphoneNatPolicy *policy); + +/** + * Seth the username used to authenticate with the STUN/TURN server. + * The authentication will search for a LinphoneAuthInfo with this username. + * If it is not set the username of the currently used LinphoneProxyConfig is used to search for a LinphoneAuthInfo. + * @param[in] policy LinphoneNatPolicy object + * @param[in] username The username used to authenticate with the STUN/TURN server. + */ +LINPHONE_PUBLIC void linphone_nat_policy_set_stun_server_username(LinphoneNatPolicy *policy, const char *username); + +/** + * 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); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LINPHONE_NAT_POLICY_H_ */ diff --git a/coreapi/presence.c b/coreapi/presence.c index 5986a988d..ad74eb83a 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -448,6 +448,7 @@ int linphone_presence_model_add_activity(LinphonePresenceModel *model, LinphoneP return -1; presence_model_add_person(model, person); + linphone_presence_person_unref(person); } else { /* Add the activity to the first person in the model. */ person = (LinphonePresencePerson *)ms_list_nth_data(model->persons, 0); diff --git a/coreapi/private.h b/coreapi/private.h index c073baab6..fd5b16b35 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -440,6 +440,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); @@ -616,6 +617,7 @@ struct _LinphoneProxyConfig char *dial_prefix; LinphoneRegistrationState state; LinphoneAVPFMode avpf_mode; + LinphoneNatPolicy *nat_policy; bool_t commit; bool_t reg_sendregister; @@ -800,7 +802,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; @@ -959,6 +960,7 @@ struct _LinphoneCore char* user_certificates_path; LinphoneVideoPolicy video_policy; time_t network_last_check; + LinphoneNatPolicy *nat_policy; bool_t use_files; bool_t apply_nat_settings; @@ -1187,6 +1189,25 @@ struct _LinphoneBuffer { 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 *stun_server_username; + char *ref; + bool_t stun_enabled; + bool_t turn_enabled; + bool_t ice_enabled; + bool_t upnp_enabled; +}; + +BELLE_SIP_DECLARE_VPTR(LinphoneNatPolicy); + +void linphone_nat_policy_save_to_config(const LinphoneNatPolicy *policy); + /***************************************************************************** * XML-RPC interface * @@ -1407,7 +1428,8 @@ BELLE_SIP_TYPE_ID(LinphoneXmlRpcRequestCbs), BELLE_SIP_TYPE_ID(LinphoneXmlRpcSession), BELLE_SIP_TYPE_ID(LinphoneTunnelConfig), BELLE_SIP_TYPE_ID(LinphoneFriendListCbs), -BELLE_SIP_TYPE_ID(LinphoneEvent) +BELLE_SIP_TYPE_ID(LinphoneEvent), +BELLE_SIP_TYPE_ID(LinphoneNatPolicy) BELLE_SIP_DECLARE_TYPES_END diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 6cd24c61f..1a8f964c4 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -221,6 +221,9 @@ void _linphone_proxy_config_destroy(LinphoneProxyConfig *cfg){ if (cfg->sent_headers!=NULL) sal_custom_header_free(cfg->sent_headers); if (cfg->pending_contact) linphone_address_unref(cfg->pending_contact); if (cfg->refkey) ms_free(cfg->refkey); + if (cfg->nat_policy != NULL) { + linphone_nat_policy_unref(cfg->nat_policy); + } _linphone_proxy_config_release_ops(cfg); } @@ -1355,6 +1358,11 @@ void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyC lp_config_set_int(config,key,"privacy",cfg->privacy); if (cfg->refkey) lp_config_set_string(config,key,"refkey",cfg->refkey); lp_config_set_int(config, key, "publish_expires", cfg->publish_expires); + + 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); + } } @@ -1377,6 +1385,7 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LinphoneCore* lc LinphoneProxyConfig *cfg; char key[50]; LpConfig *config=lc->config; + const char *nat_policy_ref; sprintf(key,"proxy_%i",index); @@ -1415,6 +1424,11 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LinphoneCore* lc CONFIGURE_STRING_VALUE(cfg,config,key,ref_key,"refkey") CONFIGURE_INT_VALUE(cfg,config,key,publish_expires,"publish_expires") + nat_policy_ref = lp_config_get_string(config, key, "nat_policy_ref", NULL); + if (nat_policy_ref != NULL) { + cfg->nat_policy = linphone_core_create_nat_policy_from_config(lc, nat_policy_ref); + } + return cfg; } @@ -1677,3 +1691,13 @@ void linphone_proxy_config_set_ref_key(LinphoneProxyConfig *cfg, const char *ref } if (refkey) cfg->refkey=ms_strdup(refkey); } + +LinphoneNatPolicy * linphone_proxy_config_get_nat_policy(const LinphoneProxyConfig *cfg) { + return cfg->nat_policy; +} + +void linphone_proxy_config_set_nat_policy(LinphoneProxyConfig *cfg, LinphoneNatPolicy *policy) { + if (policy != NULL) policy = linphone_nat_policy_ref(policy); /* Prevent object destruction if the same policy is used */ + if (cfg->nat_policy != NULL) linphone_nat_policy_unref(cfg->nat_policy); + if (policy != NULL) cfg->nat_policy = policy; +} diff --git a/include/sal/sal.h b/include/sal/sal.h index 11b86d93b..b885cf6a1 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -811,6 +811,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); diff --git a/tester/call_tester.c b/tester/call_tester.c index 98a67def3..de678eac8 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -1175,7 +1175,7 @@ static void call_with_no_sdp_ack_without_sdp(void){ linphone_core_manager_destroy(pauline); } -static void check_nb_media_starts(LinphoneCoreManager *caller, LinphoneCoreManager *callee, unsigned int caller_nb_media_starts, unsigned int callee_nb_media_starts) { +void check_nb_media_starts(LinphoneCoreManager *caller, LinphoneCoreManager *callee, unsigned int caller_nb_media_starts, unsigned int callee_nb_media_starts) { LinphoneCall *c1 = linphone_core_get_current_call(caller->lc); LinphoneCall *c2 = linphone_core_get_current_call(callee->lc); BC_ASSERT_PTR_NOT_NULL(c1); @@ -3999,10 +3999,10 @@ void check_media_direction(LinphoneCoreManager* mgr, LinphoneCall *call, MSList* if (video_dir != LinphoneMediaDirectionInactive){ BC_ASSERT_TRUE(linphone_call_params_video_enabled(params)); + BC_ASSERT_EQUAL(linphone_call_params_get_video_direction(params), video_dir, int, "%d"); + linphone_call_set_next_video_frame_decoded_callback(call,linphone_call_iframe_decoded_cb,mgr->lc); + linphone_call_send_vfu_request(call); } - BC_ASSERT_EQUAL(linphone_call_params_get_video_direction(params), video_dir, int, "%d"); - linphone_call_set_next_video_frame_decoded_callback(call,linphone_call_iframe_decoded_cb,mgr->lc); - linphone_call_send_vfu_request(call); switch (video_dir) { case LinphoneMediaDirectionInactive: diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index b02861d6a..7c17d7231 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -369,6 +369,7 @@ void liblinphone_tester_init(void(*ftester_printf)(int level, const char *fmt, v void liblinphone_tester_uninit(void); int liblinphone_tester_set_log_file(const char *filename); bool_t check_ice(LinphoneCoreManager* caller, LinphoneCoreManager* callee, LinphoneIceState state); +void check_nb_media_starts(LinphoneCoreManager *caller, LinphoneCoreManager *callee, unsigned int caller_nb_media_starts, unsigned int callee_nb_media_starts); LinphoneConferenceServer* linphone_conference_server_new(const char *rc_file, bool_t do_registration); void linphone_conference_server_destroy(LinphoneConferenceServer *conf_srv); diff --git a/tester/stun_tester.c b/tester/stun_tester.c index e52927386..41df639cb 100644 --- a/tester/stun_tester.c +++ b/tester/stun_tester.c @@ -16,7 +16,6 @@ along with this program. If not, see . */ - #include "linphonecore.h" #include "private.h" #include "liblinphone_tester.h" @@ -24,52 +23,34 @@ #include "ortp/port.h" -static const char* stun_address = "stun.linphone.org"; +static const char *stun_address = "stun.linphone.org"; -static int test_stun_encode( char*buffer, size_t len, bool_t expect_fail ) +static size_t test_stun_encode(char **buffer) { - StunAtrString username; - StunAtrString password; - StunMessage req; - memset(&req, 0, sizeof(StunMessage)); - memset(&username,0,sizeof(username)); - memset(&password,0,sizeof(password)); - stunBuildReqSimple( &req, &username, TRUE , TRUE , 11); - len = stunEncodeMessage( &req, buffer, (unsigned int)len, &password); - if (len<=0){ - if( expect_fail ) - ms_message("Fail to encode stun message (EXPECTED).\n"); - else - ms_error("Fail to encode stun message.\n"); - return -1; - } - return (int)len; + MSStunMessage *req = ms_stun_binding_request_create(); + UInt96 tr_id = ms_stun_message_get_tr_id(req); + tr_id.octet[0] = 11; + ms_stun_message_set_tr_id(req, tr_id); + return ms_stun_message_encode(req, buffer); } - static void linphone_stun_test_encode(void) { - char smallBuff[12]; - size_t smallLen = 12; - char bigBuff[STUN_MAX_MESSAGE_SIZE]; - size_t bigLen = STUN_MAX_MESSAGE_SIZE; - - size_t len = test_stun_encode(smallBuff, smallLen, TRUE); - BC_ASSERT(len == -1); - - len = test_stun_encode(bigBuff, bigLen, TRUE); + char *buffer = NULL; + size_t len = test_stun_encode(&buffer); BC_ASSERT(len > 0); + BC_ASSERT_PTR_NOT_NULL(buffer); + if (buffer != NULL) ms_free(buffer); ms_message("STUN message encoded in %i bytes", (int)len); } - static void linphone_stun_test_grab_ip(void) { - LinphoneCoreManager* lc_stun = linphone_core_manager_new2( "stun_rc", FALSE); + LinphoneCoreManager* lc_stun = linphone_core_manager_new2("stun_rc", FALSE); LinphoneCall dummy_call; int ping_time; - int tmp=0; + int tmp = 0; memset(&dummy_call, 0, sizeof(LinphoneCall)); dummy_call.main_audio_stream_index = 0; @@ -82,41 +63,121 @@ static void linphone_stun_test_grab_ip(void) linphone_core_set_stun_server(lc_stun->lc, stun_address); BC_ASSERT_STRING_EQUAL(stun_address, linphone_core_get_stun_server(lc_stun->lc)); - wait_for(lc_stun->lc,lc_stun->lc,&tmp,1); + wait_for(lc_stun->lc, lc_stun->lc, &tmp, 1); ping_time = linphone_core_run_stun_tests(lc_stun->lc, &dummy_call); BC_ASSERT(ping_time != -1); ms_message("Round trip to STUN: %d ms", ping_time); - BC_ASSERT( dummy_call.ac.addr[0] != '\0'); - BC_ASSERT( dummy_call.ac.port != 0); + BC_ASSERT(dummy_call.ac.addr[0] != '\0'); + BC_ASSERT(dummy_call.ac.port != 0); #ifdef VIDEO_ENABLED - BC_ASSERT( dummy_call.vc.addr[0] != '\0'); - BC_ASSERT( dummy_call.vc.port != 0); + BC_ASSERT(dummy_call.vc.addr[0] != '\0'); + BC_ASSERT(dummy_call.vc.port != 0); #endif - BC_ASSERT( dummy_call.tc.addr[0] != '\0'); - BC_ASSERT( dummy_call.tc.port != 0); + BC_ASSERT(dummy_call.tc.addr[0] != '\0'); + BC_ASSERT(dummy_call.tc.port != 0); - ms_message("STUN test result: local audio port maps to %s:%i", - dummy_call.ac.addr, - dummy_call.ac.port); + ms_message("STUN test result: local audio port maps to %s:%i", dummy_call.ac.addr, dummy_call.ac.port); #ifdef VIDEO_ENABLED - ms_message("STUN test result: local video port maps to %s:%i", - dummy_call.vc.addr, - dummy_call.vc.port); + ms_message("STUN test result: local video port maps to %s:%i", dummy_call.vc.addr, dummy_call.vc.port); #endif - ms_message("STUN test result: local text port maps to %s:%i", - dummy_call.tc.addr, - dummy_call.tc.port); + ms_message("STUN test result: local text port maps to %s:%i", dummy_call.tc.addr, dummy_call.tc.port); linphone_core_manager_destroy(lc_stun); } +static void configure_nat_policy(LinphoneCore *lc, bool_t turn_enabled) { + const char *username = "liblinphone-tester"; + const char *password = "retset-enohpnilbil"; + LinphoneAuthInfo *auth_info = linphone_core_create_auth_info(lc, username, NULL, password, NULL, "sip.linphone.org", NULL); + LinphoneNatPolicy *nat_policy = linphone_core_create_nat_policy(lc); + linphone_nat_policy_enable_ice(nat_policy, TRUE); + if (turn_enabled) { + linphone_nat_policy_enable_turn(nat_policy, TRUE); + linphone_nat_policy_set_stun_server(nat_policy, "sip1.linphone.org:3479"); + linphone_nat_policy_set_stun_server_username(nat_policy, username); + } else { + linphone_nat_policy_enable_stun(nat_policy, TRUE); + linphone_nat_policy_set_stun_server(nat_policy, "stun.linphone.org"); + } + linphone_core_set_nat_policy(lc, nat_policy); + linphone_core_add_auth_info(lc, auth_info); + linphone_nat_policy_unref(nat_policy); + linphone_auth_info_destroy(auth_info); +} + +static void ice_turn_call_base(bool_t forced_relay, bool_t caller_turn_enabled, bool_t callee_turn_enabled) { + LinphoneCoreManager *marie; + LinphoneCoreManager *pauline; + LinphoneIceState expected_ice_state = LinphoneIceStateHostConnection; + MSList *lcs = NULL; + + marie = linphone_core_manager_new("marie_rc"); + lcs = ms_list_append(lcs, marie->lc); + pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + lcs = ms_list_append(lcs, pauline->lc); + + configure_nat_policy(marie->lc, caller_turn_enabled); + configure_nat_policy(pauline->lc, callee_turn_enabled); + if (forced_relay == TRUE) { + linphone_core_enable_forced_ice_relay(marie->lc, TRUE); + linphone_core_enable_forced_ice_relay(pauline->lc, TRUE); + expected_ice_state = LinphoneIceStateRelayConnection; + } + + BC_ASSERT_TRUE(call(marie, pauline)); + + /* Wait for the ICE reINVITE to complete */ + BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2)); + BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2)); + BC_ASSERT_TRUE(check_ice(pauline, marie, expected_ice_state)); + check_nb_media_starts(pauline, marie, 1, 1); + check_media_direction(marie, linphone_core_get_current_call(marie->lc), lcs, LinphoneMediaDirectionSendRecv, LinphoneMediaDirectionInactive); + check_media_direction(pauline, linphone_core_get_current_call(pauline->lc), lcs, LinphoneMediaDirectionSendRecv, LinphoneMediaDirectionInactive); + liblinphone_tester_check_rtcp(marie, pauline); + if (forced_relay == TRUE) { + LinphoneCall *call = linphone_core_get_current_call(marie->lc); + BC_ASSERT_PTR_NOT_NULL(call->ice_session); + if (call->ice_session != NULL) { + IceCheckList *cl = ice_session_check_list(call->ice_session, 0); + BC_ASSERT_PTR_NOT_NULL(cl); + if (cl != NULL) { + BC_ASSERT_TRUE(cl->rtp_turn_context->stats_nb_send_indication > 0); + BC_ASSERT_TRUE(cl->rtp_turn_context->stats_nb_data_indication > 0); + BC_ASSERT_TRUE(cl->rtp_turn_context->stats_nb_received_channel_msg > 0); + BC_ASSERT_TRUE(cl->rtp_turn_context->stats_nb_sent_channel_msg > 0); + } + } + } + + end_call(marie, pauline); + + linphone_core_manager_destroy(pauline); + linphone_core_manager_destroy(marie); + ms_list_free(lcs); +} + +static void basic_ice_turn_call(void) { + ice_turn_call_base(FALSE, TRUE, TRUE); +} + +static void relayed_ice_turn_call(void) { + ice_turn_call_base(TRUE, TRUE, TRUE); +} + +static void relayed_ice_turn_to_ice_stun_call(void) { + ice_turn_call_base(TRUE, TRUE, FALSE); +} + test_t stun_tests[] = { - TEST_NO_TAG("Basic Stun test (Ping/public IP)", linphone_stun_test_grab_ip), - TEST_NO_TAG("STUN encode buffer protection", linphone_stun_test_encode) + TEST_ONE_TAG("Basic Stun test (Ping/public IP)", linphone_stun_test_grab_ip, "STUN"), + TEST_ONE_TAG("STUN encode", linphone_stun_test_encode, "STUN"), + TEST_TWO_TAGS("Basic ICE+TURN call", basic_ice_turn_call, "ICE", "TURN"), + TEST_TWO_TAGS("Relayed ICE+TURN call", relayed_ice_turn_call, "ICE", "TURN"), + TEST_TWO_TAGS("Relayed ICE+TURN to ICE+STUN call", relayed_ice_turn_to_ice_stun_call, "ICE", "TURN") }; test_suite_t stun_test_suite = {"Stun", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each, diff --git a/tester/tester.c b/tester/tester.c index 928cff4a2..315428bc1 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -218,13 +218,12 @@ bool_t wait_for_list(MSList* lcs,int* counter,int value,int timeout_ms) { bool_t wait_for_stun_resolution(LinphoneCoreManager *m) { MSTimeSpec start; int timeout_ms = 10000; - liblinphone_tester_clock_start(&start); - while (m->lc->net_conf.stun_addrinfo == NULL && !liblinphone_tester_clock_elapsed(&start,timeout_ms)) { + while (linphone_core_get_stun_server_addrinfo(m->lc) == NULL && !liblinphone_tester_clock_elapsed(&start,timeout_ms)) { linphone_core_iterate(m->lc); ms_usleep(20000); } - return m->lc->net_conf.stun_addrinfo != NULL; + return linphone_core_get_stun_server_addrinfo(m->lc) != NULL; } static void set_codec_enable(LinphoneCore* lc,const char* type,int rate,bool_t enable) { @@ -342,6 +341,7 @@ void linphone_core_manager_init(LinphoneCoreManager *mgr, const char* rc_file) { void linphone_core_manager_start(LinphoneCoreManager *mgr, int check_for_proxies) { LinphoneProxyConfig* proxy; + LinphoneNatPolicy *nat_policy; int proxy_count; /*BC_ASSERT_EQUAL(ms_list_size(linphone_core_get_proxy_config_list(lc)),proxy_count, int, "%d");*/ @@ -373,7 +373,9 @@ void linphone_core_manager_start(LinphoneCoreManager *mgr, int check_for_proxies linphone_address_clean(mgr->identity); } - if (linphone_core_get_stun_server(mgr->lc) != NULL){ + nat_policy = linphone_core_get_nat_policy(mgr->lc); + if ((nat_policy != NULL) && (linphone_nat_policy_get_stun_server(nat_policy) != NULL) && + (linphone_nat_policy_stun_enabled(nat_policy) || linphone_nat_policy_turn_enabled(nat_policy))) { /*before we go, ensure that the stun server is resolved, otherwise all ice related test will fail*/ BC_ASSERT_TRUE(wait_for_stun_resolution(mgr)); } @@ -559,6 +561,7 @@ int liblinphone_tester_after_each(void) { ms_error("%s", format); all_leaks_buffer = ms_strcat_printf(all_leaks_buffer, "\n%s", format); + ms_free(format); } // prevent any future leaks