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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #include "upnp.h"
21 #include "private.h"
22 #include "linphone/lpconfig.h"
23 #include <ctype.h>
24
25 #define UPNP_STRINGIFY(x) #x
26 #define UPNP_TOSTRING(x) UPNP_STRINGIFY(x)
27
28 #define UPNP_ADD_MAX_RETRY 4
29 #define UPNP_REMOVE_MAX_RETRY 4
30 #define UPNP_SECTION_NAME "uPnP"
31 #define UPNP_CORE_READY_CHECK 1
32 #define UPNP_CORE_RETRY_DELAY 10
33 #define UPNP_CALL_RETRY_DELAY 3
34 #define UPNP_UUID_LEN 128
35 #define UPNP_UUID_LEN_STR UPNP_TOSTRING(UPNP_UUID_LEN)
36 /*
37 * uPnP Definitions
38 */
39
40 typedef struct _UpnpPortBinding {
41 ms_mutex_t mutex;
42 LinphoneUpnpState state;
43 upnp_igd_ip_protocol protocol;
44 char *device_id;
45 char local_addr[LINPHONE_IPADDR_SIZE];
46 int local_port;
47 char external_addr[LINPHONE_IPADDR_SIZE];
48 int external_port;
49 int retry;
50 int ref;
51 bool_t to_remove;
52 bool_t to_add;
53 time_t last_update;
54 } UpnpPortBinding;
55
56 typedef struct _UpnpStream {
57 UpnpPortBinding *rtp;
58 UpnpPortBinding *rtcp;
59 LinphoneUpnpState state;
60 } UpnpStream;
61
62 struct _UpnpSession {
63 LinphoneCall *call;
64 UpnpStream *audio;
65 UpnpStream *video;
66 LinphoneUpnpState state;
67 };
68
69 struct _UpnpContext {
70 LinphoneCore *lc;
71 upnp_igd_context *upnp_igd_ctxt;
72 UpnpPortBinding *sip_tcp;
73 UpnpPortBinding *sip_tls;
74 UpnpPortBinding *sip_udp;
75 LinphoneUpnpState state;
76 bctbx_list_t *removing_configs;
77 bctbx_list_t *adding_configs;
78 bctbx_list_t *pending_bindings;
79
80 ms_mutex_t mutex;
81 ms_cond_t empty_cond;
82
83 time_t last_ready_check;
84 LinphoneUpnpState last_ready_state;
85 };
86
87
88 bool_t linphone_core_upnp_hook(void *data);
89 void linphone_upnp_update(UpnpContext *ctx);
90 bool_t linphone_upnp_is_blacklisted(UpnpContext *ctx);
91
92 UpnpPortBinding *linphone_upnp_port_binding_new(void);
93 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port);
94 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(bctbx_list_t *list, upnp_igd_ip_protocol protocol, int local_port, int external_port);
95 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port);
96 void linphone_upnp_port_binding_set_device_id(UpnpPortBinding *port, const char * device_id);
97 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2);
98 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(bctbx_list_t *list, const UpnpPortBinding *port);
99 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port);
100 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay);
101 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
102 void linphone_upnp_port_binding_release(UpnpPortBinding *port);
103 void linphone_upnp_update_config(UpnpContext *lupnp);
104 void linphone_upnp_update_proxy(UpnpContext *lupnp, bool_t force);
105
106 // Configuration
107 bctbx_list_t *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc, const char *device_id);
108 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
109 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
110
111 // uPnP
112 int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry);
113 int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry);
114
linphone_upnp_strncmpi(const char * str1,const char * str2,int len)115 static int linphone_upnp_strncmpi(const char *str1, const char *str2, int len) {
116 int i = 0;
117 char char1, char2;
118 while(i < len) {
119 char1 = toupper(*str1);
120 char2 = toupper(*str2);
121 if(char1 == '\0' || char1 != char2) {
122 return char1 - char2;
123 }
124 str1++;
125 str2++;
126 i++;
127 }
128 return 0;
129 }
130
linphone_upnp_str_min(const char * str1,const char * str2)131 static int linphone_upnp_str_min(const char *str1, const char *str2) {
132 int len1 = strlen(str1);
133 int len2 = strlen(str2);
134 if(len1 > len2) {
135 return len2;
136 }
137 return len1;
138 }
139
linphone_upnp_format_device_id(const char * device_id)140 char * linphone_upnp_format_device_id(const char *device_id) {
141 char *ret = NULL;
142 char *tmp;
143 char tchar;
144 bool_t copy;
145 if(device_id == NULL) {
146 return ret;
147 }
148 ret = ms_new0(char, UPNP_UUID_LEN + 1);
149 tmp = ret;
150 if(linphone_upnp_strncmpi(device_id, "uuid:", linphone_upnp_str_min(device_id, "uuid:")) == 0) {
151 device_id += strlen("uuid:");
152 }
153 while(*device_id != '\0' && tmp - ret < UPNP_UUID_LEN) {
154 copy = FALSE;
155 tchar = *device_id;
156 if(tchar >= '0' && tchar <= '9')
157 copy = TRUE;
158 if(!copy && tchar >= 'A' && tchar <= 'Z')
159 copy = TRUE;
160 if(!copy && tchar >= 'a' && tchar <= 'z')
161 copy = TRUE;
162 if(copy) {
163 *tmp = *device_id;
164 tmp++;
165 }
166 device_id++;
167 }
168 *tmp = '\0';
169 return ret;
170 }
171
172 /**
173 * uPnP Callbacks
174 */
175
176 /* Convert uPnP IGD logs to ortp logs */
linphone_upnp_igd_print(void * cookie,upnp_igd_print_level level,const char * fmt,va_list list)177 void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) {
178 int ortp_level = ORTP_DEBUG;
179 switch(level) {
180 case UPNP_IGD_MESSAGE:
181 ortp_level = ORTP_MESSAGE;
182 break;
183 case UPNP_IGD_WARNING:
184 ortp_level = ORTP_DEBUG; // Too verbose otherwise
185 break;
186 case UPNP_IGD_ERROR:
187 ortp_level = ORTP_DEBUG; // Too verbose otherwise
188 break;
189 default:
190 break;
191 }
192 ortp_logv(ORTP_LOG_DOMAIN, ortp_level, fmt, list);
193 }
194
linphone_upnp_igd_callback(void * cookie,upnp_igd_event event,void * arg)195 void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
196 UpnpContext *lupnp = (UpnpContext *)cookie;
197 upnp_igd_port_mapping *mapping = NULL;
198 UpnpPortBinding *port_mapping = NULL;
199 const char *ip_address = NULL;
200 const char *connection_status = NULL;
201 bool_t nat_enabled = FALSE;
202 bool_t blacklisted = FALSE;
203 LinphoneUpnpState old_state;
204
205 if(lupnp == NULL || lupnp->upnp_igd_ctxt == NULL) {
206 ms_error("uPnP IGD: Invalid context in callback");
207 return;
208 }
209
210 ms_mutex_lock(&lupnp->mutex);
211 old_state = lupnp->state;
212
213 switch(event) {
214 case UPNP_IGD_DEVICE_ADDED:
215 case UPNP_IGD_DEVICE_REMOVED:
216 case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED:
217 case UPNP_IGD_NAT_ENABLED_CHANGED:
218 case UPNP_IGD_CONNECTION_STATUS_CHANGED:
219 ip_address = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
220 connection_status = upnp_igd_get_connection_status(lupnp->upnp_igd_ctxt);
221 nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt);
222 blacklisted = linphone_upnp_is_blacklisted(lupnp);
223
224 if(ip_address == NULL || connection_status == NULL) {
225 ms_message("uPnP IGD: Pending");
226 lupnp->state = LinphoneUpnpStatePending;
227 } else if(strcasecmp(connection_status, "Connected") || !nat_enabled) {
228 ms_message("uPnP IGD: Not Available");
229 lupnp->state = LinphoneUpnpStateNotAvailable;
230 } else if(blacklisted) {
231 ms_message("uPnP IGD: Router is blacklisted");
232 lupnp->state = LinphoneUpnpStateBlacklisted;
233 } else {
234 ms_message("uPnP IGD: Connected");
235 lupnp->state = LinphoneUpnpStateOk;
236 if(old_state != LinphoneUpnpStateOk) {
237 linphone_upnp_update(lupnp);
238 }
239 }
240
241 break;
242
243 case UPNP_IGD_PORT_MAPPING_ADD_SUCCESS:
244 mapping = (upnp_igd_port_mapping *) arg;
245 port_mapping = (UpnpPortBinding*) mapping->cookie;
246 port_mapping->external_port = mapping->remote_port;
247 port_mapping->state = LinphoneUpnpStateOk;
248 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping);
249 linphone_upnp_config_add_port_binding(lupnp, port_mapping);
250
251 break;
252
253 case UPNP_IGD_PORT_MAPPING_ADD_FAILURE:
254 mapping = (upnp_igd_port_mapping *) arg;
255 port_mapping = (UpnpPortBinding*) mapping->cookie;
256 port_mapping->external_port = -1; //Force random external port
257 if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE) != 0) {
258 linphone_upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping);
259 }
260
261 break;
262
263 case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS:
264 mapping = (upnp_igd_port_mapping *) arg;
265 port_mapping = (UpnpPortBinding*) mapping->cookie;
266 port_mapping->state = LinphoneUpnpStateIdle;
267 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping);
268 linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
269
270 break;
271
272 case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE:
273 mapping = (upnp_igd_port_mapping *) arg;
274 port_mapping = (UpnpPortBinding*) mapping->cookie;
275 if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE) != 0) {
276 linphone_upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping);
277 linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
278 }
279
280 break;
281
282 default:
283 break;
284 }
285
286 if(port_mapping != NULL) {
287 /*
288 * Execute delayed actions
289 */
290 if(port_mapping->to_remove) {
291 if(port_mapping->state == LinphoneUpnpStateOk) {
292 port_mapping->to_remove = FALSE;
293 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, FALSE);
294 } else if(port_mapping->state == LinphoneUpnpStateKo) {
295 port_mapping->to_remove = FALSE;
296 }
297 }
298 if(port_mapping->to_add) {
299 if(port_mapping->state == LinphoneUpnpStateIdle || port_mapping->state == LinphoneUpnpStateKo) {
300 port_mapping->to_add = FALSE;
301 linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, FALSE);
302 }
303 }
304
305 lupnp->pending_bindings = bctbx_list_remove(lupnp->pending_bindings, port_mapping);
306 linphone_upnp_port_binding_release(port_mapping);
307 }
308
309 /*
310 * If there is no pending binding emit a signal
311 */
312 if(lupnp->pending_bindings == NULL) {
313 ms_cond_signal(&lupnp->empty_cond);
314 }
315 ms_mutex_unlock(&lupnp->mutex);
316 }
317
318
319 /**
320 * uPnP Context
321 */
322
linphone_upnp_context_new(LinphoneCore * lc)323 UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
324 UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1);
325 char address[LINPHONE_IPADDR_SIZE];
326 const char*upnp_binding_address=address;
327 if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,NULL,address)) {
328 ms_warning("Linphone core [%p] cannot guess local address for upnp, let's choice the lib",lc);
329 upnp_binding_address=NULL;
330 }
331 ms_mutex_init(&lupnp->mutex, NULL);
332 ms_cond_init(&lupnp->empty_cond, NULL);
333
334 lupnp->last_ready_check = 0;
335 lupnp->last_ready_state = LinphoneUpnpStateIdle;
336
337 lupnp->lc = lc;
338 lupnp->pending_bindings = NULL;
339 lupnp->adding_configs = NULL;
340 lupnp->removing_configs = NULL;
341 lupnp->state = LinphoneUpnpStateIdle;
342 ms_message("uPnP IGD: New %p for core %p bound to %s", lupnp, lc,upnp_binding_address);
343
344 // Init ports
345 lupnp->sip_udp = NULL;
346 lupnp->sip_tcp = NULL;
347 lupnp->sip_tls = NULL;
348
349 linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp);
350
351 lupnp->upnp_igd_ctxt = NULL;
352 lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, address, lupnp);
353 if(lupnp->upnp_igd_ctxt == NULL) {
354 lupnp->state = LinphoneUpnpStateKo;
355 ms_error("Can't create uPnP IGD context");
356 return NULL;
357 }
358
359 lupnp->state = LinphoneUpnpStatePending;
360 upnp_igd_start(lupnp->upnp_igd_ctxt);
361
362 return lupnp;
363 }
364
linphone_upnp_context_destroy(UpnpContext * lupnp)365 void linphone_upnp_context_destroy(UpnpContext *lupnp) {
366 linphone_core_remove_iterate_hook(lupnp->lc, linphone_core_upnp_hook, lupnp);
367
368 ms_mutex_lock(&lupnp->mutex);
369
370 if(lupnp->lc->sip_network_reachable) {
371 /* Send port binding removes */
372 if(lupnp->sip_udp != NULL) {
373 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp, TRUE);
374 }
375 if(lupnp->sip_tcp != NULL) {
376 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp, TRUE);
377 }
378 if(lupnp->sip_tls != NULL) {
379 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls, TRUE);
380 }
381 }
382
383 /* Wait all pending bindings are done */
384 if(lupnp->pending_bindings != NULL) {
385 ms_message("uPnP IGD: Wait all pending port bindings ...");
386 ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex);
387 }
388 ms_mutex_unlock(&lupnp->mutex);
389
390 if(lupnp->upnp_igd_ctxt != NULL) {
391 upnp_igd_destroy(lupnp->upnp_igd_ctxt);
392 lupnp->upnp_igd_ctxt = NULL;
393 }
394
395 /* No more multi threading here */
396
397 /* Run one more time configuration update and proxy */
398 linphone_upnp_update_config(lupnp);
399 linphone_upnp_update_proxy(lupnp, TRUE);
400
401 /* Release port bindings */
402 if(lupnp->sip_udp != NULL) {
403 linphone_upnp_port_binding_release(lupnp->sip_udp);
404 lupnp->sip_udp = NULL;
405 }
406 if(lupnp->sip_tcp != NULL) {
407 linphone_upnp_port_binding_release(lupnp->sip_tcp);
408 lupnp->sip_tcp = NULL;
409 }
410 if(lupnp->sip_tls != NULL) {
411 linphone_upnp_port_binding_release(lupnp->sip_tls);
412 lupnp->sip_tcp = NULL;
413 }
414
415 /* Release lists */
416 bctbx_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
417 lupnp->adding_configs = bctbx_list_free(lupnp->adding_configs);
418 bctbx_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
419 lupnp->removing_configs = bctbx_list_free(lupnp->removing_configs);
420 bctbx_list_for_each(lupnp->pending_bindings,(void (*)(void*))linphone_upnp_port_binding_release);
421 lupnp->pending_bindings = bctbx_list_free(lupnp->pending_bindings);
422
423 ms_mutex_destroy(&lupnp->mutex);
424 ms_cond_destroy(&lupnp->empty_cond);
425
426 ms_message("uPnP IGD: destroy %p", lupnp);
427 ms_free(lupnp);
428 }
429
linphone_upnp_context_get_state(UpnpContext * lupnp)430 LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) {
431 LinphoneUpnpState state = LinphoneUpnpStateKo;
432 if(lupnp != NULL) {
433 ms_mutex_lock(&lupnp->mutex);
434 state = lupnp->state;
435 ms_mutex_unlock(&lupnp->mutex);
436 }
437 return state;
438 }
439
_linphone_upnp_context_is_ready_for_register(UpnpContext * lupnp)440 bool_t _linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
441 bool_t ready = TRUE;
442
443 // 1 Check global uPnP state
444 ready = (lupnp->state == LinphoneUpnpStateOk);
445
446 // 2 Check external ip address
447 if(ready) {
448 if (upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt) == NULL) {
449 ready = FALSE;
450 }
451 }
452
453 // 3 Check sip ports bindings
454 if(ready) {
455 if(lupnp->sip_udp != NULL) {
456 if(lupnp->sip_udp->state != LinphoneUpnpStateOk) {
457 ready = FALSE;
458 }
459 } else if(lupnp->sip_tcp != NULL) {
460 if(lupnp->sip_tcp->state != LinphoneUpnpStateOk) {
461 ready = FALSE;
462 }
463 } else if(lupnp->sip_tls != NULL) {
464 if(lupnp->sip_tls->state != LinphoneUpnpStateOk) {
465 ready = FALSE;
466 }
467 } else {
468 ready = FALSE;
469 }
470 }
471
472 return ready;
473 }
474
linphone_upnp_context_is_ready_for_register(UpnpContext * lupnp)475 bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
476 bool_t ready = FALSE;
477 if(lupnp != NULL) {
478 ms_mutex_lock(&lupnp->mutex);
479 ready = _linphone_upnp_context_is_ready_for_register(lupnp);
480 ms_mutex_unlock(&lupnp->mutex);
481 }
482 return ready;
483 }
484
linphone_upnp_context_get_external_port(UpnpContext * lupnp)485 int linphone_upnp_context_get_external_port(UpnpContext *lupnp) {
486 int port = -1;
487 if(lupnp != NULL) {
488 ms_mutex_lock(&lupnp->mutex);
489
490 if(lupnp->sip_udp != NULL) {
491 if(lupnp->sip_udp->state == LinphoneUpnpStateOk) {
492 port = lupnp->sip_udp->external_port;
493 }
494 } else if(lupnp->sip_tcp != NULL) {
495 if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) {
496 port = lupnp->sip_tcp->external_port;
497 }
498 } else if(lupnp->sip_tls != NULL) {
499 if(lupnp->sip_tls->state == LinphoneUpnpStateOk) {
500 port = lupnp->sip_tls->external_port;
501 }
502 }
503
504 ms_mutex_unlock(&lupnp->mutex);
505 }
506 return port;
507 }
508
linphone_upnp_is_blacklisted(UpnpContext * lupnp)509 bool_t linphone_upnp_is_blacklisted(UpnpContext *lupnp) {
510 const char * device_model_name = upnp_igd_get_device_model_name(lupnp->upnp_igd_ctxt);
511 const char * device_model_number = upnp_igd_get_device_model_number(lupnp->upnp_igd_ctxt);
512 const char * blacklist = lp_config_get_string(lupnp->lc->config, "net", "upnp_blacklist", NULL);
513 bool_t blacklisted = FALSE;
514 char *str;
515 char *pch;
516 char *model_name;
517 char *model_number;
518
519 // Sanity checks
520 if(device_model_name == NULL || device_model_number == NULL || blacklist == NULL) {
521 return FALSE;
522 }
523
524 // Find in the list
525 str = strdup(blacklist);
526 pch = strtok(str, ";");
527 while (pch != NULL && !blacklisted) {
528 // Extract model name & number
529 model_name = pch;
530 model_number = strstr(pch, ",");
531 if(model_number != NULL) {
532 *(model_number++) = '\0';
533 }
534
535 // Compare with current device
536 if(strcmp(model_name, device_model_name) == 0) {
537 if(model_number == NULL || strcmp(model_number, device_model_number) == 0) {
538 blacklisted = TRUE;
539 }
540 }
541 pch = strtok(NULL, ";");
542 }
543 free(str);
544
545 return blacklisted;
546 }
547
linphone_upnp_refresh(UpnpContext * lupnp)548 void linphone_upnp_refresh(UpnpContext * lupnp) {
549 upnp_igd_refresh(lupnp->upnp_igd_ctxt);
550 }
551
linphone_upnp_context_get_external_ipaddress(UpnpContext * lupnp)552 const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *lupnp) {
553 const char* addr = NULL;
554 if(lupnp != NULL) {
555 ms_mutex_lock(&lupnp->mutex);
556 addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
557 ms_mutex_unlock(&lupnp->mutex);
558 }
559 return addr;
560 }
561
linphone_upnp_context_send_add_port_binding(UpnpContext * lupnp,UpnpPortBinding * port,bool_t retry)562 int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
563 upnp_igd_port_mapping mapping;
564 char description[128];
565 int ret;
566
567 if(lupnp->state != LinphoneUpnpStateOk) {
568 return -2;
569 }
570
571 // Compute port binding state
572 if(port->state != LinphoneUpnpStateAdding) {
573 port->to_remove = FALSE;
574 switch(port->state) {
575 case LinphoneUpnpStateKo:
576 case LinphoneUpnpStateIdle: {
577 port->retry = 0;
578 port->state = LinphoneUpnpStateAdding;
579 }
580 break;
581 case LinphoneUpnpStateRemoving: {
582 port->to_add = TRUE;
583 return 0;
584 }
585 break;
586 default:
587 return 0;
588 }
589 }
590
591 // No retry if specified
592 if(port->retry != 0 && !retry) {
593 return -1;
594 }
595
596 if(port->retry >= UPNP_ADD_MAX_RETRY) {
597 ret = -1;
598 } else {
599 linphone_upnp_port_binding_set_device_id(port, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
600 mapping.cookie = linphone_upnp_port_binding_retain(port);
601 lupnp->pending_bindings = bctbx_list_append(lupnp->pending_bindings, mapping.cookie);
602
603 mapping.local_port = port->local_port;
604 mapping.local_host = port->local_addr;
605 if(port->external_port == -1)
606 port->external_port = rand()%(0xffff - 1024) + 1024;
607 mapping.remote_port = port->external_port;
608 mapping.remote_host = "";
609 snprintf(description, 128, "%s %s at %s:%d",
610 "Linphone",
611 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP": "UDP",
612 port->local_addr, port->local_port);
613 mapping.description = description;
614 mapping.protocol = port->protocol;
615
616 port->retry++;
617 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to add port binding", port);
618 ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
619 }
620 if(ret != 0) {
621 port->state = LinphoneUpnpStateKo;
622 }
623 return ret;
624 }
625
linphone_upnp_context_send_remove_port_binding(UpnpContext * lupnp,UpnpPortBinding * port,bool_t retry)626 int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
627 upnp_igd_port_mapping mapping;
628 int ret;
629
630 if(lupnp->state != LinphoneUpnpStateOk) {
631 return -2;
632 }
633
634 // Compute port binding state
635 if(port->state != LinphoneUpnpStateRemoving) {
636 port->to_add = FALSE;
637 switch(port->state) {
638 case LinphoneUpnpStateOk: {
639 port->retry = 0;
640 port->state = LinphoneUpnpStateRemoving;
641 }
642 break;
643 case LinphoneUpnpStateAdding: {
644 port->to_remove = TRUE;
645 return 0;
646 }
647 break;
648 default:
649 return 0;
650 }
651 }
652
653 // No retry if specified
654 if(port->retry != 0 && !retry) {
655 return 1;
656 }
657
658 if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
659 ret = -1;
660 } else {
661 linphone_upnp_port_binding_set_device_id(port, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
662 mapping.cookie = linphone_upnp_port_binding_retain(port);
663 lupnp->pending_bindings = bctbx_list_append(lupnp->pending_bindings, mapping.cookie);
664
665 mapping.remote_port = port->external_port;
666 mapping.remote_host = "";
667 mapping.protocol = port->protocol;
668 port->retry++;
669 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to remove port binding", port);
670 ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
671 }
672 if(ret != 0) {
673 port->state = LinphoneUpnpStateKo;
674 }
675 return ret;
676 }
677
678 /*
679 * uPnP Core interfaces
680 */
681
linphone_call_update_upnp_audio_video(LinphoneCall * call,bool_t audio,bool_t video)682 int linphone_call_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) {
683 LinphoneCore *lc = call->core;
684 UpnpContext *lupnp = lc->upnp;
685 int ret = -1;
686
687 if(lupnp == NULL) {
688 return ret;
689 }
690
691 ms_mutex_lock(&lupnp->mutex);
692
693 // Don't handle when the call
694 if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
695 ret = 0;
696
697 /*
698 * Audio part
699 */
700 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtp,
701 UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->media_ports[call->main_audio_stream_index].rtp_port:0, UPNP_CALL_RETRY_DELAY);
702
703 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtcp,
704 UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->media_ports[call->main_audio_stream_index].rtcp_port:0, UPNP_CALL_RETRY_DELAY);
705
706 /*
707 * Video part
708 */
709 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtp,
710 UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->media_ports[call->main_video_stream_index].rtp_port:0, UPNP_CALL_RETRY_DELAY);
711
712 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtcp,
713 UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->media_ports[call->main_video_stream_index].rtcp_port:0, UPNP_CALL_RETRY_DELAY);
714 }
715
716 ms_mutex_unlock(&lupnp->mutex);
717
718 /*
719 * Update uPnP call state
720 */
721 linphone_upnp_call_process(call);
722
723 return ret;
724 }
725
726
727
linphone_call_update_upnp_from_remote_media_description(LinphoneCall * call,const SalMediaDescription * md)728 int linphone_call_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) {
729 bool_t audio = FALSE;
730 bool_t video = FALSE;
731 int i;
732 const SalStreamDescription *stream;
733
734 for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
735 stream = &md->streams[i];
736 if (!sal_stream_description_active(stream)) continue;
737 if(stream->type == SalAudio) {
738 audio = TRUE;
739 } else if(stream->type == SalVideo) {
740 video = TRUE;
741 }
742 }
743
744 return linphone_call_update_upnp_audio_video(call, audio, video);
745 }
746
linphone_call_update_upnp(LinphoneCall * call)747 int linphone_call_update_upnp(LinphoneCall *call) {
748 return linphone_call_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
749 }
750
linphone_call_update_upnp_state_in_call_stats(LinphoneCall * call)751 void linphone_call_update_upnp_state_in_call_stats(LinphoneCall *call) {
752 call->audio_stats->upnp_state = call->upnp_session->audio->state;
753 call->video_stats->upnp_state = call->upnp_session->video->state;
754 }
755
linphone_upnp_update_stream_state(UpnpStream * stream)756 void linphone_upnp_update_stream_state(UpnpStream *stream) {
757 if((stream->rtp == NULL || stream->rtp->state == LinphoneUpnpStateOk || stream->rtp->state == LinphoneUpnpStateIdle) &&
758 (stream->rtcp == NULL || stream->rtcp->state == LinphoneUpnpStateOk || stream->rtcp->state == LinphoneUpnpStateIdle)) {
759 stream->state = LinphoneUpnpStateOk;
760 } else if((stream->rtp != NULL &&
761 (stream->rtp->state == LinphoneUpnpStateAdding || stream->rtp->state == LinphoneUpnpStateRemoving)) ||
762 (stream->rtcp != NULL &&
763 (stream->rtcp->state == LinphoneUpnpStateAdding || stream->rtcp->state == LinphoneUpnpStateRemoving))) {
764 stream->state = LinphoneUpnpStatePending;
765 } else if((stream->rtp != NULL && stream->rtp->state == LinphoneUpnpStateKo) ||
766 (stream->rtcp != NULL && stream->rtcp->state == LinphoneUpnpStateKo)) {
767 stream->state = LinphoneUpnpStateKo;
768 } else {
769 ms_error("Invalid stream %p state", stream);
770 }
771 }
772
linphone_upnp_call_process(LinphoneCall * call)773 int linphone_upnp_call_process(LinphoneCall *call) {
774 LinphoneCore *lc = call->core;
775 UpnpContext *lupnp = lc->upnp;
776 int ret = -1;
777 LinphoneUpnpState oldState = 0, newState = 0;
778
779 if(lupnp == NULL) {
780 return ret;
781 }
782
783 ms_mutex_lock(&lupnp->mutex);
784
785 // Don't handle when the call
786 if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
787 ret = 0;
788
789 /*
790 * Update Audio state
791 */
792 linphone_upnp_update_stream_state(call->upnp_session->audio);
793
794 /*
795 * Update Video state
796 */
797 linphone_upnp_update_stream_state(call->upnp_session->video);
798
799 /*
800 * Update stat
801 */
802 linphone_call_update_upnp_state_in_call_stats(call);
803
804 /*
805 * Update session state
806 */
807 oldState = call->upnp_session->state;
808 if(call->upnp_session->audio->state == LinphoneUpnpStateOk &&
809 call->upnp_session->video->state == LinphoneUpnpStateOk) {
810 call->upnp_session->state = LinphoneUpnpStateOk;
811 } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending ||
812 call->upnp_session->video->state == LinphoneUpnpStatePending) {
813 call->upnp_session->state = LinphoneUpnpStatePending;
814 } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo ||
815 call->upnp_session->video->state == LinphoneUpnpStateKo) {
816 call->upnp_session->state = LinphoneUpnpStateKo;
817 } else {
818 call->upnp_session->state = LinphoneUpnpStateIdle;
819 }
820 newState = call->upnp_session->state;
821 }
822
823 ms_mutex_unlock(&lupnp->mutex);
824
825 /* When change is done proceed update */
826 if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
827 (newState == LinphoneUpnpStateOk || newState == LinphoneUpnpStateKo)) {
828 if(call->upnp_session->state == LinphoneUpnpStateOk)
829 ms_message("uPnP IGD: uPnP for Call %p is ok", call);
830 else
831 ms_message("uPnP IGD: uPnP for Call %p is ko", call);
832
833 switch (call->state) {
834 case LinphoneCallUpdating:
835 linphone_call_start_update(call);
836 break;
837 case LinphoneCallUpdatedByRemote:
838 linphone_call_start_accept_update(call, call->prevstate, linphone_call_state_to_string(call->prevstate));
839 break;
840 case LinphoneCallOutgoingInit:
841 linphone_call_proceed_with_invite_if_ready(call, NULL);
842 break;
843 case LinphoneCallIdle:
844 linphone_call_update_local_media_description_from_ice_or_upnp(call);
845 sal_call_set_local_media_description(call->op,call->localdesc);
846 linphone_core_notify_incoming_call(lc, call);
847 break;
848 default:
849 break;
850 }
851 }
852
853 return ret;
854 }
855
linphone_core_upnp_get_charptr_null(const char * str)856 static const char *linphone_core_upnp_get_charptr_null(const char *str) {
857 if(str != NULL) {
858 return str;
859 }
860 return "(Null)";
861 }
862
linphone_upnp_update(UpnpContext * lupnp)863 void linphone_upnp_update(UpnpContext *lupnp) {
864 bctbx_list_t *global_list = NULL;
865 bctbx_list_t *list = NULL;
866 bctbx_list_t *item;
867 LinphoneCall *call;
868 UpnpPortBinding *port_mapping, *port_mapping2;
869
870 ms_message("uPnP IGD: Name:%s", linphone_core_upnp_get_charptr_null(upnp_igd_get_device_name(lupnp->upnp_igd_ctxt)));
871 ms_message("uPnP IGD: Device:%s %s",
872 linphone_core_upnp_get_charptr_null(upnp_igd_get_device_model_name(lupnp->upnp_igd_ctxt)),
873 linphone_core_upnp_get_charptr_null(upnp_igd_get_device_model_number(lupnp->upnp_igd_ctxt)));
874 ms_message("uPnP IGD: Refresh mappings");
875
876 if(lupnp->sip_udp != NULL) {
877 global_list = bctbx_list_append(global_list, lupnp->sip_udp);
878 }
879 if(lupnp->sip_tcp != NULL) {
880 global_list = bctbx_list_append(global_list, lupnp->sip_tcp);
881 }
882 if(lupnp->sip_tls != NULL) {
883 global_list = bctbx_list_append(global_list, lupnp->sip_tls);
884 }
885
886 list = lupnp->lc->calls;
887 while(list != NULL) {
888 call = (LinphoneCall *)list->data;
889 if(call->upnp_session != NULL) {
890 if(call->upnp_session->audio->rtp != NULL) {
891 global_list = bctbx_list_append(global_list, call->upnp_session->audio->rtp);
892 }
893 if(call->upnp_session->audio->rtcp != NULL) {
894 global_list = bctbx_list_append(global_list, call->upnp_session->audio->rtcp);
895 }
896 if(call->upnp_session->video->rtp != NULL) {
897 global_list = bctbx_list_append(global_list, call->upnp_session->video->rtp);
898 }
899 if(call->upnp_session->video->rtcp != NULL) {
900 global_list = bctbx_list_append(global_list, call->upnp_session->video->rtcp);
901 }
902 }
903 list = list->next;
904 }
905
906 list = linphone_upnp_config_list_port_bindings(lupnp->lc->config, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
907 for(item = list;item != NULL; item = item->next) {
908 port_mapping = (UpnpPortBinding *)item->data;
909 port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping);
910 if(port_mapping2 == NULL) {
911 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
912 } else if(port_mapping2->state == LinphoneUpnpStateIdle){
913 /* Force to remove */
914 port_mapping2->state = LinphoneUpnpStateOk;
915 }
916 }
917 bctbx_list_for_each(list, (void (*)(void*))linphone_upnp_port_binding_release);
918 list = bctbx_list_free(list);
919
920
921 // (Re)Add removed port bindings
922 list = global_list;
923 while(list != NULL) {
924 port_mapping = (UpnpPortBinding *)list->data;
925 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
926 linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE);
927 list = list->next;
928 }
929 global_list = bctbx_list_free(global_list);
930 }
931
linphone_upnp_update_port_binding(UpnpContext * lupnp,UpnpPortBinding ** port_mapping,upnp_igd_ip_protocol protocol,int port,int retry_delay)932 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay) {
933 const char *local_addr, *external_addr;
934 time_t now = time(NULL);
935 if(port != 0) {
936 if(*port_mapping != NULL) {
937 if(port != (*port_mapping)->local_port) {
938 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
939 *port_mapping = NULL;
940 }
941 }
942 if(*port_mapping == NULL) {
943 *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port);
944 }
945
946 // Get addresses
947 local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
948 external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
949
950 // Force binding update on local address change
951 if(local_addr != NULL) {
952 if(strncmp((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr))) {
953 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
954 strncpy((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr));
955 }
956 }
957 if(external_addr != NULL) {
958 strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr));
959 }
960
961 // Add (if not already done) the binding
962 if(now - (*port_mapping)->last_update >= retry_delay) {
963 (*port_mapping)->last_update = now;
964 linphone_upnp_context_send_add_port_binding(lupnp, *port_mapping, FALSE);
965 }
966 } else {
967 if(*port_mapping != NULL) {
968 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
969 *port_mapping = NULL;
970 }
971 }
972 }
973
linphone_upnp_update_config(UpnpContext * lupnp)974 void linphone_upnp_update_config(UpnpContext* lupnp) {
975 char key[64];
976 const bctbx_list_t *item;
977 UpnpPortBinding *port_mapping;
978
979 /* Add configs */
980 for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
981 port_mapping = (UpnpPortBinding *)item->data;
982 snprintf(key, sizeof(key), "%s-%s-%d-%d",
983 port_mapping->device_id,
984 (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
985 port_mapping->external_port,
986 port_mapping->local_port);
987 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP");
988 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
989 }
990 bctbx_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
991 lupnp->adding_configs = bctbx_list_free(lupnp->adding_configs);
992
993 /* Remove configs */
994 for(item = lupnp->removing_configs;item!=NULL;item=item->next) {
995 port_mapping = (UpnpPortBinding *)item->data;
996 snprintf(key, sizeof(key), "%s-%s-%d-%d",
997 port_mapping->device_id,
998 (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
999 port_mapping->external_port,
1000 port_mapping->local_port);
1001 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL);
1002 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
1003 }
1004 bctbx_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
1005 lupnp->removing_configs = bctbx_list_free(lupnp->removing_configs);
1006 }
1007
linphone_upnp_update_proxy(UpnpContext * lupnp,bool_t force)1008 void linphone_upnp_update_proxy(UpnpContext* lupnp, bool_t force) {
1009 LinphoneUpnpState ready_state;
1010 const bctbx_list_t *item;
1011 time_t now = (force)? (lupnp->last_ready_check + UPNP_CORE_READY_CHECK) : time(NULL);
1012
1013 /* Refresh registers if we are ready */
1014 if(now - lupnp->last_ready_check >= UPNP_CORE_READY_CHECK) {
1015 lupnp->last_ready_check = now;
1016 ready_state = (_linphone_upnp_context_is_ready_for_register(lupnp))? LinphoneUpnpStateOk: LinphoneUpnpStateKo;
1017 if(ready_state != lupnp->last_ready_state) {
1018 for(item=linphone_core_get_proxy_config_list(lupnp->lc);item!=NULL;item=item->next) {
1019 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)item->data;
1020 if (linphone_proxy_config_register_enabled(cfg)) {
1021 if (ready_state != LinphoneUpnpStateOk) {
1022 // Only reset ithe registration if we require that upnp should be ok
1023 if(lupnp->lc->sip_conf.register_only_when_upnp_is_ok) {
1024 linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone, "Registration impossible (uPnP not ready)");
1025 } else {
1026 cfg->commit=TRUE;
1027 }
1028 } else {
1029 cfg->commit=TRUE;
1030 }
1031 }
1032 }
1033 lupnp->last_ready_state = ready_state;
1034 }
1035 }
1036 }
1037
linphone_core_upnp_hook(void * data)1038 bool_t linphone_core_upnp_hook(void *data) {
1039 LCSipTransports transport;
1040 UpnpContext *lupnp = (UpnpContext *)data;
1041
1042 ms_mutex_lock(&lupnp->mutex);
1043
1044 /* Update ports */
1045 if(lupnp->state == LinphoneUpnpStateOk) {
1046 linphone_core_get_sip_transports(lupnp->lc, &transport);
1047 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_udp, UPNP_IGD_IP_PROTOCOL_UDP, transport.udp_port, UPNP_CORE_RETRY_DELAY);
1048 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tcp, UPNP_IGD_IP_PROTOCOL_TCP, transport.tcp_port, UPNP_CORE_RETRY_DELAY);
1049 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tls, UPNP_IGD_IP_PROTOCOL_TCP, transport.tls_port, UPNP_CORE_RETRY_DELAY);
1050 }
1051
1052 linphone_upnp_update_proxy(lupnp, FALSE);
1053 linphone_upnp_update_config(lupnp);
1054
1055 ms_mutex_unlock(&lupnp->mutex);
1056 return TRUE;
1057 }
1058
linphone_call_update_local_media_description_from_upnp(SalMediaDescription * desc,UpnpSession * session)1059 int linphone_call_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) {
1060 int i;
1061 SalStreamDescription *stream;
1062 UpnpStream *upnpStream;
1063
1064 for (i = 0; i < SAL_MEDIA_DESCRIPTION_MAX_STREAMS; i++) {
1065 stream = &desc->streams[i];
1066 if (!sal_stream_description_active(stream)) continue;
1067 upnpStream = NULL;
1068 if(stream->type == SalAudio) {
1069 upnpStream = session->audio;
1070 } else if(stream->type == SalVideo) {
1071 upnpStream = session->video;
1072 }
1073 if(upnpStream != NULL) {
1074 if(upnpStream->rtp != NULL && upnpStream->rtp->state == LinphoneUpnpStateOk) {
1075 strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE);
1076 stream->rtp_port = upnpStream->rtp->external_port;
1077 }
1078 if(upnpStream->rtcp != NULL && upnpStream->rtcp->state == LinphoneUpnpStateOk) {
1079 strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE);
1080 stream->rtcp_port = upnpStream->rtcp->external_port;
1081 }
1082 }
1083 }
1084 return 0;
1085 }
1086
1087
1088 /*
1089 * uPnP Port Binding
1090 */
1091
linphone_upnp_port_binding_new(void)1092 UpnpPortBinding *linphone_upnp_port_binding_new(void) {
1093 UpnpPortBinding *port = NULL;
1094 port = ms_new0(UpnpPortBinding,1);
1095 ms_mutex_init(&port->mutex, NULL);
1096 port->state = LinphoneUpnpStateIdle;
1097 port->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
1098 port->device_id = NULL;
1099 port->local_addr[0] = '\0';
1100 port->local_port = -1;
1101 port->external_addr[0] = '\0';
1102 port->external_port = -1;
1103 port->to_remove = FALSE;
1104 port->to_add = FALSE;
1105 port->ref = 1;
1106 port->last_update = 0;
1107 return port;
1108 }
1109
linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol,int local_port,int external_port)1110 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port) {
1111 UpnpPortBinding *port_binding = linphone_upnp_port_binding_new();
1112 port_binding->protocol = protocol;
1113 port_binding->local_port = local_port;
1114 port_binding->external_port = external_port;
1115 return port_binding;
1116 }
1117
linphone_upnp_port_binding_new_or_collect(bctbx_list_t * list,upnp_igd_ip_protocol protocol,int local_port,int external_port)1118 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(bctbx_list_t *list, upnp_igd_ip_protocol protocol, int local_port, int external_port) {
1119 UpnpPortBinding *tmp_binding;
1120 UpnpPortBinding *end_binding;
1121
1122 // Seek an binding with same protocol and local port
1123 end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, -1);
1124 tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding);
1125
1126 // Must be not attached to any struct
1127 if(tmp_binding != NULL && tmp_binding->ref == 1) {
1128 linphone_upnp_port_binding_release(end_binding);
1129 end_binding = linphone_upnp_port_binding_retain(tmp_binding);
1130 } else {
1131 end_binding->external_port = external_port;
1132 }
1133 return end_binding;
1134 }
1135
linphone_upnp_port_binding_copy(const UpnpPortBinding * port)1136 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) {
1137 UpnpPortBinding *new_port = NULL;
1138 new_port = ms_new0(UpnpPortBinding,1);
1139 memcpy(new_port, port, sizeof(UpnpPortBinding));
1140 new_port->device_id = NULL;
1141 linphone_upnp_port_binding_set_device_id(new_port, port->device_id);
1142 ms_mutex_init(&new_port->mutex, NULL);
1143 new_port->ref = 1;
1144 return new_port;
1145 }
1146
linphone_upnp_port_binding_set_device_id(UpnpPortBinding * port,const char * device_id)1147 void linphone_upnp_port_binding_set_device_id(UpnpPortBinding *port, const char *device_id) {
1148 char *formated_device_id = linphone_upnp_format_device_id(device_id);
1149 if(formated_device_id != NULL && port->device_id != NULL) {
1150 if(strcmp(formated_device_id, port->device_id) == 0) {
1151 ms_free(formated_device_id);
1152 return;
1153 }
1154 }
1155 if(port->device_id != NULL) {
1156 ms_free(port->device_id);
1157 }
1158 port->device_id = formated_device_id;
1159 }
1160
linphone_upnp_port_binding_log(int level,const char * msg,const UpnpPortBinding * port)1161 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
1162 if(strlen(port->local_addr)) {
1163 ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d (retry %d)", msg,
1164 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
1165 port->external_port,
1166 port->local_addr,
1167 port->local_port,
1168 port->retry - 1);
1169 } else {
1170 ortp_log(level, "uPnP IGD: %s %s|%d->%d (retry %d)", msg,
1171 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
1172 port->external_port,
1173 port->local_port,
1174 port->retry - 1);
1175 }
1176 }
1177
1178 // Return true if the binding are equivalent. (Note external_port == -1 means "don't care")
linphone_upnp_port_binding_equal(const UpnpPortBinding * port1,const UpnpPortBinding * port2)1179 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
1180 return port1->protocol == port2->protocol &&
1181 port1->local_port == port2->local_port &&
1182 (port1->external_port == -1 || port2->external_port == -1 || port1->external_port == port2->external_port);
1183 }
1184
linphone_upnp_port_binding_equivalent_in_list(bctbx_list_t * list,const UpnpPortBinding * port)1185 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(bctbx_list_t *list, const UpnpPortBinding *port) {
1186 UpnpPortBinding *port_mapping;
1187 while(list != NULL) {
1188 port_mapping = (UpnpPortBinding *)list->data;
1189 if(linphone_upnp_port_binding_equal(port, port_mapping)) {
1190 return port_mapping;
1191 }
1192 list = list->next;
1193 }
1194
1195 return NULL;
1196 }
1197
linphone_upnp_port_binding_retain(UpnpPortBinding * port)1198 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port) {
1199 ms_mutex_lock(&port->mutex);
1200 port->ref++;
1201 ms_mutex_unlock(&port->mutex);
1202 return port;
1203 }
1204
linphone_upnp_port_binding_release(UpnpPortBinding * port)1205 void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
1206 ms_mutex_lock(&port->mutex);
1207 if(--port->ref == 0) {
1208 if(port->device_id != NULL) {
1209 ms_free(port->device_id);
1210 }
1211 ms_mutex_unlock(&port->mutex);
1212 ms_mutex_destroy(&port->mutex);
1213 ms_free(port);
1214 return;
1215 }
1216 ms_mutex_unlock(&port->mutex);
1217 }
1218
1219
1220 /*
1221 * uPnP Stream
1222 */
1223
linphone_upnp_stream_new(void)1224 UpnpStream* linphone_upnp_stream_new(void) {
1225 UpnpStream *stream = ms_new0(UpnpStream,1);
1226 stream->state = LinphoneUpnpStateIdle;
1227 stream->rtp = NULL;
1228 stream->rtcp = NULL;
1229 return stream;
1230 }
1231
linphone_upnp_stream_destroy(UpnpStream * stream)1232 void linphone_upnp_stream_destroy(UpnpStream* stream) {
1233 if(stream->rtp != NULL) {
1234 linphone_upnp_port_binding_release(stream->rtp);
1235 stream->rtp = NULL;
1236 }
1237 if(stream->rtcp != NULL) {
1238 linphone_upnp_port_binding_release(stream->rtcp);
1239 stream->rtcp = NULL;
1240 }
1241 ms_free(stream);
1242 }
1243
1244
1245 /*
1246 * uPnP Session
1247 */
1248
linphone_upnp_session_new(LinphoneCall * call)1249 UpnpSession* linphone_upnp_session_new(LinphoneCall* call) {
1250 UpnpSession *session = ms_new0(UpnpSession,1);
1251 session->call = call;
1252 session->state = LinphoneUpnpStateIdle;
1253 session->audio = linphone_upnp_stream_new();
1254 session->video = linphone_upnp_stream_new();
1255 return session;
1256 }
1257
linphone_upnp_session_destroy(UpnpSession * session)1258 void linphone_upnp_session_destroy(UpnpSession *session) {
1259 LinphoneCore *lc = session->call->core;
1260
1261 if(lc->upnp != NULL) {
1262 /* Remove bindings */
1263 if(session->audio->rtp != NULL) {
1264 linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp, TRUE);
1265 }
1266 if(session->audio->rtcp != NULL) {
1267 linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp, TRUE);
1268 }
1269 if(session->video->rtp != NULL) {
1270 linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp, TRUE);
1271 }
1272 if(session->video->rtcp != NULL) {
1273 linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE);
1274 }
1275 }
1276
1277 session->call->audio_stats->upnp_state = LinphoneUpnpStateKo;
1278 session->call->video_stats->upnp_state = LinphoneUpnpStateKo;
1279
1280 linphone_upnp_stream_destroy(session->audio);
1281 linphone_upnp_stream_destroy(session->video);
1282 ms_free(session);
1283 }
1284
linphone_upnp_session_get_state(UpnpSession * session)1285 LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) {
1286 return session->state;
1287 }
1288
1289
1290 /*
1291 * uPnP Config
1292 */
1293
1294 struct linphone_upnp_config_list_port_bindings_struct {
1295 struct _LpConfig *lpc;
1296 bctbx_list_t *retList;
1297 const char *device_id;
1298 };
1299
linphone_upnp_config_list_port_bindings_cb(const char * entry,struct linphone_upnp_config_list_port_bindings_struct * cookie)1300 static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct linphone_upnp_config_list_port_bindings_struct *cookie) {
1301 char device_id[UPNP_UUID_LEN + 1];
1302 char protocol_str[4]; // TCP or UDP
1303 upnp_igd_ip_protocol protocol;
1304 int external_port;
1305 int local_port;
1306 int ret;
1307 bool_t valid = TRUE;
1308 UpnpPortBinding *port;
1309
1310 ret = sscanf(entry, "%"UPNP_UUID_LEN_STR"[^-]-%3s-%i-%i", device_id, protocol_str, &external_port, &local_port);
1311 if(ret == 4) {
1312 // Handle only wanted device bindings
1313 if(device_id != NULL && strcmp(cookie->device_id, device_id) != 0) {
1314 return;
1315 }
1316 if(linphone_upnp_strncmpi(protocol_str, "TCP", 3) == 0) {
1317 protocol = UPNP_IGD_IP_PROTOCOL_TCP;
1318 } else if(linphone_upnp_strncmpi(protocol_str, "UDP", 3) == 0) {
1319 protocol = UPNP_IGD_IP_PROTOCOL_UDP;
1320 } else {
1321 valid = FALSE;
1322 }
1323 if(valid) {
1324 port = linphone_upnp_port_binding_new();
1325 linphone_upnp_port_binding_set_device_id(port, device_id);
1326 port->state = LinphoneUpnpStateOk;
1327 port->protocol = protocol;
1328 port->external_port = external_port;
1329 port->local_port = local_port;
1330 cookie->retList = bctbx_list_append(cookie->retList, port);
1331 }
1332 } else {
1333 valid = FALSE;
1334 }
1335 if(!valid) {
1336 ms_warning("uPnP configuration invalid line: %s", entry);
1337 }
1338 }
1339
linphone_upnp_config_list_port_bindings(struct _LpConfig * lpc,const char * device_id)1340 bctbx_list_t *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc, const char *device_id) {
1341 char *formated_device_id = linphone_upnp_format_device_id(device_id);
1342 struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL, formated_device_id};
1343 lp_config_for_each_entry(lpc, UPNP_SECTION_NAME, (void(*)(const char *, void*))linphone_upnp_config_list_port_bindings_cb, &cookie);
1344 ms_free(formated_device_id);
1345 return cookie.retList;
1346 }
1347
linphone_upnp_config_add_port_binding(UpnpContext * lupnp,const UpnpPortBinding * port)1348 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1349 bctbx_list_t *list;
1350 UpnpPortBinding *list_port;
1351
1352 if(port->device_id == NULL) {
1353 ms_error("Can't remove port binding without device_id");
1354 return;
1355 }
1356
1357 list = lupnp->removing_configs;
1358 while(list != NULL) {
1359 list_port = (UpnpPortBinding *)list->data;
1360 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1361 lupnp->removing_configs = bctbx_list_remove(lupnp->removing_configs, list_port);
1362 linphone_upnp_port_binding_release(list_port);
1363 return;
1364 }
1365 list = bctbx_list_next(list);
1366 }
1367
1368 list = lupnp->adding_configs;
1369 while(list != NULL) {
1370 list_port = (UpnpPortBinding *)list->data;
1371 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1372 return;
1373 }
1374 list = bctbx_list_next(list);
1375 }
1376
1377 list_port = linphone_upnp_port_binding_copy(port);
1378 lupnp->adding_configs = bctbx_list_append(lupnp->adding_configs, list_port);
1379 }
1380
linphone_upnp_config_remove_port_binding(UpnpContext * lupnp,const UpnpPortBinding * port)1381 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1382 bctbx_list_t *list;
1383 UpnpPortBinding *list_port;
1384
1385 if(port->device_id == NULL) {
1386 ms_error("Can't remove port binding without device_id");
1387 return;
1388 }
1389
1390 list = lupnp->adding_configs;
1391 while(list != NULL) {
1392 list_port = (UpnpPortBinding *)list->data;
1393 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1394 lupnp->adding_configs = bctbx_list_remove(lupnp->adding_configs, list_port);
1395 linphone_upnp_port_binding_release(list_port);
1396 return;
1397 }
1398 list = bctbx_list_next(list);
1399 }
1400
1401 list = lupnp->removing_configs;
1402 while(list != NULL) {
1403 list_port = (UpnpPortBinding *)list->data;
1404 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1405 return;
1406 }
1407 list = bctbx_list_next(list);
1408 }
1409
1410 list_port = linphone_upnp_port_binding_copy(port);
1411 lupnp->removing_configs = bctbx_list_append(lupnp->removing_configs, list_port);
1412 }
1413