Changeset 2032 for pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c
- Timestamp:
- Jun 19, 2008 2:10:28 PM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c
r1989 r2032 24 24 25 25 #define THIS_FILE "transport_ice.c" 26 27 static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; 26 #if 0 27 # define TRACE__(expr) PJ_LOG(5,expr) 28 #else 29 # define TRACE__(expr) 30 #endif 31 32 enum oa_role 33 { 34 ROLE_NONE, 35 ROLE_OFFERER, 36 ROLE_ANSWERER 37 }; 38 39 struct sdp_state 40 { 41 unsigned match_comp_cnt; /* Matching number of components */ 42 pj_bool_t ice_mismatch; /* Address doesn't match candidates */ 43 pj_bool_t ice_restart; /* Offer to restart ICE */ 44 pj_ice_sess_role local_role; /* Our role */ 45 }; 28 46 29 47 struct transport_ice … … 32 50 pj_pool_t *pool; 33 51 int af; 52 34 53 unsigned comp_cnt; 35 54 pj_ice_strans *ice_st; 55 36 56 pjmedia_ice_cb cb; 37 57 unsigned media_option; 58 59 pj_bool_t initial_sdp; 60 enum oa_role oa_role; /**< Last role in SDP offer/answer */ 61 struct sdp_state rem_offer_state;/**< Describes the remote offer */ 38 62 39 63 void *stream; … … 85 109 pj_pool_t *pool, 86 110 unsigned options, 87 pjmedia_sdp_session *sdp_local,88 111 const pjmedia_sdp_session *rem_sdp, 89 112 unsigned media_index); 90 static pj_status_t transport_media_start (pjmedia_transport *tp, 113 static pj_status_t transport_encode_sdp(pjmedia_transport *tp, 114 pj_pool_t *tmp_pool, 115 pjmedia_sdp_session *sdp_local, 116 const pjmedia_sdp_session *rem_sdp, 117 unsigned media_index); 118 static pj_status_t transport_media_start(pjmedia_transport *tp, 91 119 pj_pool_t *pool, 92 pjmedia_sdp_session *sdp_local,120 const pjmedia_sdp_session *sdp_local, 93 121 const pjmedia_sdp_session *rem_sdp, 94 122 unsigned media_index); … … 121 149 &transport_send_rtcp2, 122 150 &transport_media_create, 151 &transport_encode_sdp, 123 152 &transport_media_start, 124 153 &transport_media_stop, … … 127 156 }; 128 157 129 static const pj_str_t STR_CANDIDATE = {"candidate", 9}; 130 static const pj_str_t STR_ICE_LITE = {"ice-lite", 8}; 131 static const pj_str_t STR_ICE_MISMATCH = {"ice-mismatch", 12}; 132 158 static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 }; 159 static const pj_str_t STR_CANDIDATE = { "candidate", 9}; 160 static const pj_str_t STR_REM_CAND = { "remote-candidates", 17 }; 161 static const pj_str_t STR_ICE_LITE = { "ice-lite", 8}; 162 static const pj_str_t STR_ICE_MISMATCH = { "ice-mismatch", 12}; 163 static const pj_str_t STR_ICE_UFRAG = { "ice-ufrag", 9 }; 164 static const pj_str_t STR_ICE_PWD = { "ice-pwd", 7 }; 165 static const pj_str_t STR_IP4 = { "IP4", 3 }; 166 static const pj_str_t STR_IP6 = { "IP6", 3 }; 167 static const pj_str_t STR_RTCP = { "rtcp", 4 }; 168 169 enum { 170 COMP_RTP = 1, 171 COMP_RTCP = 2 172 }; 133 173 134 174 /* … … 158 198 tp_ice->base.op = &transport_ice_op; 159 199 tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE; 200 tp_ice->initial_sdp = PJ_TRUE; 201 tp_ice->oa_role = ROLE_NONE; 160 202 161 203 if (cb) … … 184 226 return PJ_SUCCESS; 185 227 } 228 229 /* Disable ICE when SDP from remote doesn't contain a=candidate line */ 230 static void set_no_ice(struct transport_ice *tp_ice, const char *reason, 231 pj_status_t err) 232 { 233 if (err != PJ_SUCCESS) { 234 char errmsg[PJ_ERR_MSG_SIZE]; 235 pj_strerror(err, errmsg, sizeof(errmsg)); 236 PJ_LOG(4,(tp_ice->base.name, 237 "Stopping ICE, reason=%s:%s", reason, errmsg)); 238 } else { 239 PJ_LOG(4,(tp_ice->base.name, 240 "Stopping ICE, reason=%s", reason)); 241 } 242 243 pj_ice_strans_stop_ice(tp_ice->ice_st); 244 } 245 186 246 187 247 /* Create SDP candidate attribute */ … … 228 288 } 229 289 290 291 /* Get ice-ufrag and ice-pwd attribute */ 292 static void get_ice_attr(const pjmedia_sdp_session *rem_sdp, 293 const pjmedia_sdp_media *rem_m, 294 const pjmedia_sdp_attr **p_ice_ufrag, 295 const pjmedia_sdp_attr **p_ice_pwd) 296 { 297 pjmedia_sdp_attr *attr; 298 299 /* Find ice-ufrag attribute in media descriptor */ 300 attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, 301 &STR_ICE_UFRAG, NULL); 302 if (attr == NULL) { 303 /* Find ice-ufrag attribute in session descriptor */ 304 attr = pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, 305 &STR_ICE_UFRAG, NULL); 306 } 307 *p_ice_ufrag = attr; 308 309 /* Find ice-pwd attribute in media descriptor */ 310 attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, 311 &STR_ICE_PWD, NULL); 312 if (attr == NULL) { 313 /* Find ice-pwd attribute in session descriptor */ 314 attr = pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, 315 &STR_ICE_PWD, NULL); 316 } 317 *p_ice_pwd = attr; 318 } 319 320 321 /* Encode and add "a=ice-mismatch" attribute in the SDP */ 322 static void encode_ice_mismatch(pj_pool_t *sdp_pool, 323 pjmedia_sdp_session *sdp_local, 324 unsigned media_index) 325 { 326 pjmedia_sdp_attr *attr; 327 pjmedia_sdp_media *m = sdp_local->media[media_index]; 328 329 attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr); 330 attr->name = STR_ICE_MISMATCH; 331 attr->value.slen = 0; 332 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 333 } 334 335 336 /* Encode ICE information in SDP */ 337 static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice, 338 pj_pool_t *sdp_pool, 339 pjmedia_sdp_session *sdp_local, 340 unsigned media_index, 341 unsigned comp_cnt, 342 pj_bool_t restart_session) 343 { 344 enum { 345 ATTR_BUF_LEN = 160, /* Max len of a=candidate attr */ 346 RATTR_BUF_LEN= 160 /* Max len of a=remote-candidates attr */ 347 }; 348 pjmedia_sdp_media *m = sdp_local->media[media_index]; 349 pj_str_t local_ufrag, local_pwd; 350 pjmedia_sdp_attr *attr; 351 pj_status_t status; 352 353 /* Must have a session */ 354 PJ_ASSERT_RETURN(pj_ice_strans_has_sess(tp_ice->ice_st), PJ_EBUG); 355 356 /* Get ufrag and pwd from current session */ 357 pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, &local_ufrag, &local_pwd, 358 NULL, NULL); 359 360 /* The listing of candidates depends on whether ICE has completed 361 * or not. When ICE has completed: 362 * 363 * 9.1.2.2: Existing Media Streams with ICE Completed 364 * The agent MUST include a candidate attributes for candidates 365 * matching the default destination for each component of the 366 * media stream, and MUST NOT include any other candidates. 367 * 368 * When ICE has not completed, we shall include all candidates. 369 * 370 * Except when we have detected that remote is offering to restart 371 * the session, in this case we will answer with full ICE SDP and 372 * new ufrag/pwd pair. 373 */ 374 if (!restart_session && pj_ice_strans_sess_is_complete(tp_ice->ice_st)) { 375 const pj_ice_sess_check *check; 376 char *attr_buf; 377 pjmedia_sdp_conn *conn; 378 pjmedia_sdp_attr *a_rtcp; 379 pj_str_t rem_cand; 380 unsigned comp; 381 382 /* Encode ice-ufrag attribute */ 383 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, 384 &local_ufrag); 385 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 386 387 /* Encode ice-pwd attribute */ 388 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, 389 &local_pwd); 390 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 391 392 /* Prepare buffer */ 393 attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN); 394 rem_cand.ptr = (char*) pj_pool_alloc(sdp_pool, RATTR_BUF_LEN); 395 rem_cand.slen = 0; 396 397 /* 9.1.2.2: Existing Media Streams with ICE Completed 398 * The default destination for media (i.e., the values of 399 * the IP addresses and ports in the m and c line used for 400 * that media stream) MUST be the local candidate from the 401 * highest priority nominated pair in the valid list for each 402 * component. 403 */ 404 check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, 1); 405 if (check == NULL) { 406 pj_assert(!"Shouldn't happen"); 407 return PJ_EBUG; 408 } 409 410 /* Override connection line address and media port number */ 411 conn = m->conn; 412 if (conn == NULL) 413 conn = sdp_local->conn; 414 415 conn->addr.ptr = (char*) pj_pool_alloc(sdp_pool, 416 PJ_INET6_ADDRSTRLEN); 417 pj_sockaddr_print(&check->lcand->addr, conn->addr.ptr, 418 PJ_INET6_ADDRSTRLEN, 0); 419 conn->addr.slen = pj_ansi_strlen(conn->addr.ptr); 420 m->desc.port = pj_sockaddr_get_port(&check->lcand->addr); 421 422 /* Override address RTCP attribute if it's present */ 423 if (comp_cnt == 2 && 424 (check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, 425 COMP_RTCP)) != NULL && 426 (a_rtcp = pjmedia_sdp_attr_find(m->attr_count, m->attr, 427 &STR_RTCP, 0)) != NULL) 428 { 429 pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a_rtcp); 430 431 a_rtcp = pjmedia_sdp_attr_create_rtcp(sdp_pool, 432 &check->lcand->addr); 433 if (a_rtcp) 434 pjmedia_sdp_attr_add(&m->attr_count, m->attr, a_rtcp); 435 } 436 437 /* Encode only candidates matching the default destination 438 * for each component 439 */ 440 for (comp=0; comp < comp_cnt; ++comp) { 441 int len; 442 pj_str_t value; 443 444 /* Get valid pair for this component */ 445 check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, comp+1); 446 if (check == NULL) 447 continue; 448 449 /* Print and add local candidate in the pair */ 450 value.ptr = attr_buf; 451 value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN, 452 check->lcand); 453 if (value.slen < 0) { 454 pj_assert(!"Not enough attr_buf to print candidate"); 455 return PJ_EBUG; 456 } 457 458 attr = pjmedia_sdp_attr_create(sdp_pool, STR_CANDIDATE.ptr, 459 &value); 460 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 461 462 /* Append to a=remote-candidates attribute */ 463 if (pj_ice_strans_get_role(tp_ice->ice_st) == 464 PJ_ICE_SESS_ROLE_CONTROLLING) 465 { 466 char rem_addr[PJ_INET6_ADDRSTRLEN]; 467 468 pj_sockaddr_print(&check->rcand->addr, rem_addr, 469 sizeof(rem_addr), 0); 470 len = pj_ansi_snprintf( 471 rem_cand.ptr + rem_cand.slen, 472 RATTR_BUF_LEN - rem_cand.slen, 473 "%s%u %s %u", 474 (rem_cand.slen==0? "" : " "), 475 comp+1, rem_addr, 476 pj_sockaddr_get_port(&check->rcand->addr) 477 ); 478 if (len < 1 || len >= RATTR_BUF_LEN) { 479 pj_assert(!"Not enough buffer to print " 480 "remote-candidates"); 481 return PJ_EBUG; 482 } 483 484 rem_cand.slen += len; 485 } 486 } 487 488 /* 9.1.2.2: Existing Media Streams with ICE Completed 489 * In addition, if the agent is controlling, it MUST include 490 * the a=remote-candidates attribute for each media stream 491 * whose check list is in the Completed state. The attribute 492 * contains the remote candidates from the highest priority 493 * nominated pair in the valid list for each component of that 494 * media stream. 495 */ 496 if (pj_ice_strans_get_role(tp_ice->ice_st) == 497 PJ_ICE_SESS_ROLE_CONTROLLING) 498 { 499 attr = pjmedia_sdp_attr_create(sdp_pool, STR_REM_CAND.ptr, 500 &rem_cand); 501 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 502 } 503 504 } else if (pj_ice_strans_has_sess(tp_ice->ice_st)) { 505 /* Encode all candidates to SDP media */ 506 char *attr_buf; 507 unsigned comp; 508 509 /* If ICE is not restarted, encode current ICE ufrag/pwd. 510 * Otherwise generate new one. 511 */ 512 if (!restart_session) { 513 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, 514 &local_ufrag); 515 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 516 517 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, 518 &local_pwd); 519 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 520 521 } else { 522 pj_str_t str; 523 524 str.slen = PJ_ICE_UFRAG_LEN; 525 str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen); 526 pj_create_random_string(str.ptr, str.slen); 527 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &str); 528 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 529 530 str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen); 531 pj_create_random_string(str.ptr, str.slen); 532 attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &str); 533 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 534 } 535 536 /* Create buffer to encode candidates as SDP attribute */ 537 attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN); 538 539 for (comp=0; comp < comp_cnt; ++comp) { 540 unsigned cand_cnt; 541 pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; 542 unsigned i; 543 544 cand_cnt = PJ_ARRAY_SIZE(cand); 545 status = pj_ice_strans_enum_cands(tp_ice->ice_st, comp+1, 546 &cand_cnt, cand); 547 if (status != PJ_SUCCESS) 548 return status; 549 550 for (i=0; i<cand_cnt; ++i) { 551 pj_str_t value; 552 553 value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN, 554 &cand[i]); 555 if (value.slen < 0) { 556 pj_assert(!"Not enough attr_buf to print candidate"); 557 return PJ_EBUG; 558 } 559 560 value.ptr = attr_buf; 561 attr = pjmedia_sdp_attr_create(sdp_pool, 562 STR_CANDIDATE.ptr, 563 &value); 564 pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); 565 } 566 } 567 } else { 568 /* ICE has failed, application should have terminated this call */ 569 } 570 571 return PJ_SUCCESS; 572 } 573 574 575 /* Parse a=candidate line */ 576 static pj_status_t parse_cand(const char *obj_name, 577 pj_pool_t *pool, 578 const pj_str_t *orig_input, 579 pj_ice_sess_cand *cand) 580 { 581 pj_str_t input; 582 char *token, *host; 583 int af; 584 pj_str_t s; 585 pj_status_t status = PJNATH_EICEINCANDSDP; 586 587 pj_bzero(cand, sizeof(*cand)); 588 pj_strdup_with_null(pool, &input, orig_input); 589 590 PJ_UNUSED_ARG(obj_name); 591 592 /* Foundation */ 593 token = strtok(input.ptr, " "); 594 if (!token) { 595 TRACE__((obj_name, "Expecting ICE foundation in candidate")); 596 goto on_return; 597 } 598 pj_strdup2(pool, &cand->foundation, token); 599 600 /* Component ID */ 601 token = strtok(NULL, " "); 602 if (!token) { 603 TRACE__((obj_name, "Expecting ICE component ID in candidate")); 604 goto on_return; 605 } 606 cand->comp_id = (pj_uint8_t) atoi(token); 607 608 /* Transport */ 609 token = strtok(NULL, " "); 610 if (!token) { 611 TRACE__((obj_name, "Expecting ICE transport in candidate")); 612 goto on_return; 613 } 614 if (pj_ansi_stricmp(token, "UDP") != 0) { 615 TRACE__((obj_name, 616 "Expecting ICE UDP transport only in candidate")); 617 goto on_return; 618 } 619 620 /* Priority */ 621 token = strtok(NULL, " "); 622 if (!token) { 623 TRACE__((obj_name, "Expecting ICE priority in candidate")); 624 goto on_return; 625 } 626 cand->prio = atoi(token); 627 628 /* Host */ 629 host = strtok(NULL, " "); 630 if (!host) { 631 TRACE__((obj_name, "Expecting ICE host in candidate")); 632 goto on_return; 633 } 634 /* Detect address family */ 635 if (pj_ansi_strchr(host, ':')) 636 af = pj_AF_INET6(); 637 else 638 af = pj_AF_INET(); 639 /* Assign address */ 640 if (pj_sockaddr_init(af, &cand->addr, pj_cstr(&s, host), 0)) { 641 TRACE__((obj_name, "Invalid ICE candidate address")); 642 goto on_return; 643 } 644 645 /* Port */ 646 token = strtok(NULL, " "); 647 if (!token) { 648 TRACE__((obj_name, "Expecting ICE port number in candidate")); 649 goto on_return; 650 } 651 pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)atoi(token)); 652 653 /* typ */ 654 token = strtok(NULL, " "); 655 if (!token) { 656 TRACE__((obj_name, "Expecting ICE \"typ\" in candidate")); 657 goto on_return; 658 } 659 if (pj_ansi_stricmp(token, "typ") != 0) { 660 TRACE__((obj_name, "Expecting ICE \"typ\" in candidate")); 661 goto on_return; 662 } 663 664 /* candidate type */ 665 token = strtok(NULL, " "); 666 if (!token) { 667 TRACE__((obj_name, "Expecting ICE candidate type in candidate")); 668 goto on_return; 669 } 670 671 if (pj_ansi_stricmp(token, "host") == 0) { 672 cand->type = PJ_ICE_CAND_TYPE_HOST; 673 674 } else if (pj_ansi_stricmp(token, "srflx") == 0) { 675 cand->type = PJ_ICE_CAND_TYPE_SRFLX; 676 677 } else if (pj_ansi_stricmp(token, "relay") == 0) { 678 cand->type = PJ_ICE_CAND_TYPE_RELAYED; 679 680 } else if (pj_ansi_stricmp(token, "prflx") == 0) { 681 cand->type = PJ_ICE_CAND_TYPE_PRFLX; 682 683 } else { 684 PJ_LOG(5,(obj_name, "Invalid ICE candidate type %s in candidate", 685 token)); 686 goto on_return; 687 } 688 689 status = PJ_SUCCESS; 690 691 on_return: 692 return status; 693 } 694 695 696 /* Create initial SDP offer */ 697 static pj_status_t create_initial_offer(struct transport_ice *tp_ice, 698 pj_pool_t *sdp_pool, 699 pjmedia_sdp_session *loc_sdp, 700 unsigned media_index) 701 { 702 pj_status_t status; 703 704 /* Encode ICE in SDP */ 705 status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, 706 tp_ice->comp_cnt, PJ_FALSE); 707 if (status != PJ_SUCCESS) { 708 set_no_ice(tp_ice, "Error encoding SDP answer", status); 709 return status; 710 } 711 712 return PJ_SUCCESS; 713 } 714 715 716 /* Verify incoming offer */ 717 static pj_status_t verify_ice_sdp(struct transport_ice *tp_ice, 718 pj_pool_t *tmp_pool, 719 const pjmedia_sdp_session *rem_sdp, 720 unsigned media_index, 721 pj_ice_sess_role current_ice_role, 722 struct sdp_state *sdp_state) 723 { 724 const pjmedia_sdp_media *rem_m; 725 const pjmedia_sdp_attr *attr, *ufrag_attr, *pwd_attr; 726 const pjmedia_sdp_conn *rem_conn; 727 pj_bool_t comp1_found=PJ_FALSE, comp2_found=PJ_FALSE; 728 pj_sockaddr rem_conn_addr, rtcp_addr; 729 unsigned i; 730 pj_status_t status; 731 732 rem_m = rem_sdp->media[media_index]; 733 734 /* Get the "ice-ufrag" and "ice-pwd" attributes */ 735 get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); 736 737 /* If "ice-ufrag" or "ice-pwd" are not found, disable ICE */ 738 if (ufrag_attr==NULL || pwd_attr==NULL) { 739 sdp_state->match_comp_cnt = 0; 740 return PJ_SUCCESS; 741 } 742 743 /* Verify that default target for each component matches one of the 744 * candidatefor the component. Otherwise stop ICE with ICE ice_mismatch 745 * error. 746 */ 747 748 /* Component 1 is the c= line */ 749 rem_conn = rem_m->conn; 750 if (rem_conn == NULL) 751 rem_conn = rem_sdp->conn; 752 if (!rem_conn) 753 return PJMEDIA_SDP_EMISSINGCONN; 754 755 /* Verify address family matches */ 756 if ((tp_ice->af==pj_AF_INET() && 757 pj_strcmp(&rem_conn->addr_type, &STR_IP4)!=0) || 758 (tp_ice->af==pj_AF_INET6() && 759 pj_strcmp(&rem_conn->addr_type, &STR_IP6)!=0)) 760 { 761 return PJMEDIA_SDP_ETPORTNOTEQUAL; 762 } 763 764 /* Assign remote connection address */ 765 status = pj_sockaddr_init(tp_ice->af, &rem_conn_addr, &rem_conn->addr, 766 (pj_uint16_t)rem_m->desc.port); 767 if (status != PJ_SUCCESS) 768 return status; 769 770 /* Component 2 is a=rtcp line, if present. */ 771 attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, 772 &STR_RTCP, NULL); 773 if (attr && tp_ice->comp_cnt > 1) { 774 pjmedia_sdp_rtcp_attr rtcp_attr; 775 776 status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp_attr); 777 if (status != PJ_SUCCESS) { 778 /* Error parsing a=rtcp attribute */ 779 return status; 780 } 781 782 /* Verify address family matches */ 783 if ((tp_ice->af==pj_AF_INET() && 784 pj_strcmp(&rtcp_attr.addr_type, &STR_IP4)!=0) || 785 (tp_ice->af==pj_AF_INET6() && 786 pj_strcmp(&rtcp_attr.addr_type, &STR_IP6)!=0)) 787 { 788 return PJMEDIA_SDP_ETPORTNOTEQUAL; 789 } 790 791 /* Assign RTCP address */ 792 status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, 793 &rtcp_attr.addr, 794 (pj_uint16_t)rtcp_attr.port); 795 if (status != PJ_SUCCESS) { 796 return PJMEDIA_SDP_EINRTCP; 797 } 798 799 sdp_state->match_comp_cnt = 2; 800 801 } else { 802 /* Don't have RTCP component */ 803 comp2_found = PJ_TRUE; 804 sdp_state->match_comp_cnt = 1; 805 } 806 807 /* Find the default address in a=candidate attributes. 808 */ 809 for (i=0; i<rem_m->attr_count; ++i) { 810 pj_ice_sess_cand cand; 811 812 if (pj_strcmp(&rem_m->attr[i]->name, &STR_CANDIDATE)!=0) 813 continue; 814 815 status = parse_cand(tp_ice->base.name, tmp_pool, 816 &rem_m->attr[i]->value, &cand); 817 if (status != PJ_SUCCESS) { 818 PJ_LOG(4,(tp_ice->base.name, 819 "Error in parsing SDP candidate attribute '%.*s', " 820 "candidate is ignored", 821 (int)rem_m->attr[i]->value.slen, 822 rem_m->attr[i]->value.ptr)); 823 continue; 824 } 825 826 if (!comp1_found && cand.comp_id==COMP_RTP && 827 pj_sockaddr_cmp(&rem_conn_addr, &cand.addr)==0) 828 { 829 /* Found */ 830 comp1_found = PJ_TRUE; 831 if (comp1_found && comp2_found) 832 break; 833 } else if (!comp2_found && cand.comp_id==COMP_RTCP && 834 pj_sockaddr_cmp(&rtcp_addr, &cand.addr)==0) 835 { 836 /* Found */ 837 comp2_found = PJ_TRUE; 838 if (comp1_found && comp2_found) 839 break; 840 } 841 842 } 843 844 if (!comp1_found || !comp2_found) { 845 /* ICE ice_mismatch */ 846 sdp_state->ice_mismatch = PJ_TRUE; 847 } else { 848 sdp_state->ice_mismatch = PJ_FALSE; 849 } 850 851 /* Detect remote restarting session */ 852 if (pj_ice_strans_has_sess(tp_ice->ice_st) && 853 (pj_ice_strans_sess_is_running(tp_ice->ice_st) || 854 pj_ice_strans_sess_is_complete(tp_ice->ice_st))) 855 { 856 pj_str_t rem_run_ufrag, rem_run_pwd; 857 pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, NULL, NULL, 858 &rem_run_ufrag, &rem_run_pwd); 859 if (pj_strcmp(&ufrag_attr->value, &rem_run_ufrag) || 860 pj_strcmp(&pwd_attr->value, &rem_run_pwd)) 861 { 862 /* Remote offers to restart ICE */ 863 sdp_state->ice_restart = PJ_TRUE; 864 } else { 865 sdp_state->ice_restart = PJ_FALSE; 866 } 867 } else { 868 sdp_state->ice_restart = PJ_FALSE; 869 } 870 871 /* Detect our role */ 872 if (current_ice_role==PJ_ICE_SESS_ROLE_CONTROLLING) { 873 sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; 874 } else { 875 if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, 876 &STR_ICE_LITE, NULL) != NULL) 877 { 878 /* Remote is ICE Lite */ 879 sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; 880 } else { 881 sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLED; 882 } 883 } 884 885 PJ_LOG(4,(tp_ice->base.name, 886 "Processing SDP: support ICE=%u, common comp_cnt=%u, " 887 "ice_mismatch=%u, ice_restart=%u, local_role=%s", 888 (sdp_state->match_comp_cnt != 0), 889 sdp_state->match_comp_cnt, 890 sdp_state->ice_mismatch, 891 sdp_state->ice_restart, 892 pj_ice_sess_role_name(sdp_state->local_role))); 893 894 return PJ_SUCCESS; 895 896 } 897 898 899 /* Verify incoming offer and create initial answer */ 900 static pj_status_t create_initial_answer(struct transport_ice *tp_ice, 901 pj_pool_t *sdp_pool, 902 pjmedia_sdp_session *loc_sdp, 903 const pjmedia_sdp_session *rem_sdp, 904 unsigned media_index) 905 { 906 const pjmedia_sdp_media *rem_m = rem_sdp->media[media_index]; 907 pj_status_t status; 908 909 /* Check if media is removed (just in case) */ 910 if (rem_m->desc.port == 0) { 911 return PJ_SUCCESS; 912 } 913 914 /* Verify the offer */ 915 status = verify_ice_sdp(tp_ice, sdp_pool, rem_sdp, media_index, 916 PJ_ICE_SESS_ROLE_CONTROLLED, 917 &tp_ice->rem_offer_state); 918 if (status != PJ_SUCCESS) { 919 set_no_ice(tp_ice, "Invalid SDP offer", status); 920 return status; 921 } 922 923 /* Does remote support ICE? */ 924 if (tp_ice->rem_offer_state.match_comp_cnt==0) { 925 set_no_ice(tp_ice, "No ICE found in SDP offer", PJ_SUCCESS); 926 return PJ_SUCCESS; 927 } 928 929 /* ICE ice_mismatch? */ 930 if (tp_ice->rem_offer_state.ice_mismatch) { 931 set_no_ice(tp_ice, "ICE ice_mismatch in remote offer", PJ_SUCCESS); 932 encode_ice_mismatch(sdp_pool, loc_sdp, media_index); 933 return PJ_SUCCESS; 934 } 935 936 /* Encode ICE in SDP */ 937 status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, 938 tp_ice->rem_offer_state.match_comp_cnt, 939 PJ_FALSE); 940 if (status != PJ_SUCCESS) { 941 set_no_ice(tp_ice, "Error encoding SDP answer", status); 942 return status; 943 } 944 945 return PJ_SUCCESS; 946 } 947 948 949 /* Create subsequent SDP offer */ 950 static pj_status_t create_subsequent_offer(struct transport_ice *tp_ice, 951 pj_pool_t *sdp_pool, 952 pjmedia_sdp_session *loc_sdp, 953 unsigned media_index) 954 { 955 unsigned comp_cnt; 956 957 if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) { 958 /* We don't have ICE */ 959 return PJ_SUCCESS; 960 } 961 962 comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st); 963 return encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, 964 comp_cnt, PJ_FALSE); 965 } 966 967 968 /* Create subsequent SDP answer */ 969 static pj_status_t create_subsequent_answer(struct transport_ice *tp_ice, 970 pj_pool_t *sdp_pool, 971 pjmedia_sdp_session *loc_sdp, 972 const pjmedia_sdp_session *rem_sdp, 973 unsigned media_index) 974 { 975 pj_status_t status; 976 977 /* We have a session */ 978 status = verify_ice_sdp(tp_ice, sdp_pool, rem_sdp, media_index, 979 PJ_ICE_SESS_ROLE_CONTROLLED, 980 &tp_ice->rem_offer_state); 981 if (status != PJ_SUCCESS) { 982 /* Something wrong with the offer */ 983 return status; 984 } 985 986 if (pj_ice_strans_has_sess(tp_ice->ice_st)) { 987 /* 988 * Received subsequent offer while we have ICE active. 989 */ 990 991 if (tp_ice->rem_offer_state.match_comp_cnt == 0) { 992 /* Remote no longer offers ICE */ 993 return PJ_SUCCESS; 994 } 995 996 if (tp_ice->rem_offer_state.ice_mismatch) { 997 encode_ice_mismatch(sdp_pool, loc_sdp, media_index); 998 return PJ_SUCCESS; 999 } 1000 1001 status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, 1002 tp_ice->rem_offer_state.match_comp_cnt, 1003 tp_ice->rem_offer_state.ice_restart); 1004 if (status != PJ_SUCCESS) 1005 return status; 1006 1007 /* Done */ 1008 1009 } else { 1010 /* 1011 * Received subsequent offer while we DON'T have ICE active. 1012 */ 1013 1014 if (tp_ice->rem_offer_state.match_comp_cnt == 0) { 1015 /* Remote does not support ICE */ 1016 return PJ_SUCCESS; 1017 } 1018 1019 if (tp_ice->rem_offer_state.ice_mismatch) { 1020 encode_ice_mismatch(sdp_pool, loc_sdp, media_index); 1021 return PJ_SUCCESS; 1022 } 1023 1024 /* Looks like now remote is offering ICE, so we need to create 1025 * ICE session now. 1026 */ 1027 status = pj_ice_strans_init_ice(tp_ice->ice_st, 1028 PJ_ICE_SESS_ROLE_CONTROLLED, 1029 NULL, NULL); 1030 if (status != PJ_SUCCESS) { 1031 /* Fail to create new ICE session */ 1032 return status; 1033 } 1034 1035 status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, 1036 tp_ice->rem_offer_state.match_comp_cnt, 1037 tp_ice->rem_offer_state.ice_restart); 1038 if (status != PJ_SUCCESS) 1039 return status; 1040 1041 /* Done */ 1042 } 1043 1044 return PJ_SUCCESS; 1045 } 1046 1047 230 1048 /* 231 1049 * For both UAC and UAS, pass in the SDP before sending it to remote. … … 233 1051 */ 234 1052 static pj_status_t transport_media_create(pjmedia_transport *tp, 235 pj_pool_t * pool,1053 pj_pool_t *sdp_pool, 236 1054 unsigned options, 237 pjmedia_sdp_session *sdp_local,238 1055 const pjmedia_sdp_session *rem_sdp, 239 1056 unsigned media_index) 240 1057 { 241 1058 struct transport_ice *tp_ice = (struct transport_ice*)tp; 242 pj_bool_t init_ice; 243 unsigned i; 1059 pj_ice_sess_role ice_role; 244 1060 pj_status_t status; 245 1061 1062 PJ_UNUSED_ARG(media_index); 1063 PJ_UNUSED_ARG(sdp_pool); 1064 246 1065 tp_ice->media_option = options; 1066 tp_ice->oa_role = ROLE_NONE; 1067 tp_ice->initial_sdp = PJ_TRUE; 1068 1069 /* Init ICE, the initial role is set now based on availability of 1070 * rem_sdp, but it will be checked again later. 1071 */ 1072 ice_role = (rem_sdp==NULL ? PJ_ICE_SESS_ROLE_CONTROLLING : 1073 PJ_ICE_SESS_ROLE_CONTROLLED); 1074 status = pj_ice_strans_init_ice(tp_ice->ice_st, ice_role, NULL, NULL); 1075 1076 /* Done */ 1077 return status; 1078 } 1079 1080 1081 static pj_status_t transport_encode_sdp(pjmedia_transport *tp, 1082 pj_pool_t *sdp_pool, 1083 pjmedia_sdp_session *sdp_local, 1084 const pjmedia_sdp_session *rem_sdp, 1085 unsigned media_index) 1086 { 1087 struct transport_ice *tp_ice = (struct transport_ice*)tp; 1088 pj_status_t status; 247 1089 248 1090 /* Validate media transport */ 249 1091 /* For now, this transport only support RTP/AVP transport */ 250 1092 if ((tp_ice->media_option & PJMEDIA_TPMED_NO_TRANSPORT_CHECKING) == 0) { 251 pjmedia_sdp_media * m_rem, *m_loc;252 253 m_rem = rem_sdp? rem_sdp->media[media_index] : NULL;254 m_loc= sdp_local->media[media_index];255 256 if (pj_stricmp(& m_loc->desc.transport, &ID_RTP_AVP) ||257 ( m_rem && pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP)))1093 pjmedia_sdp_media *loc_m, *rem_m; 1094 1095 rem_m = rem_sdp? rem_sdp->media[media_index] : NULL; 1096 loc_m = sdp_local->media[media_index]; 1097 1098 if (pj_stricmp(&loc_m->desc.transport, &STR_RTP_AVP) || 1099 (rem_m && pj_stricmp(&rem_m->desc.transport, &STR_RTP_AVP))) 258 1100 { 259 pjmedia_sdp_media_deactivate( pool, m_loc);1101 pjmedia_sdp_media_deactivate(sdp_pool, loc_m); 260 1102 return PJMEDIA_SDP_EINPROTO; 261 1103 } 262 1104 } 263 1105 264 /* If we are UAS, check that the incoming SDP contains support for ICE. */ 265 if (rem_sdp) { 266 const pjmedia_sdp_media *rem_m; 267 268 rem_m = rem_sdp->media[media_index]; 269 270 init_ice = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 271 "ice-ufrag", NULL) != NULL; 272 if (init_ice == PJ_FALSE) { 273 init_ice = pjmedia_sdp_attr_find2(rem_sdp->attr_count, 274 rem_sdp->attr, 275 "ice-ufrag", NULL) != NULL; 276 } 277 278 if (init_ice) { 279 init_ice = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 280 "candidate", NULL) != NULL; 1106 if (tp_ice->initial_sdp) { 1107 if (rem_sdp) { 1108 status = create_initial_answer(tp_ice, sdp_pool, sdp_local, 1109 rem_sdp, media_index); 1110 } else { 1111 status = create_initial_offer(tp_ice, sdp_pool, sdp_local, 1112 media_index); 281 1113 } 282 1114 } else { 283 init_ice = PJ_TRUE; 284 } 285 286 /* Init ICE */ 287 if (init_ice) { 288 pj_ice_sess_role ice_role; 289 enum { MAXLEN = 256 }; 290 pj_str_t ufrag, pass; 291 char *buffer; 1115 if (rem_sdp) { 1116 status = create_subsequent_answer(tp_ice, sdp_pool, sdp_local, 1117 rem_sdp, media_index); 1118 } else { 1119 status = create_subsequent_offer(tp_ice, sdp_pool, sdp_local, 1120 media_index); 1121 } 1122 } 1123 1124 if (status==PJ_SUCCESS) { 1125 if (rem_sdp) 1126 tp_ice->oa_role = ROLE_ANSWERER; 1127 else 1128 tp_ice->oa_role = ROLE_OFFERER; 1129 } 1130 1131 return status; 1132 } 1133 1134 1135 /* Start ICE session with the specified remote SDP */ 1136 static pj_status_t start_ice(struct transport_ice *tp_ice, 1137 pj_pool_t *tmp_pool, 1138 const pjmedia_sdp_session *rem_sdp, 1139 unsigned media_index) 1140 { 1141 pjmedia_sdp_media *rem_m = rem_sdp->media[media_index]; 1142 pjmedia_sdp_attr *ufrag_attr, *pwd_attr; 1143 pj_ice_sess_cand *cand; 1144 unsigned i, cand_cnt; 1145 pj_status_t status; 1146 1147 get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); 1148 1149 /* Allocate candidate array */ 1150 cand = (pj_ice_sess_cand*) 1151 pj_pool_calloc(tmp_pool, PJ_ICE_MAX_CAND, 1152 sizeof(pj_ice_sess_cand)); 1153 1154 /* Get all candidates in the media */ 1155 cand_cnt = 0; 1156 for (i=0; i<rem_m->attr_count && cand_cnt < PJ_ICE_MAX_CAND; ++i) { 292 1157 pjmedia_sdp_attr *attr; 293 unsigned comp; 294 295 ice_role = (rem_sdp==NULL ? PJ_ICE_SESS_ROLE_CONTROLLING : 296 PJ_ICE_SESS_ROLE_CONTROLLED); 297 298 ufrag.ptr = (char*) pj_pool_alloc(pool, PJ_ICE_UFRAG_LEN); 299 pj_create_random_string(ufrag.ptr, PJ_ICE_UFRAG_LEN); 300 ufrag.slen = PJ_ICE_UFRAG_LEN; 301 302 pass.ptr = (char*) pj_pool_alloc(pool, PJ_ICE_UFRAG_LEN); 303 pj_create_random_string(pass.ptr, PJ_ICE_UFRAG_LEN); 304 pass.slen = PJ_ICE_UFRAG_LEN; 305 306 status = pj_ice_strans_init_ice(tp_ice->ice_st, ice_role, 307 &ufrag, &pass); 308 if (status != PJ_SUCCESS) 309 return status; 310 311 /* Create ice-ufrag attribute */ 312 attr = pjmedia_sdp_attr_create(pool, "ice-ufrag", &ufrag); 313 sdp_local->attr[sdp_local->attr_count++] = attr; 314 315 /* Create ice-pwd attribute */ 316 attr = pjmedia_sdp_attr_create(pool, "ice-pwd", &pass); 317 sdp_local->attr[sdp_local->attr_count++] = attr; 318 319 /* Encode all candidates to SDP media */ 320 321 buffer = (char*) pj_pool_alloc(pool, MAXLEN); 322 323 for (comp=0; comp < tp_ice->comp_cnt; ++comp) { 324 unsigned cand_cnt; 325 pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; 326 327 cand_cnt = PJ_ARRAY_SIZE(cand); 328 status = pj_ice_strans_enum_cands(tp_ice->ice_st, comp+1, 329 &cand_cnt, cand); 330 if (status != PJ_SUCCESS) 331 return status; 332 333 for (i=0; i<cand_cnt; ++i) { 334 pjmedia_sdp_media *m; 335 pj_str_t value; 336 337 value.slen = print_sdp_cand_attr(buffer, MAXLEN, &cand[i]); 338 if (value.slen < 0) { 339 pj_assert(!"Not enough buffer to print candidate"); 340 return PJ_EBUG; 341 } 342 343 value.ptr = buffer; 344 attr = pjmedia_sdp_attr_create(pool, "candidate", &value); 345 m = sdp_local->media[media_index]; 346 m->attr[m->attr_count++] = attr; 347 } 348 } 349 } 350 351 /* Done */ 352 return PJ_SUCCESS; 353 } 354 355 356 /* Parse a=candidate line */ 357 static pj_status_t parse_cand(pj_pool_t *pool, 358 const pj_str_t *orig_input, 359 pj_ice_sess_cand *cand) 360 { 361 pj_str_t input; 362 char *token, *host; 363 pj_str_t s; 364 pj_status_t status = PJNATH_EICEINCANDSDP; 365 366 pj_bzero(cand, sizeof(*cand)); 367 pj_strdup_with_null(pool, &input, orig_input); 368 369 /* Foundation */ 370 token = strtok(input.ptr, " "); 371 if (!token) { 372 PJ_LOG(5,(THIS_FILE, "Expecting ICE foundation in candidate")); 373 goto on_return; 374 } 375 pj_strdup2(pool, &cand->foundation, token); 376 377 /* Component ID */ 378 token = strtok(NULL, " "); 379 if (!token) { 380 PJ_LOG(5,(THIS_FILE, "Expecting ICE component ID in candidate")); 381 goto on_return; 382 } 383 cand->comp_id = (pj_uint8_t) atoi(token); 384 385 /* Transport */ 386 token = strtok(NULL, " "); 387 if (!token) { 388 PJ_LOG(5,(THIS_FILE, "Expecting ICE transport in candidate")); 389 goto on_return; 390 } 391 if (pj_ansi_stricmp(token, "UDP") != 0) { 392 PJ_LOG(5,(THIS_FILE, 393 "Expecting ICE UDP transport only in candidate")); 394 goto on_return; 395 } 396 397 /* Priority */ 398 token = strtok(NULL, " "); 399 if (!token) { 400 PJ_LOG(5,(THIS_FILE, "Expecting ICE priority in candidate")); 401 goto on_return; 402 } 403 cand->prio = atoi(token); 404 405 /* Host */ 406 host = strtok(NULL, " "); 407 if (!host) { 408 PJ_LOG(5,(THIS_FILE, "Expecting ICE host in candidate")); 409 goto on_return; 410 } 411 if (pj_sockaddr_in_init(&cand->addr.ipv4, pj_cstr(&s, host), 0)) { 412 PJ_LOG(5,(THIS_FILE, 413 "Expecting ICE IPv4 transport address in candidate")); 414 goto on_return; 415 } 416 417 /* Port */ 418 token = strtok(NULL, " "); 419 if (!token) { 420 PJ_LOG(5,(THIS_FILE, "Expecting ICE port number in candidate")); 421 goto on_return; 422 } 423 cand->addr.ipv4.sin_port = pj_htons((pj_uint16_t)atoi(token)); 424 425 /* typ */ 426 token = strtok(NULL, " "); 427 if (!token) { 428 PJ_LOG(5,(THIS_FILE, "Expecting ICE \"typ\" in candidate")); 429 goto on_return; 430 } 431 if (pj_ansi_stricmp(token, "typ") != 0) { 432 PJ_LOG(5,(THIS_FILE, "Expecting ICE \"typ\" in candidate")); 433 goto on_return; 434 } 435 436 /* candidate type */ 437 token = strtok(NULL, " "); 438 if (!token) { 439 PJ_LOG(5,(THIS_FILE, "Expecting ICE candidate type in candidate")); 440 goto on_return; 441 } 442 443 if (pj_ansi_stricmp(token, "host") == 0) { 444 cand->type = PJ_ICE_CAND_TYPE_HOST; 445 446 } else if (pj_ansi_stricmp(token, "srflx") == 0) { 447 cand->type = PJ_ICE_CAND_TYPE_SRFLX; 448 449 } else if (pj_ansi_stricmp(token, "relay") == 0) { 450 cand->type = PJ_ICE_CAND_TYPE_RELAYED; 451 452 } else if (pj_ansi_stricmp(token, "prflx") == 0) { 453 cand->type = PJ_ICE_CAND_TYPE_PRFLX; 454 455 } else { 456 PJ_LOG(5,(THIS_FILE, "Invalid ICE candidate type %s in candidate", 457 token)); 458 goto on_return; 459 } 460 461 462 status = PJ_SUCCESS; 463 464 on_return: 465 return status; 466 } 467 468 469 /* Disable ICE when SDP from remote doesn't contain a=candidate line */ 470 static void set_no_ice(struct transport_ice *tp_ice, const char *reason) 471 { 472 PJ_LOG(4,(tp_ice->base.name, 473 "Disabling local ICE, reason=%s", reason)); 474 transport_media_stop(&tp_ice->base); 1158 1159 attr = rem_m->attr[i]; 1160 1161 if (pj_strcmp(&attr->name, &STR_CANDIDATE)!=0) 1162 continue; 1163 1164 /* Parse candidate */ 1165 status = parse_cand(tp_ice->base.name, tmp_pool, &attr->value, 1166 &cand[cand_cnt]); 1167 if (status != PJ_SUCCESS) { 1168 PJ_LOG(4,(tp_ice->base.name, 1169 "Error in parsing SDP candidate attribute '%.*s', " 1170 "candidate is ignored", 1171 (int)attr->value.slen, attr->value.ptr)); 1172 continue; 1173 } 1174 1175 cand_cnt++; 1176 } 1177 1178 /* Start ICE */ 1179 return pj_ice_strans_start_ice(tp_ice->ice_st, &ufrag_attr->value, 1180 &pwd_attr->value, cand_cnt, cand); 475 1181 } 476 1182 477 1183 478 1184 /* 479 * Start ICE checks when both offer and answer are available. 1185 * Start ICE checks when both offer and answer have been negotiated 1186 * by SDP negotiator. 480 1187 */ 481 1188 static pj_status_t transport_media_start(pjmedia_transport *tp, 482 pj_pool_t * pool,483 pjmedia_sdp_session *sdp_local,1189 pj_pool_t *tmp_pool, 1190 const pjmedia_sdp_session *sdp_local, 484 1191 const pjmedia_sdp_session *rem_sdp, 485 1192 unsigned media_index) 486 1193 { 487 1194 struct transport_ice *tp_ice = (struct transport_ice*)tp; 488 const pjmedia_sdp_attr *attr; 489 unsigned i, cand_cnt; 490 pj_ice_sess_cand *cand; 491 const pjmedia_sdp_media *sdp_med; 492 pj_bool_t remote_is_lite = PJ_FALSE; 493 pj_bool_t ice_mismatch = PJ_FALSE; 494 pjmedia_sdp_conn *conn = NULL; 495 pj_sockaddr conn_addr; 496 pj_bool_t conn_found_in_candidate = PJ_FALSE; 497 pj_str_t uname, pass; 1195 pjmedia_sdp_media *rem_m; 1196 enum oa_role current_oa_role; 1197 pj_bool_t initial_oa; 498 1198 pj_status_t status; 499 1199 500 PJ_ASSERT_RETURN(tp && pool && rem_sdp, PJ_EINVAL);1200 PJ_ASSERT_RETURN(tp && tmp_pool && rem_sdp, PJ_EINVAL); 501 1201 PJ_ASSERT_RETURN(media_index < rem_sdp->media_count, PJ_EINVAL); 502 1202 503 sdp_med = rem_sdp->media[media_index]; 504 505 /* Validate media transport */ 506 /* By now, this transport only support RTP/AVP transport */ 507 if ((tp_ice->media_option & PJMEDIA_TPMED_NO_TRANSPORT_CHECKING) == 0) { 508 pjmedia_sdp_media *m_rem, *m_loc; 509 510 m_rem = rem_sdp->media[media_index]; 511 m_loc = sdp_local->media[media_index]; 512 513 if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP) || 514 (pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP))) 1203 rem_m = rem_sdp->media[media_index]; 1204 1205 initial_oa = tp_ice->initial_sdp; 1206 current_oa_role = tp_ice->oa_role; 1207 1208 /* SDP has been negotiated */ 1209 tp_ice->initial_sdp = PJ_FALSE; 1210 tp_ice->oa_role = ROLE_NONE; 1211 1212 /* Nothing to do if we don't have ICE session */ 1213 if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) { 1214 return PJ_SUCCESS; 1215 } 1216 1217 /* Processing depends on the offer/answer role */ 1218 if (current_oa_role == ROLE_OFFERER) { 1219 /* 1220 * We are offerer. So this will be the first time we see the 1221 * remote's SDP. 1222 */ 1223 struct sdp_state answer_state; 1224 1225 /* Verify the answer */ 1226 status = verify_ice_sdp(tp_ice, tmp_pool, rem_sdp, media_index, 1227 PJ_ICE_SESS_ROLE_CONTROLLING, &answer_state); 1228 if (status != PJ_SUCCESS) { 1229 /* Something wrong in the SDP answer */ 1230 set_no_ice(tp_ice, "Invalid remote SDP answer", status); 1231 return status; 1232 } 1233 1234 /* Does it have ICE? */ 1235 if (answer_state.match_comp_cnt == 0) { 1236 /* Remote doesn't support ICE */ 1237 set_no_ice(tp_ice, "Remote answer doesn't support ICE", 1238 PJ_SUCCESS); 1239 return PJ_SUCCESS; 1240 } 1241 1242 /* Check if remote has reported ice-mismatch */ 1243 if (pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, 1244 &STR_ICE_MISMATCH, NULL) != NULL) 515 1245 { 516 pjmedia_sdp_media_deactivate(pool, m_loc); 517 return PJMEDIA_SDP_EINPROTO; 518 } 519 } 520 521 /* Get the SDP connection for the media stream. 522 * We'll verify later if the SDP connection address is specified 523 * as one of the candidate. 524 */ 525 conn = sdp_med->conn; 526 if (conn == NULL) 527 conn = rem_sdp->conn; 528 529 if (conn == NULL) { 530 /* Unable to find SDP connection */ 531 return PJMEDIA_SDP_EMISSINGCONN; 532 } 533 534 pj_sockaddr_in_init(&conn_addr.ipv4, &conn->addr, 535 (pj_uint16_t)sdp_med->desc.port); 536 537 /* Find ice-ufrag attribute in media descriptor */ 538 attr = pjmedia_sdp_attr_find2(sdp_med->attr_count, sdp_med->attr, 539 "ice-ufrag", NULL); 540 if (attr == NULL) { 541 /* Find ice-ufrag attribute in session descriptor */ 542 attr = pjmedia_sdp_attr_find2(rem_sdp->attr_count, rem_sdp->attr, 543 "ice-ufrag", NULL); 544 if (attr == NULL) { 545 set_no_ice(tp_ice, "ice-ufrag attribute not found"); 1246 /* Remote has reported ice-mismatch */ 1247 set_no_ice(tp_ice, 1248 "Remote answer contains 'ice-mismatch' attribute", 1249 PJ_SUCCESS); 546 1250 return PJ_SUCCESS; 547 1251 } 548 } 549 uname = attr->value; 550 551 /* Find ice-pwd attribute in media descriptor */ 552 attr = pjmedia_sdp_attr_find2(sdp_med->attr_count, sdp_med->attr, 553 "ice-pwd", NULL); 554 if (attr == NULL) { 555 /* Find ice-pwd attribute in session descriptor */ 556 attr = pjmedia_sdp_attr_find2(rem_sdp->attr_count, rem_sdp->attr, 557 "ice-pwd", NULL); 558 if (attr == NULL) { 559 set_no_ice(tp_ice, "ice-pwd attribute not found"); 1252 1253 /* Check if remote has indicated a restart */ 1254 if (answer_state.ice_restart) { 1255 PJ_LOG(2,(tp_ice->base.name, 1256 "Warning: remote has signalled ICE restart in SDP " 1257 "answer which is disallowed. Remote ICE negotiation" 1258 " may fail.")); 1259 } 1260 1261 /* Check if the answer itself is mismatched */ 1262 if (answer_state.ice_mismatch) { 1263 /* This happens either when a B2BUA modified remote answer but 1264 * strangely didn't modify our offer, or remote is not capable 1265 * of detecting mismatch in our offer (it didn't put 1266 * 'ice-mismatch' attribute in the answer). 1267 */ 1268 PJ_LOG(2,(tp_ice->base.name, 1269 "Warning: remote answer mismatch, but it does not " 1270 "reject our offer with 'ice-mismatch'. ICE negotiation " 1271 "may fail")); 1272 } 1273 1274 /* Do nothing if ICE is complete or running */ 1275 if (pj_ice_strans_sess_is_running(tp_ice->ice_st)) { 1276 PJ_LOG(4,(tp_ice->base.name, 1277 "Ignored offer/answer because ICE is running")); 560 1278 return PJ_SUCCESS; 561 1279 } 562 } 563 pass = attr->value; 564 565 /* Allocate candidate array */ 566 cand = (pj_ice_sess_cand*) 567 pj_pool_calloc(pool, PJ_ICE_MAX_CAND, sizeof(pj_ice_sess_cand)); 568 569 /* Get all candidates in the media */ 570 cand_cnt = 0; 571 for (i=0; i<sdp_med->attr_count && cand_cnt < PJ_ICE_MAX_CAND; ++i) { 572 pjmedia_sdp_attr *attr; 573 574 attr = sdp_med->attr[i]; 575 576 /* Detect if remote is ICE lite */ 577 if (pj_stricmp(&attr->name, &STR_ICE_LITE)==0) { 578 remote_is_lite = PJ_TRUE; 579 continue; 580 } 581 582 /* Detect if remote has reported ICE mismatch */ 583 if (pj_stricmp(&attr->name, &STR_ICE_MISMATCH)==0) { 584 ice_mismatch = PJ_TRUE; 585 continue; 586 } 587 588 if (pj_stricmp(&attr->name, &STR_CANDIDATE)!=0) 589 continue; 590 591 /* Parse candidate */ 592 status = parse_cand(pool, &attr->value, &cand[cand_cnt]); 593 if (status != PJ_SUCCESS) { 594 PJ_LOG(4,(THIS_FILE, 595 "Error in parsing SDP candidate attribute, " 596 "candidate is ignored")); 597 continue; 598 } 599 600 /* Check if this candidate is equal to the connection line */ 601 if (!conn_found_in_candidate && 602 pj_memcmp(&conn_addr.ipv4, &cand[cand_cnt].addr.ipv4, 603 sizeof(pj_sockaddr_in))==0) 604 { 605 conn_found_in_candidate = PJ_TRUE; 606 } 607 608 cand_cnt++; 609 } 610 611 /* Handle ice-mismatch case */ 612 if (ice_mismatch) { 613 set_no_ice(tp_ice, "remote reported ice-mismatch"); 614 return PJ_SUCCESS; 615 } 616 617 /* Handle case where SDP connection address is not specified as 618 * one of the candidate. 619 */ 620 if (!conn_found_in_candidate) { 621 set_no_ice(tp_ice, "local reported ice-mismatch"); 622 return PJ_SUCCESS; 623 } 624 625 /* If our role was controlled but it turns out that remote is 626 * a lite implementation, change our role to controlling. 627 */ 628 if (remote_is_lite && 629 pj_ice_strans_get_role(tp_ice->ice_st) == PJ_ICE_SESS_ROLE_CONTROLLED) 630 { 631 pj_ice_strans_change_role(tp_ice->ice_st, 632 PJ_ICE_SESS_ROLE_CONTROLLING); 633 } 634 635 /* Start ICE */ 636 return pj_ice_strans_start_ice(tp_ice->ice_st, &uname, &pass, 637 cand_cnt, cand); 1280 1281 if (pj_ice_strans_sess_is_complete(tp_ice->ice_st)) { 1282 PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged")); 1283 return PJ_SUCCESS; 1284 } 1285 1286 /* Start ICE */ 1287 1288 } else { 1289 /* 1290 * We are answerer. We've seen and negotiated remote's SDP 1291 * before, and the result is in "rem_offer_state". 1292 */ 1293 const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; 1294 1295 /* Check for ICE in remote offer */ 1296 if (tp_ice->rem_offer_state.match_comp_cnt == 0) { 1297 /* No ICE attribute present */ 1298 set_no_ice(tp_ice, "Remote no longer offers ICE", 1299 PJ_SUCCESS); 1300 return PJ_SUCCESS; 1301 } 1302 1303 /* Check for ICE ice_mismatch condition in the offer */ 1304 if (tp_ice->rem_offer_state.ice_mismatch) { 1305 set_no_ice(tp_ice, "Remote offer mismatch: ", 1306 PJNATH_EICEMISMATCH); 1307 return PJ_SUCCESS; 1308 } 1309 1310 /* If ICE is complete and remote doesn't request restart, 1311 * then leave the session as is. 1312 */ 1313 if (!initial_oa && tp_ice->rem_offer_state.ice_restart == PJ_FALSE) { 1314 /* Remote has not requested ICE restart, so session is 1315 * unchanged. 1316 */ 1317 PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged")); 1318 return PJ_SUCCESS; 1319 } 1320 1321 /* Either remote has requested ICE restart or this is our 1322 * first answer. 1323 */ 1324 1325 /* Stop ICE */ 1326 if (!initial_oa) { 1327 set_no_ice(tp_ice, "restarting by remote request..", PJ_SUCCESS); 1328 1329 /* We have put new ICE ufrag and pwd in the answer. Now 1330 * create a new ICE session with that ufrag/pwd pair. 1331 */ 1332 get_ice_attr(sdp_local, sdp_local->media[media_index], 1333 &ufrag_attr, &pwd_attr); 1334 status = pj_ice_strans_init_ice(tp_ice->ice_st, 1335 tp_ice->rem_offer_state.local_role, 1336 &ufrag_attr->value, 1337 &pwd_attr->value); 1338 if (status != PJ_SUCCESS) { 1339 PJ_LOG(1,(tp_ice->base.name, 1340 "ICE re-initialization failed (status=%d)!", 1341 status)); 1342 return status; 1343 } 1344 } 1345 1346 /* start ICE */ 1347 } 1348 1349 /* Now start ICE */ 1350 status = start_ice(tp_ice, tmp_pool, rem_sdp, media_index); 1351 if (status != PJ_SUCCESS) { 1352 PJ_LOG(1,(tp_ice->base.name, 1353 "ICE restart failed (status=%d)!", 1354 status)); 1355 return status; 1356 } 1357 1358 /* Done */ 1359 1360 return PJ_SUCCESS; 638 1361 } 639 1362 … … 642 1365 { 643 1366 struct transport_ice *tp_ice = (struct transport_ice*)tp; 644 return pj_ice_strans_stop_ice(tp_ice->ice_st); 1367 1368 set_no_ice(tp_ice, "media stop requested", PJ_SUCCESS); 1369 1370 return PJ_SUCCESS; 645 1371 } 646 1372
Note: See TracChangeset
for help on using the changeset viewer.