📄 sip_transaction.c
字号:
/* $Id: sip_transaction.c 1251 2007-05-04 10:52:41Z 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 t2_timer_val = { PJSIP_T2_TIMEOUT/1000, PJSIP_T2_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; /* Create hash table. */ mod_tsx_layer.htable = pj_hash_create( pool, PJSIP_MAX_TSX_COUNT ); if (!mod_tsx_layer.htable) { pjsip_endpt_release_pool(endpt, pool); return PJ_ENOMEM; } /* Create mutex. */ status = pj_mutex_create_recursive(pool, "tsxlayer", &mod_tsx_layer.mutex); if (status != PJ_SUCCESS) { pjsip_endpt_release_pool(endpt, pool); return status; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -