]> sjero.net Git - linphone/blobdiff - coreapi/upnp.c
Fix upnp forgotten retain
[linphone] / coreapi / upnp.c
index e9ce18df7661c469167e44309895a3764bc5fdee..0922dad59eb7aa7c66d8f7517da589715f2c1422 100644 (file)
@@ -20,14 +20,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "upnp.h"
 #include "private.h"
 #include "lpconfig.h"
+#include <ctype.h>
+
+#define UPNP_STRINGIFY(x)     #x
+#define UPNP_TOSTRING(x)      UPNP_STRINGIFY(x)
 
 #define UPNP_ADD_MAX_RETRY    4
 #define UPNP_REMOVE_MAX_RETRY 4
 #define UPNP_SECTION_NAME     "uPnP"
 #define UPNP_CORE_READY_CHECK 1 
-#define UPNP_CORE_RETRY_DELAY 4
-#define UPNP_CALL_RETRY_DELAY 1
-
+#define UPNP_CORE_RETRY_DELAY 10
+#define UPNP_CALL_RETRY_DELAY 3
+#define UPNP_UUID_LEN         128
+#define UPNP_UUID_LEN_STR     UPNP_TOSTRING(UPNP_UUID_LEN)
 /*
  * uPnP Definitions
  */
@@ -36,6 +41,7 @@ typedef struct _UpnpPortBinding {
        ms_mutex_t mutex;
        LinphoneUpnpState state;
        upnp_igd_ip_protocol protocol;
+       char *device_id;
        char local_addr[LINPHONE_IPADDR_SIZE];
        int local_port;
        char external_addr[LINPHONE_IPADDR_SIZE];
@@ -86,6 +92,7 @@ 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);
+void linphone_upnp_port_binding_set_device_id(UpnpPortBinding *port, const char * device_id);
 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);
@@ -93,9 +100,10 @@ void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **por
 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
 void linphone_upnp_port_binding_release(UpnpPortBinding *port);
 void linphone_upnp_update_config(UpnpContext *lupnp);
+void linphone_upnp_update_proxy(UpnpContext *lupnp, bool_t force);
 
 // Configuration
-MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc);
+MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc, const char *device_id);
 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
 
@@ -103,6 +111,62 @@ void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPort
 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);
 
+static int linphone_upnp_strncmpi(const char *str1, const char *str2, int len) {
+       int i = 0;
+       char char1, char2;
+       while(i < len) {
+               char1 = toupper(*str1);
+               char2 = toupper(*str2);
+               if(char1 == '\0' || char1 != char2) {
+                       return char1 - char2;
+               }
+               str1++;
+               str2++;
+               i++;
+       }
+       return 0;
+}
+
+static int linphone_upnp_str_min(const char *str1, const char *str2) {
+       int len1 = strlen(str1);
+       int len2 = strlen(str2);
+       if(len1 > len2) {
+               return len2;
+       }
+       return len1;
+}
+
+char * linphone_upnp_format_device_id(const char *device_id) {
+       char *ret = NULL;
+       char *tmp;
+       char tchar;
+       bool_t copy;    
+       if(device_id == NULL) {
+               return ret;
+       }
+       ret = ms_new(char, UPNP_UUID_LEN + 1);
+       tmp = ret;
+       if(linphone_upnp_strncmpi(device_id, "uuid:", linphone_upnp_str_min(device_id, "uuid:")) == 0) {
+               device_id += strlen("uuid:");
+       }
+       while(*device_id != '\0' && tmp - ret < UPNP_UUID_LEN) {
+               copy = FALSE;
+               tchar = *device_id;
+               if(tchar >= '0' && tchar <= '9')
+                       copy = TRUE;
+               if(!copy && tchar >= 'A' && tchar <= 'Z')
+                       copy = TRUE;
+               if(!copy && tchar >= 'a' && tchar <= 'z')
+                       copy = TRUE;
+               if(copy) {
+                       *tmp = *device_id;
+                       tmp++;
+               }
+               device_id++;
+       }
+       *tmp = '\0';
+       return ret;
+}
 
 /**
  * uPnP Callbacks
@@ -240,7 +304,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
         * If there is no pending binding emit a signal
         */
        if(lupnp->pending_bindings == NULL) {
-               pthread_cond_signal(&lupnp->empty_cond);
+               ms_cond_signal(&lupnp->empty_cond);
        }
        ms_mutex_unlock(&lupnp->mutex);
 }
@@ -274,7 +338,7 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
        linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp);
 
        lupnp->upnp_igd_ctxt = NULL;
-       lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lupnp);
+       lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, NULL, lupnp);
        if(lupnp->upnp_igd_ctxt == NULL) {
                lupnp->state = LinphoneUpnpStateKo;
                ms_error("Can't create uPnP IGD context");
@@ -292,15 +356,17 @@ void linphone_upnp_context_destroy(UpnpContext *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, TRUE);
-       }
-       if(lupnp->sip_tcp != NULL) {
-               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, TRUE);
+       if(lupnp->lc->network_reachable) {
+               /* Send port binding removes */
+               if(lupnp->sip_udp != NULL) {
+                       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, TRUE);
+               }
+               if(lupnp->sip_tls != NULL) {
+                       linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls, TRUE);
+               }
        }
 
        /* Wait all pending bindings are done */
@@ -308,14 +374,18 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) {
                ms_message("uPnP IGD: Wait all pending port bindings ...");
                ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex);
        }
+       ms_mutex_unlock(&lupnp->mutex);
 
        if(lupnp->upnp_igd_ctxt != NULL) {
                upnp_igd_destroy(lupnp->upnp_igd_ctxt);
                lupnp->upnp_igd_ctxt = NULL;
        }
+       
+       /* No more multi threading here */
 
-       /* Run one more time configuration update */
+       /* Run one more time configuration update and proxy */
        linphone_upnp_update_config(lupnp);
+       linphone_upnp_update_proxy(lupnp, TRUE);
 
        /* Release port bindings */
        if(lupnp->sip_udp != NULL) {
@@ -339,8 +409,6 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) {
        ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))linphone_upnp_port_binding_release);
        lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings);
        
-       ms_mutex_unlock(&lupnp->mutex);
-
        ms_mutex_destroy(&lupnp->mutex);
        ms_cond_destroy(&lupnp->empty_cond);
 
@@ -349,10 +417,12 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) {
 }
 
 LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) {
-       LinphoneUpnpState state;
-       ms_mutex_lock(&lupnp->mutex);
-       state = lupnp->state;
-       ms_mutex_unlock(&lupnp->mutex);
+       LinphoneUpnpState state = LinphoneUpnpStateKo;
+       if(lupnp != NULL) {
+               ms_mutex_lock(&lupnp->mutex);
+               state = lupnp->state;
+               ms_mutex_unlock(&lupnp->mutex);
+       }
        return state;
 }
 
@@ -392,40 +462,50 @@ bool_t _linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
 }
 
 bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
