Tristan Matthews | 0a329cc | 2013-07-17 13:20:14 -0400 | [diff] [blame] | 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 | }; |
| 48 | |
| 49 | struct transport_ice |
| 50 | { |
| 51 | pjmedia_transport base; |
| 52 | pj_pool_t *pool; |
| 53 | int af; |
| 54 | unsigned options; /**< Transport options. */ |
| 55 | |
| 56 | unsigned comp_cnt; |
| 57 | pj_ice_strans *ice_st; |
| 58 | |
| 59 | pjmedia_ice_cb cb; |
| 60 | unsigned media_option; |
| 61 | |
| 62 | pj_bool_t initial_sdp; |
| 63 | enum oa_role oa_role; /**< Last role in SDP offer/answer */ |
| 64 | struct sdp_state rem_offer_state;/**< Describes the remote offer */ |
| 65 | |
| 66 | void *stream; |
| 67 | pj_sockaddr remote_rtp; |
| 68 | pj_sockaddr remote_rtcp; |
| 69 | unsigned addr_len; /**< Length of addresses. */ |
| 70 | |
| 71 | pj_bool_t use_ice; |
| 72 | pj_sockaddr rtp_src_addr; /**< Actual source RTP address. */ |
| 73 | pj_sockaddr rtcp_src_addr; /**< Actual source RTCP address. */ |
| 74 | unsigned rtp_src_cnt; /**< How many pkt from this addr. */ |
| 75 | unsigned rtcp_src_cnt; /**< How many pkt from this addr. */ |
| 76 | |
| 77 | unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */ |
| 78 | unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */ |
| 79 | |
| 80 | void (*rtp_cb)(void*, |
| 81 | void*, |
| 82 | pj_ssize_t); |
| 83 | void (*rtcp_cb)(void*, |
| 84 | void*, |
| 85 | pj_ssize_t); |
| 86 | }; |
| 87 | |
| 88 | |
| 89 | /* |
| 90 | * These are media transport operations. |
| 91 | */ |
| 92 | static pj_status_t transport_get_info (pjmedia_transport *tp, |
| 93 | pjmedia_transport_info *info); |
| 94 | static pj_status_t transport_attach (pjmedia_transport *tp, |
| 95 | void *user_data, |
| 96 | const pj_sockaddr_t *rem_addr, |
| 97 | const pj_sockaddr_t *rem_rtcp, |
| 98 | unsigned addr_len, |
| 99 | void (*rtp_cb)(void*, |
| 100 | void*, |
| 101 | pj_ssize_t), |
| 102 | void (*rtcp_cb)(void*, |
| 103 | void*, |
| 104 | pj_ssize_t)); |
| 105 | static void transport_detach (pjmedia_transport *tp, |
| 106 | void *strm); |
| 107 | static pj_status_t transport_send_rtp( pjmedia_transport *tp, |
| 108 | const void *pkt, |
| 109 | pj_size_t size); |
| 110 | static pj_status_t transport_send_rtcp(pjmedia_transport *tp, |
| 111 | const void *pkt, |
| 112 | pj_size_t size); |
| 113 | static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, |
| 114 | const pj_sockaddr_t *addr, |
| 115 | unsigned addr_len, |
| 116 | const void *pkt, |
| 117 | pj_size_t size); |
| 118 | static pj_status_t transport_media_create(pjmedia_transport *tp, |
| 119 | pj_pool_t *pool, |
| 120 | unsigned options, |
| 121 | const pjmedia_sdp_session *rem_sdp, |
| 122 | unsigned media_index); |
| 123 | static pj_status_t transport_encode_sdp(pjmedia_transport *tp, |
| 124 | pj_pool_t *tmp_pool, |
| 125 | pjmedia_sdp_session *sdp_local, |
| 126 | const pjmedia_sdp_session *rem_sdp, |
| 127 | unsigned media_index); |
| 128 | static pj_status_t transport_media_start(pjmedia_transport *tp, |
| 129 | pj_pool_t *pool, |
| 130 | const pjmedia_sdp_session *sdp_local, |
| 131 | const pjmedia_sdp_session *rem_sdp, |
| 132 | unsigned media_index); |
| 133 | static pj_status_t transport_media_stop(pjmedia_transport *tp); |
| 134 | static pj_status_t transport_simulate_lost(pjmedia_transport *tp, |
| 135 | pjmedia_dir dir, |
| 136 | unsigned pct_lost); |
| 137 | static pj_status_t transport_destroy (pjmedia_transport *tp); |
| 138 | |
| 139 | /* |
| 140 | * And these are ICE callbacks. |
| 141 | */ |
| 142 | static void ice_on_rx_data(pj_ice_strans *ice_st, |
| 143 | unsigned comp_id, |
| 144 | void *pkt, pj_size_t size, |
| 145 | const pj_sockaddr_t *src_addr, |
| 146 | unsigned src_addr_len); |
| 147 | static void ice_on_ice_complete(pj_ice_strans *ice_st, |
| 148 | pj_ice_strans_op op, |
| 149 | pj_status_t status); |
| 150 | |
| 151 | |
| 152 | static pjmedia_transport_op transport_ice_op = |
| 153 | { |
| 154 | &transport_get_info, |
| 155 | &transport_attach, |
| 156 | &transport_detach, |
| 157 | &transport_send_rtp, |
| 158 | &transport_send_rtcp, |
| 159 | &transport_send_rtcp2, |
| 160 | &transport_media_create, |
| 161 | &transport_encode_sdp, |
| 162 | &transport_media_start, |
| 163 | &transport_media_stop, |
| 164 | &transport_simulate_lost, |
| 165 | &transport_destroy |
| 166 | }; |
| 167 | |
| 168 | static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 }; |
| 169 | static const pj_str_t STR_CANDIDATE = { "candidate", 9}; |
| 170 | static const pj_str_t STR_REM_CAND = { "remote-candidates", 17 }; |
| 171 | static const pj_str_t STR_ICE_LITE = { "ice-lite", 8}; |
| 172 | static const pj_str_t STR_ICE_MISMATCH = { "ice-mismatch", 12}; |
| 173 | static const pj_str_t STR_ICE_UFRAG = { "ice-ufrag", 9 }; |
| 174 | static const pj_str_t STR_ICE_PWD = { "ice-pwd", 7 }; |
| 175 | static const pj_str_t STR_IP4 = { "IP4", 3 }; |
| 176 | static const pj_str_t STR_IP6 = { "IP6", 3 }; |
| 177 | static const pj_str_t STR_RTCP = { "rtcp", 4 }; |
| 178 | static const pj_str_t STR_BANDW_RR = { "RR", 2 }; |
| 179 | static const pj_str_t STR_BANDW_RS = { "RS", 2 }; |
| 180 | |
| 181 | enum { |
| 182 | COMP_RTP = 1, |
| 183 | COMP_RTCP = 2 |
| 184 | }; |
| 185 | |
| 186 | /* |
| 187 | * Create ICE media transport. |
| 188 | */ |
| 189 | PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, |
| 190 | const char *name, |
| 191 | unsigned comp_cnt, |
| 192 | const pj_ice_strans_cfg *cfg, |
| 193 | const pjmedia_ice_cb *cb, |
| 194 | pjmedia_transport **p_tp) |
| 195 | { |
| 196 | return pjmedia_ice_create2(endpt, name, comp_cnt, cfg, cb, 0, p_tp); |
| 197 | } |
| 198 | |
| 199 | /* |
| 200 | * Create ICE media transport. |
| 201 | */ |
| 202 | PJ_DEF(pj_status_t) pjmedia_ice_create2(pjmedia_endpt *endpt, |
| 203 | const char *name, |
| 204 | unsigned comp_cnt, |
| 205 | const pj_ice_strans_cfg *cfg, |
| 206 | const pjmedia_ice_cb *cb, |
| 207 | unsigned options, |
| 208 | pjmedia_transport **p_tp) |
| 209 | { |
| 210 | return pjmedia_ice_create3(endpt, name, comp_cnt, cfg, cb, |
| 211 | options, NULL, p_tp); |
| 212 | } |
| 213 | |
| 214 | /* |
| 215 | * Create ICE media transport. |
| 216 | */ |
| 217 | PJ_DEF(pj_status_t) pjmedia_ice_create3(pjmedia_endpt *endpt, |
| 218 | const char *name, |
| 219 | unsigned comp_cnt, |
| 220 | const pj_ice_strans_cfg *cfg, |
| 221 | const pjmedia_ice_cb *cb, |
| 222 | unsigned options, |
| 223 | void *user_data, |
| 224 | pjmedia_transport **p_tp) |
| 225 | { |
| 226 | pj_pool_t *pool; |
| 227 | pj_ice_strans_cb ice_st_cb; |
| 228 | pj_ice_strans_cfg ice_st_cfg; |
| 229 | struct transport_ice *tp_ice; |
| 230 | pj_status_t status; |
| 231 | |
| 232 | PJ_ASSERT_RETURN(endpt && comp_cnt && cfg && p_tp, PJ_EINVAL); |
| 233 | |
| 234 | /* Create transport instance */ |
| 235 | pool = pjmedia_endpt_create_pool(endpt, name, 512, 512); |
| 236 | tp_ice = PJ_POOL_ZALLOC_T(pool, struct transport_ice); |
| 237 | tp_ice->pool = pool; |
| 238 | tp_ice->af = cfg->af; |
| 239 | tp_ice->options = options; |
| 240 | tp_ice->comp_cnt = comp_cnt; |
| 241 | pj_ansi_strcpy(tp_ice->base.name, pool->obj_name); |
| 242 | tp_ice->base.op = &transport_ice_op; |
| 243 | tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE; |
| 244 | tp_ice->base.user_data = user_data; |
| 245 | tp_ice->initial_sdp = PJ_TRUE; |
| 246 | tp_ice->oa_role = ROLE_NONE; |
| 247 | tp_ice->use_ice = PJ_FALSE; |
| 248 | |
| 249 | pj_memcpy(&ice_st_cfg, cfg, sizeof(pj_ice_strans_cfg)); |
| 250 | if (cb) |
| 251 | pj_memcpy(&tp_ice->cb, cb, sizeof(pjmedia_ice_cb)); |
| 252 | |
| 253 | /* Assign return value first because ICE might call callback |
| 254 | * in create() |
| 255 | */ |
| 256 | *p_tp = &tp_ice->base; |
| 257 | |
| 258 | /* Configure ICE callbacks */ |
| 259 | pj_bzero(&ice_st_cb, sizeof(ice_st_cb)); |
| 260 | ice_st_cb.on_ice_complete = &ice_on_ice_complete; |
| 261 | ice_st_cb.on_rx_data = &ice_on_rx_data; |
| 262 | |
| 263 | /* Configure RTP socket buffer settings, if not set */ |
| 264 | if (ice_st_cfg.comp[COMP_RTP-1].so_rcvbuf_size == 0) { |
| 265 | ice_st_cfg.comp[COMP_RTP-1].so_rcvbuf_size = |
| 266 | PJMEDIA_TRANSPORT_SO_RCVBUF_SIZE; |
| 267 | } |
| 268 | if (ice_st_cfg.comp[COMP_RTP-1].so_sndbuf_size == 0) { |
| 269 | ice_st_cfg.comp[COMP_RTP-1].so_sndbuf_size = |
| 270 | PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE; |
| 271 | } |
| 272 | |
| 273 | /* Create ICE */ |
| 274 | status = pj_ice_strans_create(name, &ice_st_cfg, comp_cnt, tp_ice, |
| 275 | &ice_st_cb, &tp_ice->ice_st); |
| 276 | if (status != PJ_SUCCESS) { |
| 277 | pj_pool_release(pool); |
| 278 | *p_tp = NULL; |
| 279 | return status; |
| 280 | } |
| 281 | |
| 282 | /* Done */ |
| 283 | return PJ_SUCCESS; |
| 284 | } |
| 285 | |
| 286 | /* Disable ICE when SDP from remote doesn't contain a=candidate line */ |
| 287 | static void set_no_ice(struct transport_ice *tp_ice, const char *reason, |
| 288 | pj_status_t err) |
| 289 | { |
| 290 | if (err != PJ_SUCCESS) { |
| 291 | char errmsg[PJ_ERR_MSG_SIZE]; |
| 292 | pj_strerror(err, errmsg, sizeof(errmsg)); |
| 293 | PJ_LOG(4,(tp_ice->base.name, |
| 294 | "Stopping ICE, reason=%s:%s", reason, errmsg)); |
| 295 | } else { |
| 296 | PJ_LOG(4,(tp_ice->base.name, |
| 297 | "Stopping ICE, reason=%s", reason)); |
| 298 | } |
| 299 | |
| 300 | if (tp_ice->ice_st) { |
| 301 | pj_ice_strans_stop_ice(tp_ice->ice_st); |
| 302 | } |
| 303 | |
| 304 | tp_ice->use_ice = PJ_FALSE; |
| 305 | } |
| 306 | |
| 307 | |
| 308 | /* Create SDP candidate attribute */ |
| 309 | static int print_sdp_cand_attr(char *buffer, int max_len, |
| 310 | const pj_ice_sess_cand *cand) |
| 311 | { |
| 312 | char ipaddr[PJ_INET6_ADDRSTRLEN+2]; |
| 313 | int len, len2; |
| 314 | |
| 315 | len = pj_ansi_snprintf( buffer, max_len, |
| 316 | "%.*s %u UDP %u %s %u typ ", |
| 317 | (int)cand->foundation.slen, |
| 318 | cand->foundation.ptr, |
| 319 | (unsigned)cand->comp_id, |
| 320 | cand->prio, |
| 321 | pj_sockaddr_print(&cand->addr, ipaddr, |
| 322 | sizeof(ipaddr), 0), |
| 323 | (unsigned)pj_sockaddr_get_port(&cand->addr)); |
| 324 | if (len < 1 || len >= max_len) |
| 325 | return -1; |
| 326 | |
| 327 | switch (cand->type) { |
| 328 | case PJ_ICE_CAND_TYPE_HOST: |
| 329 | len2 = pj_ansi_snprintf(buffer+len, max_len-len, "host"); |
| 330 | break; |
| 331 | case PJ_ICE_CAND_TYPE_SRFLX: |
| 332 | case PJ_ICE_CAND_TYPE_RELAYED: |
| 333 | case PJ_ICE_CAND_TYPE_PRFLX: |
| 334 | len2 = pj_ansi_snprintf(buffer+len, max_len-len, |
| 335 | "%s raddr %s rport %d", |
| 336 | pj_ice_get_cand_type_name(cand->type), |
| 337 | pj_sockaddr_print(&cand->rel_addr, ipaddr, |
| 338 | sizeof(ipaddr), 0), |
| 339 | (int)pj_sockaddr_get_port(&cand->rel_addr)); |
| 340 | break; |
| 341 | default: |
| 342 | pj_assert(!"Invalid candidate type"); |
| 343 | len2 = -1; |
| 344 | break; |
| 345 | } |
| 346 | if (len2 < 1 || len2 >= max_len) |
| 347 | return -1; |
| 348 | |
| 349 | return len+len2; |
| 350 | } |
| 351 | |
| 352 | |
| 353 | /* Get ice-ufrag and ice-pwd attribute */ |
| 354 | static void get_ice_attr(const pjmedia_sdp_session *rem_sdp, |
| 355 | const pjmedia_sdp_media *rem_m, |
| 356 | const pjmedia_sdp_attr **p_ice_ufrag, |
| 357 | const pjmedia_sdp_attr **p_ice_pwd) |
| 358 | { |
| 359 | pjmedia_sdp_attr *attr; |
| 360 | |
| 361 | /* Find ice-ufrag attribute in media descriptor */ |
| 362 | attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, |
| 363 | &STR_ICE_UFRAG, NULL); |
| 364 | if (attr == NULL) { |
| 365 | /* Find ice-ufrag attribute in session descriptor */ |
| 366 | attr = pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, |
| 367 | &STR_ICE_UFRAG, NULL); |
| 368 | } |
| 369 | *p_ice_ufrag = attr; |
| 370 | |
| 371 | /* Find ice-pwd attribute in media descriptor */ |
| 372 | attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, |
| 373 | &STR_ICE_PWD, NULL); |
| 374 | if (attr == NULL) { |
| 375 | /* Find ice-pwd attribute in session descriptor */ |
| 376 | attr = pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, |
| 377 | &STR_ICE_PWD, NULL); |
| 378 | } |
| 379 | *p_ice_pwd = attr; |
| 380 | } |
| 381 | |
| 382 | |
| 383 | /* Encode and add "a=ice-mismatch" attribute in the SDP */ |
| 384 | static void encode_ice_mismatch(pj_pool_t *sdp_pool, |
| 385 | pjmedia_sdp_session *sdp_local, |
| 386 | unsigned media_index) |
| 387 | { |
| 388 | pjmedia_sdp_attr *attr; |
| 389 | pjmedia_sdp_media *m = sdp_local->media[media_index]; |
| 390 | |
| 391 | attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr); |
| 392 | attr->name = STR_ICE_MISMATCH; |
| 393 | attr->value.slen = 0; |
| 394 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 395 | } |
| 396 | |
| 397 | |
| 398 | /* Encode ICE information in SDP */ |
| 399 | static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice, |
| 400 | pj_pool_t *sdp_pool, |
| 401 | pjmedia_sdp_session *sdp_local, |
| 402 | unsigned media_index, |
| 403 | unsigned comp_cnt, |
| 404 | pj_bool_t restart_session) |
| 405 | { |
| 406 | enum { |
| 407 | ATTR_BUF_LEN = 160, /* Max len of a=candidate attr */ |
| 408 | RATTR_BUF_LEN= 160 /* Max len of a=remote-candidates attr */ |
| 409 | }; |
| 410 | pjmedia_sdp_media *m = sdp_local->media[media_index]; |
| 411 | pj_str_t local_ufrag, local_pwd; |
| 412 | pjmedia_sdp_attr *attr; |
| 413 | pj_status_t status; |
| 414 | |
| 415 | /* Must have a session */ |
| 416 | PJ_ASSERT_RETURN(pj_ice_strans_has_sess(tp_ice->ice_st), PJ_EBUG); |
| 417 | |
| 418 | /* Get ufrag and pwd from current session */ |
| 419 | pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, &local_ufrag, &local_pwd, |
| 420 | NULL, NULL); |
| 421 | |
| 422 | /* The listing of candidates depends on whether ICE has completed |
| 423 | * or not. When ICE has completed: |
| 424 | * |
| 425 | * 9.1.2.2: Existing Media Streams with ICE Completed |
| 426 | * The agent MUST include a candidate attributes for candidates |
| 427 | * matching the default destination for each component of the |
| 428 | * media stream, and MUST NOT include any other candidates. |
| 429 | * |
| 430 | * When ICE has not completed, we shall include all candidates. |
| 431 | * |
| 432 | * Except when we have detected that remote is offering to restart |
| 433 | * the session, in this case we will answer with full ICE SDP and |
| 434 | * new ufrag/pwd pair. |
| 435 | */ |
| 436 | if (!restart_session && pj_ice_strans_sess_is_complete(tp_ice->ice_st) && |
| 437 | pj_ice_strans_get_state(tp_ice->ice_st) != PJ_ICE_STRANS_STATE_FAILED) |
| 438 | { |
| 439 | const pj_ice_sess_check *check; |
| 440 | char *attr_buf; |
| 441 | pjmedia_sdp_conn *conn; |
| 442 | pjmedia_sdp_attr *a_rtcp; |
| 443 | pj_str_t rem_cand; |
| 444 | unsigned comp; |
| 445 | |
| 446 | /* Encode ice-ufrag attribute */ |
| 447 | attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, |
| 448 | &local_ufrag); |
| 449 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 450 | |
| 451 | /* Encode ice-pwd attribute */ |
| 452 | attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, |
| 453 | &local_pwd); |
| 454 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 455 | |
| 456 | /* Prepare buffer */ |
| 457 | attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN); |
| 458 | rem_cand.ptr = (char*) pj_pool_alloc(sdp_pool, RATTR_BUF_LEN); |
| 459 | rem_cand.slen = 0; |
| 460 | |
| 461 | /* 9.1.2.2: Existing Media Streams with ICE Completed |
| 462 | * The default destination for media (i.e., the values of |
| 463 | * the IP addresses and ports in the m and c line used for |
| 464 | * that media stream) MUST be the local candidate from the |
| 465 | * highest priority nominated pair in the valid list for each |
| 466 | * component. |
| 467 | */ |
| 468 | check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, 1); |
| 469 | if (check == NULL) { |
| 470 | pj_assert(!"Shouldn't happen"); |
| 471 | return PJ_EBUG; |
| 472 | } |
| 473 | |
| 474 | /* Override connection line address and media port number */ |
| 475 | conn = m->conn; |
| 476 | if (conn == NULL) |
| 477 | conn = sdp_local->conn; |
| 478 | |
| 479 | conn->addr.ptr = (char*) pj_pool_alloc(sdp_pool, |
| 480 | PJ_INET6_ADDRSTRLEN); |
| 481 | pj_sockaddr_print(&check->lcand->addr, conn->addr.ptr, |
| 482 | PJ_INET6_ADDRSTRLEN, 0); |
| 483 | conn->addr.slen = pj_ansi_strlen(conn->addr.ptr); |
| 484 | m->desc.port = pj_sockaddr_get_port(&check->lcand->addr); |
| 485 | |
| 486 | /* Override address RTCP attribute if it's present */ |
| 487 | if (comp_cnt == 2 && |
| 488 | (check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, |
| 489 | COMP_RTCP)) != NULL && |
| 490 | (a_rtcp = pjmedia_sdp_attr_find(m->attr_count, m->attr, |
| 491 | &STR_RTCP, 0)) != NULL) |
| 492 | { |
| 493 | pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a_rtcp); |
| 494 | |
| 495 | a_rtcp = pjmedia_sdp_attr_create_rtcp(sdp_pool, |
| 496 | &check->lcand->addr); |
| 497 | if (a_rtcp) |
| 498 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, a_rtcp); |
| 499 | } |
| 500 | |
| 501 | /* Encode only candidates matching the default destination |
| 502 | * for each component |
| 503 | */ |
| 504 | for (comp=0; comp < comp_cnt; ++comp) { |
| 505 | int len; |
| 506 | pj_str_t value; |
| 507 | |
| 508 | /* Get valid pair for this component */ |
| 509 | check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, comp+1); |
| 510 | if (check == NULL) |
| 511 | continue; |
| 512 | |
| 513 | /* Print and add local candidate in the pair */ |
| 514 | value.ptr = attr_buf; |
| 515 | value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN, |
| 516 | check->lcand); |
| 517 | if (value.slen < 0) { |
| 518 | pj_assert(!"Not enough attr_buf to print candidate"); |
| 519 | return PJ_EBUG; |
| 520 | } |
| 521 | |
| 522 | attr = pjmedia_sdp_attr_create(sdp_pool, STR_CANDIDATE.ptr, |
| 523 | &value); |
| 524 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 525 | |
| 526 | /* Append to a=remote-candidates attribute */ |
| 527 | if (pj_ice_strans_get_role(tp_ice->ice_st) == |
| 528 | PJ_ICE_SESS_ROLE_CONTROLLING) |
| 529 | { |
| 530 | char rem_addr[PJ_INET6_ADDRSTRLEN]; |
| 531 | |
| 532 | pj_sockaddr_print(&check->rcand->addr, rem_addr, |
| 533 | sizeof(rem_addr), 0); |
| 534 | len = pj_ansi_snprintf( |
| 535 | rem_cand.ptr + rem_cand.slen, |
| 536 | RATTR_BUF_LEN - rem_cand.slen, |
| 537 | "%s%u %s %u", |
| 538 | (rem_cand.slen==0? "" : " "), |
| 539 | comp+1, rem_addr, |
| 540 | pj_sockaddr_get_port(&check->rcand->addr) |
| 541 | ); |
| 542 | if (len < 1 || len >= RATTR_BUF_LEN) { |
| 543 | pj_assert(!"Not enough buffer to print " |
| 544 | "remote-candidates"); |
| 545 | return PJ_EBUG; |
| 546 | } |
| 547 | |
| 548 | rem_cand.slen += len; |
| 549 | } |
| 550 | } |
| 551 | |
| 552 | /* 9.1.2.2: Existing Media Streams with ICE Completed |
| 553 | * In addition, if the agent is controlling, it MUST include |
| 554 | * the a=remote-candidates attribute for each media stream |
| 555 | * whose check list is in the Completed state. The attribute |
| 556 | * contains the remote candidates from the highest priority |
| 557 | * nominated pair in the valid list for each component of that |
| 558 | * media stream. |
| 559 | */ |
| 560 | if (pj_ice_strans_get_role(tp_ice->ice_st) == |
| 561 | PJ_ICE_SESS_ROLE_CONTROLLING) |
| 562 | { |
| 563 | attr = pjmedia_sdp_attr_create(sdp_pool, STR_REM_CAND.ptr, |
| 564 | &rem_cand); |
| 565 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 566 | } |
| 567 | |
| 568 | } else if (pj_ice_strans_has_sess(tp_ice->ice_st) && |
| 569 | pj_ice_strans_get_state(tp_ice->ice_st) != |
| 570 | PJ_ICE_STRANS_STATE_FAILED) |
| 571 | { |
| 572 | /* Encode all candidates to SDP media */ |
| 573 | char *attr_buf; |
| 574 | unsigned comp; |
| 575 | |
| 576 | /* If ICE is not restarted, encode current ICE ufrag/pwd. |
| 577 | * Otherwise generate new one. |
| 578 | */ |
| 579 | if (!restart_session) { |
| 580 | attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, |
| 581 | &local_ufrag); |
| 582 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 583 | |
| 584 | attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, |
| 585 | &local_pwd); |
| 586 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 587 | |
| 588 | } else { |
| 589 | pj_str_t str; |
| 590 | |
| 591 | str.slen = PJ_ICE_UFRAG_LEN; |
| 592 | str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen); |
| 593 | pj_create_random_string(str.ptr, str.slen); |
| 594 | attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &str); |
| 595 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 596 | |
| 597 | str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen); |
| 598 | pj_create_random_string(str.ptr, str.slen); |
| 599 | attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &str); |
| 600 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 601 | } |
| 602 | |
| 603 | /* Create buffer to encode candidates as SDP attribute */ |
| 604 | attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN); |
| 605 | |
| 606 | for (comp=0; comp < comp_cnt; ++comp) { |
| 607 | unsigned cand_cnt; |
| 608 | pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; |
| 609 | unsigned i; |
| 610 | |
| 611 | cand_cnt = PJ_ARRAY_SIZE(cand); |
| 612 | status = pj_ice_strans_enum_cands(tp_ice->ice_st, comp+1, |
| 613 | &cand_cnt, cand); |
| 614 | if (status != PJ_SUCCESS) |
| 615 | return status; |
| 616 | |
| 617 | for (i=0; i<cand_cnt; ++i) { |
| 618 | pj_str_t value; |
| 619 | |
| 620 | value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN, |
| 621 | &cand[i]); |
| 622 | if (value.slen < 0) { |
| 623 | pj_assert(!"Not enough attr_buf to print candidate"); |
| 624 | return PJ_EBUG; |
| 625 | } |
| 626 | |
| 627 | value.ptr = attr_buf; |
| 628 | attr = pjmedia_sdp_attr_create(sdp_pool, |
| 629 | STR_CANDIDATE.ptr, |
| 630 | &value); |
| 631 | pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); |
| 632 | } |
| 633 | } |
| 634 | } else { |
| 635 | /* ICE has failed, application should have terminated this call */ |
| 636 | } |
| 637 | |
| 638 | /* Removing a=rtcp line when there is only one component. */ |
| 639 | if (comp_cnt == 1) { |
| 640 | attr = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_RTCP, NULL); |
| 641 | if (attr) |
| 642 | pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr); |
| 643 | /* If RTCP is not in use, we MUST send b=RS:0 and b=RR:0. */ |
| 644 | pj_assert(m->bandw_count + 2 <= PJ_ARRAY_SIZE(m->bandw)); |
| 645 | if (m->bandw_count + 2 <= PJ_ARRAY_SIZE(m->bandw)) { |
| 646 | m->bandw[m->bandw_count] = PJ_POOL_ZALLOC_T(sdp_pool, |
| 647 | pjmedia_sdp_bandw); |
| 648 | pj_memcpy(&m->bandw[m->bandw_count]->modifier, &STR_BANDW_RS, |
| 649 | sizeof(pj_str_t)); |
| 650 | m->bandw_count++; |
| 651 | m->bandw[m->bandw_count] = PJ_POOL_ZALLOC_T(sdp_pool, |
| 652 | pjmedia_sdp_bandw); |
| 653 | pj_memcpy(&m->bandw[m->bandw_count]->modifier, &STR_BANDW_RR, |
| 654 | sizeof(pj_str_t)); |
| 655 | m->bandw_count++; |
| 656 | } |
| 657 | } |
| 658 | |
| 659 | |
| 660 | return PJ_SUCCESS; |
| 661 | } |
| 662 | |
| 663 | |
| 664 | /* Parse a=candidate line */ |
| 665 | static pj_status_t parse_cand(const char *obj_name, |
| 666 | pj_pool_t *pool, |
| 667 | const pj_str_t *orig_input, |
| 668 | pj_ice_sess_cand *cand) |
| 669 | { |
| 670 | pj_str_t input; |
| 671 | char *token, *host; |
| 672 | int af; |
| 673 | pj_str_t s; |
| 674 | pj_status_t status = PJNATH_EICEINCANDSDP; |
| 675 | |
| 676 | pj_bzero(cand, sizeof(*cand)); |
| 677 | pj_strdup_with_null(pool, &input, orig_input); |
| 678 | |
| 679 | PJ_UNUSED_ARG(obj_name); |
| 680 | |
| 681 | /* Foundation */ |
| 682 | token = strtok(input.ptr, " "); |
| 683 | if (!token) { |
| 684 | TRACE__((obj_name, "Expecting ICE foundation in candidate")); |
| 685 | goto on_return; |
| 686 | } |
| 687 | pj_strdup2(pool, &cand->foundation, token); |
| 688 | |
| 689 | /* Component ID */ |
| 690 | token = strtok(NULL, " "); |
| 691 | if (!token) { |
| 692 | TRACE__((obj_name, "Expecting ICE component ID in candidate")); |
| 693 | goto on_return; |
| 694 | } |
| 695 | cand->comp_id = (pj_uint8_t) atoi(token); |
| 696 | |
| 697 | /* Transport */ |
| 698 | token = strtok(NULL, " "); |
| 699 | if (!token) { |
| 700 | TRACE__((obj_name, "Expecting ICE transport in candidate")); |
| 701 | goto on_return; |
| 702 | } |
| 703 | if (pj_ansi_stricmp(token, "UDP") != 0) { |
| 704 | TRACE__((obj_name, |
| 705 | "Expecting ICE UDP transport only in candidate")); |
| 706 | goto on_return; |
| 707 | } |
| 708 | |
| 709 | /* Priority */ |
| 710 | token = strtok(NULL, " "); |
| 711 | if (!token) { |
| 712 | TRACE__((obj_name, "Expecting ICE priority in candidate")); |
| 713 | goto on_return; |
| 714 | } |
| 715 | cand->prio = atoi(token); |
| 716 | |
| 717 | /* Host */ |
| 718 | host = strtok(NULL, " "); |
| 719 | if (!host) { |
| 720 | TRACE__((obj_name, "Expecting ICE host in candidate")); |
| 721 | goto on_return; |
| 722 | } |
| 723 | /* Detect address family */ |
| 724 | if (pj_ansi_strchr(host, ':')) |
| 725 | af = pj_AF_INET6(); |
| 726 | else |
| 727 | af = pj_AF_INET(); |
| 728 | /* Assign address */ |
| 729 | if (pj_sockaddr_init(af, &cand->addr, pj_cstr(&s, host), 0)) { |
| 730 | TRACE__((obj_name, "Invalid ICE candidate address")); |
| 731 | goto on_return; |
| 732 | } |
| 733 | |
| 734 | /* Port */ |
| 735 | token = strtok(NULL, " "); |
| 736 | if (!token) { |
| 737 | TRACE__((obj_name, "Expecting ICE port number in candidate")); |
| 738 | goto on_return; |
| 739 | } |
| 740 | pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)atoi(token)); |
| 741 | |
| 742 | /* typ */ |
| 743 | token = strtok(NULL, " "); |
| 744 | if (!token) { |
| 745 | TRACE__((obj_name, "Expecting ICE \"typ\" in candidate")); |
| 746 | goto on_return; |
| 747 | } |
| 748 | if (pj_ansi_stricmp(token, "typ") != 0) { |
| 749 | TRACE__((obj_name, "Expecting ICE \"typ\" in candidate")); |
| 750 | goto on_return; |
| 751 | } |
| 752 | |
| 753 | /* candidate type */ |
| 754 | token = strtok(NULL, " "); |
| 755 | if (!token) { |
| 756 | TRACE__((obj_name, "Expecting ICE candidate type in candidate")); |
| 757 | goto on_return; |
| 758 | } |
| 759 | |
| 760 | if (pj_ansi_stricmp(token, "host") == 0) { |
| 761 | cand->type = PJ_ICE_CAND_TYPE_HOST; |
| 762 | |
| 763 | } else if (pj_ansi_stricmp(token, "srflx") == 0) { |
| 764 | cand->type = PJ_ICE_CAND_TYPE_SRFLX; |
| 765 | |
| 766 | } else if (pj_ansi_stricmp(token, "relay") == 0) { |
| 767 | cand->type = PJ_ICE_CAND_TYPE_RELAYED; |
| 768 | |
| 769 | } else if (pj_ansi_stricmp(token, "prflx") == 0) { |
| 770 | cand->type = PJ_ICE_CAND_TYPE_PRFLX; |
| 771 | |
| 772 | } else { |
| 773 | PJ_LOG(5,(obj_name, "Invalid ICE candidate type %s in candidate", |
| 774 | token)); |
| 775 | goto on_return; |
| 776 | } |
| 777 | |
| 778 | status = PJ_SUCCESS; |
| 779 | |
| 780 | on_return: |
| 781 | return status; |
| 782 | } |
| 783 | |
| 784 | |
| 785 | /* Create initial SDP offer */ |
| 786 | static pj_status_t create_initial_offer(struct transport_ice *tp_ice, |
| 787 | pj_pool_t *sdp_pool, |
| 788 | pjmedia_sdp_session *loc_sdp, |
| 789 | unsigned media_index) |
| 790 | { |
| 791 | pj_status_t status; |
| 792 | |
| 793 | /* Encode ICE in SDP */ |
| 794 | status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, |
| 795 | tp_ice->comp_cnt, PJ_FALSE); |
| 796 | if (status != PJ_SUCCESS) { |
| 797 | set_no_ice(tp_ice, "Error encoding SDP answer", status); |
| 798 | return status; |
| 799 | } |
| 800 | |
| 801 | return PJ_SUCCESS; |
| 802 | } |
| 803 | |
| 804 | |
| 805 | /* Verify incoming offer */ |
| 806 | static pj_status_t verify_ice_sdp(struct transport_ice *tp_ice, |
| 807 | pj_pool_t *tmp_pool, |
| 808 | const pjmedia_sdp_session *rem_sdp, |
| 809 | unsigned media_index, |
| 810 | pj_ice_sess_role current_ice_role, |
| 811 | struct sdp_state *sdp_state) |
| 812 | { |
| 813 | const pjmedia_sdp_media *rem_m; |
| 814 | const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; |
| 815 | const pjmedia_sdp_conn *rem_conn; |
| 816 | pj_bool_t comp1_found=PJ_FALSE, comp2_found=PJ_FALSE, has_rtcp=PJ_FALSE; |
| 817 | pj_sockaddr rem_conn_addr, rtcp_addr; |
| 818 | unsigned i; |
| 819 | pj_status_t status; |
| 820 | |
| 821 | rem_m = rem_sdp->media[media_index]; |
| 822 | |
| 823 | /* Get the "ice-ufrag" and "ice-pwd" attributes */ |
| 824 | get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); |
| 825 | |
| 826 | /* If "ice-ufrag" or "ice-pwd" are not found, disable ICE */ |
| 827 | if (ufrag_attr==NULL || pwd_attr==NULL) { |
| 828 | sdp_state->match_comp_cnt = 0; |
| 829 | return PJ_SUCCESS; |
| 830 | } |
| 831 | |
| 832 | /* Verify that default target for each component matches one of the |
| 833 | * candidate for the component. Otherwise stop ICE with ICE ice_mismatch |
| 834 | * error. |
| 835 | */ |
| 836 | |
| 837 | /* Component 1 is the c= line */ |
| 838 | rem_conn = rem_m->conn; |
| 839 | if (rem_conn == NULL) |
| 840 | rem_conn = rem_sdp->conn; |
| 841 | if (!rem_conn) |
| 842 | return PJMEDIA_SDP_EMISSINGCONN; |
| 843 | |
| 844 | /* Verify address family matches */ |
| 845 | if ((tp_ice->af==pj_AF_INET() && |
| 846 | pj_strcmp(&rem_conn->addr_type, &STR_IP4)!=0) || |
| 847 | (tp_ice->af==pj_AF_INET6() && |
| 848 | pj_strcmp(&rem_conn->addr_type, &STR_IP6)!=0)) |
| 849 | { |
| 850 | return PJMEDIA_SDP_ETPORTNOTEQUAL; |
| 851 | } |
| 852 | |
| 853 | /* Assign remote connection address */ |
| 854 | status = pj_sockaddr_init(tp_ice->af, &rem_conn_addr, &rem_conn->addr, |
| 855 | (pj_uint16_t)rem_m->desc.port); |
| 856 | if (status != PJ_SUCCESS) |
| 857 | return status; |
| 858 | |
| 859 | if (tp_ice->comp_cnt > 1) { |
| 860 | const pjmedia_sdp_attr *attr; |
| 861 | |
| 862 | /* Get default RTCP candidate from a=rtcp line, if present, otherwise |
| 863 | * calculate default RTCP candidate from default RTP target. |
| 864 | */ |
| 865 | attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, |
| 866 | &STR_RTCP, NULL); |
| 867 | has_rtcp = (attr != NULL); |
| 868 | |
| 869 | if (attr) { |
| 870 | pjmedia_sdp_rtcp_attr rtcp_attr; |
| 871 | |
| 872 | status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp_attr); |
| 873 | if (status != PJ_SUCCESS) { |
| 874 | /* Error parsing a=rtcp attribute */ |
| 875 | return status; |
| 876 | } |
| 877 | |
| 878 | if (rtcp_attr.addr.slen) { |
| 879 | /* Verify address family matches */ |
| 880 | if ((tp_ice->af==pj_AF_INET() && |
| 881 | pj_strcmp(&rtcp_attr.addr_type, &STR_IP4)!=0) || |
| 882 | (tp_ice->af==pj_AF_INET6() && |
| 883 | pj_strcmp(&rtcp_attr.addr_type, &STR_IP6)!=0)) |
| 884 | { |
| 885 | return PJMEDIA_SDP_ETPORTNOTEQUAL; |
| 886 | } |
| 887 | |
| 888 | /* Assign RTCP address */ |
| 889 | status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, |
| 890 | &rtcp_attr.addr, |
| 891 | (pj_uint16_t)rtcp_attr.port); |
| 892 | if (status != PJ_SUCCESS) { |
| 893 | return PJMEDIA_SDP_EINRTCP; |
| 894 | } |
| 895 | } else { |
| 896 | /* Assign RTCP address */ |
| 897 | status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, |
| 898 | NULL, |
| 899 | (pj_uint16_t)rtcp_attr.port); |
| 900 | if (status != PJ_SUCCESS) { |
| 901 | return PJMEDIA_SDP_EINRTCP; |
| 902 | } |
| 903 | pj_sockaddr_copy_addr(&rtcp_addr, &rem_conn_addr); |
| 904 | } |
| 905 | } else { |
| 906 | unsigned rtcp_port; |
| 907 | |
| 908 | rtcp_port = pj_sockaddr_get_port(&rem_conn_addr) + 1; |
| 909 | pj_sockaddr_cp(&rtcp_addr, &rem_conn_addr); |
| 910 | pj_sockaddr_set_port(&rtcp_addr, (pj_uint16_t)rtcp_port); |
| 911 | } |
| 912 | } |
| 913 | |
| 914 | /* Find the default addresses in a=candidate attributes. |
| 915 | */ |
| 916 | for (i=0; i<rem_m->attr_count; ++i) { |
| 917 | pj_ice_sess_cand cand; |
| 918 | |
| 919 | if (pj_strcmp(&rem_m->attr[i]->name, &STR_CANDIDATE)!=0) |
| 920 | continue; |
| 921 | |
| 922 | status = parse_cand(tp_ice->base.name, tmp_pool, |
| 923 | &rem_m->attr[i]->value, &cand); |
| 924 | if (status != PJ_SUCCESS) { |
| 925 | PJ_LOG(4,(tp_ice->base.name, |
| 926 | "Error in parsing SDP candidate attribute '%.*s', " |
| 927 | "candidate is ignored", |
| 928 | (int)rem_m->attr[i]->value.slen, |
| 929 | rem_m->attr[i]->value.ptr)); |
| 930 | continue; |
| 931 | } |
| 932 | |
| 933 | if (!comp1_found && cand.comp_id==COMP_RTP && |
| 934 | pj_sockaddr_cmp(&rem_conn_addr, &cand.addr)==0) |
| 935 | { |
| 936 | comp1_found = PJ_TRUE; |
| 937 | } else if (!comp2_found && cand.comp_id==COMP_RTCP && |
| 938 | pj_sockaddr_cmp(&rtcp_addr, &cand.addr)==0) |
| 939 | { |
| 940 | comp2_found = PJ_TRUE; |
| 941 | } |
| 942 | |
| 943 | if (cand.comp_id == COMP_RTCP) |
| 944 | has_rtcp = PJ_TRUE; |
| 945 | |
| 946 | if (comp1_found && (comp2_found || tp_ice->comp_cnt==1)) |
| 947 | break; |
| 948 | } |
| 949 | |
| 950 | /* Check matched component count and ice_mismatch */ |
| 951 | if (comp1_found && (tp_ice->comp_cnt==1 || !has_rtcp)) { |
| 952 | sdp_state->match_comp_cnt = 1; |
| 953 | sdp_state->ice_mismatch = PJ_FALSE; |
| 954 | } else if (comp1_found && comp2_found) { |
| 955 | sdp_state->match_comp_cnt = 2; |
| 956 | sdp_state->ice_mismatch = PJ_FALSE; |
| 957 | } else { |
| 958 | sdp_state->match_comp_cnt = (tp_ice->comp_cnt > 1 && has_rtcp)? 2 : 1; |
| 959 | sdp_state->ice_mismatch = PJ_TRUE; |
| 960 | } |
| 961 | |
| 962 | |
| 963 | /* Detect remote restarting session */ |
| 964 | if (pj_ice_strans_has_sess(tp_ice->ice_st) && |
| 965 | (pj_ice_strans_sess_is_running(tp_ice->ice_st) || |
| 966 | pj_ice_strans_sess_is_complete(tp_ice->ice_st))) |
| 967 | { |
| 968 | pj_str_t rem_run_ufrag, rem_run_pwd; |
| 969 | pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, NULL, NULL, |
| 970 | &rem_run_ufrag, &rem_run_pwd); |
| 971 | if (pj_strcmp(&ufrag_attr->value, &rem_run_ufrag) || |
| 972 | pj_strcmp(&pwd_attr->value, &rem_run_pwd)) |
| 973 | { |
| 974 | /* Remote offers to restart ICE */ |
| 975 | sdp_state->ice_restart = PJ_TRUE; |
| 976 | } else { |
| 977 | sdp_state->ice_restart = PJ_FALSE; |
| 978 | } |
| 979 | } else { |
| 980 | sdp_state->ice_restart = PJ_FALSE; |
| 981 | } |
| 982 | |
| 983 | /* Detect our role */ |
| 984 | if (current_ice_role==PJ_ICE_SESS_ROLE_CONTROLLING) { |
| 985 | sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; |
| 986 | } else { |
| 987 | if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, |
| 988 | &STR_ICE_LITE, NULL) != NULL) |
| 989 | { |
| 990 | /* Remote is ICE Lite */ |
| 991 | sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; |
| 992 | } else { |
| 993 | sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLED; |
| 994 | } |
| 995 | } |
| 996 | |
| 997 | PJ_LOG(4,(tp_ice->base.name, |
| 998 | "Processing SDP: support ICE=%u, common comp_cnt=%u, " |
| 999 | "ice_mismatch=%u, ice_restart=%u, local_role=%s", |
| 1000 | (sdp_state->match_comp_cnt != 0), |
| 1001 | sdp_state->match_comp_cnt, |
| 1002 | sdp_state->ice_mismatch, |
| 1003 | sdp_state->ice_restart, |
| 1004 | pj_ice_sess_role_name(sdp_state->local_role))); |
| 1005 | |
| 1006 | return PJ_SUCCESS; |
| 1007 | |
| 1008 | } |
| 1009 | |
| 1010 | |
| 1011 | /* Verify incoming offer and create initial answer */ |
| 1012 | static pj_status_t create_initial_answer(struct transport_ice *tp_ice, |
| 1013 | pj_pool_t *sdp_pool, |
| 1014 | pjmedia_sdp_session *loc_sdp, |
| 1015 | const pjmedia_sdp_session *rem_sdp, |
| 1016 | unsigned media_index) |
| 1017 | { |
| 1018 | const pjmedia_sdp_media *rem_m = rem_sdp->media[media_index]; |
| 1019 | pj_status_t status; |
| 1020 | |
| 1021 | /* Check if media is removed (just in case) */ |
| 1022 | if (rem_m->desc.port == 0) { |
| 1023 | return PJ_SUCCESS; |
| 1024 | } |
| 1025 | |
| 1026 | /* Verify the offer */ |
| 1027 | status = verify_ice_sdp(tp_ice, sdp_pool, rem_sdp, media_index, |
| 1028 | PJ_ICE_SESS_ROLE_CONTROLLED, |
| 1029 | &tp_ice->rem_offer_state); |
| 1030 | if (status != PJ_SUCCESS) { |
| 1031 | set_no_ice(tp_ice, "Invalid SDP offer", status); |
| 1032 | return status; |
| 1033 | } |
| 1034 | |
| 1035 | /* Does remote support ICE? */ |
| 1036 | if (tp_ice->rem_offer_state.match_comp_cnt==0) { |
| 1037 | set_no_ice(tp_ice, "No ICE found in SDP offer", PJ_SUCCESS); |
| 1038 | return PJ_SUCCESS; |
| 1039 | } |
| 1040 | |
| 1041 | /* ICE ice_mismatch? */ |
| 1042 | if (tp_ice->rem_offer_state.ice_mismatch) { |
| 1043 | set_no_ice(tp_ice, "ICE ice_mismatch in remote offer", PJ_SUCCESS); |
| 1044 | encode_ice_mismatch(sdp_pool, loc_sdp, media_index); |
| 1045 | return PJ_SUCCESS; |
| 1046 | } |
| 1047 | |
| 1048 | /* Encode ICE in SDP */ |
| 1049 | status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, |
| 1050 | tp_ice->rem_offer_state.match_comp_cnt, |
| 1051 | PJ_FALSE); |
| 1052 | if (status != PJ_SUCCESS) { |
| 1053 | set_no_ice(tp_ice, "Error encoding SDP answer", status); |
| 1054 | return status; |
| 1055 | } |
| 1056 | |
| 1057 | return PJ_SUCCESS; |
| 1058 | } |
| 1059 | |
| 1060 | |
| 1061 | /* Create subsequent SDP offer */ |
| 1062 | static pj_status_t create_subsequent_offer(struct transport_ice *tp_ice, |
| 1063 | pj_pool_t *sdp_pool, |
| 1064 | pjmedia_sdp_session *loc_sdp, |
| 1065 | unsigned media_index) |
| 1066 | { |
| 1067 | unsigned comp_cnt; |
| 1068 | |
| 1069 | if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) { |
| 1070 | /* We don't have ICE */ |
| 1071 | return PJ_SUCCESS; |
| 1072 | } |
| 1073 | |
| 1074 | comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st); |
| 1075 | return encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, |
| 1076 | comp_cnt, PJ_FALSE); |
| 1077 | } |
| 1078 | |
| 1079 | |
| 1080 | /* Create subsequent SDP answer */ |
| 1081 | static pj_status_t create_subsequent_answer(struct transport_ice *tp_ice, |
| 1082 | pj_pool_t *sdp_pool, |
| 1083 | pjmedia_sdp_session *loc_sdp, |
| 1084 | const pjmedia_sdp_session *rem_sdp, |
| 1085 | unsigned media_index) |
| 1086 | { |
| 1087 | pj_status_t status; |
| 1088 | |
| 1089 | /* We have a session */ |
| 1090 | status = verify_ice_sdp(tp_ice, sdp_pool, rem_sdp, media_index, |
| 1091 | PJ_ICE_SESS_ROLE_CONTROLLED, |
| 1092 | &tp_ice->rem_offer_state); |
| 1093 | if (status != PJ_SUCCESS) { |
| 1094 | /* Something wrong with the offer */ |
| 1095 | return status; |
| 1096 | } |
| 1097 | |
| 1098 | if (pj_ice_strans_has_sess(tp_ice->ice_st)) { |
| 1099 | /* |
| 1100 | * Received subsequent offer while we have ICE active. |
| 1101 | */ |
| 1102 | |
| 1103 | if (tp_ice->rem_offer_state.match_comp_cnt == 0) { |
| 1104 | /* Remote no longer offers ICE */ |
| 1105 | return PJ_SUCCESS; |
| 1106 | } |
| 1107 | |
| 1108 | if (tp_ice->rem_offer_state.ice_mismatch) { |
| 1109 | encode_ice_mismatch(sdp_pool, loc_sdp, media_index); |
| 1110 | return PJ_SUCCESS; |
| 1111 | } |
| 1112 | |
| 1113 | status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, |
| 1114 | tp_ice->rem_offer_state.match_comp_cnt, |
| 1115 | tp_ice->rem_offer_state.ice_restart); |
| 1116 | if (status != PJ_SUCCESS) |
| 1117 | return status; |
| 1118 | |
| 1119 | /* Done */ |
| 1120 | |
| 1121 | } else { |
| 1122 | /* |
| 1123 | * Received subsequent offer while we DON'T have ICE active. |
| 1124 | */ |
| 1125 | |
| 1126 | if (tp_ice->rem_offer_state.match_comp_cnt == 0) { |
| 1127 | /* Remote does not support ICE */ |
| 1128 | return PJ_SUCCESS; |
| 1129 | } |
| 1130 | |
| 1131 | if (tp_ice->rem_offer_state.ice_mismatch) { |
| 1132 | encode_ice_mismatch(sdp_pool, loc_sdp, media_index); |
| 1133 | return PJ_SUCCESS; |
| 1134 | } |
| 1135 | |
| 1136 | /* Looks like now remote is offering ICE, so we need to create |
| 1137 | * ICE session now. |
| 1138 | */ |
| 1139 | status = pj_ice_strans_init_ice(tp_ice->ice_st, |
| 1140 | PJ_ICE_SESS_ROLE_CONTROLLED, |
| 1141 | NULL, NULL); |
| 1142 | if (status != PJ_SUCCESS) { |
| 1143 | /* Fail to create new ICE session */ |
| 1144 | return status; |
| 1145 | } |
| 1146 | |
| 1147 | status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, |
| 1148 | tp_ice->rem_offer_state.match_comp_cnt, |
| 1149 | tp_ice->rem_offer_state.ice_restart); |
| 1150 | if (status != PJ_SUCCESS) |
| 1151 | return status; |
| 1152 | |
| 1153 | /* Done */ |
| 1154 | } |
| 1155 | |
| 1156 | return PJ_SUCCESS; |
| 1157 | } |
| 1158 | |
| 1159 | |
| 1160 | /* |
| 1161 | * For both UAC and UAS, pass in the SDP before sending it to remote. |
| 1162 | * This will add ICE attributes to the SDP. |
| 1163 | */ |
| 1164 | static pj_status_t transport_media_create(pjmedia_transport *tp, |
| 1165 | pj_pool_t *sdp_pool, |
| 1166 | unsigned options, |
| 1167 | const pjmedia_sdp_session *rem_sdp, |
| 1168 | unsigned media_index) |
| 1169 | { |
| 1170 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1171 | pj_ice_sess_role ice_role; |
| 1172 | pj_status_t status; |
| 1173 | |
| 1174 | PJ_UNUSED_ARG(media_index); |
| 1175 | PJ_UNUSED_ARG(sdp_pool); |
| 1176 | |
| 1177 | tp_ice->media_option = options; |
| 1178 | tp_ice->oa_role = ROLE_NONE; |
| 1179 | tp_ice->initial_sdp = PJ_TRUE; |
| 1180 | |
| 1181 | /* Init ICE, the initial role is set now based on availability of |
| 1182 | * rem_sdp, but it will be checked again later. |
| 1183 | */ |
| 1184 | ice_role = (rem_sdp==NULL ? PJ_ICE_SESS_ROLE_CONTROLLING : |
| 1185 | PJ_ICE_SESS_ROLE_CONTROLLED); |
| 1186 | status = pj_ice_strans_init_ice(tp_ice->ice_st, ice_role, NULL, NULL); |
| 1187 | |
| 1188 | /* Done */ |
| 1189 | return status; |
| 1190 | } |
| 1191 | |
| 1192 | |
| 1193 | static pj_status_t transport_encode_sdp(pjmedia_transport *tp, |
| 1194 | pj_pool_t *sdp_pool, |
| 1195 | pjmedia_sdp_session *sdp_local, |
| 1196 | const pjmedia_sdp_session *rem_sdp, |
| 1197 | unsigned media_index) |
| 1198 | { |
| 1199 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1200 | pj_status_t status; |
| 1201 | |
| 1202 | /* Validate media transport */ |
| 1203 | /* This transport only support RTP/AVP transport, unless if |
| 1204 | * transport checking is disabled |
| 1205 | */ |
| 1206 | if ((tp_ice->media_option & PJMEDIA_TPMED_NO_TRANSPORT_CHECKING) == 0) { |
| 1207 | pjmedia_sdp_media *loc_m, *rem_m; |
| 1208 | |
| 1209 | rem_m = rem_sdp? rem_sdp->media[media_index] : NULL; |
| 1210 | loc_m = sdp_local->media[media_index]; |
| 1211 | |
| 1212 | if (pj_stricmp(&loc_m->desc.transport, &STR_RTP_AVP) || |
| 1213 | (rem_m && pj_stricmp(&rem_m->desc.transport, &STR_RTP_AVP))) |
| 1214 | { |
| 1215 | pjmedia_sdp_media_deactivate(sdp_pool, loc_m); |
| 1216 | return PJMEDIA_SDP_EINPROTO; |
| 1217 | } |
| 1218 | } |
| 1219 | |
| 1220 | if (tp_ice->initial_sdp) { |
| 1221 | if (rem_sdp) { |
| 1222 | status = create_initial_answer(tp_ice, sdp_pool, sdp_local, |
| 1223 | rem_sdp, media_index); |
| 1224 | } else { |
| 1225 | status = create_initial_offer(tp_ice, sdp_pool, sdp_local, |
| 1226 | media_index); |
| 1227 | } |
| 1228 | } else { |
| 1229 | if (rem_sdp) { |
| 1230 | status = create_subsequent_answer(tp_ice, sdp_pool, sdp_local, |
| 1231 | rem_sdp, media_index); |
| 1232 | } else { |
| 1233 | status = create_subsequent_offer(tp_ice, sdp_pool, sdp_local, |
| 1234 | media_index); |
| 1235 | } |
| 1236 | } |
| 1237 | |
| 1238 | if (status==PJ_SUCCESS) { |
| 1239 | if (rem_sdp) |
| 1240 | tp_ice->oa_role = ROLE_ANSWERER; |
| 1241 | else |
| 1242 | tp_ice->oa_role = ROLE_OFFERER; |
| 1243 | } |
| 1244 | |
| 1245 | return status; |
| 1246 | } |
| 1247 | |
| 1248 | |
| 1249 | /* Start ICE session with the specified remote SDP */ |
| 1250 | static pj_status_t start_ice(struct transport_ice *tp_ice, |
| 1251 | pj_pool_t *tmp_pool, |
| 1252 | const pjmedia_sdp_session *rem_sdp, |
| 1253 | unsigned media_index) |
| 1254 | { |
| 1255 | pjmedia_sdp_media *rem_m = rem_sdp->media[media_index]; |
| 1256 | const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; |
| 1257 | pj_ice_sess_cand *cand; |
| 1258 | unsigned i, cand_cnt; |
| 1259 | pj_status_t status; |
| 1260 | |
| 1261 | get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); |
| 1262 | |
| 1263 | /* Allocate candidate array */ |
| 1264 | cand = (pj_ice_sess_cand*) |
| 1265 | pj_pool_calloc(tmp_pool, PJ_ICE_MAX_CAND, |
| 1266 | sizeof(pj_ice_sess_cand)); |
| 1267 | |
| 1268 | /* Get all candidates in the media */ |
| 1269 | cand_cnt = 0; |
| 1270 | for (i=0; i<rem_m->attr_count && cand_cnt < PJ_ICE_MAX_CAND; ++i) { |
| 1271 | pjmedia_sdp_attr *attr; |
| 1272 | |
| 1273 | attr = rem_m->attr[i]; |
| 1274 | |
| 1275 | if (pj_strcmp(&attr->name, &STR_CANDIDATE)!=0) |
| 1276 | continue; |
| 1277 | |
| 1278 | /* Parse candidate */ |
| 1279 | status = parse_cand(tp_ice->base.name, tmp_pool, &attr->value, |
| 1280 | &cand[cand_cnt]); |
| 1281 | if (status != PJ_SUCCESS) { |
| 1282 | PJ_LOG(4,(tp_ice->base.name, |
| 1283 | "Error in parsing SDP candidate attribute '%.*s', " |
| 1284 | "candidate is ignored", |
| 1285 | (int)attr->value.slen, attr->value.ptr)); |
| 1286 | continue; |
| 1287 | } |
| 1288 | |
| 1289 | cand_cnt++; |
| 1290 | } |
| 1291 | |
| 1292 | /* Start ICE */ |
| 1293 | return pj_ice_strans_start_ice(tp_ice->ice_st, &ufrag_attr->value, |
| 1294 | &pwd_attr->value, cand_cnt, cand); |
| 1295 | } |
| 1296 | |
| 1297 | |
| 1298 | /* |
| 1299 | * Start ICE checks when both offer and answer have been negotiated |
| 1300 | * by SDP negotiator. |
| 1301 | */ |
| 1302 | static pj_status_t transport_media_start(pjmedia_transport *tp, |
| 1303 | pj_pool_t *tmp_pool, |
| 1304 | const pjmedia_sdp_session *sdp_local, |
| 1305 | const pjmedia_sdp_session *rem_sdp, |
| 1306 | unsigned media_index) |
| 1307 | { |
| 1308 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1309 | pjmedia_sdp_media *rem_m; |
| 1310 | enum oa_role current_oa_role; |
| 1311 | pj_bool_t initial_oa; |
| 1312 | pj_status_t status; |
| 1313 | |
| 1314 | PJ_ASSERT_RETURN(tp && tmp_pool && rem_sdp, PJ_EINVAL); |
| 1315 | PJ_ASSERT_RETURN(media_index < rem_sdp->media_count, PJ_EINVAL); |
| 1316 | |
| 1317 | rem_m = rem_sdp->media[media_index]; |
| 1318 | |
| 1319 | initial_oa = tp_ice->initial_sdp; |
| 1320 | current_oa_role = tp_ice->oa_role; |
| 1321 | |
| 1322 | /* SDP has been negotiated */ |
| 1323 | tp_ice->initial_sdp = PJ_FALSE; |
| 1324 | tp_ice->oa_role = ROLE_NONE; |
| 1325 | |
| 1326 | /* Nothing to do if we don't have ICE session */ |
| 1327 | if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) { |
| 1328 | return PJ_SUCCESS; |
| 1329 | } |
| 1330 | |
| 1331 | /* Special case for Session Timer. The re-INVITE for session refresh |
| 1332 | * doesn't call transport_encode_sdp(), causing current_oa_role to |
| 1333 | * be set to ROLE_NONE. This is a workaround. |
| 1334 | */ |
| 1335 | if (current_oa_role == ROLE_NONE) { |
| 1336 | current_oa_role = ROLE_OFFERER; |
| 1337 | } |
| 1338 | |
| 1339 | /* Processing depends on the offer/answer role */ |
| 1340 | if (current_oa_role == ROLE_OFFERER) { |
| 1341 | /* |
| 1342 | * We are offerer. So this will be the first time we see the |
| 1343 | * remote's SDP. |
| 1344 | */ |
| 1345 | struct sdp_state answer_state; |
| 1346 | |
| 1347 | /* Verify the answer */ |
| 1348 | status = verify_ice_sdp(tp_ice, tmp_pool, rem_sdp, media_index, |
| 1349 | PJ_ICE_SESS_ROLE_CONTROLLING, &answer_state); |
| 1350 | if (status != PJ_SUCCESS) { |
| 1351 | /* Something wrong in the SDP answer */ |
| 1352 | set_no_ice(tp_ice, "Invalid remote SDP answer", status); |
| 1353 | return status; |
| 1354 | } |
| 1355 | |
| 1356 | /* Does it have ICE? */ |
| 1357 | if (answer_state.match_comp_cnt == 0) { |
| 1358 | /* Remote doesn't support ICE */ |
| 1359 | set_no_ice(tp_ice, "Remote answer doesn't support ICE", |
| 1360 | PJ_SUCCESS); |
| 1361 | return PJ_SUCCESS; |
| 1362 | } |
| 1363 | |
| 1364 | /* Check if remote has reported ice-mismatch */ |
| 1365 | if (pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, |
| 1366 | &STR_ICE_MISMATCH, NULL) != NULL) |
| 1367 | { |
| 1368 | /* Remote has reported ice-mismatch */ |
| 1369 | set_no_ice(tp_ice, |
| 1370 | "Remote answer contains 'ice-mismatch' attribute", |
| 1371 | PJ_SUCCESS); |
| 1372 | return PJ_SUCCESS; |
| 1373 | } |
| 1374 | |
| 1375 | /* Check if remote has indicated a restart */ |
| 1376 | if (answer_state.ice_restart) { |
| 1377 | PJ_LOG(2,(tp_ice->base.name, |
| 1378 | "Warning: remote has signalled ICE restart in SDP " |
| 1379 | "answer which is disallowed. Remote ICE negotiation" |
| 1380 | " may fail.")); |
| 1381 | } |
| 1382 | |
| 1383 | /* Check if the answer itself is mismatched */ |
| 1384 | if (answer_state.ice_mismatch) { |
| 1385 | /* This happens either when a B2BUA modified remote answer but |
| 1386 | * strangely didn't modify our offer, or remote is not capable |
| 1387 | * of detecting mismatch in our offer (it didn't put |
| 1388 | * 'ice-mismatch' attribute in the answer). |
| 1389 | */ |
| 1390 | PJ_LOG(2,(tp_ice->base.name, |
| 1391 | "Warning: remote answer mismatch, but it does not " |
| 1392 | "reject our offer with 'ice-mismatch'. ICE negotiation " |
| 1393 | "may fail")); |
| 1394 | } |
| 1395 | |
| 1396 | /* Do nothing if ICE is complete or running */ |
| 1397 | if (pj_ice_strans_sess_is_running(tp_ice->ice_st)) { |
| 1398 | PJ_LOG(4,(tp_ice->base.name, |
| 1399 | "Ignored offer/answer because ICE is running")); |
| 1400 | return PJ_SUCCESS; |
| 1401 | } |
| 1402 | |
| 1403 | if (pj_ice_strans_sess_is_complete(tp_ice->ice_st)) { |
| 1404 | PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged")); |
| 1405 | return PJ_SUCCESS; |
| 1406 | } |
| 1407 | |
| 1408 | /* Start ICE */ |
| 1409 | |
| 1410 | } else { |
| 1411 | /* |
| 1412 | * We are answerer. We've seen and negotiated remote's SDP |
| 1413 | * before, and the result is in "rem_offer_state". |
| 1414 | */ |
| 1415 | const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; |
| 1416 | |
| 1417 | /* Check for ICE in remote offer */ |
| 1418 | if (tp_ice->rem_offer_state.match_comp_cnt == 0) { |
| 1419 | /* No ICE attribute present */ |
| 1420 | set_no_ice(tp_ice, "Remote no longer offers ICE", |
| 1421 | PJ_SUCCESS); |
| 1422 | return PJ_SUCCESS; |
| 1423 | } |
| 1424 | |
| 1425 | /* Check for ICE ice_mismatch condition in the offer */ |
| 1426 | if (tp_ice->rem_offer_state.ice_mismatch) { |
| 1427 | set_no_ice(tp_ice, "Remote offer mismatch: ", |
| 1428 | PJNATH_EICEMISMATCH); |
| 1429 | return PJ_SUCCESS; |
| 1430 | } |
| 1431 | |
| 1432 | /* If ICE is complete and remote doesn't request restart, |
| 1433 | * then leave the session as is. |
| 1434 | */ |
| 1435 | if (!initial_oa && tp_ice->rem_offer_state.ice_restart == PJ_FALSE) { |
| 1436 | /* Remote has not requested ICE restart, so session is |
| 1437 | * unchanged. |
| 1438 | */ |
| 1439 | PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged")); |
| 1440 | return PJ_SUCCESS; |
| 1441 | } |
| 1442 | |
| 1443 | /* Either remote has requested ICE restart or this is our |
| 1444 | * first answer. |
| 1445 | */ |
| 1446 | |
| 1447 | /* Stop ICE */ |
| 1448 | if (!initial_oa) { |
| 1449 | set_no_ice(tp_ice, "restarting by remote request..", PJ_SUCCESS); |
| 1450 | |
| 1451 | /* We have put new ICE ufrag and pwd in the answer. Now |
| 1452 | * create a new ICE session with that ufrag/pwd pair. |
| 1453 | */ |
| 1454 | get_ice_attr(sdp_local, sdp_local->media[media_index], |
| 1455 | &ufrag_attr, &pwd_attr); |
| 1456 | status = pj_ice_strans_init_ice(tp_ice->ice_st, |
| 1457 | tp_ice->rem_offer_state.local_role, |
| 1458 | &ufrag_attr->value, |
| 1459 | &pwd_attr->value); |
| 1460 | if (status != PJ_SUCCESS) { |
| 1461 | PJ_LOG(1,(tp_ice->base.name, |
| 1462 | "ICE re-initialization failed (status=%d)!", |
| 1463 | status)); |
| 1464 | return status; |
| 1465 | } |
| 1466 | } |
| 1467 | |
| 1468 | /* Ticket #977: Update role if turns out we're supposed to be the |
| 1469 | * Controlling agent (e.g. when talking to ice-lite peer). |
| 1470 | */ |
| 1471 | if (tp_ice->rem_offer_state.local_role==PJ_ICE_SESS_ROLE_CONTROLLING && |
| 1472 | pj_ice_strans_has_sess(tp_ice->ice_st)) |
| 1473 | { |
| 1474 | pj_ice_strans_change_role(tp_ice->ice_st, |
| 1475 | PJ_ICE_SESS_ROLE_CONTROLLING); |
| 1476 | } |
| 1477 | |
| 1478 | |
| 1479 | /* start ICE */ |
| 1480 | } |
| 1481 | |
| 1482 | /* Now start ICE */ |
| 1483 | status = start_ice(tp_ice, tmp_pool, rem_sdp, media_index); |
| 1484 | if (status != PJ_SUCCESS) { |
| 1485 | PJ_LOG(1,(tp_ice->base.name, |
| 1486 | "ICE restart failed (status=%d)!", |
| 1487 | status)); |
| 1488 | return status; |
| 1489 | } |
| 1490 | |
| 1491 | /* Done */ |
| 1492 | tp_ice->use_ice = PJ_TRUE; |
| 1493 | |
| 1494 | return PJ_SUCCESS; |
| 1495 | } |
| 1496 | |
| 1497 | |
| 1498 | static pj_status_t transport_media_stop(pjmedia_transport *tp) |
| 1499 | { |
| 1500 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1501 | |
| 1502 | set_no_ice(tp_ice, "media stop requested", PJ_SUCCESS); |
| 1503 | |
| 1504 | return PJ_SUCCESS; |
| 1505 | } |
| 1506 | |
| 1507 | |
| 1508 | static pj_status_t transport_get_info(pjmedia_transport *tp, |
| 1509 | pjmedia_transport_info *info) |
| 1510 | { |
| 1511 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1512 | pj_ice_sess_cand cand; |
| 1513 | pj_status_t status; |
| 1514 | |
| 1515 | pj_bzero(&info->sock_info, sizeof(info->sock_info)); |
| 1516 | info->sock_info.rtp_sock = info->sock_info.rtcp_sock = PJ_INVALID_SOCKET; |
| 1517 | |
| 1518 | /* Get RTP default address */ |
| 1519 | status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 1, &cand); |
| 1520 | if (status != PJ_SUCCESS) |
| 1521 | return status; |
| 1522 | |
| 1523 | pj_sockaddr_cp(&info->sock_info.rtp_addr_name, &cand.addr); |
| 1524 | |
| 1525 | /* Get RTCP default address */ |
| 1526 | if (tp_ice->comp_cnt > 1) { |
| 1527 | status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 2, &cand); |
| 1528 | if (status != PJ_SUCCESS) |
| 1529 | return status; |
| 1530 | |
| 1531 | pj_sockaddr_cp(&info->sock_info.rtcp_addr_name, &cand.addr); |
| 1532 | } |
| 1533 | |
| 1534 | /* Set remote address originating RTP & RTCP if this transport has |
| 1535 | * ICE activated or received any packets. |
| 1536 | */ |
| 1537 | if (tp_ice->use_ice || tp_ice->rtp_src_cnt) { |
| 1538 | info->src_rtp_name = tp_ice->rtp_src_addr; |
| 1539 | } |
| 1540 | if (tp_ice->use_ice || tp_ice->rtcp_src_cnt) { |
| 1541 | info->src_rtcp_name = tp_ice->rtcp_src_addr; |
| 1542 | } |
| 1543 | |
| 1544 | /* Fill up transport specific info */ |
| 1545 | if (info->specific_info_cnt < PJ_ARRAY_SIZE(info->spc_info)) { |
| 1546 | pjmedia_transport_specific_info *tsi; |
| 1547 | pjmedia_ice_transport_info *ii; |
| 1548 | unsigned i; |
| 1549 | |
| 1550 | pj_assert(sizeof(*ii) <= sizeof(tsi->buffer)); |
| 1551 | tsi = &info->spc_info[info->specific_info_cnt++]; |
| 1552 | tsi->type = PJMEDIA_TRANSPORT_TYPE_ICE; |
| 1553 | tsi->cbsize = sizeof(*ii); |
| 1554 | |
| 1555 | ii = (pjmedia_ice_transport_info*) tsi->buffer; |
| 1556 | pj_bzero(ii, sizeof(*ii)); |
| 1557 | |
| 1558 | ii->active = tp_ice->use_ice; |
| 1559 | |
| 1560 | if (pj_ice_strans_has_sess(tp_ice->ice_st)) |
| 1561 | ii->role = pj_ice_strans_get_role(tp_ice->ice_st); |
| 1562 | else |
| 1563 | ii->role = PJ_ICE_SESS_ROLE_UNKNOWN; |
| 1564 | ii->sess_state = pj_ice_strans_get_state(tp_ice->ice_st); |
| 1565 | ii->comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st); |
| 1566 | |
| 1567 | for (i=1; i<=ii->comp_cnt && i<=PJ_ARRAY_SIZE(ii->comp); ++i) { |
| 1568 | const pj_ice_sess_check *chk; |
| 1569 | |
| 1570 | chk = pj_ice_strans_get_valid_pair(tp_ice->ice_st, i); |
| 1571 | if (chk) { |
| 1572 | ii->comp[i-1].lcand_type = chk->lcand->type; |
| 1573 | pj_sockaddr_cp(&ii->comp[i-1].lcand_addr, |
| 1574 | &chk->lcand->addr); |
| 1575 | ii->comp[i-1].rcand_type = chk->rcand->type; |
| 1576 | pj_sockaddr_cp(&ii->comp[i-1].rcand_addr, |
| 1577 | &chk->rcand->addr); |
| 1578 | } |
| 1579 | } |
| 1580 | } |
| 1581 | |
| 1582 | return PJ_SUCCESS; |
| 1583 | } |
| 1584 | |
| 1585 | |
| 1586 | static pj_status_t transport_attach (pjmedia_transport *tp, |
| 1587 | void *stream, |
| 1588 | const pj_sockaddr_t *rem_addr, |
| 1589 | const pj_sockaddr_t *rem_rtcp, |
| 1590 | unsigned addr_len, |
| 1591 | void (*rtp_cb)(void*, |
| 1592 | void*, |
| 1593 | pj_ssize_t), |
| 1594 | void (*rtcp_cb)(void*, |
| 1595 | void*, |
| 1596 | pj_ssize_t)) |
| 1597 | { |
| 1598 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1599 | |
| 1600 | tp_ice->stream = stream; |
| 1601 | tp_ice->rtp_cb = rtp_cb; |
| 1602 | tp_ice->rtcp_cb = rtcp_cb; |
| 1603 | |
| 1604 | pj_memcpy(&tp_ice->remote_rtp, rem_addr, addr_len); |
| 1605 | pj_memcpy(&tp_ice->remote_rtcp, rem_rtcp, addr_len); |
| 1606 | tp_ice->addr_len = addr_len; |
| 1607 | |
| 1608 | /* Init source RTP & RTCP addresses and counter */ |
| 1609 | tp_ice->rtp_src_addr = tp_ice->remote_rtp; |
| 1610 | tp_ice->rtcp_src_addr = tp_ice->remote_rtcp; |
| 1611 | tp_ice->rtp_src_cnt = 0; |
| 1612 | tp_ice->rtcp_src_cnt = 0; |
| 1613 | |
| 1614 | return PJ_SUCCESS; |
| 1615 | } |
| 1616 | |
| 1617 | |
| 1618 | static void transport_detach(pjmedia_transport *tp, |
| 1619 | void *strm) |
| 1620 | { |
| 1621 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1622 | |
| 1623 | /* TODO: need to solve ticket #460 here */ |
| 1624 | |
| 1625 | tp_ice->rtp_cb = NULL; |
| 1626 | tp_ice->rtcp_cb = NULL; |
| 1627 | tp_ice->stream = NULL; |
| 1628 | |
| 1629 | PJ_UNUSED_ARG(strm); |
| 1630 | } |
| 1631 | |
| 1632 | |
| 1633 | static pj_status_t transport_send_rtp(pjmedia_transport *tp, |
| 1634 | const void *pkt, |
| 1635 | pj_size_t size) |
| 1636 | { |
| 1637 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1638 | |
| 1639 | /* Simulate packet lost on TX direction */ |
| 1640 | if (tp_ice->tx_drop_pct) { |
| 1641 | if ((pj_rand() % 100) <= (int)tp_ice->tx_drop_pct) { |
| 1642 | PJ_LOG(5,(tp_ice->base.name, |
| 1643 | "TX RTP packet dropped because of pkt lost " |
| 1644 | "simulation")); |
| 1645 | return PJ_SUCCESS; |
| 1646 | } |
| 1647 | } |
| 1648 | |
| 1649 | return pj_ice_strans_sendto(tp_ice->ice_st, 1, |
| 1650 | pkt, size, &tp_ice->remote_rtp, |
| 1651 | tp_ice->addr_len); |
| 1652 | } |
| 1653 | |
| 1654 | |
| 1655 | static pj_status_t transport_send_rtcp(pjmedia_transport *tp, |
| 1656 | const void *pkt, |
| 1657 | pj_size_t size) |
| 1658 | { |
| 1659 | return transport_send_rtcp2(tp, NULL, 0, pkt, size); |
| 1660 | } |
| 1661 | |
| 1662 | static pj_status_t transport_send_rtcp2(pjmedia_transport *tp, |
| 1663 | const pj_sockaddr_t *addr, |
| 1664 | unsigned addr_len, |
| 1665 | const void *pkt, |
| 1666 | pj_size_t size) |
| 1667 | { |
| 1668 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1669 | if (tp_ice->comp_cnt > 1) { |
| 1670 | if (addr == NULL) { |
| 1671 | addr = &tp_ice->remote_rtcp; |
| 1672 | addr_len = pj_sockaddr_get_len(addr); |
| 1673 | } |
| 1674 | return pj_ice_strans_sendto(tp_ice->ice_st, 2, pkt, size, |
| 1675 | addr, addr_len); |
| 1676 | } else { |
| 1677 | return PJ_SUCCESS; |
| 1678 | } |
| 1679 | } |
| 1680 | |
| 1681 | |
| 1682 | static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id, |
| 1683 | void *pkt, pj_size_t size, |
| 1684 | const pj_sockaddr_t *src_addr, |
| 1685 | unsigned src_addr_len) |
| 1686 | { |
| 1687 | struct transport_ice *tp_ice; |
| 1688 | pj_bool_t discard = PJ_FALSE; |
| 1689 | |
| 1690 | tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st); |
| 1691 | |
| 1692 | if (comp_id==1 && tp_ice->rtp_cb) { |
| 1693 | |
| 1694 | /* Simulate packet lost on RX direction */ |
| 1695 | if (tp_ice->rx_drop_pct) { |
| 1696 | if ((pj_rand() % 100) <= (int)tp_ice->rx_drop_pct) { |
| 1697 | PJ_LOG(5,(tp_ice->base.name, |
| 1698 | "RX RTP packet dropped because of pkt lost " |
| 1699 | "simulation")); |
| 1700 | return; |
| 1701 | } |
| 1702 | } |
| 1703 | |
| 1704 | /* See if source address of RTP packet is different than the |
| 1705 | * configured address, and switch RTP remote address to |
| 1706 | * source packet address after several consecutive packets |
| 1707 | * have been received. |
| 1708 | */ |
| 1709 | if (!tp_ice->use_ice) { |
| 1710 | pj_bool_t enable_switch = |
| 1711 | ((tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0); |
| 1712 | |
| 1713 | if (!enable_switch || |
| 1714 | pj_sockaddr_cmp(&tp_ice->remote_rtp, src_addr) == 0) |
| 1715 | { |
| 1716 | /* Don't switch while we're receiving from remote_rtp */ |
| 1717 | tp_ice->rtp_src_cnt = 0; |
| 1718 | } else { |
| 1719 | |
| 1720 | ++tp_ice->rtp_src_cnt; |
| 1721 | |
| 1722 | /* Check if the source address is recognized. */ |
| 1723 | if (pj_sockaddr_cmp(src_addr, &tp_ice->rtp_src_addr) != 0) { |
| 1724 | /* Remember the new source address. */ |
| 1725 | pj_sockaddr_cp(&tp_ice->rtp_src_addr, src_addr); |
| 1726 | /* Reset counter */ |
| 1727 | tp_ice->rtp_src_cnt = 0; |
| 1728 | discard = PJ_TRUE; |
| 1729 | } |
| 1730 | |
| 1731 | if (tp_ice->rtp_src_cnt < PJMEDIA_RTP_NAT_PROBATION_CNT) { |
| 1732 | discard = PJ_TRUE; |
| 1733 | } else { |
| 1734 | char addr_text[80]; |
| 1735 | |
| 1736 | /* Set remote RTP address to source address */ |
| 1737 | pj_sockaddr_cp(&tp_ice->remote_rtp, &tp_ice->rtp_src_addr); |
| 1738 | tp_ice->addr_len = pj_sockaddr_get_len(&tp_ice->remote_rtp); |
| 1739 | |
| 1740 | /* Reset counter */ |
| 1741 | tp_ice->rtp_src_cnt = 0; |
| 1742 | |
| 1743 | PJ_LOG(4,(tp_ice->base.name, |
| 1744 | "Remote RTP address switched to %s", |
| 1745 | pj_sockaddr_print(&tp_ice->remote_rtp, addr_text, |
| 1746 | sizeof(addr_text), 3))); |
| 1747 | |
| 1748 | /* Also update remote RTCP address if actual RTCP source |
| 1749 | * address is not heard yet. |
| 1750 | */ |
| 1751 | if (!pj_sockaddr_has_addr(&tp_ice->rtcp_src_addr)) { |
| 1752 | pj_uint16_t port; |
| 1753 | |
| 1754 | pj_sockaddr_cp(&tp_ice->remote_rtcp, |
| 1755 | &tp_ice->remote_rtp); |
| 1756 | |
| 1757 | port = (pj_uint16_t) |
| 1758 | (pj_sockaddr_get_port(&tp_ice->remote_rtp)+1); |
| 1759 | pj_sockaddr_set_port(&tp_ice->remote_rtcp, port); |
| 1760 | |
| 1761 | PJ_LOG(4,(tp_ice->base.name, |
| 1762 | "Remote RTCP address switched to predicted " |
| 1763 | "address %s", |
| 1764 | pj_sockaddr_print(&tp_ice->remote_rtcp, |
| 1765 | addr_text, |
| 1766 | sizeof(addr_text), 3))); |
| 1767 | } |
| 1768 | } |
| 1769 | } |
| 1770 | } |
| 1771 | |
| 1772 | if (!discard) |
| 1773 | (*tp_ice->rtp_cb)(tp_ice->stream, pkt, size); |
| 1774 | |
| 1775 | } else if (comp_id==2 && tp_ice->rtcp_cb) { |
| 1776 | |
| 1777 | /* Check if RTCP source address is the same as the configured |
| 1778 | * remote address, and switch the address when they are |
| 1779 | * different. |
| 1780 | */ |
| 1781 | if (!tp_ice->use_ice && |
| 1782 | (tp_ice->options & PJMEDIA_ICE_NO_SRC_ADDR_CHECKING)==0) |
| 1783 | { |
| 1784 | if (pj_sockaddr_cmp(&tp_ice->remote_rtcp, src_addr) == 0) { |
| 1785 | tp_ice->rtcp_src_cnt = 0; |
| 1786 | } else { |
| 1787 | char addr_text[80]; |
| 1788 | |
| 1789 | ++tp_ice->rtcp_src_cnt; |
| 1790 | if (tp_ice->rtcp_src_cnt < PJMEDIA_RTCP_NAT_PROBATION_CNT) { |
| 1791 | discard = PJ_TRUE; |
| 1792 | } else { |
| 1793 | tp_ice->rtcp_src_cnt = 0; |
| 1794 | pj_sockaddr_cp(&tp_ice->rtcp_src_addr, src_addr); |
| 1795 | pj_sockaddr_cp(&tp_ice->remote_rtcp, src_addr); |
| 1796 | |
| 1797 | pj_assert(tp_ice->addr_len==pj_sockaddr_get_len(src_addr)); |
| 1798 | |
| 1799 | PJ_LOG(4,(tp_ice->base.name, |
| 1800 | "Remote RTCP address switched to %s", |
| 1801 | pj_sockaddr_print(&tp_ice->remote_rtcp, |
| 1802 | addr_text, sizeof(addr_text), |
| 1803 | 3))); |
| 1804 | } |
| 1805 | } |
| 1806 | } |
| 1807 | |
| 1808 | if (!discard) |
| 1809 | (*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size); |
| 1810 | } |
| 1811 | |
| 1812 | PJ_UNUSED_ARG(src_addr_len); |
| 1813 | } |
| 1814 | |
| 1815 | |
| 1816 | static void ice_on_ice_complete(pj_ice_strans *ice_st, |
| 1817 | pj_ice_strans_op op, |
| 1818 | pj_status_t result) |
| 1819 | { |
| 1820 | struct transport_ice *tp_ice; |
| 1821 | |
| 1822 | tp_ice = (struct transport_ice*) pj_ice_strans_get_user_data(ice_st); |
| 1823 | |
| 1824 | /* Notify application */ |
| 1825 | if (tp_ice->cb.on_ice_complete) |
| 1826 | (*tp_ice->cb.on_ice_complete)(&tp_ice->base, op, result); |
| 1827 | } |
| 1828 | |
| 1829 | |
| 1830 | /* Simulate lost */ |
| 1831 | static pj_status_t transport_simulate_lost(pjmedia_transport *tp, |
| 1832 | pjmedia_dir dir, |
| 1833 | unsigned pct_lost) |
| 1834 | { |
| 1835 | struct transport_ice *ice = (struct transport_ice*) tp; |
| 1836 | |
| 1837 | PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL); |
| 1838 | |
| 1839 | if (dir & PJMEDIA_DIR_ENCODING) |
| 1840 | ice->tx_drop_pct = pct_lost; |
| 1841 | |
| 1842 | if (dir & PJMEDIA_DIR_DECODING) |
| 1843 | ice->rx_drop_pct = pct_lost; |
| 1844 | |
| 1845 | return PJ_SUCCESS; |
| 1846 | } |
| 1847 | |
| 1848 | |
| 1849 | /* |
| 1850 | * Destroy ICE media transport. |
| 1851 | */ |
| 1852 | static pj_status_t transport_destroy(pjmedia_transport *tp) |
| 1853 | { |
| 1854 | struct transport_ice *tp_ice = (struct transport_ice*)tp; |
| 1855 | |
| 1856 | if (tp_ice->ice_st) { |
| 1857 | pj_ice_strans_destroy(tp_ice->ice_st); |
| 1858 | tp_ice->ice_st = NULL; |
| 1859 | } |
| 1860 | |
| 1861 | if (tp_ice->pool) { |
| 1862 | pj_pool_t *pool = tp_ice->pool; |
| 1863 | tp_ice->pool = NULL; |
| 1864 | pj_pool_release(pool); |
| 1865 | } |
| 1866 | |
| 1867 | return PJ_SUCCESS; |
| 1868 | } |
| 1869 | |