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

📄 sip_endpoint.c

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