-       bool_t ready;
-       ms_mutex_lock(&lupnp->mutex);
-       ready = _linphone_upnp_context_is_ready_for_register(lupnp);
-       ms_mutex_unlock(&lupnp->mutex);
+       bool_t ready = FALSE;
+       if(lupnp != NULL) {
+               ms_mutex_lock(&lupnp->mutex);
+               ready = _linphone_upnp_context_is_ready_for_register(lupnp);
+               ms_mutex_unlock(&lupnp->mutex);
+       }
        return ready;
 }
 
 int linphone_upnp_context_get_external_port(UpnpContext *lupnp) {
        int port = -1;
-       ms_mutex_lock(&lupnp->mutex);
-       
-       if(lupnp->sip_udp != NULL) {
-               if(lupnp->sip_udp->state == LinphoneUpnpStateOk) {
-                       port = lupnp->sip_udp->external_port;
-               }
-       } else if(lupnp->sip_tcp != NULL) {
-               if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) {
-                       port = lupnp->sip_tcp->external_port;
-               }
-       } else if(lupnp->sip_tls != NULL) {
-               if(lupnp->sip_tls->state == LinphoneUpnpStateOk) {
-                       port = lupnp->sip_tls->external_port;
+       if(lupnp != NULL) {
+               ms_mutex_lock(&lupnp->mutex);
+               
+               if(lupnp->sip_udp != NULL) {
+                       if(lupnp->sip_udp->state == LinphoneUpnpStateOk) {
+                               port = lupnp->sip_udp->external_port;
+                       }
+               } else if(lupnp->sip_tcp != NULL) {
+                       if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) {
+                               port = lupnp->sip_tcp->external_port;
+                       }
+               } else if(lupnp->sip_tls != NULL) {
+                       if(lupnp->sip_tls->state == LinphoneUpnpStateOk) {
+                               port = lupnp->sip_tls->external_port;
+                       }
                }
+               
+               ms_mutex_unlock(&lupnp->mutex);
        }
-       
-       ms_mutex_unlock(&lupnp->mutex);
        return port;
 }
 
+void linphone_upnp_refresh(UpnpContext * lupnp) {
+       upnp_igd_refresh(lupnp->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);
+       if(lupnp != NULL) {
+               ms_mutex_lock(&lupnp->mutex);
+               addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
+               ms_mutex_unlock(&lupnp->mutex);
+       }
        return addr;
 }
 
