sip_xaction_state_mc.c revision 40cb5e5daa7b80bb70fcf8dadfb20f9281566331
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* SIP Client/Server Invite/Non-Invite Transaction State machine.
*/
#include "sip_parse_uri.h"
#include "sip_msg.h"
#include "sip_miscdefs.h"
#include "sip_xaction.h"
/*
* Some Timer related info from RFC 3261, page 265.
*
* ----------------------------------------------------------------------
* Timer Value Section Meaning
* ----------------------------------------------------------------------
* T1 500ms default Section 17.1.1.1 RTT Estimate
* T2 4s Section 17.1.2.2 The maximum retransmit
* interval for non-INVITE
* requests and INVITE
* responses
* T4 5s Section 17.1.2.2 Maximum duration a
* message will
* remain in the network
* ----------------------------------------------------------------------
* Timer A initially T1 Section 17.1.1.2 INVITE request retransmit
* interval, for UDP only
* Timer B 64*T1 Section 17.1.1.2 INVITE transaction
* timeout timer
* Timer C > 3min Section 16.6 proxy INVITE transaction
* bullet 11 timeout
* Timer D > 32s for UDP Section 17.1.1.2 Wait time for response
* 0s for TCP/SCTP retransmits
* Timer E initially T1 Section 17.1.2.2 non-INVITE request
* retransmit interval,
* UDP only
* Timer F 64*T1 Section 17.1.2.2 non-INVITE transaction
* timeout timer
* Timer G initially T1 Section 17.2.1 INVITE response
* retransmit interval
* Timer H 64*T1 Section 17.2.1 Wait time for
* ACK receipt
* Timer I T4 for UDP Section 17.2.1 Wait time for
* 0s for TCP/SCTP ACK retransmits
* Timer J 64*T1 for UDP Section 17.2.2 Wait time for
* 0s for TCP/SCTP non-INVITE request
* retransmits
* Timer K T4 for UDP Section 17.1.2.2 Wait time for
* 0s for TCP/SCTP response retransmits
* ----------------------------------------------------------------------
*/
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a):(b))
#endif
/*
* Arg to the timer fire routine
*/
typedef struct sip_xaction_timer_obj_s {
sip_xaction_timer_type_t sip_xaction_timer_type;
sip_xaction_t *sip_trans;
int sip_xaction_timer_xport;
} sip_xaction_time_obj_t;
int sip_xaction_output(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t *);
int sip_xaction_input(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t **);
void sip_xaction_terminate(sip_xaction_t *, _sip_msg_t *, int);
static int sip_clnt_xaction_output(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t *);
static int sip_clnt_xaction_input(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t **);
static int sip_clnt_xaction_inv_res(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t **);
static int sip_clnt_xaction_noninv_res(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t **);
static int sip_srv_xaction_output(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t *);
static int sip_srv_xaction_input(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t **);
static int sip_srv_xaction_inv_res(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t *);
static int sip_srv_xaction_noninv_res(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t *);
static int sip_create_send_nonOKack(sip_conn_object_t, sip_xaction_t *,
_sip_msg_t *, boolean_t);
void sip_xaction_state_timer_fire(void *);
static sip_xaction_time_obj_t *sip_setup_timer(sip_conn_object_t,
sip_xaction_t *, _sip_msg_t *,
sip_timer_t, int);
/*
* Return a timer object
*/
static sip_xaction_time_obj_t *
sip_setup_timer(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t *sip_msg, sip_timer_t timer, int type)
{
sip_xaction_time_obj_t *sip_timer_obj = NULL;
sip_timer_obj = (sip_xaction_time_obj_t *)
malloc(sizeof (sip_xaction_time_obj_t));
if (sip_timer_obj == NULL)
return (NULL);
if (SIP_IS_TIMER_RUNNING(timer))
SIP_CANCEL_TIMER(timer);
sip_timer_obj->sip_xaction_timer_type = type;
sip_timer_obj->sip_xaction_timer_xport = sip_conn_transport(conn_obj);
sip_timer_obj->sip_trans = sip_trans;
/*
* Save the message
*/
if (sip_msg != NULL) {
(void) sip_add_conn_obj_cache(conn_obj, (void *)sip_trans);
if (sip_trans->sip_xaction_last_msg != NULL) {
SIP_MSG_REFCNT_DECR(sip_trans->sip_xaction_last_msg);
sip_trans->sip_xaction_last_msg = NULL;
}
SIP_MSG_REFCNT_INCR(sip_msg);
sip_trans->sip_xaction_last_msg = sip_msg;
}
return (sip_timer_obj);
}
/*
* --------------------------- Output Routines ---------------------------
*/
/*
* Send a SIP message, request or response, out
*/
int
sip_xaction_output(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t *msg)
{
sip_message_type_t *sip_msg_info;
int ret;
assert(conn_obj != NULL);
sip_msg_info = msg->sip_msg_req_res;
if (sip_msg_info->is_request)
return (sip_clnt_xaction_output(conn_obj, sip_trans, msg));
ret = sip_srv_xaction_output(conn_obj, sip_trans, msg);
return (ret);
}
/*
* Send a Request out
*/
static int
sip_clnt_xaction_output(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t *msg)
{
sip_xaction_time_obj_t *timer_obj_A = NULL;
sip_xaction_time_obj_t *timer_obj_B = NULL;
sip_xaction_time_obj_t *timer_obj_E = NULL;
sip_xaction_time_obj_t *timer_obj_F = NULL;
sip_message_type_t *sip_msg_info;
int prev_state;
int error = 0;
boolean_t isreliable;
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
prev_state = sip_trans->sip_xaction_state;
assert(msg->sip_msg_req_res != NULL);
sip_msg_info = msg->sip_msg_req_res;
isreliable = sip_is_conn_reliable(conn_obj);
if (sip_msg_info->sip_req_method == INVITE) {
/*
* if transport is not reliable, start TIMER A.
*/
if (!isreliable) {
timer_obj_A = sip_setup_timer(conn_obj, sip_trans,
msg, sip_trans->sip_xaction_TA,
SIP_XACTION_TIMER_A);
if (timer_obj_A == NULL) {
error = ENOMEM;
goto error_ret;
}
}
timer_obj_B = sip_setup_timer(conn_obj, sip_trans, NULL,
sip_trans->sip_xaction_TB, SIP_XACTION_TIMER_B);
if (timer_obj_B == NULL) {
error = ENOMEM;
goto error_ret;
}
if (timer_obj_A != NULL) {
SIP_SCHED_TIMER(sip_trans->sip_xaction_TA, timer_obj_A,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TA)) {
error = ENOMEM;
goto error_ret;
}
}
SIP_SCHED_TIMER(sip_trans->sip_xaction_TB, timer_obj_B,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TB)) {
if (timer_obj_A != NULL)
SIP_CANCEL_TIMER(sip_trans->sip_xaction_TA)
error = ENOMEM;
goto error_ret;
}
sip_trans->sip_xaction_state = SIP_CLNT_CALLING;
} else {
/*
* if transport is not reliable, start rexmit Timer E.
*/
if (!isreliable) {
timer_obj_E = sip_setup_timer(conn_obj, sip_trans, msg,
sip_trans->sip_xaction_TE, SIP_XACTION_TIMER_E);
if (timer_obj_E == NULL) {
error = ENOMEM;
goto error_ret;
}
}
/*
* Start transaction Timer F
*/
timer_obj_F = sip_setup_timer(conn_obj, sip_trans, NULL,
sip_trans->sip_xaction_TF, SIP_XACTION_TIMER_F);
if (timer_obj_F == NULL) {
error = ENOMEM;
goto error_ret;
}
if (timer_obj_E != NULL) {
SIP_SCHED_TIMER(sip_trans->sip_xaction_TE, timer_obj_E,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TE)) {
error = ENOMEM;
goto error_ret;
}
}
SIP_SCHED_TIMER(sip_trans->sip_xaction_TF, timer_obj_F,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TF)) {
if (timer_obj_E != NULL)
SIP_CANCEL_TIMER(sip_trans->sip_xaction_TE)
error = ENOMEM;
goto error_ret;
}
sip_trans->sip_xaction_state = SIP_CLNT_TRYING;
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
(sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
}
return (0);
error_ret:
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (timer_obj_A != NULL)
free(timer_obj_A);
if (timer_obj_B != NULL)
free(timer_obj_B);
if (timer_obj_E != NULL)
free(timer_obj_E);
if (timer_obj_F != NULL)
free(timer_obj_F);
return (error);
}
/*
* Send a response out
*/
static int
sip_srv_xaction_output(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t *msg)
{
int ret;
if (sip_trans->sip_xaction_method == INVITE)
ret = sip_srv_xaction_inv_res(conn_obj, sip_trans, msg);
else
ret = sip_srv_xaction_noninv_res(conn_obj, sip_trans, msg);
return (ret);
}
/*
* Send a INVITE response out
*/
static int
sip_srv_xaction_inv_res(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t *msg)
{
int resp_code;
sip_xaction_time_obj_t *timer_obj_G = NULL;
sip_xaction_time_obj_t *timer_obj_H = NULL;
sip_message_type_t *sip_msg_info = msg->sip_msg_req_res;
int prev_state;
boolean_t isreliable;
isreliable = sip_is_conn_reliable(conn_obj);
resp_code = sip_msg_info->sip_resp_code;
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
prev_state = sip_trans->sip_xaction_state;
switch (sip_trans->sip_xaction_state) {
case SIP_SRV_INV_PROCEEDING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
if (sip_trans->sip_xaction_last_msg != NULL) {
SIP_MSG_REFCNT_DECR(
sip_trans->sip_xaction_last_msg);
sip_trans->sip_xaction_last_msg = NULL;
}
SIP_MSG_REFCNT_INCR(msg);
sip_trans->sip_xaction_last_msg = msg;
(void) sip_add_conn_obj_cache(conn_obj,
(void *)sip_trans);
} else if (SIP_OK_RESP(resp_code)) {
sip_trans->sip_xaction_state =
SIP_SRV_INV_TERMINATED;
} else if (SIP_NONOK_FINAL_RESP(resp_code)) {
if (sip_trans->sip_xaction_last_msg != NULL) {
SIP_MSG_REFCNT_DECR(
sip_trans->sip_xaction_last_msg);
sip_trans->sip_xaction_last_msg = NULL;
}
SIP_MSG_REFCNT_INCR(msg);
sip_trans->sip_xaction_last_msg = msg;
(void) sip_add_conn_obj_cache(conn_obj,
(void *)sip_trans);
/*
* For unreliable transport start timer G
*/
if (!isreliable) {
timer_obj_G = sip_setup_timer(
conn_obj, sip_trans,
NULL, sip_trans->sip_xaction_TG,
SIP_XACTION_TIMER_G);
if (timer_obj_G == NULL) {
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
return (ENOMEM);
}
}
/*
* Start Timer H
*/
timer_obj_H = sip_setup_timer(
conn_obj, sip_trans,
NULL, sip_trans->sip_xaction_TH,
SIP_XACTION_TIMER_H);
if (timer_obj_H == NULL) {
if (timer_obj_G != NULL)
free(timer_obj_G);
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (ENOMEM);
}
if (timer_obj_G != NULL) {
SIP_SCHED_TIMER(
sip_trans->sip_xaction_TG,
timer_obj_G,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TG)) {
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
free(timer_obj_G);
return (ENOMEM);
}
}
if (timer_obj_H != NULL) {
SIP_SCHED_TIMER(
sip_trans->sip_xaction_TH,
timer_obj_H,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TH)) {
if (timer_obj_G != NULL) {
SIP_CANCEL_TIMER(
sip_trans->
sip_xaction_TG);
free(timer_obj_G);
}
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
free(timer_obj_H);
return (ENOMEM);
}
}
sip_trans->sip_xaction_state =
SIP_SRV_INV_COMPLETED;
}
break;
default:
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (prev_state != sip_trans->sip_xaction_state &&
sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
(sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
}
return (0);
}
/*
* Send a NON-INVITE response out
*/
static int
sip_srv_xaction_noninv_res(sip_conn_object_t conn_obj,
sip_xaction_t *sip_trans, _sip_msg_t *msg)
{
int resp_code;
sip_xaction_time_obj_t *timer_obj_J = NULL;
sip_message_type_t *sip_msg_info = msg->sip_msg_req_res;
int prev_state;
boolean_t isreliable;
resp_code = sip_msg_info->sip_resp_code;
isreliable = sip_is_conn_reliable(conn_obj);
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
prev_state = sip_trans->sip_xaction_state;
switch (sip_trans->sip_xaction_state) {
case SIP_SRV_TRYING:
if (sip_trans->sip_xaction_last_msg != NULL) {
SIP_MSG_REFCNT_DECR(
sip_trans->sip_xaction_last_msg);
sip_trans->sip_xaction_last_msg = NULL;
}
SIP_MSG_REFCNT_INCR(msg);
sip_trans->sip_xaction_last_msg = msg;
(void) sip_add_conn_obj_cache(conn_obj,
(void *)sip_trans);
if (SIP_PROVISIONAL_RESP(resp_code)) {
sip_trans->sip_xaction_state =
SIP_SRV_NONINV_PROCEEDING;
} else if (SIP_FINAL_RESP(resp_code)) {
/*
* For unreliable transports, start Timer J
*/
if (!isreliable) {
timer_obj_J = sip_setup_timer(
conn_obj, sip_trans,
NULL, sip_trans->sip_xaction_TJ,
SIP_XACTION_TIMER_J);
if (timer_obj_J == NULL) {
(void) pthread_mutex_unlock(&
sip_trans->
sip_xaction_mutex);
return (ENOMEM);
}
SIP_SCHED_TIMER(
sip_trans->sip_xaction_TJ,
timer_obj_J,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TJ)) {
(void) pthread_mutex_unlock(&
sip_trans->
sip_xaction_mutex);
free(timer_obj_J);
return (ENOMEM);
}
sip_trans->sip_xaction_state =
SIP_SRV_NONINV_COMPLETED;
} else {
sip_trans->sip_xaction_state =
SIP_SRV_NONINV_TERMINATED;
}
}
break;
case SIP_SRV_NONINV_PROCEEDING:
if (sip_trans->sip_xaction_last_msg != NULL) {
SIP_MSG_REFCNT_DECR(
sip_trans->sip_xaction_last_msg);
sip_trans->sip_xaction_last_msg = NULL;
}
SIP_MSG_REFCNT_INCR(msg);
sip_trans->sip_xaction_last_msg = msg;
(void) sip_add_conn_obj_cache(conn_obj,
(void *)sip_trans);
if (SIP_PROVISIONAL_RESP(resp_code)) {
break;
} else if (SIP_FINAL_RESP(resp_code)) {
/*
* For unreliable transports, start Timer J
*/
if (!isreliable) {
timer_obj_J = sip_setup_timer(
conn_obj, sip_trans,
NULL, sip_trans->sip_xaction_TJ,
SIP_XACTION_TIMER_J);
if (timer_obj_J == NULL) {
(void) pthread_mutex_unlock(&
sip_trans->
sip_xaction_mutex);
return (ENOMEM);
}
SIP_SCHED_TIMER(
sip_trans->sip_xaction_TJ,
timer_obj_J,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TJ)) {
(void) pthread_mutex_unlock(&
sip_trans->
sip_xaction_mutex);
free(timer_obj_J);
return (ENOMEM);
}
sip_trans->sip_xaction_state =
SIP_SRV_NONINV_COMPLETED;
} else {
sip_trans->sip_xaction_state =
SIP_SRV_NONINV_TERMINATED;
}
}
break;
default:
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (prev_state != sip_trans->sip_xaction_state &&
sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
(sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
}
return (0);
}
/*
* -------------------------- Input Routines ---------------------------
*/
/*
* Process an incoming SIP message Request or Response
*/
int
sip_xaction_input(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t **sip_msg)
{
sip_message_type_t *sip_msg_info;
int ret;
sip_msg_info = (*sip_msg)->sip_msg_req_res;
if (sip_msg_info->is_request)
ret = sip_srv_xaction_input(conn_obj, sip_trans, sip_msg);
else
ret = sip_clnt_xaction_input(conn_obj, sip_trans, sip_msg);
return (ret);
}
/*
* Process a Request from the transport
*/
static int
sip_srv_xaction_input(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t **sip_msg)
{
sip_message_type_t *sip_msg_info;
_sip_msg_t *msg = *sip_msg;
int prev_state;
boolean_t isreliable;
sip_msg_info = msg->sip_msg_req_res;
isreliable = sip_is_conn_reliable(conn_obj);
/*
* Cancel if the original transaction has not yet got a final
* response and send a 487 response.
*/
if (sip_msg_info->sip_req_method == ACK) {
_sip_msg_t *sip_last_resp;
const sip_str_t *resp_to_tag;
const sip_str_t *req_to_tag;
int error;
sip_message_type_t *last_msg_info;
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
if (sip_trans->sip_xaction_last_msg != NULL)
sip_last_resp = sip_trans->sip_xaction_last_msg;
else
sip_last_resp = sip_trans->sip_xaction_orig_msg;
last_msg_info = sip_last_resp->sip_msg_req_res;
if (last_msg_info->is_request) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (0);
}
req_to_tag = sip_get_to_tag((sip_msg_t)msg, &error);
if (req_to_tag == NULL || error != 0) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (0);
}
resp_to_tag = sip_get_to_tag((sip_msg_t)sip_last_resp,
&error);
if (req_to_tag == NULL || error != 0) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (0);
}
if (resp_to_tag->sip_str_len != req_to_tag->sip_str_len ||
strncmp(resp_to_tag->sip_str_ptr, req_to_tag->sip_str_ptr,
req_to_tag->sip_str_len) != 0) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (0);
}
prev_state = sip_trans->sip_xaction_state;
if (sip_trans->sip_xaction_state == SIP_SRV_INV_COMPLETED) {
sip_xaction_time_obj_t *timer_obj_I = NULL;
SIP_CANCEL_TIMER(sip_trans->sip_xaction_TG);
/*
* Cancel Timer H and goto TERMINATED state for
* reliable transports.
*/
if (isreliable) {
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TH);
sip_trans->sip_xaction_state =
SIP_SRV_INV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans,
(sip_msg_t)msg, prev_state,
sip_trans->sip_xaction_state);
}
return (0);
}
/*
* For unreliable transports, start TIMER I and
* transition to CONFIRMED state.
*/
timer_obj_I = sip_setup_timer(conn_obj, sip_trans,
NULL,
sip_trans->sip_xaction_TI, SIP_XACTION_TIMER_I);
if (timer_obj_I == NULL) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (ENOMEM);
}
SIP_SCHED_TIMER(sip_trans->sip_xaction_TI,
timer_obj_I, sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TI)) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
free(timer_obj_I);
return (ENOMEM);
}
sip_trans->sip_xaction_state = SIP_SRV_CONFIRMED;
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (prev_state != sip_trans->sip_xaction_state &&
sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
(sip_msg_t)msg, prev_state,
sip_trans->sip_xaction_state);
}
return (0);
} else if (sip_msg_info->sip_req_method == CANCEL) {
if (sip_trans->sip_xaction_method == INVITE) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (0);
}
}
if (sip_msg_info->sip_req_method == INVITE) {
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
assert(sip_trans->sip_xaction_method == INVITE);
/*
* Retransmitted invite
*/
switch (sip_trans->sip_xaction_state) {
case SIP_SRV_INV_PROCEEDING:
case SIP_SRV_INV_COMPLETED:
if (sip_trans->sip_xaction_last_msg != NULL) {
_sip_msg_t *new_msg;
new_msg =
sip_trans->sip_xaction_last_msg;
(void) sip_stack_send(conn_obj,
new_msg->sip_msg_buf,
new_msg->sip_msg_len);
}
break;
default:
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
sip_free_msg((sip_msg_t)msg);
*sip_msg = NULL;
return (0);
}
/*
* Retransmitted request
*/
assert(sip_trans->sip_xaction_method != INVITE);
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
switch (sip_trans->sip_xaction_state) {
case SIP_SRV_NONINV_PROCEEDING:
case SIP_SRV_NONINV_COMPLETED:
if (sip_trans->sip_xaction_last_msg != NULL) {
_sip_msg_t *new_msg;
new_msg = sip_trans->sip_xaction_last_msg;
(void) sip_stack_send(conn_obj,
new_msg->sip_msg_buf, new_msg->sip_msg_len);
}
break;
default:
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
sip_free_msg((sip_msg_t)msg);
*sip_msg = NULL;
return (0);
}
/*
* Process a Response
*/
static int
sip_clnt_xaction_input(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t **msg)
{
int ret;
if (sip_trans->sip_xaction_method == INVITE)
ret = sip_clnt_xaction_inv_res(conn_obj, sip_trans, msg);
else
ret = sip_clnt_xaction_noninv_res(conn_obj, sip_trans, msg);
return (ret);
}
static int
sip_create_send_nonOKack(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t *msg, boolean_t copy)
{
_sip_msg_t *ack_msg;
int ret = 0;
ack_msg = (_sip_msg_t *)sip_new_msg();
if (ack_msg == NULL)
return (ENOMEM);
if ((ret = sip_create_nonOKack(
(sip_msg_t)sip_trans->sip_xaction_orig_msg, (sip_msg_t)msg,
(sip_msg_t)ack_msg)) != 0) {
sip_free_msg((sip_msg_t)ack_msg);
return (ret);
}
if ((ret = sip_stack_send(conn_obj, ack_msg->sip_msg_buf,
ack_msg->sip_msg_len)) != 0) {
sip_free_msg((sip_msg_t)ack_msg);
return (ret);
}
if (copy) {
SIP_MSG_REFCNT_INCR(ack_msg);
if (sip_trans->sip_xaction_last_msg != NULL) {
SIP_MSG_REFCNT_DECR(sip_trans->sip_xaction_last_msg);
sip_trans->sip_xaction_last_msg = NULL;
}
sip_trans->sip_xaction_last_msg = ack_msg;
}
sip_free_msg((sip_msg_t)ack_msg);
return (0);
}
/*
* Process a INVITE Response
*/
static int
sip_clnt_xaction_inv_res(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
_sip_msg_t **sip_msg)
{
int resp_code;
_sip_msg_t *msg = *sip_msg;
sip_xaction_time_obj_t *timer_obj_D = NULL;
sip_message_type_t *sip_msg_info;
int prev_state;
boolean_t isreliable;
assert(msg->sip_msg_req_res != NULL);
sip_msg_info = msg->sip_msg_req_res;
resp_code = sip_msg_info->sip_resp_code;
isreliable = sip_is_conn_reliable(conn_obj);
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
prev_state = sip_trans->sip_xaction_state;
switch (sip_trans->sip_xaction_state) {
case SIP_CLNT_CALLING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
/*
* sip_trans->sip_xaction_last_msg ?
*/
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TA);
sip_trans->sip_xaction_state =
SIP_CLNT_INV_PROCEEDING;
} else if (SIP_OK_RESP(resp_code)) {
/*
* sip_trans->sip_xaction_last_msg ?
*/
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TA);
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TB);
sip_trans->sip_xaction_state =
SIP_CLNT_INV_TERMINATED;
} else if (SIP_NONOK_FINAL_RESP(resp_code)) {
int ret;
/*
* sip_trans->sip_xaction_last_msg ?
*/
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TA);
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TB);
if ((ret = sip_create_send_nonOKack(conn_obj,
sip_trans, msg, B_FALSE)) != 0) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (ret);
}
/*
* start timer D for unreliable transports
*/
if (!isreliable) {
timer_obj_D = sip_setup_timer(
conn_obj, sip_trans,
NULL, sip_trans->sip_xaction_TD,
SIP_XACTION_TIMER_D);
if (timer_obj_D == NULL) {
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
return (ENOMEM);
}
SIP_SCHED_TIMER(
sip_trans->sip_xaction_TD,
timer_obj_D,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TD)) {
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
free(timer_obj_D);
return (ENOMEM);
}
sip_trans->sip_xaction_state =
SIP_CLNT_INV_COMPLETED;
} else {
sip_trans->sip_xaction_state =
SIP_CLNT_INV_TERMINATED;
}
} else {
/*
* Invalid resp_code
*/
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
break;
case SIP_CLNT_INV_PROCEEDING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
break;
} else if (SIP_OK_RESP(resp_code)) {
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TB);
sip_trans->sip_xaction_state =
SIP_CLNT_INV_TERMINATED;
} else if (SIP_NONOK_FINAL_RESP(resp_code)) {
int ret;
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TB);
if ((ret = sip_create_send_nonOKack(conn_obj,
sip_trans, msg, B_FALSE)) != 0) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (ret);
}
/*
* start timer D for unreliable transports
*/
if (!isreliable) {
timer_obj_D = sip_setup_timer(
conn_obj, sip_trans,
NULL, sip_trans->sip_xaction_TD,
SIP_XACTION_TIMER_D);
if (timer_obj_D == NULL) {
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
return (ENOMEM);
}
SIP_SCHED_TIMER(
sip_trans->sip_xaction_TD,
timer_obj_D,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TD)) {
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
free(timer_obj_D);
return (ENOMEM);
}
sip_trans->sip_xaction_state =
SIP_CLNT_INV_COMPLETED;
} else {
sip_trans->sip_xaction_state =
SIP_CLNT_INV_TERMINATED;
}
} else {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
break;
case SIP_CLNT_INV_COMPLETED:
/*
* Transport error takes it to
* SIP_CLNT_INV_TERMINATED
*/
if (SIP_NONOK_FINAL_RESP(resp_code)) {
int ret;
if ((ret = sip_create_send_nonOKack(conn_obj,
sip_trans, msg, B_FALSE)) != 0) {
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (ret);
}
} else {
/*
* Invalid resp_code
*/
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
break;
default:
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (prev_state != sip_trans->sip_xaction_state &&
sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
(sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
}
return (0);
}
/*
* Process a NON-INVITE Response
*/
static int
sip_clnt_xaction_noninv_res(sip_conn_object_t conn_obj,
sip_xaction_t *sip_trans, _sip_msg_t **sip_msg)
{
int resp_code;
sip_xaction_time_obj_t *timer_obj_K = NULL;
sip_message_type_t *sip_msg_info;
int prev_state;
_sip_msg_t *msg = *sip_msg;
boolean_t isreliable;
assert(msg->sip_msg_req_res != NULL);
assert(sip_trans != NULL);
sip_msg_info = msg->sip_msg_req_res;
isreliable = sip_is_conn_reliable(conn_obj);
resp_code = sip_msg_info->sip_resp_code;
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
prev_state = sip_trans->sip_xaction_state;
switch (sip_trans->sip_xaction_state) {
case SIP_CLNT_TRYING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_PROCEEDING;
} else if (SIP_FINAL_RESP(resp_code)) {
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TE);
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TF);
/*
* Start timer K for unreliable transports
*/
if (!isreliable) {
timer_obj_K = sip_setup_timer(
conn_obj, sip_trans,
NULL, sip_trans->sip_xaction_TK,
SIP_XACTION_TIMER_K);
if (timer_obj_K == NULL) {
(void) pthread_mutex_unlock(&
sip_trans->
sip_xaction_mutex);
return (ENOMEM);
}
SIP_SCHED_TIMER(
sip_trans->sip_xaction_TK,
timer_obj_K,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TK)) {
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
free(timer_obj_K);
return (ENOMEM);
}
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_COMPLETED;
} else {
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_TERMINATED;
}
}
break;
case SIP_CLNT_NONINV_PROCEEDING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
break;
} else if (SIP_FINAL_RESP(resp_code)) {
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TE);
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TF);
/*
* Start timer K for unreliable transports
*/
if (!isreliable) {
timer_obj_K = sip_setup_timer(
conn_obj, sip_trans,
NULL, sip_trans->sip_xaction_TK,
SIP_XACTION_TIMER_K);
if (timer_obj_K == NULL) {
(void) pthread_mutex_unlock(&
sip_trans->
sip_xaction_mutex);
return (ENOMEM);
}
SIP_SCHED_TIMER(
sip_trans->sip_xaction_TK,
timer_obj_K,
sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TK)) {
(void) pthread_mutex_unlock(
&sip_trans->
sip_xaction_mutex);
free(timer_obj_K);
return (ENOMEM);
}
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_COMPLETED;
} else {
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_TERMINATED;
}
}
break;
default:
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return (EPROTO);
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (prev_state != sip_trans->sip_xaction_state &&
sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
(sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
}
return (0);
}
/*
* If there is a transport error, sending the message out, terminate the
* transaction.
*/
/* ARGSUSED */
void
sip_xaction_terminate(sip_xaction_t *sip_trans, _sip_msg_t *msg, int transport)
{
sip_message_type_t *sip_msg_info;
int state;
int prev_state;
sip_msg_info = msg->sip_msg_req_res;
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
if (sip_msg_info->is_request) {
if (sip_trans->sip_xaction_method == INVITE)
state = SIP_CLNT_INV_TERMINATED;
else
state = SIP_CLNT_NONINV_TERMINATED;
} else {
if (sip_trans->sip_xaction_method == INVITE)
state = SIP_SRV_INV_TERMINATED;
else
state = SIP_SRV_NONINV_TERMINATED;
}
prev_state = sip_trans->sip_xaction_state;
sip_trans->sip_xaction_state = state;
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
(sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
}
sip_xaction_delete(sip_trans);
}
/*
* --------------------------- Timer Routine ---------------------------
*/
void
sip_xaction_state_timer_fire(void *args)
{
sip_xaction_time_obj_t *time_obj = (sip_xaction_time_obj_t *)args;
sip_xaction_t *sip_trans = time_obj->sip_trans;
_sip_msg_t *new_msg;
boolean_t destroy_trans = B_FALSE;
sip_conn_object_t conn_obj;
int prev_state;
assert(time_obj != NULL);
(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
prev_state = sip_trans->sip_xaction_state;
switch (time_obj->sip_xaction_timer_type) {
case SIP_XACTION_TIMER_A:
if (sip_trans->sip_xaction_state != SIP_CLNT_CALLING)
break;
/*
* Assert candidate
*/
if (sip_trans->sip_xaction_last_msg == NULL)
break;
if (sip_trans->sip_xaction_conn_obj == NULL)
break;
new_msg = sip_trans->sip_xaction_last_msg;
conn_obj = sip_trans->sip_xaction_conn_obj;
if (sip_stack_send(conn_obj, new_msg->sip_msg_buf,
new_msg->sip_msg_len) != 0) {
sip_del_conn_obj_cache(
sip_trans->sip_xaction_conn_obj,
(void *)sip_trans);
sip_trans->sip_xaction_state =
SIP_CLNT_INV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
SIP_SET_TIMEOUT(sip_trans->sip_xaction_TA,
2 * SIP_GET_TIMEOUT(sip_trans->sip_xaction_TA));
/*
* Reschedule the timer
*/
SIP_SCHED_TIMER(sip_trans->sip_xaction_TA,
time_obj, sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TA)) {
sip_del_conn_obj_cache(
sip_trans->sip_xaction_conn_obj,
(void *)sip_trans);
sip_trans->sip_xaction_state =
SIP_CLNT_INV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return;
case SIP_XACTION_TIMER_B:
SIP_CANCEL_TIMER(sip_trans->sip_xaction_TA);
if (sip_trans->sip_xaction_state == SIP_CLNT_CALLING) {
sip_trans->sip_xaction_state =
SIP_CLNT_INV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
break;
case SIP_XACTION_TIMER_D:
if (sip_trans->sip_xaction_state ==
SIP_CLNT_INV_COMPLETED) {
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TB);
sip_trans->sip_xaction_state =
SIP_CLNT_INV_TERMINATED;
destroy_trans = B_TRUE;
}
break;
case SIP_XACTION_TIMER_E:
/*
* Assert candidate
*/
if (sip_trans->sip_xaction_state != SIP_CLNT_TRYING &&
sip_trans->sip_xaction_state !=
SIP_CLNT_NONINV_PROCEEDING) {
break;
}
/*
* Assert candidate
*/
if (sip_trans->sip_xaction_last_msg == NULL)
break;
if (sip_trans->sip_xaction_conn_obj == NULL)
break;
conn_obj = sip_trans->sip_xaction_conn_obj;
new_msg = sip_trans->sip_xaction_last_msg;
if (sip_stack_send(conn_obj, new_msg->sip_msg_buf,
new_msg->sip_msg_len) != 0) {
sip_del_conn_obj_cache(
sip_trans->sip_xaction_conn_obj,
(void *)sip_trans);
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
SIP_SET_TIMEOUT(sip_trans->sip_xaction_TE,
MIN(SIP_TIMER_T2,
2 * SIP_GET_TIMEOUT(sip_trans->sip_xaction_TE)));
/*
* Reschedule the timer
*/
SIP_SCHED_TIMER(sip_trans->sip_xaction_TE,
time_obj, sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TE)) {
sip_del_conn_obj_cache(
sip_trans->sip_xaction_conn_obj,
(void *)sip_trans);
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return;
case SIP_XACTION_TIMER_F:
SIP_CANCEL_TIMER(sip_trans->sip_xaction_TE);
if (sip_trans->sip_xaction_state == SIP_CLNT_TRYING ||
sip_trans->sip_xaction_state ==
SIP_CLNT_NONINV_PROCEEDING) {
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
break;
case SIP_XACTION_TIMER_G:
/*
* Assert candidate
*/
if (sip_trans->sip_xaction_last_msg == NULL)
break;
if (sip_trans->sip_xaction_conn_obj == NULL)
break;
if (sip_trans->sip_xaction_state !=
SIP_SRV_INV_COMPLETED) {
break;
}
new_msg = sip_trans->sip_xaction_last_msg;
conn_obj = sip_trans->sip_xaction_conn_obj;
if (sip_stack_send(conn_obj, new_msg->sip_msg_buf,
new_msg->sip_msg_len) != 0) {
sip_del_conn_obj_cache(
sip_trans->sip_xaction_conn_obj,
(void *)sip_trans);
sip_trans->sip_xaction_state =
SIP_SRV_INV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
SIP_SET_TIMEOUT(sip_trans->sip_xaction_TG,
MIN(SIP_TIMER_T2,
2 * SIP_GET_TIMEOUT(sip_trans->sip_xaction_TG)));
SIP_SCHED_TIMER(sip_trans->sip_xaction_TG,
time_obj, sip_xaction_state_timer_fire);
if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TG)) {
sip_del_conn_obj_cache(
sip_trans->sip_xaction_conn_obj,
(void *)sip_trans);
sip_trans->sip_xaction_state =
SIP_SRV_INV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
return;
case SIP_XACTION_TIMER_H:
SIP_CANCEL_TIMER(sip_trans->sip_xaction_TG);
if (sip_trans->sip_xaction_state ==
SIP_SRV_INV_COMPLETED) {
sip_trans->sip_xaction_state =
SIP_SRV_INV_TERMINATED;
(void) pthread_mutex_unlock(
&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL) {
sip_xaction_ulp_state_cb(
(sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
if (sip_xaction_ulp_trans_err != NULL) {
sip_xaction_ulp_trans_err(sip_trans, 0,
NULL);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
break;
case SIP_XACTION_TIMER_I:
if (sip_trans->sip_xaction_state ==
SIP_SRV_CONFIRMED) {
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TH);
sip_trans->sip_xaction_state =
SIP_SRV_INV_TERMINATED;
destroy_trans = B_TRUE;
}
break;
case SIP_XACTION_TIMER_J:
if (sip_trans->sip_xaction_state ==
SIP_SRV_NONINV_COMPLETED) {
sip_trans->sip_xaction_state =
SIP_SRV_NONINV_TERMINATED;
destroy_trans = B_TRUE;
}
break;
case SIP_XACTION_TIMER_K:
if (sip_trans->sip_xaction_state ==
SIP_CLNT_NONINV_COMPLETED) {
SIP_CANCEL_TIMER(
sip_trans->sip_xaction_TF);
sip_trans->sip_xaction_state =
SIP_CLNT_NONINV_TERMINATED;
destroy_trans = B_TRUE;
}
break;
default:
break;
}
if (destroy_trans) {
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL &&
prev_state != sip_trans->sip_xaction_state) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
NULL, prev_state, sip_trans->sip_xaction_state);
}
sip_xaction_delete(sip_trans);
free(time_obj);
return;
}
(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
if (sip_xaction_ulp_state_cb != NULL &&
prev_state != sip_trans->sip_xaction_state) {
sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans, NULL,
prev_state, sip_trans->sip_xaction_state);
}
free(time_obj);
}