3 Copyright (C) 2012 Belledonne Communications SARL
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #define UPNP_ADD_MAX_RETRY 4
24 #define UPNP_REMOVE_MAX_RETRY 4
25 #define UPNP_SECTION_NAME "uPnP"
27 /* Define private types */
28 typedef struct _LpItem{
33 typedef struct _LpSection{
38 typedef struct _LpConfig{
46 /* Declare private functions */
47 LpSection *lp_config_find_section(LpConfig *lpconfig, const char *name);
48 void lp_section_remove_item(LpSection *sec, LpItem *item);
49 void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value);
51 bool_t linphone_core_upnp_hook(void *data);
53 UpnpPortBinding *upnp_port_binding_new();
54 UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port);
55 bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2);
56 UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port);
57 void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
58 void upnp_port_binding_release(UpnpPortBinding *port);
60 MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc);
61 void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port);
62 void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port);
64 int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port);
65 int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port);
72 /* Convert uPnP IGD logs to ortp logs */
73 void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) {
74 int ortp_level = ORTP_DEBUG;
76 case UPNP_IGD_MESSAGE:
77 ortp_level = ORTP_MESSAGE;
79 case UPNP_IGD_WARNING:
80 ortp_level = ORTP_DEBUG; // Too verbose otherwise
83 ortp_level = ORTP_DEBUG; // Too verbose otherwise
88 ortp_logv(ortp_level, fmt, list);
91 void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
92 LinphoneCore *lc = (LinphoneCore *)cookie;
93 UpnpContext *lupnp = &lc->upnp;
94 upnp_igd_port_mapping *mapping = NULL;
95 UpnpPortBinding *port_mapping = NULL;
96 const char *ip_address = NULL;
97 const char *connection_status = NULL;
98 bool_t nat_enabled = FALSE;
99 ms_mutex_lock(&lupnp->mutex);
102 case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED:
103 case UPNP_IGD_NAT_ENABLED_CHANGED:
104 case UPNP_IGD_CONNECTION_STATUS_CHANGED:
105 ip_address = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
106 connection_status = upnp_igd_get_connection_status(lupnp->upnp_igd_ctxt);
107 nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt);
109 if(ip_address == NULL || connection_status == NULL) {
110 ms_message("uPnP IGD: Pending");
111 lupnp->state = LinphoneUpnpStatePending;
112 } else if(strcasecmp(connection_status, "Connected") || !nat_enabled) {
113 ms_message("uPnP IGD: Not Available");
114 lupnp->state = LinphoneUpnpStateNotAvailable;
116 ms_message("uPnP IGD: Connected");
117 if(lupnp->state != LinphoneUpnpStateOk) {
118 lupnp->clean = TRUE; // Remove saved port mapping configurations
120 lupnp->state = LinphoneUpnpStateOk;
125 case UPNP_IGD_PORT_MAPPING_ADD_SUCCESS:
126 mapping = (upnp_igd_port_mapping *) arg;
127 port_mapping = (UpnpPortBinding*) mapping->cookie;
128 port_mapping->external_port = mapping->remote_port;
129 port_mapping->state = LinphoneUpnpStateOk;
130 upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping);
131 upnp_config_add_port_binding(lc, port_mapping);
133 lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
134 upnp_port_binding_release(port_mapping);
137 case UPNP_IGD_PORT_MAPPING_ADD_FAILURE:
138 mapping = (upnp_igd_port_mapping *) arg;
139 port_mapping = (UpnpPortBinding*) mapping->cookie;
140 if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) {
141 upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping);
144 lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
145 upnp_port_binding_release(port_mapping);
148 case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS:
149 mapping = (upnp_igd_port_mapping *) arg;
150 port_mapping = (UpnpPortBinding*) mapping->cookie;
151 port_mapping->state = LinphoneUpnpStateIdle;
152 upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping);
153 upnp_config_remove_port_binding(lc, port_mapping);
155 lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
156 upnp_port_binding_release(port_mapping);
159 case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE:
160 mapping = (upnp_igd_port_mapping *) arg;
161 port_mapping = (UpnpPortBinding*) mapping->cookie;
162 if(upnp_context_send_remove_port_binding(lc, port_mapping) != 0) {
163 upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping);
164 upnp_config_remove_port_binding(lc, port_mapping);
167 lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
168 upnp_port_binding_release(port_mapping);
175 if(lupnp->pending_bindings == NULL) {
176 if(lupnp->cleaning == TRUE) {
177 lupnp->emit = TRUE; // Emit port bindings
178 lupnp->cleaning = FALSE;
180 pthread_cond_signal(&lupnp->cond);
182 ms_mutex_unlock(&lupnp->mutex);
190 int upnp_context_init(LinphoneCore *lc) {
191 LCSipTransports transport;
192 UpnpContext *lupnp = &lc->upnp;
193 const char *ip_address;
195 ms_mutex_init(&lupnp->mutex, NULL);
196 ms_cond_init(&lupnp->cond, NULL);
198 lupnp->pending_bindings = NULL;
199 lupnp->adding_configs = NULL;
200 lupnp->removing_configs = NULL;
201 lupnp->clean = FALSE;
202 lupnp->cleaning = FALSE;
204 lupnp->state = LinphoneUpnpStateIdle;
205 ms_message("uPnP IGD: Init");
207 linphone_core_get_sip_transports(lc, &transport);
208 if(transport.udp_port != 0) {
209 lupnp->sip_udp = upnp_port_binding_new();
210 lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
211 lupnp->sip_udp->local_port = transport.udp_port;
212 lupnp->sip_udp->external_port = transport.udp_port;
214 lupnp->sip_udp = NULL;
216 if(transport.tcp_port != 0) {
217 lupnp->sip_tcp = upnp_port_binding_new();
218 lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP;
219 lupnp->sip_tcp->local_port = transport.tcp_port;
220 lupnp->sip_tcp->external_port = transport.tcp_port;
222 lupnp->sip_tcp = NULL;
224 if(transport.tls_port != 0) {
225 lupnp->sip_tls = upnp_port_binding_new();
226 lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP;
227 lupnp->sip_tls->local_port = transport.tls_port;
228 lupnp->sip_tls->external_port = transport.tls_port;
230 lupnp->sip_tls = NULL;
233 linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lc);
235 lupnp->upnp_igd_ctxt = NULL;
236 lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lc);
237 if(lupnp->upnp_igd_ctxt == NULL) {
238 lupnp->state = LinphoneUpnpStateKo;
239 ms_error("Can't create uPnP IGD context");
243 ip_address = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
244 if(lupnp->sip_udp != NULL) {
245 strncpy(lupnp->sip_udp->local_addr, ip_address, sizeof(lupnp->sip_udp->local_addr));
247 if(lupnp->sip_tcp != NULL) {
248 strncpy(lupnp->sip_tcp->local_addr, ip_address, sizeof(lupnp->sip_tcp->local_addr));
250 if(lupnp->sip_tls != NULL) {
251 strncpy(lupnp->sip_tls->local_addr, ip_address, sizeof(lupnp->sip_tls->local_addr));
254 lupnp->state = LinphoneUpnpStatePending;
258 void upnp_context_uninit(LinphoneCore *lc) {
259 UpnpContext *lupnp = &lc->upnp;
262 * Not need, all hooks are removed before
263 * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc);
266 /* Send port binding removes */
267 if(lupnp->sip_udp != NULL) {
268 upnp_context_send_remove_port_binding(lc, lupnp->sip_udp);
269 lupnp->sip_udp = NULL;
271 if(lupnp->sip_tcp != NULL) {
272 upnp_context_send_remove_port_binding(lc, lupnp->sip_tcp);
273 lupnp->sip_tcp = NULL;
275 if(lupnp->sip_tls != NULL) {
276 upnp_context_send_remove_port_binding(lc, lupnp->sip_tls);
277 lupnp->sip_tcp = NULL;
280 /* Wait all pending bindings are done */
281 ms_message("uPnP IGD: Wait all pending port bindings ...");
282 ms_mutex_lock(&lupnp->mutex);
283 ms_cond_wait(&lupnp->cond, &lupnp->mutex);
284 ms_mutex_unlock(&lupnp->mutex);
286 if(lupnp->upnp_igd_ctxt != NULL) {
287 upnp_igd_destroy(lupnp->upnp_igd_ctxt);
290 /* Run one time the hook for configuration update */
291 linphone_core_upnp_hook(lc);
293 /* Release port bindings */
294 if(lupnp->sip_udp != NULL) {
295 upnp_port_binding_release(lupnp->sip_udp);
296 lupnp->sip_udp = NULL;
298 if(lupnp->sip_tcp != NULL) {
299 upnp_port_binding_release(lupnp->sip_tcp);
300 lupnp->sip_tcp = NULL;
302 if(lupnp->sip_tls != NULL) {
303 upnp_port_binding_release(lupnp->sip_tls);
304 lupnp->sip_tcp = NULL;
308 ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release);
309 lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
310 ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release);
311 lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
312 ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))upnp_port_binding_release);
313 lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings);
315 ms_mutex_destroy(&lupnp->mutex);
316 ms_cond_destroy(&lupnp->cond);
318 ms_message("uPnP IGD: Uninit");
321 int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) {
322 UpnpContext *lupnp = &lc->upnp;
323 upnp_igd_port_mapping mapping;
324 char description[128];
326 if(port->state == LinphoneUpnpStateIdle) {
328 port->state = LinphoneUpnpStateAdding;
329 } else if(port->state != LinphoneUpnpStateAdding) {
330 ms_error("uPnP: try to add a port binding in wrong state: %d", port->state);
334 if(port->retry >= UPNP_ADD_MAX_RETRY) {
337 mapping.cookie = upnp_port_binding_retain(port);
338 lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
340 mapping.local_port = port->local_port;
341 mapping.local_host = port->local_addr;
342 if(port->external_port == -1)
343 mapping.remote_port = rand()%(0xffff - 1024) + 1024;
345 mapping.remote_port = port->external_port;
346 mapping.remote_host = "";
347 snprintf(description, 128, "%s %s at %s:%d",
349 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP": "UDP",
350 port->local_addr, port->local_port);
351 mapping.description = description;
352 mapping.protocol = port->protocol;
355 upnp_port_binding_log(ORTP_DEBUG, "Adding port binding...", port);
356 ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
359 port->state = LinphoneUpnpStateKo;
364 int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port) {
365 UpnpContext *lupnp = &lc->upnp;
366 upnp_igd_port_mapping mapping;
368 if(port->state == LinphoneUpnpStateOk) {
370 port->state = LinphoneUpnpStateRemoving;
371 } else if(port->state != LinphoneUpnpStateRemoving) {
372 ms_error("uPnP: try to remove a port binding in wrong state: %d", port->state);
376 if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
379 mapping.cookie = upnp_port_binding_retain(port);
380 lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
382 mapping.remote_port = port->external_port;
383 mapping.remote_host = "";
384 mapping.protocol = port->protocol;
386 upnp_port_binding_log(ORTP_DEBUG, "Removing port binding...", port);
387 ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
390 port->state = LinphoneUpnpStateKo;
396 * uPnP Core interfaces
399 int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) {
400 LinphoneCore *lc = call->core;
401 UpnpContext *lupnp = &lc->upnp;
403 const char *local_addr, *external_addr;
405 ms_mutex_lock(&lupnp->mutex);
406 // Don't handle when the call
407 if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
409 local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
410 external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
415 strncpy(call->upnp_session->audio->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE);
416 strncpy(call->upnp_session->audio->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE);
417 call->upnp_session->audio->rtp->local_port = call->audio_port;
418 if(call->upnp_session->audio->rtp->external_port == -1) {
419 call->upnp_session->audio->rtp->external_port = call->audio_port;
421 strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE);
422 strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE);
423 call->upnp_session->audio->rtcp->local_port = call->audio_port+1;
424 if(call->upnp_session->audio->rtcp->external_port == -1) {
425 call->upnp_session->audio->rtcp->external_port = call->audio_port+1;
427 if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && audio) {
428 // Add audio port binding
429 upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtp);
430 } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk && !audio) {
431 // Remove audio port binding
432 upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp);
434 if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle && audio) {
435 // Add audio port binding
436 upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtcp);
437 } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk && !audio) {
438 // Remove audio port binding
439 upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp);
445 strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE);
446 strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE);
447 call->upnp_session->video->rtp->local_port = call->video_port;
448 if(call->upnp_session->video->rtp->external_port == -1) {
449 call->upnp_session->video->rtp->external_port = call->video_port;
451 strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE);
452 strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE);
453 call->upnp_session->video->rtcp->local_port = call->video_port+1;
454 if(call->upnp_session->video->rtcp->external_port == -1) {
455 call->upnp_session->video->rtcp->external_port = call->video_port+1;
457 if(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && video) {
458 // Add video port binding
459 upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp);
460 } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateOk && !video) {
461 // Remove video port binding
462 upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp);
464 if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle && video) {
465 // Add video port binding
466 upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtcp);
467 } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk && !video) {
468 // Remove video port binding
469 upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp);
473 ms_mutex_unlock(&lupnp->mutex);
476 * Update uPnP call state
478 upnp_call_process(call);
484 int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) {
485 bool_t audio = FALSE;
486 bool_t video = FALSE;
488 const SalStreamDescription *stream;
490 for (i = 0; i < md->nstreams; i++) {
491 stream = &md->streams[i];
492 if(stream->type == SalAudio) {
494 } else if(stream->type == SalVideo) {
499 return linphone_core_update_upnp_audio_video(call, audio, video);
502 int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) {
503 return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
506 int upnp_call_process(LinphoneCall *call) {
507 LinphoneCore *lc = call->core;
508 UpnpContext *lupnp = &lc->upnp;
512 ms_mutex_lock(&lupnp->mutex);
514 // Don't handle when the call
515 if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
521 if((call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle) &&
522 (call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle)) {
523 call->upnp_session->audio->state = LinphoneUpnpStateOk;
524 } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateAdding ||
525 call->upnp_session->audio->rtp->state == LinphoneUpnpStateRemoving ||
526 call->upnp_session->audio->rtcp->state == LinphoneUpnpStateAdding ||
527 call->upnp_session->audio->rtcp->state == LinphoneUpnpStateRemoving) {
528 call->upnp_session->audio->state = LinphoneUpnpStatePending;
529 } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateKo ||
530 call->upnp_session->audio->rtp->state == LinphoneUpnpStateKo) {
531 call->upnp_session->audio->state = LinphoneUpnpStateKo;
533 call->upnp_session->audio->state = LinphoneUpnpStateIdle;
539 if((call->upnp_session->video->rtp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle) &&
540 (call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle)) {
541 call->upnp_session->video->state = LinphoneUpnpStateOk;
542 } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateAdding ||
543 call->upnp_session->video->rtp->state == LinphoneUpnpStateRemoving ||
544 call->upnp_session->video->rtcp->state == LinphoneUpnpStateAdding ||
545 call->upnp_session->video->rtcp->state == LinphoneUpnpStateRemoving) {
546 call->upnp_session->video->state = LinphoneUpnpStatePending;
547 } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateKo ||
548 call->upnp_session->video->rtp->state == LinphoneUpnpStateKo) {
549 call->upnp_session->video->state = LinphoneUpnpStateKo;
551 call->upnp_session->video->state = LinphoneUpnpStateIdle;
555 * Update session state
557 oldState = call->upnp_session->state;
558 if(call->upnp_session->audio->state == LinphoneUpnpStateOk &&
559 call->upnp_session->video->state == LinphoneUpnpStateOk) {
560 call->upnp_session->state = LinphoneUpnpStateOk;
561 } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending ||
562 call->upnp_session->video->state == LinphoneUpnpStatePending) {
563 call->upnp_session->state = LinphoneUpnpStatePending;
564 } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo ||
565 call->upnp_session->video->state == LinphoneUpnpStateKo) {
566 call->upnp_session->state = LinphoneUpnpStateKo;
568 call->upnp_session->state = LinphoneUpnpStateIdle;
571 /* When change is done proceed update */
572 if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
573 (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) {
574 if(call->upnp_session->state == LinphoneUpnpStateOk)
575 ms_message("uPnP IGD: uPnP for Call %p is ok", call);
577 ms_message("uPnP IGD: uPnP for Call %p is ko", call);
579 switch (call->state) {
580 case LinphoneCallUpdating:
581 linphone_core_start_update_call(lc, call);
583 case LinphoneCallUpdatedByRemote:
584 linphone_core_start_accept_call_update(lc, call);
586 case LinphoneCallOutgoingInit:
587 linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
589 case LinphoneCallIdle:
590 linphone_core_notify_incoming_call(lc, call);
598 ms_mutex_unlock(&lupnp->mutex);
602 bool_t linphone_core_upnp_hook(void *data) {
606 UpnpPortBinding *port_mapping;
607 LinphoneCore *lc = (LinphoneCore *)data;
609 UpnpContext *lupnp = &lc->upnp;
610 ms_mutex_lock(&lupnp->mutex);
612 if(lupnp->clean && !lupnp->cleaning) {
613 ms_message("uPnP IGD: Clean port mappings");
614 lupnp->clean = FALSE;
615 // Remove old mapping
616 list = upnp_config_list_port_bindings(lc->config);
620 lupnp->cleaning = TRUE;
621 for(item = list;item != NULL; item = item->next) {
622 port_mapping = (UpnpPortBinding *)item->data;
623 upnp_context_send_remove_port_binding(lc, port_mapping);
625 ms_list_for_each(list,(void (*)(void*))upnp_port_binding_release);
626 list = ms_list_free(list);
631 ms_message("uPnP IGD: Update port mappings");
634 /* Force port bindings */
635 if(lupnp->sip_udp != NULL) {
636 lupnp->sip_udp->state = LinphoneUpnpStateIdle;
637 upnp_context_send_add_port_binding(lc, lupnp->sip_udp);
639 if(lupnp->sip_tcp != NULL) {
640 lupnp->sip_udp->state = LinphoneUpnpStateIdle;
641 upnp_context_send_add_port_binding(lc, lupnp->sip_tcp);
643 if(lupnp->sip_tls != NULL) {
644 lupnp->sip_udp->state = LinphoneUpnpStateIdle;
645 upnp_context_send_add_port_binding(lc, lupnp->sip_tls);
648 while(list != NULL) {
649 call = (LinphoneCall *)list->data;
650 call->upnp_session->audio->rtp->state = LinphoneUpnpStateIdle;
651 call->upnp_session->audio->rtcp->state = LinphoneUpnpStateIdle;
652 call->upnp_session->video->rtp->state = LinphoneUpnpStateIdle;
653 call->upnp_session->video->rtcp->state = LinphoneUpnpStateIdle;
654 linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
660 for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
661 port_mapping = (UpnpPortBinding *)item->data;
662 snprintf(key, sizeof(key), "%s-%d-%d",
663 (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
664 port_mapping->external_port,
665 port_mapping->local_port);
666 lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, "uPnP");
667 upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
669 ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release);
670 lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
673 for(item = lupnp->removing_configs;item!=NULL;item=item->next) {
674 port_mapping = (UpnpPortBinding *)item->data;
675 snprintf(key, sizeof(key), "%s-%d-%d",
676 (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
677 port_mapping->external_port,
678 port_mapping->local_port);
679 lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, NULL);
680 upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
682 ms_list_for_each(lupnp->removing_configs,(void (*)(void*))upnp_port_binding_release);
683 lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
685 ms_mutex_unlock(&lupnp->mutex);
689 void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) {
691 SalStreamDescription *stream;
692 UpnpStream *upnpStream;
694 for (i = 0; i < desc->nstreams; i++) {
695 stream = &desc->streams[i];
697 if(stream->type == SalAudio) {
698 upnpStream = session->audio;
699 } else if(stream->type == SalVideo) {
700 upnpStream = session->video;
702 if(upnpStream != NULL) {
703 if(upnpStream->rtp->state == LinphoneUpnpStateOk) {
704 strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE);
705 stream->rtp_port = upnpStream->rtp->external_port;
707 if(upnpStream->rtcp->state == LinphoneUpnpStateOk) {
708 strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE);
709 stream->rtcp_port = upnpStream->rtcp->external_port;
720 UpnpPortBinding *upnp_port_binding_new() {
721 UpnpPortBinding *port = NULL;
722 port = ms_new0(UpnpPortBinding,1);
723 ms_mutex_init(&port->mutex, NULL);
724 port->state = LinphoneUpnpStateIdle;
725 port->local_addr[0] = '\0';
726 port->local_port = -1;
727 port->external_addr[0] = '\0';
728 port->external_port = -1;
733 UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port) {
734 UpnpPortBinding *new_port = NULL;
735 new_port = ms_new0(UpnpPortBinding,1);
736 memcpy(new_port, port, sizeof(UpnpPortBinding));
737 ms_mutex_init(&new_port->mutex, NULL);
742 void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
743 if(strlen(port->local_addr)) {
744 ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d", msg,
745 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
750 ortp_log(level, "uPnP IGD: %s %s|%d->%d", msg,
751 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
757 bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
758 return port1->protocol == port2->protocol &&
759 port1->local_port == port2->local_port &&
760 port1->external_port == port2->external_port;
763 UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port) {
764 ms_mutex_lock(&port->mutex);
766 ms_mutex_unlock(&port->mutex);
770 void upnp_port_binding_release(UpnpPortBinding *port) {
771 ms_mutex_lock(&port->mutex);
772 if(--port->ref == 0) {
773 ms_mutex_unlock(&port->mutex);
774 ms_mutex_destroy(&port->mutex);
778 ms_mutex_unlock(&port->mutex);
786 UpnpStream* upnp_stream_new() {
787 UpnpStream *stream = ms_new0(UpnpStream,1);
788 stream->state = LinphoneUpnpStateIdle;
789 stream->rtp = upnp_port_binding_new();
790 stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
791 stream->rtcp = upnp_port_binding_new();
792 stream->rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
796 void upnp_stream_destroy(UpnpStream* stream) {
797 upnp_port_binding_release(stream->rtp);
799 upnp_port_binding_release(stream->rtcp);
809 UpnpSession* upnp_session_new() {
810 UpnpSession *session = ms_new0(UpnpSession,1);
811 session->state = LinphoneUpnpStateIdle;
812 session->audio = upnp_stream_new();
813 session->video = upnp_stream_new();
817 void upnp_session_destroy(LinphoneCall* call) {
818 LinphoneCore *lc = call->core;
820 /* Remove bindings */
821 if(call->upnp_session->audio->rtp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtp->state != LinphoneUpnpStateIdle) {
822 upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp);
824 if(call->upnp_session->audio->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtcp->state != LinphoneUpnpStateIdle) {
825 upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp);
827 if(call->upnp_session->video->rtp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtp->state != LinphoneUpnpStateIdle) {
828 upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp);
830 if(call->upnp_session->video->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtcp->state != LinphoneUpnpStateIdle) {
831 upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp);
834 upnp_stream_destroy(call->upnp_session->audio);
835 upnp_stream_destroy(call->upnp_session->video);
836 ms_free(call->upnp_session);
837 call->upnp_session = NULL;
845 MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) {
846 char protocol_str[4]; // TCP or UDP
847 upnp_igd_ip_protocol protocol;
850 MSList *retList = NULL;
851 UpnpPortBinding *port;
855 LpSection *sec=lp_config_find_section(lpc, UPNP_SECTION_NAME);
860 while(elem != NULL) {
861 item=(LpItem*)elem->data;
863 if(sscanf(item->key, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) {
864 if(strcasecmp(protocol_str, "TCP") == 0) {
865 protocol = UPNP_IGD_IP_PROTOCOL_TCP;
866 } else if(strcasecmp(protocol_str, "UDP") == 0) {
867 protocol = UPNP_IGD_IP_PROTOCOL_UDP;
872 port = upnp_port_binding_new();
873 port->state = LinphoneUpnpStateOk;
874 port->protocol = protocol;
875 port->external_port = external_port;
876 port->local_port = local_port;
877 retList = ms_list_append(retList, port);
882 elem = ms_list_next(elem);
884 ms_warning("uPnP configuration invalid line: %s", item->key);
885 lp_section_remove_item(sec, item);
892 void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) {
893 UpnpContext *lupnp = &lc->upnp;
895 UpnpPortBinding *list_port;
897 list = lupnp->removing_configs;
898 while(list != NULL) {
899 list_port = (UpnpPortBinding *)list->data;
900 if(upnp_port_binding_equal(list_port, port) == TRUE) {
901 lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port);
902 upnp_port_binding_release(list_port);
905 list = ms_list_next(list);
908 list = lupnp->adding_configs;
909 while(list != NULL) {
910 list_port = (UpnpPortBinding *)list->data;
911 if(upnp_port_binding_equal(list_port, port) == TRUE) {
914 list = ms_list_next(list);
917 list_port = upnp_port_binding_copy(port);
918 lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port);
921 void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) {
922 UpnpContext *lupnp = &lc->upnp;
924 UpnpPortBinding *list_port;
926 list = lupnp->adding_configs;
927 while(list != NULL) {
928 list_port = (UpnpPortBinding *)list->data;
929 if(upnp_port_binding_equal(list_port, port) == TRUE) {
930 lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port);
931 upnp_port_binding_release(list_port);
934 list = ms_list_next(list);
937 list = lupnp->removing_configs;
938 while(list != NULL) {
939 list_port = (UpnpPortBinding *)list->data;
940 if(upnp_port_binding_equal(list_port, port) == TRUE) {
943 list = ms_list_next(list);
946 list_port = upnp_port_binding_copy(port);
947 lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port);