@@ -466,15 +546,15 @@ int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBind
        if(port->retry >= UPNP_ADD_MAX_RETRY) {
                ret = -1;
        } else {
+               linphone_upnp_port_binding_set_device_id(port, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
                mapping.cookie = linphone_upnp_port_binding_retain(port);
                lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
 
                mapping.local_port = port->local_port;
                mapping.local_host = port->local_addr;
                if(port->external_port == -1)
-                       mapping.remote_port = rand()%(0xffff - 1024) + 1024;
-               else
-                       mapping.remote_port = port->external_port;
+                       port->external_port = rand()%(0xffff - 1024) + 1024;
+               mapping.remote_port = port->external_port;
                mapping.remote_host = "";
                snprintf(description, 128, "%s %s at %s:%d",
                                PACKAGE_NAME,
@@ -528,6 +608,7 @@ int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortB
        if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
                ret = -1;
        } else {
+               linphone_upnp_port_binding_set_device_id(port, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
                mapping.cookie = linphone_upnp_port_binding_retain(port);
                lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
 
@@ -758,7 +839,7 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) {
                list = list->next;
        }
 
-       list = linphone_upnp_config_list_port_bindings(lupnp->lc->config);
+       list = linphone_upnp_config_list_port_bindings(lupnp->lc->config, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
        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);
@@ -834,10 +915,11 @@ void linphone_upnp_update_config(UpnpContext* lupnp) {
        /* Add configs */
        for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
                port_mapping = (UpnpPortBinding *)item->data;
-               snprintf(key, sizeof(key), "%s-%d-%d",
+               snprintf(key, sizeof(key), "%s-%s-%d-%d",
+                                       port_mapping->device_id,
                                        (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
-                                                       port_mapping->external_port,
-                                                       port_mapping->local_port);
+                                       port_mapping->external_port,
+                                       port_mapping->local_port);
                lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP");
                linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
        }
@@ -847,10 +929,11 @@ void linphone_upnp_update_config(UpnpContext* lupnp) {
        /* Remove configs */
        for(item = lupnp->removing_configs;item!=NULL;item=item->next) {
                port_mapping = (UpnpPortBinding *)item->data;
-               snprintf(key, sizeof(key), "%s-%d-%d",
+               snprintf(key, sizeof(key), "%s-%s-%d-%d",
+                                       port_mapping->device_id,
                                        (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
-                                                       port_mapping->external_port,
-                                                       port_mapping->local_port);
+                                       port_mapping->external_port,
+                                       port_mapping->local_port);
                lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL);
                linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
        }
@@ -858,23 +941,11 @@ void linphone_upnp_update_config(UpnpContext* lupnp) {
        lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
 }
 
-bool_t linphone_core_upnp_hook(void *data) {
-       LCSipTransports transport;
+void linphone_upnp_update_proxy(UpnpContext* lupnp, bool_t force) {
        LinphoneUpnpState ready_state;
        const MSList *item;
-       time_t now = time(NULL);
-       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);
-       }
-
+       time_t now = (force)? (lupnp->last_ready_check + UPNP_CORE_READY_CHECK) : time(NULL);
+       
        /* Refresh registers if we are ready */
        if(now - lupnp->last_ready_check >= UPNP_CORE_READY_CHECK) {
                lupnp->last_ready_check = now;
@@ -887,6 +958,8 @@ bool_t linphone_core_upnp_hook(void *data) {
                                                // Only reset ithe registration if we require that upnp should be ok
                                                if(lupnp->lc->sip_conf.register_only_when_upnp_is_ok) {
                                                        linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone, "Registration impossible (uPnP not ready)");
+                                               } else {
+                                                       cfg->commit=TRUE;
                                                }
                                        } else {
                                                cfg->commit=TRUE;
@@ -896,7 +969,23 @@ bool_t linphone_core_upnp_hook(void *data) {
                        lupnp->last_ready_state = ready_state;
                }
        }
-                       
+}
+
+bool_t linphone_core_upnp_hook(void *data) {
+       LCSipTransports transport;
+       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);
+       }
+
+       linphone_upnp_update_proxy(lupnp, FALSE);       
        linphone_upnp_update_config(lupnp);
 
        ms_mutex_unlock(&lupnp->mutex);
@@ -941,6 +1030,7 @@ UpnpPortBinding *linphone_upnp_port_binding_new() {
        ms_mutex_init(&port->mutex, NULL);
        port->state = LinphoneUpnpStateIdle;
        port->protocol = UPNP_IGD_IP_PROTOCOL_UDP;      
+       port->device_id = NULL;
        port->local_addr[0] = '\0';
        port->local_port = -1;
        port->external_addr[0] = '\0';
@@ -963,11 +1053,17 @@ UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_prot
 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);
+       
+       // Seek an binding with same protocol and local port
+       end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, -1);
        tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding);
-       if(tmp_binding != NULL) {
+       
+       // Must be not attached to any struct 
+       if(tmp_binding != NULL && tmp_binding->ref == 1) {
                linphone_upnp_port_binding_release(end_binding);
-               end_binding = tmp_binding;
+               end_binding = linphone_upnp_port_binding_retain(tmp_binding);
+       } else {
+               end_binding->external_port = external_port;
        }
        return end_binding;     
 } 
@@ -976,11 +1072,27 @@ UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) {
        UpnpPortBinding *new_port = NULL;
        new_port = ms_new0(UpnpPortBinding,1);
        memcpy(new_port, port, sizeof(UpnpPortBinding));
+       new_port->device_id = NULL;
+       linphone_upnp_port_binding_set_device_id(new_port, port->device_id);
        ms_mutex_init(&new_port->mutex, NULL);
        new_port->ref = 1;
        return new_port;
 }
 
+void linphone_upnp_port_binding_set_device_id(UpnpPortBinding *port, const char *device_id) {
+       char *formated_device_id = linphone_upnp_format_device_id(device_id);
+       if(formated_device_id != NULL && port->device_id != NULL) {
+               if(strcmp(formated_device_id, port->device_id) == 0) {
+                       ms_free(formated_device_id);
+                       return;
+               }
+       }
+       if(port->device_id != NULL) {
+               ms_free(port->device_id);
+       }
+       port->device_id = formated_device_id;
+}
+
 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
        if(strlen(port->local_addr)) {
                ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d (retry %d)", msg,
@@ -998,10 +1110,11 @@ void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBi
        }
 }
 
+// Return true if the binding are equivalent. (Note external_port == -1 means "don't care")
 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
        return port1->protocol == port2->protocol &&
