Ignore:
Timestamp:
Jun 19, 2008 2:10:28 PM (16 years ago)
Author:
bennylp
Message:

Ticket #549: major modification in media transport API to support more offer/answer scenarios

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c

    r1989 r2032  
    2424 
    2525#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 
     32enum oa_role 
     33{ 
     34    ROLE_NONE, 
     35    ROLE_OFFERER, 
     36    ROLE_ANSWERER 
     37}; 
     38 
     39struct 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}; 
    2846 
    2947struct transport_ice 
     
    3250    pj_pool_t           *pool; 
    3351    int                  af; 
     52 
    3453    unsigned             comp_cnt; 
    3554    pj_ice_strans       *ice_st; 
     55 
    3656    pjmedia_ice_cb       cb; 
    3757    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            */ 
    3862 
    3963    void                *stream; 
     
    85109                                       pj_pool_t *pool, 
    86110                                       unsigned options, 
    87                                        pjmedia_sdp_session *sdp_local, 
    88111                                       const pjmedia_sdp_session *rem_sdp, 
    89112                                       unsigned media_index); 
    90 static pj_status_t transport_media_start (pjmedia_transport *tp, 
     113static 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); 
     118static pj_status_t transport_media_start(pjmedia_transport *tp, 
    91119                                       pj_pool_t *pool, 
    92                                        pjmedia_sdp_session *sdp_local, 
     120                                       const pjmedia_sdp_session *sdp_local, 
    93121                                       const pjmedia_sdp_session *rem_sdp, 
    94122                                       unsigned media_index); 
     
    121149    &transport_send_rtcp2, 
    122150    &transport_media_create, 
     151    &transport_encode_sdp, 
    123152    &transport_media_start, 
    124153    &transport_media_stop, 
     
    127156}; 
    128157 
    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  
     158static const pj_str_t STR_RTP_AVP       = { "RTP/AVP", 7 }; 
     159static const pj_str_t STR_CANDIDATE     = { "candidate", 9}; 
     160static const pj_str_t STR_REM_CAND      = { "remote-candidates", 17 }; 
     161static const pj_str_t STR_ICE_LITE      = { "ice-lite", 8}; 
     162static const pj_str_t STR_ICE_MISMATCH  = { "ice-mismatch", 12}; 
     163static const pj_str_t STR_ICE_UFRAG     = { "ice-ufrag", 9 }; 
     164static const pj_str_t STR_ICE_PWD       = { "ice-pwd", 7 }; 
     165static const pj_str_t STR_IP4           = { "IP4", 3 }; 
     166static const pj_str_t STR_IP6           = { "IP6", 3 }; 
     167static const pj_str_t STR_RTCP          = { "rtcp", 4 }; 
     168 
     169enum { 
     170    COMP_RTP = 1, 
     171    COMP_RTCP = 2 
     172}; 
    133173 
    134174/* 
     
    158198    tp_ice->base.op = &transport_ice_op; 
    159199    tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE; 
     200    tp_ice->initial_sdp = PJ_TRUE; 
     201    tp_ice->oa_role = ROLE_NONE; 
    160202 
    161203    if (cb) 
     
    184226    return PJ_SUCCESS; 
    185227} 
     228 
     229/* Disable ICE when SDP from remote doesn't contain a=candidate line */ 
     230static 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 
    186246 
    187247/* Create SDP candidate attribute */ 
     
    228288} 
    229289 
     290 
     291/* Get ice-ufrag and ice-pwd attribute */ 
     292static 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 */ 
     322static 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 */ 
     337static 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 */ 
     576static 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 
     691on_return: 
     692    return status; 
     693} 
     694 
     695 
     696/* Create initial SDP offer */ 
     697static 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 */ 
     717static 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 */ 
     900static 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 */ 
     950static 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 */ 
     969static 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 
    2301048/* 
    2311049 * For both UAC and UAS, pass in the SDP before sending it to remote. 
     
    2331051 */ 
    2341052static pj_status_t transport_media_create(pjmedia_transport *tp, 
    235                                           pj_pool_t *pool, 
     1053                                          pj_pool_t *sdp_pool, 
    2361054                                          unsigned options, 
    237                                           pjmedia_sdp_session *sdp_local, 
    2381055                                          const pjmedia_sdp_session *rem_sdp, 
    2391056                                          unsigned media_index) 
    2401057{ 
    2411058    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; 
    2441060    pj_status_t status; 
    2451061 
     1062    PJ_UNUSED_ARG(media_index); 
     1063    PJ_UNUSED_ARG(sdp_pool); 
     1064 
    2461065    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 
     1081static 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; 
    2471089 
    2481090    /* Validate media transport */ 
    2491091    /* For now, this transport only support RTP/AVP transport */ 
    2501092    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))) 
    2581100        { 
    259             pjmedia_sdp_media_deactivate(pool, m_loc); 
     1101            pjmedia_sdp_media_deactivate(sdp_pool, loc_m); 
    2601102            return PJMEDIA_SDP_EINPROTO; 
    2611103        } 
    2621104    } 
    2631105 
    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); 
    2811113        } 
    2821114    } 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 */ 
     1136static 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) { 
    2921157        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); 
    4751181} 
    4761182 
    4771183 
    4781184/* 
    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. 
    4801187 */ 
    4811188static 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, 
    4841191                                         const pjmedia_sdp_session *rem_sdp, 
    4851192                                         unsigned media_index) 
    4861193{ 
    4871194    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; 
    4981198    pj_status_t status; 
    4991199 
    500     PJ_ASSERT_RETURN(tp && pool && rem_sdp, PJ_EINVAL); 
     1200    PJ_ASSERT_RETURN(tp && tmp_pool && rem_sdp, PJ_EINVAL); 
    5011201    PJ_ASSERT_RETURN(media_index < rem_sdp->media_count, PJ_EINVAL); 
    5021202 
    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) 
    5151245        { 
    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); 
    5461250            return PJ_SUCCESS; 
    5471251        } 
    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")); 
    5601278            return PJ_SUCCESS; 
    5611279        } 
    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; 
    6381361} 
    6391362 
     
    6421365{ 
    6431366    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; 
    6451371} 
    6461372 
Note: See TracChangeset for help on using the changeset viewer.