Commit a947f14e authored by Pekka Pessi's avatar Pekka Pessi

nua_session.c: fixed session timer negotiation when UAS does refreshing with INVITEs

The session-expires header had "uac" even when uac did not support timer.
The UAS failed to send re-INVITEs.

Thanks for Chung Pak Lai for reporting this problem.

darcs-hash:20061116152534-65a35-84ba5e3a90dd6db4face90925f173b2141999971.gz
parent 29c6d7bd
......@@ -69,6 +69,42 @@ unsigned char const _bnf_table[256] = {
alpha, alpha, alpha, sep, 0, sep, mtok, 0, /* xyz{|}~ */
};
#define BM(c, m00, m32, m64, m96) \
((c < 64) \
? ((c < 32) \
? (m00 & (1 << (31 - c))) \
: (m32 & (1 << (63 - c)))) \
: ((c < 96) \
? (m64 & (1 << (95 - c))) \
: (m96 & (1 << (127 - c)))))
/** Span of a token */
size_t bnf_span_token(char const *s)
{
char const *e = s;
unsigned const m32 = 0x4536FFC0U, m64 = 0x7FFFFFE1U, m96 = 0xFFFFFFE2U;
while (BM(*e, 0, m32, m64, m96))
e++;
return e - s;
}
/** Span of a token */
size_t bnf_span_token4(char const *s)
{
char const *e = s;
while (_bnf_table[(unsigned char)(*e)] & bnf_token)
e++;
return e - s;
}
char * bnf_span_token_end(char const *s)
{
return (char *)s;
}
/** Return length of decimal-octet */
static inline int span_ip4_octet(char const *host)
{
......
......@@ -195,6 +195,7 @@ static inline isize_t span_token_lws(char const *s)
return e - s;
}
#if 1
/** Calculate span of a token characters. */
static inline isize_t span_token(char const *s)
{
......@@ -203,6 +204,11 @@ static inline isize_t span_token(char const *s)
e++;
return e - s;
}
#else
size_t bnf_span_token(char const *s);
#define span_token(s) bnf_span_token((s))
#endif
/** Calculate span of a alphabetic characters. */
static inline isize_t span_alpha(char const *s)
......
......@@ -184,6 +184,7 @@ int ip_test(void)
TEST_S(ip4b, "::");
s = ip4c; TEST(scan_ip6_address(&s), 19); TEST_S(s, ",");
TEST_S(ip4c, "::1");
TEST_S(ip5, "dead:beef:feed::0ded:0:1:2:3");
s = ip5; TEST(scan_ip6_address(&s), 28); TEST_S(s, "");
TEST_S(ip5, "dead:beef:feed:ded::1:2:3");
s = ip6; TEST(scan_ip6_address(&s), 40); TEST_S(s, "+");
......
......@@ -317,6 +317,7 @@ auth_status_t *auth_status_init_with(void *p,
}
/** Allocate a new auth_status_t structure. @relates auth_status_t */
auth_status_t *auth_status_new(su_home_t *home)
{
auth_status_t *as = su_home_clone(home, (sizeof *as));
......
......@@ -268,12 +268,16 @@ static int process_response_to_prack(nua_handle_t *nh,
static void nua_session_usage_destroy(nua_handle_t *, nua_session_usage_t *);
static void session_timer_preferences(nua_session_usage_t *ss,
unsigned expires,
unsigned min_se,
enum nua_session_refresher refresher);
static int session_timer_is_supported(nua_handle_t const *nh);
static int prefer_session_timer(nua_handle_t const *nh);
static int use_session_timer(nua_session_usage_t *ss, int uas, int always,
msg_t *msg, sip_t *);
static int init_session_timer(nua_session_usage_t *ss, sip_t const *, int refresher);
static int init_session_timer(nua_session_usage_t *ss, sip_t const *, int refresher);
static void set_session_timer(nua_session_usage_t *ss);
static int
......@@ -527,8 +531,19 @@ nua_stack_invite2(nua_t *nua, nua_handle_t *nh, nua_event_t e,
if (ss->ss_state == nua_callstate_terminated)
ss->ss_state = nua_callstate_init;
if (restarted && !cr->cr_msg)
cr->cr_msg = msg_dup(du->du_msg);
if (!restarted) {
session_timer_preferences(ss,
NH_PGET(nh, session_timer),
NH_PGET(nh, min_se),
NH_PGET(nh, refresher));
}
if (restarted && !cr->cr_msg) {
if (du->du_msg)
cr->cr_msg = msg_dup(du->du_msg);
else
restarted = 0;
}
msg = nua_creq_msg(nua, nh, cr, restarted,
SIP_METHOD_INVITE,
......@@ -537,14 +552,13 @@ nua_stack_invite2(nua_t *nua, nua_handle_t *nh, nua_event_t e,
TAG_NEXT(tags));
sip = sip_object(msg);
if (!sip)
if (!sip) {
what = "Cannot Initialize Request";
goto failure;
}
if (!restarted) {
msg_destroy(du->du_msg), du->du_msg = msg_dup(msg);
ss->ss_min_se = NH_PGET(nh, min_se);
ss->ss_session_timer = NH_PGET(nh, session_timer);
ss->ss_refresher = NH_PGET(nh, refresher);
}
if (nh->nh_soa) {
......@@ -1455,6 +1469,10 @@ static int respond_to_invite(nua_server_request_t *sr, tagi_t const *tags);
static int
preprocess_invite(nua_t *, nua_handle_t *, nua_server_request_t **, sip_t *),
session_check_request(nua_t *nua,
nua_handle_t *nh,
nta_incoming_t *irq,
sip_t const *sip),
process_invite(nua_t *, nua_handle_t *, nua_server_request_t *, sip_t *),
process_prack(nua_handle_t *, nta_reliable_t *, nta_incoming_t *,
sip_t const *);
......@@ -1612,7 +1630,6 @@ int preprocess_invite(nua_t *nua,
int have_sdp;
char const *sdp;
size_t len;
char const *user_agent;
if (nh) {
ds = nh->nh_ds;
......@@ -1625,38 +1642,11 @@ int preprocess_invite(nua_t *nua,
sr->sr_usage = du;
user_agent = NUA_PGET(nua, nh, user_agent);
if (!NUA_PGET(nua, nh, invite_enable))
return SR_STATUS1(sr, SIP_403_FORBIDDEN);
if (nh->nh_soa) {
/* Make sure caller uses application/sdp without compression */
if (nta_check_session_content(sr->sr_irq, sip,
nua->nua_invite_accept,
SIPTAG_USER_AGENT_STR(user_agent),
SIPTAG_ACCEPT_ENCODING_STR(""),
TAG_END()))
return 415;
/* Make sure caller accepts application/sdp */
if (nta_check_accept(sr->sr_irq, sip,
nua->nua_invite_accept,
NULL,
SIPTAG_USER_AGENT_STR(user_agent),
SIPTAG_ACCEPT_ENCODING_STR(""),
TAG_END()))
return 406;
}
if (sip->sip_session_expires) {
unsigned min_se = ss ? ss->ss_min_se : NH_PGET(nh, min_se);
if (nta_check_session_expires(sr->sr_irq, sip,
min_se,
SIPTAG_USER_AGENT_STR(user_agent),
TAG_END()))
return 422;
}
if (session_check_request(nua, nh, sr->sr_irq, sip))
return 500;
have_sdp = session_get_description(sip, &sdp, &len);
......@@ -1724,6 +1714,47 @@ int preprocess_invite(nua_t *nua,
return 0;
}
static int
session_check_request(nua_t *nua,
nua_handle_t *nh,
nta_incoming_t *irq,
sip_t const *sip)
{
char const *user_agent = NUA_PGET(nua, nh, user_agent);
if (nh->nh_soa) {
/* Make sure caller uses application/sdp without compression */
if (nta_check_session_content(irq, sip,
nua->nua_invite_accept,
SIPTAG_USER_AGENT_STR(user_agent),
SIPTAG_ACCEPT_ENCODING_STR(""),
TAG_END()))
return 415;
/* Make sure caller accepts application/sdp */
if (nta_check_accept(irq, sip,
nua->nua_invite_accept,
NULL,
SIPTAG_USER_AGENT_STR(user_agent),
SIPTAG_ACCEPT_ENCODING_STR(""),
TAG_END()))
return 406;
}
if (sip->sip_session_expires) {
unsigned min_se = NH_PGET(nh, min_se);
if (sip->sip_min_se && min_se < sip->sip_min_se->min_delta)
min_se = sip->sip_min_se->min_delta;
if (nta_check_session_expires(irq, sip,
min_se,
SIPTAG_USER_AGENT_STR(user_agent),
TAG_END()))
return 422;
}
return 0;
}
/** @internal Process incoming invite - initiate media, etc. */
static
int process_invite(nua_t *nua,
......@@ -1740,9 +1771,11 @@ int process_invite(nua_t *nua,
ss->ss_precondition = sip_has_feature(sip->sip_require, "precondition");
if (ss->ss_precondition)
ss->ss_100rel = 1;
ss->ss_min_se = NH_PGET(nh, min_se);
ss->ss_session_timer = NH_PGET(nh, session_timer);
ss->ss_refresher = NH_PGET(nh, refresher);
session_timer_preferences(ss,
NH_PGET(nh, session_timer),
NH_PGET(nh, min_se),
NH_PGET(nh, refresher));
/* Session Timer negotiation */
if (sip_has_supported(NH_PGET(nh, supported), "timer"))
......@@ -2345,6 +2378,24 @@ static int prefer_session_timer(nua_handle_t const *nh)
NH_PGET(nh, session_timer) != 0;
}
/* Initialize session timer */
static
void session_timer_preferences(nua_session_usage_t *ss,
unsigned expires,
unsigned min_se,
enum nua_session_refresher refresher)
{
if (expires < min_se)
expires = min_se;
if (refresher && expires == 0)
expires = 3600;
ss->ss_min_se = min_se;
ss->ss_session_timer = expires;
ss->ss_refresher = refresher;
}
/** Add timer featuretag and Session-Expires/Min-SE headers */
static int
use_session_timer(nua_session_usage_t *ss, int uas, int always,
......@@ -2390,8 +2441,11 @@ init_session_timer(nua_session_usage_t *ss,
int server;
/* Session timer is not needed */
if (!sip->sip_session_expires)
if (!sip->sip_session_expires) {
if (!sip_has_supported(sip->sip_supported, "timer"))
ss->ss_refresher = nua_local_refresher;
return 0;
}
ss->ss_refresher = nua_no_refresher;
ss->ss_session_timer = sip->sip_session_expires->x_delta;
......@@ -2422,7 +2476,7 @@ init_session_timer(nua_session_usage_t *ss,
server ? sip->sip_request->rq_method_name : "response to",
server ? "request" : sip->sip_cseq->cs_method_name));
return 0;
return 1;
}
static void
......@@ -2996,8 +3050,11 @@ int nua_stack_process_update(nua_t *nua,
return 481;
}
if (session_check_request(nua, nh, irq, sip))
return 501;
/* Do session timer negotiation */
if (status < 300 && sip->sip_session_expires) {
if (sip->sip_session_expires) {
use_timer = 1;
init_session_timer(ss, sip, NH_PGET(nh, refresher));
}
......
......@@ -54,12 +54,18 @@ int test_session_timer(struct context *ctx)
struct event *e;
sip_t const *sip;
if (print_headings)
printf("TEST NUA-8.1: Session timers\n");
/* Session timer test:
A P B
|-------INVITE------>|
|<----100 Trying-----|
| |
|<----180 Ringing----|
| |
|<------200 OK-------|
|------------------->|
| |
| |
|--INVITE->| |
|<--422----| |
|---ACK--->| |
......@@ -82,14 +88,92 @@ int test_session_timer(struct context *ctx)
*/
if (print_headings)
printf("TEST NUA-8.1.1: Session timers\n");
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
nua_set_params(ctx->b.nua,
NUTAG_SESSION_REFRESHER(nua_any_refresher),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
SIPTAG_SUPPORTED_STR("100rel"),
TAG_END());
run_ab_until(ctx, -1, until_ready, -1, accept_call);
/* Client transitions:
INIT -(C1)-> CALLING: nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
TEST_1(is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 180);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 200);
TEST_1(sip = sip_object(e->data->e_msg));
TEST_1(sip->sip_session_expires);
TEST_S(sip->sip_session_expires->x_refresher, "uas");
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
TEST(e->data->e_status, 100);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
TEST_1(is_offer_recv(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
TEST_1(is_answer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
TEST_1(!e->next);
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-8.1.1: PASSED\n");
if (print_headings)
printf("TEST NUA-8.1.2: Session timers negotiation\n");
nua_set_hparams(b_call->nh,
NUTAG_AUTOANSWER(0),
NUTAG_MIN_SE(120),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
SIPTAG_SUPPORTED_STR("100rel, timer"),
NUTAG_SESSION_TIMER(15),
NUTAG_MIN_SE(5),
TAG_END());
......
......@@ -13,7 +13,8 @@ TAGFILES += \
../docs/msg.doxytags=../msg \
../docs/sdp.doxytags=../sdp \
../docs/iptsec.doxytags=../iptsec \
../docs/nta.doxytags=../nta
../docs/nta.doxytags=../nta \
../docs/nua.doxytags=../nua
GENERATE_TAGFILE = ../docs/sip.doxytags
......
......@@ -783,7 +783,7 @@ defined in @RFC3264. The session object is used to
store the SDP template from user, remote SDP received from the network and
the negotiation result, local SDP.
@par Functions used to create, copy and destroy @soa session objects:
@par Functions used to create, copy and destroy "soa" session objects:
@code
soa_session_t *soa_create(char const *name, su_root_t *, soa_magic_t *);
......@@ -814,7 +814,7 @@ int soa_get_warning(soa_session_t *ss, char const **return_phrase);
@endcode
@par Functions Used to Store and Retrieve SDP Descriptions:
@par Functions used to store and retrieve SDP descriptions:
@code
int soa_set_capability_sdp(soa_session_t *ss,
struct sdp_session_s const *sdp,
......@@ -856,7 +856,7 @@ int soa_get_local_sdp(soa_session_t const *ss,
@endcode
@par Functions for Executing Offer/Answer Negotiation Steps:
@par Functions for executing Offer/Answer negotiation steps:
@code
int soa_init_offer_answer(soa_session_t *ss);
int soa_generate_offer(soa_session_t *, int always, soa_callback_f *);
......@@ -866,12 +866,14 @@ int soa_process_reject(soa_session_t *, soa_callback_f *);
int soa_is_complete(soa_session_t const *ss);
@endcode
@par Functions for Signaling Completion of Session Signaling:
@par Functions for signaling events of session signaling:
@code
int soa_activate(soa_session_t *, char const *option);
int soa_deactivate(soa_session_t *, char const *option);
void soa_terminate(soa_session_t *, char const *option);
@endcode
These functions are used to activate actions taken by @soa, for instance,
a COMEDIA connection is established with soa_activate().
@par Functions for Checking Activated Media:
@code
......
......@@ -1297,6 +1297,8 @@ void *su_realloc(su_home_t *home, void *data, isize_t size)
*
* @retval 1 if @a memory has been allocated from @a home.
* @retval 0 otherwise
*
* @since New in @VERSION_1_12_4.
*/
int su_in_home(su_home_t *home, void const *memory)
{
......
......@@ -1478,4 +1478,3 @@ su_timer_t **su_port_timers(su_port_t *self)
{
return &self->sup_timers;
}
......@@ -316,7 +316,7 @@ tagi_t *tl_adup(su_home_t *home, tagi_t const lst[])
d = tl_dup(newlst, lst, &b);
assert(b == end); assert(tend == d);
assert(b == end); assert(tend == d); (void)end; (void)tend;
return newlst;
}
......
......@@ -137,7 +137,8 @@ struct tport_pending_s {
void *p_client;
tport_pending_error_f *p_callback;
msg_t *p_msg;
unsigned p_reported;
unsigned short p_reported;
unsigned short p_on_success;
};
/** Return true if transport is master. */
......
......@@ -183,7 +183,8 @@ struct tport_s {
tport_pending_t *tp_released; /**< Released pends */
unsigned tp_plen; /**< Size of tp_pending */
unsigned tp_pused; /**< Used pends */
unsigned tp_reported; /**< Report counter */
unsigned short tp_reported; /**< Report counter */
unsigned short tp_pad;
/* ==== Send queue ===================================================== */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment