/* * The encode_sdp() is called when we're about to send SDP to remote party, * either as SDP offer or as SDP answer. */ static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* If "rem_sdp" is not NULL, it means we're encoding SDP answer. You may * do some more checking on the SDP's once again to make sure that * everything is okay before we send SDP. */ if (rem_sdp) { /* Do checking stuffs here.. */ } /* You may do anything to the local_sdp, e.g. adding new attributes, or * even modifying the SDP if you want. */ if (0) { /* Say we add a proprietary attribute here.. */ pjmedia_sdp_attr *my_attr; my_attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr); pj_strdup2(sdp_pool, &my_attr->name, "X-zrtp"); pj_strdup2(sdp_pool, &my_attr->value, "some value"); pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count, local_sdp->media[media_index]->attr, my_attr); } /* And then pass the call to slave transport to let it encode its * information in the SDP. You may choose to call encode_sdp() to slave * first before adding your custom attributes if you want. */ return pjmedia_transport_encode_sdp(zrtp->slave_tp, sdp_pool, local_sdp, rem_sdp, media_index); }
static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pjmedia_sdp_media *m_rem, *m_loc; enum { MAXLEN = 512 }; char buffer[MAXLEN]; int buffer_len; pj_status_t status; pjmedia_sdp_attr *attr; pj_str_t attr_value; unsigned i, j; PJ_ASSERT_RETURN(tp && sdp_pool && sdp_local, PJ_EINVAL); srtp->offerer_side = sdp_remote == NULL; m_rem = sdp_remote ? sdp_remote->media[media_index] : NULL; m_loc = sdp_local->media[media_index]; /* Bypass if media transport is not RTP/AVP or RTP/SAVP */ if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP) != 0 && pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0) goto BYPASS_SRTP; /* If the media is inactive, do nothing. */ if (pjmedia_sdp_media_find_attr(m_loc, &ID_INACTIVE, NULL) || (m_rem && pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL))) goto BYPASS_SRTP; /* Check remote media transport & set local media transport * based on SRTP usage option. */ if (srtp->offerer_side) { /* Generate transport */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: goto BYPASS_SRTP; case PJMEDIA_SRTP_OPTIONAL: m_loc->desc.transport = (srtp->peer_use == PJMEDIA_SRTP_MANDATORY)? ID_RTP_SAVP : ID_RTP_AVP; break; case PJMEDIA_SRTP_MANDATORY: m_loc->desc.transport = ID_RTP_SAVP; break; } /* Generate crypto attribute if not yet */ if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) { for (i=0; i<srtp->setting.crypto_count; ++i) { /* Offer crypto-suites based on setting. */ buffer_len = MAXLEN; status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, &srtp->setting.crypto[i], i+1); if (status != PJ_SUCCESS) return status; /* If buffer_len==0, just skip the crypto attribute. */ if (buffer_len) { pj_strset(&attr_value, buffer, buffer_len); attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr, &attr_value); m_loc->attr[m_loc->attr_count++] = attr; } } } } else { /* Answerer side */ pj_assert(sdp_remote && m_rem); /* Generate transport */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; goto BYPASS_SRTP; case PJMEDIA_SRTP_OPTIONAL: m_loc->desc.transport = m_rem->desc.transport; break; case PJMEDIA_SRTP_MANDATORY: if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0) return PJMEDIA_SRTP_ESDPINTRANSPORT; m_loc->desc.transport = ID_RTP_SAVP; break; } /* Generate crypto attribute if not yet */ if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) { pjmedia_srtp_crypto tmp_rx_crypto; pj_bool_t has_crypto_attr = PJ_FALSE; int matched_idx = -1; int chosen_tag = 0; int tags[64]; /* assume no more than 64 crypto attrs in a media */ unsigned cr_attr_count = 0; /* Find supported crypto-suite, get the tag, and assign policy_local */ for (i=0; i<m_rem->attr_count; ++i) { if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0) continue; has_crypto_attr = PJ_TRUE; status = parse_attr_crypto(srtp->pool, m_rem->attr[i], &tmp_rx_crypto, &tags[cr_attr_count]); if (status != PJ_SUCCESS) return status; /* Check duplicated tag */ for (j=0; j<cr_attr_count; ++j) { if (tags[j] == tags[cr_attr_count]) { DEACTIVATE_MEDIA(sdp_pool, m_loc); return PJMEDIA_SRTP_ESDPDUPCRYPTOTAG; } } if (matched_idx == -1) { /* lets see if the crypto-suite offered is supported */ for (j=0; j<srtp->setting.crypto_count; ++j) if (pj_stricmp(&tmp_rx_crypto.name, &srtp->setting.crypto[j].name) == 0) { int cs_idx = get_crypto_idx(&tmp_rx_crypto.name); /* Force to use test key */ /* bad keys for snom: */ //char *hex_test_key = "58b29c5c8f42308120ce857e439f2d" // "7810a8b10ad0b1446be5470faea496"; //char *hex_test_key = "20a26aac7ba062d356ff52b61e3993" // "ccb78078f12c64db94b9c294927fd0"; //pj_str_t *test_key = &srtp->setting.crypto[j].key; //char *raw_test_key = pj_pool_zalloc(srtp->pool, 64); //hex_string_to_octet_string( // raw_test_key, // hex_test_key, // strlen(hex_test_key)); //pj_strset(test_key, raw_test_key, // crypto_suites[cs_idx].cipher_key_len); /* EO Force to use test key */ if (tmp_rx_crypto.key.slen != (int)crypto_suites[cs_idx].cipher_key_len) return PJMEDIA_SRTP_EINKEYLEN; srtp->rx_policy_neg = tmp_rx_crypto; chosen_tag = tags[cr_attr_count]; matched_idx = j; break; } } cr_attr_count++; } /* Check crypto negotiation result */ switch (srtp->setting.use) { case PJMEDIA_SRTP_DISABLED: pj_assert(!"Should never reach here"); break; case PJMEDIA_SRTP_OPTIONAL: /* bypass SRTP when no crypto-attr and remote uses RTP/AVP */ if (!has_crypto_attr && pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) goto BYPASS_SRTP; /* bypass SRTP when nothing match and remote uses RTP/AVP */ else if (matched_idx == -1 && pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0) goto BYPASS_SRTP; break; case PJMEDIA_SRTP_MANDATORY: /* Do nothing, intentional */ break; } /* No crypto attr */ if (!has_crypto_attr) { DEACTIVATE_MEDIA(sdp_pool, m_loc); return PJMEDIA_SRTP_ESDPREQCRYPTO; } /* No crypto match */ if (matched_idx == -1) { DEACTIVATE_MEDIA(sdp_pool, m_loc); return PJMEDIA_SRTP_ENOTSUPCRYPTO; } /* we have to generate crypto answer, * with srtp->tx_policy_neg matched the offer * and rem_tag contains matched offer tag. */ buffer_len = MAXLEN; status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len, &srtp->setting.crypto[matched_idx], chosen_tag); if (status != PJ_SUCCESS) return status; srtp->tx_policy_neg = srtp->setting.crypto[matched_idx]; /* If buffer_len==0, just skip the crypto attribute. */ if (buffer_len) { pj_strset(&attr_value, buffer, buffer_len); attr = pjmedia_sdp_attr_create(sdp_pool, ID_CRYPTO.ptr, &attr_value); m_loc->attr[m_loc->attr_count++] = attr; } /* At this point, we get valid rx_policy_neg & tx_policy_neg. */ } } goto PROPAGATE_MEDIA_CREATE; BYPASS_SRTP: srtp->bypass_srtp = PJ_TRUE; PROPAGATE_MEDIA_CREATE: return pjmedia_transport_encode_sdp(srtp->member_tp, sdp_pool, sdp_local, sdp_remote, media_index); }
/* * The encode_sdp() is called when we're about to send SDP to remote party, * either as SDP offer or as SDP answer. */ static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct tp_zrtp *zrtp = (struct tp_zrtp*)tp; int32_t numVersions, i; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* If "rem_sdp" is not NULL, it means we're encoding SDP answer. You may * do some more checking on the SDP's once again to make sure that * everything is okay before we send SDP. */ if (rem_sdp) { /* Do checking stuffs here.. */ } /* Add zrtp-hash attributes to both INVITE and 200 OK. */ numVersions = zrtp_getNumberSupportedVersions(zrtp->zrtpCtx); for (i = 0; i < numVersions; i++) { char *zrtp_hello_hash = zrtp_getHelloHash(zrtp->zrtpCtx, i); if (zrtp_hello_hash && *zrtp_hello_hash) { int zrtp_hello_hash_len = strlen(zrtp_hello_hash); pj_str_t *zrtp_hash_str = PJ_POOL_ALLOC_T(sdp_pool, pj_str_t); pjmedia_sdp_attr *zrtp_hash = NULL; zrtp_hash_str->ptr = zrtp_hello_hash; zrtp_hash_str->slen = zrtp_hello_hash_len; zrtp_hash = pjmedia_sdp_attr_create(sdp_pool, "zrtp-hash", zrtp_hash_str); if (zrtp_hash && pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count, local_sdp->media[media_index]->attr, zrtp_hash) == PJ_SUCCESS) { PJ_LOG(4, (THIS_FILE, "attribute added: a=zrtp-hash:%s", zrtp_hello_hash)); } else { PJ_LOG(4, (THIS_FILE, "error adding attribute: a=zrtp-hash:%s", zrtp_hello_hash)); } } } /* You may do anything to the local_sdp, e.g. adding new attributes, or * even modifying the SDP if you want. */ if (0) { /* Say we add a proprietary attribute here.. */ pjmedia_sdp_attr *my_attr; my_attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr); pj_strdup2(sdp_pool, &my_attr->name, "X-zrtp"); pj_strdup2(sdp_pool, &my_attr->value, "some value"); pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count, local_sdp->media[media_index]->attr, my_attr); } /* And then pass the call to slave transport to let it encode its * information in the SDP. You may choose to call encode_sdp() to slave * first before adding your custom attributes if you want. */ return pjmedia_transport_encode_sdp(zrtp->slave_tp, sdp_pool, local_sdp, rem_sdp, media_index); }
static pj_status_t transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pj_status_t last_err_st = PJ_EBUG; pj_status_t status; unsigned i; PJ_ASSERT_RETURN(tp && sdp_pool && sdp_local, PJ_EINVAL); pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg)); pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg)); srtp->offerer_side = (sdp_remote == NULL); status = pjmedia_transport_encode_sdp(srtp->member_tp, sdp_pool, sdp_local, sdp_remote, media_index); if (status != PJ_SUCCESS || srtp->bypass_srtp) return status; /* Invoke encode_sdp() of all keying methods */ for (i=0; i < srtp->keying_cnt; ) { pj_status_t st; st = pjmedia_transport_encode_sdp(srtp->keying[i], sdp_pool, sdp_local, sdp_remote, media_index); if (st != PJ_SUCCESS) { /* This keying method returns error, remove it */ pj_array_erase(srtp->keying, sizeof(srtp->keying[0]), srtp->keying_cnt, i); srtp->keying_cnt--; last_err_st = st; continue; } if (!srtp_crypto_empty(&srtp->tx_policy_neg) && !srtp_crypto_empty(&srtp->rx_policy_neg)) { /* SRTP nego is done, let's destroy any other keying. */ unsigned j; for (j = 0; j < srtp->keying_cnt; ++j) { if (j != i) pjmedia_transport_close(srtp->keying[j]); } srtp->keying_cnt = 1; srtp->keying[0] = srtp->keying[i]; srtp->keying_pending_cnt = 0; break; } i++; } /* All keying method failed to process remote SDP? */ if (srtp->keying_cnt == 0) return last_err_st; return PJ_SUCCESS; }