-                       port1->local_port == port2->local_port &&
-                       port1->external_port == port2->external_port;
+              port1->local_port == port2->local_port &&
+              (port1->external_port == -1 || port2->external_port == -1 || port1->external_port == port2->external_port);
 }
 
 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port) {
@@ -1027,6 +1140,9 @@ UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port) {
 void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
        ms_mutex_lock(&port->mutex);
        if(--port->ref == 0) {
+               if(port->device_id != NULL) {
+                       ms_free(port->device_id);
+               }
                ms_mutex_unlock(&port->mutex);
                ms_mutex_destroy(&port->mutex);
                ms_free(port);
@@ -1092,7 +1208,10 @@ void linphone_upnp_session_destroy(UpnpSession *session) {
                        linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE);
                }
        }
-
+       
+       session->call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = LinphoneUpnpStateKo;
+       session->call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = LinphoneUpnpStateKo;
+       
        linphone_upnp_stream_destroy(session->audio);
        linphone_upnp_stream_destroy(session->video);
        ms_free(session);
@@ -1110,25 +1229,35 @@ LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) {
 struct linphone_upnp_config_list_port_bindings_struct {
        struct _LpConfig *lpc;
        MSList *retList;
+       const char *device_id;
 };
 
 static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct linphone_upnp_config_list_port_bindings_struct *cookie) {
+       char device_id[UPNP_UUID_LEN + 1];
        char protocol_str[4]; // TCP or UDP
        upnp_igd_ip_protocol protocol;
        int external_port;
        int local_port;
+       int ret;
        bool_t valid = TRUE;
        UpnpPortBinding *port;
-       if(sscanf(entry, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) {
-               if(strcasecmp(protocol_str, "TCP") == 0) {
+       
+       ret = sscanf(entry, "%"UPNP_UUID_LEN_STR"[^-]-%3s-%i-%i", device_id, protocol_str, &external_port, &local_port);
+       if(ret == 4) {
+               // Handle only wanted device bindings
+               if(device_id != NULL && strcmp(cookie->device_id, device_id) != 0) {
+                       return; 
+               }
+               if(linphone_upnp_strncmpi(protocol_str, "TCP", 3) == 0) {
                        protocol = UPNP_IGD_IP_PROTOCOL_TCP;
-               } else if(strcasecmp(protocol_str, "UDP") == 0) {
+               } else if(linphone_upnp_strncmpi(protocol_str, "UDP", 3) == 0) {
                        protocol = UPNP_IGD_IP_PROTOCOL_UDP;
                } else {
                        valid = FALSE;
                }
                if(valid) {
                        port = linphone_upnp_port_binding_new();
+                       linphone_upnp_port_binding_set_device_id(port, device_id);
                        port->state = LinphoneUpnpStateOk;
                        port->protocol = protocol;
                        port->external_port = external_port;
@@ -1143,15 +1272,22 @@ static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct
        }
 }
 
-MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) {
-       struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL};
+MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc, const char *device_id) {
+       char *formated_device_id = linphone_upnp_format_device_id(device_id);
+       struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL, formated_device_id};
        lp_config_for_each_entry(lpc, UPNP_SECTION_NAME, (void(*)(const char *, void*))linphone_upnp_config_list_port_bindings_cb, &cookie);
+       ms_free(formated_device_id);
        return cookie.retList;
 }
 
 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
        MSList *list;
        UpnpPortBinding *list_port;
+       
+       if(port->device_id == NULL) {
+               ms_error("Can't remove port binding without device_id");
+               return;
+       }
 
        list = lupnp->removing_configs;
        while(list != NULL) {
@@ -1181,6 +1317,11 @@ void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPort
        MSList *list;
        UpnpPortBinding *list_port;
 
+       if(port->device_id == NULL) {
+               ms_error("Can't remove port binding without device_id");
+               return;
+       }
+
        list = lupnp->adding_configs;
        while(list != NULL) {
                list_port = (UpnpPortBinding *)list->data;