📄 sip_transaction.c
字号:
/* $Id: sip_transaction.c 1077 2007-03-17 22:21:58Z bennylp $ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjsip/sip_transaction.h>
#include <pjsip/sip_util.h>
#include <pjsip/sip_module.h>
#include <pjsip/sip_endpoint.h>
#include <pjsip/sip_errno.h>
#include <pjsip/sip_event.h>
#include <pj/hash.h>
#include <pj/pool.h>
#include <pj/os.h>
#include <pj/string.h>
#include <pj/assert.h>
#include <pj/guid.h>
#include <pj/log.h>
#define THIS_FILE "sip_transaction.c"
#if 0
#define TSX_TRACE_(expr) PJ_LOG(3,expr)
#else
#define TSX_TRACE_(expr)
#endif
/* When this macro is set, transaction will keep the hashed value
* so that future lookup (to unregister transaction) does not need
* to recalculate the hash again. It should gains a little bit of
* performance, so generally we'd want this.
*/
#define PRECALC_HASH
/* Defined in sip_util_statefull.c */
extern pjsip_module mod_stateful_util;
/*****************************************************************************
**
** Declarations and static variable definitions section.
**
*****************************************************************************
**/
/* Prototypes. */
static pj_status_t mod_tsx_layer_load(pjsip_endpoint *endpt);
static pj_status_t mod_tsx_layer_start(void);
static pj_status_t mod_tsx_layer_stop(void);
static pj_status_t mod_tsx_layer_unload(void);
static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata);
static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata);
/* Transaction layer module definition. */
static struct mod_tsx_layer
{
struct pjsip_module mod;
pj_pool_t *pool;
pjsip_endpoint *endpt;
pj_mutex_t *mutex;
pj_hash_table_t *htable;
} mod_tsx_layer =
{ {
NULL, NULL, /* List's prev and next. */
{ "mod-tsx-layer", 13 }, /* Module name. */
-1, /* Module ID */
PJSIP_MOD_PRIORITY_TSX_LAYER, /* Priority. */
mod_tsx_layer_load, /* load(). */
mod_tsx_layer_start, /* start() */
mod_tsx_layer_stop, /* stop() */
mod_tsx_layer_unload, /* unload() */
mod_tsx_layer_on_rx_request, /* on_rx_request() */
mod_tsx_layer_on_rx_response, /* on_rx_response() */
NULL
}
};
/* Thread Local Storage ID for transaction lock */
static long pjsip_tsx_lock_tls_id;
/* Transaction state names */
static const char *state_str[] =
{
"Null",
"Calling",
"Trying",
"Proceeding",
"Completed",
"Confirmed",
"Terminated",
"Destroyed",
};
/* Role names */
static const char *role_name[] =
{
"UAC",
"UAS"
};
/* Transport flag. */
enum
{
TSX_HAS_PENDING_TRANSPORT = 1,
TSX_HAS_PENDING_RESCHED = 2,
TSX_HAS_PENDING_SEND = 4,
TSX_HAS_PENDING_DESTROY = 8,
TSX_HAS_RESOLVED_SERVER = 16,
};
/* Transaction lock. */
typedef struct tsx_lock_data {
struct tsx_lock_data *prev;
pjsip_transaction *tsx;
int is_alive;
} tsx_lock_data;
/* Timer timeout value constants */
static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000,
PJSIP_T1_TIMEOUT%1000 };
static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000,
PJSIP_T4_TIMEOUT%1000 };
static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000,
PJSIP_TD_TIMEOUT%1000 };
static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000,
(64*PJSIP_T1_TIMEOUT)%1000 };
/* Internal timer IDs */
enum Transaction_Timer_Id
{
TSX_TIMER_RETRANSMISSION,
TSX_TIMER_TIMEOUT,
};
/* Prototypes. */
static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck);
static pj_status_t unlock_tsx( pjsip_transaction *tsx,
struct tsx_lock_data *lck);
static pj_status_t tsx_on_state_null( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_calling( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_trying( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_proceeding_uac( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_completed_uas( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_completed_uac( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_confirmed( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_terminated( pjsip_transaction *tsx,
pjsip_event *event);
static pj_status_t tsx_on_state_destroyed( pjsip_transaction *tsx,
pjsip_event *event);
static void tsx_timer_callback( pj_timer_heap_t *theap,
pj_timer_entry *entry);
static pj_status_t tsx_create( pjsip_module *tsx_user,
pjsip_transaction **p_tsx);
static pj_status_t tsx_destroy( pjsip_transaction *tsx );
static void tsx_resched_retransmission( pjsip_transaction *tsx );
static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched);
static int tsx_send_msg( pjsip_transaction *tsx,
pjsip_tx_data *tdata);
/* State handlers for UAC, indexed by state */
static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
pjsip_event *) =
{
&tsx_on_state_null,
&tsx_on_state_calling,
NULL,
&tsx_on_state_proceeding_uac,
&tsx_on_state_completed_uac,
&tsx_on_state_confirmed,
&tsx_on_state_terminated,
&tsx_on_state_destroyed,
};
/* State handlers for UAS */
static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
pjsip_event *) =
{
&tsx_on_state_null,
NULL,
&tsx_on_state_trying,
&tsx_on_state_proceeding_uas,
&tsx_on_state_completed_uas,
&tsx_on_state_confirmed,
&tsx_on_state_terminated,
&tsx_on_state_destroyed,
};
/*****************************************************************************
**
** Utilities
**
*****************************************************************************
*/
/*
* Get transaction state name.
*/
PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state)
{
return state_str[state];
}
/*
* Get the role name.
*/
PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role)
{
return role_name[role];
}
/*
* Create transaction key for RFC2543 compliant messages, which don't have
* unique branch parameter in the top most Via header.
*
* INVITE requests matches a transaction if the following attributes
* match the original request:
* - Request-URI
* - To tag
* - From tag
* - Call-ID
* - CSeq
* - top Via header
*
* CANCEL matching is done similarly as INVITE, except:
* - CSeq method will differ
* - To tag is not matched.
*
* ACK matching is done similarly, except that:
* - method of the CSeq will differ,
* - To tag is matched to the response sent by the server transaction.
*
* The transaction key is constructed from the common components of above
* components. Additional comparison is needed to fully match a transaction.
*/
static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
pj_str_t *str,
pjsip_role_e role,
const pjsip_method *method,
const pjsip_rx_data *rdata )
{
#define SEPARATOR '$'
char *key, *p, *end;
int len;
pj_size_t len_required;
pjsip_uri *req_uri;
pj_str_t *host;
PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL);
PJ_ASSERT_RETURN(rdata->msg_info.msg, PJ_EINVAL);
PJ_ASSERT_RETURN(rdata->msg_info.via, PJSIP_EMISSINGHDR);
PJ_ASSERT_RETURN(rdata->msg_info.cseq, PJSIP_EMISSINGHDR);
PJ_ASSERT_RETURN(rdata->msg_info.from, PJSIP_EMISSINGHDR);
host = &rdata->msg_info.via->sent_by.host;
req_uri = (pjsip_uri*)rdata->msg_info.msg->line.req.uri;
/* Calculate length required. */
len_required = 9 + /* CSeq number */
rdata->msg_info.from->tag.slen + /* From tag. */
rdata->msg_info.cid->id.slen + /* Call-ID */
host->slen + /* Via host. */
9 + /* Via port. */
16; /* Separator+Allowance. */
key = p = pj_pool_alloc(pool, len_required);
end = p + len_required;
/* Add role. */
*p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
*p++ = SEPARATOR;
/* Add method, except when method is INVITE or ACK. */
if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
pj_memcpy(p, method->name.ptr, method->name.slen);
p += method->name.slen;
*p++ = '$';
}
/* Add CSeq (only the number). */
len = pj_utoa(rdata->msg_info.cseq->cseq, p);
p += len;
*p++ = SEPARATOR;
/* Add From tag. */
len = rdata->msg_info.from->tag.slen;
pj_memcpy( p, rdata->msg_info.from->tag.ptr, len);
p += len;
*p++ = SEPARATOR;
/* Add Call-ID. */
len = rdata->msg_info.cid->id.slen;
pj_memcpy( p, rdata->msg_info.cid->id.ptr, len );
p += len;
*p++ = SEPARATOR;
/* Add top Via header.
* We don't really care whether the port contains the real port (because
* it can be omited if default port is used). Anyway this function is
* only used to match request retransmission, and we expect that the
* request retransmissions will contain the same port.
*/
pj_memcpy(p, host->ptr, host->slen);
p += host->slen;
*p++ = ':';
len = pj_utoa(rdata->msg_info.via->sent_by.port, p);
p += len;
*p++ = SEPARATOR;
*p++ = '\0';
/* Done. */
str->ptr = key;
str->slen = p-key;
return PJ_SUCCESS;
}
/*
* Create transaction key for RFC3161 compliant system.
*/
static pj_status_t create_tsx_key_3261( pj_pool_t *pool,
pj_str_t *key,
pjsip_role_e role,
const pjsip_method *method,
const pj_str_t *branch)
{
char *p;
PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL);
p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 );
/* Add role. */
*p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
*p++ = SEPARATOR;
/* Add method, except when method is INVITE or ACK. */
if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
pj_memcpy(p, method->name.ptr, method->name.slen);
p += method->name.slen;
*p++ = '$';
}
/* Add branch ID. */
pj_memcpy(p, branch->ptr, branch->slen);
p += branch->slen;
/* Set length */
key->slen = p - key->ptr;
return PJ_SUCCESS;
}
/*
* Create key from the incoming data, to be used to search the transaction
* in the transaction hash table.
*/
PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key,
pjsip_role_e role,
const pjsip_method *method,
const pjsip_rx_data *rdata)
{
pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID,
PJSIP_RFC3261_BRANCH_LEN};
/* Get the branch parameter in the top-most Via.
* If branch parameter is started with "z9hG4bK", then the message was
* generated by agent compliant with RFC3261. Otherwise, it will be
* handled as RFC2543.
*/
const pj_str_t *branch = &rdata->msg_info.via->branch_param;
if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
/* Create transaction key. */
return create_tsx_key_3261(pool, key, role, method, branch);
} else {
/* Create the key for the message. This key will be matched up
* with the transaction key. For RFC2563 transactions, the
* transaction key was created by the same function, so it will
* match the message.
*/
return create_tsx_key_2543( pool, key, role, method, rdata );
}
}
/*****************************************************************************
**
** Transaction layer module
**
*****************************************************************************
**/
/*
* Create transaction layer module and registers it to the endpoint.
*/
PJ_DEF(pj_status_t) pjsip_tsx_layer_init_module(pjsip_endpoint *endpt)
{
pj_pool_t *pool;
pj_status_t status;
PJ_ASSERT_RETURN(mod_tsx_layer.endpt==NULL, PJ_EINVALIDOP);
/* Initialize TLS ID for transaction lock. */
status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
if (status != PJ_SUCCESS)
return status;
pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
/*
* Initialize transaction layer structure.
*/
/* Create pool for the module. */
pool = pjsip_endpt_create_pool(endpt, "tsxlayer",
PJSIP_POOL_TSX_LAYER_LEN,
PJSIP_POOL_TSX_LAYER_INC );
if (!pool)
return PJ_ENOMEM;
/* Initialize some attributes. */
mod_tsx_layer.pool = pool;
mod_tsx_layer.endpt = endpt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -