#include "upnp.h"
#include "private.h"
#include "lpconfig.h"
+#include <ctype.h>
-#define UPNP_ADD_MAX_RETRY 4
-#define UPNP_REMOVE_MAX_RETRY 4
-#define UPNP_SECTION_NAME "uPnP"
+#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 10
+#define UPNP_CALL_RETRY_DELAY 3
+#define UPNP_UUID_LEN 128
+#define UPNP_UUID_LEN_STR UPNP_TOSTRING(UPNP_UUID_LEN)
/*
* uPnP Definitions
*/
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];
int ref;
bool_t to_remove;
bool_t to_add;
+ time_t last_update;
} UpnpPortBinding;
typedef struct _UpnpStream {
ms_mutex_t mutex;
ms_cond_t empty_cond;
-
+
+ time_t last_ready_check;
+ LinphoneUpnpState last_ready_state;
};
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);
+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);
+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);
+void linphone_upnp_update_config(UpnpContext *lupnp);
+void linphone_upnp_update_proxy(UpnpContext *lupnp, bool_t force);
-MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc);
+// Configuration
+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);
-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);
+
+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
old_state = lupnp->state;
switch(event) {
+ case UPNP_IGD_DEVICE_ADDED:
+ case UPNP_IGD_DEVICE_REMOVED:
case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED:
case UPNP_IGD_NAT_ENABLED_CHANGED:
case UPNP_IGD_CONNECTION_STATUS_CHANGED:
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);
}
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);
}
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;
}
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);
}
}
* 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);
}
*/
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);
+ lupnp->last_ready_check = 0;
+ lupnp->last_ready_state = LinphoneUpnpStateIdle;
+
lupnp->lc = lc;
lupnp->pending_bindings = NULL;
lupnp->adding_configs = NULL;
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);
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");
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);
}
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);
- /* Send port binding removes */
- if(lupnp->sip_udp != NULL) {
- linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp);
- lupnp->sip_udp = NULL;
- }
- if(lupnp->sip_tcp != NULL) {
- linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp);
- lupnp->sip_tcp = NULL;
- }
- if(lupnp->sip_tls != NULL) {
- linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls);
- lupnp->sip_tcp = NULL;
+ ms_mutex_lock(&lupnp->mutex);
+
+ 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 */
- ms_message("uPnP IGD: Wait all pending port bindings ...");
- ms_mutex_lock(&lupnp->mutex);
- ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex);
+ if(lupnp->pending_bindings != NULL) {
+ 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 time the hook for configuration update */
- linphone_core_upnp_hook(lupnp);
+ /* 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) {
lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
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_destroy(&lupnp->mutex);
ms_cond_destroy(&lupnp->empty_cond);
ms_free(lupnp);
}
-LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx) {
- return ctx->state;
+LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) {
+ LinphoneUpnpState state = LinphoneUpnpStateKo;
+ if(lupnp != NULL) {
+ ms_mutex_lock(&lupnp->mutex);
+ state = lupnp->state;
+ ms_mutex_unlock(&lupnp->mutex);
+ }
+ return state;
+}
+
+bool_t _linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
+ bool_t ready = TRUE;
+
+ // 1 Check global uPnP state
+ ready = (lupnp->state == LinphoneUpnpStateOk);
+
+ // 2 Check external ip address
+ if(ready) {
+ if (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;
+ }
+ }
+
+ return ready;
+}
+
+bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
+ 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;
+ 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);
+ }
+ return port;
+}
+
+void linphone_upnp_refresh(UpnpContext * lupnp) {
+ upnp_igd_refresh(lupnp->upnp_igd_ctxt);
}
-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;
+ 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;
}
-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;
+
+ if(lupnp->state != LinphoneUpnpStateOk) {
+ return -2;
+ }
// Compute port binding state
if(port->state != LinphoneUpnpStateAdding) {
return 0;
}
}
+
+ // No retry if specified
+ if(port->retry != 0 && !retry) {
+ return -1;
+ }
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,
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;
+
+ if(lupnp->state != LinphoneUpnpStateOk) {
+ return -2;
+ }
// Compute port binding state
if(port->state != LinphoneUpnpStateRemoving) {
return 0;
}
}
+
+ // No retry if specified
+ if(port->retry != 0 && !retry) {
+ return 1;
+ }
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);
LinphoneCore *lc = call->core;
UpnpContext *lupnp = lc->upnp;
int ret = -1;
- const char *local_addr, *external_addr;
if(lupnp == NULL) {
return ret;
}
ms_mutex_lock(&lupnp->mutex);
+
// 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);
}
+
int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) {
bool_t audio = FALSE;
bool_t video = FALSE;
int i;
const SalStreamDescription *stream;
- for (i = 0; i < md->nstreams; i++) {
+ for (i = 0; i < md->n_total_streams; i++) {
stream = &md->streams[i];
if(stream->type == SalAudio) {
audio = TRUE;
return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
}
+void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) {
+ call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = call->upnp_session->audio->state;
+ 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;
int ret = -1;
- LinphoneUpnpState oldState;
+ LinphoneUpnpState oldState = 0, newState = 0;
if(lupnp == NULL) {
return ret;
/*
* 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 stat
+ */
+ linphone_core_update_upnp_state_in_call_stats(call);
+
/*
* Update session state
*/
} else {
call->upnp_session->state = LinphoneUpnpStateIdle;
}
+ newState = call->upnp_session->state;
+ }
- /* When change is done proceed update */
- if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
- (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) {
- if(call->upnp_session->state == LinphoneUpnpStateOk)
- ms_message("uPnP IGD: uPnP for Call %p is ok", call);
- else
- ms_message("uPnP IGD: uPnP for Call %p is ko", call);
-
- switch (call->state) {
- case LinphoneCallUpdating:
- linphone_core_start_update_call(lc, call);
- break;
- case LinphoneCallUpdatedByRemote:
- linphone_core_start_accept_call_update(lc, call);
- break;
- case LinphoneCallOutgoingInit:
- linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
- break;
- case LinphoneCallIdle:
- linphone_core_notify_incoming_call(lc, call);
- break;
- default:
- break;
- }
+ ms_mutex_unlock(&lupnp->mutex);
+
+ /* When change is done proceed update */
+ if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
+ (newState == LinphoneUpnpStateOk || newState == LinphoneUpnpStateKo)) {
+ if(call->upnp_session->state == LinphoneUpnpStateOk)
+ ms_message("uPnP IGD: uPnP for Call %p is ok", call);
+ else
+ ms_message("uPnP IGD: uPnP for Call %p is ko", call);
+
+ switch (call->state) {
+ case LinphoneCallUpdating:
+ linphone_core_start_update_call(lc, call);
+ break;
+ case LinphoneCallUpdatedByRemote:
+ linphone_core_start_accept_call_update(lc, call);
+ break;
+ case LinphoneCallOutgoingInit:
+ linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
+ break;
+ case LinphoneCallIdle:
+ linphone_core_notify_incoming_call(lc, call);
+ break;
+ default:
+ break;
}
}
- ms_mutex_unlock(&lupnp->mutex);
return ret;
}
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);
}
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);
+ 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);
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;
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);
}
-bool_t linphone_core_upnp_hook(void *data) {
+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));
+ }
+ }
+ if(external_addr != NULL) {
+ strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr));
+ }
+
+ // 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;
+ }
+ }
+}
+
+void linphone_upnp_update_config(UpnpContext* lupnp) {
char key[64];
- MSList *item;
+ const MSList *item;
UpnpPortBinding *port_mapping;
- UpnpContext *lupnp = (UpnpContext *)data;
- ms_mutex_lock(&lupnp->mutex);
-
+
/* 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);
}
/* 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);
}
ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
+}
+
+void linphone_upnp_update_proxy(UpnpContext* lupnp, bool_t force) {
+ LinphoneUpnpState ready_state;
+ const MSList *item;
+ 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;
+ ready_state = (_linphone_upnp_context_is_ready_for_register(lupnp))? LinphoneUpnpStateOk: LinphoneUpnpStateKo;
+ if(ready_state != lupnp->last_ready_state) {
+ for(item=linphone_core_get_proxy_config_list(lupnp->lc);item!=NULL;item=item->next) {
+ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)item->data;
+ if (linphone_proxy_config_register_enabled(cfg)) {
+ if (ready_state != LinphoneUpnpStateOk) {
+ // 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;
+ }
+ }
+ }
+ 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);
return TRUE;
SalStreamDescription *stream;
UpnpStream *upnpStream;
- for (i = 0; i < desc->nstreams; i++) {
+ for (i = 0; i < desc->n_active_streams; i++) {
stream = &desc->streams[i];
upnpStream = NULL;
if(stream->type == SalAudio) {
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;
}
port = ms_new0(UpnpPortBinding,1);
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';
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;
+
+ // 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);
+
+ // Must be not attached to any struct
+ if(tmp_binding != NULL && tmp_binding->ref == 1) {
+ linphone_upnp_port_binding_release(end_binding);
+ end_binding = linphone_upnp_port_binding_retain(tmp_binding);
+ } else {
+ end_binding->external_port = external_port;
+ }
+ return end_binding;
+}
+
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", msg,
+ ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d (retry %d)", msg,
(port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
port->external_port,
port->local_addr,
- port->local_port);
+ port->local_port,
+ port->retry - 1);
} else {
- ortp_log(level, "uPnP IGD: %s %s|%d->%d", msg,
+ ortp_log(level, "uPnP IGD: %s %s|%d->%d (retry %d)", msg,
(port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
port->external_port,
- port->local_port);
+ port->local_port,
+ port->retry - 1);
}
}
+// 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) {
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);
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);
}
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);
+ }
}
-
+
+ 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);
return session->state;
}
+
/*
* uPnP Config
*/
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;
}
}
-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) {
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;