sip_xaction_state_mc.c revision 2c2c41837e330b002c4220a39638150db504fe0e
/*
* 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
* 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 2007 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 <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <sip.h>
#include "sip_miscdefs.h"
#include "sip_msg.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
* 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
* Timer J 64*T1 for UDP Section 17.2.2 Wait time for
* retransmits
* Timer K T4 for UDP Section 17.1.2.2 Wait time for
* ----------------------------------------------------------------------
*/
#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_msg_t *);
_sip_msg_t **);
_sip_msg_t *);
_sip_msg_t **);
_sip_msg_t **);
_sip_msg_t **);
_sip_msg_t *);
_sip_msg_t **);
_sip_msg_t *);
_sip_msg_t *);
_sip_msg_t *, boolean_t);
void sip_xaction_state_timer_fire(void *);
sip_xaction_t *, _sip_msg_t *,
sip_timer_t, int);
/*
* Return a timer object
*/
static 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))
/*
* Save the message
*/
}
}
return (sip_timer_obj);
}
/*
* --------------------------- Output Routines ---------------------------
*/
/*
* Send a SIP message, request or response, out
*/
int
{
int ret;
if (sip_msg_info->is_request)
return (ret);
}
/*
* Send a Request out
*/
static int
{
int prev_state;
int error = 0;
/*
* if transport is not reliable, start TIMER A.
*/
if (!isreliable) {
if (timer_obj_A == NULL) {
goto error_ret;
}
}
if (timer_obj_B == NULL) {
goto error_ret;
}
if (timer_obj_A != NULL) {
goto error_ret;
}
}
if (timer_obj_A != NULL)
goto error_ret;
}
} else {
/*
* if transport is not reliable, start rexmit Timer E.
*/
if (!isreliable) {
if (timer_obj_E == NULL) {
goto error_ret;
}
}
/*
* Start transaction Timer F
*/
if (timer_obj_F == NULL) {
goto error_ret;
}
if (timer_obj_E != NULL) {
goto error_ret;
}
}
if (timer_obj_E != NULL)
goto error_ret;
}
}
if (sip_xaction_ulp_state_cb != NULL) {
}
return (0);
if (timer_obj_A != NULL)
if (timer_obj_B != NULL)
if (timer_obj_E != NULL)
if (timer_obj_F != NULL)
return (error);
}
/*
* Send a response out
*/
static int
{
int ret;
else
return (ret);
}
/*
* Send a INVITE response out
*/
static int
{
int resp_code;
int prev_state;
switch (sip_trans->sip_xaction_state) {
case SIP_SRV_INV_PROCEEDING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
}
(void) sip_add_conn_obj_cache(conn_obj,
(void *)sip_trans);
} else if (SIP_OK_RESP(resp_code)) {
} else if (SIP_NONOK_FINAL_RESP(resp_code)) {
}
(void) sip_add_conn_obj_cache(conn_obj,
(void *)sip_trans);
/*
* For unreliable transport start timer G
*/
if (!isreliable) {
if (timer_obj_G == NULL) {
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
}
/*
* Start Timer H
*/
if (timer_obj_H == NULL) {
if (timer_obj_G != NULL)
(void) pthread_mutex_unlock(
return (ENOMEM);
}
if (timer_obj_G != NULL) {
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TG)) {
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
}
if (timer_obj_H != NULL) {
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TH)) {
if (timer_obj_G != NULL) {
}
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
}
}
break;
default:
(void) pthread_mutex_unlock(
return (EPROTO);
}
sip_xaction_ulp_state_cb != NULL) {
}
return (0);
}
/*
* Send a NON-INVITE response out
*/
static int
{
int resp_code;
int prev_state;
switch (sip_trans->sip_xaction_state) {
case SIP_SRV_TRYING:
}
(void) sip_add_conn_obj_cache(conn_obj,
(void *)sip_trans);
if (SIP_PROVISIONAL_RESP(resp_code)) {
} else if (SIP_FINAL_RESP(resp_code)) {
/*
* For unreliable transports, start Timer J
*/
if (!isreliable) {
if (timer_obj_J == NULL) {
(void) pthread_mutex_unlock(&
return (ENOMEM);
}
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TJ)) {
(void) pthread_mutex_unlock(&
return (ENOMEM);
}
} else {
}
}
break;
}
(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) {
if (timer_obj_J == NULL) {
(void) pthread_mutex_unlock(&
return (ENOMEM);
}
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TJ)) {
(void) pthread_mutex_unlock(&
return (ENOMEM);
}
} else {
}
}
break;
default:
(void) pthread_mutex_unlock(
return (EPROTO);
}
sip_xaction_ulp_state_cb != NULL) {
}
return (0);
}
/*
* -------------------------- Input Routines ---------------------------
*/
/*
* Process an incoming SIP message Request or Response
*/
int
{
int ret;
if (sip_msg_info->is_request)
else
return (ret);
}
/*
* Process a Request from the transport
*/
static int
{
int prev_state;
/*
* Cancel if the original transaction has not yet got a final
* response and send a 487 response.
*/
const sip_str_t *resp_to_tag;
const sip_str_t *req_to_tag;
int error;
else
if (last_msg_info->is_request) {
(void) pthread_mutex_unlock(
return (0);
}
(void) pthread_mutex_unlock(
return (0);
}
&error);
(void) pthread_mutex_unlock(
return (0);
}
req_to_tag->sip_str_len) != 0) {
(void) pthread_mutex_unlock(
return (0);
}
/*
* Cancel Timer H and goto TERMINATED state for
* reliable transports.
*/
if (isreliable) {
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
return (0);
}
/*
* For unreliable transports, start TIMER I and
* transition to CONFIRMED state.
*/
NULL,
if (timer_obj_I == NULL) {
(void) pthread_mutex_unlock(
return (ENOMEM);
}
(void) pthread_mutex_unlock(
return (ENOMEM);
}
}
sip_xaction_ulp_state_cb != NULL) {
}
return (0);
(void) pthread_mutex_unlock(
return (0);
}
}
/*
* Retransmitted invite
*/
switch (sip_trans->sip_xaction_state) {
case SIP_SRV_INV_PROCEEDING:
case SIP_SRV_INV_COMPLETED:
new_msg =
(void) sip_stack_send(conn_obj,
}
break;
default:
(void) pthread_mutex_unlock(
return (EPROTO);
}
return (0);
}
/*
* Retransmitted request
*/
switch (sip_trans->sip_xaction_state) {
case SIP_SRV_NONINV_COMPLETED:
(void) sip_stack_send(conn_obj,
}
break;
default:
(void) pthread_mutex_unlock(
return (EPROTO);
}
return (0);
}
/*
* Process a Response
*/
static int
_sip_msg_t **msg)
{
int ret;
else
return (ret);
}
static int
{
int ret = 0;
return (ENOMEM);
if ((ret = sip_create_nonOKack(
return (ret);
}
ack_msg->sip_msg_len)) != 0) {
return (ret);
}
if (copy) {
}
}
return (0);
}
/*
* Process a INVITE Response
*/
static int
{
int resp_code;
int prev_state;
switch (sip_trans->sip_xaction_state) {
case SIP_CLNT_CALLING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
/*
* sip_trans->sip_xaction_last_msg ?
*/
} else if (SIP_OK_RESP(resp_code)) {
/*
* sip_trans->sip_xaction_last_msg ?
*/
} else if (SIP_NONOK_FINAL_RESP(resp_code)) {
int ret;
/*
* sip_trans->sip_xaction_last_msg ?
*/
(void) pthread_mutex_unlock(
return (ret);
}
/*
* start timer D for unreliable transports
*/
if (!isreliable) {
if (timer_obj_D == NULL) {
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TD)) {
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
} else {
}
} else {
/*
* Invalid resp_code
*/
(void) pthread_mutex_unlock(
return (EPROTO);
}
break;
case SIP_CLNT_INV_PROCEEDING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
break;
} else if (SIP_OK_RESP(resp_code)) {
} else if (SIP_NONOK_FINAL_RESP(resp_code)) {
int ret;
(void) pthread_mutex_unlock(
return (ret);
}
/*
* start timer D for unreliable transports
*/
if (!isreliable) {
if (timer_obj_D == NULL) {
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TD)) {
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
} else {
}
} else {
(void) pthread_mutex_unlock(
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;
(void) pthread_mutex_unlock(
return (ret);
}
} else {
/*
* Invalid resp_code
*/
(void) pthread_mutex_unlock(
return (EPROTO);
}
break;
default:
(void) pthread_mutex_unlock(
return (EPROTO);
}
sip_xaction_ulp_state_cb != NULL) {
}
return (0);
}
/*
* Process a NON-INVITE Response
*/
static int
{
int resp_code;
int prev_state;
switch (sip_trans->sip_xaction_state) {
case SIP_CLNT_TRYING:
if (SIP_PROVISIONAL_RESP(resp_code)) {
} else if (SIP_FINAL_RESP(resp_code)) {
/*
* Start timer K for unreliable transports
*/
if (!isreliable) {
if (timer_obj_K == NULL) {
(void) pthread_mutex_unlock(&
return (ENOMEM);
}
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TK)) {
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
} else {
}
}
break;
if (SIP_PROVISIONAL_RESP(resp_code)) {
break;
} else if (SIP_FINAL_RESP(resp_code)) {
/*
* Start timer K for unreliable transports
*/
if (!isreliable) {
if (timer_obj_K == NULL) {
(void) pthread_mutex_unlock(&
return (ENOMEM);
}
if (!SIP_IS_TIMER_RUNNING(
sip_trans->sip_xaction_TK)) {
(void) pthread_mutex_unlock(
&sip_trans->
return (ENOMEM);
}
} else {
}
}
break;
default:
(void) pthread_mutex_unlock(
return (EPROTO);
}
sip_xaction_ulp_state_cb != NULL) {
}
return (0);
}
/*
* If there is a transport error, sending the message out, terminate the
* transaction.
*/
/* ARGSUSED */
void
{
int state;
int prev_state;
if (sip_msg_info->is_request) {
else
} else {
else
}
if (sip_xaction_ulp_state_cb != NULL) {
}
}
/*
* --------------------------- Timer Routine ---------------------------
*/
void
sip_xaction_state_timer_fire(void *args)
{
int prev_state;
switch (time_obj->sip_xaction_timer_type) {
case SIP_XACTION_TIMER_A:
break;
/*
* Assert candidate
*/
break;
break;
new_msg->sip_msg_len) != 0) {
(void *)sip_trans);
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
/*
* Reschedule the timer
*/
(void *)sip_trans);
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
(void) pthread_mutex_unlock(
return;
case SIP_XACTION_TIMER_B:
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
break;
case SIP_XACTION_TIMER_D:
if (sip_trans->sip_xaction_state ==
}
break;
case SIP_XACTION_TIMER_E:
/*
* Assert candidate
*/
break;
}
/*
* Assert candidate
*/
break;
break;
new_msg->sip_msg_len) != 0) {
(void *)sip_trans);
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
/*
* Reschedule the timer
*/
(void *)sip_trans);
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
(void) pthread_mutex_unlock(
return;
case SIP_XACTION_TIMER_F:
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
break;
case SIP_XACTION_TIMER_G:
/*
* Assert candidate
*/
break;
break;
if (sip_trans->sip_xaction_state !=
break;
}
new_msg->sip_msg_len) != 0) {
(void *)sip_trans);
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
(void *)sip_trans);
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
(void) pthread_mutex_unlock(
return;
case SIP_XACTION_TIMER_H:
if (sip_trans->sip_xaction_state ==
(void) pthread_mutex_unlock(
if (sip_xaction_ulp_state_cb != NULL) {
}
if (sip_xaction_ulp_trans_err != NULL) {
NULL);
}
return;
}
break;
case SIP_XACTION_TIMER_I:
if (sip_trans->sip_xaction_state ==
}
break;
case SIP_XACTION_TIMER_J:
if (sip_trans->sip_xaction_state ==
}
break;
case SIP_XACTION_TIMER_K:
if (sip_trans->sip_xaction_state ==
}
break;
default:
break;
}
if (destroy_trans) {
if (sip_xaction_ulp_state_cb != NULL &&
}
return;
}
if (sip_xaction_ulp_state_cb != NULL &&
}
}