Compare commits
24 Commits
Author | SHA1 | Date |
---|---|---|
Benny Prijono | 94b8179c12 | |
Benny Prijono | 6a3899c4a9 | |
Benny Prijono | 19553c2a8c | |
Benny Prijono | a35e43eff6 | |
Benny Prijono | 700c4e198c | |
Nanang Izzuddin | ef5af745eb | |
Benny Prijono | b16bd3622e | |
Benny Prijono | 360c04183d | |
Benny Prijono | 5c7039612e | |
Benny Prijono | 42f11696a7 | |
Benny Prijono | b7d38a0d68 | |
Benny Prijono | 93228364cb | |
Benny Prijono | e4c51d63ee | |
Benny Prijono | ffc4ebd95b | |
Nanang Izzuddin | 23e117f78e | |
Benny Prijono | fcf385d5b5 | |
Benny Prijono | 7040288974 | |
Benny Prijono | 06306be201 | |
Benny Prijono | f4b8e93569 | |
Nanang Izzuddin | 2efb80bba6 | |
Nanang Izzuddin | f46451ae6d | |
Nanang Izzuddin | 9372e496ff | |
Nanang Izzuddin | 94bca3319f | |
Nanang Izzuddin | e5eb59cc67 |
|
@ -28,6 +28,7 @@
|
|||
#include <pj/os.h>
|
||||
#include <pj/pool.h>
|
||||
#include <pj/pool_buf.h>
|
||||
#include <pj/rand.h>
|
||||
#include <pj/string.h>
|
||||
#include <pj/sock.h>
|
||||
#include <pj/timer.h>
|
||||
|
@ -783,6 +784,7 @@ PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver,
|
|||
q = alloc_qnode(resolver, options, user_data, cb);
|
||||
|
||||
/* Save the ID and key */
|
||||
/* TODO: dnsext-forgery-resilient: randomize id for security */
|
||||
q->id = resolver->last_id++;
|
||||
if (resolver->last_id == 0)
|
||||
resolver->last_id = 1;
|
||||
|
|
|
@ -233,17 +233,19 @@ static struct ipp_codec {
|
|||
ipp_codec[] =
|
||||
{
|
||||
# if PJMEDIA_HAS_INTEL_IPP_CODEC_AMR
|
||||
/* AMR-NB SID seems to produce noise, so let's just disable its VAD. */
|
||||
{1, "AMR", PJMEDIA_RTP_PT_AMR, &USC_GSMAMR_Fxns, 8000, 1, 160,
|
||||
5900, 12200, 4, 1, 1,
|
||||
&predecode_amr, &parse_amr, &pack_amr
|
||||
/*, {1, {{{"octet-align", 11}, {"1", 1}}} } */
|
||||
7400, 12200, 2, 0, 1,
|
||||
&predecode_amr, &parse_amr, &pack_amr,
|
||||
{1, {{{"octet-align", 11}, {"1", 1}}} }
|
||||
},
|
||||
# endif
|
||||
|
||||
# if PJMEDIA_HAS_INTEL_IPP_CODEC_AMRWB
|
||||
{1, "AMR-WB", PJMEDIA_RTP_PT_AMRWB, &USC_AMRWB_Fxns, 16000, 1, 320,
|
||||
15850, 23850, 1, 1, 1,
|
||||
&predecode_amr, &parse_amr, &pack_amr
|
||||
&predecode_amr, &parse_amr, &pack_amr,
|
||||
{1, {{{"octet-align", 11}, {"1", 1}}} }
|
||||
},
|
||||
# endif
|
||||
|
||||
|
@ -560,7 +562,14 @@ static pj_status_t parse_amr(ipp_private_t *codec_data, void *pkt,
|
|||
|
||||
/* Check Change Mode Request. */
|
||||
if ((setting->amr_nb && cmr <= 7) || (!setting->amr_nb && cmr <= 8)) {
|
||||
struct ipp_codec *ippc = &ipp_codec[codec_data->codec_idx];
|
||||
|
||||
s->enc_mode = cmr;
|
||||
codec_data->info->params.modes.bitrate = s->enc_setting.amr_nb?
|
||||
pjmedia_codec_amrnb_bitrates[s->enc_mode] :
|
||||
pjmedia_codec_amrwb_bitrates[s->enc_mode];
|
||||
ippc->fxns->std.Control(&codec_data->info->params.modes,
|
||||
codec_data->enc);
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
|
@ -1033,7 +1042,7 @@ static pj_status_t ipp_codec_open( pjmedia_codec *codec,
|
|||
codec_data->info->params.direction = USC_DECODE;
|
||||
|
||||
/* Not sure if VAD affects decoder, just try to be safe */
|
||||
codec_data->info->params.modes.vad = ippc->has_native_vad;
|
||||
//codec_data->info->params.modes.vad = ippc->has_native_vad;
|
||||
|
||||
/* Get number of memory blocks needed by the decoder */
|
||||
if (USC_NoError != ippc->fxns->std.NumAlloc(&codec_data->info->params,
|
||||
|
@ -1084,34 +1093,79 @@ static pj_status_t ipp_codec_open( pjmedia_codec *codec,
|
|||
if (ippc->pt == PJMEDIA_RTP_PT_AMR || ippc->pt == PJMEDIA_RTP_PT_AMRWB) {
|
||||
amr_settings_t *s;
|
||||
pj_uint8_t octet_align = 0;
|
||||
const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
|
||||
pj_int8_t enc_mode = -1;
|
||||
|
||||
/* Check AMR specific attributes */
|
||||
|
||||
/* Check octet-align */
|
||||
for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
|
||||
/* octet-align, one of the parameters that must have same value
|
||||
* in offer & answer (RFC 4867 Section 8.3.1). Just check fmtp
|
||||
* in the decoder side, since it's value is guaranteed to fulfil
|
||||
* above requirement (by SDP negotiator).
|
||||
*/
|
||||
const pj_str_t STR_FMTP_OCTET_ALIGN = {"octet-align", 11};
|
||||
|
||||
if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name,
|
||||
&STR_FMTP_OCTET_ALIGN) == 0)
|
||||
{
|
||||
octet_align=(pj_uint8_t)
|
||||
(pj_strtoul(&attr->setting.dec_fmtp.param[i].val));
|
||||
pj_strtoul(&attr->setting.dec_fmtp.param[i].val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
|
||||
/* mode-set */
|
||||
const pj_str_t STR_FMTP_MODE_SET = {"mode-set", 8};
|
||||
|
||||
if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name,
|
||||
&STR_FMTP_MODE_SET) == 0)
|
||||
{
|
||||
pj_int8_t tmp;
|
||||
|
||||
/* Just get the first value. */
|
||||
tmp = (pj_int8_t)
|
||||
pj_strtoul(&attr->setting.enc_fmtp.param[i].val);
|
||||
|
||||
if ((ippc->pt == PJMEDIA_RTP_PT_AMR && tmp > 0 && tmp < 8) ||
|
||||
(ippc->pt == PJMEDIA_RTP_PT_AMRWB && tmp > 0 && tmp < 9))
|
||||
{
|
||||
enc_mode = tmp;
|
||||
PJ_LOG(4,(THIS_FILE, "Remote specifies AMR mode-set attr, "
|
||||
"selected: %d", enc_mode));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize AMR specific settings */
|
||||
s = PJ_POOL_ZALLOC_T(pool, amr_settings_t);
|
||||
codec_data->codec_setting = s;
|
||||
|
||||
s->enc_mode = pjmedia_codec_amr_get_mode(ippc->def_bitrate);
|
||||
if (s->enc_mode < 0)
|
||||
goto on_error;
|
||||
|
||||
s->enc_setting.amr_nb = (pj_uint8_t)(ippc->pt == PJMEDIA_RTP_PT_AMR);
|
||||
s->enc_setting.octet_aligned = octet_align;
|
||||
s->enc_setting.reorder = PJ_TRUE;
|
||||
s->enc_setting.cmr = 15;
|
||||
|
||||
|
||||
s->dec_setting.amr_nb = (pj_uint8_t)(ippc->pt == PJMEDIA_RTP_PT_AMR);
|
||||
s->dec_setting.octet_aligned = octet_align;
|
||||
s->dec_setting.reorder = PJ_TRUE;
|
||||
|
||||
s->enc_mode = pjmedia_codec_amr_get_mode(
|
||||
codec_data->info->params.modes.bitrate);
|
||||
if (s->enc_mode < 0)
|
||||
goto on_error;
|
||||
|
||||
if (enc_mode != -1) {
|
||||
s->enc_mode = enc_mode;
|
||||
|
||||
/* Apply requested encoder bitrate */
|
||||
codec_data->info->params.modes.bitrate = s->enc_setting.amr_nb?
|
||||
pjmedia_codec_amrnb_bitrates[s->enc_mode] :
|
||||
pjmedia_codec_amrwb_bitrates[s->enc_mode];
|
||||
ippc->fxns->std.Control(&codec_data->info->params.modes,
|
||||
codec_data->enc);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -553,10 +553,32 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
|
|||
return PJMEDIA_ENCSAMPLESPFRAME;
|
||||
}
|
||||
|
||||
/* Check if sink is listening to other ports */
|
||||
/* If sink is currently listening to other ports, it needs to be released
|
||||
* first before the new connection made.
|
||||
*/
|
||||
if (dst_port->transmitter_cnt > 0) {
|
||||
pj_mutex_unlock(conf->mutex);
|
||||
return PJ_ETOOMANYCONN;
|
||||
unsigned j;
|
||||
pj_bool_t transmitter_found = PJ_FALSE;
|
||||
|
||||
pj_assert(dst_port->transmitter_cnt == 1);
|
||||
for (j=0; j<conf->max_ports && !transmitter_found; ++j) {
|
||||
if (conf->ports[j]) {
|
||||
unsigned k;
|
||||
|
||||
for (k=0; k < conf->ports[j]->listener_cnt; ++k) {
|
||||
if (conf->ports[j]->listener_slots[k] == sink_slot) {
|
||||
PJ_LOG(4,(THIS_FILE, "Connection [%d->%d] is "
|
||||
"disconnected forcedly for the new "
|
||||
"connection [%d->%d]",
|
||||
j, sink_slot, src_slot, sink_slot));
|
||||
pjmedia_conf_disconnect_port(conf, j, sink_slot);
|
||||
transmitter_found = PJ_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pj_assert(dst_port->transmitter_cnt == 0);
|
||||
}
|
||||
|
||||
/* Check if connection has been made */
|
||||
|
|
|
@ -865,12 +865,14 @@ static pj_status_t process_m_answer( pj_pool_t *pool,
|
|||
(ar.param.slen==1 && *ar.param.ptr=='1')))
|
||||
{
|
||||
/* Further check for G7221, negotiate bitrate. */
|
||||
if (pj_strcmp2(&or_.enc_name, "G7221") == 0) {
|
||||
if (pj_stricmp2(&or_.enc_name, "G7221") == 0) {
|
||||
if (match_g7221(offer, i, answer, j))
|
||||
break;
|
||||
} else
|
||||
/* Further check for AMR, negotiate fmtp. */
|
||||
if (pj_strcmp2(&or_.enc_name, "AMR") == 0) {
|
||||
if (pj_stricmp2(&or_.enc_name, "AMR") == 0 ||
|
||||
pj_stricmp2(&or_.enc_name, "AMR-WB") == 0)
|
||||
{
|
||||
if (match_amr(offer, i, answer, j, PJ_FALSE,
|
||||
NULL))
|
||||
break;
|
||||
|
@ -1070,7 +1072,7 @@ static pj_status_t match_offer(pj_pool_t *pool,
|
|||
}
|
||||
pjmedia_sdp_attr_get_rtpmap(a, &or_);
|
||||
|
||||
if (!pj_strcmp2(&or_.enc_name, "telephone-event")) {
|
||||
if (!pj_stricmp2(&or_.enc_name, "telephone-event")) {
|
||||
master_has_telephone_event = 1;
|
||||
if (found_matching_telephone_event)
|
||||
continue;
|
||||
|
@ -1097,19 +1099,21 @@ static pj_status_t match_offer(pj_pool_t *pool,
|
|||
*/
|
||||
if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
|
||||
or_.clock_rate == lr.clock_rate &&
|
||||
(pj_strcmp(&or_.param, &lr.param)==0 ||
|
||||
(pj_stricmp(&or_.param, &lr.param)==0 ||
|
||||
(or_.param.slen==1 && *or_.param.ptr=='1')))
|
||||
{
|
||||
/* Match! */
|
||||
if (is_codec) {
|
||||
/* Further check for G7221, negotiate bitrate */
|
||||
if (pj_strcmp2(&or_.enc_name, "G7221") == 0 &&
|
||||
if (pj_stricmp2(&or_.enc_name, "G7221") == 0 &&
|
||||
!match_g7221(master, i, slave, j))
|
||||
{
|
||||
continue;
|
||||
} else
|
||||
/* Further check for AMR, negotiate fmtp */
|
||||
if (pj_strcmp2(&or_.enc_name, "AMR")==0) {
|
||||
if (pj_stricmp2(&or_.enc_name, "AMR")==0 ||
|
||||
pj_stricmp2(&or_.enc_name, "AMR-WB")==0)
|
||||
{
|
||||
unsigned o_med_idx, a_med_idx;
|
||||
|
||||
o_med_idx = prefer_remote_codec_order? i:j;
|
||||
|
|
|
@ -384,13 +384,13 @@ static pjmedia_tone_digit_map digit_map =
|
|||
{ '0', 941, 1336 },
|
||||
{ '1', 697, 1209 },
|
||||
{ '2', 697, 1336 },
|
||||
{ '3', 697, 1447 },
|
||||
{ '3', 697, 1477 },
|
||||
{ '4', 770, 1209 },
|
||||
{ '5', 770, 1336 },
|
||||
{ '6', 770, 1447 },
|
||||
{ '6', 770, 1477 },
|
||||
{ '7', 852, 1209 },
|
||||
{ '8', 852, 1336 },
|
||||
{ '9', 852, 1447 },
|
||||
{ '9', 852, 1477 },
|
||||
{ 'a', 697, 1633 },
|
||||
{ 'b', 770, 1633 },
|
||||
{ 'c', 852, 1633 },
|
||||
|
|
|
@ -259,12 +259,23 @@
|
|||
*/
|
||||
#define PJ_ICE_MAX_COMP (2<<PJ_ICE_COMP_BITS)
|
||||
|
||||
/**
|
||||
* Use the priority value according to the ice-draft.
|
||||
*/
|
||||
#ifndef PJNATH_ICE_PRIO_STD
|
||||
# define PJNATH_ICE_PRIO_STD 1
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* The number of bits to represent candidate type preference.
|
||||
*/
|
||||
#ifndef PJ_ICE_CAND_TYPE_PREF_BITS
|
||||
# define PJ_ICE_CAND_TYPE_PREF_BITS 2
|
||||
# if PJNATH_ICE_PRIO_STD
|
||||
# define PJ_ICE_CAND_TYPE_PREF_BITS 8
|
||||
# else
|
||||
# define PJ_ICE_CAND_TYPE_PREF_BITS 2
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -324,32 +335,74 @@
|
|||
|
||||
|
||||
/**
|
||||
* Minimum interval value to be used for sending STUN keep-alive on the ICE
|
||||
* stream transport, in seconds. This minimum interval, plus a random value
|
||||
* which maximum is PJ_ICE_ST_KEEP_ALIVE_MAX_RAND, specify the actual interval
|
||||
* of the STUN keep-alive.
|
||||
* For a controlled agent, specify how long it wants to wait (in milliseconds)
|
||||
* for the controlling agent to complete sending connectivity check with
|
||||
* nominated flag set to true for all components after the controlled agent
|
||||
* has found that all connectivity checks in its checklist have been completed
|
||||
* and there is at least one successful (but not nominated) check for every
|
||||
* component.
|
||||
*
|
||||
* Default: 20 seconds
|
||||
* When selecting the value, bear in mind that the connectivity check from
|
||||
* controlling agent may be delayed because of delay in receiving SDP answer
|
||||
* from the controlled agent.
|
||||
*
|
||||
* @see PJ_ICE_ST_KEEP_ALIVE_MAX_RAND
|
||||
* Application may set this value to -1 to disable this timer.
|
||||
*
|
||||
* Default: 10000 (milliseconds)
|
||||
*/
|
||||
#ifndef PJ_ICE_ST_KEEP_ALIVE_MIN
|
||||
# define PJ_ICE_ST_KEEP_ALIVE_MIN 20
|
||||
#ifndef ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT
|
||||
# define ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT 10000
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* For controlling agent if it uses regular nomination, specify the delay to
|
||||
* perform nominated check (connectivity check with USE-CANDIDATE attribute)
|
||||
* after all components have a valid pair.
|
||||
*
|
||||
* Default: 4*PJ_STUN_RTO_VALUE (milliseconds)
|
||||
*/
|
||||
#ifndef PJ_ICE_NOMINATED_CHECK_DELAY
|
||||
# define PJ_ICE_NOMINATED_CHECK_DELAY (4*PJ_STUN_RTO_VALUE)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Minimum interval value to be used for sending STUN keep-alive on the ICE
|
||||
* session, in seconds. This minimum interval, plus a random value
|
||||
* which maximum is PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND, specify the actual interval
|
||||
* of the STUN keep-alive.
|
||||
*
|
||||
* Default: 15 seconds
|
||||
*
|
||||
* @see PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND
|
||||
*/
|
||||
#ifndef PJ_ICE_SESS_KEEP_ALIVE_MIN
|
||||
# define PJ_ICE_SESS_KEEP_ALIVE_MIN 20
|
||||
#endif
|
||||
|
||||
/* Warn about deprecated macro */
|
||||
#ifdef PJ_ICE_ST_KEEP_ALIVE_MIN
|
||||
# error PJ_ICE_ST_KEEP_ALIVE_MIN is deprecated
|
||||
#endif
|
||||
|
||||
/**
|
||||
* To prevent STUN keep-alives to be sent simultaneously, application should
|
||||
* add random interval to minimum interval (PJ_ICE_ST_KEEP_ALIVE_MIN). This
|
||||
* add random interval to minimum interval (PJ_ICE_SESS_KEEP_ALIVE_MIN). This
|
||||
* setting specifies the maximum random value to be added to the minimum
|
||||
* interval, in seconds.
|
||||
*
|
||||
* Default: 5 seconds
|
||||
*
|
||||
* @see PJ_ICE_ST_KEEP_ALIVE_MIN
|
||||
* @see PJ_ICE_SESS_KEEP_ALIVE_MIN
|
||||
*/
|
||||
#ifndef PJ_ICE_ST_KEEP_ALIVE_MAX_RAND
|
||||
# define PJ_ICE_ST_KEEP_ALIVE_MAX_RAND 5
|
||||
#ifndef PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND
|
||||
# define PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND 5
|
||||
#endif
|
||||
|
||||
/* Warn about deprecated macro */
|
||||
#ifdef PJ_ICE_ST_KEEP_ALIVE_MAX_RAND
|
||||
# error PJ_ICE_ST_KEEP_ALIVE_MAX_RAND is deprecated
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -196,7 +196,12 @@
|
|||
* host candidate.
|
||||
*/
|
||||
#define PJNATH_EICENOHOSTCAND (PJNATH_ERRNO_START+92) /* 370092 */
|
||||
|
||||
/**
|
||||
* @hideinitializer
|
||||
* Controlled agent timed-out in waiting for the controlling agent to
|
||||
* send nominated check after all connectivity checks have completed.
|
||||
*/
|
||||
#define PJNATH_EICENOMTIMEOUT (PJNATH_ERRNO_START+93) /* 370093 */
|
||||
|
||||
/************************************************************
|
||||
* TURN ERROR CODES
|
||||
|
|
|
@ -176,12 +176,19 @@ typedef struct pj_ice_sess_check pj_ice_sess_check;
|
|||
typedef struct pj_ice_sess_comp
|
||||
{
|
||||
/**
|
||||
* The pointer to ICE check which was nominated for this component.
|
||||
* The value will be NULL if a nominated check has not been found
|
||||
* for this component.
|
||||
* Pointer to ICE check with highest priority which connectivity check
|
||||
* has been successful. The value will be NULL if a no successful check
|
||||
* has not been found for this component.
|
||||
*/
|
||||
pj_ice_sess_check *valid_check;
|
||||
|
||||
/**
|
||||
* Pointer to ICE check with highest priority which connectivity check
|
||||
* has been successful and it has been nominated. The value may be NULL
|
||||
* if there is no such check yet.
|
||||
*/
|
||||
pj_ice_sess_check *nominated_check;
|
||||
|
||||
/**
|
||||
* The STUN session to be used to send and receive STUN messages for this
|
||||
* component.
|
||||
|
@ -552,6 +559,44 @@ typedef struct pj_ice_rx_check
|
|||
} pj_ice_rx_check;
|
||||
|
||||
|
||||
/**
|
||||
* This structure describes various ICE session options. Application
|
||||
* configure the ICE session with these options by calling
|
||||
* #pj_ice_sess_set_options().
|
||||
*/
|
||||
typedef struct pj_ice_sess_options
|
||||
{
|
||||
/**
|
||||
* Specify whether to use aggressive nomination.
|
||||
*/
|
||||
pj_bool_t aggressive;
|
||||
|
||||
/**
|
||||
* For controlling agent if it uses regular nomination, specify the delay
|
||||
* to perform nominated check (connectivity check with USE-CANDIDATE
|
||||
* attribute) after all components have a valid pair.
|
||||
*
|
||||
* Default value is PJ_ICE_NOMINATED_CHECK_DELAY.
|
||||
*/
|
||||
unsigned nominated_check_delay;
|
||||
|
||||
/**
|
||||
* For a controlled agent, specify how long it wants to wait (in
|
||||
* milliseconds) for the controlling agent to complete sending
|
||||
* connectivity check with nominated flag set to true for all components
|
||||
* after the controlled agent has found that all connectivity checks in
|
||||
* its checklist have been completed and there is at least one successful
|
||||
* (but not nominated) check for every component.
|
||||
*
|
||||
* Default value for this option is
|
||||
* ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable
|
||||
* this timer.
|
||||
*/
|
||||
int controlled_agent_want_nom_timeout;
|
||||
|
||||
} pj_ice_sess_options;
|
||||
|
||||
|
||||
/**
|
||||
* This structure describes the ICE session. For this version of PJNATH,
|
||||
* an ICE session corresponds to a single media stream (unlike the ICE
|
||||
|
@ -569,11 +614,13 @@ struct pj_ice_sess
|
|||
void *user_data; /**< App. data. */
|
||||
pj_mutex_t *mutex; /**< Mutex. */
|
||||
pj_ice_sess_role role; /**< ICE role. */
|
||||
pj_ice_sess_options opt; /**< Options */
|
||||
pj_timestamp tie_breaker; /**< Tie breaker value */
|
||||
pj_uint8_t *prefs; /**< Type preference. */
|
||||
pj_bool_t is_nominating; /**< Nominating stage */
|
||||
pj_bool_t is_complete; /**< Complete? */
|
||||
pj_status_t ice_status; /**< Error status. */
|
||||
pj_timer_entry completion_timer; /**< To call callback. */
|
||||
pj_timer_entry timer; /**< ICE timer. */
|
||||
pj_ice_sess_cb cb; /**< Callback. */
|
||||
|
||||
pj_stun_config stun_cfg; /**< STUN settings. */
|
||||
|
@ -589,6 +636,7 @@ struct pj_ice_sess
|
|||
/* Components */
|
||||
unsigned comp_cnt; /**< # of components. */
|
||||
pj_ice_sess_comp comp[PJ_ICE_MAX_COMP]; /**< Component array */
|
||||
unsigned comp_ka; /**< Next comp for KA */
|
||||
|
||||
/* Local candidates */
|
||||
unsigned lcand_cnt; /**< # of local cand. */
|
||||
|
@ -654,6 +702,12 @@ PJ_DECL(void) pj_ice_calc_foundation(pj_pool_t *pool,
|
|||
pj_ice_cand_type type,
|
||||
const pj_sockaddr *base_addr);
|
||||
|
||||
/**
|
||||
* Initialize ICE session options with library default values.
|
||||
*
|
||||
* @param opt ICE session options.
|
||||
*/
|
||||
PJ_DECL(void) pj_ice_sess_options_default(pj_ice_sess_options *opt);
|
||||
|
||||
/**
|
||||
* Create ICE session with the specified role and number of components.
|
||||
|
@ -688,6 +742,34 @@ PJ_DECL(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
|
|||
const pj_str_t *local_passwd,
|
||||
pj_ice_sess **p_ice);
|
||||
|
||||
/**
|
||||
* Get the value of various options of the ICE session.
|
||||
*
|
||||
* @param ice The ICE session.
|
||||
* @param opt The options to be initialized with the values
|
||||
* from the ICE session.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the appropriate error.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice,
|
||||
pj_ice_sess_options *opt);
|
||||
|
||||
/**
|
||||
* Specify various options for this ICE session. Application MUST only
|
||||
* call this function after the ICE session has been created but before
|
||||
* any connectivity check is started.
|
||||
*
|
||||
* Application should call #pj_ice_sess_get_options() to initialize the
|
||||
* options with their default values.
|
||||
*
|
||||
* @param ice The ICE session.
|
||||
* @param opt Options to be applied to the ICE session.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the appropriate error.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice,
|
||||
const pj_ice_sess_options *opt);
|
||||
|
||||
/**
|
||||
* Destroy ICE session. This will cancel any connectivity checks currently
|
||||
* running, if any, and any other events scheduled by this session, as well
|
||||
|
|
|
@ -196,6 +196,13 @@ typedef struct pj_ice_strans_cfg
|
|||
*/
|
||||
pj_dns_resolver *resolver;
|
||||
|
||||
/**
|
||||
* This contains various STUN session options. Once the ICE stream
|
||||
* transport is created, application may also change the options
|
||||
* with #pj_ice_strans_set_options().
|
||||
*/
|
||||
pj_ice_sess_options opt;
|
||||
|
||||
/**
|
||||
* STUN and local transport settings. This specifies the
|
||||
* settings for local UDP socket, which will be resolved
|
||||
|
@ -209,12 +216,12 @@ typedef struct pj_ice_strans_cfg
|
|||
pj_stun_sock_cfg cfg;
|
||||
|
||||
/**
|
||||
* Disable host candidates. When this option is set, no
|
||||
* host candidates will be added.
|
||||
* Maximum number of host candidates to be added. If the
|
||||
* value is zero, no host candidates will be added.
|
||||
*
|
||||
* Default: PJ_FALSE
|
||||
* Default: 64
|
||||
*/
|
||||
pj_bool_t no_host_cands;
|
||||
unsigned max_host_cands;
|
||||
|
||||
/**
|
||||
* Include loopback addresses in the host candidates.
|
||||
|
@ -385,6 +392,32 @@ PJ_DECL(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st);
|
|||
PJ_DECL(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st);
|
||||
|
||||
|
||||
/**
|
||||
* Get the value of various options of the ICE stream transport.
|
||||
*
|
||||
* @param ice_st The ICE stream transport.
|
||||
* @param opt The options to be initialized with the values
|
||||
* from the ICE stream transport.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the appropriate error.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_ice_strans_get_options(pj_ice_strans *ice_st,
|
||||
pj_ice_sess_options *opt);
|
||||
|
||||
/**
|
||||
* Specify various options for this ICE stream transport. Application
|
||||
* should call #pj_ice_strans_get_options() to initialize the options
|
||||
* with their default values.
|
||||
*
|
||||
* @param ice_st The ICE stream transport.
|
||||
* @param opt Options to be applied to this ICE stream transport.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the appropriate error.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_ice_strans_set_options(pj_ice_strans *ice_st,
|
||||
const pj_ice_sess_options *opt);
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the ICE session in the ICE stream transport.
|
||||
* When application is about to send an offer containing ICE capability,
|
||||
|
|
|
@ -197,6 +197,10 @@ typedef enum pj_stun_msg_type
|
|||
*/
|
||||
PJ_STUN_BINDING_ERROR_RESPONSE = 0x0111,
|
||||
|
||||
/**
|
||||
* Binding Indication (ICE)
|
||||
*/
|
||||
PJ_STUN_BINDING_INDICATION = 0x0011,
|
||||
|
||||
/**
|
||||
* STUN SHARED-SECRET reqeust.
|
||||
|
|
|
@ -485,6 +485,18 @@ PJ_DECL(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
|
|||
*/
|
||||
PJ_DECL(void) pj_stun_session_set_log(pj_stun_session *sess,
|
||||
unsigned flags);
|
||||
/**
|
||||
* Configure whether the STUN session should utilize FINGERPRINT in
|
||||
* outgoing messages.
|
||||
*
|
||||
* @param sess The STUN session instance.
|
||||
* @param use Boolean for the setting.
|
||||
*
|
||||
* @return The previous configured value of FINGERPRINT
|
||||
* utilization of the sessoin.
|
||||
*/
|
||||
PJ_DECL(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess,
|
||||
pj_bool_t use);
|
||||
|
||||
/**
|
||||
* Create a STUN request message. After the message has been successfully
|
||||
|
|
|
@ -64,6 +64,8 @@ struct test_cfg
|
|||
unsigned destroy_delay; /* Delay before destroy() */
|
||||
|
||||
struct test_result expected;/* Expected result */
|
||||
|
||||
pj_bool_t nom_regular; /* Use regular nomination? */
|
||||
};
|
||||
|
||||
/* ICE endpoint state */
|
||||
|
@ -141,9 +143,9 @@ static int create_ice_strans(struct test_sess *test_sess,
|
|||
}
|
||||
|
||||
if (ept->cfg.enable_host == 0) {
|
||||
ice_cfg.stun.no_host_cands = PJ_TRUE;
|
||||
ice_cfg.stun.max_host_cands = 0;
|
||||
} else {
|
||||
ice_cfg.stun.no_host_cands = PJ_FALSE;
|
||||
//ice_cfg.stun.no_host_cands = PJ_FALSE;
|
||||
ice_cfg.stun.loop_addr = PJ_TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ static const struct
|
|||
PJ_BUILD_ERR( PJNATH_EICEMISSINGSDP, "Missing ICE SDP attribute"),
|
||||
PJ_BUILD_ERR( PJNATH_EICEINCANDSDP, "Invalid SDP \"candidate\" attribute"),
|
||||
PJ_BUILD_ERR( PJNATH_EICENOHOSTCAND, "No host candidate associated with srflx"),
|
||||
PJ_BUILD_ERR( PJNATH_EICENOMTIMEOUT, "Controlled agent timed out waiting for nomination"),
|
||||
|
||||
/* TURN related errors */
|
||||
PJ_BUILD_ERR( PJNATH_ETURNINTP, "Invalid/unsupported transport"),
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <pj/rand.h>
|
||||
#include <pj/string.h>
|
||||
|
||||
|
||||
/* String names for candidate types */
|
||||
static const char *cand_type_names[] =
|
||||
{
|
||||
|
@ -66,6 +65,20 @@ static const char *role_names[] =
|
|||
"Controlling"
|
||||
};
|
||||
|
||||
enum timer_type
|
||||
{
|
||||
TIMER_NONE, /**< Timer not active */
|
||||
TIMER_COMPLETION_CALLBACK, /**< Call on_ice_complete() callback */
|
||||
TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for
|
||||
controlling agent to send connectivity
|
||||
check with nominated flag after it has
|
||||
valid check for every components. */
|
||||
TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity
|
||||
checks with USE-CANDIDATE flag. */
|
||||
TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */
|
||||
|
||||
};
|
||||
|
||||
/* Candidate type preference */
|
||||
static pj_uint8_t cand_type_prefs[4] =
|
||||
{
|
||||
|
@ -118,10 +131,14 @@ typedef struct timer_data
|
|||
|
||||
|
||||
/* Forward declarations */
|
||||
static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te);
|
||||
static void on_ice_complete(pj_ice_sess *ice, pj_status_t status);
|
||||
static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now);
|
||||
static void destroy_ice(pj_ice_sess *ice,
|
||||
pj_status_t reason);
|
||||
static pj_status_t start_periodic_check(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te);
|
||||
static void start_nominated_check(pj_ice_sess *ice);
|
||||
static void periodic_timer(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te);
|
||||
static void handle_incoming_check(pj_ice_sess *ice,
|
||||
|
@ -225,7 +242,7 @@ PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool,
|
|||
pj_ice_cand_type type,
|
||||
const pj_sockaddr *base_addr)
|
||||
{
|
||||
#if 0
|
||||
#if PJNATH_ICE_PRIO_STD
|
||||
char buf[64];
|
||||
pj_uint32_t val;
|
||||
|
||||
|
@ -296,6 +313,15 @@ static pj_status_t init_comp(pj_ice_sess *ice,
|
|||
}
|
||||
|
||||
|
||||
/* Init options with default values */
|
||||
PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt)
|
||||
{
|
||||
opt->aggressive = PJ_TRUE;
|
||||
opt->nominated_check_delay = PJ_ICE_NOMINATED_CHECK_DELAY;
|
||||
opt->controlled_agent_want_nom_timeout =
|
||||
ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create ICE session.
|
||||
*/
|
||||
|
@ -326,6 +352,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
|
|||
ice->tie_breaker.u32.hi = pj_rand();
|
||||
ice->tie_breaker.u32.lo = pj_rand();
|
||||
ice->prefs = cand_type_prefs;
|
||||
pj_ice_sess_options_default(&ice->opt);
|
||||
|
||||
pj_timer_entry_init(&ice->timer, TIMER_NONE, (void*)ice, &on_timer);
|
||||
|
||||
pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name),
|
||||
name, ice);
|
||||
|
@ -345,6 +374,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
|
|||
pj_ice_sess_comp *comp;
|
||||
comp = &ice->comp[i];
|
||||
comp->valid_check = NULL;
|
||||
comp->nominated_check = NULL;
|
||||
|
||||
status = init_comp(ice, i+1, comp);
|
||||
if (status != PJ_SUCCESS) {
|
||||
|
@ -388,6 +418,31 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the value of various options of the ICE session.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice,
|
||||
pj_ice_sess_options *opt)
|
||||
{
|
||||
PJ_ASSERT_RETURN(ice, PJ_EINVAL);
|
||||
pj_memcpy(opt, &ice->opt, sizeof(*opt));
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Specify various options for this ICE session.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice,
|
||||
const pj_ice_sess_options *opt)
|
||||
{
|
||||
PJ_ASSERT_RETURN(ice && opt, PJ_EINVAL);
|
||||
pj_memcpy(&ice->opt, opt, sizeof(*opt));
|
||||
LOG5((ice->obj_name, "ICE nomination type set to %s",
|
||||
(ice->opt.aggressive ? "aggressive" : "regular")));
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Destroy
|
||||
*/
|
||||
|
@ -406,10 +461,10 @@ static void destroy_ice(pj_ice_sess *ice,
|
|||
pj_mutex_unlock(ice->mutex);
|
||||
}
|
||||
|
||||
if (ice->completion_timer.id) {
|
||||
if (ice->timer.id) {
|
||||
pj_timer_heap_cancel(ice->stun_cfg.timer_heap,
|
||||
&ice->completion_timer);
|
||||
ice->completion_timer.id = PJ_FALSE;
|
||||
&ice->timer);
|
||||
ice->timer.id = PJ_FALSE;
|
||||
}
|
||||
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
|
@ -603,7 +658,7 @@ static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice,
|
|||
pj_uint32_t local_pref,
|
||||
pj_uint32_t comp_id)
|
||||
{
|
||||
#if 0
|
||||
#if PJNATH_ICE_PRIO_STD
|
||||
return ((ice->prefs[type] & 0xFF) << 24) +
|
||||
((local_pref & 0xFFFF) << 8) +
|
||||
(((256 - comp_id) & 0xFF) << 0);
|
||||
|
@ -1039,18 +1094,112 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
|
|||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Timer callback to call on_ice_complete() callback */
|
||||
static void on_completion_timer(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te)
|
||||
/* Timer callback */
|
||||
static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te)
|
||||
{
|
||||
pj_ice_sess *ice = (pj_ice_sess*) te->user_data;
|
||||
enum timer_type type = (enum timer_type)te->id;
|
||||
|
||||
PJ_UNUSED_ARG(th);
|
||||
|
||||
te->id = PJ_FALSE;
|
||||
pj_mutex_lock(ice->mutex);
|
||||
|
||||
if (ice->cb.on_ice_complete)
|
||||
(*ice->cb.on_ice_complete)(ice, ice->ice_status);
|
||||
te->id = TIMER_NONE;
|
||||
|
||||
switch (type) {
|
||||
case TIMER_CONTROLLED_WAIT_NOM:
|
||||
LOG4((ice->obj_name,
|
||||
"Controlled agent timed-out in waiting for the controlling "
|
||||
"agent to send nominated check. Setting state to fail now.."));
|
||||
on_ice_complete(ice, PJNATH_EICENOMTIMEOUT);
|
||||
break;
|
||||
case TIMER_COMPLETION_CALLBACK:
|
||||
/* Start keep-alive timer but don't send any packets yet.
|
||||
* Need to do it here just in case app destroy the session
|
||||
* in the callback.
|
||||
*/
|
||||
if (ice->ice_status == PJ_SUCCESS)
|
||||
ice_keep_alive(ice, PJ_FALSE);
|
||||
|
||||
/* Notify app about ICE completion*/
|
||||
if (ice->cb.on_ice_complete)
|
||||
(*ice->cb.on_ice_complete)(ice, ice->ice_status);
|
||||
break;
|
||||
case TIMER_START_NOMINATED_CHECK:
|
||||
start_nominated_check(ice);
|
||||
break;
|
||||
case TIMER_KEEP_ALIVE:
|
||||
ice_keep_alive(ice, PJ_TRUE);
|
||||
break;
|
||||
case TIMER_NONE:
|
||||
/* Nothing to do, just to get rid of gcc warning */
|
||||
break;
|
||||
}
|
||||
|
||||
pj_mutex_unlock(ice->mutex);
|
||||
}
|
||||
|
||||
/* Send keep-alive */
|
||||
static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now)
|
||||
{
|
||||
if (send_now) {
|
||||
/* Send Binding Indication for the component */
|
||||
pj_ice_sess_comp *comp = &ice->comp[ice->comp_ka];
|
||||
pj_stun_tx_data *tdata;
|
||||
pj_ice_sess_check *the_check;
|
||||
pj_ice_msg_data *msg_data;
|
||||
int addr_len;
|
||||
pj_bool_t saved;
|
||||
pj_status_t status;
|
||||
|
||||
/* Must have nominated check by now */
|
||||
pj_assert(comp->nominated_check != NULL);
|
||||
the_check = comp->nominated_check;
|
||||
|
||||
/* Create the Binding Indication */
|
||||
status = pj_stun_session_create_ind(comp->stun_sess,
|
||||
PJ_STUN_BINDING_INDICATION,
|
||||
&tdata);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto done;
|
||||
|
||||
/* Need the transport_id */
|
||||
msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data);
|
||||
msg_data->transport_id = the_check->lcand->transport_id;
|
||||
|
||||
/* Temporarily disable FINGERPRINT. The Binding Indication
|
||||
* SHOULD NOT contain any attributes.
|
||||
*/
|
||||
saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_FALSE);
|
||||
|
||||
/* Send to session */
|
||||
addr_len = pj_sockaddr_get_len(&the_check->rcand->addr);
|
||||
status = pj_stun_session_send_msg(comp->stun_sess, msg_data,
|
||||
PJ_FALSE, PJ_FALSE,
|
||||
&the_check->rcand->addr,
|
||||
addr_len, tdata);
|
||||
|
||||
/* Restore FINGERPRINT usage */
|
||||
pj_stun_session_use_fingerprint(comp->stun_sess, saved);
|
||||
|
||||
done:
|
||||
ice->comp_ka = (ice->comp_ka + 1) % ice->comp_cnt;
|
||||
}
|
||||
|
||||
if (ice->timer.id == TIMER_NONE) {
|
||||
pj_time_val delay = { 0, 0 };
|
||||
|
||||
delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN +
|
||||
(pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 /
|
||||
ice->comp_cnt;
|
||||
pj_time_val_normalize(&delay);
|
||||
|
||||
ice->timer.id = TIMER_KEEP_ALIVE;
|
||||
pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
|
||||
|
||||
} else {
|
||||
pj_assert(!"Not expected any timer active");
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is called when ICE processing completes */
|
||||
|
@ -1060,6 +1209,11 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
|||
ice->is_complete = PJ_TRUE;
|
||||
ice->ice_status = status;
|
||||
|
||||
if (ice->timer.id != TIMER_NONE) {
|
||||
pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
|
||||
ice->timer.id = TIMER_NONE;
|
||||
}
|
||||
|
||||
/* Log message */
|
||||
LOG4((ice->obj_name, "ICE process complete, status=%s",
|
||||
pj_strerror(status, ice->tmp.errmsg,
|
||||
|
@ -1071,26 +1225,49 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
|||
if (ice->cb.on_ice_complete) {
|
||||
pj_time_val delay = {0, 0};
|
||||
|
||||
ice->completion_timer.cb = &on_completion_timer;
|
||||
ice->completion_timer.user_data = (void*) ice;
|
||||
ice->completion_timer.id = PJ_TRUE;
|
||||
|
||||
ice->timer.id = TIMER_COMPLETION_CALLBACK;
|
||||
pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
|
||||
&ice->completion_timer,
|
||||
&delay);
|
||||
&ice->timer, &delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update valid check and nominated check for the candidate */
|
||||
static void update_comp_check(pj_ice_sess *ice, unsigned comp_id,
|
||||
pj_ice_sess_check *check)
|
||||
{
|
||||
pj_ice_sess_comp *comp;
|
||||
|
||||
comp = find_comp(ice, comp_id);
|
||||
if (comp->valid_check == NULL) {
|
||||
comp->valid_check = check;
|
||||
} else {
|
||||
if (CMP_CHECK_PRIO(comp->valid_check, check) < 0)
|
||||
comp->valid_check = check;
|
||||
}
|
||||
|
||||
if (check->nominated) {
|
||||
/* Update the nominated check for the component */
|
||||
if (comp->nominated_check == NULL) {
|
||||
comp->nominated_check = check;
|
||||
} else {
|
||||
if (CMP_CHECK_PRIO(comp->nominated_check, check) < 0)
|
||||
comp->nominated_check = check;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is called when one check completes */
|
||||
static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
||||
pj_ice_sess_check *check)
|
||||
{
|
||||
pj_ice_sess_comp *comp;
|
||||
unsigned i;
|
||||
|
||||
pj_assert(check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED);
|
||||
|
||||
comp = find_comp(ice, check->lcand->comp_id);
|
||||
|
||||
/* 7.1.2.2.2. Updating Pair States
|
||||
*
|
||||
* The agent sets the state of the pair that generated the check to
|
||||
|
@ -1104,6 +1281,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
* always.
|
||||
*/
|
||||
if (check->err_code==PJ_SUCCESS) {
|
||||
|
||||
for (i=0; i<ice->clist.count; ++i) {
|
||||
pj_ice_sess_check *c = &ice->clist.checks[i];
|
||||
if (pj_strcmp(&c->lcand->foundation, &check->lcand->foundation)==0
|
||||
|
@ -1112,6 +1290,11 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
|
||||
}
|
||||
}
|
||||
|
||||
LOG5((ice->obj_name, "Check %d is successful%s",
|
||||
GET_CHECK_ID(&ice->clist, check),
|
||||
(check->nominated ? " and nominated" : "")));
|
||||
|
||||
}
|
||||
|
||||
/* 8.2. Updating States
|
||||
|
@ -1136,12 +1319,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
* than the lowest priority nominated pair for that component
|
||||
*/
|
||||
if (check->err_code==PJ_SUCCESS && check->nominated) {
|
||||
pj_ice_sess_comp *comp;
|
||||
|
||||
LOG5((ice->obj_name, "Check %d is successful and nominated",
|
||||
GET_CHECK_ID(&ice->clist, check)));
|
||||
|
||||
comp = find_comp(ice, check->lcand->comp_id);
|
||||
|
||||
for (i=0; i<ice->clist.count; ++i) {
|
||||
|
||||
|
@ -1179,14 +1356,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the nominated check for the component */
|
||||
if (comp->valid_check == NULL) {
|
||||
comp->valid_check = check;
|
||||
} else {
|
||||
if (CMP_CHECK_PRIO(comp->valid_check, check) < 0)
|
||||
comp->valid_check = check;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1211,7 +1380,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
* ICE processing as success, otherwise wait.
|
||||
*/
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
if (ice->comp[i].valid_check == NULL)
|
||||
if (ice->comp[i].nominated_check == NULL)
|
||||
break;
|
||||
}
|
||||
if (i == ice->comp_cnt) {
|
||||
|
@ -1258,23 +1427,16 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
/* All checks have completed, but we don't have nominated pair.
|
||||
* If agent's role is controlled, check if all components have
|
||||
* valid pair. If it does, this means the controlled agent has
|
||||
* finished the check list early and it's waiting for controlling
|
||||
* agent to send a check with USE-CANDIDATE flag set.
|
||||
* finished the check list and it's waiting for controlling
|
||||
* agent to send checks with USE-CANDIDATE flag set.
|
||||
*/
|
||||
if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) {
|
||||
unsigned comp_id;
|
||||
for (comp_id=1; comp_id <= ice->comp_cnt; ++comp_id) {
|
||||
unsigned j;
|
||||
for (j=0; j<ice->valid_list.count; ++j) {
|
||||
pj_ice_sess_check *vc = &ice->valid_list.checks[j];
|
||||
if (vc->lcand->comp_id == comp_id)
|
||||
break;
|
||||
}
|
||||
if (j == ice->valid_list.count)
|
||||
for (i=0; i < ice->comp_cnt; ++i) {
|
||||
if (ice->comp[i].valid_check == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (comp_id <= ice->comp_cnt) {
|
||||
if (i < ice->comp_cnt) {
|
||||
/* This component ID doesn't have valid pair.
|
||||
* Mark ICE as failed.
|
||||
*/
|
||||
|
@ -1284,12 +1446,109 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
/* All components have a valid pair.
|
||||
* We should wait until we receive nominated checks.
|
||||
*/
|
||||
if (ice->timer.id == TIMER_NONE &&
|
||||
ice->opt.controlled_agent_want_nom_timeout >= 0)
|
||||
{
|
||||
pj_time_val delay;
|
||||
|
||||
delay.sec = 0;
|
||||
delay.msec = ice->opt.controlled_agent_want_nom_timeout;
|
||||
pj_time_val_normalize(&delay);
|
||||
|
||||
ice->timer.id = TIMER_CONTROLLED_WAIT_NOM;
|
||||
pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
|
||||
&ice->timer,
|
||||
&delay);
|
||||
|
||||
LOG5((ice->obj_name,
|
||||
"All checks have completed. Controlled agent now "
|
||||
"waits for nomination from controlling agent "
|
||||
"(timeout=%d msec)",
|
||||
ice->opt.controlled_agent_want_nom_timeout));
|
||||
}
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
/* Unreached */
|
||||
|
||||
} else if (ice->is_nominating) {
|
||||
/* We are controlling agent and all checks have completed but
|
||||
* there's at least one component without nominated pair (or
|
||||
* more likely we don't have any nominated pairs at all).
|
||||
*/
|
||||
on_ice_complete(ice, PJNATH_EICEFAILED);
|
||||
return PJ_TRUE;
|
||||
|
||||
} else {
|
||||
/* We are controlling agent and all checks have completed. If
|
||||
* we have valid list for every component, then move on to
|
||||
* sending nominated check, otherwise we have failed.
|
||||
*/
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
if (ice->comp[i].valid_check == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < ice->comp_cnt) {
|
||||
/* At least one component doesn't have a valid check. Mark
|
||||
* ICE as failed.
|
||||
*/
|
||||
on_ice_complete(ice, PJNATH_EICEFAILED);
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
/* Now it's time to send connectivity check with nomination
|
||||
* flag set.
|
||||
*/
|
||||
LOG4((ice->obj_name,
|
||||
"All checks have completed, starting nominated checks now"));
|
||||
start_nominated_check(ice);
|
||||
return PJ_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this connectivity check has been successful, scan all components
|
||||
* and see if they have a valid pair, if we are controlling and we haven't
|
||||
* started our nominated check yet.
|
||||
*/
|
||||
if (check->err_code == PJ_SUCCESS &&
|
||||
ice->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
|
||||
!ice->is_nominating &&
|
||||
ice->timer.id == TIMER_NONE)
|
||||
{
|
||||
pj_time_val delay;
|
||||
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
if (ice->comp[i].valid_check == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
on_ice_complete(ice, PJNATH_EICEFAILED);
|
||||
return PJ_TRUE;
|
||||
if (i < ice->comp_cnt) {
|
||||
/* Some components still don't have valid pair, continue
|
||||
* processing.
|
||||
*/
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
LOG4((ice->obj_name,
|
||||
"Scheduling nominated check in %d ms",
|
||||
ice->opt.nominated_check_delay));
|
||||
|
||||
if (ice->timer.id != TIMER_NONE) {
|
||||
pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
|
||||
ice->timer.id = TIMER_NONE;
|
||||
}
|
||||
|
||||
/* All components have valid pair. Let connectivity checks run for
|
||||
* a little bit more time, then start our nominated check.
|
||||
*/
|
||||
delay.sec = 0;
|
||||
delay.msec = ice->opt.nominated_check_delay;
|
||||
pj_time_val_normalize(&delay);
|
||||
|
||||
ice->timer.id = TIMER_START_NOMINATED_CHECK;
|
||||
pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
/* We still have checks to perform */
|
||||
|
@ -1297,7 +1556,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* Create checklist by pairing local candidates with remote candidates */
|
||||
PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
|
||||
pj_ice_sess *ice,
|
||||
|
@ -1430,10 +1688,11 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
|
|||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Perform check on the specified candidate pair */
|
||||
/* Perform check on the specified candidate pair. */
|
||||
static pj_status_t perform_check(pj_ice_sess *ice,
|
||||
pj_ice_sess_checklist *clist,
|
||||
unsigned check_id)
|
||||
unsigned check_id,
|
||||
pj_bool_t nominate)
|
||||
{
|
||||
pj_ice_sess_comp *comp;
|
||||
pj_ice_msg_data *msg_data;
|
||||
|
@ -1472,8 +1731,13 @@ static pj_status_t perform_check(pj_ice_sess *ice,
|
|||
msg_data->data.req.ckid = check_id;
|
||||
|
||||
/* Add PRIORITY */
|
||||
#if PJNATH_ICE_PRIO_STD
|
||||
prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535,
|
||||
lcand->comp_id);
|
||||
#else
|
||||
prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0,
|
||||
lcand->comp_id);
|
||||
#endif
|
||||
pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg,
|
||||
PJ_STUN_ATTR_PRIORITY, prio);
|
||||
|
||||
|
@ -1481,9 +1745,11 @@ static pj_status_t perform_check(pj_ice_sess *ice,
|
|||
* Also add ICE-CONTROLLING or ICE-CONTROLLED
|
||||
*/
|
||||
if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) {
|
||||
pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
|
||||
PJ_STUN_ATTR_USE_CANDIDATE);
|
||||
check->nominated = PJ_TRUE;
|
||||
if (nominate) {
|
||||
pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
|
||||
PJ_STUN_ATTR_USE_CANDIDATE);
|
||||
check->nominated = PJ_TRUE;
|
||||
}
|
||||
|
||||
pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg,
|
||||
PJ_STUN_ATTR_ICE_CONTROLLING,
|
||||
|
@ -1549,7 +1815,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
|
|||
pj_ice_sess_check *check = &clist->checks[i];
|
||||
|
||||
if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) {
|
||||
status = perform_check(ice, clist, i);
|
||||
status = perform_check(ice, clist, i, ice->is_nominating);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_mutex_unlock(ice->mutex);
|
||||
return status;
|
||||
|
@ -1568,7 +1834,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
|
|||
pj_ice_sess_check *check = &clist->checks[i];
|
||||
|
||||
if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) {
|
||||
status = perform_check(ice, clist, i);
|
||||
status = perform_check(ice, clist, i, ice->is_nominating);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_mutex_unlock(ice->mutex);
|
||||
return status;
|
||||
|
@ -1596,6 +1862,66 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
|
|||
}
|
||||
|
||||
|
||||
/* Start sending connectivity check with USE-CANDIDATE */
|
||||
static void start_nominated_check(pj_ice_sess *ice)
|
||||
{
|
||||
pj_time_val delay;
|
||||
unsigned i;
|
||||
pj_status_t status;
|
||||
|
||||
LOG4((ice->obj_name, "Starting nominated check.."));
|
||||
|
||||
pj_assert(ice->is_nominating == PJ_FALSE);
|
||||
|
||||
/* Stop our timer if it's active */
|
||||
if (ice->timer.id == TIMER_START_NOMINATED_CHECK) {
|
||||
pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
|
||||
ice->timer.id = TIMER_NONE;
|
||||
}
|
||||
|
||||
/* For each component, set the check state of valid check with
|
||||
* highest priority to Waiting (it should have Success state now).
|
||||
*/
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
unsigned j;
|
||||
const pj_ice_sess_check *vc = ice->comp[i].valid_check;
|
||||
|
||||
pj_assert(ice->comp[i].nominated_check == NULL);
|
||||
pj_assert(vc->err_code == PJ_SUCCESS);
|
||||
|
||||
for (j=0; j<ice->clist.count; ++j) {
|
||||
pj_ice_sess_check *c = &ice->clist.checks[j];
|
||||
if (c->lcand->transport_id == vc->lcand->transport_id &&
|
||||
c->rcand == vc->rcand)
|
||||
{
|
||||
pj_assert(c->err_code == PJ_SUCCESS);
|
||||
c->state = PJ_ICE_SESS_CHECK_STATE_FROZEN;
|
||||
check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING,
|
||||
PJ_SUCCESS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* And (re)start the periodic check */
|
||||
if (!ice->clist.timer.id) {
|
||||
pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer);
|
||||
ice->clist.timer.id = PJ_FALSE;
|
||||
}
|
||||
|
||||
ice->clist.timer.id = PJ_TRUE;
|
||||
delay.sec = delay.msec = 0;
|
||||
status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
|
||||
&ice->clist.timer, &delay);
|
||||
if (status != PJ_SUCCESS) {
|
||||
ice->clist.timer.id = PJ_FALSE;
|
||||
} else {
|
||||
LOG5((ice->obj_name, "Periodic timer rescheduled.."));
|
||||
}
|
||||
|
||||
ice->is_nominating = PJ_TRUE;
|
||||
}
|
||||
|
||||
/* Timer callback to perform periodic check */
|
||||
static void periodic_timer(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te)
|
||||
|
@ -1642,6 +1968,10 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
|
|||
|
||||
LOG4((ice->obj_name, "Starting ICE check.."));
|
||||
|
||||
/* If we are using aggressive nomination, set the is_nominating state */
|
||||
if (ice->opt.aggressive)
|
||||
ice->is_nominating = PJ_TRUE;
|
||||
|
||||
/* The agent examines the check list for the first media stream (a
|
||||
* media stream is the first media stream when it is described by
|
||||
* the first m-line in the SDP offer and answer). For that media
|
||||
|
@ -1826,7 +2156,8 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
|
|||
/* Resend request */
|
||||
LOG4((ice->obj_name, "Resending check because of role conflict"));
|
||||
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
|
||||
perform_check(ice, clist, msg_data->data.req.ckid);
|
||||
perform_check(ice, clist, msg_data->data.req.ckid,
|
||||
check->nominated || ice->is_nominating);
|
||||
pj_mutex_unlock(ice->mutex);
|
||||
return;
|
||||
}
|
||||
|
@ -1959,19 +2290,34 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
|
|||
* equals the destination address to which the request was sent.
|
||||
*/
|
||||
|
||||
/* Add pair to valid list */
|
||||
pj_assert(ice->valid_list.count < PJ_ICE_MAX_CHECKS);
|
||||
new_check = &ice->valid_list.checks[ice->valid_list.count++];
|
||||
new_check->lcand = lcand;
|
||||
new_check->rcand = check->rcand;
|
||||
new_check->prio = CALC_CHECK_PRIO(ice, lcand, check->rcand);
|
||||
new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED;
|
||||
new_check->nominated = check->nominated;
|
||||
new_check->err_code = PJ_SUCCESS;
|
||||
/* Add pair to valid list, if it's not there, otherwise just update
|
||||
* nominated flag
|
||||
*/
|
||||
for (i=0; i<ice->valid_list.count; ++i) {
|
||||
if (ice->valid_list.checks[i].lcand == lcand &&
|
||||
ice->valid_list.checks[i].rcand == check->rcand)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i==ice->valid_list.count) {
|
||||
pj_assert(ice->valid_list.count < PJ_ICE_MAX_CHECKS);
|
||||
new_check = &ice->valid_list.checks[ice->valid_list.count++];
|
||||
new_check->lcand = lcand;
|
||||
new_check->rcand = check->rcand;
|
||||
new_check->prio = CALC_CHECK_PRIO(ice, lcand, check->rcand);
|
||||
new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED;
|
||||
new_check->nominated = check->nominated;
|
||||
new_check->err_code = PJ_SUCCESS;
|
||||
} else {
|
||||
new_check = &ice->valid_list.checks[i];
|
||||
ice->valid_list.checks[i].nominated = check->nominated;
|
||||
}
|
||||
|
||||
/* Sort valid_list */
|
||||
sort_checklist(&ice->valid_list);
|
||||
|
||||
/* Update valid check and nominated check for the component */
|
||||
update_comp_check(ice, new_check->lcand->comp_id, new_check);
|
||||
|
||||
/* 7.1.2.2.2. Updating Pair States
|
||||
*
|
||||
|
@ -2312,8 +2658,11 @@ static void handle_incoming_check(pj_ice_sess *ice,
|
|||
if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN ||
|
||||
c->state == PJ_ICE_SESS_CHECK_STATE_WAITING)
|
||||
{
|
||||
/* See if we shall nominate this check */
|
||||
pj_bool_t nominate = (c->nominated || ice->is_nominating);
|
||||
|
||||
LOG5((ice->obj_name, "Performing triggered check for check %d",i));
|
||||
perform_check(ice, &ice->clist, i);
|
||||
perform_check(ice, &ice->clist, i, nominate);
|
||||
|
||||
} else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) {
|
||||
/* Should retransmit immediately
|
||||
|
@ -2336,8 +2685,17 @@ static void handle_incoming_check(pj_ice_sess *ice,
|
|||
if (rcheck->use_candidate) {
|
||||
for (j=0; j<ice->valid_list.count; ++j) {
|
||||
pj_ice_sess_check *vc = &ice->valid_list.checks[j];
|
||||
if (vc->lcand == c->lcand && vc->rcand == c->rcand) {
|
||||
if (vc->lcand->transport_id == c->lcand->transport_id &&
|
||||
vc->rcand == c->rcand)
|
||||
{
|
||||
/* Set nominated flag */
|
||||
vc->nominated = PJ_TRUE;
|
||||
|
||||
/* Update valid check and nominated check for the component */
|
||||
update_comp_check(ice, vc->lcand->comp_id, vc);
|
||||
|
||||
dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->valid_list, vc);
|
||||
LOG5((ice->obj_name, "Valid check %s is nominated", ice->tmp.txt));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2361,6 +2719,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
|
|||
else if (ice->clist.count < PJ_ICE_MAX_CHECKS) {
|
||||
|
||||
pj_ice_sess_check *c = &ice->clist.checks[ice->clist.count];
|
||||
pj_bool_t nominate;
|
||||
|
||||
c->lcand = lcand;
|
||||
c->rcand = rcand;
|
||||
|
@ -2369,9 +2728,11 @@ static void handle_incoming_check(pj_ice_sess *ice,
|
|||
c->nominated = rcheck->use_candidate;
|
||||
c->err_code = PJ_SUCCESS;
|
||||
|
||||
nominate = (c->nominated || ice->is_nominating);
|
||||
|
||||
LOG4((ice->obj_name, "New triggered check added: %d",
|
||||
ice->clist.count));
|
||||
perform_check(ice, &ice->clist, ice->clist.count++);
|
||||
perform_check(ice, &ice->clist, ice->clist.count++, nominate);
|
||||
|
||||
} else {
|
||||
LOG4((ice->obj_name, "Error: unable to perform triggered check: "
|
||||
|
@ -2388,6 +2749,8 @@ static pj_status_t on_stun_rx_indication(pj_stun_session *sess,
|
|||
const pj_sockaddr_t *src_addr,
|
||||
unsigned src_addr_len)
|
||||
{
|
||||
struct stun_data *sd;
|
||||
|
||||
PJ_UNUSED_ARG(sess);
|
||||
PJ_UNUSED_ARG(pkt);
|
||||
PJ_UNUSED_ARG(pkt_len);
|
||||
|
@ -2396,9 +2759,18 @@ static pj_status_t on_stun_rx_indication(pj_stun_session *sess,
|
|||
PJ_UNUSED_ARG(src_addr);
|
||||
PJ_UNUSED_ARG(src_addr_len);
|
||||
|
||||
PJ_TODO(SUPPORT_RX_BIND_REQUEST_AS_INDICATION);
|
||||
sd = (struct stun_data*) pj_stun_session_get_user_data(sess);
|
||||
|
||||
return PJ_ENOTSUP;
|
||||
if (msg->hdr.type == PJ_STUN_BINDING_INDICATION) {
|
||||
LOG5((sd->ice->obj_name, "Received Binding Indication keep-alive "
|
||||
"for component %d", sd->comp_id));
|
||||
} else {
|
||||
LOG4((sd->ice->obj_name, "Received unexpected %s indication "
|
||||
"for component %d", pj_stun_get_method_name(msg->hdr.type),
|
||||
sd->comp_id));
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -50,20 +50,34 @@ enum tp_type
|
|||
/* Candidate's local preference values. This is mostly used to
|
||||
* specify preference among candidates with the same type. Since
|
||||
* we don't have the facility to specify that, we'll just set it
|
||||
* all to zero.
|
||||
* all to the same value.
|
||||
*/
|
||||
#define SRFLX_PREF 0
|
||||
#define HOST_PREF 0
|
||||
#define RELAY_PREF 0
|
||||
#if PJNATH_ICE_PRIO_STD
|
||||
# define SRFLX_PREF 65535
|
||||
# define HOST_PREF 65535
|
||||
# define RELAY_PREF 65535
|
||||
#else
|
||||
# define SRFLX_PREF 0
|
||||
# define HOST_PREF 0
|
||||
# define RELAY_PREF 0
|
||||
#endif
|
||||
|
||||
|
||||
/* The candidate type preference when STUN candidate is used */
|
||||
static pj_uint8_t srflx_pref_table[4] =
|
||||
{
|
||||
#if PJNATH_ICE_PRIO_STD
|
||||
100, /**< PJ_ICE_HOST_PREF */
|
||||
126, /**< PJ_ICE_SRFLX_PREF */
|
||||
110, /**< PJ_ICE_PRFLX_PREF */
|
||||
0 /**< PJ_ICE_RELAYED_PREF */
|
||||
#else
|
||||
/* Keep it to 2 bits */
|
||||
1, /**< PJ_ICE_HOST_PREF */
|
||||
2, /**< PJ_ICE_SRFLX_PREF */
|
||||
3, /**< PJ_ICE_PRFLX_PREF */
|
||||
0 /**< PJ_ICE_RELAYED_PREF */
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
@ -197,9 +211,13 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg)
|
|||
pj_stun_sock_cfg_default(&cfg->stun.cfg);
|
||||
pj_turn_alloc_param_default(&cfg->turn.alloc_param);
|
||||
|
||||
pj_ice_sess_options_default(&cfg->opt);
|
||||
|
||||
cfg->af = pj_AF_INET();
|
||||
cfg->stun.port = PJ_STUN_PORT;
|
||||
cfg->turn.conn_type = PJ_TURN_TP_UDP;
|
||||
|
||||
cfg->stun.max_host_cands = 64;
|
||||
}
|
||||
|
||||
|
||||
|
@ -245,7 +263,7 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
|
|||
comp->default_cand = 0;
|
||||
|
||||
/* Create STUN transport if configured */
|
||||
if (ice_st->cfg.stun.server.slen || !ice_st->cfg.stun.no_host_cands) {
|
||||
if (ice_st->cfg.stun.server.slen || ice_st->cfg.stun.max_host_cands) {
|
||||
pj_stun_sock_cb stun_sock_cb;
|
||||
pj_ice_sess_cand *cand;
|
||||
|
||||
|
@ -309,10 +327,10 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
|
|||
|
||||
}
|
||||
|
||||
/* Add local addresses to host candidates, unless no_host_cands
|
||||
* flag is set.
|
||||
/* Add local addresses to host candidates, unless max_host_cands
|
||||
* is set to zero.
|
||||
*/
|
||||
if (ice_st->cfg.stun.no_host_cands == PJ_FALSE) {
|
||||
if (ice_st->cfg.stun.max_host_cands) {
|
||||
pj_stun_sock_info stun_sock_info;
|
||||
unsigned i;
|
||||
|
||||
|
@ -321,7 +339,9 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
|
|||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
||||
for (i=0; i<stun_sock_info.alias_cnt; ++i) {
|
||||
for (i=0; i<stun_sock_info.alias_cnt &&
|
||||
i<ice_st->cfg.stun.max_host_cands; ++i)
|
||||
{
|
||||
char addrinfo[PJ_INET6_ADDRSTRLEN+10];
|
||||
const pj_sockaddr *addr = &stun_sock_info.aliases[i];
|
||||
|
||||
|
@ -646,6 +666,30 @@ PJ_DEF(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the value of various options of the ICE stream transport.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_ice_strans_get_options( pj_ice_strans *ice_st,
|
||||
pj_ice_sess_options *opt)
|
||||
{
|
||||
PJ_ASSERT_RETURN(ice_st && opt, PJ_EINVAL);
|
||||
pj_memcpy(opt, &ice_st->cfg.opt, sizeof(*opt));
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Specify various options for this ICE stream transport.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_ice_strans_set_options(pj_ice_strans *ice_st,
|
||||
const pj_ice_sess_options *opt)
|
||||
{
|
||||
PJ_ASSERT_RETURN(ice_st && opt, PJ_EINVAL);
|
||||
pj_memcpy(&ice_st->cfg.opt, opt, sizeof(*opt));
|
||||
if (ice_st->ice)
|
||||
pj_ice_sess_set_options(ice_st->ice, &ice_st->cfg.opt);
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create ICE!
|
||||
*/
|
||||
|
@ -682,6 +726,9 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
|
|||
/* Associate user data */
|
||||
ice_st->ice->user_data = (void*)ice_st;
|
||||
|
||||
/* Set options */
|
||||
pj_ice_sess_set_options(ice_st->ice, &ice_st->cfg.opt);
|
||||
|
||||
/* If default candidate for components are SRFLX one, upload a custom
|
||||
* type priority to ICE session so that SRFLX candidates will get
|
||||
* checked first.
|
||||
|
|
|
@ -528,6 +528,7 @@ PJ_DEF(pj_bool_t) pj_stun_auth_valid_for_msg(const pj_stun_msg *msg)
|
|||
switch (err_attr->err_code) {
|
||||
case PJ_STUN_SC_BAD_REQUEST: /* 400 (Bad Request) */
|
||||
case PJ_STUN_SC_UNAUTHORIZED: /* 401 (Unauthorized) */
|
||||
case PJ_STUN_SC_STALE_NONCE: /* 438 (Stale Nonce) */
|
||||
|
||||
/* Due to the way this response is generated here, we can't really
|
||||
* authenticate 420 (Unknown Attribute) response */
|
||||
|
|
|
@ -57,11 +57,19 @@ static int print_attr(char *buffer, unsigned length,
|
|||
const pj_stun_attr_hdr *ahdr)
|
||||
{
|
||||
char *p = buffer, *end = buffer + length;
|
||||
const char *attr_name = pj_stun_get_attr_name(ahdr->type);
|
||||
char attr_buf[32];
|
||||
int len;
|
||||
|
||||
if (*attr_name == '?') {
|
||||
pj_ansi_snprintf(attr_buf, sizeof(attr_buf), "Attr 0x%x",
|
||||
ahdr->type);
|
||||
attr_name = attr_buf;
|
||||
}
|
||||
|
||||
len = pj_ansi_snprintf(p, end-p,
|
||||
" %s: length=%d",
|
||||
pj_stun_get_attr_name(ahdr->type),
|
||||
attr_name,
|
||||
(int)ahdr->length);
|
||||
APPLY();
|
||||
|
||||
|
|
|
@ -219,10 +219,15 @@ static pj_status_t apply_msg_options(pj_stun_session *sess,
|
|||
|
||||
/* If the agent is sending a request, it SHOULD add a SOFTWARE attribute
|
||||
* to the request. The server SHOULD include a SOFTWARE attribute in all
|
||||
* responses
|
||||
* responses.
|
||||
*
|
||||
* If magic value is not PJ_STUN_MAGIC, only apply the attribute for
|
||||
* responses.
|
||||
*/
|
||||
if (sess->srv_name.slen && !PJ_STUN_IS_INDICATION(msg->hdr.type) &&
|
||||
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL)
|
||||
if (sess->srv_name.slen &&
|
||||
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL &&
|
||||
(PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
|
||||
PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))
|
||||
{
|
||||
pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE,
|
||||
&sess->srv_name);
|
||||
|
@ -630,6 +635,18 @@ PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess,
|
|||
sess->log_flag = flags;
|
||||
}
|
||||
|
||||
PJ_DEF(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess,
|
||||
pj_bool_t use)
|
||||
{
|
||||
pj_bool_t old_use;
|
||||
|
||||
PJ_ASSERT_RETURN(sess, PJ_FALSE);
|
||||
|
||||
old_use = sess->use_fingerprint;
|
||||
sess->use_fingerprint = use;
|
||||
return old_use;
|
||||
}
|
||||
|
||||
static pj_status_t get_auth(pj_stun_session *sess,
|
||||
pj_stun_tx_data *tdata)
|
||||
{
|
||||
|
|
|
@ -264,7 +264,8 @@ static void usage(void)
|
|||
puts ("");
|
||||
puts ("Media Transport Options:");
|
||||
puts (" --use-ice Enable ICE (default:no)");
|
||||
puts (" --ice-no-host Disable ICE host candidates (default: no)");
|
||||
puts (" --ice-regular Use ICE regular nomination (default: aggressive)");
|
||||
puts (" --ice-max-hosts=N Set maximum number of ICE host candidates");
|
||||
puts (" --ice-no-rtcp Disable RTCP component in ICE (default: no)");
|
||||
puts (" --rtp-port=N Base port to try for RTP (default=4000)");
|
||||
puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
|
||||
|
@ -476,8 +477,8 @@ static pj_status_t parse_args(int argc, char *argv[],
|
|||
OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
|
||||
OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
|
||||
OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
|
||||
OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
|
||||
OPT_USE_TURN, OPT_ICE_NO_HOST, OPT_ICE_NO_RTCP, OPT_TURN_SRV,
|
||||
OPT_USE_ICE, OPT_ICE_REGULAR, OPT_USE_SRTP, OPT_SRTP_SECURE,
|
||||
OPT_USE_TURN, OPT_ICE_MAX_HOSTS, OPT_ICE_NO_RTCP, OPT_TURN_SRV,
|
||||
OPT_TURN_TCP, OPT_TURN_USER, OPT_TURN_PASSWD,
|
||||
OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
|
||||
OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
|
||||
|
@ -553,8 +554,9 @@ static pj_status_t parse_args(int argc, char *argv[],
|
|||
{ "rtp-port", 1, 0, OPT_RTP_PORT},
|
||||
|
||||
{ "use-ice", 0, 0, OPT_USE_ICE},
|
||||
{ "ice-regular",0, 0, OPT_ICE_REGULAR},
|
||||
{ "use-turn", 0, 0, OPT_USE_TURN},
|
||||
{ "ice-no-host",0, 0, OPT_ICE_NO_HOST},
|
||||
{ "ice-max-hosts",1, 0, OPT_ICE_MAX_HOSTS},
|
||||
{ "ice-no-rtcp",0, 0, OPT_ICE_NO_RTCP},
|
||||
{ "turn-srv", 1, 0, OPT_TURN_SRV},
|
||||
{ "turn-tcp", 0, 0, OPT_TURN_TCP},
|
||||
|
@ -992,12 +994,16 @@ static pj_status_t parse_args(int argc, char *argv[],
|
|||
cfg->media_cfg.enable_ice = PJ_TRUE;
|
||||
break;
|
||||
|
||||
case OPT_ICE_REGULAR:
|
||||
cfg->media_cfg.ice_opt.aggressive = PJ_FALSE;
|
||||
break;
|
||||
|
||||
case OPT_USE_TURN:
|
||||
cfg->media_cfg.enable_turn = PJ_TRUE;
|
||||
break;
|
||||
|
||||
case OPT_ICE_NO_HOST:
|
||||
cfg->media_cfg.ice_no_host_cands = PJ_TRUE;
|
||||
case OPT_ICE_MAX_HOSTS:
|
||||
cfg->media_cfg.ice_max_host_cands = my_atoi(pj_optarg);
|
||||
break;
|
||||
|
||||
case OPT_ICE_NO_RTCP:
|
||||
|
@ -1644,11 +1650,17 @@ static int write_settings(const struct app_config *config,
|
|||
if (config->media_cfg.enable_ice)
|
||||
pj_strcat2(&cfg, "--use-ice\n");
|
||||
|
||||
if (config->media_cfg.ice_opt.aggressive == PJ_FALSE)
|
||||
pj_strcat2(&cfg, "--ice-regular\n");
|
||||
|
||||
if (config->media_cfg.enable_turn)
|
||||
pj_strcat2(&cfg, "--use-turn\n");
|
||||
|
||||
if (config->media_cfg.ice_no_host_cands)
|
||||
pj_strcat2(&cfg, "--ice-no-host\n");
|
||||
if (config->media_cfg.ice_max_host_cands >= 0) {
|
||||
pj_ansi_sprintf(line, "--ice_max_host_cands %d\n",
|
||||
config->media_cfg.ice_max_host_cands);
|
||||
pj_strcat2(&cfg, line);
|
||||
}
|
||||
|
||||
if (config->media_cfg.ice_no_rtcp)
|
||||
pj_strcat2(&cfg, "--ice-no-rtcp\n");
|
||||
|
@ -1885,7 +1897,7 @@ static int write_settings(const struct app_config *config,
|
|||
pj_strcat2(&cfg, "--use-compact-form\n");
|
||||
}
|
||||
|
||||
if (config->cfg.force_lr) {
|
||||
if (!config->cfg.force_lr) {
|
||||
pj_strcat2(&cfg, "--no-force-lr\n");
|
||||
}
|
||||
|
||||
|
@ -3687,7 +3699,9 @@ void console_app_main(const pj_str_t *uri_to_call)
|
|||
pj_list_push_back(&msg_data.hdr_list, &refer_sub);
|
||||
}
|
||||
|
||||
pjsua_call_xfer_replaces(call, dst_call, 0, &msg_data);
|
||||
pjsua_call_xfer_replaces(call, dst_call,
|
||||
PJSUA_XFER_NO_REQUIRE_REPLACES,
|
||||
&msg_data);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -4718,19 +4732,62 @@ static pj_status_t create_ipv6_media_transports(void)
|
|||
|
||||
for (i=0; i<app_config.cfg.max_calls; ++i) {
|
||||
enum { MAX_RETRY = 10 };
|
||||
pj_sock_t sock[2];
|
||||
pjmedia_sock_info si;
|
||||
unsigned j;
|
||||
|
||||
/* Get rid of uninitialized var compiler warning with MSVC */
|
||||
status = PJ_SUCCESS;
|
||||
|
||||
for (j=0; j<MAX_RETRY; ++j) {
|
||||
status = pjmedia_transport_udp_create3(pjsua_get_pjmedia_endpt(),
|
||||
pj_AF_INET6(),
|
||||
NULL,
|
||||
&app_config.rtp_cfg.bound_addr,
|
||||
port,
|
||||
0, &tp[i].transport);
|
||||
unsigned k;
|
||||
|
||||
for (k=0; k<2; ++k) {
|
||||
pj_sockaddr bound_addr;
|
||||
|
||||
status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0, &sock[k]);
|
||||
if (status != PJ_SUCCESS)
|
||||
break;
|
||||
|
||||
status = pj_sockaddr_init(pj_AF_INET6(), &bound_addr,
|
||||
&app_config.rtp_cfg.bound_addr,
|
||||
(unsigned short)(port+k));
|
||||
if (status != PJ_SUCCESS)
|
||||
break;
|
||||
|
||||
status = pj_sock_bind(sock[k], &bound_addr,
|
||||
pj_sockaddr_get_len(&bound_addr));
|
||||
if (status != PJ_SUCCESS)
|
||||
break;
|
||||
}
|
||||
if (status != PJ_SUCCESS) {
|
||||
if (k==1)
|
||||
pj_sock_close(sock[0]);
|
||||
|
||||
if (port != 0)
|
||||
port += 10;
|
||||
else
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
pj_bzero(&si, sizeof(si));
|
||||
si.rtp_sock = sock[0];
|
||||
si.rtcp_sock = sock[1];
|
||||
|
||||
pj_sockaddr_init(pj_AF_INET6(), &si.rtp_addr_name,
|
||||
&app_config.rtp_cfg.public_addr,
|
||||
(unsigned short)(port));
|
||||
pj_sockaddr_init(pj_AF_INET6(), &si.rtcp_addr_name,
|
||||
&app_config.rtp_cfg.public_addr,
|
||||
(unsigned short)(port+1));
|
||||
|
||||
status = pjmedia_transport_udp_attach(pjsua_get_pjmedia_endpt(),
|
||||
NULL,
|
||||
&si,
|
||||
0,
|
||||
&tp[i].transport);
|
||||
if (port != 0)
|
||||
port += 10;
|
||||
else
|
||||
|
|
|
@ -28,5 +28,5 @@
|
|||
* E.g.:
|
||||
* #include "playfile.c"
|
||||
*/
|
||||
#include "auddemo.c"
|
||||
#include "icedemo.c"
|
||||
|
||||
|
|
|
@ -39,7 +39,8 @@ static struct app_t
|
|||
{
|
||||
unsigned comp_cnt;
|
||||
pj_str_t ns;
|
||||
pj_bool_t no_host;
|
||||
int max_host;
|
||||
pj_bool_t regular;
|
||||
pj_str_t stun_srv;
|
||||
pj_str_t turn_srv;
|
||||
pj_bool_t turn_tcp;
|
||||
|
@ -306,8 +307,15 @@ static pj_status_t icedemo_init(void)
|
|||
|
||||
/* -= Start initializing ICE stream transport config =- */
|
||||
|
||||
/* Disable host candidates? */
|
||||
icedemo.ice_cfg.stun.no_host_cands = icedemo.opt.no_host;
|
||||
/* Maximum number of host candidates */
|
||||
if (icedemo.opt.max_host != -1)
|
||||
icedemo.ice_cfg.stun.max_host_cands = icedemo.opt.max_host;
|
||||
|
||||
/* Nomination strategy */
|
||||
if (icedemo.opt.regular)
|
||||
icedemo.ice_cfg.opt.aggressive = PJ_FALSE;
|
||||
else
|
||||
icedemo.ice_cfg.opt.aggressive = PJ_TRUE;
|
||||
|
||||
/* Configure STUN/srflx candidate resolution */
|
||||
if (icedemo.opt.stun_srv.slen) {
|
||||
|
@ -960,7 +968,7 @@ static void icedemo_send_data(unsigned comp_id, const char *data)
|
|||
}
|
||||
*/
|
||||
|
||||
if (comp_id > pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
|
||||
if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
|
||||
PJ_LOG(1,(THIS_FILE, "Error: invalid component ID"));
|
||||
return;
|
||||
}
|
||||
|
@ -1137,7 +1145,8 @@ static void icedemo_usage()
|
|||
puts(" --comp-cnt, -c N Component count (default=1)");
|
||||
puts(" --nameserver, -n IP Configure nameserver to activate DNS SRV");
|
||||
puts(" resolution");
|
||||
puts(" --no-host, -H Disable host candidates");
|
||||
puts(" --max-host, -H N Set max number of host candidates to N");
|
||||
puts(" --regular, -R Use regular nomination (default aggressive)");
|
||||
puts(" --help, -h Display this screen.");
|
||||
puts("");
|
||||
puts("STUN related options:");
|
||||
|
@ -1165,21 +1174,23 @@ int main(int argc, char *argv[])
|
|||
struct pj_getopt_option long_options[] = {
|
||||
{ "comp-cnt", 1, 0, 'c'},
|
||||
{ "nameserver", 1, 0, 'n'},
|
||||
{ "no-host", 0, 0, 'H'},
|
||||
{ "max-host", 1, 0, 'H'},
|
||||
{ "help", 0, 0, 'h'},
|
||||
{ "stun-srv", 1, 0, 's'},
|
||||
{ "turn-srv", 1, 0, 't'},
|
||||
{ "turn-tcp", 0, 0, 'T'},
|
||||
{ "turn-username", 1, 0, 'u'},
|
||||
{ "turn-password", 1, 0, 'p'},
|
||||
{ "turn-fingerprint", 0, 0, 'F'}
|
||||
{ "turn-fingerprint", 0, 0, 'F'},
|
||||
{ "regular", 0, 0, 'R'}
|
||||
};
|
||||
int c, opt_id;
|
||||
pj_status_t status;
|
||||
|
||||
icedemo.opt.comp_cnt = 1;
|
||||
icedemo.opt.max_host = -1;
|
||||
|
||||
while((c=pj_getopt_long(argc,argv, "n:s:t:u:p:HhTF", long_options, &opt_id))!=-1) {
|
||||
while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:hTFR", long_options, &opt_id))!=-1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
icedemo.opt.comp_cnt = atoi(pj_optarg);
|
||||
|
@ -1192,7 +1203,7 @@ int main(int argc, char *argv[])
|
|||
icedemo.opt.ns = pj_str(pj_optarg);
|
||||
break;
|
||||
case 'H':
|
||||
icedemo.opt.no_host = PJ_TRUE;
|
||||
icedemo.opt.max_host = atoi(pj_optarg);
|
||||
break;
|
||||
case 'h':
|
||||
icedemo_usage();
|
||||
|
@ -1215,6 +1226,9 @@ int main(int argc, char *argv[])
|
|||
case 'F':
|
||||
icedemo.opt.turn_fingerprint = PJ_TRUE;
|
||||
break;
|
||||
case 'R':
|
||||
icedemo.opt.regular = PJ_TRUE;
|
||||
break;
|
||||
default:
|
||||
printf("Argument \"%s\" is not valid. Use -h to see help",
|
||||
argv[pj_optind]);
|
||||
|
|
|
@ -77,7 +77,7 @@ static void print_call(int call_index)
|
|||
|
||||
/* Call identification */
|
||||
len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
|
||||
if (len < 1)
|
||||
if (len < 0)
|
||||
pj_ansi_strcpy(userinfo, "<--uri too long-->");
|
||||
else
|
||||
userinfo[len] = '\0';
|
||||
|
|
|
@ -226,6 +226,21 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void)
|
|||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* RFC 3261 section 18.1.1:
|
||||
* If a request is within 200 bytes of the path MTU, or if it is larger
|
||||
* than 1300 bytes and the path MTU is unknown, the request MUST be sent
|
||||
* using an RFC 2914 [43] congestion controlled transport protocol, such
|
||||
* as TCP.
|
||||
*
|
||||
* This setting controls the threshold of the UDP packet, which if it's
|
||||
* larger than this value the request will be sent with TCP. Default is
|
||||
* 1300 bytes.
|
||||
*/
|
||||
#ifndef PJSIP_UDP_SIZE_THRESHOLD
|
||||
# define PJSIP_UDP_SIZE_THRESHOLD 1300
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Encode SIP headers in their short forms to reduce size. By default,
|
||||
* SIP headers in outgoing messages will be encoded in their full names.
|
||||
|
|
|
@ -579,6 +579,17 @@ PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata );
|
|||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );
|
||||
|
||||
/**
|
||||
* Print the SIP message to transmit data buffer's internal buffer. This
|
||||
* may allocate memory for the buffer, if the buffer has not been allocated
|
||||
* yet, and encode the SIP message to that buffer.
|
||||
*
|
||||
* @param tdata The transmit buffer.
|
||||
*
|
||||
* @return PJ_SUCCESS on success of the appropriate error code.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata);
|
||||
|
||||
/**
|
||||
* Check if transmit data buffer contains a valid message.
|
||||
*
|
||||
|
|
|
@ -4260,9 +4260,16 @@ struct pjsua_media_config
|
|||
pj_bool_t enable_ice;
|
||||
|
||||
/**
|
||||
* Disable ICE host candidates.
|
||||
* Set the maximum number of host candidates.
|
||||
*
|
||||
* Default: -1 (maximum not set)
|
||||
*/
|
||||
pj_bool_t ice_no_host_cands;
|
||||
int ice_max_host_cands;
|
||||
|
||||
/**
|
||||
* ICE session options.
|
||||
*/
|
||||
pj_ice_sess_options ice_opt;
|
||||
|
||||
/**
|
||||
* Disable RTCP component.
|
||||
|
|
|
@ -456,16 +456,18 @@ PJ_DEF(pj_ssize_t) pjsip_msg_print( const pjsip_msg *msg,
|
|||
|
||||
/* Print each of the headers. */
|
||||
for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
|
||||
len = (*hdr->vptr->print_on)(hdr, p, end-p);
|
||||
if (len < 1)
|
||||
return -1;
|
||||
p += len;
|
||||
|
||||
if (p+3 >= end)
|
||||
len = pjsip_hdr_print_on(hdr, p, end-p);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
*p++ = '\r';
|
||||
*p++ = '\n';
|
||||
if (len > 0) {
|
||||
p += len;
|
||||
if (p+3 >= end)
|
||||
return -1;
|
||||
|
||||
*p++ = '\r';
|
||||
*p++ = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
/* Process message body. */
|
||||
|
@ -1601,6 +1603,25 @@ static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr,
|
|||
char *startbuf = buf;
|
||||
char *endbuf = buf + size;
|
||||
const pjsip_parser_const_t *pc = pjsip_parser_const();
|
||||
pjsip_sip_uri *sip_uri;
|
||||
pjsip_param *p;
|
||||
|
||||
/* Check the proprietary param 'hide', don't print this header
|
||||
* if it exists in the route URI.
|
||||
*/
|
||||
sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->name_addr.uri);
|
||||
p = sip_uri->other_param.next;
|
||||
while (p != &sip_uri->other_param) {
|
||||
const pj_str_t st_hide = {"hide", 4};
|
||||
|
||||
if (pj_stricmp(&p->name, &st_hide) == 0) {
|
||||
/* Check if param 'hide' is specified without 'lr'. */
|
||||
pj_assert(sip_uri->lr_param != 0);
|
||||
return 0;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
/* Route and Record-Route don't compact forms */
|
||||
|
||||
copy_advance(buf, hdr->name);
|
||||
|
|
|
@ -1725,6 +1725,19 @@ static void send_msg_callback( pjsip_send_state *send_state,
|
|||
"will try next server. Err=%d (%s)",
|
||||
pjsip_tx_data_get_info(send_state->tdata), -sent,
|
||||
pj_strerror(-sent, errmsg, sizeof(errmsg)).ptr));
|
||||
|
||||
/* Reset retransmission count */
|
||||
tsx->retransmit_count = 0;
|
||||
|
||||
/* And reset timeout timer */
|
||||
if (tsx->timeout_timer.id) {
|
||||
pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
|
||||
tsx->timeout_timer.id = TIMER_INACTIVE;
|
||||
|
||||
tsx->timeout_timer.id = TIMER_ACTIVE;
|
||||
pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
|
||||
&timeout_timer_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -434,6 +434,45 @@ PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
|
|||
tdata->info = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the SIP message to transmit data buffer's internal buffer.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata)
|
||||
{
|
||||
/* Allocate buffer if necessary. */
|
||||
if (tdata->buf.start == NULL) {
|
||||
PJ_USE_EXCEPTION;
|
||||
|
||||
PJ_TRY {
|
||||
tdata->buf.start = (char*)
|
||||
pj_pool_alloc(tdata->pool, PJSIP_MAX_PKT_LEN);
|
||||
}
|
||||
PJ_CATCH_ANY {
|
||||
return PJ_ENOMEM;
|
||||
}
|
||||
PJ_END
|
||||
|
||||
tdata->buf.cur = tdata->buf.start;
|
||||
tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
|
||||
}
|
||||
|
||||
/* Do we need to reprint? */
|
||||
if (!pjsip_tx_data_is_valid(tdata)) {
|
||||
pj_ssize_t size;
|
||||
|
||||
size = pjsip_msg_print( tdata->msg, tdata->buf.start,
|
||||
tdata->buf.end - tdata->buf.start);
|
||||
if (size < 0) {
|
||||
return PJSIP_EMSGTOOLONG;
|
||||
}
|
||||
pj_assert(size != 0);
|
||||
tdata->buf.cur[size] = '\0';
|
||||
tdata->buf.cur += size;
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata )
|
||||
{
|
||||
return tdata->buf.cur != tdata->buf.start;
|
||||
|
@ -567,38 +606,7 @@ static void transport_send_callback(pjsip_transport *transport,
|
|||
*/
|
||||
static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata)
|
||||
{
|
||||
/* Allocate buffer if necessary. */
|
||||
if (tdata->buf.start == NULL) {
|
||||
PJ_USE_EXCEPTION;
|
||||
|
||||
PJ_TRY {
|
||||
tdata->buf.start = (char*)
|
||||
pj_pool_alloc(tdata->pool, PJSIP_MAX_PKT_LEN);
|
||||
}
|
||||
PJ_CATCH_ANY {
|
||||
return PJ_ENOMEM;
|
||||
}
|
||||
PJ_END
|
||||
|
||||
tdata->buf.cur = tdata->buf.start;
|
||||
tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
|
||||
}
|
||||
|
||||
/* Do we need to reprint? */
|
||||
if (!pjsip_tx_data_is_valid(tdata)) {
|
||||
pj_ssize_t size;
|
||||
|
||||
size = pjsip_msg_print( tdata->msg, tdata->buf.start,
|
||||
tdata->buf.end - tdata->buf.start);
|
||||
if (size < 0) {
|
||||
return PJSIP_EMSGTOOLONG;
|
||||
}
|
||||
pj_assert(size != 0);
|
||||
tdata->buf.cur[size] = '\0';
|
||||
tdata->buf.cur += size;
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
return pjsip_tx_data_encode(tdata);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -907,7 +907,7 @@ static void print_dialog( const char *title,
|
|||
char userinfo[128];
|
||||
|
||||
len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
|
||||
if (len < 1)
|
||||
if (len < 0)
|
||||
pj_ansi_strcpy(userinfo, "<--uri too long-->");
|
||||
else
|
||||
userinfo[len] = '\0';
|
||||
|
|
|
@ -1135,6 +1135,8 @@ static void stateless_send_transport_cb( void *token,
|
|||
via->sent_by = stateless_data->cur_transport->local_name;
|
||||
via->rport_param = 0;
|
||||
|
||||
pjsip_tx_data_invalidate_msg(tdata);
|
||||
|
||||
/* Send message using this transport. */
|
||||
status = pjsip_transport_send( stateless_data->cur_transport,
|
||||
tdata,
|
||||
|
@ -1181,6 +1183,51 @@ stateless_send_resolver_callback( pj_status_t status,
|
|||
/* Copy server addresses */
|
||||
pj_memcpy( &stateless_data->addr, addr, sizeof(pjsip_server_addresses));
|
||||
|
||||
/* RFC 3261 section 18.1.1:
|
||||
* If a request is within 200 bytes of the path MTU, or if it is larger
|
||||
* than 1300 bytes and the path MTU is unknown, the request MUST be sent
|
||||
* using an RFC 2914 [43] congestion controlled transport protocol, such
|
||||
* as TCP.
|
||||
*/
|
||||
if (stateless_data->tdata->msg->type == PJSIP_REQUEST_MSG &&
|
||||
addr->count > 0 &&
|
||||
addr->entry[0].type == PJSIP_TRANSPORT_UDP)
|
||||
{
|
||||
int len;
|
||||
|
||||
/* Encode the request */
|
||||
status = pjsip_tx_data_encode(stateless_data->tdata);
|
||||
if (status != PJ_SUCCESS) {
|
||||
if (stateless_data->app_cb) {
|
||||
pj_bool_t cont = PJ_FALSE;
|
||||
(*stateless_data->app_cb)(stateless_data, -status, &cont);
|
||||
}
|
||||
pjsip_tx_data_dec_ref(stateless_data->tdata);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if request message is larger than 1300 bytes. */
|
||||
len = stateless_data->tdata->buf.cur -
|
||||
stateless_data->tdata->buf.start;
|
||||
if (len >= PJSIP_UDP_SIZE_THRESHOLD) {
|
||||
int i;
|
||||
int count = stateless_data->addr.count;
|
||||
|
||||
/* Insert "TCP version" of resolved UDP addresses at the
|
||||
* beginning.
|
||||
*/
|
||||
if (count * 2 > PJSIP_MAX_RESOLVED_ADDRESSES)
|
||||
count = PJSIP_MAX_RESOLVED_ADDRESSES / 2;
|
||||
for (i = 0; i < count; ++i) {
|
||||
pj_memcpy(&stateless_data->addr.entry[i+count],
|
||||
&stateless_data->addr.entry[i],
|
||||
sizeof(stateless_data->addr.entry[0]));
|
||||
stateless_data->addr.entry[i].type = PJSIP_TRANSPORT_TCP;
|
||||
}
|
||||
stateless_data->addr.count = count * 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process the addresses. */
|
||||
stateless_send_transport_cb( stateless_data, stateless_data->tdata,
|
||||
-PJ_EPENDING);
|
||||
|
|
|
@ -2694,7 +2694,7 @@ void print_call(const char *title,
|
|||
/* Dump invite sesion info. */
|
||||
|
||||
len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
|
||||
if (len < 1)
|
||||
if (len < 0)
|
||||
pj_ansi_strcpy(userinfo, "<--uri too long-->");
|
||||
else
|
||||
userinfo[len] = '\0';
|
||||
|
|
|
@ -182,6 +182,9 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
|
|||
cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
|
||||
cfg->snd_auto_close_time = 1;
|
||||
|
||||
cfg->ice_max_host_cands = -1;
|
||||
pj_ice_sess_options_default(&cfg->ice_opt);
|
||||
|
||||
cfg->turn_conn_type = PJ_TURN_TP_UDP;
|
||||
}
|
||||
|
||||
|
@ -647,7 +650,8 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
|
|||
/* Initialize logging first so that info/errors can be captured */
|
||||
if (log_cfg) {
|
||||
status = pjsua_reconfigure_logging(log_cfg);
|
||||
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
}
|
||||
|
||||
/* If nameserver is configured, create DNS resolver instance and
|
||||
|
|
|
@ -804,13 +804,16 @@ static pj_status_t create_ice_media_transports(void)
|
|||
ice_cfg.af = pj_AF_INET();
|
||||
ice_cfg.resolver = pjsua_var.resolver;
|
||||
|
||||
ice_cfg.opt = pjsua_var.media_cfg.ice_opt;
|
||||
|
||||
/* Configure STUN settings */
|
||||
if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
|
||||
pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
|
||||
ice_cfg.stun.server = pj_str(stunip);
|
||||
ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
|
||||
}
|
||||
ice_cfg.stun.no_host_cands = pjsua_var.media_cfg.ice_no_host_cands;
|
||||
if (pjsua_var.media_cfg.ice_max_host_cands >= 0)
|
||||
ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands;
|
||||
|
||||
/* Configure TURN settings */
|
||||
if (pjsua_var.media_cfg.enable_turn) {
|
||||
|
|
|
@ -365,16 +365,16 @@ parse_msg:
|
|||
hdr2 = ref_msg->hdr.next;
|
||||
|
||||
while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) {
|
||||
len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN);
|
||||
if (len < 1) {
|
||||
len = pjsip_hdr_print_on(hdr1, str1.ptr, BUFLEN);
|
||||
if (len < 0) {
|
||||
status = -40;
|
||||
goto on_return;
|
||||
}
|
||||
str1.ptr[len] = '\0';
|
||||
str1.slen = len;
|
||||
|
||||
len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
|
||||
if (len < 1) {
|
||||
len = pjsip_hdr_print_on(hdr2, str2.ptr, BUFLEN);
|
||||
if (len < 0) {
|
||||
status = -50;
|
||||
goto on_return;
|
||||
}
|
||||
|
@ -1944,7 +1944,7 @@ static int hdr_test(void)
|
|||
/* Print the parsed header*/
|
||||
output = (char*) pj_pool_alloc(pool, 1024);
|
||||
len = pjsip_hdr_print_on(parsed_hdr1, output, 1024);
|
||||
if (len < 1 || len >= 1024) {
|
||||
if (len < 0 || len >= 1024) {
|
||||
PJ_LOG(3,(THIS_FILE, " header too long: %s: %s", test->hname, test->hcontent));
|
||||
return -530;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue