From: Yann Diorcet Date: Fri, 15 Feb 2013 15:25:47 +0000 (+0100) Subject: Improve uPnP behaviour (Firewall policy change, local ip change, ...) X-Git-Url: http://sjero.net/git/?p=linphone;a=commitdiff_plain;h=5017c6ebbad5e66daf5119ad929a69fb089bb951 Improve uPnP behaviour (Firewall policy change, local ip change, ...) Hide uPnP firewall setting if uPnP is not available --- diff --git a/configure.ac b/configure.ac index 1372c5af..43efbff3 100644 --- a/configure.ac +++ b/configure.ac @@ -782,6 +782,7 @@ printf "* %-30s %s\n" "Account assistant" $build_wizard printf "* %-30s %s\n" "Console interface" $console_ui printf "* %-30s %s\n" "Tools" $build_tools printf "* %-30s %s\n" "zRTP encryption (GPLv3)" $zrtp +printf "* %-30s %s\n" "uPnP support" $build_upnp if test "$enable_tunnel" = "true" ; then printf "* Tunnel support\t\ttrue\n" diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index ad1bb566..608d1c6e 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1304,9 +1304,6 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta lc->tunnel=linphone_core_tunnel_new(lc); if (lc->tunnel) linphone_tunnel_configure(lc->tunnel); #endif -#ifdef BUILD_UPNP - lc->upnp = linphone_upnp_context_new(lc); -#endif //BUILD_UPNP if (lc->vtable.display_status) lc->vtable.display_status(lc,_("Ready")); lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon; @@ -4211,6 +4208,17 @@ void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy ms_warning("UPNP is not available, reset firewall policy to no firewall"); pol = LinphonePolicyNoFirewall; } +#else //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; + } + } #endif //BUILD_UPNP lc->net_conf.firewall_policy=pol; if (lc->sip_conf.contact) update_primary_contact(lc); @@ -5170,8 +5178,10 @@ static void linphone_core_uninit(LinphoneCore *lc) } #ifdef BUILD_UPNP - linphone_upnp_context_destroy(lc->upnp); - lc->upnp = NULL; + if(lc->upnp != NULL) { + linphone_upnp_context_destroy(lc->upnp); + lc->upnp = NULL; + } #endif //BUILD_UPNP if (lc->friends) @@ -5506,7 +5516,7 @@ void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook for(elem=lc->hooks;elem!=NULL;elem=elem->next){ Hook *h=(Hook*)elem->data; if (h->fun==hook && h->data==hook_data){ - ms_list_remove_link(lc->hooks,elem); + lc->hooks = ms_list_remove_link(lc->hooks,elem); ms_free(h); return; } diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 4aae6d3b..55954772 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -1097,9 +1097,18 @@ void linphone_proxy_config_update(LinphoneProxyConfig *cfg){ if (cfg->type && cfg->ssctx==NULL){ linphone_proxy_config_activate_sip_setup(cfg); } - if ((!lc->sip_conf.register_only_when_network_is_up || lc->network_reachable) && - (!lc->sip_conf.register_only_when_upnp_is_ok || linphone_core_get_upnp_state(lc) == LinphoneUpnpStateOk)) - linphone_proxy_config_register(cfg); + switch(linphone_core_get_firewall_policy(lc)) { + case LinphonePolicyUseUpnp: +#ifdef BUILD_UPNP + if(lc->upnp != NULL && !linphone_upnp_context_is_ready_for_register(lc->upnp)) { + break; + } +#endif + default: + if ((!lc->sip_conf.register_only_when_network_is_up || lc->network_reachable)) { + linphone_proxy_config_register(cfg); + } + } if (cfg->publish && cfg->publish_op==NULL){ linphone_proxy_config_send_publish(cfg,lc->presence_mode); } diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 105f4a8d..0713a1d5 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -24,6 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define UPNP_ADD_MAX_RETRY 4 #define UPNP_REMOVE_MAX_RETRY 4 #define UPNP_SECTION_NAME "uPnP" +#define UPNP_CORE_RETRY_DELAY 4 +#define UPNP_CALL_RETRY_DELAY 1 /* * uPnP Definitions @@ -41,6 +43,7 @@ typedef struct _UpnpPortBinding { int ref; bool_t to_remove; bool_t to_add; + time_t last_update; } UpnpPortBinding; typedef struct _UpnpStream { @@ -77,19 +80,24 @@ bool_t linphone_core_upnp_hook(void *data); void linphone_core_upnp_refresh(UpnpContext *ctx); UpnpPortBinding *linphone_upnp_port_binding_new(); +UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port); +UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port); UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port); bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2); UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port); UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port); +void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay); void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port); void linphone_upnp_port_binding_release(UpnpPortBinding *port); +// Configuration MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc); void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port); -int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); -int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port); +// uPnP +int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry); +int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry); /** @@ -172,7 +180,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; port_mapping->external_port = -1; //Force random external port - if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping) != 0) { + if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE) != 0) { linphone_upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping); } @@ -190,7 +198,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE: mapping = (upnp_igd_port_mapping *) arg; port_mapping = (UpnpPortBinding*) mapping->cookie; - if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping) != 0) { + if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE) != 0) { linphone_upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping); linphone_upnp_config_remove_port_binding(lupnp, port_mapping); } @@ -208,7 +216,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { if(port_mapping->to_remove) { if(port_mapping->state == LinphoneUpnpStateOk) { port_mapping->to_remove = FALSE; - linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, FALSE); } else if(port_mapping->state == LinphoneUpnpStateKo) { port_mapping->to_remove = FALSE; } @@ -216,7 +224,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { if(port_mapping->to_add) { if(port_mapping->state == LinphoneUpnpStateIdle || port_mapping->state == LinphoneUpnpStateKo) { port_mapping->to_add = FALSE; - linphone_upnp_context_send_add_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, FALSE); } } @@ -239,9 +247,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) { */ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { - LCSipTransports transport; UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1); - const char *ip_address; ms_mutex_init(&lupnp->mutex, NULL); ms_cond_init(&lupnp->empty_cond, NULL); @@ -253,31 +259,10 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { lupnp->state = LinphoneUpnpStateIdle; ms_message("uPnP IGD: New %p for core %p", lupnp, lc); - linphone_core_get_sip_transports(lc, &transport); - if(transport.udp_port != 0) { - lupnp->sip_udp = linphone_upnp_port_binding_new(); - lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - lupnp->sip_udp->local_port = transport.udp_port; - lupnp->sip_udp->external_port = transport.udp_port; - } else { - lupnp->sip_udp = NULL; - } - if(transport.tcp_port != 0) { - lupnp->sip_tcp = linphone_upnp_port_binding_new(); - lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP; - lupnp->sip_tcp->local_port = transport.tcp_port; - lupnp->sip_tcp->external_port = transport.tcp_port; - } else { - lupnp->sip_tcp = NULL; - } - if(transport.tls_port != 0) { - lupnp->sip_tls = linphone_upnp_port_binding_new(); - lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP; - lupnp->sip_tls->local_port = transport.tls_port; - lupnp->sip_tls->external_port = transport.tls_port; - } else { - lupnp->sip_tls = NULL; - } + // Init ports + lupnp->sip_udp = NULL; + lupnp->sip_tcp = NULL; + lupnp->sip_tls = NULL; linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp); @@ -289,17 +274,6 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { return NULL; } - ip_address = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); - if(lupnp->sip_udp != NULL) { - strncpy(lupnp->sip_udp->local_addr, ip_address, sizeof(lupnp->sip_udp->local_addr)); - } - if(lupnp->sip_tcp != NULL) { - strncpy(lupnp->sip_tcp->local_addr, ip_address, sizeof(lupnp->sip_tcp->local_addr)); - } - if(lupnp->sip_tls != NULL) { - strncpy(lupnp->sip_tls->local_addr, ip_address, sizeof(lupnp->sip_tls->local_addr)); - } - lupnp->state = LinphoneUpnpStatePending; upnp_igd_start(lupnp->upnp_igd_ctxt); @@ -307,22 +281,19 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) { } void linphone_upnp_context_destroy(UpnpContext *lupnp) { - /* - * Not need, all hooks are removed before - * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc); - */ + linphone_core_remove_iterate_hook(lupnp->lc, linphone_core_upnp_hook, lupnp); ms_mutex_lock(&lupnp->mutex); /* Send port binding removes */ if(lupnp->sip_udp != NULL) { - linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp, TRUE); } if(lupnp->sip_tcp != NULL) { - linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp, TRUE); } if(lupnp->sip_tls != NULL) { - linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls); + linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls, TRUE); } /* Wait all pending bindings are done */ @@ -376,31 +347,72 @@ LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) { return state; } +bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) { + bool_t ready = TRUE; + ms_mutex_lock(&lupnp->mutex); + + // 1 Check global uPnP state + ready = (lupnp->state == LinphoneUpnpStateOk); + + // 2 Check external ip address + if(ready && upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt) == NULL) { + ready = FALSE; + } + + // 3 Check sip ports bindings + if(ready) { + if(lupnp->sip_udp != NULL) { + if(lupnp->sip_udp->state != LinphoneUpnpStateOk) { + ready = FALSE; + } + } else if(lupnp->sip_tcp != NULL) { + if(lupnp->sip_tcp->state != LinphoneUpnpStateOk) { + ready = FALSE; + } + } else if(lupnp->sip_tls != NULL) { + if(lupnp->sip_tls->state != LinphoneUpnpStateOk) { + ready = FALSE; + } + } else { + ready = FALSE; + } + } + + ms_mutex_unlock(&lupnp->mutex); + return ready; +} + int linphone_upnp_context_get_external_port(UpnpContext *lupnp) { int port = -1; ms_mutex_lock(&lupnp->mutex); - /* Send port binding removes */ if(lupnp->sip_udp != NULL) { - if(lupnp->sip_udp->state == LinphoneUpnpStateOk) + if(lupnp->sip_udp->state == LinphoneUpnpStateOk) { port = lupnp->sip_udp->external_port; + } } else if(lupnp->sip_tcp != NULL) { - if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) + if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) { port = lupnp->sip_tcp->external_port; + } } else if(lupnp->sip_tls != NULL) { - if(lupnp->sip_tls->state == LinphoneUpnpStateOk) + if(lupnp->sip_tls->state == LinphoneUpnpStateOk) { port = lupnp->sip_tls->external_port; + } } ms_mutex_unlock(&lupnp->mutex); return port; } -const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx) { - return upnp_igd_get_external_ipaddress(ctx->upnp_igd_ctxt); +const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *lupnp) { + const char* addr = NULL; + ms_mutex_lock(&lupnp->mutex); + addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); + ms_mutex_unlock(&lupnp->mutex); + return addr; } -int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { +int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) { upnp_igd_port_mapping mapping; char description[128]; int ret; @@ -428,6 +440,11 @@ int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBind return 0; } } + + // No retry if specified + if(port->retry != 0 && !retry) { + return -1; + } if(port->retry >= UPNP_ADD_MAX_RETRY) { ret = -1; @@ -459,7 +476,7 @@ int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBind return ret; } -int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) { +int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) { upnp_igd_port_mapping mapping; int ret; @@ -485,6 +502,11 @@ int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortB return 0; } } + + // No retry if specified + if(port->retry != 0 && !retry) { + return 1; + } if(port->retry >= UPNP_REMOVE_MAX_RETRY) { ret = -1; @@ -513,7 +535,6 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool LinphoneCore *lc = call->core; UpnpContext *lupnp = lc->upnp; int ret = -1; - const char *local_addr, *external_addr; if(lupnp == NULL) { return ret; @@ -523,58 +544,24 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool // Don't handle when the call if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) { ret = 0; - local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); - external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); /* * Audio part */ - strncpy(call->upnp_session->audio->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->audio->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->audio->rtp->local_port = call->audio_port; - if(call->upnp_session->audio->rtp->external_port == -1) { - call->upnp_session->audio->rtp->external_port = call->audio_port; - } - strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->audio->rtcp->local_port = call->audio_port+1; - if(call->upnp_session->audio->rtcp->external_port == -1) { - call->upnp_session->audio->rtcp->external_port = call->audio_port+1; - } - if(audio) { - // Add audio port binding - linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtp); - linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtcp); - } else { - // Remove audio port binding - linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtp); - linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtcp); - } + linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtp, + UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port:0, UPNP_CALL_RETRY_DELAY); + linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtcp, + UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port+1:0, UPNP_CALL_RETRY_DELAY); + /* * Video part */ - strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->video->rtp->local_port = call->video_port; - if(call->upnp_session->video->rtp->external_port == -1) { - call->upnp_session->video->rtp->external_port = call->video_port; - } - strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE); - strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE); - call->upnp_session->video->rtcp->local_port = call->video_port+1; - if(call->upnp_session->video->rtcp->external_port == -1) { - call->upnp_session->video->rtcp->external_port = call->video_port+1; - } - if(video) { - // Add video port binding - linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtp); - linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtcp); - } else { - // Remove video port binding - linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtp); - linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtcp); - } + linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtp, + UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port:0, UPNP_CALL_RETRY_DELAY); + + linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtcp, + UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port+1:0, UPNP_CALL_RETRY_DELAY); } ms_mutex_unlock(&lupnp->mutex); @@ -588,6 +575,7 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool } + int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) { bool_t audio = FALSE; bool_t video = FALSE; @@ -615,6 +603,23 @@ void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) { call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = call->upnp_session->video->state; } +void linphone_upnp_update_stream_state(UpnpStream *stream) { + if((stream->rtp == NULL || stream->rtp->state == LinphoneUpnpStateOk || stream->rtp->state == LinphoneUpnpStateIdle) && + (stream->rtcp == NULL || stream->rtcp->state == LinphoneUpnpStateOk || stream->rtcp->state == LinphoneUpnpStateIdle)) { + stream->state = LinphoneUpnpStateOk; + } else if((stream->rtp != NULL && + (stream->rtp->state == LinphoneUpnpStateAdding || stream->rtp->state == LinphoneUpnpStateRemoving)) || + (stream->rtcp != NULL && + (stream->rtcp->state == LinphoneUpnpStateAdding || stream->rtcp->state == LinphoneUpnpStateRemoving))) { + stream->state = LinphoneUpnpStatePending; + } else if((stream->rtp != NULL && stream->rtp->state == LinphoneUpnpStateKo) || + (stream->rtcp != NULL && stream->rtcp->state == LinphoneUpnpStateKo)) { + stream->state = LinphoneUpnpStateKo; + } else { + ms_error("Invalid stream %p state", stream); + } +} + int linphone_upnp_call_process(LinphoneCall *call) { LinphoneCore *lc = call->core; UpnpContext *lupnp = lc->upnp; @@ -634,38 +639,12 @@ int linphone_upnp_call_process(LinphoneCall *call) { /* * Update Audio state */ - if((call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle) && - (call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle)) { - call->upnp_session->audio->state = LinphoneUpnpStateOk; - } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateAdding || - call->upnp_session->audio->rtp->state == LinphoneUpnpStateRemoving || - call->upnp_session->audio->rtcp->state == LinphoneUpnpStateAdding || - call->upnp_session->audio->rtcp->state == LinphoneUpnpStateRemoving) { - call->upnp_session->audio->state = LinphoneUpnpStatePending; - } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateKo || - call->upnp_session->audio->rtp->state == LinphoneUpnpStateKo) { - call->upnp_session->audio->state = LinphoneUpnpStateKo; - } else { - call->upnp_session->audio->state = LinphoneUpnpStateIdle; - } + linphone_upnp_update_stream_state(call->upnp_session->audio); /* * Update Video state */ - if((call->upnp_session->video->rtp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle) && - (call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle)) { - call->upnp_session->video->state = LinphoneUpnpStateOk; - } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateAdding || - call->upnp_session->video->rtp->state == LinphoneUpnpStateRemoving || - call->upnp_session->video->rtcp->state == LinphoneUpnpStateAdding || - call->upnp_session->video->rtcp->state == LinphoneUpnpStateRemoving) { - call->upnp_session->video->state = LinphoneUpnpStatePending; - } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateKo || - call->upnp_session->video->rtp->state == LinphoneUpnpStateKo) { - call->upnp_session->video->state = LinphoneUpnpStateKo; - } else { - call->upnp_session->video->state = LinphoneUpnpStateIdle; - } + linphone_upnp_update_stream_state(call->upnp_session->video); /* * Update session state @@ -733,7 +712,6 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) { ms_message("uPnP IGD: Refresh mappings"); - /* Remove context port bindings */ if(lupnp->sip_udp != NULL) { global_list = ms_list_append(global_list, lupnp->sip_udp); } @@ -744,26 +722,32 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) { global_list = ms_list_append(global_list, lupnp->sip_tls); } - /* Remove call port bindings */ list = lupnp->lc->calls; while(list != NULL) { call = (LinphoneCall *)list->data; if(call->upnp_session != NULL) { - global_list = ms_list_append(global_list, call->upnp_session->audio->rtp); - global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp); - global_list = ms_list_append(global_list, call->upnp_session->video->rtp); - global_list = ms_list_append(global_list, call->upnp_session->video->rtcp); + if(call->upnp_session->audio->rtp != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->audio->rtp); + } + if(call->upnp_session->audio->rtcp != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp); + } + if(call->upnp_session->video->rtp != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->video->rtp); + } + if(call->upnp_session->video->rtcp != NULL) { + global_list = ms_list_append(global_list, call->upnp_session->video->rtcp); + } } list = list->next; } - // Remove port binding configurations list = linphone_upnp_config_list_port_bindings(lupnp->lc->config); for(item = list;item != NULL; item = item->next) { port_mapping = (UpnpPortBinding *)item->data; port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping); if(port_mapping2 == NULL) { - linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE); } else if(port_mapping2->state == LinphoneUpnpStateIdle){ /* Force to remove */ port_mapping2->state = LinphoneUpnpStateOk; @@ -777,20 +761,76 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) { list = global_list; while(list != NULL) { port_mapping = (UpnpPortBinding *)list->data; - linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping); - linphone_upnp_context_send_add_port_binding(lupnp, port_mapping); + linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE); + linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE); list = list->next; } global_list = ms_list_free(global_list); } +void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay) { + const char *local_addr, *external_addr; + time_t now = time(NULL); + if(port != 0) { + if(*port_mapping != NULL) { + if(port != (*port_mapping)->local_port) { + linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE); + *port_mapping = NULL; + } + } + if(*port_mapping == NULL) { + *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port); + } + + // Get addresses + local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt); + external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt); + + // Force binding update on local address change + if(local_addr != NULL) { + if(strncmp((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr))) { + linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE); + strncpy((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr)); + } + } else { + ms_warning("uPnP IGD: can't get local address"); + } + if(external_addr != NULL) { + strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr)); + } else { + ms_warning("uPnP IGD: can't get external address"); + } + + // Add (if not already done) the binding + if(now - (*port_mapping)->last_update >= retry_delay) { + (*port_mapping)->last_update = now; + linphone_upnp_context_send_add_port_binding(lupnp, *port_mapping, FALSE); + } + } else { + if(*port_mapping != NULL) { + linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE); + *port_mapping = NULL; + } + } +} + bool_t linphone_core_upnp_hook(void *data) { char key[64]; + LCSipTransports transport; MSList *item; UpnpPortBinding *port_mapping; UpnpContext *lupnp = (UpnpContext *)data; + ms_mutex_lock(&lupnp->mutex); + /* Update ports */ + if(lupnp->state == LinphoneUpnpStateOk) { + linphone_core_get_sip_transports(lupnp->lc, &transport); + linphone_upnp_update_port_binding(lupnp, &lupnp->sip_udp, UPNP_IGD_IP_PROTOCOL_UDP, transport.udp_port, UPNP_CORE_RETRY_DELAY); + linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tcp, UPNP_IGD_IP_PROTOCOL_TCP, transport.tcp_port, UPNP_CORE_RETRY_DELAY); + linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tls, UPNP_IGD_IP_PROTOCOL_TCP, transport.tls_port, UPNP_CORE_RETRY_DELAY); + } + /* Add configs */ for(item = lupnp->adding_configs;item!=NULL;item=item->next) { port_mapping = (UpnpPortBinding *)item->data; @@ -835,11 +875,11 @@ int linphone_core_update_local_media_description_from_upnp(SalMediaDescription * upnpStream = session->video; } if(upnpStream != NULL) { - if(upnpStream->rtp->state == LinphoneUpnpStateOk) { + if(upnpStream->rtp != NULL && upnpStream->rtp->state == LinphoneUpnpStateOk) { strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE); stream->rtp_port = upnpStream->rtp->external_port; } - if(upnpStream->rtcp->state == LinphoneUpnpStateOk) { + if(upnpStream->rtcp != NULL && upnpStream->rtcp->state == LinphoneUpnpStateOk) { strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE); stream->rtcp_port = upnpStream->rtcp->external_port; } @@ -858,6 +898,7 @@ UpnpPortBinding *linphone_upnp_port_binding_new() { port = ms_new0(UpnpPortBinding,1); ms_mutex_init(&port->mutex, NULL); port->state = LinphoneUpnpStateIdle; + port->protocol = UPNP_IGD_IP_PROTOCOL_UDP; port->local_addr[0] = '\0'; port->local_port = -1; port->external_addr[0] = '\0'; @@ -865,9 +906,30 @@ UpnpPortBinding *linphone_upnp_port_binding_new() { port->to_remove = FALSE; port->to_add = FALSE; port->ref = 1; + port->last_update = 0; return port; } +UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port) { + UpnpPortBinding *port_binding = linphone_upnp_port_binding_new(); + port_binding->protocol = protocol; + port_binding->local_port = local_port; + port_binding->external_port = external_port; + return port_binding; +} + +UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port) { + UpnpPortBinding *tmp_binding; + UpnpPortBinding *end_binding; + end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, external_port); + tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding); + if(tmp_binding != NULL) { + linphone_upnp_port_binding_release(end_binding); + end_binding = tmp_binding; + } + return end_binding; +} + UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) { UpnpPortBinding *new_port = NULL; new_port = ms_new0(UpnpPortBinding,1); @@ -939,18 +1001,20 @@ void linphone_upnp_port_binding_release(UpnpPortBinding *port) { UpnpStream* linphone_upnp_stream_new() { UpnpStream *stream = ms_new0(UpnpStream,1); stream->state = LinphoneUpnpStateIdle; - stream->rtp = linphone_upnp_port_binding_new(); - stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; - stream->rtcp = linphone_upnp_port_binding_new(); - stream->rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP; + stream->rtp = NULL; + stream->rtcp = NULL; return stream; } void linphone_upnp_stream_destroy(UpnpStream* stream) { - linphone_upnp_port_binding_release(stream->rtp); - stream->rtp = NULL; - linphone_upnp_port_binding_release(stream->rtcp); - stream->rtcp = NULL; + if(stream->rtp != NULL) { + linphone_upnp_port_binding_release(stream->rtp); + stream->rtp = NULL; + } + if(stream->rtcp != NULL) { + linphone_upnp_port_binding_release(stream->rtcp); + stream->rtcp = NULL; + } ms_free(stream); } @@ -973,10 +1037,18 @@ void linphone_upnp_session_destroy(UpnpSession *session) { if(lc->upnp != NULL) { /* Remove bindings */ - linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp); - linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp); - linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp); - linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp); + if(session->audio->rtp != NULL) { + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp, TRUE); + } + if(session->audio->rtcp != NULL) { + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp, TRUE); + } + if(session->video->rtp != NULL) { + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp, TRUE); + } + if(session->video->rtcp != NULL) { + linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE); + } } linphone_upnp_stream_destroy(session->audio); diff --git a/coreapi/upnp.h b/coreapi/upnp.h index 08483588..fe403a77 100644 --- a/coreapi/upnp.h +++ b/coreapi/upnp.h @@ -41,6 +41,7 @@ void linphone_upnp_context_destroy(UpnpContext *ctx); LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx); const char *linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx); int linphone_upnp_context_get_external_port(UpnpContext *ctx); +bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *ctx); void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call); #endif //LINPHONE_UPNP_H diff --git a/gtk/propertybox.c b/gtk/propertybox.c index c2258ea5..5aee3f43 100644 --- a/gtk/propertybox.c +++ b/gtk/propertybox.c @@ -1083,6 +1083,10 @@ void linphone_gtk_show_parameters(void){ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_upnp")),TRUE); break; } + if(!linphone_core_upnp_available(lc)) { + gtk_widget_hide(linphone_gtk_get_widget(pb,"use_upnp")); + } + mtu=linphone_core_get_mtu(lc); if (mtu<=0){ gtk_widget_set_sensitive(linphone_gtk_get_widget(pb,"mtu"),FALSE);