]> sjero.net Git - linphone/blob - coreapi/upnp.c
Improve uPnP behaviour (Firewall policy change, local ip change, ...)
[linphone] / coreapi / upnp.c
1 /*
2 linphone
3 Copyright (C) 2012  Belledonne Communications SARL
4
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.
9
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.
14
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.
18 */
19
20 #include "upnp.h"
21 #include "private.h"
22 #include "lpconfig.h"
23
24 #define UPNP_ADD_MAX_RETRY 4
25 #define UPNP_REMOVE_MAX_RETRY 4
26 #define UPNP_SECTION_NAME "uPnP"
27 #define UPNP_CORE_RETRY_DELAY 4
28 #define UPNP_CALL_RETRY_DELAY 1
29
30 /*
31  * uPnP Definitions
32  */
33
34 typedef struct _UpnpPortBinding {
35         ms_mutex_t mutex;
36         LinphoneUpnpState state;
37         upnp_igd_ip_protocol protocol;
38         char local_addr[LINPHONE_IPADDR_SIZE];
39         int local_port;
40         char external_addr[LINPHONE_IPADDR_SIZE];
41         int external_port;
42         int retry;
43         int ref;
44         bool_t to_remove;
45         bool_t to_add;
46         time_t last_update;
47 } UpnpPortBinding;
48
49 typedef struct _UpnpStream {
50         UpnpPortBinding *rtp;
51         UpnpPortBinding *rtcp;
52         LinphoneUpnpState state;
53 } UpnpStream;
54
55 struct _UpnpSession {
56         LinphoneCall *call;
57         UpnpStream *audio;
58         UpnpStream *video;
59         LinphoneUpnpState state;
60 };
61
62 struct _UpnpContext {
63         LinphoneCore *lc;
64         upnp_igd_context *upnp_igd_ctxt;
65         UpnpPortBinding *sip_tcp;
66         UpnpPortBinding *sip_tls;
67         UpnpPortBinding *sip_udp;
68         LinphoneUpnpState state;
69         MSList *removing_configs;
70         MSList *adding_configs;
71         MSList *pending_bindings;
72
73         ms_mutex_t mutex;
74         ms_cond_t empty_cond;
75
76 };
77
78
79 bool_t linphone_core_upnp_hook(void *data);
80 void linphone_core_upnp_refresh(UpnpContext *ctx);
81
82 UpnpPortBinding *linphone_upnp_port_binding_new();
83 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port);
84 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port); 
85 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port);
86 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2);
87 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port);
88 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port);
89 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay); 
90 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
91 void linphone_upnp_port_binding_release(UpnpPortBinding *port);
92
93 // Configuration
94 MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc);
95 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
96 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
97
98 // uPnP 
99 int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry);
100 int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry);
101
102
103 /**
104  * uPnP Callbacks
105  */
106
107 /* Convert uPnP IGD logs to ortp logs */
108 void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) {
109         int ortp_level = ORTP_DEBUG;
110         switch(level) {
111         case UPNP_IGD_MESSAGE:
112                 ortp_level = ORTP_MESSAGE;
113                 break;
114         case UPNP_IGD_WARNING:
115                 ortp_level = ORTP_DEBUG; // Too verbose otherwise
116                 break;
117         case UPNP_IGD_ERROR:
118                 ortp_level = ORTP_DEBUG; // Too verbose otherwise
119                 break;
120         default:
121                 break;
122         }
123         ortp_logv(ortp_level, fmt, list);
124 }
125
126 void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
127         UpnpContext *lupnp = (UpnpContext *)cookie;
128         upnp_igd_port_mapping *mapping = NULL;
129         UpnpPortBinding *port_mapping = NULL;
130         const char *ip_address = NULL;
131         const char *connection_status = NULL;
132         bool_t nat_enabled = FALSE;
133         LinphoneUpnpState old_state;
134
135         if(lupnp == NULL || lupnp->upnp_igd_ctxt == NULL) {
136                 ms_error("uPnP IGD: Invalid context in callback");
137                 return;
138         }
139
140         ms_mutex_lock(&lupnp->mutex);
141         old_state = lupnp->state;
142
143         switch(event) {
144         case UPNP_IGD_DEVICE_ADDED:
145         case UPNP_IGD_DEVICE_REMOVED:
146         case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED:
147         case UPNP_IGD_NAT_ENABLED_CHANGED:
148         case UPNP_IGD_CONNECTION_STATUS_CHANGED:
149                 ip_address = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
150                 connection_status = upnp_igd_get_connection_status(lupnp->upnp_igd_ctxt);
151                 nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt);
152
153                 if(ip_address == NULL || connection_status == NULL) {
154                         ms_message("uPnP IGD: Pending");
155                         lupnp->state = LinphoneUpnpStatePending;
156                 } else if(strcasecmp(connection_status, "Connected")  || !nat_enabled) {
157                         ms_message("uPnP IGD: Not Available");
158                         lupnp->state = LinphoneUpnpStateNotAvailable;
159                 } else {
160                         ms_message("uPnP IGD: Connected");
161                         lupnp->state = LinphoneUpnpStateOk;
162                         if(old_state != LinphoneUpnpStateOk) {
163                                 linphone_core_upnp_refresh(lupnp);
164                         }
165                 }
166
167                 break;
168
169         case UPNP_IGD_PORT_MAPPING_ADD_SUCCESS:
170                 mapping = (upnp_igd_port_mapping *) arg;
171                 port_mapping = (UpnpPortBinding*) mapping->cookie;
172                 port_mapping->external_port = mapping->remote_port;
173                 port_mapping->state = LinphoneUpnpStateOk;
174                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping);
175                 linphone_upnp_config_add_port_binding(lupnp, port_mapping);
176
177                 break;
178
179         case UPNP_IGD_PORT_MAPPING_ADD_FAILURE:
180                 mapping = (upnp_igd_port_mapping *) arg;
181                 port_mapping = (UpnpPortBinding*) mapping->cookie;
182                 port_mapping->external_port = -1; //Force random external port
183                 if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE) != 0) {
184                         linphone_upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping);
185                 }
186
187                 break;
188
189         case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS:
190                 mapping = (upnp_igd_port_mapping *) arg;
191                 port_mapping = (UpnpPortBinding*) mapping->cookie;
192                 port_mapping->state = LinphoneUpnpStateIdle;
193                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping);
194                 linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
195
196                 break;
197
198         case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE:
199                 mapping = (upnp_igd_port_mapping *) arg;
200                 port_mapping = (UpnpPortBinding*) mapping->cookie;
201                 if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE) != 0) {
202                         linphone_upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping);
203                         linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
204                 }
205
206                 break;
207
208         default:
209                 break;
210         }
211
212         if(port_mapping != NULL) {
213                 /*
214                  * Execute delayed actions
215                  */
216                 if(port_mapping->to_remove) {
217                         if(port_mapping->state == LinphoneUpnpStateOk) {
218                                 port_mapping->to_remove = FALSE;
219                                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, FALSE);
220                         } else if(port_mapping->state == LinphoneUpnpStateKo) {
221                                 port_mapping->to_remove = FALSE;
222                         }
223                 }
224                 if(port_mapping->to_add) {
225                         if(port_mapping->state == LinphoneUpnpStateIdle || port_mapping->state == LinphoneUpnpStateKo) {
226                                 port_mapping->to_add = FALSE;
227                                 linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, FALSE);
228                         }
229                 }
230
231                 lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
232                 linphone_upnp_port_binding_release(port_mapping);
233         }
234
235         /*
236          * If there is no pending binding emit a signal
237          */
238         if(lupnp->pending_bindings == NULL) {
239                 pthread_cond_signal(&lupnp->empty_cond);
240         }
241         ms_mutex_unlock(&lupnp->mutex);
242 }
243
244
245 /**
246  * uPnP Context
247  */
248
249 UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
250         UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1);
251
252         ms_mutex_init(&lupnp->mutex, NULL);
253         ms_cond_init(&lupnp->empty_cond, NULL);
254
255         lupnp->lc = lc;
256         lupnp->pending_bindings = NULL;
257         lupnp->adding_configs = NULL;
258         lupnp->removing_configs = NULL;
259         lupnp->state = LinphoneUpnpStateIdle;
260         ms_message("uPnP IGD: New %p for core %p", lupnp, lc);
261
262         // Init ports
263         lupnp->sip_udp = NULL;
264         lupnp->sip_tcp = NULL;
265         lupnp->sip_tls = NULL;
266
267         linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp);
268
269         lupnp->upnp_igd_ctxt = NULL;
270         lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lupnp);
271         if(lupnp->upnp_igd_ctxt == NULL) {
272                 lupnp->state = LinphoneUpnpStateKo;
273                 ms_error("Can't create uPnP IGD context");
274                 return NULL;
275         }
276
277         lupnp->state = LinphoneUpnpStatePending;
278         upnp_igd_start(lupnp->upnp_igd_ctxt);
279
280         return lupnp;
281 }
282
283 void linphone_upnp_context_destroy(UpnpContext *lupnp) {
284         linphone_core_remove_iterate_hook(lupnp->lc, linphone_core_upnp_hook, lupnp);
285
286         ms_mutex_lock(&lupnp->mutex);
287         
288         /* Send port binding removes */
289         if(lupnp->sip_udp != NULL) {
290                 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp, TRUE);
291         }
292         if(lupnp->sip_tcp != NULL) {
293                 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp, TRUE);
294         }
295         if(lupnp->sip_tls != NULL) {
296                 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls, TRUE);
297         }
298
299         /* Wait all pending bindings are done */
300         if(lupnp->pending_bindings != NULL) {
301                 ms_message("uPnP IGD: Wait all pending port bindings ...");
302                 ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex);
303         }
304         ms_mutex_unlock(&lupnp->mutex);
305
306         if(lupnp->upnp_igd_ctxt != NULL) {
307                 upnp_igd_destroy(lupnp->upnp_igd_ctxt);
308         }
309
310         /* Run one time the hook for configuration update */
311         linphone_core_upnp_hook(lupnp);
312
313         /* Release port bindings */
314         if(lupnp->sip_udp != NULL) {
315                 linphone_upnp_port_binding_release(lupnp->sip_udp);
316                 lupnp->sip_udp = NULL;
317         }
318         if(lupnp->sip_tcp != NULL) {
319                 linphone_upnp_port_binding_release(lupnp->sip_tcp);
320                 lupnp->sip_tcp = NULL;
321         }
322         if(lupnp->sip_tls != NULL) {
323                 linphone_upnp_port_binding_release(lupnp->sip_tls);
324                 lupnp->sip_tcp = NULL;
325         }
326
327         /* Release lists */
328         ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
329         lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
330         ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
331         lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
332         ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))linphone_upnp_port_binding_release);
333         lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings);
334
335         ms_mutex_destroy(&lupnp->mutex);
336         ms_cond_destroy(&lupnp->empty_cond);
337
338         ms_message("uPnP IGD: destroy %p", lupnp);
339         ms_free(lupnp);
340 }
341
342 LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) {
343         LinphoneUpnpState state;
344         ms_mutex_lock(&lupnp->mutex);
345         state = lupnp->state;
346         ms_mutex_unlock(&lupnp->mutex);
347         return state;
348 }
349
350 bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
351         bool_t ready = TRUE;
352         ms_mutex_lock(&lupnp->mutex);
353
354         // 1 Check global uPnP state
355         ready = (lupnp->state == LinphoneUpnpStateOk);
356         
357         // 2 Check external ip address
358         if(ready && upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt) == NULL) {
359                 ready = FALSE;
360         }
361         
362         // 3 Check sip ports bindings
363         if(ready) {
364                 if(lupnp->sip_udp != NULL) {
365                         if(lupnp->sip_udp->state != LinphoneUpnpStateOk) {
366                                 ready = FALSE;
367                         }
368                 } else if(lupnp->sip_tcp != NULL) {
369                         if(lupnp->sip_tcp->state != LinphoneUpnpStateOk) {
370                                 ready = FALSE;
371                         }
372                 } else if(lupnp->sip_tls != NULL) {
373                         if(lupnp->sip_tls->state != LinphoneUpnpStateOk) {
374                                 ready = FALSE;
375                         }
376                 } else {
377                         ready = FALSE;
378                 }
379         }
380
381         ms_mutex_unlock(&lupnp->mutex);
382         return ready;
383 }
384
385 int linphone_upnp_context_get_external_port(UpnpContext *lupnp) {
386         int port = -1;
387         ms_mutex_lock(&lupnp->mutex);
388         
389         if(lupnp->sip_udp != NULL) {
390                 if(lupnp->sip_udp->state == LinphoneUpnpStateOk) {
391                         port = lupnp->sip_udp->external_port;
392                 }
393         } else if(lupnp->sip_tcp != NULL) {
394                 if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) {
395                         port = lupnp->sip_tcp->external_port;
396                 }
397         } else if(lupnp->sip_tls != NULL) {
398                 if(lupnp->sip_tls->state == LinphoneUpnpStateOk) {
399                         port = lupnp->sip_tls->external_port;
400                 }
401         }
402         
403         ms_mutex_unlock(&lupnp->mutex);
404         return port;
405 }
406
407 const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *lupnp) {
408         const char* addr = NULL;
409         ms_mutex_lock(&lupnp->mutex);
410         addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
411         ms_mutex_unlock(&lupnp->mutex);
412         return addr;
413 }
414
415 int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
416         upnp_igd_port_mapping mapping;
417         char description[128];
418         int ret;
419         
420         if(lupnp->state != LinphoneUpnpStateOk) {
421                 return -2;
422         }
423
424         // Compute port binding state
425         if(port->state != LinphoneUpnpStateAdding) {
426                 port->to_remove = FALSE;
427                 switch(port->state) {
428                         case LinphoneUpnpStateKo:
429                         case LinphoneUpnpStateIdle: {
430                                 port->retry = 0;
431                                 port->state = LinphoneUpnpStateAdding;
432                         }
433                         break;
434                         case LinphoneUpnpStateRemoving: {
435                                 port->to_add = TRUE;
436                                 return 0;
437                         }
438                         break;
439                         default:
440                                 return 0;
441                 }
442         }
443         
444         // No retry if specified
445         if(port->retry != 0 && !retry) {
446                 return -1;
447         }
448
449         if(port->retry >= UPNP_ADD_MAX_RETRY) {
450                 ret = -1;
451         } else {
452                 mapping.cookie = linphone_upnp_port_binding_retain(port);
453                 lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
454
455                 mapping.local_port = port->local_port;
456                 mapping.local_host = port->local_addr;
457                 if(port->external_port == -1)
458                         mapping.remote_port = rand()%(0xffff - 1024) + 1024;
459                 else
460                         mapping.remote_port = port->external_port;
461                 mapping.remote_host = "";
462                 snprintf(description, 128, "%s %s at %s:%d",
463                                 PACKAGE_NAME,
464                                 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP": "UDP",
465                                 port->local_addr, port->local_port);
466                 mapping.description = description;
467                 mapping.protocol = port->protocol;
468
469                 port->retry++;
470                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to add port binding", port);
471                 ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
472         }
473         if(ret != 0) {
474                 port->state = LinphoneUpnpStateKo;
475         }
476         return ret;
477 }
478
479 int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
480         upnp_igd_port_mapping mapping;
481         int ret;
482         
483         if(lupnp->state != LinphoneUpnpStateOk) {
484                 return -2;
485         }
486
487         // Compute port binding state
488         if(port->state != LinphoneUpnpStateRemoving) {
489                 port->to_add = FALSE;
490                 switch(port->state) {
491                         case LinphoneUpnpStateOk: {
492                                 port->retry = 0;
493                                 port->state = LinphoneUpnpStateRemoving;
494                         }
495                         break;
496                         case LinphoneUpnpStateAdding: {
497                                 port->to_remove = TRUE;
498                                 return 0;
499                         }
500                         break;
501                         default:
502                                 return 0;
503                 }
504         }
505         
506         // No retry if specified
507         if(port->retry != 0 && !retry) {
508                 return 1;
509         }
510
511         if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
512                 ret = -1;
513         } else {
514                 mapping.cookie = linphone_upnp_port_binding_retain(port);
515                 lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
516
517                 mapping.remote_port = port->external_port;
518                 mapping.remote_host = "";
519                 mapping.protocol = port->protocol;
520                 port->retry++;
521                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to remove port binding", port);
522                 ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
523         }
524         if(ret != 0) {
525                 port->state = LinphoneUpnpStateKo;
526         }
527         return ret;
528 }
529
530 /*
531  * uPnP Core interfaces
532  */
533
534 int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) {
535         LinphoneCore *lc = call->core;
536         UpnpContext *lupnp = lc->upnp;
537         int ret = -1;
538
539         if(lupnp == NULL) {
540                 return ret;
541         }
542
543         ms_mutex_lock(&lupnp->mutex);
544         // Don't handle when the call
545         if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
546                 ret = 0;
547
548                 /*
549                  * Audio part
550                  */
551                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtp, 
552                         UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port:0, UPNP_CALL_RETRY_DELAY);
553
554                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtcp, 
555                         UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port+1:0, UPNP_CALL_RETRY_DELAY);
556                 
557                 /*
558                  * Video part
559                  */
560                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtp, 
561                         UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port:0, UPNP_CALL_RETRY_DELAY);
562
563                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtcp, 
564                         UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port+1:0, UPNP_CALL_RETRY_DELAY);
565         }
566
567         ms_mutex_unlock(&lupnp->mutex);
568
569         /*
570          * Update uPnP call state
571          */
572         linphone_upnp_call_process(call);
573
574         return ret;
575 }
576
577
578
579 int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) {
580         bool_t audio = FALSE;
581         bool_t video = FALSE;
582         int i;
583         const SalStreamDescription *stream;
584
585         for (i = 0; i < md->n_total_streams; i++) {
586                 stream = &md->streams[i];
587                 if(stream->type == SalAudio) {
588                         audio = TRUE;
589                 } else if(stream->type == SalVideo) {
590                         video = TRUE;
591                 }
592         }
593
594         return linphone_core_update_upnp_audio_video(call, audio, video);
595 }
596
597 int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) {
598         return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
599 }
600
601 void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) {
602         call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = call->upnp_session->audio->state;
603         call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = call->upnp_session->video->state;
604 }
605
606 void linphone_upnp_update_stream_state(UpnpStream *stream) {
607         if((stream->rtp == NULL || stream->rtp->state == LinphoneUpnpStateOk || stream->rtp->state == LinphoneUpnpStateIdle) &&
608            (stream->rtcp == NULL || stream->rtcp->state == LinphoneUpnpStateOk || stream->rtcp->state == LinphoneUpnpStateIdle)) {
609                 stream->state = LinphoneUpnpStateOk;
610         } else if((stream->rtp != NULL && 
611                      (stream->rtp->state == LinphoneUpnpStateAdding || stream->rtp->state == LinphoneUpnpStateRemoving)) ||
612                   (stream->rtcp != NULL && 
613                      (stream->rtcp->state == LinphoneUpnpStateAdding || stream->rtcp->state == LinphoneUpnpStateRemoving))) {
614                 stream->state = LinphoneUpnpStatePending;
615         } else if((stream->rtp != NULL && stream->rtp->state == LinphoneUpnpStateKo) ||
616                         (stream->rtcp != NULL && stream->rtcp->state == LinphoneUpnpStateKo)) {
617                 stream->state = LinphoneUpnpStateKo;
618         } else {
619                 ms_error("Invalid stream %p state", stream);            
620         }
621 }
622
623 int linphone_upnp_call_process(LinphoneCall *call) {
624         LinphoneCore *lc = call->core;
625         UpnpContext *lupnp = lc->upnp;
626         int ret = -1;
627         LinphoneUpnpState oldState = 0, newState = 0;
628
629         if(lupnp == NULL) {
630                 return ret;
631         }
632
633         ms_mutex_lock(&lupnp->mutex);
634
635         // Don't handle when the call
636         if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
637                 ret = 0;
638
639                 /*
640                  * Update Audio state
641                  */
642                 linphone_upnp_update_stream_state(call->upnp_session->audio);
643
644                 /*
645                  * Update Video state
646                  */
647                 linphone_upnp_update_stream_state(call->upnp_session->video);
648
649                 /*
650                  * Update session state
651                  */
652                 oldState = call->upnp_session->state;
653                 if(call->upnp_session->audio->state == LinphoneUpnpStateOk &&
654                         call->upnp_session->video->state == LinphoneUpnpStateOk) {
655                         call->upnp_session->state = LinphoneUpnpStateOk;
656                 } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending ||
657                                 call->upnp_session->video->state == LinphoneUpnpStatePending) {
658                         call->upnp_session->state = LinphoneUpnpStatePending;
659                 } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo ||
660                                 call->upnp_session->video->state == LinphoneUpnpStateKo) {
661                         call->upnp_session->state = LinphoneUpnpStateKo;
662                 } else {
663                         call->upnp_session->state = LinphoneUpnpStateIdle;
664                 }
665                 newState = call->upnp_session->state;
666
667                 /* When change is done proceed update */
668                 if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
669                                 (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) {
670                         if(call->upnp_session->state == LinphoneUpnpStateOk)
671                                 ms_message("uPnP IGD: uPnP for Call %p is ok", call);
672                         else
673                                 ms_message("uPnP IGD: uPnP for Call %p is ko", call);
674
675                         switch (call->state) {
676                                 case LinphoneCallUpdating:
677                                         linphone_core_start_update_call(lc, call);
678                                         break;
679                                 case LinphoneCallUpdatedByRemote:
680                                         linphone_core_start_accept_call_update(lc, call);
681                                         break;
682                                 case LinphoneCallOutgoingInit:
683                                         linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
684                                         break;
685                                 case LinphoneCallIdle:
686                                         linphone_core_notify_incoming_call(lc, call);
687                                         break;
688                                 default:
689                                         break;
690                         }
691                 }
692         }
693
694         ms_mutex_unlock(&lupnp->mutex);
695
696         /*
697          * Update uPnP call stats
698          */
699         if(oldState != newState) {
700                 linphone_core_update_upnp_state_in_call_stats(call);
701         }
702
703         return ret;
704 }
705
706 void linphone_core_upnp_refresh(UpnpContext *lupnp) {
707         MSList *global_list = NULL;
708         MSList *list = NULL;
709         MSList *item;
710         LinphoneCall *call;
711         UpnpPortBinding *port_mapping, *port_mapping2;
712
713         ms_message("uPnP IGD: Refresh mappings");
714
715         if(lupnp->sip_udp != NULL) {
716                 global_list = ms_list_append(global_list, lupnp->sip_udp);
717         }
718         if(lupnp->sip_tcp != NULL) {
719                 global_list = ms_list_append(global_list, lupnp->sip_tcp);
720         }
721         if(lupnp->sip_tls != NULL) {
722                 global_list = ms_list_append(global_list, lupnp->sip_tls);
723         }
724
725         list = lupnp->lc->calls;
726         while(list != NULL) {
727                 call = (LinphoneCall *)list->data;
728                 if(call->upnp_session != NULL) {
729                         if(call->upnp_session->audio->rtp != NULL) {
730                                 global_list = ms_list_append(global_list, call->upnp_session->audio->rtp);
731                         }
732                         if(call->upnp_session->audio->rtcp != NULL) {
733                                 global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp);
734                         }
735                         if(call->upnp_session->video->rtp != NULL) {
736                                 global_list = ms_list_append(global_list, call->upnp_session->video->rtp);
737                         }
738                         if(call->upnp_session->video->rtcp != NULL) {
739                                 global_list = ms_list_append(global_list, call->upnp_session->video->rtcp);
740                         }
741                 }
742                 list = list->next;
743         }
744
745         list = linphone_upnp_config_list_port_bindings(lupnp->lc->config);
746         for(item = list;item != NULL; item = item->next) {
747                         port_mapping = (UpnpPortBinding *)item->data;
748                         port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping);
749                         if(port_mapping2 == NULL) {
750                                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
751                         } else if(port_mapping2->state == LinphoneUpnpStateIdle){
752                                 /* Force to remove */
753                                 port_mapping2->state = LinphoneUpnpStateOk;
754                         }
755         }
756         ms_list_for_each(list, (void (*)(void*))linphone_upnp_port_binding_release);
757         list = ms_list_free(list);
758
759
760         // (Re)Add removed port bindings
761         list = global_list;
762         while(list != NULL) {
763                 port_mapping = (UpnpPortBinding *)list->data;
764                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
765                 linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE);
766                 list = list->next;
767         }
768         global_list = ms_list_free(global_list);
769 }
770
771 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay) {
772         const char *local_addr, *external_addr;
773         time_t now = time(NULL);
774         if(port != 0) {
775                 if(*port_mapping != NULL) {
776                         if(port != (*port_mapping)->local_port) {
777                                 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
778                                 *port_mapping = NULL;
779                         }
780                 }
781                 if(*port_mapping == NULL) {
782                         *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port);
783                 }
784                 
785                 // Get addresses
786                 local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
787                 external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
788                 
789                 // Force binding update on local address change
790                 if(local_addr != NULL) {
791                         if(strncmp((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr))) {
792                                 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
793                                 strncpy((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr));
794                         }
795                 } else {
796                         ms_warning("uPnP IGD: can't get local address");
797                 }
798                 if(external_addr != NULL) {
799                         strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr));
800                 } else {
801                         ms_warning("uPnP IGD: can't get external address");
802                 }
803
804                 // Add (if not already done) the binding
805                 if(now - (*port_mapping)->last_update >= retry_delay) {
806                         (*port_mapping)->last_update = now;
807                         linphone_upnp_context_send_add_port_binding(lupnp, *port_mapping, FALSE);
808                 }
809         } else {
810                 if(*port_mapping != NULL) {
811                         linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
812                         *port_mapping = NULL;
813                 }
814         }
815 }
816
817 bool_t linphone_core_upnp_hook(void *data) {
818         char key[64];
819         LCSipTransports transport;
820         MSList *item;
821         UpnpPortBinding *port_mapping;
822         UpnpContext *lupnp = (UpnpContext *)data;
823
824         ms_mutex_lock(&lupnp->mutex);
825
826         /* Update ports */
827         if(lupnp->state == LinphoneUpnpStateOk) {
828                 linphone_core_get_sip_transports(lupnp->lc, &transport);
829                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_udp, UPNP_IGD_IP_PROTOCOL_UDP, transport.udp_port, UPNP_CORE_RETRY_DELAY);
830                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tcp, UPNP_IGD_IP_PROTOCOL_TCP, transport.tcp_port, UPNP_CORE_RETRY_DELAY);
831                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tls, UPNP_IGD_IP_PROTOCOL_TCP, transport.tls_port, UPNP_CORE_RETRY_DELAY);
832         }
833
834         /* Add configs */
835         for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
836                 port_mapping = (UpnpPortBinding *)item->data;
837                 snprintf(key, sizeof(key), "%s-%d-%d",
838                                         (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
839                                                         port_mapping->external_port,
840                                                         port_mapping->local_port);
841                 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP");
842                 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
843         }
844         ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
845         lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
846
847         /* Remove configs */
848         for(item = lupnp->removing_configs;item!=NULL;item=item->next) {
849                 port_mapping = (UpnpPortBinding *)item->data;
850                 snprintf(key, sizeof(key), "%s-%d-%d",
851                                         (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
852                                                         port_mapping->external_port,
853                                                         port_mapping->local_port);
854                 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL);
855                 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
856         }
857         ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
858         lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
859
860         ms_mutex_unlock(&lupnp->mutex);
861         return TRUE;
862 }
863
864 int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) {
865         int i;
866         SalStreamDescription *stream;
867         UpnpStream *upnpStream;
868
869         for (i = 0; i < desc->n_active_streams; i++) {
870                 stream = &desc->streams[i];
871                 upnpStream = NULL;
872                 if(stream->type == SalAudio) {
873                         upnpStream = session->audio;
874                 } else if(stream->type == SalVideo) {
875                         upnpStream = session->video;
876                 }
877                 if(upnpStream != NULL) {
878                         if(upnpStream->rtp != NULL && upnpStream->rtp->state == LinphoneUpnpStateOk) {
879                                 strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE);
880                                 stream->rtp_port = upnpStream->rtp->external_port;
881                         }
882                         if(upnpStream->rtcp != NULL && upnpStream->rtcp->state == LinphoneUpnpStateOk) {
883                                 strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE);
884                                 stream->rtcp_port = upnpStream->rtcp->external_port;
885                         }
886                 }
887         }
888         return 0;
889 }
890
891
892 /*
893  * uPnP Port Binding
894  */
895
896 UpnpPortBinding *linphone_upnp_port_binding_new() {
897         UpnpPortBinding *port = NULL;
898         port = ms_new0(UpnpPortBinding,1);
899         ms_mutex_init(&port->mutex, NULL);
900         port->state = LinphoneUpnpStateIdle;
901         port->protocol = UPNP_IGD_IP_PROTOCOL_UDP;      
902         port->local_addr[0] = '\0';
903         port->local_port = -1;
904         port->external_addr[0] = '\0';
905         port->external_port = -1;
906         port->to_remove = FALSE;
907         port->to_add = FALSE;
908         port->ref = 1;
909         port->last_update = 0;
910         return port;
911 }
912
913 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port) {
914         UpnpPortBinding *port_binding = linphone_upnp_port_binding_new();
915         port_binding->protocol = protocol;
916         port_binding->local_port = local_port;
917         port_binding->external_port = external_port;
918         return port_binding;
919 }
920
921 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port) {
922         UpnpPortBinding *tmp_binding;
923         UpnpPortBinding *end_binding;
924         end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, external_port);
925         tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding);
926         if(tmp_binding != NULL) {
927                 linphone_upnp_port_binding_release(end_binding);
928                 end_binding = tmp_binding;
929         }
930         return end_binding;     
931
932
933 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) {
934         UpnpPortBinding *new_port = NULL;
935         new_port = ms_new0(UpnpPortBinding,1);
936         memcpy(new_port, port, sizeof(UpnpPortBinding));
937         ms_mutex_init(&new_port->mutex, NULL);
938         new_port->ref = 1;
939         return new_port;
940 }
941
942 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
943         if(strlen(port->local_addr)) {
944                 ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d (retry %d)", msg,
945                                                         (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
946                                                                         port->external_port,
947                                                                         port->local_addr,
948                                                                         port->local_port,
949                                                                         port->retry - 1);
950         } else {
951                 ortp_log(level, "uPnP IGD: %s %s|%d->%d (retry %d)", msg,
952                                                         (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
953                                                                         port->external_port,
954                                                                         port->local_port,
955                                                                         port->retry - 1);
956         }
957 }
958
959 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
960         return port1->protocol == port2->protocol &&
961                         port1->local_port == port2->local_port &&
962                         port1->external_port == port2->external_port;
963 }
964
965 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port) {
966         UpnpPortBinding *port_mapping;
967         while(list != NULL) {
968                 port_mapping = (UpnpPortBinding *)list->data;
969                 if(linphone_upnp_port_binding_equal(port, port_mapping)) {
970                         return port_mapping;
971                 }
972                 list = list->next;
973         }
974
975         return NULL;
976 }
977
978 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port) {
979         ms_mutex_lock(&port->mutex);
980         port->ref++;
981         ms_mutex_unlock(&port->mutex);
982         return port;
983 }
984
985 void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
986         ms_mutex_lock(&port->mutex);
987         if(--port->ref == 0) {
988                 ms_mutex_unlock(&port->mutex);
989                 ms_mutex_destroy(&port->mutex);
990                 ms_free(port);
991                 return;
992         }
993         ms_mutex_unlock(&port->mutex);
994 }
995
996
997 /*
998  * uPnP Stream
999  */
1000
1001 UpnpStream* linphone_upnp_stream_new() {
1002         UpnpStream *stream = ms_new0(UpnpStream,1);
1003         stream->state = LinphoneUpnpStateIdle;
1004         stream->rtp = NULL; 
1005         stream->rtcp = NULL;
1006         return stream;
1007 }
1008
1009 void linphone_upnp_stream_destroy(UpnpStream* stream) {
1010         if(stream->rtp != NULL) {
1011                 linphone_upnp_port_binding_release(stream->rtp);
1012                 stream->rtp = NULL;
1013         }
1014         if(stream->rtcp != NULL) {
1015                 linphone_upnp_port_binding_release(stream->rtcp);
1016                 stream->rtcp = NULL;
1017         }
1018         ms_free(stream);
1019 }
1020
1021
1022 /*
1023  * uPnP Session
1024  */
1025
1026 UpnpSession* linphone_upnp_session_new(LinphoneCall* call) {
1027         UpnpSession *session = ms_new0(UpnpSession,1);
1028         session->call = call;
1029         session->state = LinphoneUpnpStateIdle;
1030         session->audio = linphone_upnp_stream_new();
1031         session->video = linphone_upnp_stream_new();
1032         return session;
1033 }
1034
1035 void linphone_upnp_session_destroy(UpnpSession *session) {
1036         LinphoneCore *lc = session->call->core;
1037
1038         if(lc->upnp != NULL) {
1039                 /* Remove bindings */
1040                 if(session->audio->rtp != NULL) {
1041                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp, TRUE);
1042                 }
1043                 if(session->audio->rtcp != NULL) {
1044                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp, TRUE);
1045                 }
1046                 if(session->video->rtp != NULL) {
1047                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp, TRUE);
1048                 }
1049                 if(session->video->rtcp != NULL) {
1050                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE);
1051                 }
1052         }
1053
1054         linphone_upnp_stream_destroy(session->audio);
1055         linphone_upnp_stream_destroy(session->video);
1056         ms_free(session);
1057 }
1058
1059 LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) {
1060         return session->state;
1061 }
1062
1063
1064 /*
1065  * uPnP Config
1066  */
1067
1068 struct linphone_upnp_config_list_port_bindings_struct {
1069         struct _LpConfig *lpc;
1070         MSList *retList;
1071 };
1072
1073 static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct linphone_upnp_config_list_port_bindings_struct *cookie) {
1074         char protocol_str[4]; // TCP or UDP
1075         upnp_igd_ip_protocol protocol;
1076         int external_port;
1077         int local_port;
1078         bool_t valid = TRUE;
1079         UpnpPortBinding *port;
1080         if(sscanf(entry, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) {
1081                 if(strcasecmp(protocol_str, "TCP") == 0) {
1082                         protocol = UPNP_IGD_IP_PROTOCOL_TCP;
1083                 } else if(strcasecmp(protocol_str, "UDP") == 0) {
1084                         protocol = UPNP_IGD_IP_PROTOCOL_UDP;
1085                 } else {
1086                         valid = FALSE;
1087                 }
1088                 if(valid) {
1089                         port = linphone_upnp_port_binding_new();
1090                         port->state = LinphoneUpnpStateOk;
1091                         port->protocol = protocol;
1092                         port->external_port = external_port;
1093                         port->local_port = local_port;
1094                         cookie->retList = ms_list_append(cookie->retList, port);
1095                 }
1096         } else {
1097                 valid = FALSE;
1098         }
1099         if(!valid) {
1100                 ms_warning("uPnP configuration invalid line: %s", entry);
1101         }
1102 }
1103
1104 MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) {
1105         struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL};
1106         lp_config_for_each_entry(lpc, UPNP_SECTION_NAME, (void(*)(const char *, void*))linphone_upnp_config_list_port_bindings_cb, &cookie);
1107         return cookie.retList;
1108 }
1109
1110 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1111         MSList *list;
1112         UpnpPortBinding *list_port;
1113
1114         list = lupnp->removing_configs;
1115         while(list != NULL) {
1116                 list_port = (UpnpPortBinding *)list->data;
1117                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1118                         lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port);
1119                         linphone_upnp_port_binding_release(list_port);
1120                         return;
1121                 }
1122                 list = ms_list_next(list);
1123         }
1124
1125         list = lupnp->adding_configs;
1126         while(list != NULL) {
1127                 list_port = (UpnpPortBinding *)list->data;
1128                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1129                         return;
1130                 }
1131                 list = ms_list_next(list);
1132         }
1133
1134         list_port = linphone_upnp_port_binding_copy(port);
1135         lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port);
1136 }
1137
1138 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1139         MSList *list;
1140         UpnpPortBinding *list_port;
1141
1142         list = lupnp->adding_configs;
1143         while(list != NULL) {
1144                 list_port = (UpnpPortBinding *)list->data;
1145                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1146                         lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port);
1147                         linphone_upnp_port_binding_release(list_port);
1148                         return;
1149                 }
1150                 list = ms_list_next(list);
1151         }
1152
1153         list = lupnp->removing_configs;
1154         while(list != NULL) {
1155                 list_port = (UpnpPortBinding *)list->data;
1156                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1157                         return;
1158                 }
1159                 list = ms_list_next(list);
1160         }
1161
1162         list_port = linphone_upnp_port_binding_copy(port);
1163         lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port);
1164 }