diff options
author | David M. Lee <dlee@digium.com> | 2013-01-07 14:24:28 -0600 |
---|---|---|
committer | David M. Lee <dlee@digium.com> | 2013-01-07 14:24:28 -0600 |
commit | f3ab456a17af1c89a6e3be4d20c5944853df1cb0 (patch) | |
tree | d00e1a332cd038a6d906a1ea0ac91e1a4458e617 /pjsip/src/pjsip/sip_endpoint.c |
Import pjproject-2.0.1
Diffstat (limited to 'pjsip/src/pjsip/sip_endpoint.c')
-rw-r--r-- | pjsip/src/pjsip/sip_endpoint.c | 1245 |
1 files changed, 1245 insertions, 0 deletions
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c new file mode 100644 index 0000000..2510d14 --- /dev/null +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -0,0 +1,1245 @@ +/* $Id: sip_endpoint.c 4154 2012-06-05 10:41:17Z bennylp $ */ +/* + * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 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 + + +/* List of SIP endpoint exit callback. */ +typedef struct exit_cb +{ + PJ_DECL_LIST_MEMBER (struct exit_cb); + pjsip_endpt_exit_callback func; +} exit_cb; + + +/** + * 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; + + /** List of exit callback. */ + exit_cb exit_cb_list; +}; + + +#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 ); +static pj_status_t unload_module(pjsip_endpoint *endpt, + pjsip_module *mod); + +/* 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); + + +/* + * 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((const pj_str_t*)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; + unsigned 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<(int)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; + } + + /* Unload module */ + status = unload_module(endpt, mod); + +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; +} + +static pj_status_t unload_module(pjsip_endpoint *endpt, + pjsip_module *mod) +{ + pj_status_t status; + + /* Try to unload the module. */ + if (mod->unload) { + status = (*mod->unload)(); + if (status != PJ_SUCCESS) + return status; + } + + /* 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)); + + 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; + + + status = pj_register_strerror(PJSIP_ERRNO_START, PJ_ERRNO_SPACE_SIZE, + &pjsip_strerror); + pj_assert(status == PJ_SUCCESS); + + 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_T(pool, pjsip_endpoint); + endpt->pool = pool; + endpt->pf = pf; + + /* Init modules list. */ + pj_list_init(&endpt->module_list); + + /* Initialize exit callback list. */ + pj_list_init(&endpt->exit_cb_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; + exit_cb *ecb; + + PJ_LOG(5, (THIS_FILE, "Destroying endpoing instance..")); + + /* Phase 1: stop all modules */ + mod = endpt->module_list.prev; + while (mod != &endpt->module_list) { + pjsip_module *prev = mod->prev; + if (mod->stop) { + (*mod->stop)(); + } + mod = prev; + } + + /* Phase 2: unload modules. */ + mod = endpt->module_list.prev; + while (mod != &endpt->module_list) { + pjsip_module *prev = mod->prev; + unload_module(endpt, mod); + mod = prev; + } + + /* Destroy resolver */ + pjsip_resolver_destroy(endpt->resolver); + + /* Shutdown and destroy all transports. */ + pjsip_tpmgr_destroy(endpt->transport_mgr); + + /* Destroy ioqueue */ + pj_ioqueue_destroy(endpt->ioqueue); + + /* Destroy timer heap */ +#if PJ_TIMER_DEBUG + pj_timer_heap_dump(endpt->timer_heap); +#endif + pj_timer_heap_destroy(endpt->timer_heap); + + /* Call all registered exit callbacks */ + ecb = endpt->exit_cb_list.next; + while (ecb != &endpt->exit_cb_list) { + (*ecb->func)(endpt); + ecb = ecb->next; + } + + /* Delete endpoint mutex. */ + pj_mutex_destroy(endpt->mutex); + + /* Deinit parser */ + deinit_sip_parser(); + + /* Delete module's mutex */ + pj_rwmutex_destroy(endpt->mod_mutex); + + /* Finally destroy pool. */ + pj_pool_release(endpt->pool); + + PJ_LOG(4, (THIS_FILE, "Endpoint %p destroyed", endpt)); +} + +/* + * Get endpoint name. + */ +PJ_DEF(const pj_str_t*) pjsip_endpt_name(const pjsip_endpoint *endpt) +{ + return &endpt->name; +} + + +/* + * Create new pool. + */ +PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, + const char *pool_name, + pj_size_t initial, + pj_size_t increment ) +{ + pj_pool_t *pool; + + /* Lock endpoint mutex. */ + /* No need to lock mutex. Factory is thread safe. + pj_mutex_lock(endpt->mutex); + */ + + /* Create pool */ + pool = pj_pool_create( endpt->pf, pool_name, + initial, increment, &pool_callback); + + /* Unlock mutex. */ + /* No need to lock mutex. Factory is thread safe. + pj_mutex_unlock(endpt->mutex); + */ + + if (!pool) { + PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name)); + } + + return pool; +} + +/* + * Return back pool to endpoint's pool manager to be either destroyed or + * recycled. + */ +PJ_DEF(void) pjsip_endpt_release_pool( pjsip_endpoint *endpt, pj_pool_t *pool ) +{ + PJ_LOG(6, (THIS_FILE, "Releasing pool %s", pj_pool_getobjname(pool))); + + /* Don't need to acquire mutex since pool factory is thread safe + pj_mutex_lock(endpt->mutex); + */ + pj_pool_release( pool ); + + PJ_UNUSED_ARG(endpt); + /* + pj_mutex_unlock(endpt->mutex); + */ +} + + +PJ_DEF(pj_status_t) pjsip_endpt_handle_events2(pjsip_endpoint *endpt, + const pj_time_val *max_timeout, + unsigned *p_count) +{ + /* timeout is 'out' var. This just to make compiler happy. */ + pj_time_val timeout = { 0, 0}; + unsigned count = 0, net_event_count = 0; + int c; + + PJ_LOG(6, (THIS_FILE, "pjsip_endpt_handle_events()")); + + /* Poll the timer. The timer heap has its own mutex for better + * granularity, so we don't need to lock end endpoint. + */ + timeout.sec = timeout.msec = 0; + c = pj_timer_heap_poll( endpt->timer_heap, &timeout ); + if (c > 0) + count += c; + + /* timer_heap_poll should never ever returns negative value, or otherwise + * ioqueue_poll() will block forever! + */ + pj_assert(timeout.sec >= 0 && timeout.msec >= 0); + if (timeout.msec >= 1000) timeout.msec = 999; + + /* If caller specifies maximum time to wait, then compare the value with + * the timeout to wait from timer, and use the minimum value. + */ + if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) { + timeout = *max_timeout; + } + + /* Poll ioqueue. + * Repeat polling the ioqueue while we have immediate events, because + * timer heap may process more than one events, so if we only process + * one network events at a time (such as when IOCP backend is used), + * the ioqueue may have trouble keeping up with the request rate. + * + * For example, for each send() request, one network event will be + * reported by ioqueue for the send() completion. If we don't poll + * the ioqueue often enough, the send() completion will not be + * reported in timely manner. + */ + do { + c = pj_ioqueue_poll( endpt->ioqueue, &timeout); + if (c < 0) { + pj_status_t err = pj_get_netos_error(); + pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout)); + if (p_count) + *p_count = count; + return err; + } else if (c == 0) { + break; + } else { + net_event_count += c; + timeout.sec = timeout.msec = 0; + } + } while (c > 0 && net_event_count < PJSIP_MAX_NET_EVENTS); + + count += net_event_count; + if (p_count) + *p_count = count; + + return PJ_SUCCESS; +} + +/* + * Handle events. + */ +PJ_DEF(pj_status_t) pjsip_endpt_handle_events(pjsip_endpoint *endpt, + const pj_time_val *max_timeout) +{ + return pjsip_endpt_handle_events2(endpt, max_timeout, NULL); +} + +/* + * Schedule timer. + */ +#if PJ_TIMER_DEBUG +PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer_dbg(pjsip_endpoint *endpt, + pj_timer_entry *entry, + const pj_time_val *delay, + const char *src_file, + int src_line) +{ + PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", + entry, delay->sec, delay->msec)); + return pj_timer_heap_schedule_dbg(endpt->timer_heap, entry, delay, + src_file, src_line); +} +#else +PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry, + const pj_time_val *delay ) +{ + PJ_LOG(6, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", + entry, delay->sec, delay->msec)); + return pj_timer_heap_schedule( endpt->timer_heap, entry, delay ); +} +#endif + +/* + * Cancel the previously registered timer. + */ +PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry ) +{ + PJ_LOG(6, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry)); + pj_timer_heap_cancel( endpt->timer_heap, entry ); +} + +/* + * Get the timer heap instance of the SIP endpoint. + */ +PJ_DEF(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt) +{ + return endpt->timer_heap; +} + +/* + * This is the callback that is called by the transport manager when it + * receives a message from the network. + */ +static void endpt_on_rx_msg( pjsip_endpoint *endpt, + pj_status_t status, + pjsip_rx_data *rdata ) +{ + pjsip_msg *msg = rdata->msg_info.msg; + + if (status != PJ_SUCCESS) { + char info[30]; + char errmsg[PJ_ERR_MSG_SIZE]; + + info[0] = '\0'; + + if (status == PJSIP_EMISSINGHDR) { + pj_str_t p; + + p.ptr = info; p.slen = 0; + + if (rdata->msg_info.cid == NULL || rdata->msg_info.cid->id.slen) + pj_strcpy2(&p, "Call-ID"); + if (rdata->msg_info.from == NULL) + pj_strcpy2(&p, " From"); + if (rdata->msg_info.to == NULL) + pj_strcpy2(&p, " To"); + if (rdata->msg_info.via == NULL) + pj_strcpy2(&p, " Via"); + if (rdata->msg_info.cseq == NULL) + pj_strcpy2(&p, " CSeq"); + + p.ptr[p.slen] = '\0'; + } + + pj_strerror(status, errmsg, sizeof(errmsg)); + + PJ_LOG(1, (THIS_FILE, + "Error processing packet from %s:%d: %s %s [code %d]:\n" + "%.*s\n" + "-- end of packet.", + rdata->pkt_info.src_name, + rdata->pkt_info.src_port, + errmsg, + info, + status, + (int)rdata->msg_info.len, + rdata->msg_info.msg_buf)); + return; + } + + PJ_LOG(5, (THIS_FILE, "Processing incoming message: %s", + pjsip_rx_data_get_info(rdata))); + pj_log_push_indent(); + +#if defined(PJSIP_CHECK_VIA_SENT_BY) && PJSIP_CHECK_VIA_SENT_BY != 0 + /* For response, check that the value in Via sent-by match the transport. + * If not matched, silently drop the response. + * Ref: RFC3261 Section 18.1.2 Receiving Response + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + const pj_str_t *local_addr; + int port = rdata->msg_info.via->sent_by.port; + pj_bool_t mismatch = PJ_FALSE; + if (port == 0) { + pjsip_transport_type_e type; + type = (pjsip_transport_type_e)rdata->tp_info.transport->key.type; + port = pjsip_transport_get_default_port_for_type(type); + } + local_addr = &rdata->tp_info.transport->local_name.host; + + if (pj_strcmp(&rdata->msg_info.via->sent_by.host, local_addr) != 0) { + + /* The RFC says that we should drop response when sent-by + * address mismatch. But it could happen (e.g. with SER) when + * endpoint with private IP is sending request to public + * server. + + mismatch = PJ_TRUE; + + */ + + } else if (port != rdata->tp_info.transport->local_name.port) { + /* Port or address mismatch, we should discard response */ + /* But we saw one implementation (we don't want to name it to + * protect the innocence) which put wrong sent-by port although + * the "rport" parameter is correct. + * So we discard the response only if the port doesn't match + * both the port in sent-by and rport. We try to be lenient here! + */ + if (rdata->msg_info.via->rport_param != + rdata->tp_info.transport->local_name.port) + mismatch = PJ_TRUE; + else { + PJ_LOG(4,(THIS_FILE, "Message %s from %s has mismatch port in " + "sent-by but the rport parameter is " + "correct", + pjsip_rx_data_get_info(rdata), + rdata->pkt_info.src_name)); + } + } + + if (mismatch) { + PJ_TODO(ENDPT_REPORT_WHEN_DROPPING_MESSAGE); + PJ_LOG(4,(THIS_FILE, "Dropping response %s from %s:%d because " + "sent-by is mismatch", + pjsip_rx_data_get_info(rdata), + rdata->pkt_info.src_name, + rdata->pkt_info.src_port)); + pj_log_pop_indent(); + return; + } + } +#endif + + + /* Distribute to modules, starting from modules with highest priority */ + LOCK_MODULE_ACCESS(endpt); + + if (msg->type == PJSIP_REQUEST_MSG) { + pjsip_module *mod; + pj_bool_t handled = PJ_FALSE; + + mod = endpt->module_list.next; + while (mod != &endpt->module_list) { + if (mod->on_rx_request) + handled = (*mod->on_rx_request)(rdata); + if (handled) + break; + mod = mod->next; + } + + /* No module is able to handle the request. */ + if (!handled) { + PJ_TODO(ENDPT_RESPOND_UNHANDLED_REQUEST); + PJ_LOG(4,(THIS_FILE, "Message %s from %s:%d was dropped/unhandled by" + " any modules", + pjsip_rx_data_get_info(rdata), + rdata->pkt_info.src_name, + rdata->pkt_info.src_port)); + } + + } else { + pjsip_module *mod; + pj_bool_t handled = PJ_FALSE; + + mod = endpt->module_list.next; + while (mod != &endpt->module_list) { + if (mod->on_rx_response) + handled = (*mod->on_rx_response)(rdata); + if (handled) + break; + mod = mod->next; + } + + if (!handled) { + PJ_LOG(4,(THIS_FILE, "Message %s from %s:%d was dropped/unhandled" + " by any modules", + pjsip_rx_data_get_info(rdata), + rdata->pkt_info.src_name, + rdata->pkt_info.src_port)); + } + } + + UNLOCK_MODULE_ACCESS(endpt); + + /* Must clear mod_data before returning rdata to transport, since + * rdata may be reused. + */ + pj_bzero(&rdata->endpt_info, sizeof(rdata->endpt_info)); + + pj_log_pop_indent(); +} + +/* + * This callback is called by transport manager before message is sent. + * Modules may inspect the message before it's actually sent. + */ +static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt, + pjsip_tx_data *tdata ) +{ + pj_status_t status = PJ_SUCCESS; + pjsip_module *mod; + + /* Distribute to modules, starting from modules with LOWEST priority */ + LOCK_MODULE_ACCESS(endpt); + + mod = endpt->module_list.prev; + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + while (mod != &endpt->module_list) { + if (mod->on_tx_request) + status = (*mod->on_tx_request)(tdata); + if (status != PJ_SUCCESS) + break; + mod = mod->prev; + } + + } else { + while (mod != &endpt->module_list) { + if (mod->on_tx_response) + status = (*mod->on_tx_response)(tdata); + if (status != PJ_SUCCESS) + break; + mod = mod->prev; + } + } + + UNLOCK_MODULE_ACCESS(endpt); + + return status; +} + + +/* + * Create transmit data buffer. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt, + pjsip_tx_data **p_tdata) +{ + return pjsip_tx_data_create(endpt->transport_mgr, p_tdata); +} + +/* + * Create the DNS resolver instance. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_resolver(pjsip_endpoint *endpt, + pj_dns_resolver **p_resv) +{ +#if PJSIP_HAS_RESOLVER + PJ_ASSERT_RETURN(endpt && p_resv, PJ_EINVAL); + return pj_dns_resolver_create( endpt->pf, NULL, 0, endpt->timer_heap, + endpt->ioqueue, p_resv); +#else + PJ_UNUSED_ARG(endpt); + PJ_UNUSED_ARG(p_resv); + pj_assert(!"Resolver is disabled (PJSIP_HAS_RESOLVER==0)"); + return PJ_EINVALIDOP; +#endif +} + +/* + * Set DNS resolver to be used by the SIP resolver. + */ +PJ_DEF(pj_status_t) pjsip_endpt_set_resolver( pjsip_endpoint *endpt, + pj_dns_resolver *resv) +{ + return pjsip_resolver_set_resolver(endpt->resolver, resv); +} + +/* + * Get the DNS resolver being used by the SIP resolver. + */ +PJ_DEF(pj_dns_resolver*) pjsip_endpt_get_resolver(pjsip_endpoint *endpt) +{ + PJ_ASSERT_RETURN(endpt, NULL); + return pjsip_resolver_get_resolver(endpt->resolver); +} + +/* + * Resolve + */ +PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, + pj_pool_t *pool, + pjsip_host_info *target, + void *token, + pjsip_resolver_callback *cb) +{ + pjsip_resolve( endpt->resolver, pool, target, token, cb); +} + +/* + * Get transport manager. + */ +PJ_DEF(pjsip_tpmgr*) pjsip_endpt_get_tpmgr(pjsip_endpoint *endpt) +{ + return endpt->transport_mgr; +} + +/* + * Get ioqueue instance. + */ +PJ_DEF(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt) +{ + return endpt->ioqueue; +} + +/* + * Find/create transport. + */ +PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport(pjsip_endpoint *endpt, + pjsip_transport_type_e type, + const pj_sockaddr_t *remote, + int addr_len, + const pjsip_tpselector *sel, + pjsip_transport **transport) +{ + return pjsip_tpmgr_acquire_transport(endpt->transport_mgr, type, + remote, addr_len, sel, transport); +} + + +/* + * Find/create transport. + */ +PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport2(pjsip_endpoint *endpt, + pjsip_transport_type_e type, + const pj_sockaddr_t *remote, + int addr_len, + const pjsip_tpselector *sel, + pjsip_tx_data *tdata, + pjsip_transport **transport) +{ + return pjsip_tpmgr_acquire_transport2(endpt->transport_mgr, type, remote, + addr_len, sel, tdata, transport); +} + + +/* + * Report error. + */ +PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt, + const char *sender, + pj_status_t error_code, + const char *format, + ... ) +{ +#if PJ_LOG_MAX_LEVEL > 0 + char newformat[256]; + int len; + va_list marker; + + va_start(marker, format); + + PJ_UNUSED_ARG(endpt); + + len = pj_ansi_strlen(format); + if (len < (int)sizeof(newformat)-30) { + pj_str_t errstr; + + pj_ansi_strcpy(newformat, format); + pj_ansi_snprintf(newformat+len, sizeof(newformat)-len-1, + ": [err %d] ", error_code); + len += pj_ansi_strlen(newformat+len); + + errstr = pj_strerror( error_code, newformat+len, + sizeof(newformat)-len-1); + + len += errstr.slen; + newformat[len] = '\0'; + + pj_log(sender, 1, newformat, marker); + } else { + pj_log(sender, 1, format, marker); + } + + va_end(marker); +#else + PJ_UNUSED_ARG(format); + PJ_UNUSED_ARG(error_code); + PJ_UNUSED_ARG(sender); + PJ_UNUSED_ARG(endpt); +#endif +} + + +/* + * Dump endpoint. + */ +PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()")); + + /* Lock mutex. */ + pj_mutex_lock(endpt->mutex); + + PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt)); + + /* Dumping pool factory. */ + pj_pool_factory_dump(endpt->pf, detail); + + /* Pool health. */ + PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u", + pj_pool_get_capacity(endpt->pool), + pj_pool_get_used_size(endpt->pool))); + + /* Resolver */ +#if PJSIP_HAS_RESOLVER + if (pjsip_endpt_get_resolver(endpt)) { + pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), detail); + } +#endif + + /* Transports. + */ + pjsip_tpmgr_dump_transports( endpt->transport_mgr ); + + /* Timer. */ +#if PJ_TIMER_DEBUG + pj_timer_heap_dump(endpt->timer_heap); +#else + PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries", + pj_timer_heap_count(endpt->timer_heap))); +#endif + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->mutex); +#else + PJ_UNUSED_ARG(endpt); + PJ_UNUSED_ARG(detail); + PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled.")); +#endif +} + + +PJ_DEF(pj_status_t) pjsip_endpt_atexit( pjsip_endpoint *endpt, + pjsip_endpt_exit_callback func) +{ + exit_cb *new_cb; + + PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL); + + new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb); + new_cb->func = func; + + pj_mutex_lock(endpt->mutex); + pj_list_push_back(&endpt->exit_cb_list, new_cb); + pj_mutex_unlock(endpt->mutex); + + return PJ_SUCCESS; +} |