Improve uPnP behaviour (Firewall policy change, local ip change, ...)

Hide uPnP firewall setting if uPnP is not available
This commit is contained in:
Yann Diorcet 2013-02-15 16:25:47 +01:00
parent e3c60a69c8
commit 5017c6ebba
6 changed files with 262 additions and 165 deletions

View file

@ -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"

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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

View file

@ -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);