📄 sip_endpoint.c
字号:
/* $Id: sip_endpoint.c 1018 2007-02-28 15:38:01Z 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_endpoint.h>
#include <pjsip/sip_transaction.h>
#include <pjsip/sip_private.h>
#include <pjsip/sip_event.h>
#include <pjsip/sip_resolve.h>
#include <pjsip/sip_module.h>
#include <pjsip/sip_util.h>
#include <pjsip/sip_errno.h>
#include <pj/except.h>
#include <pj/log.h>
#include <pj/string.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pj/hash.h>
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/lock.h>
#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION
#define THIS_FILE "sip_endpoint.c"
#define MAX_METHODS 32
/**
* The SIP endpoint.
*/
struct pjsip_endpoint
{
/** Pool to allocate memory for the endpoint. */
pj_pool_t *pool;
/** Mutex for the pool, hash table, and event list/queue. */
pj_mutex_t *mutex;
/** Pool factory. */
pj_pool_factory *pf;
/** Name. */
pj_str_t name;
/** Timer heap. */
pj_timer_heap_t *timer_heap;
/** Transport manager. */
pjsip_tpmgr *transport_mgr;
/** Ioqueue. */
pj_ioqueue_t *ioqueue;
/** Last ioqueue err */
pj_status_t ioq_last_err;
/** DNS Resolver. */
pjsip_resolver_t *resolver;
/** Modules lock. */
pj_rwmutex_t *mod_mutex;
/** Modules. */
pjsip_module *modules[PJSIP_MAX_MODULE];
/** Module list, sorted by priority. */
pjsip_module module_list;
/** Capability header list. */
pjsip_hdr cap_hdr;
/** Additional request headers. */
pjsip_hdr req_hdr;
};
#if defined(PJSIP_SAFE_MODULE) && PJSIP_SAFE_MODULE!=0
# define LOCK_MODULE_ACCESS(ept) pj_rwmutex_lock_read(ept->mod_mutex)
# define UNLOCK_MODULE_ACCESS(ept) pj_rwmutex_unlock_read(ept->mod_mutex)
#else
# define LOCK_MODULE_ACCESS(endpt)
# define UNLOCK_MODULE_ACCESS(endpt)
#endif
/*
* Prototypes.
*/
static void endpt_on_rx_msg( pjsip_endpoint*,
pj_status_t, pjsip_rx_data*);
static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt,
pjsip_tx_data *tdata );
/* Defined in sip_parser.c */
void init_sip_parser(void);
void deinit_sip_parser(void);
/* Defined in sip_tel_uri.c */
pj_status_t pjsip_tel_uri_subsys_init(void);
/* Specifies whether error subsystem has been registered to pjlib. */
static int error_subsys_initialized;
/**
* Defined in sip_errno.c
*
* Get error message for the specified error code. This can only get
* PJSIP specific error message. To get all types of error message,
* use pj_strerror() instead.
*
* @param status The error code.
* @param buffer The buffer where to put the error message.
* @param bufsize Size of the buffer.
*
* @return The error message as NULL terminated string,
* wrapped with pj_str_t.
*/
PJ_DECL(pj_str_t) pjsip_strerror( pj_status_t status, char *buffer,
pj_size_t bufsize);
/*
* This is the global handler for memory allocation failure, for pools that
* are created by the endpoint (by default, all pools ARE allocated by
* endpoint). The error is handled by throwing exception, and hopefully,
* the exception will be handled by the application (or this library).
*/
static void pool_callback( pj_pool_t *pool, pj_size_t size )
{
PJ_UNUSED_ARG(pool);
PJ_UNUSED_ARG(size);
PJ_THROW(PJSIP_EX_NO_MEMORY);
}
/* Compare module name, used for searching module based on name. */
static int cmp_mod_name(void *name, const void *mod)
{
return pj_stricmp(name, &((pjsip_module*)mod)->name);
}
/*
* Register new module to the endpoint.
* The endpoint will then call the load and start function in the module to
* properly initialize the module, and assign a unique module ID for the
* module.
*/
PJ_DEF(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt,
pjsip_module *mod )
{
pj_status_t status = PJ_SUCCESS;
pjsip_module *m;
int i;
pj_rwmutex_lock_write(endpt->mod_mutex);
/* Make sure that this module has not been registered. */
PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == NULL,
{status = PJ_EEXISTS; goto on_return;});
/* Make sure that no module with the same name has been registered. */
PJ_ASSERT_ON_FAIL( pj_list_search(&endpt->module_list, &mod->name,
&cmp_mod_name)==NULL,
{status = PJ_EEXISTS; goto on_return; });
/* Find unused ID for this module. */
for (i=0; i<PJ_ARRAY_SIZE(endpt->modules); ++i) {
if (endpt->modules[i] == NULL)
break;
}
if (i == PJ_ARRAY_SIZE(endpt->modules)) {
pj_assert(!"Too many modules registered!");
status = PJ_ETOOMANY;
goto on_return;
}
/* Assign the ID. */
mod->id = i;
/* Try to load the module. */
if (mod->load) {
status = (*mod->load)(endpt);
if (status != PJ_SUCCESS)
goto on_return;
}
/* Try to start the module. */
if (mod->start) {
status = (*mod->start)();
if (status != PJ_SUCCESS)
goto on_return;
}
/* Save the module. */
endpt->modules[i] = mod;
/* Put in the module list, sorted by priority. */
m = endpt->module_list.next;
while (m != &endpt->module_list) {
if (m->priority > mod->priority)
break;
m = m->next;
}
pj_list_insert_before(m, mod);
/* Done. */
PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" registered",
(int)mod->name.slen, mod->name.ptr));
on_return:
pj_rwmutex_unlock_write(endpt->mod_mutex);
return status;
}
/*
* Unregister a module from the endpoint.
* The endpoint will then call the stop and unload function in the module to
* properly shutdown the module.
*/
PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
pjsip_module *mod )
{
pj_status_t status;
pj_rwmutex_lock_write(endpt->mod_mutex);
/* Make sure the module exists in the list. */
PJ_ASSERT_ON_FAIL( pj_list_find_node(&endpt->module_list, mod) == mod,
{status = PJ_ENOTFOUND;goto on_return;} );
/* Make sure the module exists in the array. */
PJ_ASSERT_ON_FAIL( mod->id>=0 && mod->id<PJ_ARRAY_SIZE(endpt->modules) &&
endpt->modules[mod->id] == mod,
{status = PJ_ENOTFOUND; goto on_return;});
/* Try to stop the module. */
if (mod->stop) {
status = (*mod->stop)();
if (status != PJ_SUCCESS) goto on_return;
}
/* Try to unload the module. */
if (mod->unload) {
status = (*mod->unload)();
if (status != PJ_SUCCESS) goto on_return;
}
/* Module MUST NOT set module ID to -1. */
pj_assert(mod->id >= 0);
/* Remove module from array. */
endpt->modules[mod->id] = NULL;
/* Remove module from list. */
pj_list_erase(mod);
/* Set module Id to -1. */
mod->id = -1;
/* Done. */
status = PJ_SUCCESS;
PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" unregistered",
(int)mod->name.slen, mod->name.ptr));
on_return:
pj_rwmutex_unlock_write(endpt->mod_mutex);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(3,(THIS_FILE, "Module \"%.*s\" can not be unregistered: %s",
(int)mod->name.slen, mod->name.ptr, errmsg));
}
return status;
}
/*
* Get the value of the specified capability header field.
*/
PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_capability( pjsip_endpoint *endpt,
int htype,
const pj_str_t *hname)
{
pjsip_hdr *hdr = endpt->cap_hdr.next;
/* Check arguments. */
PJ_ASSERT_RETURN(endpt != NULL, NULL);
PJ_ASSERT_RETURN(htype != PJSIP_H_OTHER || hname, NULL);
if (htype != PJSIP_H_OTHER) {
while (hdr != &endpt->cap_hdr) {
if (hdr->type == htype)
return hdr;
hdr = hdr->next;
}
}
return NULL;
}
/*
* Check if the specified capability is supported.
*/
PJ_DEF(pj_bool_t) pjsip_endpt_has_capability( pjsip_endpoint *endpt,
int htype,
const pj_str_t *hname,
const pj_str_t *token)
{
const pjsip_generic_array_hdr *hdr;
unsigned i;
hdr = (const pjsip_generic_array_hdr*)
pjsip_endpt_get_capability(endpt, htype, hname);
if (!hdr)
return PJ_FALSE;
PJ_ASSERT_RETURN(token != NULL, PJ_FALSE);
for (i=0; i<hdr->count; ++i) {
if (!pj_stricmp(&hdr->values[i], token))
return PJ_TRUE;
}
return PJ_FALSE;
}
/*
* Add or register new capabilities as indicated by the tags to the
* appropriate header fields in the endpoint.
*/
PJ_DEF(pj_status_t) pjsip_endpt_add_capability( pjsip_endpoint *endpt,
pjsip_module *mod,
int htype,
const pj_str_t *hname,
unsigned count,
const pj_str_t tags[])
{
pjsip_generic_array_hdr *hdr;
unsigned i;
PJ_UNUSED_ARG(mod);
/* Check arguments. */
PJ_ASSERT_RETURN(endpt!=NULL && count>0 && tags, PJ_EINVAL);
PJ_ASSERT_RETURN(htype==PJSIP_H_ACCEPT ||
htype==PJSIP_H_ALLOW ||
htype==PJSIP_H_SUPPORTED,
PJ_EINVAL);
/* Find the header. */
hdr = (pjsip_generic_array_hdr*) pjsip_endpt_get_capability(endpt,
htype, hname);
/* Create the header when it's not present */
if (hdr == NULL) {
switch (htype) {
case PJSIP_H_ACCEPT:
hdr = pjsip_accept_hdr_create(endpt->pool);
break;
case PJSIP_H_ALLOW:
hdr = pjsip_allow_hdr_create(endpt->pool);
break;
case PJSIP_H_SUPPORTED:
hdr = pjsip_supported_hdr_create(endpt->pool);
break;
default:
return PJ_EINVAL;
}
if (hdr) {
pj_list_push_back(&endpt->cap_hdr, hdr);
}
}
/* Add the tags to the header. */
for (i=0; i<count; ++i) {
pj_strdup(endpt->pool, &hdr->values[hdr->count], &tags[i]);
++hdr->count;
}
/* Done. */
return PJ_SUCCESS;
}
/*
* Get additional headers to be put in outgoing request message.
*/
PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt)
{
return &endpt->req_hdr;
}
/*
* Initialize endpoint.
*/
PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
const char *name,
pjsip_endpoint **p_endpt)
{
pj_status_t status;
pj_pool_t *pool;
pjsip_endpoint *endpt;
pjsip_max_fwd_hdr *mf_hdr;
pj_lock_t *lock = NULL;
if (!error_subsys_initialized) {
pj_register_strerror(PJSIP_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
&pjsip_strerror);
error_subsys_initialized = 1;
}
PJ_LOG(5, (THIS_FILE, "Creating endpoint instance..."));
*p_endpt = NULL;
/* Create pool */
pool = pj_pool_create(pf, "pept%p",
PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT,
&pool_callback);
if (!pool)
return PJ_ENOMEM;
/* Create endpoint. */
endpt = pj_pool_zalloc(pool, sizeof(*endpt));
endpt->pool = pool;
endpt->pf = pf;
/* Init modules list. */
pj_list_init(&endpt->module_list);
/* Create R/W mutex for module manipulation. */
status = pj_rwmutex_create(endpt->pool, "ept%p", &endpt->mod_mutex);
if (status != PJ_SUCCESS)
goto on_error;
/* Init parser. */
init_sip_parser();
/* Init tel: uri */
pjsip_tel_uri_subsys_init();
/* Get name. */
if (name != NULL) {
pj_str_t temp;
pj_strdup_with_null(endpt->pool, &endpt->name, pj_cstr(&temp, name));
} else {
pj_strdup_with_null(endpt->pool, &endpt->name, pj_gethostname());
}
/* Create mutex for the events, etc. */
status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex );
if (status != PJ_SUCCESS) {
goto on_error;
}
/* Create timer heap to manage all timers within this endpoint. */
status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT,
&endpt->timer_heap);
if (status != PJ_SUCCESS) {
goto on_error;
}
/* Set recursive lock for the timer heap. */
status = pj_lock_create_recursive_mutex( endpt->pool, "edpt%p", &lock);
if (status != PJ_SUCCESS) {
goto on_error;
}
pj_timer_heap_set_lock(endpt->timer_heap, lock, PJ_TRUE);
/* Set maximum timed out entries to process in a single poll. */
pj_timer_heap_set_max_timed_out_per_poll(endpt->timer_heap,
PJSIP_MAX_TIMED_OUT_ENTRIES);
/* Create ioqueue. */
status = pj_ioqueue_create( endpt->pool, PJSIP_MAX_TRANSPORTS, &endpt->ioqueue);
if (status != PJ_SUCCESS) {
goto on_error;
}
/* Create transport manager. */
status = pjsip_tpmgr_create( endpt->pool, endpt,
&endpt_on_rx_msg,
&endpt_on_tx_msg,
&endpt->transport_mgr);
if (status != PJ_SUCCESS) {
goto on_error;
}
/* Create asynchronous DNS resolver. */
status = pjsip_resolver_create(endpt->pool, &endpt->resolver);
if (status != PJ_SUCCESS) {
PJ_LOG(4, (THIS_FILE, "Error creating resolver instance"));
goto on_error;
}
/* Initialize request headers. */
pj_list_init(&endpt->req_hdr);
/* Add "Max-Forwards" for request header. */
mf_hdr = pjsip_max_fwd_hdr_create(endpt->pool,
PJSIP_MAX_FORWARDS_VALUE);
pj_list_insert_before( &endpt->req_hdr, mf_hdr);
/* Initialize capability header list. */
pj_list_init(&endpt->cap_hdr);
/* Done. */
*p_endpt = endpt;
return status;
on_error:
if (endpt->transport_mgr) {
pjsip_tpmgr_destroy(endpt->transport_mgr);
endpt->transport_mgr = NULL;
}
if (endpt->ioqueue) {
pj_ioqueue_destroy(endpt->ioqueue);
endpt->ioqueue = NULL;
}
if (endpt->timer_heap) {
pj_timer_heap_destroy(endpt->timer_heap);
endpt->timer_heap = NULL;
}
if (endpt->mutex) {
pj_mutex_destroy(endpt->mutex);
endpt->mutex = NULL;
}
if (endpt->mod_mutex) {
pj_rwmutex_destroy(endpt->mod_mutex);
endpt->mod_mutex = NULL;
}
pj_pool_release( endpt->pool );
PJ_LOG(4, (THIS_FILE, "Error creating endpoint"));
return status;
}
/*
* Destroy endpoint.
*/
PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
{
pjsip_module *mod;
PJ_LOG(5, (THIS_FILE, "Destroying endpoing instance.."));
/* Unregister modules. */
mod = endpt->module_list.prev;
while (mod != &endpt->module_list) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -