⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_transaction.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
/* $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 + -