1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 #include <pjmedia/transport_ice.h>
21 #include <pjnath/errno.h>
22 #include <pj/assert.h>
23 #include <pj/log.h>
24 #include <pj/pool.h>
25 #include <pj/rand.h>
26
27 #define THIS_FILE "transport_ice.c"
28 #if 0
29 # define TRACE__(expr) PJ_LOG(5,expr)
30 #else
31 # define TRACE__(expr)
32 #endif
33
34 enum oa_role
35 {
36 ROLE_NONE,
37 ROLE_OFFERER,
38 ROLE_ANSWERER
39 };
40
41 struct sdp_state
42 {
43 unsigned match_comp_cnt; /* Matching number of components */
44 pj_bool_t ice_mismatch; /* Address doesn't match candidates */
45 pj_bool_t ice_restart; /* Offer to restart ICE */
46 pj_ice_sess_role local_role; /* Our role */
47 pj_bool_t has_trickle; /* Has trickle ICE attribute */
48 };
49
50 /* ICE listener */
51 typedef struct ice_listener
52 {
53 PJ_DECL_LIST_MEMBER(struct ice_listener);
54 pjmedia_ice_cb cb;
55 void *user_data;
56 } ice_listener;
57
58 struct transport_ice
59 {
60 pjmedia_transport base;
61 pj_pool_t *pool;
62 unsigned options; /**< Transport options. */
63
64 unsigned comp_cnt;
65 pj_ice_strans *ice_st;
66
67 pjmedia_ice_cb cb;
68 ice_listener listener;
69 ice_listener listener_empty;
70 unsigned media_option;
71
72 pj_bool_t initial_sdp;
73 enum oa_role oa_role; /**< Last role in SDP offer/answer */
74 struct sdp_state rem_offer_state;/**< Describes the remote offer */
75
76 void *stream;
77 pj_sockaddr remote_rtp;
78 pj_sockaddr remote_rtcp;
79 unsigned addr_len; /**< Length of addresses. */
80
81 pj_bool_t use_ice;
82 pj_sockaddr rtp_src_addr; /**< Actual source RTP address. */
83 unsigned rtp_src_cnt; /**< How many pkt from this addr. */
84 pj_sockaddr rtcp_src_addr; /**< Actual source RTCP address. */
85 unsigned rtcp_src_cnt; /**< How many pkt from this addr. */
86 pj_bool_t enable_rtcp_mux;/**< Enable RTP& RTCP multiplexing?*/
87 pj_bool_t use_rtcp_mux; /**< Use RTP & RTCP multiplexing? */
88
89 unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */
90 unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */
91
92 pj_ice_sess_trickle trickle_ice; /**< Trickle ICE mode. */
93 unsigned last_send_cand_cnt[PJ_ICE_MAX_COMP];
94 /**< Last local candidate count. */
95 pj_bool_t end_of_cand; /**< Local cand gathering done? */
96 pj_str_t sdp_mid; /**< SDP "a=mid" attribute. */
97
98 void (*rtp_cb)(void*,
99 void*,
100 pj_ssize_t);
101 void (*rtp_cb2)(pjmedia_tp_cb_param*);
102 void (*rtcp_cb)(void*,
103 void*,
104 pj_ssize_t);
105 };
106
107
108 /*
109 * These are media transport operations.
110 */
111 static pj_status_t transport_get_info (pjmedia_transport *tp,
112 pjmedia_transport_info *info);
113 static pj_status_t transport_attach (pjmedia_transport *tp,
114 void *user_data,
115 const pj_sockaddr_t *rem_addr,
116 const pj_sockaddr_t *rem_rtcp,
117 unsigned addr_len,
118 void (*rtp_cb)(void*,
119 void*,
120 pj_ssize_t),
121 void (*rtcp_cb)(void*,
122 void*,
123 pj_ssize_t));
124 static pj_status_t transport_attach2 (pjmedia_transport *tp,
125 pjmedia_transport_attach_param
126 *att_param);
127 static void transport_detach (pjmedia_transport *tp,
128 void *strm);
129 static pj_status_t transport_send_rtp( pjmedia_transport *tp,
130 const void *pkt,
131 pj_size_t size);
132 static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
133 const void *pkt,
134 pj_size_t size);
135 static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
136 const pj_sockaddr_t *addr,
137 unsigned addr_len,
138 const void *pkt,
139 pj_size_t size);
140 static pj_status_t transport_media_create(pjmedia_transport *tp,
141 pj_pool_t *pool,
142 unsigned options,
143 const pjmedia_sdp_session *rem_sdp,
144 unsigned media_index);
145 static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
146 pj_pool_t *tmp_pool,
147 pjmedia_sdp_session *sdp_local,
148 const pjmedia_sdp_session *rem_sdp,
149 unsigned media_index);
150 static pj_status_t transport_media_start(pjmedia_transport *tp,
151 pj_pool_t *pool,
152 const pjmedia_sdp_session *sdp_local,
153 const pjmedia_sdp_session *rem_sdp,
154 unsigned media_index);
155 static pj_status_t transport_media_stop(pjmedia_transport *tp);
156 static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
157 pjmedia_dir dir,
158 unsigned pct_lost);
159 static pj_status_t transport_destroy (pjmedia_transport *tp);
160
161 /*
162 * And these are ICE callbacks.
163 */
164 static void ice_on_rx_data(pj_ice_strans *ice_st,
165 unsigned comp_id,
166 void *pkt, pj_size_t size,
167 const pj_sockaddr_t *src_addr,
168 unsigned src_addr_len);
169 static void ice_on_ice_complete(pj_ice_strans *ice_st,
170 pj_ice_strans_op op,
171 pj_status_t status);
172 static void ice_on_new_candidate(pj_ice_strans *ice_st,
173 const pj_ice_sess_cand *cand,
174 pj_bool_t last);
175
176 /*
177 * Clean up ICE resources.
178 */
179 static void tp_ice_on_destroy(void *arg);
180
181
182 static pjmedia_transport_op transport_ice_op =
183 {
184 &transport_get_info,
185 &transport_attach,
186 &transport_detach,
187 &transport_send_rtp,
188 &transport_send_rtcp,
189 &transport_send_rtcp2,
190 &transport_media_create,
191 &transport_encode_sdp,
192 &transport_media_start,
193 &transport_media_stop,
194 &transport_simulate_lost,
195 &transport_destroy,
196 &transport_attach2
197 };
198
199 static const pj_str_t STR_CANDIDATE = { "candidate", 9};
200 static const pj_str_t STR_REM_CAND = { "remote-candidates", 17 };
201 static const pj_str_t STR_ICE_LITE = { "ice-lite", 8};
202 static const pj_str_t STR_ICE_MISMATCH = { "ice-mismatch", 12};
203 static const pj_str_t STR_ICE_UFRAG = { "ice-ufrag", 9 };
204 static const pj_str_t STR_ICE_PWD = { "ice-pwd", 7 };
205 static const pj_str_t STR_IP4 = { "IP4", 3 };
206 static const pj_str_t STR_IP6 = { "IP6", 3 };
207 static const pj_str_t STR_RTCP = { "rtcp", 4 };
208 static const pj_str_t STR_RTCP_MUX = { "rtcp-mux", 8 };
209 static const pj_str_t STR_BANDW_RR = { "RR", 2 };
210 static const pj_str_t STR_BANDW_RS = { "RS", 2 };
211 static const pj_str_t STR_ICE_OPTIONS = { "ice-options", 11 };
212 static const pj_str_t STR_TRICKLE = { "trickle", 7 };
213 static const pj_str_t STR_END_OF_CAND = { "end-of-candidates", 17 };
214
215 enum {
216 COMP_RTP = 1,
217 COMP_RTCP = 2
218 };
219
220
221 /* Forward declaration of internal functions */
222
223 static int print_sdp_cand_attr(char *buffer, int max_len,
224 const pj_ice_sess_cand *cand);
225 static void get_ice_attr(const pjmedia_sdp_session *rem_sdp,
226 const pjmedia_sdp_media *rem_m,
227 const pjmedia_sdp_attr **p_ice_ufrag,
228 const pjmedia_sdp_attr **p_ice_pwd);
229 static pj_status_t parse_cand(const char *obj_name,
230 pj_pool_t *pool,
231 const pj_str_t *orig_input,
232 pj_ice_sess_cand *cand);
233
234
235 /*
236 * Create ICE media transport.
237 */
pjmedia_ice_create(pjmedia_endpt * endpt,const char * name,unsigned comp_cnt,const pj_ice_strans_cfg * cfg,const pjmedia_ice_cb * cb,pjmedia_transport ** p_tp)238 PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
239 const char *name,
240 unsigned comp_cnt,
241 const pj_ice_strans_cfg *cfg,
242 const pjmedia_ice_cb *cb,
243 pjmedia_transport **p_tp)
244 {
245 return pjmedia_ice_create2(endpt, name, comp_cnt, cfg, cb, 0, p_tp);
246 }
247
248 /*
249 * Create ICE media transport.
250 */
pjmedia_ice_create2(pjmedia_endpt * endpt,const char * name,unsigned comp_cnt,const pj_ice_strans_cfg * cfg,const pjmedia_ice_cb * cb,unsigned options,pjmedia_transport ** p_tp)251 PJ_DEF(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt,
252 const char *name,
253 unsigned comp_cnt,
254 const pj_ice_strans_cfg *cfg,
255 const pjmedia_ice_cb *cb,
256 unsigned options,
257 pjmedia_transport **p_tp)
258 {
259 return pjmedia_ice_create3(endpt, name, comp_cnt, cfg, cb,
260 options, NULL, p_tp);
261 }
262
263 /*
264 * Create ICE media transport.
265 */
pjmedia_ice_create3(pjmedia_endpt * endpt,const char * name,unsigned comp_cnt,const pj_ice_strans_cfg * cfg,const pjmedia_ice_cb * cb,unsigned options,void * user_data,pjmedia_transport ** p_tp)266 PJ_DEF(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt,
267 const char *name,
268 unsigned comp_cnt,
269 const pj_ice_strans_cfg *cfg,
270 const pjmedia_ice_cb *cb,
271 unsigned options,
272 void *user_data,
273 pjmedia_transport **p_tp)
274 {
275 pj_pool_t *pool;
276 pj_ice_strans_cb ice_st_cb;
277 pj_ice_strans_cfg ice_st_cfg;
278 struct transport_ice *tp_ice;
279 pj_status_t status;
280
281 PJ_ASSERT_RETURN(endpt && comp_cnt && cfg && p_tp, PJ_EINVAL);
282
283 /* Create transport instance */
284 pool = pjmedia_endpt_create_pool(endpt, name, 512, 512);
285 tp_ice = PJ_POOL_ZALLOC_T(pool, struct transport_ice);
286 tp_ice->pool = pool;
287 tp_ice->options = options;
288 tp_ice->comp_cnt = comp_cnt;
289 pj_ansi_strcpy(tp_ice->base.name, pool->obj_name);
290 tp_ice->base.op = &transport_ice_op;
291 tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE;
292 tp_ice->base.user_data = user_data;
293 tp_ice->initial_sdp = PJ_TRUE;
294 tp_ice->oa_role = ROLE_NONE;
295 tp_ice->use_ice = PJ_FALSE;
296 tp_ice->trickle_ice = cfg->opt.trickle;
297 pj_list_init(&tp_ice->listener);
298 pj_list_init(&tp_ice->listener_empty);
299
300 pj_memcpy(&ice_st_cfg, cfg, sizeof(pj_ice_strans_cfg));
301 if (cb)
302 pj_memcpy(&tp_ice->cb, cb, sizeof(pjmedia_ice_cb));
303
304 /* Assign return value first because ICE might call callback
305 * in create()
306 */
307 *p_tp = &tp_ice->base;
308
309 /* Configure ICE callbacks */
310 pj_bzero(&ice_st_cb, sizeof(ice_st_cb));
311 ice_st_cb.on_ice_complete = &ice_on_ice_complete;
312 ice_st_cb.on_rx_data = &ice_on_rx_data;
313 ice_st_cb.on_new_candidate = &ice_on_new_candidate;
314
315 /* Configure RTP socket buffer settings, if not set */
316 if (ice_st_cfg.comp[COMP_RTP-1].so_rcvbuf_size == 0) {
317 ice_st_cfg.comp[COMP_RTP-1].so_rcvbuf_size =
318 PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE;
319 }
320 if (ice_st_cfg.comp[COMP_RTP-1].so_sndbuf_size == 0) {
321 ice_st_cfg.comp[COMP_RTP-1].so_sndbuf_size =
322 PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE;
323 }
324 if (ice_st_cfg.send_buf_size == 0)
325 ice_st_cfg.send_buf_size = PJMEDIA_MAX_MTU;
326
327 /* Create ICE */
328 status = pj_ice_strans_create(name, &ice_st_cfg, comp_cnt, tp_ice,
329 &ice_st_cb, &tp_ice->ice_st);
330 if (status != PJ_SUCCESS) {
331 pj_pool_release(pool);
332 *p_tp = NULL;
333 return status;
334 }
335
336 /* Sync to ICE */
337 {
338 pj_grp_lock_t *grp_lock = pj_ice_strans_get_grp_lock(tp_ice->ice_st);
339 pj_grp_lock_add_ref(grp_lock);
340 pj_grp_lock_add_handler(grp_lock, pool, tp_ice, &tp_ice_on_destroy);
341 }
342
343 /* Done */
344 return PJ_SUCCESS;
345 }
346
pjmedia_ice_get_grp_lock(pjmedia_transport * tp)347 PJ_DEF(pj_grp_lock_t *) pjmedia_ice_get_grp_lock(pjmedia_transport *tp)
348 {
349 PJ_ASSERT_RETURN(tp, NULL);
350 return pj_ice_strans_get_grp_lock(((struct transport_ice *)tp)->ice_st);
351 }
352
353
354 /*
355 * Add application to receive ICE notifications from the specified ICE media
356 * transport.
357 */
pjmedia_ice_add_ice_cb(pjmedia_transport * tp,const pjmedia_ice_cb * cb,void * user_data)358 PJ_DEF(pj_status_t) pjmedia_ice_add_ice_cb( pjmedia_transport *tp,
359 const pjmedia_ice_cb *cb,
360 void *user_data)
361 {
362 struct transport_ice *tp_ice = (struct transport_ice*)tp;
363 ice_listener *il;
364 pj_grp_lock_t *grp_lock;
365
366 PJ_ASSERT_RETURN(tp && cb, PJ_EINVAL);
367 grp_lock = pjmedia_ice_get_grp_lock(tp);
368 PJ_ASSERT_RETURN(grp_lock, PJ_EINVAL);
369
370 pj_grp_lock_acquire(grp_lock);
371
372 if (!pj_list_empty(&tp_ice->listener_empty)) {
373 il = tp_ice->listener_empty.next;
374 pj_list_erase(il);
375 il->cb = *cb;
376 il->user_data = user_data;
377 pj_list_push_back(&tp_ice->listener, il);
378 } else {
379 il = PJ_POOL_ZALLOC_T(tp_ice->pool, ice_listener);
380 pj_list_init(il);
381 il->cb = *cb;
382 il->user_data = user_data;
383 pj_list_push_back(&tp_ice->listener, il);
384 }
385
386 pj_grp_lock_release(grp_lock);
387
388 return PJ_SUCCESS;
389 }
390
391
392 /*
393 * Remove application to stop receiving ICE notifications the specified
394 * ICE media transport.
395 */
pjmedia_ice_remove_ice_cb(pjmedia_transport * tp,const pjmedia_ice_cb * cb,void * user_data)396 PJ_DEF(pj_status_t) pjmedia_ice_remove_ice_cb( pjmedia_transport *tp,
397 const pjmedia_ice_cb *cb,
398 void *user_data)
399 {
400 struct transport_ice *tp_ice = (struct transport_ice*)tp;
401 ice_listener *il;
402 pj_grp_lock_t *grp_lock;
403
404 PJ_ASSERT_RETURN(tp && cb, PJ_EINVAL);
405 grp_lock = pjmedia_ice_get_grp_lock(tp);
406 PJ_ASSERT_RETURN(grp_lock, PJ_EINVAL);
407
408 pj_grp_lock_acquire(grp_lock);
409
410 for (il=tp_ice->listener.next; il!=&tp_ice->listener; il=il->next) {
411 if (pj_memcmp(&il->cb, cb, sizeof(cb))==0 && il->user_data==user_data)
412 break;
413 }
414 if (il != &tp_ice->listener) {
415 pj_list_erase(il);
416 pj_list_push_back(&tp_ice->listener_empty, il);
417 }
418
419 pj_grp_lock_release(grp_lock);
420
421 return (il != &tp_ice->listener? PJ_SUCCESS : PJ_ENOTFOUND);
422 }
423
424
425 /* Check if trickle support is signalled in the specified SDP. */
pjmedia_ice_sdp_has_trickle(const pjmedia_sdp_session * sdp,unsigned med_idx)426 PJ_DEF(pj_bool_t) pjmedia_ice_sdp_has_trickle( const pjmedia_sdp_session *sdp,
427 unsigned med_idx)
428 {
429 const pjmedia_sdp_media *m;
430 const pjmedia_sdp_attr *a;
431
432 PJ_ASSERT_RETURN(sdp && med_idx < sdp->media_count, PJ_EINVAL);
433
434 /* Find in media level */
435 m = sdp->media[med_idx];
436 a = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_ICE_OPTIONS, NULL);
437 if (a && pj_strstr(&a->value, &STR_TRICKLE))
438 return PJ_TRUE;
439
440 /* Find in session level */
441 a = pjmedia_sdp_attr_find(sdp->attr_count, sdp->attr, &STR_ICE_OPTIONS,
442 NULL);
443 if (a && pj_strstr(&a->value, &STR_TRICKLE))
444 return PJ_TRUE;
445
446 return PJ_FALSE;
447 }
448
449
450 /* Update check list after discovering and conveying new local ICE candidate,
451 * or receiving update of remote ICE candidates in trickle ICE.
452 */
pjmedia_ice_trickle_update(pjmedia_transport * tp,const pj_str_t * rem_ufrag,const pj_str_t * rem_passwd,unsigned rcand_cnt,const pj_ice_sess_cand rcand[],pj_bool_t rcand_end)453 PJ_DEF(pj_status_t) pjmedia_ice_trickle_update(
454 pjmedia_transport *tp,
455 const pj_str_t *rem_ufrag,
456 const pj_str_t *rem_passwd,
457 unsigned rcand_cnt,
458 const pj_ice_sess_cand rcand[],
459 pj_bool_t rcand_end)
460 {
461 struct transport_ice *tp_ice = (struct transport_ice*)tp;
462 pj_status_t status;
463
464 PJ_ASSERT_RETURN(tp_ice && tp_ice->ice_st, PJ_EINVAL);
465 PJ_ASSERT_RETURN(tp_ice->trickle_ice != PJ_ICE_SESS_TRICKLE_DISABLED,
466 PJ_EINVALIDOP);
467
468
469 /* Update the checklist */
470 status = pj_ice_strans_update_check_list(tp_ice->ice_st,
471 rem_ufrag, rem_passwd,
472 rcand_cnt, rcand, rcand_end);
473 if (status != PJ_SUCCESS)
474 return status;
475
476 /* Start ICE if both sides have sent their (initial) SDPs */
477 if (!pj_ice_strans_sess_is_running(tp_ice->ice_st)) {
478 unsigned i, comp_cnt;
479
480 comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
481 for (i = 0; i < comp_cnt; ++i) {
482 if (tp_ice->last_send_cand_cnt[i] > 0)
483 break;
484 }
485 if (i != comp_cnt) {
486 pj_str_t rufrag;
487 pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, NULL, NULL,
488 &rufrag, NULL);
489 if (rufrag.slen > 0) {
490 PJ_LOG(3,(THIS_FILE,"Trickle ICE starts connectivity check"));
491 status = pj_ice_strans_start_ice(tp_ice->ice_st, NULL, NULL,
492 0, NULL);
493 }
494 }
495 }
496
497 return status;
498 }
499
500
501 /* Fetch trickle ICE info from the specified SDP. */
pjmedia_ice_trickle_decode_sdp(const pjmedia_sdp_session * sdp,unsigned media_index,pj_str_t * mid,pj_str_t * ufrag,pj_str_t * passwd,unsigned * cand_cnt,pj_ice_sess_cand cand[],pj_bool_t * end_of_cand)502 PJ_DEF(pj_status_t) pjmedia_ice_trickle_decode_sdp(
503 const pjmedia_sdp_session *sdp,
504 unsigned media_index,
505 pj_str_t *mid,
506 pj_str_t *ufrag,
507 pj_str_t *passwd,
508 unsigned *cand_cnt,
509 pj_ice_sess_cand cand[],
510 pj_bool_t *end_of_cand)
511 {
512 const pjmedia_sdp_media *m;
513 const pjmedia_sdp_attr *a;
514
515 PJ_ASSERT_RETURN(sdp && media_index < sdp->media_count, PJ_EINVAL);
516
517 m = sdp->media[media_index];
518
519 if (mid) {
520 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "mid", NULL);
521 if (a) {
522 *mid = a->value;
523 } else {
524 pj_bzero(mid, sizeof(*mid));
525 }
526 }
527
528 if (ufrag && passwd) {
529 const pjmedia_sdp_attr *a_ufrag, *a_pwd;
530 get_ice_attr(sdp, m, &a_ufrag, &a_pwd);
531 if (a_ufrag && a_pwd) {
532 *ufrag = a_ufrag->value;
533 *passwd = a_pwd->value;
534 } else {
535 pj_bzero(ufrag, sizeof(*ufrag));
536 pj_bzero(passwd, sizeof(*passwd));
537 }
538 }
539
540 if (cand_cnt && cand && *cand_cnt > 0) {
541 pj_status_t status;
542 unsigned i, cnt = 0;
543
544 for (i=0; i<m->attr_count && cnt<*cand_cnt; ++i) {
545 a = m->attr[i];
546 if (pj_strcmp(&a->name, &STR_CANDIDATE)!=0)
547 continue;
548
549 /* Parse candidate */
550 status = parse_cand("trickle-ice", NULL, &a->value, &cand[cnt]);
551 if (status != PJ_SUCCESS) {
552 PJ_PERROR(4,("trickle-ice", status,
553 "Error in parsing SDP candidate attribute '%.*s', "
554 "candidate is ignored",
555 (int)a->value.slen, a->value.ptr));
556 continue;
557 }
558 ++cnt;
559 }
560 *cand_cnt = cnt;
561 }
562
563 if (end_of_cand) {
564 a = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_END_OF_CAND,
565 NULL);
566 if (!a) {
567 a = pjmedia_sdp_attr_find(sdp->attr_count, sdp->attr,
568 &STR_END_OF_CAND, NULL);
569 }
570 *end_of_cand = (a != NULL);
571 }
572 return PJ_SUCCESS;
573 }
574
575
576 /* Generate SDP attributes for trickle ICE in the specified SDP. */
pjmedia_ice_trickle_encode_sdp(pj_pool_t * sdp_pool,pjmedia_sdp_session * sdp,const pj_str_t * mid,const pj_str_t * ufrag,const pj_str_t * passwd,unsigned cand_cnt,const pj_ice_sess_cand cand[],pj_bool_t end_of_cand)577 PJ_DEF(pj_status_t) pjmedia_ice_trickle_encode_sdp(
578 pj_pool_t *sdp_pool,
579 pjmedia_sdp_session *sdp,
580 const pj_str_t *mid,
581 const pj_str_t *ufrag,
582 const pj_str_t *passwd,
583 unsigned cand_cnt,
584 const pj_ice_sess_cand cand[],
585 pj_bool_t end_of_cand)
586 {
587 pjmedia_sdp_media *m = NULL;
588 pjmedia_sdp_attr *a;
589 unsigned i;
590
591 PJ_ASSERT_RETURN(sdp_pool && sdp, PJ_EINVAL);
592
593 /* Find media by checking "a=mid"*/
594 for (i = 0; i < sdp->media_count; ++i) {
595 m = sdp->media[i];
596 a = pjmedia_sdp_media_find_attr2(m, "mid", NULL);
597 if (a && pj_strcmp(&a->value, mid)==0)
598 break;
599 }
600
601 /* Media not exist, try to add it */
602 if (i == sdp->media_count) {
603 if (sdp->media_count >= PJMEDIA_MAX_SDP_MEDIA) {
604 PJ_LOG(3,(THIS_FILE,"Trickle ICE failed to encode candidates, "
605 "the specified SDP has too many media"));
606 return PJ_ETOOMANY;
607 }
608
609 /* Add a new media to the SDP */
610 m = PJ_POOL_ZALLOC_T(sdp_pool, pjmedia_sdp_media);
611 m->desc.media = pj_str("audio");
612 m->desc.fmt_count = 1;
613 m->desc.fmt[0] = pj_str("0");
614 m->desc.transport = pj_str("RTP/AVP");
615 sdp->media[sdp->media_count++] = m;
616
617 /* Add media ID attribute "a=mid" */
618 a = pjmedia_sdp_attr_create(sdp_pool, "mid", mid);
619 pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
620 }
621
622 /* Add "a=ice-options:trickle" in session level */
623 a = pjmedia_sdp_attr_find(sdp->attr_count, sdp->attr, &STR_ICE_OPTIONS,
624 NULL);
625 if (!a || !pj_strstr(&a->value, &STR_TRICKLE)) {
626 a = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_OPTIONS.ptr,
627 &STR_TRICKLE);
628 pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
629 }
630
631 /* Add "a=ice-options:trickle" in media level */
632 /*
633 a = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_ICE_OPTIONS,
634 NULL);
635 if (!a || !pj_strstr(&a->value, &STR_TRICKLE)) {
636 a = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_OPTIONS.ptr,
637 &STR_TRICKLE);
638 pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
639 }
640 */
641
642 /* Add ice-ufrag & ice-pwd attributes */
643 if (ufrag && passwd &&
644 !pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_ICE_UFRAG, NULL))
645 {
646 a = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, ufrag);
647 pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
648
649 a = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, passwd);
650 pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
651 }
652
653 /* Add candidates */
654 for (i=0; i<cand_cnt; ++i) {
655 enum {
656 ATTR_BUF_LEN = 160, /* Max len of a=candidate attr */
657 RATTR_BUF_LEN= 160 /* Max len of a=remote-candidates attr */
658 };
659 char attr_buf[ATTR_BUF_LEN];
660 pj_str_t value;
661
662 value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN, &cand[i]);
663 if (value.slen < 0) {
664 pj_assert(!"Not enough attr_buf to print candidate");
665 return PJ_EBUG;
666 }
667
668 value.ptr = attr_buf;
669 a = pjmedia_sdp_attr_create(sdp_pool, STR_CANDIDATE.ptr, &value);
670 pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
671 }
672
673 /* Add "a=end-of-candidates" */
674 if (end_of_cand) {
675 a = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_END_OF_CAND,
676 NULL);
677 if (!a) {
678 a = pjmedia_sdp_attr_create(sdp_pool, STR_END_OF_CAND.ptr, NULL);
679 pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
680 }
681 }
682
683 return PJ_SUCCESS;
684 }
685
686
pjmedia_ice_trickle_has_new_cand(pjmedia_transport * tp)687 PJ_DEF(pj_bool_t) pjmedia_ice_trickle_has_new_cand(pjmedia_transport *tp)
688 {
689 struct transport_ice *tp_ice = (struct transport_ice*)tp;
690 unsigned i, comp_cnt;
691
692 /* Make sure ICE transport has session already */
693 if (!tp_ice->ice_st || !pj_ice_strans_has_sess(tp_ice->ice_st))
694 return PJ_FALSE;
695
696 /* Count all local candidates */
697 comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
698 for (i = 0; i < comp_cnt; ++i) {
699 if (tp_ice->last_send_cand_cnt[i] <
700 pj_ice_strans_get_cands_count(tp_ice->ice_st, i+1))
701 {
702 return PJ_TRUE;
703 }
704 }
705 return PJ_FALSE;
706 }
707
708
709 /* Add any new local candidates to the specified SDP to be conveyed to
710 * remote (e.g: via SIP INFO).
711 */
pjmedia_ice_trickle_send_local_cand(pjmedia_transport * tp,pj_pool_t * sdp_pool,pjmedia_sdp_session * sdp,pj_bool_t * p_end_of_cand)712 PJ_DEF(pj_status_t) pjmedia_ice_trickle_send_local_cand(
713 pjmedia_transport *tp,
714 pj_pool_t *sdp_pool,
715 pjmedia_sdp_session *sdp,
716 pj_bool_t *p_end_of_cand)
717 {
718 struct transport_ice *tp_ice = (struct transport_ice*)tp;
719 pj_str_t ufrag, pwd;
720 pj_ice_sess_cand cand[PJ_ICE_MAX_CAND];
721 unsigned cand_cnt, i, comp_cnt;
722 pj_bool_t end_of_cand;
723 pj_status_t status;
724
725 PJ_ASSERT_RETURN(tp && sdp_pool && sdp, PJ_EINVAL);
726
727 /* Make sure ICE transport has session already */
728 if (!tp_ice->ice_st || !pj_ice_strans_has_sess(tp_ice->ice_st))
729 return PJ_EINVALIDOP;
730
731 end_of_cand = tp_ice->end_of_cand;
732
733 /* Get ufrag and pwd from current session */
734 pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, &ufrag, &pwd, NULL, NULL);
735
736 cand_cnt = 0;
737 comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
738 for (i = 0; i < comp_cnt; ++i) {
739 unsigned cnt = PJ_ICE_MAX_CAND - cand_cnt;
740
741 /* Get all local candidates for this comp */
742 status = pj_ice_strans_enum_cands(tp_ice->ice_st, i+1,
743 &cnt, &cand[cand_cnt]);
744 if (status != PJ_SUCCESS) {
745 PJ_PERROR(3,(tp_ice->base.name, status,
746 "Failed enumerating local candidates for comp-id=%d",
747 i+1));
748 continue;
749 }
750 cand_cnt += cnt;
751
752 tp_ice->last_send_cand_cnt[i] = cnt;
753 }
754
755 /* Update the SDP with all local candidates (not just the new ones).
756 * https://tools.ietf.org/html/draft-ietf-mmusic-trickle-ice-sip-18:
757 * 4.4. Delivering Candidates in INFO Requests: the agent MUST
758 * repeat in the INFO request body all candidates that were previously
759 * sent under the same combination of "a=ice-pwd:" and "a=ice-ufrag:"
760 * in the same order as they were sent before.
761 */
762 status = pjmedia_ice_trickle_encode_sdp(sdp_pool, sdp, &tp_ice->sdp_mid,
763 &ufrag, &pwd, cand_cnt, cand,
764 end_of_cand);
765 if (status != PJ_SUCCESS) {
766 PJ_PERROR(3,(tp_ice->base.name, status,
767 "Failed encoding local candidates to SDP"));
768 }
769
770 if (p_end_of_cand)
771 *p_end_of_cand = end_of_cand;
772
773 return PJ_SUCCESS;
774 }
775
776
777 /* Disable ICE when SDP from remote doesn't contain a=candidate line */
set_no_ice(struct transport_ice * tp_ice,const char * reason,pj_status_t err)778 static void set_no_ice(struct transport_ice *tp_ice, const char *reason,
779 pj_status_t err)
780 {
781 if (err != PJ_SUCCESS) {
782 PJ_PERROR(4,(tp_ice->base.name, err,
783 "Stopping ICE, reason=%s", reason));
784 } else {
785 PJ_LOG(4,(tp_ice->base.name,
786 "Stopping ICE, reason=%s", reason));
787 }
788
789 if (tp_ice->ice_st) {
790 pj_ice_strans_stop_ice(tp_ice->ice_st);
791 }
792
793 tp_ice->use_ice = PJ_FALSE;
794 }
795
796
797 /* Create SDP candidate attribute */
print_sdp_cand_attr(char * buffer,int max_len,const pj_ice_sess_cand * cand)798 static int print_sdp_cand_attr(char *buffer, int max_len,
799 const pj_ice_sess_cand *cand)
800 {
801 char ipaddr[PJ_INET6_ADDRSTRLEN+2];
802 int len, len2;
803
804 len = pj_ansi_snprintf( buffer, max_len,
805 "%.*s %u UDP %u %s %u typ ",
806 (int)cand->foundation.slen,
807 cand->foundation.ptr,
808 (unsigned)cand->comp_id,
809 cand->prio,
810 pj_sockaddr_print(&cand->addr, ipaddr,
811 sizeof(ipaddr), 0),
812 (unsigned)pj_sockaddr_get_port(&cand->addr));
813 if (len < 1 || len >= max_len)
814 return -1;
815
816 switch (cand->type) {
817 case PJ_ICE_CAND_TYPE_HOST:
818 len2 = pj_ansi_snprintf(buffer+len, max_len-len, "host");
819 break;
820 case PJ_ICE_CAND_TYPE_SRFLX:
821 case PJ_ICE_CAND_TYPE_RELAYED:
822 case PJ_ICE_CAND_TYPE_PRFLX:
823 len2 = pj_ansi_snprintf(buffer+len, max_len-len,
824 "%s raddr %s rport %d",
825 pj_ice_get_cand_type_name(cand->type),
826 pj_sockaddr_print(&cand->rel_addr, ipaddr,
827 sizeof(ipaddr), 0),
828 (int)pj_sockaddr_get_port(&cand->rel_addr));
829 break;
830 default:
831 pj_assert(!"Invalid candidate type");
832 len2 = -1;
833 break;
834 }
835 if (len2 < 1 || len2 >= max_len-len)
836 return -1;
837
838 return len+len2;
839 }
840
841
842 /* Get ice-ufrag and ice-pwd attribute */
get_ice_attr(const pjmedia_sdp_session * rem_sdp,const pjmedia_sdp_media * rem_m,const pjmedia_sdp_attr ** p_ice_ufrag,const pjmedia_sdp_attr ** p_ice_pwd)843 static void get_ice_attr(const pjmedia_sdp_session *rem_sdp,
844 const pjmedia_sdp_media *rem_m,
845 const pjmedia_sdp_attr **p_ice_ufrag,
846 const pjmedia_sdp_attr **p_ice_pwd)
847 {
848 pjmedia_sdp_attr *attr;
849
850 /* Find ice-ufrag attribute in media descriptor */
851 attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr,
852 &STR_ICE_UFRAG, NULL);
853 if (attr == NULL) {
854 /* Find ice-ufrag attribute in session descriptor */
855 attr = pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr,
856 &STR_ICE_UFRAG, NULL);
857 }
858 *p_ice_ufrag = attr;
859
860 /* Find ice-pwd attribute in media descriptor */
861 attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr,
862 &STR_ICE_PWD, NULL);
863 if (attr == NULL) {
864 /* Find ice-pwd attribute in session descriptor */
865 attr = pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr,
866 &STR_ICE_PWD, NULL);
867 }
868 *p_ice_pwd = attr;
869 }
870
871
872 /* Encode and add "a=ice-mismatch" attribute in the SDP */
encode_ice_mismatch(pj_pool_t * sdp_pool,pjmedia_sdp_session * sdp_local,unsigned media_index)873 static void encode_ice_mismatch(pj_pool_t *sdp_pool,
874 pjmedia_sdp_session *sdp_local,
875 unsigned media_index)
876 {
877 pjmedia_sdp_attr *attr;
878 pjmedia_sdp_media *m = sdp_local->media[media_index];
879
880 attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr);
881 attr->name = STR_ICE_MISMATCH;
882 attr->value.slen = 0;
883 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
884 }
885
886
887 /* Encode ICE information in SDP */
encode_session_in_sdp(struct transport_ice * tp_ice,pj_pool_t * sdp_pool,pjmedia_sdp_session * sdp_local,unsigned media_index,unsigned comp_cnt,pj_bool_t restart_session,pj_bool_t rtcp_mux,pj_bool_t trickle)888 static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice,
889 pj_pool_t *sdp_pool,
890 pjmedia_sdp_session *sdp_local,
891 unsigned media_index,
892 unsigned comp_cnt,
893 pj_bool_t restart_session,
894 pj_bool_t rtcp_mux,
895 pj_bool_t trickle)
896 {
897 enum {
898 ATTR_BUF_LEN = 160, /* Max len of a=candidate attr */
899 RATTR_BUF_LEN= 160 /* Max len of a=remote-candidates attr */
900 };
901 pjmedia_sdp_media *m = sdp_local->media[media_index];
902 pj_str_t local_ufrag, local_pwd;
903 pjmedia_sdp_attr *attr;
904 pj_status_t status;
905
906 /* Must have a session */
907 PJ_ASSERT_RETURN(pj_ice_strans_has_sess(tp_ice->ice_st), PJ_EBUG);
908
909 /* Get ufrag and pwd from current session */
910 pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, &local_ufrag, &local_pwd,
911 NULL, NULL);
912
913 /* The listing of candidates depends on whether ICE has completed
914 * or not. When ICE has completed:
915 *
916 * 9.1.2.2: Existing Media Streams with ICE Completed
917 * The agent MUST include a candidate attributes for candidates
918 * matching the default destination for each component of the
919 * media stream, and MUST NOT include any other candidates.
920 *
921 * When ICE has not completed, we shall include all candidates.
922 *
923 * Except when we have detected that remote is offering to restart
924 * the session, in this case we will answer with full ICE SDP and
925 * new ufrag/pwd pair.
926 */
927 if (!restart_session && pj_ice_strans_sess_is_complete(tp_ice->ice_st) &&
928 pj_ice_strans_get_state(tp_ice->ice_st) != PJ_ICE_STRANS_STATE_FAILED)
929 {
930 const pj_ice_sess_check *check;
931 char *attr_buf;
932 pjmedia_sdp_conn *conn;
933 pjmedia_sdp_attr *a_rtcp;
934 pj_str_t rem_cand;
935 unsigned comp;
936
937 /* Encode ice-ufrag attribute */
938 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr,
939 &local_ufrag);
940 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
941
942 /* Encode ice-pwd attribute */
943 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr,
944 &local_pwd);
945 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
946
947 /* Prepare buffer */
948 attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN);
949 rem_cand.ptr = (char*) pj_pool_alloc(sdp_pool, RATTR_BUF_LEN);
950 rem_cand.slen = 0;
951
952 /* 9.1.2.2: Existing Media Streams with ICE Completed
953 * The default destination for media (i.e., the values of
954 * the IP addresses and ports in the m and c line used for
955 * that media stream) MUST be the local candidate from the
956 * highest priority nominated pair in the valid list for each
957 * component.
958 */
959 check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, 1);
960 if (check == NULL) {
961 pj_assert(!"Shouldn't happen");
962 return PJ_EBUG;
963 }
964
965 /* Override connection line address and media port number */
966 conn = m->conn;
967 if (conn == NULL)
968 conn = sdp_local->conn;
969
970 conn->addr.ptr = (char*) pj_pool_alloc(sdp_pool,
971 PJ_INET6_ADDRSTRLEN);
972 pj_sockaddr_print(&check->lcand->addr, conn->addr.ptr,
973 PJ_INET6_ADDRSTRLEN, 0);
974 conn->addr.slen = pj_ansi_strlen(conn->addr.ptr);
975 m->desc.port = pj_sockaddr_get_port(&check->lcand->addr);
976
977 /* Override address RTCP attribute if it's present */
978 if (comp_cnt == 2 &&
979 (check = pj_ice_strans_get_valid_pair(tp_ice->ice_st,
980 COMP_RTCP)) != NULL &&
981 (a_rtcp = pjmedia_sdp_attr_find(m->attr_count, m->attr,
982 &STR_RTCP, 0)) != NULL)
983 {
984 pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a_rtcp);
985
986 a_rtcp = pjmedia_sdp_attr_create_rtcp(sdp_pool,
987 &check->lcand->addr);
988 if (a_rtcp)
989 pjmedia_sdp_attr_add(&m->attr_count, m->attr, a_rtcp);
990 }
991
992 /* Encode only candidates matching the default destination
993 * for each component
994 */
995 for (comp=0; comp < comp_cnt; ++comp) {
996 int len;
997 pj_str_t value;
998
999 /* Get valid pair for this component */
1000 check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, comp+1);
1001 if (check == NULL)
1002 continue;
1003
1004 /* Print and add local candidate in the pair */
1005 value.ptr = attr_buf;
1006 value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN,
1007 check->lcand);
1008 if (value.slen < 0) {
1009 pj_assert(!"Not enough attr_buf to print candidate");
1010 return PJ_EBUG;
1011 }
1012
1013 attr = pjmedia_sdp_attr_create(sdp_pool, STR_CANDIDATE.ptr,
1014 &value);
1015 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1016
1017 /* Append to a=remote-candidates attribute */
1018 if (pj_ice_strans_get_role(tp_ice->ice_st) ==
1019 PJ_ICE_SESS_ROLE_CONTROLLING)
1020 {
1021 char rem_addr[PJ_INET6_ADDRSTRLEN];
1022
1023 pj_sockaddr_print(&check->rcand->addr, rem_addr,
1024 sizeof(rem_addr), 0);
1025 len = pj_ansi_snprintf(
1026 rem_cand.ptr + rem_cand.slen,
1027 RATTR_BUF_LEN - rem_cand.slen,
1028 "%s%u %s %u",
1029 (rem_cand.slen==0? "" : " "),
1030 comp+1, rem_addr,
1031 pj_sockaddr_get_port(&check->rcand->addr)
1032 );
1033 if (len < 1 || len >= RATTR_BUF_LEN - rem_cand.slen) {
1034 pj_assert(!"Not enough buffer to print "
1035 "remote-candidates");
1036 return PJ_EBUG;
1037 }
1038
1039 rem_cand.slen += len;
1040 }
1041 }
1042
1043 /* 9.1.2.2: Existing Media Streams with ICE Completed
1044 * In addition, if the agent is controlling, it MUST include
1045 * the a=remote-candidates attribute for each media stream
1046 * whose check list is in the Completed state. The attribute
1047 * contains the remote candidates from the highest priority
1048 * nominated pair in the valid list for each component of that
1049 * media stream.
1050 */
1051 if (pj_ice_strans_get_role(tp_ice->ice_st) ==
1052 PJ_ICE_SESS_ROLE_CONTROLLING)
1053 {
1054 attr = pjmedia_sdp_attr_create(sdp_pool, STR_REM_CAND.ptr,
1055 &rem_cand);
1056 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1057 }
1058
1059 } else if (pj_ice_strans_has_sess(tp_ice->ice_st) &&
1060 (restart_session || pj_ice_strans_get_state(tp_ice->ice_st) !=
1061 PJ_ICE_STRANS_STATE_FAILED))
1062 {
1063 /* Encode all candidates to SDP media */
1064 char *attr_buf;
1065 unsigned comp;
1066
1067 /* If ICE is not restarted, encode current ICE ufrag/pwd.
1068 * Otherwise generate new one.
1069 */
1070 if (!restart_session) {
1071 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr,
1072 &local_ufrag);
1073 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1074
1075 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr,
1076 &local_pwd);
1077 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1078
1079 } else {
1080 pj_str_t str;
1081
1082 str.slen = PJ_ICE_UFRAG_LEN;
1083 str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen);
1084 pj_create_random_string(str.ptr, str.slen);
1085 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &str);
1086 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1087
1088 str.slen = PJ_ICE_PWD_LEN;
1089 str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen);
1090 pj_create_random_string(str.ptr, str.slen);
1091 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &str);
1092 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1093 }
1094
1095 /* Create buffer to encode candidates as SDP attribute */
1096 attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN);
1097
1098 for (comp=0; comp < comp_cnt; ++comp) {
1099 unsigned cand_cnt;
1100 pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];
1101 unsigned i;
1102
1103 cand_cnt = PJ_ARRAY_SIZE(cand);
1104 status = pj_ice_strans_enum_cands(tp_ice->ice_st, comp+1,
1105 &cand_cnt, cand);
1106 if (status != PJ_SUCCESS)
1107 return status;
1108
1109 for (i=0; i<cand_cnt; ++i) {
1110 pj_str_t value;
1111
1112 value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN,
1113 &cand[i]);
1114 if (value.slen < 0) {
1115 pj_assert(!"Not enough attr_buf to print candidate");
1116 return PJ_EBUG;
1117 }
1118
1119 value.ptr = attr_buf;
1120 attr = pjmedia_sdp_attr_create(sdp_pool,
1121 STR_CANDIDATE.ptr,
1122 &value);
1123 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1124 }
1125 }
1126 } else {
1127 /* ICE has failed, application should have terminated this call */
1128 }
1129
1130 /* Removing a=rtcp line when there is only one component. */
1131 if (comp_cnt == 1) {
1132 attr = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_RTCP, NULL);
1133 if (attr)
1134 pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr);
1135 /* If RTCP is not in use, we MUST send b=RS:0 and b=RR:0. */
1136 pj_assert(m->bandw_count + 2 <= PJ_ARRAY_SIZE(m->bandw));
1137 if (m->bandw_count + 2 <= PJ_ARRAY_SIZE(m->bandw)) {
1138 m->bandw[m->bandw_count] = PJ_POOL_ZALLOC_T(sdp_pool,
1139 pjmedia_sdp_bandw);
1140 pj_memcpy(&m->bandw[m->bandw_count]->modifier, &STR_BANDW_RS,
1141 sizeof(pj_str_t));
1142 m->bandw_count++;
1143 m->bandw[m->bandw_count] = PJ_POOL_ZALLOC_T(sdp_pool,
1144 pjmedia_sdp_bandw);
1145 pj_memcpy(&m->bandw[m->bandw_count]->modifier, &STR_BANDW_RR,
1146 sizeof(pj_str_t));
1147 m->bandw_count++;
1148 }
1149 }
1150
1151 /* Add a=rtcp-mux attribute */
1152 if (rtcp_mux) {
1153 pjmedia_sdp_attr *add_attr;
1154
1155 add_attr = PJ_POOL_ZALLOC_T(sdp_pool, pjmedia_sdp_attr);
1156 add_attr->name = STR_RTCP_MUX;
1157 m->attr[m->attr_count++] = add_attr;
1158 }
1159
1160 /* Add trickle ICE attributes */
1161 if (trickle) {
1162 pj_bool_t end_of_cand;
1163
1164 /* Add media ID attribute "a=mid" */
1165 attr = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "mid", NULL);
1166 if (!attr) {
1167 attr = pjmedia_sdp_attr_create(sdp_pool, "mid", &tp_ice->sdp_mid);
1168 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1169 }
1170
1171 end_of_cand = tp_ice->end_of_cand;
1172 status = pjmedia_ice_trickle_encode_sdp(sdp_pool, sdp_local,
1173 &tp_ice->sdp_mid, NULL, NULL,
1174 0, NULL, end_of_cand);
1175 if (status != PJ_SUCCESS) {
1176 PJ_PERROR(3,(tp_ice->base.name, status,
1177 "Failed in adding trickle ICE attributes"));
1178 return status;
1179 }
1180 }
1181
1182 return PJ_SUCCESS;
1183 }
1184
1185
1186 /* Parse a=candidate line */
parse_cand(const char * obj_name,pj_pool_t * pool,const pj_str_t * orig_input,pj_ice_sess_cand * cand)1187 static pj_status_t parse_cand(const char *obj_name,
1188 pj_pool_t *pool,
1189 const pj_str_t *orig_input,
1190 pj_ice_sess_cand *cand)
1191 {
1192 pj_str_t token, delim, host;
1193 int af;
1194 pj_ssize_t found_idx;
1195 pj_status_t status = PJNATH_EICEINCANDSDP;
1196
1197 pj_bzero(cand, sizeof(*cand));
1198
1199 PJ_UNUSED_ARG(obj_name);
1200
1201 /* Foundation */
1202 delim = pj_str(" ");
1203 found_idx = pj_strtok(orig_input, &delim, &token, 0);
1204 if (found_idx == orig_input->slen) {
1205 TRACE__((obj_name, "Expecting ICE foundation in candidate"));
1206 goto on_return;
1207 }
1208 if (pool) {
1209 pj_strdup(pool, &cand->foundation, &token);
1210 } else {
1211 cand->foundation = token;
1212 }
1213
1214 /* Component ID */
1215 found_idx = pj_strtok(orig_input, &delim, &token, found_idx + token.slen);
1216 if (found_idx == orig_input->slen) {
1217 TRACE__((obj_name, "Expecting ICE component ID in candidate"));
1218 goto on_return;
1219 }
1220 cand->comp_id = (pj_uint8_t)pj_strtoul(&token);
1221
1222 /* Transport */
1223 found_idx = pj_strtok(orig_input, &delim, &token, found_idx + token.slen);
1224 if (found_idx == orig_input->slen) {
1225 TRACE__((obj_name, "Expecting ICE transport in candidate"));
1226 goto on_return;
1227 }
1228 if (pj_stricmp2(&token, "UDP") != 0) {
1229 TRACE__((obj_name,
1230 "Expecting ICE UDP transport only in candidate"));
1231 goto on_return;
1232 }
1233
1234 /* Priority */
1235 found_idx = pj_strtok(orig_input, &delim, &token, found_idx + token.slen);
1236 if (found_idx == orig_input->slen) {
1237 TRACE__((obj_name, "Expecting ICE priority in candidate"));
1238 goto on_return;
1239 }
1240 cand->prio = pj_strtoul(&token);
1241
1242 /* Host */
1243 found_idx = pj_strtok(orig_input, &delim, &host, found_idx + token.slen);
1244 if (found_idx == orig_input->slen) {
1245 TRACE__((obj_name, "Expecting ICE host in candidate"));
1246 goto on_return;
1247 }
1248 /* Detect address family */
1249 if (pj_strchr(&host, ':'))
1250 af = pj_AF_INET6();
1251 else
1252 af = pj_AF_INET();
1253 /* Assign address */
1254 if (pj_sockaddr_init(af, &cand->addr, &host, 0)) {
1255 TRACE__((obj_name, "Invalid ICE candidate address"));
1256 goto on_return;
1257 }
1258
1259 /* Port */
1260 found_idx = pj_strtok(orig_input, &delim, &token, found_idx + host.slen);
1261 if (found_idx == orig_input->slen) {
1262 TRACE__((obj_name, "Expecting ICE port number in candidate"));
1263 goto on_return;
1264 }
1265 pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)pj_strtoul(&token));
1266
1267 /* typ */
1268 found_idx = pj_strtok(orig_input, &delim, &token, found_idx + token.slen);
1269 if (found_idx == orig_input->slen) {
1270 TRACE__((obj_name, "Expecting ICE \"typ\" in candidate"));
1271 goto on_return;
1272 }
1273 if (pj_stricmp2(&token, "typ") != 0) {
1274 TRACE__((obj_name, "Expecting ICE \"typ\" in candidate"));
1275 goto on_return;
1276 }
1277
1278 /* candidate type */
1279 found_idx = pj_strtok(orig_input, &delim, &token, found_idx + token.slen);
1280 if (found_idx == orig_input->slen) {
1281 TRACE__((obj_name, "Expecting ICE candidate type in candidate"));
1282 goto on_return;
1283 }
1284
1285 if (pj_stricmp2(&token, "host") == 0) {
1286 cand->type = PJ_ICE_CAND_TYPE_HOST;
1287
1288 } else if (pj_stricmp2(&token, "srflx") == 0) {
1289 cand->type = PJ_ICE_CAND_TYPE_SRFLX;
1290
1291 } else if (pj_stricmp2(&token, "relay") == 0) {
1292 cand->type = PJ_ICE_CAND_TYPE_RELAYED;
1293
1294 } else if (pj_stricmp2(&token, "prflx") == 0) {
1295 cand->type = PJ_ICE_CAND_TYPE_PRFLX;
1296
1297 } else {
1298 PJ_LOG(5,(obj_name, "Invalid ICE candidate type %.*s in candidate",
1299 token.slen, token.ptr));
1300 goto on_return;
1301 }
1302
1303 status = PJ_SUCCESS;
1304
1305 on_return:
1306 return status;
1307 }
1308
1309
1310 /* Create initial SDP offer */
create_initial_offer(struct transport_ice * tp_ice,pj_pool_t * sdp_pool,pjmedia_sdp_session * loc_sdp,unsigned media_index)1311 static pj_status_t create_initial_offer(struct transport_ice *tp_ice,
1312 pj_pool_t *sdp_pool,
1313 pjmedia_sdp_session *loc_sdp,
1314 unsigned media_index)
1315 {
1316 pj_status_t status;
1317
1318 /* Encode ICE in SDP */
1319 status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index,
1320 tp_ice->comp_cnt, PJ_FALSE,
1321 tp_ice->enable_rtcp_mux,
1322 tp_ice->trickle_ice !=
1323 PJ_ICE_SESS_TRICKLE_DISABLED);
1324 if (status != PJ_SUCCESS) {
1325 set_no_ice(tp_ice, "Error encoding SDP answer", status);
1326 return status;
1327 }
1328
1329 return PJ_SUCCESS;
1330 }
1331
1332
1333 /* Verify incoming offer */
verify_ice_sdp(struct transport_ice * tp_ice,pj_pool_t * tmp_pool,const pjmedia_sdp_session * rem_sdp,unsigned media_index,pj_ice_sess_role current_ice_role,struct sdp_state * sdp_state)1334 static pj_status_t verify_ice_sdp(struct transport_ice *tp_ice,
1335 pj_pool_t *tmp_pool,
1336 const pjmedia_sdp_session *rem_sdp,
1337 unsigned media_index,
1338 pj_ice_sess_role current_ice_role,
1339 struct sdp_state *sdp_state)
1340 {
1341 const pjmedia_sdp_media *rem_m;
1342 const pjmedia_sdp_attr *ufrag_attr, *pwd_attr;
1343 const pjmedia_sdp_conn *rem_conn;
1344 pj_bool_t comp1_found=PJ_FALSE, comp2_found=PJ_FALSE, has_rtcp=PJ_FALSE;
1345 pj_sockaddr rem_conn_addr, rtcp_addr;
1346 unsigned i;
1347 int rem_af = 0;
1348 pj_status_t status;
1349
1350 rem_m = rem_sdp->media[media_index];
1351
1352 /* Check if remote wants RTCP mux */
1353 if (tp_ice->enable_rtcp_mux) {
1354 pjmedia_sdp_attr *attr;
1355
1356 attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr,
1357 &STR_RTCP_MUX, NULL);
1358 tp_ice->use_rtcp_mux = (attr? PJ_TRUE: PJ_FALSE);
1359 }
1360
1361 /* Get the "ice-ufrag" and "ice-pwd" attributes */
1362 get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr);
1363
1364 /* If "ice-ufrag" or "ice-pwd" are not found, disable ICE */
1365 if (ufrag_attr==NULL || pwd_attr==NULL) {
1366 sdp_state->match_comp_cnt = 0;
1367 return PJ_SUCCESS;
1368 }
1369
1370 /* Verify that default target for each component matches one of the
1371 * candidate for the component. Otherwise stop ICE with ICE ice_mismatch
1372 * error.
1373 */
1374
1375 /* Component 1 is the c= line */
1376 rem_conn = rem_m->conn;
1377 if (rem_conn == NULL)
1378 rem_conn = rem_sdp->conn;
1379 if (!rem_conn)
1380 return PJMEDIA_SDP_EMISSINGCONN;
1381
1382 /* Verify address family matches */
1383 /*
1384 if ((tp_ice->af==pj_AF_INET() &&
1385 pj_strcmp(&rem_conn->addr_type, &STR_IP4)!=0) ||
1386 (tp_ice->af==pj_AF_INET6() &&
1387 pj_strcmp(&rem_conn->addr_type, &STR_IP6)!=0))
1388 {
1389 return PJMEDIA_SDP_ETPORTNOTEQUAL;
1390 }
1391 */
1392
1393 /* Get remote address family */
1394 if (pj_strcmp(&rem_conn->addr_type, &STR_IP4)==0)
1395 rem_af = pj_AF_INET();
1396 else if (pj_strcmp(&rem_conn->addr_type, &STR_IP6)==0)
1397 rem_af = pj_AF_INET6();
1398 else
1399 pj_assert(!"Unsupported address family");
1400
1401 /* Assign remote connection address */
1402 status = pj_sockaddr_init(rem_af, &rem_conn_addr, &rem_conn->addr,
1403 (pj_uint16_t)rem_m->desc.port);
1404 if (status != PJ_SUCCESS)
1405 return status;
1406
1407 if (tp_ice->comp_cnt > 1) {
1408 const pjmedia_sdp_attr *attr;
1409
1410 /* Get default RTCP candidate from a=rtcp line, if present, otherwise
1411 * calculate default RTCP candidate from default RTP target.
1412 */
1413 attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr,
1414 &STR_RTCP, NULL);
1415 has_rtcp = (attr != NULL);
1416
1417 if (attr) {
1418 pjmedia_sdp_rtcp_attr rtcp_attr;
1419
1420 status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp_attr);
1421 if (status != PJ_SUCCESS) {
1422 /* Error parsing a=rtcp attribute */
1423 return status;
1424 }
1425
1426 if (rtcp_attr.addr.slen) {
1427 /* Verify address family matches */
1428 /*
1429 if ((tp_ice->af==pj_AF_INET() &&
1430 pj_strcmp(&rtcp_attr.addr_type, &STR_IP4)!=0) ||
1431 (tp_ice->af==pj_AF_INET6() &&
1432 pj_strcmp(&rtcp_attr.addr_type, &STR_IP6)!=0))
1433 {
1434 return PJMEDIA_SDP_ETPORTNOTEQUAL;
1435 }
1436 */
1437
1438 /* Assign RTCP address */
1439 status = pj_sockaddr_init(rem_af, &rtcp_addr,
1440 &rtcp_attr.addr,
1441 (pj_uint16_t)rtcp_attr.port);
1442 if (status != PJ_SUCCESS) {
1443 return PJMEDIA_SDP_EINRTCP;
1444 }
1445 } else {
1446 /* Assign RTCP address */
1447 status = pj_sockaddr_init(rem_af, &rtcp_addr,
1448 NULL,
1449 (pj_uint16_t)rtcp_attr.port);
1450 if (status != PJ_SUCCESS) {
1451 return PJMEDIA_SDP_EINRTCP;
1452 }
1453 pj_sockaddr_copy_addr(&rtcp_addr, &rem_conn_addr);
1454 }
1455 } else {
1456 unsigned rtcp_port;
1457
1458 rtcp_port = pj_sockaddr_get_port(&rem_conn_addr) + 1;
1459 pj_sockaddr_cp(&rtcp_addr, &rem_conn_addr);
1460 pj_sockaddr_set_port(&rtcp_addr, (pj_uint16_t)rtcp_port);
1461 }
1462 }
1463
1464 /* Find the default addresses in a=candidate attributes.
1465 */
1466 for (i=0; i<rem_m->attr_count; ++i) {
1467 pj_ice_sess_cand cand;
1468 unsigned disable_ice_mismatch = tp_ice->options &
1469 PJMEDIA_ICE_DISABLE_ICE_MISMATCH;
1470
1471 if (pj_strcmp(&rem_m->attr[i]->name, &STR_CANDIDATE)!=0)
1472 continue;
1473
1474 status = parse_cand(tp_ice->base.name, tmp_pool,
1475 &rem_m->attr[i]->value, &cand);
1476 if (status != PJ_SUCCESS) {
1477 PJ_PERROR(4,(tp_ice->base.name, status,
1478 "Error in parsing SDP candidate attribute '%.*s', "
1479 "candidate is ignored",
1480 (int)rem_m->attr[i]->value.slen,
1481 rem_m->attr[i]->value.ptr));
1482 continue;
1483 }
1484
1485 if (!comp1_found && cand.comp_id==COMP_RTP)
1486 {
1487 if ((disable_ice_mismatch) ||
1488 (pj_sockaddr_cmp(&rem_conn_addr, &cand.addr) == 0))
1489 {
1490 comp1_found = PJ_TRUE;
1491 }
1492 } else if (!comp2_found && cand.comp_id==COMP_RTCP)
1493 {
1494 if ((disable_ice_mismatch) ||
1495 (pj_sockaddr_cmp(&rtcp_addr, &cand.addr) == 0))
1496 {
1497 comp2_found = PJ_TRUE;
1498 }
1499 }
1500
1501 if (cand.comp_id == COMP_RTCP)
1502 has_rtcp = PJ_TRUE;
1503
1504 if (comp1_found && (comp2_found || tp_ice->comp_cnt==1))
1505 break;
1506 }
1507
1508 /* Check matched component count and ice_mismatch */
1509 if (comp1_found &&
1510 (tp_ice->comp_cnt==1 || !has_rtcp || tp_ice->use_rtcp_mux))
1511 {
1512 sdp_state->match_comp_cnt = 1;
1513 sdp_state->ice_mismatch = PJ_FALSE;
1514 } else if (comp1_found && comp2_found) {
1515 sdp_state->match_comp_cnt = 2;
1516 sdp_state->ice_mismatch = PJ_FALSE;
1517 } else {
1518 sdp_state->match_comp_cnt = (tp_ice->comp_cnt > 1 && has_rtcp)? 2 : 1;
1519 sdp_state->ice_mismatch = PJ_TRUE;
1520 }
1521
1522
1523 /* Detect remote restarting session */
1524 if (pj_ice_strans_has_sess(tp_ice->ice_st) &&
1525 (pj_ice_strans_sess_is_running(tp_ice->ice_st) ||
1526 pj_ice_strans_sess_is_complete(tp_ice->ice_st)))
1527 {
1528 pj_str_t rem_run_ufrag, rem_run_pwd;
1529 pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, NULL, NULL,
1530 &rem_run_ufrag, &rem_run_pwd);
1531 if (pj_strcmp(&ufrag_attr->value, &rem_run_ufrag) ||
1532 pj_strcmp(&pwd_attr->value, &rem_run_pwd))
1533 {
1534 /* Remote offers to restart ICE */
1535 sdp_state->ice_restart = PJ_TRUE;
1536 } else {
1537 sdp_state->ice_restart = PJ_FALSE;
1538 }
1539 } else {
1540 sdp_state->ice_restart = PJ_FALSE;
1541 }
1542
1543 /* Detect our role */
1544 if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr,
1545 &STR_ICE_LITE, NULL) != NULL)
1546 {
1547 /* Remote is ICE lite, set our role as controlling */
1548 sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING;
1549 } else {
1550 if (current_ice_role==PJ_ICE_SESS_ROLE_CONTROLLING) {
1551 sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING;
1552 } else {
1553 sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLED;
1554 }
1555 }
1556
1557 /* Check trickle ICE indication */
1558 if (tp_ice->trickle_ice != PJ_ICE_SESS_TRICKLE_DISABLED) {
1559 sdp_state->has_trickle = pjmedia_ice_sdp_has_trickle(rem_sdp,
1560 media_index);
1561
1562 /* Reset ICE mismatch flag if conn addr is default address */
1563 if (sdp_state->ice_mismatch && sdp_state->has_trickle) {
1564 pj_sockaddr def_addr;
1565 pj_sockaddr_init(rem_af, &def_addr, NULL, 9);
1566 if (pj_sockaddr_cmp(&rem_conn_addr, &def_addr)==0)
1567 sdp_state->ice_mismatch = PJ_FALSE;
1568 }
1569 } else {
1570 sdp_state->has_trickle = PJ_FALSE;
1571 }
1572
1573 PJ_LOG(4,(tp_ice->base.name,
1574 "Processing SDP: support ICE=%u, common comp_cnt=%u, "
1575 "ice_mismatch=%u, ice_restart=%u, local_role=%s, trickle=%u",
1576 (sdp_state->match_comp_cnt != 0),
1577 sdp_state->match_comp_cnt,
1578 sdp_state->ice_mismatch,
1579 sdp_state->ice_restart,
1580 pj_ice_sess_role_name(sdp_state->local_role),
1581 sdp_state->has_trickle));
1582
1583 return PJ_SUCCESS;
1584
1585 }
1586
1587 /* Encode information in SDP if ICE is not used */
encode_no_ice_in_sdp(struct transport_ice * tp_ice,pj_pool_t * pool,pjmedia_sdp_session * sdp_local,const pjmedia_sdp_session * rem_sdp,unsigned media_index)1588 static pj_status_t encode_no_ice_in_sdp( struct transport_ice *tp_ice,
1589 pj_pool_t *pool,
1590 pjmedia_sdp_session *sdp_local,
1591 const pjmedia_sdp_session *rem_sdp,
1592 unsigned media_index)
1593 {
1594 if (tp_ice->enable_rtcp_mux) {
1595 pjmedia_sdp_media *m = sdp_local->media[media_index];
1596 pjmedia_sdp_attr *attr;
1597 pj_bool_t add_rtcp_mux = PJ_TRUE;
1598
1599 if (rem_sdp)
1600 add_rtcp_mux = tp_ice->use_rtcp_mux;
1601 else {
1602 /* For subsequent offer, set it to false first since
1603 * we are still waiting for remote answer.
1604 */
1605 tp_ice->use_rtcp_mux = PJ_FALSE;
1606 }
1607
1608 /* Remove RTCP attribute because for subsequent offers/answers,
1609 * the address (obtained from transport_get_info() ) may be
1610 * incorrect if we are not yet confirmed to use RTCP mux
1611 * (because we are still waiting for remote answer) or
1612 * if remote rejects it.
1613 */
1614 pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "rtcp");
1615
1616 if (!tp_ice->use_rtcp_mux && tp_ice->comp_cnt > 1) {
1617 pj_ice_sess_cand cand;
1618 pj_status_t status;
1619
1620 status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 2, &cand);
1621 if (status == PJ_SUCCESS) {
1622 /* Add RTCP attribute if the remote doesn't offer or
1623 * rejects it.
1624 */
1625 attr = pjmedia_sdp_attr_create_rtcp(pool, &cand.addr);
1626 if (attr)
1627 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
1628 }
1629 }
1630
1631 /* Add a=rtcp-mux attribute. */
1632 if (add_rtcp_mux) {
1633 attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
1634 attr->name = STR_RTCP_MUX;
1635 m->attr[m->attr_count++] = attr;
1636 }
1637 }
1638 return PJ_SUCCESS;
1639 }
1640
1641
1642 /* Verify incoming offer and create initial answer */
create_initial_answer(struct transport_ice * tp_ice,pj_pool_t * sdp_pool,pjmedia_sdp_session * loc_sdp,const pjmedia_sdp_session * rem_sdp,unsigned media_index)1643 static pj_status_t create_initial_answer(struct transport_ice *tp_ice,
1644 pj_pool_t *sdp_pool,
1645 pjmedia_sdp_session *loc_sdp,
1646 const pjmedia_sdp_session *rem_sdp,
1647 unsigned media_index)
1648 {
1649 const pjmedia_sdp_media *rem_m = rem_sdp->media[media_index];
1650 pj_bool_t with_trickle;
1651 pj_status_t status;
1652
1653 /* Check if media is removed (just in case) */
1654 if (rem_m->desc.port == 0) {
1655 return PJ_SUCCESS;
1656 }
1657
1658 /* Verify the offer */
1659 status = verify_ice_sdp(tp_ice, sdp_pool, rem_sdp, media_index,
1660 PJ_ICE_SESS_ROLE_CONTROLLED,
1661 &tp_ice->rem_offer_state);
1662 if (status != PJ_SUCCESS) {
1663 set_no_ice(tp_ice, "Invalid SDP offer", status);
1664 return status;
1665 }
1666
1667 /* Does remote support ICE? */
1668 if (tp_ice->rem_offer_state.match_comp_cnt==0) {
1669 set_no_ice(tp_ice, "No ICE found in SDP offer", PJ_SUCCESS);
1670 encode_no_ice_in_sdp(tp_ice, sdp_pool, loc_sdp, rem_sdp,
1671 media_index);
1672 return PJ_SUCCESS;
1673 }
1674
1675 /* ICE ice_mismatch? */
1676 if (tp_ice->rem_offer_state.ice_mismatch) {
1677 set_no_ice(tp_ice, "ICE ice_mismatch in remote offer", PJ_SUCCESS);
1678 encode_ice_mismatch(sdp_pool, loc_sdp, media_index);
1679 return PJ_SUCCESS;
1680 }
1681
1682 /* Encode ICE in SDP */
1683 with_trickle = tp_ice->rem_offer_state.has_trickle &&
1684 tp_ice->trickle_ice != PJ_ICE_SESS_TRICKLE_DISABLED;
1685 status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index,
1686 tp_ice->rem_offer_state.match_comp_cnt,
1687 PJ_FALSE, tp_ice->use_rtcp_mux,
1688 with_trickle);
1689 if (status != PJ_SUCCESS) {
1690 set_no_ice(tp_ice, "Error encoding SDP answer", status);
1691 return status;
1692 }
1693
1694 return PJ_SUCCESS;
1695 }
1696
1697
1698 /* Create subsequent SDP offer */
create_subsequent_offer(struct transport_ice * tp_ice,pj_pool_t * sdp_pool,pjmedia_sdp_session * loc_sdp,unsigned media_index)1699 static pj_status_t create_subsequent_offer(struct transport_ice *tp_ice,
1700 pj_pool_t *sdp_pool,
1701 pjmedia_sdp_session *loc_sdp,
1702 unsigned media_index)
1703 {
1704 unsigned comp_cnt;
1705
1706 if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) {
1707 /* We don't have ICE */
1708 encode_no_ice_in_sdp(tp_ice, sdp_pool, loc_sdp, NULL, media_index);
1709 return PJ_SUCCESS;
1710 }
1711
1712 comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
1713 return encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index,
1714 comp_cnt, PJ_FALSE, tp_ice->enable_rtcp_mux,
1715 PJ_FALSE);
1716 }
1717
1718
1719 /* Create subsequent SDP answer */
create_subsequent_answer(struct transport_ice * tp_ice,pj_pool_t * sdp_pool,pjmedia_sdp_session * loc_sdp,const pjmedia_sdp_session * rem_sdp,unsigned media_index)1720 static pj_status_t create_subsequent_answer(struct transport_ice *tp_ice,
1721 pj_pool_t *sdp_pool,
1722 pjmedia_sdp_session *loc_sdp,
1723 const pjmedia_sdp_session *rem_sdp,
1724 unsigned media_index)
1725 {
1726 pj_status_t status;
1727
1728 /* We have a session */
1729 status = verify_ice_sdp(tp_ice, sdp_pool, rem_sdp, media_index,
1730 PJ_ICE_SESS_ROLE_CONTROLLED,
1731 &tp_ice->rem_offer_state);
1732 if (status != PJ_SUCCESS) {
1733 /* Something wrong with the offer */
1734 return status;
1735 }
1736
1737 if (pj_ice_strans_has_sess(tp_ice->ice_st)) {
1738 /*
1739 * Received subsequent offer while we have ICE active.
1740 */
1741
1742 if (tp_ice->rem_offer_state.match_comp_cnt == 0) {
1743 /* Remote no longer offers ICE */
1744 encode_no_ice_in_sdp(tp_ice, sdp_pool, loc_sdp, rem_sdp,
1745 media_index);
1746 return PJ_SUCCESS;
1747 }
1748
1749 if (tp_ice->rem_offer_state.ice_mismatch) {
1750 encode_ice_mismatch(sdp_pool, loc_sdp, media_index);
1751 return PJ_SUCCESS;
1752 }
1753
1754 status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index,
1755 tp_ice->rem_offer_state.match_comp_cnt,
1756 tp_ice->rem_offer_state.ice_restart,
1757 tp_ice->use_rtcp_mux, PJ_FALSE);
1758 if (status != PJ_SUCCESS)
1759 return status;
1760
1761 /* Done */
1762
1763 } else {
1764 pj_bool_t with_trickle;
1765
1766 /*
1767 * Received subsequent offer while we DON'T have ICE active.
1768 */
1769
1770 if (tp_ice->rem_offer_state.match_comp_cnt == 0) {
1771 /* Remote does not support ICE */
1772 encode_no_ice_in_sdp(tp_ice, sdp_pool, loc_sdp, rem_sdp,
1773 media_index);
1774 return PJ_SUCCESS;
1775 }
1776
1777 if (tp_ice->rem_offer_state.ice_mismatch) {
1778 encode_ice_mismatch(sdp_pool, loc_sdp, media_index);
1779 return PJ_SUCCESS;
1780 }
1781
1782 /* Looks like now remote is offering ICE, so we need to create
1783 * ICE session now.
1784 */
1785 status = pj_ice_strans_init_ice(tp_ice->ice_st,
1786 PJ_ICE_SESS_ROLE_CONTROLLED,
1787 NULL, NULL);
1788 if (status != PJ_SUCCESS) {
1789 /* Fail to create new ICE session */
1790 return status;
1791 }
1792
1793 with_trickle = tp_ice->rem_offer_state.has_trickle &&
1794 tp_ice->trickle_ice != PJ_ICE_SESS_TRICKLE_DISABLED;
1795 status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index,
1796 tp_ice->rem_offer_state.match_comp_cnt,
1797 tp_ice->rem_offer_state.ice_restart,
1798 tp_ice->use_rtcp_mux,
1799 with_trickle);
1800 if (status != PJ_SUCCESS)
1801 return status;
1802
1803 /* Done */
1804 }
1805
1806 return PJ_SUCCESS;
1807 }
1808
1809
1810 /*
1811 * For both UAC and UAS, pass in the SDP before sending it to remote.
1812 * This will add ICE attributes to the SDP.
1813 */
transport_media_create(pjmedia_transport * tp,pj_pool_t * sdp_pool,unsigned options,const pjmedia_sdp_session * rem_sdp,unsigned media_index)1814 static pj_status_t transport_media_create(pjmedia_transport *tp,
1815 pj_pool_t *sdp_pool,
1816 unsigned options,
1817 const pjmedia_sdp_session *rem_sdp,
1818 unsigned media_index)
1819 {
1820 struct transport_ice *tp_ice = (struct transport_ice*)tp;
1821 pj_ice_sess_role ice_role;
1822 pj_status_t status;
1823
1824 PJ_UNUSED_ARG(media_index);
1825 PJ_UNUSED_ARG(sdp_pool);
1826
1827 tp_ice->media_option = options;
1828 tp_ice->enable_rtcp_mux = ((options & PJMEDIA_TPMED_RTCP_MUX) != 0);
1829 tp_ice->oa_role = ROLE_NONE;
1830 tp_ice->initial_sdp = PJ_TRUE;
1831
1832 /* Init SDP "a=mid" attribute. Get from remote SDP for answerer or
1833 * generate one for offerer (or if remote doesn't specify).
1834 */
1835 tp_ice->sdp_mid.slen = 0;
1836 if (rem_sdp) {
1837 pjmedia_sdp_media *m = rem_sdp->media[media_index];
1838 pjmedia_sdp_attr *a;
1839 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "mid", NULL);
1840 if (a) {
1841 pj_strdup(tp_ice->pool, &tp_ice->sdp_mid, &a->value);
1842 }
1843 }
1844 if (tp_ice->sdp_mid.slen == 0) {
1845 char tmp_buf[8];
1846 pj_ansi_snprintf(tmp_buf, sizeof(tmp_buf), "%d", media_index+1);
1847 tp_ice->sdp_mid = pj_strdup3(tp_ice->pool, tmp_buf);
1848 }
1849
1850 /* If RTCP mux is being used, set component count to 1 */
1851 if (rem_sdp && tp_ice->enable_rtcp_mux) {
1852 pjmedia_sdp_media *rem_m = rem_sdp->media[media_index];
1853 pjmedia_sdp_attr *attr;
1854 attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr,
1855 &STR_RTCP_MUX, NULL);
1856 tp_ice->use_rtcp_mux = (attr? PJ_TRUE: PJ_FALSE);
1857 }
1858 if (tp_ice->use_rtcp_mux &&
1859 pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st)>1)
1860 {
1861 pj_ice_strans_update_comp_cnt(tp_ice->ice_st, 1);
1862 }
1863
1864 /* Init ICE, the initial role is set now based on availability of
1865 * rem_sdp, but it will be checked again later.
1866 */
1867 ice_role = (rem_sdp==NULL ? PJ_ICE_SESS_ROLE_CONTROLLING :
1868 PJ_ICE_SESS_ROLE_CONTROLLED);
1869 status = pj_ice_strans_init_ice(tp_ice->ice_st, ice_role, NULL, NULL);
1870
1871 /* For trickle ICE, if remote SDP has been received, process any remote
1872 * ICE info now (ICE user fragment and/or initial ICE candidate list).
1873 */
1874 if (rem_sdp && status == PJ_SUCCESS) {
1875 if (tp_ice->trickle_ice != PJ_ICE_SESS_TRICKLE_DISABLED &&
1876 pjmedia_ice_sdp_has_trickle(rem_sdp, media_index))
1877 {
1878 pj_str_t ufrag, pwd;
1879 unsigned cand_cnt = PJ_ICE_ST_MAX_CAND;
1880 pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];
1881 pj_bool_t end_of_cand;
1882
1883 status = pjmedia_ice_trickle_decode_sdp(rem_sdp, media_index,
1884 NULL, &ufrag, &pwd,
1885 &cand_cnt, cand,
1886 &end_of_cand);
1887 if (status == PJ_SUCCESS)
1888 status = pj_ice_strans_update_check_list(
1889 tp_ice->ice_st, &ufrag, &pwd,
1890 cand_cnt, cand, end_of_cand);
1891 if (status != PJ_SUCCESS) {
1892 PJ_PERROR(1,(tp_ice->base.name, status,
1893 "Failed create checklist for trickling ICE"));
1894 return status;
1895 }
1896 }
1897 }
1898
1899 /* Done */
1900 return status;
1901 }
1902
1903
transport_encode_sdp(pjmedia_transport * tp,pj_pool_t * sdp_pool,pjmedia_sdp_session * sdp_local,const pjmedia_sdp_session * rem_sdp,unsigned media_index)1904 static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
1905 pj_pool_t *sdp_pool,
1906 pjmedia_sdp_session *sdp_local,
1907 const pjmedia_sdp_session *rem_sdp,
1908 unsigned media_index)
1909 {
1910 struct transport_ice *tp_ice = (struct transport_ice*)tp;
1911 pj_status_t status;
1912
1913 /* Validate media transport */
1914 /* This transport only support RTP/AVP transport, unless if
1915 * transport checking is disabled
1916 */
1917 if ((tp_ice->media_option & PJMEDIA_TPMED_NO_TRANSPORT_CHECKING) == 0) {
1918 pjmedia_sdp_media *m_rem, *m_loc;
1919 pj_uint32_t tp_proto_loc, tp_proto_rem;
1920
1921 m_rem = rem_sdp? rem_sdp->media[media_index] : NULL;
1922 m_loc = sdp_local->media[media_index];
1923
1924 tp_proto_loc = pjmedia_sdp_transport_get_proto(&m_loc->desc.transport);
1925 tp_proto_rem = m_rem?
1926 pjmedia_sdp_transport_get_proto(&m_rem->desc.transport) : 0;
1927 PJMEDIA_TP_PROTO_TRIM_FLAG(tp_proto_loc, PJMEDIA_TP_PROFILE_RTCP_FB);
1928 PJMEDIA_TP_PROTO_TRIM_FLAG(tp_proto_rem, PJMEDIA_TP_PROFILE_RTCP_FB);
1929
1930 if ((tp_proto_loc != PJMEDIA_TP_PROTO_RTP_AVP) ||
1931 (m_rem && tp_proto_rem != PJMEDIA_TP_PROTO_RTP_AVP))
1932 {
1933 pjmedia_sdp_media_deactivate(sdp_pool, m_loc);
1934 return PJMEDIA_SDP_EINPROTO;
1935 }
1936 }
1937
1938 if (tp_ice->initial_sdp) {
1939 if (rem_sdp) {
1940 status = create_initial_answer(tp_ice, sdp_pool, sdp_local,
1941 rem_sdp, media_index);
1942 } else {
1943 status = create_initial_offer(tp_ice, sdp_pool, sdp_local,
1944 media_index);
1945 }
1946 } else {
1947 if (rem_sdp) {
1948 status = create_subsequent_answer(tp_ice, sdp_pool, sdp_local,
1949 rem_sdp, media_index);
1950 } else {
1951 status = create_subsequent_offer(tp_ice, sdp_pool, sdp_local,
1952 media_index);
1953 }
1954 }
1955
1956 if (status==PJ_SUCCESS) {
1957 if (rem_sdp)
1958 tp_ice->oa_role = ROLE_ANSWERER;
1959 else
1960 tp_ice->oa_role = ROLE_OFFERER;
1961
1962 if (tp_ice->use_ice) {
1963 /* Update last local candidate count, so trickle ICE can identify
1964 * if there is any new local candidate.
1965 */
1966 unsigned i, comp_cnt;
1967 comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
1968 for (i = 0; i < comp_cnt; ++i) {
1969 tp_ice->last_send_cand_cnt[i] =
1970 pj_ice_strans_get_cands_count(tp_ice->ice_st, i+1);
1971 }
1972 }
1973 }
1974
1975 return status;
1976 }
1977
1978
1979 /* Start ICE session with the specified remote SDP */
start_ice(struct transport_ice * tp_ice,pj_pool_t * tmp_pool,const pjmedia_sdp_session * rem_sdp,unsigned media_index)1980 static pj_status_t start_ice(struct transport_ice *tp_ice,
1981 pj_pool_t *tmp_pool,
1982 const pjmedia_sdp_session *rem_sdp,
1983 unsigned media_index)
1984 {
1985 pjmedia_sdp_media *rem_m = rem_sdp->media[media_index];
1986 const pjmedia_sdp_attr *ufrag_attr, *pwd_attr;
1987 pj_ice_sess_cand *cand;
1988 unsigned i, cand_cnt;
1989 pj_status_t status;
1990
1991 get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr);
1992
1993 /* Allocate candidate array */
1994 cand = (pj_ice_sess_cand*)
1995 pj_pool_calloc(tmp_pool, PJ_ICE_MAX_CAND,
1996 sizeof(pj_ice_sess_cand));
1997
1998 /* Get all candidates in the media */
1999 cand_cnt = 0;
2000 for (i=0; i<rem_m->attr_count && cand_cnt < PJ_ICE_MAX_CAND; ++i) {
2001 pjmedia_sdp_attr *attr;
2002
2003 attr = rem_m->attr[i];
2004
2005 if (pj_strcmp(&attr->name, &STR_CANDIDATE)!=0)
2006 continue;
2007
2008 /* Parse candidate */
2009 status = parse_cand(tp_ice->base.name, tmp_pool, &attr->value,
2010 &cand[cand_cnt]);
2011 if (status != PJ_SUCCESS) {
2012 PJ_PERROR(4,(tp_ice->base.name, status,
2013 "Error in parsing SDP candidate attribute '%.*s', "
2014 "candidate is ignored",
2015 (int)attr->value.slen, attr->value.ptr));
2016 continue;
2017 }
2018
2019 if (!tp_ice->use_rtcp_mux || cand[cand_cnt].comp_id < 2)
2020 cand_cnt++;
2021 }
2022
2023 /* Start ICE */
2024 return pj_ice_strans_start_ice(tp_ice->ice_st, &ufrag_attr->value,
2025 &pwd_attr->value, cand_cnt, cand);
2026 }
2027
2028
2029 /*
2030 * Start ICE checks when both offer and answer have been negotiated
2031 * by SDP negotiator.
2032 */
transport_media_start(pjmedia_transport * tp,pj_pool_t * tmp_pool,const pjmedia_sdp_session * sdp_local,const pjmedia_sdp_session * rem_sdp,unsigned media_index)2033 static pj_status_t transport_media_start(pjmedia_transport *tp,
2034 pj_pool_t *tmp_pool,
2035 const pjmedia_sdp_session *sdp_local,
2036 const pjmedia_sdp_session *rem_sdp,
2037 unsigned media_index)
2038 {
2039 struct transport_ice *tp_ice = (struct transport_ice*)tp;
2040 pjmedia_sdp_media *rem_m;
2041 enum oa_role current_oa_role;
2042 pj_bool_t initial_oa;
2043 pj_status_t status;
2044
2045 PJ_ASSERT_RETURN(tp && tmp_pool && rem_sdp, PJ_EINVAL);
2046 PJ_ASSERT_RETURN(media_index < rem_sdp->media_count, PJ_EINVAL);
2047
2048 rem_m = rem_sdp->media[media_index];
2049
2050 initial_oa = tp_ice->initial_sdp;
2051 current_oa_role = tp_ice->oa_role;
2052
2053 /* SDP has been negotiated */
2054 tp_ice->initial_sdp = PJ_FALSE;
2055 tp_ice->oa_role = ROLE_NONE;
2056
2057 /* Nothing to do if we don't have ICE session */
2058 if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) {
2059 return PJ_SUCCESS;
2060 }
2061
2062 /* Special case for Session Timer. The re-INVITE for session refresh
2063 * doesn't call transport_encode_sdp(), causing current_oa_role to
2064 * be set to ROLE_NONE. This is a workaround.
2065 */
2066 if (current_oa_role == ROLE_NONE) {
2067 current_oa_role = ROLE_OFFERER;
2068 }
2069
2070 /* Processing depends on the offer/answer role */
2071 if (current_oa_role == ROLE_OFFERER) {
2072 /*
2073 * We are offerer. So this will be the first time we see the
2074 * remote's SDP.
2075 */
2076 struct sdp_state answer_state;
2077
2078 /* Verify the answer */
2079 status = verify_ice_sdp(tp_ice, tmp_pool, rem_sdp, media_index,
2080 PJ_ICE_SESS_ROLE_CONTROLLING, &answer_state);
2081 if (status != PJ_SUCCESS) {
2082 /* Something wrong in the SDP answer */
2083 set_no_ice(tp_ice, "Invalid remote SDP answer", status);
2084 return status;
2085 }
2086
2087 /* Does it have ICE? */
2088 if (answer_state.match_comp_cnt == 0) {
2089 /* Remote doesn't support ICE */
2090 set_no_ice(tp_ice, "Remote answer doesn't support ICE",
2091 PJ_SUCCESS);
2092 return PJ_SUCCESS;
2093 }
2094
2095 /* Check if remote has reported ice-mismatch */
2096 if (pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr,
2097 &STR_ICE_MISMATCH, NULL) != NULL)
2098 {
2099 /* Remote has reported ice-mismatch */
2100 set_no_ice(tp_ice,
2101 "Remote answer contains 'ice-mismatch' attribute",
2102 PJ_SUCCESS);
2103 return PJ_SUCCESS;
2104 }
2105
2106 /* Check if remote has indicated a restart */
2107 if (answer_state.ice_restart) {
2108 PJ_LOG(2,(tp_ice->base.name,
2109 "Warning: remote has signalled ICE restart in SDP "
2110 "answer which is disallowed. Remote ICE negotiation"
2111 " may fail."));
2112 }
2113
2114 /* Check if the answer itself is mismatched */
2115 if (answer_state.ice_mismatch) {
2116 /* This happens either when a B2BUA modified remote answer but
2117 * strangely didn't modify our offer, or remote is not capable
2118 * of detecting mismatch in our offer (it didn't put
2119 * 'ice-mismatch' attribute in the answer).
2120 */
2121 PJ_LOG(2,(tp_ice->base.name,
2122 "Warning: remote answer mismatch, but it does not "
2123 "reject our offer with 'ice-mismatch'. ICE negotiation "
2124 "may fail"));
2125 }
2126
2127 /* Do nothing if ICE is complete or running */
2128 if (pj_ice_strans_sess_is_running(tp_ice->ice_st)) {
2129 PJ_LOG(4,(tp_ice->base.name,
2130 "Ignored offer/answer because ICE is running"));
2131 return PJ_SUCCESS;
2132 }
2133
2134 if (pj_ice_strans_sess_is_complete(tp_ice->ice_st)) {
2135 PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged"));
2136 return PJ_SUCCESS;
2137 }
2138
2139 /* Start ICE */
2140
2141 } else {
2142 /*
2143 * We are answerer. We've seen and negotiated remote's SDP
2144 * before, and the result is in "rem_offer_state".
2145 */
2146 const pjmedia_sdp_attr *ufrag_attr, *pwd_attr;
2147
2148 /* Check for ICE in remote offer */
2149 if (tp_ice->rem_offer_state.match_comp_cnt == 0) {
2150 /* No ICE attribute present */
2151 set_no_ice(tp_ice, "Remote no longer offers ICE",
2152 PJ_SUCCESS);
2153 return PJ_SUCCESS;
2154 }
2155
2156 /* Check for ICE ice_mismatch condition in the offer */
2157 if (tp_ice->rem_offer_state.ice_mismatch) {
2158 set_no_ice(tp_ice, "Remote offer mismatch: ",
2159 PJNATH_EICEMISMATCH);
2160 return PJ_SUCCESS;
2161 }
2162
2163 /* If ICE is complete and remote doesn't request restart,
2164 * then leave the session as is.
2165 */
2166 if (!initial_oa && tp_ice->rem_offer_state.ice_restart == PJ_FALSE) {
2167 /* Remote has not requested ICE restart, so session is
2168 * unchanged.
2169 */
2170 PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged"));
2171 return PJ_SUCCESS;
2172 }
2173
2174 /* Either remote has requested ICE restart or this is our
2175 * first answer.
2176 */
2177
2178 /* Stop ICE */
2179 if (!initial_oa) {
2180 set_no_ice(tp_ice, "restarting by remote request..", PJ_SUCCESS);
2181
2182 /* We have put new ICE ufrag and pwd in the answer. Now
2183 * create a new ICE session with that ufrag/pwd pair.
2184 */
2185 get_ice_attr(sdp_local, sdp_local->media[media_index],
2186 &ufrag_attr, &pwd_attr);
2187 status = pj_ice_strans_init_ice(tp_ice->ice_st,
2188 tp_ice->rem_offer_state.local_role,
2189 &ufrag_attr->value,
2190 &pwd_attr->value);
2191 if (status != PJ_SUCCESS) {
2192 PJ_PERROR(1,(tp_ice->base.name, status,
2193 "ICE re-initialization failed!"));
2194 return status;
2195 }
2196 }
2197
2198 /* Ticket #977: Update role if turns out we're supposed to be the
2199 * Controlling agent (e.g. when talking to ice-lite peer).
2200 */
2201 if (tp_ice->rem_offer_state.local_role==PJ_ICE_SESS_ROLE_CONTROLLING &&
2202 pj_ice_strans_has_sess(tp_ice->ice_st))
2203 {
2204 pj_ice_strans_change_role(tp_ice->ice_st,
2205 PJ_ICE_SESS_ROLE_CONTROLLING);
2206 }
2207
2208 /* start ICE */
2209 }
2210
2211 /* RFC 5245 section 8.1.1:
2212 * If its peer has a lite implementation, an agent MUST use
2213 * a regular nomination algorithm.
2214 */
2215 if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr,
2216 &STR_ICE_LITE, NULL) != NULL)
2217 {
2218 pj_ice_sess_options opt;
2219 pj_ice_strans_get_options(tp_ice->ice_st, &opt);
2220 if (opt.aggressive) {
2221 opt.aggressive = PJ_FALSE;
2222 pj_ice_strans_set_options(tp_ice->ice_st, &opt);
2223 PJ_LOG(4,(tp_ice->base.name, "Forcefully set ICE to use regular "
2224 "nomination as remote is lite implementation"));
2225 }
2226 }
2227
2228 /* Now start ICE, if not yet (trickle ICE may have started it earlier) */
2229 if (!pj_ice_strans_sess_is_running(tp_ice->ice_st) &&
2230 !pj_ice_strans_sess_is_complete(tp_ice->ice_st))
2231 {
2232 status = start_ice(tp_ice, tmp_pool, rem_sdp, media_index);
2233 if (status != PJ_SUCCESS) {
2234 PJ_PERROR(1,(tp_ice->base.name, status, "ICE restart failed!"));
2235 return status;
2236 }
2237 }
2238
2239 /* Done */
2240 tp_ice->use_ice = PJ_TRUE;
2241
2242 return PJ_SUCCESS;
2243 }
2244
2245
transport_media_stop(pjmedia_transport * tp)2246 static pj_status_t transport_media_stop(pjmedia_transport *tp)
2247 {
2248 struct transport_ice *tp_ice = (struct transport_ice*)tp;
2249
2250 set_no_ice(tp_ice, "media stop requested", PJ_SUCCESS);
2251
2252 return PJ_SUCCESS;
2253 }
2254
2255
transport_get_info(pjmedia_transport * tp,pjmedia_transport_info * info)2256 static pj_status_t transport_get_info(pjmedia_transport *tp,
2257 pjmedia_transport_info *info)
2258 {
2259 struct transport_ice *tp_ice = (struct transport_ice*)tp;
2260 pj_ice_sess_cand cand;
2261 pj_sockaddr_t *addr;
2262 pj_status_t status;
2263
2264 pj_bzero(&info->sock_info, sizeof(info->sock_info));
2265 info->sock_info.rtp_sock = info->sock_info.rtcp_sock = PJ_INVALID_SOCKET;
2266
2267 /* Get RTP default address */
2268 status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 1, &cand);
2269 if (status != PJ_SUCCESS)
2270 return status;
2271
2272 /* Address of the default candidate may not be available (e.g:
2273 * STUN/TURN address is still being resolved), so let's find address
2274 * of any other candidate, if still not available, the draft RFC
2275 * seems to allow us using "0.0.0.0:9" in SDP.
2276 */
2277 addr = NULL;
2278 if (pj_sockaddr_has_addr(&cand.addr)) {
2279 addr = &cand.addr;
2280 } else if (pj_ice_strans_has_sess(tp_ice->ice_st)) {
2281 unsigned i, cnt = PJ_ICE_ST_MAX_CAND;
2282 pj_ice_sess_cand cands[PJ_ICE_ST_MAX_CAND];
2283 pj_ice_strans_enum_cands(tp_ice->ice_st, 1, &cnt, cands);
2284 for (i = 0; i < cnt && !addr; ++i) {
2285 if (pj_sockaddr_has_addr(&cands[i].addr))
2286 addr = &cands[i].addr;
2287 }
2288 }
2289 if (addr) {
2290 pj_sockaddr_cp(&info->sock_info.rtp_addr_name, addr);
2291 } else {
2292 pj_sockaddr_init(PJ_AF_INET, &info->sock_info.rtp_addr_name, NULL, 9);
2293 }
2294
2295 /* Get RTCP default address */
2296 if (tp_ice->use_rtcp_mux) {
2297 pj_sockaddr_cp(&info->sock_info.rtcp_addr_name, addr);
2298 } else if (tp_ice->comp_cnt > 1) {
2299 status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 2, &cand);
2300 if (status != PJ_SUCCESS)
2301 return status;
2302
2303 /* Address of the default candidate may not be available (e.g:
2304 * STUN/TURN address is still being resolved), so let's find address
2305 * of any other candidate. If none is available, SDP must not include
2306 * "a=rtcp" attribute.
2307 */
2308 addr = NULL;
2309 if (pj_sockaddr_has_addr(&cand.addr)) {
2310 addr = &cand.addr;
2311 } else if (pj_ice_strans_has_sess(tp_ice->ice_st)) {
2312 unsigned i, cnt = PJ_ICE_ST_MAX_CAND;
2313 pj_ice_sess_cand cands[PJ_ICE_ST_MAX_CAND];
2314 pj_ice_strans_enum_cands(tp_ice->ice_st, 2, &cnt, cands);
2315 for (i = 0; i < cnt && !addr; ++i) {
2316 if (pj_sockaddr_has_addr(&cands[i].addr))
2317 addr = &cands[i].addr;
2318 }
2319 }
2320 if (addr) {
2321 pj_sockaddr_cp(&info->sock_info.rtcp_addr_name, addr);
2322 }
2323 }
2324
2325 /* Set remote address originating RTP & RTCP if this transport has
2326 * ICE activated or received any packets.
2327 */
2328 if (tp_ice->use_ice || tp_ice->rtp_src_cnt) {
2329 info->src_rtp_name = tp_ice->rtp_src_addr;
2330 if (tp_ice->use_rtcp_mux)
2331 info->src_rtcp_name = tp_ice->rtp_src_addr;
2332 }
2333 if ((!tp_ice->use_rtcp_mux) &&
2334 (tp_ice->use_ice || tp_ice->rtcp_src_cnt))
2335 {
2336 info->src_rtcp_name = tp_ice->rtcp_src_addr;
2337 }
2338
2339 /* Fill up transport specific info */
2340 if (info->specific_info_cnt < PJ_ARRAY_SIZE(info->spc_info)) {
2341 pjmedia_transport_specific_info *tsi;
2342 pjmedia_ice_transport_info *ii;
2343 unsigned i;
2344
2345 pj_assert(sizeof(*ii) <= sizeof(tsi->buffer));
2346 tsi = &info->spc_info[info->specific_info_cnt++];
2347 tsi->type = PJMEDIA_TRANSPORT_TYPE_ICE;
2348 tsi->cbsize = sizeof(*ii);
2349
2350 ii = (pjmedia_ice_transport_info*) tsi->buffer;
2351 pj_bzero(ii, sizeof(*ii));
2352
2353 ii->active = tp_ice->use_ice;
2354
2355 if (pj_ice_strans_has_sess(tp_ice->ice_st))
2356 ii->role = pj_ice_strans_get_role(tp_ice->ice_st);
2357 else
2358 ii->role = PJ_ICE_SESS_ROLE_UNKNOWN;
2359 ii->sess_state = pj_ice_strans_get_state(tp_ice->ice_st);
2360 ii->comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
2361
2362 for (i=1; i<=ii->comp_cnt && i<=PJ_ARRAY_SIZE(ii->comp); ++i) {
2363 const pj_ice_sess_check *chk;
2364
2365 chk = pj_ice_strans_get_valid_pair(tp_ice->ice_st, i);
2366 if (chk) {
2367 ii->comp[i-1].lcand_type = chk->lcand->type;
2368 pj_sockaddr_cp(&ii->comp[i-1].lcand_addr,
2369 &chk->lcand->addr);
2370 ii->comp[i-1].rcand_type = chk->rcand->type;
2371 pj_sockaddr_cp(&ii->comp[i-1].rcand_addr,
2372 &chk->rcand->addr);
2373 }
2374 }
2375 }
2376
2377 return PJ_SUCCESS;
2378 }
2379
2380
transport_attach(pjmedia_transport * tp,void * stream,const pj_sockaddr_t * rem_addr,const pj_sockaddr_t * rem_rtcp,unsigned addr_len,void (* rtp_cb)(void *,void *,pj_ssize_t),void (* rtcp_cb)(void *,void *,pj_ssize_t))2381 static pj_status_t transport_attach (pjmedia_transport *tp,
2382 void *stream,
2383 const pj_sockaddr_t *rem_addr,
2384 const pj_sockaddr_t *rem_rtcp,
2385 unsigned addr_len,
2386 void (*rtp_cb)(void*,
2387 void*,
2388 pj_ssize_t),
2389 void (*rtcp_cb)(void*,
2390 void*,
2391 pj_ssize_t))
2392 {
2393 pjmedia_transport_attach_param param;
2394
2395 pj_bzero(¶m, sizeof(param));
2396 param.user_data = stream;
2397 pj_sockaddr_cp(¶m.rem_addr, rem_addr);
2398 pj_sockaddr_cp(¶m.rem_rtcp, rem_rtcp);
2399 param.addr_len = addr_len;
2400 param.rtp_cb = rtp_cb;
2401 param.rtcp_cb = rtcp_cb;
2402 return transport_attach2(tp, ¶m);
2403 }
2404
2405
transport_attach2(pjmedia_transport * tp,pjmedia_transport_attach_param * att_param)2406 static pj_status_t transport_attach2 (pjmedia_transport *tp,
2407 pjmedia_transport_attach_param
2408 *att_param)
2409 {
2410 struct transport_ice *tp_ice = (struct transport_ice*)tp;
2411
2412 tp_ice->stream = att_param->user_data;
2413 tp_ice->rtp_cb = att_param->rtp_cb;
2414 tp_ice->rtp_cb2 = att_param->rtp_cb2;
2415 tp_ice->rtcp_cb = att_param->rtcp_cb;
2416
2417 /* Check again if we are multiplexing RTP & RTCP. */
2418 tp_ice->use_rtcp_mux = (pj_sockaddr_has_addr(&att_param->rem_addr) &&
2419 pj_sockaddr_cmp(&att_param->rem_addr,
2420 &att_param->rem_rtcp) == 0);
2421
2422 pj_memcpy(&tp_ice->remote_rtp, &att_param->rem_addr, att_param->addr_len);
2423 pj_memcpy(&tp_ice->remote_rtcp, &att_param->rem_rtcp, att_param->addr_len);
2424 tp_ice->addr_len = att_param->addr_len;
2425
2426 /* Init source RTP & RTCP addresses and counter */
2427 tp_ice->rtp_src_addr = tp_ice->remote_rtp;
2428 pj_bzero(&tp_ice->rtcp_src_addr, sizeof(tp_ice->rtcp_src_addr));
2429 tp_ice->rtp_src_cnt = tp_ice->rtcp_src_cnt = 0;
2430
2431 return PJ_SUCCESS;
2432 }
2433
2434
transport_detach(pjmedia_transport * tp,void * strm)2435 static void transport_detach(pjmedia_transport *tp,
2436 void *strm)
2437 {
2438 struct transport_ice *tp_ice = (struct transport_ice*)tp;
2439
2440 /* TODO: need to solve ticket #460 here */
2441
2442 tp_ice->rtp_cb = NULL;
2443 tp_ice->rtp_cb2 = NULL;
2444 tp_ice->rtcp_cb = NULL;
2445 tp_ice->stream = NULL;
2446
2447 PJ_UNUSED_ARG(strm);
2448 }
2449
2450
transport_send_rtp(pjmedia_transport * tp,const void * pkt,pj_size_t size)2451 static pj_status_t transport_send_rtp(pjmedia_transport *tp,
2452 const void *pkt,
2453 pj_size_t size)
2454 {
2455 struct transport_ice *tp_ice = (struct transport_ice*)tp;
2456 pj_status_t status;
2457
2458 /* Simulate packet lost on TX direction */
2459 if (tp_ice->tx_drop_pct) {
2460 if ((pj_rand() % 100) <= (int)tp_ice->tx_drop_pct) {
2461 PJ_LOG(5,(tp_ice->base.name,
2462 "TX RTP packet dropped because of pkt lost "
2463 "simulation"));
2464 return PJ_SUCCESS;
2465 }
2466 }
2467
2468 status = pj_ice_strans_sendto2(tp_ice->ice_st, 1,
2469 pkt, size, &tp_ice->remote_rtp,
2470 tp_ice->addr_len);
2471 if (status == PJ_EPENDING)
2472 status = PJ_SUCCESS;
2473
2474 return status;
2475 }
2476
2477
transport_send_rtcp(pjmedia_transport * tp,const void * pkt,pj_size_t size)2478 static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
2479 const void *pkt,
2480 pj_size_t size)
2481 {
2482 return transport_send_rtcp2(tp, NULL, 0, pkt, size);
2483 }
2484
transport_send_rtcp2(pjmedia_transport * tp,const pj_sockaddr_t * addr,unsigned addr_len,const void * pkt,pj_size_t size)2485 static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
2486 const pj_sockaddr_t *addr,
2487 unsigned addr_len,
2488 const void *pkt,
2489 pj_size_t size)
2490 {
2491 struct transport_ice *tp_ice = (struct transport_ice*)tp;
2492
2493 if (tp_ice->comp_cnt > 1 || tp_ice->use_rtcp_mux) {
2494 pj_status_t status;
2495 unsigned comp_id = (tp_ice->use_rtcp_mux? 1: 2);
2496
2497 if (addr == NULL) {
2498 addr = &tp_ice->remote_rtcp;
2499 addr_len = pj_sockaddr_get_len(addr);
2500 }
2501
2502 status = pj_ice_strans_sendto2(tp_ice->ice_st, comp_id, pkt, size,
2503 addr, addr_len);
2504 if (status == PJ_EPENDING)
2505 status = PJ_SUCCESS;
2506
2507 return status;
2508 } else {
2509 return PJ_SUCCESS;
2510 }
2511 }
2512
2513
ice_on_rx_data(pj_ice_strans * ice_st,unsigned comp_id,void * pkt,pj_size_t size,const pj_sockaddr_t * src_addr,unsigned src_addr_len)2514 static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
2515 void *pkt, pj_size_t size,
2516 const pj_sockaddr_t *src_addr,
2517 unsigned src_addr_len)
2518 {
2519 struct transport_ice *tp_ice;
2520 pj_bool_t discard = PJ_FALSE;
2521
2522 tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st);
2523 if (!tp_ice) {
2524 /* Destroy on progress */
2525 return;
2526 }
2527
2528 if (comp_id == 1) {
2529 ++tp_ice->rtp_src_cnt;
2530 pj_sockaddr_cp(&tp_ice->rtp_src_addr, src_addr);
2531 } else if (comp_id == 2) {
2532 pj_sockaddr_cp(&tp_ice->rtcp_src_addr, src_addr);
2533 }
2534
2535 if (comp_id==1 && (tp_ice->rtp_cb || tp_ice->rtp_cb2)) {
2536 pj_bool_t rem_switch = PJ_FALSE;
2537
2538 /* Simulate packet lost on RX direction */
2539 if (tp_ice->rx_drop_pct) {
2540 if ((pj_rand() % 100) <= (int)tp_ice->rx_drop_pct) {
2541 PJ_LOG(5,(tp_ice->base.name,
2542 "RX RTP packet dropped because of pkt lost "
2543 "simulation"));
2544 return;
2545 }
2546 }
2547
2548 if (!discard) {
2549 if (tp_ice->rtp_cb2) {
2550 pjmedia_tp_cb_param param;
2551
2552 param.user_data = tp_ice->stream;
2553 param.pkt = pkt;
2554 param.size = size;
2555 param.src_addr = (tp_ice->use_ice? NULL:
2556 (pj_sockaddr_t *)src_addr);
2557 param.rem_switch = PJ_FALSE;
2558 (*tp_ice->rtp_cb2)(¶m);
2559 rem_switch = param.rem_switch;
2560 } else {
2561 (*tp_ice->rtp_cb)(tp_ice->stream, pkt, size);
2562 }
2563 }
2564
2565 #if defined(PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR) && \
2566 (PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR == 1)
2567 if (rem_switch &&
2568 (tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0)
2569 {
2570 char addr_text[PJ_INET6_ADDRSTRLEN+10];
2571
2572 /* Set remote RTP address to source address */
2573 pj_sockaddr_cp(&tp_ice->rtp_src_addr, src_addr);
2574 pj_sockaddr_cp(&tp_ice->remote_rtp, src_addr);
2575 tp_ice->addr_len = pj_sockaddr_get_len(&tp_ice->remote_rtp);
2576
2577 PJ_LOG(4,(tp_ice->base.name,
2578 "Remote RTP address switched to %s",
2579 pj_sockaddr_print(&tp_ice->remote_rtp, addr_text,
2580 sizeof(addr_text), 3)));
2581
2582 if (tp_ice->use_rtcp_mux) {
2583 pj_sockaddr_cp(&tp_ice->remote_rtcp, &tp_ice->remote_rtp);
2584 } else if (!pj_sockaddr_has_addr(&tp_ice->rtcp_src_addr)) {
2585 /* Also update remote RTCP address if actual RTCP source
2586 * address is not heard yet.
2587 */
2588 pj_uint16_t port;
2589
2590 pj_sockaddr_cp(&tp_ice->remote_rtcp, &tp_ice->remote_rtp);
2591
2592 port = (pj_uint16_t)
2593 (pj_sockaddr_get_port(&tp_ice->remote_rtp)+1);
2594 pj_sockaddr_set_port(&tp_ice->remote_rtcp, port);
2595
2596 PJ_LOG(4,(tp_ice->base.name,
2597 "Remote RTCP address switched to predicted "
2598 "address %s",
2599 pj_sockaddr_print(&tp_ice->remote_rtcp,
2600 addr_text,
2601 sizeof(addr_text), 3)));
2602 }
2603 }
2604 #endif
2605
2606 } else if (comp_id==2 && tp_ice->rtcp_cb) {
2607
2608 #if defined(PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR) && \
2609 (PJMEDIA_TRANSPORT_SWITCH_REMOTE_ADDR == 1)
2610 /* Check if RTCP source address is the same as the configured
2611 * remote address, and switch the address when they are
2612 * different.
2613 */
2614 if (!tp_ice->use_ice &&
2615 (tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0)
2616 {
2617 if (pj_sockaddr_cmp(&tp_ice->remote_rtcp, src_addr) == 0) {
2618 tp_ice->rtcp_src_cnt = 0;
2619 } else {
2620 char addr_text[PJ_INET6_ADDRSTRLEN+10];
2621
2622 ++tp_ice->rtcp_src_cnt;
2623 if (tp_ice->rtcp_src_cnt < PJMEDIA_RTCP_NAT_PROBATION_CNT) {
2624 discard = PJ_TRUE;
2625 } else {
2626 tp_ice->rtcp_src_cnt = 0;
2627 pj_sockaddr_cp(&tp_ice->rtcp_src_addr, src_addr);
2628 pj_sockaddr_cp(&tp_ice->remote_rtcp, src_addr);
2629
2630 pj_assert(tp_ice->addr_len==pj_sockaddr_get_len(src_addr));
2631
2632 PJ_LOG(4,(tp_ice->base.name,
2633 "Remote RTCP address switched to %s",
2634 pj_sockaddr_print(&tp_ice->remote_rtcp,
2635 addr_text, sizeof(addr_text),
2636 3)));
2637 }
2638 }
2639 }
2640 #endif
2641
2642 if (!discard)
2643 (*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size);
2644 }
2645
2646 PJ_UNUSED_ARG(src_addr_len);
2647 }
2648
2649
ice_on_ice_complete(pj_ice_strans * ice_st,pj_ice_strans_op op,pj_status_t result)2650 static void ice_on_ice_complete(pj_ice_strans *ice_st,
2651 pj_ice_strans_op op,
2652 pj_status_t result)
2653 {
2654 struct transport_ice *tp_ice;
2655 ice_listener *il;
2656
2657 tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st);
2658 if (!tp_ice) {
2659 /* Destroy on progress */
2660 return;
2661 }
2662
2663 /* Set the flag indicating that local candidate gathering is completed */
2664 if (op == PJ_ICE_STRANS_OP_INIT && result == PJ_SUCCESS)
2665 tp_ice->end_of_cand = PJ_TRUE;
2666
2667 pj_perror(5, tp_ice->base.name, result, "ICE operation complete"
2668 " (op=%d%s)", op,
2669 (op==PJ_ICE_STRANS_OP_INIT? "/initialization" :
2670 (op==PJ_ICE_STRANS_OP_NEGOTIATION? "/negotiation":"")));
2671
2672 /* Notify application */
2673 if (tp_ice->cb.on_ice_complete)
2674 (*tp_ice->cb.on_ice_complete)(&tp_ice->base, op, result);
2675
2676 for (il=tp_ice->listener.next; il!=&tp_ice->listener; il=il->next) {
2677 if (il->cb.on_ice_complete2) {
2678 (*il->cb.on_ice_complete2)(&tp_ice->base, op, result,
2679 il->user_data);
2680 } else if (il->cb.on_ice_complete) {
2681 (*il->cb.on_ice_complete)(&tp_ice->base, op, result);
2682 }
2683 }
2684 }
2685
2686
ice_on_new_candidate(pj_ice_strans * ice_st,const pj_ice_sess_cand * cand,pj_bool_t last)2687 static void ice_on_new_candidate(pj_ice_strans *ice_st,
2688 const pj_ice_sess_cand *cand,
2689 pj_bool_t last)
2690 {
2691 struct transport_ice *tp_ice;
2692 ice_listener *il;
2693
2694 tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st);
2695 if (!tp_ice) {
2696 /* Destroy on progress */
2697 return;
2698 }
2699
2700 /* Notify application */
2701 if (tp_ice->cb.on_new_candidate)
2702 (*tp_ice->cb.on_new_candidate)(&tp_ice->base, cand, last);
2703
2704 for (il=tp_ice->listener.next; il!=&tp_ice->listener; il=il->next) {
2705 if (il->cb.on_new_candidate) {
2706 (*il->cb.on_new_candidate)(&tp_ice->base, cand, last);
2707 }
2708 }
2709 }
2710
2711
2712 /* Simulate lost */
transport_simulate_lost(pjmedia_transport * tp,pjmedia_dir dir,unsigned pct_lost)2713 static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
2714 pjmedia_dir dir,
2715 unsigned pct_lost)
2716 {
2717 struct transport_ice *ice = (struct transport_ice*) tp;
2718
2719 PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL);
2720
2721 if (dir & PJMEDIA_DIR_ENCODING)
2722 ice->tx_drop_pct = pct_lost;
2723
2724 if (dir & PJMEDIA_DIR_DECODING)
2725 ice->rx_drop_pct = pct_lost;
2726
2727 return PJ_SUCCESS;
2728 }
2729
tp_ice_on_destroy(void * arg)2730 static void tp_ice_on_destroy(void *arg)
2731 {
2732 struct transport_ice *tp_ice = (struct transport_ice*)arg;
2733 pj_pool_safe_release(&tp_ice->pool);
2734 }
2735
2736 /*
2737 * Destroy ICE media transport.
2738 */
transport_destroy(pjmedia_transport * tp)2739 static pj_status_t transport_destroy(pjmedia_transport *tp)
2740 {
2741 struct transport_ice *tp_ice = (struct transport_ice*)tp;
2742
2743 /* Reset callback and user data */
2744 pj_bzero(&tp_ice->cb, sizeof(tp_ice->cb));
2745 tp_ice->base.user_data = NULL;
2746 tp_ice->rtp_cb = NULL;
2747 tp_ice->rtp_cb2 = NULL;
2748 tp_ice->rtcp_cb = NULL;
2749
2750 if (tp_ice->ice_st) {
2751 pj_grp_lock_t *grp_lock = pj_ice_strans_get_grp_lock(tp_ice->ice_st);
2752 pj_ice_strans_destroy(tp_ice->ice_st);
2753 tp_ice->ice_st = NULL;
2754 pj_grp_lock_dec_ref(grp_lock);
2755 } else {
2756 tp_ice_on_destroy(tp);
2757 }
2758
2759 return PJ_SUCCESS;
2760 }
2761
2762