diff options
Diffstat (limited to 'pjsip/src/pjsip/sip_endpoint.c')
-rw-r--r-- | pjsip/src/pjsip/sip_endpoint.c | 2224 |
1 files changed, 1112 insertions, 1112 deletions
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index d68d35d0..53311f36 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -1,1112 +1,1112 @@ -/* $Id$ */
-/*
- * Copyright (C) 2003-2006 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 "endpoint"
-
-#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;
-
- /** Transaction table. */
- pj_hash_table_t *tsx_table;
-
- /** Mutex for transaction table. */
- pj_mutex_t *tsx_table_mutex;
-
- /** Timer heap. */
- pj_timer_heap_t *timer_heap;
-
- /** Transport manager. */
- pjsip_tpmgr *transport_mgr;
-
- /** Ioqueue. */
- pj_ioqueue_t *ioqueue;
-
- /** DNS Resolver. */
- pjsip_resolver_t *resolver;
-
- /** Number of modules registered. */
- pj_uint32_t mod_count;
-
- /** Modules. */
- pjsip_module *modules[PJSIP_MAX_MODULE];
-
- /** Number of supported methods. */
- unsigned method_cnt;
-
- /** Array of supported methods. */
- const pjsip_method *methods[MAX_METHODS];
-
- /** Allow header. */
- pjsip_allow_hdr *allow_hdr;
-
- /** Route header list. */
- pjsip_route_hdr route_hdr_list;
-
- /** Additional request headers. */
- pjsip_hdr req_hdr;
-};
-
-
-
-/*
- * Prototypes.
- */
-static void endpt_transport_callback(pjsip_endpoint*,
- pj_status_t, pjsip_rx_data*);
-
-
-/*
- * 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);
-}
-
-
-/*
- * Initialize modules.
- */
-static pj_status_t init_modules( pjsip_endpoint *endpt )
-{
- pj_status_t status;
- unsigned i;
- //pj_str_t str_COMMA = { ", ", 2 };
- extern pjsip_module aux_tsx_module;
-
- PJ_LOG(5, (THIS_FILE, "init_modules()"));
-
- /* Load static modules. */
- endpt->mod_count = PJSIP_MAX_MODULE;
- status = register_static_modules( &endpt->mod_count, endpt->modules );
- if (status != 0) {
- return status;
- }
-
- /* Add mini aux module. */
- endpt->modules[endpt->mod_count++] = &aux_tsx_module;
-
- /* Load dynamic modules. */
- // Not supported yet!
-
- /* Sort modules on the priority. */
- for (i=endpt->mod_count-1; i>0; --i) {
- pj_uint32_t max = 0;
- unsigned j;
- for (j=1; j<=i; ++j) {
- if (endpt->modules[j]->priority > endpt->modules[max]->priority)
- max = j;
- }
- if (max != i) {
- pjsip_module *temp = endpt->modules[max];
- endpt->modules[max] = endpt->modules[i];
- endpt->modules[i] = temp;
- }
- }
-
- /* Initialize each module. */
- for (i=0; i < endpt->mod_count; ++i) {
- int j;
-
- pjsip_module *mod = endpt->modules[i];
- if (mod->init_module) {
- status = mod->init_module(endpt, mod, i);
- if (status != 0) {
- return status;
- }
- }
-
- /* Collect all supported methods from modules. */
- for (j=0; j<mod->method_cnt; ++j) {
- unsigned k;
- for (k=0; k<endpt->method_cnt; ++k) {
- if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0)
- break;
- }
- if (k == endpt->method_cnt) {
- if (endpt->method_cnt < MAX_METHODS) {
- endpt->methods[endpt->method_cnt++] = mod->methods[j];
- } else {
- PJ_LOG(1,(THIS_FILE, "Too many methods"));
- return -1;
- }
- }
- }
- }
-
- /* Create Allow header. */
- endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool );
- endpt->allow_hdr->count = endpt->method_cnt;
- for (i=0; i<endpt->method_cnt; ++i) {
- endpt->allow_hdr->values[i] = endpt->methods[i]->name;
- }
-
- /* Start each module. */
- for (i=0; i < endpt->mod_count; ++i) {
- pjsip_module *mod = endpt->modules[i];
- if (mod->start_module) {
- status = mod->start_module(mod);
- if (status != 0) {
- return status;
- }
- }
- }
-
- /* Done. */
- return 0;
-}
-
-/*
- * Unregister the transaction from the hash table, and destroy the resources
- * from the transaction.
- */
-PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
-
- pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
-
- /* No need to lock transaction.
- * This function typically is called from the transaction callback, which
- * means that transaction mutex is being held.
- */
- pj_assert( pj_mutex_is_locked(tsx->mutex) );
-
- /* Lock endpoint. */
- pj_mutex_lock( endpt->tsx_table_mutex );
-
- /* Unregister from the hash table. */
- pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, NULL);
-
- /* Unlock endpoint mutex. */
- pj_mutex_unlock( endpt->tsx_table_mutex );
-
- /* Destroy transaction mutex. */
- pj_mutex_destroy( tsx->mutex );
-
- /* Release the pool for the transaction. */
- pj_pool_release(tsx->pool);
-
- PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx));
-}
-
-
-/*
- * Receive transaction events from transactions and dispatch them to the
- * modules.
- */
-static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt)
-{
- unsigned i;
-
- /* Dispatch event to modules. */
- for (i=0; i<endpt->mod_count; ++i) {
- pjsip_module *mod = endpt->modules[i];
- if (mod && mod->tsx_handler) {
- mod->tsx_handler( mod, evt );
- }
- }
-
- /* Destroy transaction if it is terminated. */
- if (evt->type == PJSIP_EVENT_TSX_STATE &&
- evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED)
- {
- /* No need to lock mutex. Mutex is locked inside the destroy function */
- pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx );
- }
-}
-
-/*
- * Receive transaction events from transactions and put in the event queue
- * to be processed later.
- */
-void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt )
-{
- // Need to protect this with try/catch?
- endpt_do_event(endpt, evt);
-}
-
-/*
- * Get "Allow" header.
- */
-PJ_DEF(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt )
-{
- return endpt->allow_hdr;
-}
-
-/*
- * 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;
-}
-
-PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
- int url_cnt, const pj_str_t url[])
-{
- int i;
- pjsip_route_hdr *hdr;
- pj_str_t str_ROUTE = { "Route", 5 };
-
- /* Lock endpoint mutex. */
- pj_mutex_lock(endpt->mutex);
-
- pj_list_init(&endpt->route_hdr_list);
-
- for (i=0; i<url_cnt; ++i) {
- int len = url[i].slen;
- char *dup = pj_pool_alloc(endpt->pool, len + 1);
- pj_memcpy(dup, url[i].ptr, len);
- dup[len] = '\0';
-
- hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL);
- if (!hdr) {
- pj_mutex_unlock(endpt->mutex);
- PJ_LOG(4,(THIS_FILE, "Invalid URL %s in proxy URL", dup));
- return -1;
- }
-
- pj_assert(hdr->type == PJSIP_H_ROUTE);
- pj_list_insert_before(&endpt->route_hdr_list, hdr);
- }
-
- /* Unlock endpoint mutex. */
- pj_mutex_unlock(endpt->mutex);
-
- return 0;
-}
-
-/*
- * Get "Route" header list.
- */
-PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt )
-{
- return &endpt->route_hdr_list;
-}
-
-
-/*
- * 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_forwards_hdr *mf_hdr;
- pj_lock_t *lock = NULL;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()"));
-
- *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_calloc(pool, 1, sizeof(*endpt));
- endpt->pool = pool;
- endpt->pf = pf;
-
- /* 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 mutex for the transaction table. */
- status = pj_mutex_create_recursive( endpt->pool, "mtbl%p",
- &endpt->tsx_table_mutex);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create hash table for transaction. */
- endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
- if (!endpt->tsx_table) {
- status = PJ_ENOMEM;
- 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_transport_callback,
- &endpt->transport_mgr);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
-
- /* Create asynchronous DNS resolver. */
- endpt->resolver = pjsip_resolver_create(endpt->pool);
- if (!endpt->resolver) {
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error creating resolver"));
- goto on_error;
- }
-
- /* Initialize TLS ID for transaction lock. */
- status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
- if (status != PJ_SUCCESS) {
- goto on_error;
- }
- pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
-
- /* Initialize request headers. */
- pj_list_init(&endpt->req_hdr);
-
- /* Initialist "Route" header list. */
- pj_list_init(&endpt->route_hdr_list);
-
- /* Add "Max-Forwards" for request header. */
- mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool);
- mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE;
- pj_list_insert_before( &endpt->req_hdr, mf_hdr);
-
- /* Load and init modules. */
- status = init_modules(endpt);
- if (status != PJ_SUCCESS) {
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()"));
- return status;
- }
-
- /* Done. */
- *p_endpt = endpt;
- return status;
-
-on_error:
- if (endpt->transport_mgr) {
- pjsip_tpmgr_destroy(endpt->transport_mgr);
- endpt->transport_mgr = NULL;
- }
- if (endpt->mutex) {
- pj_mutex_destroy(endpt->mutex);
- endpt->mutex = NULL;
- }
- if (endpt->tsx_table_mutex) {
- pj_mutex_destroy(endpt->tsx_table_mutex);
- endpt->tsx_table_mutex = NULL;
- }
- pj_pool_release( endpt->pool );
-
- PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init() failed"));
- return status;
-}
-
-/*
- * Destroy endpoint.
- */
-PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy()"));
-
- /* Shutdown and destroy all transports. */
- pjsip_tpmgr_destroy(endpt->transport_mgr);
-
- /* Delete endpoint mutex. */
- pj_mutex_destroy(endpt->mutex);
-
- /* Delete transaction table mutex. */
- pj_mutex_destroy(endpt->tsx_table_mutex);
-
- /* Finally destroy pool. */
- pj_pool_release(endpt->pool);
-}
-
-/*
- * 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;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_pool()"));
-
- /* Lock endpoint mutex. */
- pj_mutex_lock(endpt->mutex);
-
- /* Create pool */
- pool = pj_pool_create( endpt->pf, pool_name,
- initial, increment, &pool_callback);
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->mutex);
-
- if (pool) {
- PJ_LOG(5, (THIS_FILE, " pool %s created", pj_pool_getobjname(pool)));
- } else {
- 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_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool)));
-
- pj_mutex_lock(endpt->mutex);
- pj_pool_release( pool );
- pj_mutex_unlock(endpt->mutex);
-}
-
-/*
- * Handle events.
- */
-PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
- const pj_time_val *max_timeout)
-{
- /* timeout is 'out' var. This just to make compiler happy. */
- pj_time_val timeout = { 0, 0};
-
- PJ_LOG(5, (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;
- pj_timer_heap_poll( endpt->timer_heap, &timeout );
-
- /* 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. */
- pj_ioqueue_poll( endpt->ioqueue, &timeout);
-}
-
-/*
- * Schedule timer.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry,
- const pj_time_val *delay )
-{
- PJ_LOG(5, (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 );
-}
-
-/*
- * Cancel the previously registered timer.
- */
-PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
- pj_timer_entry *entry )
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry));
- pj_timer_heap_cancel( endpt->timer_heap, entry );
-}
-
-/*
- * Create a new transaction.
- * Endpoint must then initialize the new transaction as either UAS or UAC, and
- * register it to the hash table.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx)
-{
- pj_pool_t *pool;
-
- PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL);
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()"));
-
- /* Request one pool for the transaction. Mutex is locked there. */
- pool = pjsip_endpt_create_pool(endpt, "ptsx%p",
- PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
- if (pool == NULL) {
- return PJ_ENOMEM;
- }
-
- /* Create the transaction. */
- return pjsip_tsx_create(pool, endpt, p_tsx);
-}
-
-/*
- * Register the transaction to the endpoint.
- * This will put the transaction to the transaction hash table. Before calling
- * this function, the transaction must be INITIALIZED as either UAS or UAC, so
- * that the transaction key is built.
- */
-PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
- pjsip_transaction *tsx)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
-
- pj_assert(tsx->transaction_key.slen != 0);
- //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
-
- /* Lock hash table mutex. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Register the transaction to the hash table. */
- pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
- tsx->transaction_key.slen, tsx);
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-}
-
-/*
- * Find transaction by the key.
- */
-PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
- const pj_str_t *key )
-{
- pjsip_transaction *tsx;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()"));
-
- /* Start lock mutex in the endpoint. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Find the transaction in the hash table. */
- tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-
- return tsx;
-}
-
-/*
- * Create key.
- */
-static void rdata_create_key( pjsip_rx_data *rdata)
-{
- pjsip_role_e role;
- if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
- role = PJSIP_ROLE_UAS;
- } else {
- role = PJSIP_ROLE_UAC;
- }
- pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role,
- &rdata->msg_info.cseq->method, rdata);
-}
-
-/*
- * This is the callback that is called by the transport manager when it
- * receives a message from the network.
- */
-static void endpt_transport_callback( pjsip_endpoint *endpt,
- pj_status_t status,
- pjsip_rx_data *rdata )
-{
- pjsip_msg *msg = rdata->msg_info.msg;
- pjsip_transaction *tsx;
- pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
-
- PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
-
- if (status != PJ_SUCCESS) {
- const char *src_addr = pj_inet_ntoa(rdata->pkt_info.addr.sin_addr);
- int port = pj_ntohs(rdata->pkt_info.addr.sin_port);
- PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status,
- "Src.addr=%s:%d, packet:--\n"
- "%s\n"
- "-- end of packet. Error",
- src_addr, port, rdata->msg_info.msg_buf));
- return;
- }
-
- /* 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_sockaddr_in *addr;
- const char *addr_addr;
- int port = rdata->msg_info.via->sent_by.port;
- pj_bool_t mismatch = PJ_FALSE;
- if (port == 0) {
- int type;
- type = rdata->tp_info.transport->type;
- port = pjsip_transport_get_default_port_for_type(type);
- }
- addr = &rdata->tp_info.transport->public_addr;
- addr_addr = pj_inet_ntoa(addr->sin_addr);
- if (pj_strcmp2(&rdata->msg_info.via->sent_by.host, addr_addr) != 0)
- mismatch = PJ_TRUE;
- else if (port != pj_ntohs(addr->sin_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 != pj_sockaddr_in_get_port(addr))
- mismatch = PJ_TRUE;
- else {
- PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by"
- " but the rport parameter is correct",
- rdata));
- }
- }
-
- if (mismatch) {
- pjsip_event e;
-
- PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
- endpt_do_event( endpt, &e );
- return;
- }
- }
-
- /* Create key for transaction lookup. */
- rdata_create_key( rdata);
-
- /* Find the transaction for the received message. */
- PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s",
- rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr));
-
- /* Start lock mutex in the endpoint. */
- pj_mutex_lock(endpt->tsx_table_mutex);
-
- /* Find the transaction in the hash table. */
- tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen );
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->tsx_table_mutex);
-
- /* If the transaction is not found... */
- if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
-
- /*
- * For response message, discard the message, except if the response is
- * an 2xx class response to INVITE, which in this case it must be
- * passed to TU to be acked.
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
-
- /* Inform TU about the 200 message, only if it's INVITE. */
- if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
- rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD)
- {
- pjsip_event e;
-
- /* Should not happen for UA. Tsx theoritically lives until
- * all responses are absorbed.
- */
- pj_assert(0);
-
- PJSIP_EVENT_INIT_RX_200_MSG(e, rdata);
- endpt_do_event( endpt, &e );
-
- } else {
- /* Just discard the response, inform TU. */
- pjsip_event e;
-
- PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata,
- PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST));
- endpt_do_event( endpt, &e );
- }
-
- /*
- * For non-ACK request message, create a new transaction.
- */
- } else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
-
- pj_status_t status;
-
- /* Create transaction, mutex is locked there. */
- status = pjsip_endpt_create_tsx(endpt, &tsx);
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create transaction"));
- return;
- }
-
- /* Initialize transaction as UAS. */
- pjsip_tsx_init_uas( tsx, rdata );
-
- /* Register transaction, mutex is locked there. */
- pjsip_endpt_register_tsx( endpt, tsx );
-
- a_new_transaction_just_been_created = PJ_TRUE;
- }
- }
-
- /* If transaction is found (or newly created), pass the message.
- * Otherwise if it's an ACK request, pass directly to TU.
- */
- if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
- /* Dispatch message to transaction. */
- pjsip_tsx_on_rx_msg( tsx, rdata );
-
- } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
- /*
- * This is an ACK message, but the INVITE transaction could not
- * be found (possibly because the branch parameter in Via in ACK msg
- * is different than the branch in original INVITE). This happens with
- * SER!
- */
- pjsip_event event;
-
- PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata);
- endpt_do_event( endpt, &event );
- }
-
- /*
- * If a new request message has just been receieved, but no modules
- * seem to be able to handle the request message, then terminate the
- * transaction.
- *
- * Ideally for cases like "unsupported method", we should be able to
- * answer the request statelessly. But we can not do that since the
- * endpoint shoule be able to be used as both user agent and proxy stack,
- * and a proxy stack should be able to handle arbitrary methods.
- */
- if (a_new_transaction_just_been_created && tsx->status_code < 100) {
- /* Certainly no modules has sent any response message.
- * Check that any modules has attached a module data.
- */
- int i;
- for (i=0; i<PJSIP_MAX_MODULE; ++i) {
- if (tsx->module_data[i] != NULL) {
- break;
- }
- }
- if (i == PJSIP_MAX_MODULE) {
- /* No modules have attached itself to the transaction.
- * Terminate the transaction with 501/Not Implemented.
- */
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
- status = pjsip_endpt_create_response(endpt, rdata, 200,
- &tdata);
- } else {
- status = pjsip_endpt_create_response(endpt, rdata,
- PJSIP_SC_METHOD_NOT_ALLOWED,
- &tdata);
- }
-
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create response"));
- return;
- }
-
- if (endpt->allow_hdr) {
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
- }
- pjsip_tsx_on_tx_msg( tsx, tdata );
-
- } else {
- /*
- * If a module has registered itself in the transaction but it
- * hasn't responded the request, chances are the module wouldn't
- * respond to the request at all. We terminate the request here
- * with 500/Internal Server Error, to be safe.
- */
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata);
- if (status != PJ_SUCCESS) {
- PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create response"));
- return;
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
- }
-}
-
-/*
- * Create transmit data buffer.
- */
-PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
- pjsip_tx_data **p_tdata)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tdata()"));
- return pjsip_tx_data_create(endpt->transport_mgr, p_tdata);
-}
-
-/*
- * Resolve
- */
-PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
- pj_pool_t *pool,
- pjsip_host_port *target,
- void *token,
- pjsip_resolver_callback *cb)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_resolve()"));
- 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_alloc_transport( pjsip_endpoint *endpt,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport)
-{
- PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()"));
- return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type, remote,
- p_transport);
-}
-
-
-/*
- * Report error.
- */
-PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt,
- const char *sender,
- pj_status_t error_code,
- const char *format,
- ... )
-{
- char newformat[256];
- int len;
- va_list marker;
-
- va_start(marker, format);
-
- PJ_UNUSED_ARG(endpt);
-
- len = pj_native_strlen(format);
- if (len < sizeof(newformat)-30) {
- pj_str_t errstr;
-
- pj_native_strcpy(newformat, format);
- pj_snprintf(newformat+len, sizeof(newformat)-len-1,
- ": [err %d] ", error_code);
- len += pj_native_strlen(newformat+len);
-
- errstr = pjsip_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);
-}
-
-
-/*
- * Dump endpoint.
- */
-PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- unsigned count;
-
- 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. */
- (*endpt->pf->dump_status)(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)));
-
- /* Transaction tables. */
- count = pj_hash_count(endpt->tsx_table);
- PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count));
-
- if (count && detail) {
- pj_hash_iterator_t it_val;
- pj_hash_iterator_t *it;
- pj_time_val now;
-
- PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:"));
-
- pj_gettimeofday(&now);
- it = pj_hash_first(endpt->tsx_table, &it_val);
-
- while (it != NULL) {
- int timeout_diff;
-
- /* Get the transaction. No need to lock transaction's mutex
- * since we already hold endpoint mutex, so that no transactions
- * will be deleted.
- */
- pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
-
- const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
-
- if (tsx->timeout_timer._timer_id != -1) {
- if (tsx->timeout_timer._timer_value.sec > now.sec) {
- timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
- } else {
- timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
- timeout_diff = 0 - timeout_diff;
- }
- } else {
- timeout_diff = -1;
- }
-
- PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds",
- tsx->obj_name, role,
- tsx->method.name.slen, tsx->method.name.ptr,
- tsx->cseq,
- pjsip_tsx_state_str(tsx->state),
- timeout_diff));
-
- it = pj_hash_next(endpt->tsx_table, it);
- }
- }
-
- /* Transports.
- */
- pjsip_tpmgr_dump_transports( endpt->transport_mgr );
-
- /* Timer. */
- PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries",
- pj_timer_heap_count(endpt->timer_heap)));
-
- /* Unlock mutex. */
- pj_mutex_unlock(endpt->mutex);
-#else
- PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled."));
-#endif
-}
-
+/* $Id$ */ +/* + * Copyright (C) 2003-2006 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 "endpoint" + +#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; + + /** Transaction table. */ + pj_hash_table_t *tsx_table; + + /** Mutex for transaction table. */ + pj_mutex_t *tsx_table_mutex; + + /** Timer heap. */ + pj_timer_heap_t *timer_heap; + + /** Transport manager. */ + pjsip_tpmgr *transport_mgr; + + /** Ioqueue. */ + pj_ioqueue_t *ioqueue; + + /** DNS Resolver. */ + pjsip_resolver_t *resolver; + + /** Number of modules registered. */ + pj_uint32_t mod_count; + + /** Modules. */ + pjsip_module *modules[PJSIP_MAX_MODULE]; + + /** Number of supported methods. */ + unsigned method_cnt; + + /** Array of supported methods. */ + const pjsip_method *methods[MAX_METHODS]; + + /** Allow header. */ + pjsip_allow_hdr *allow_hdr; + + /** Route header list. */ + pjsip_route_hdr route_hdr_list; + + /** Additional request headers. */ + pjsip_hdr req_hdr; +}; + + + +/* + * Prototypes. + */ +static void endpt_transport_callback(pjsip_endpoint*, + pj_status_t, pjsip_rx_data*); + + +/* + * 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); +} + + +/* + * Initialize modules. + */ +static pj_status_t init_modules( pjsip_endpoint *endpt ) +{ + pj_status_t status; + unsigned i; + //pj_str_t str_COMMA = { ", ", 2 }; + extern pjsip_module aux_tsx_module; + + PJ_LOG(5, (THIS_FILE, "init_modules()")); + + /* Load static modules. */ + endpt->mod_count = PJSIP_MAX_MODULE; + status = register_static_modules( &endpt->mod_count, endpt->modules ); + if (status != 0) { + return status; + } + + /* Add mini aux module. */ + endpt->modules[endpt->mod_count++] = &aux_tsx_module; + + /* Load dynamic modules. */ + // Not supported yet! + + /* Sort modules on the priority. */ + for (i=endpt->mod_count-1; i>0; --i) { + pj_uint32_t max = 0; + unsigned j; + for (j=1; j<=i; ++j) { + if (endpt->modules[j]->priority > endpt->modules[max]->priority) + max = j; + } + if (max != i) { + pjsip_module *temp = endpt->modules[max]; + endpt->modules[max] = endpt->modules[i]; + endpt->modules[i] = temp; + } + } + + /* Initialize each module. */ + for (i=0; i < endpt->mod_count; ++i) { + int j; + + pjsip_module *mod = endpt->modules[i]; + if (mod->init_module) { + status = mod->init_module(endpt, mod, i); + if (status != 0) { + return status; + } + } + + /* Collect all supported methods from modules. */ + for (j=0; j<mod->method_cnt; ++j) { + unsigned k; + for (k=0; k<endpt->method_cnt; ++k) { + if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0) + break; + } + if (k == endpt->method_cnt) { + if (endpt->method_cnt < MAX_METHODS) { + endpt->methods[endpt->method_cnt++] = mod->methods[j]; + } else { + PJ_LOG(1,(THIS_FILE, "Too many methods")); + return -1; + } + } + } + } + + /* Create Allow header. */ + endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool ); + endpt->allow_hdr->count = endpt->method_cnt; + for (i=0; i<endpt->method_cnt; ++i) { + endpt->allow_hdr->values[i] = endpt->methods[i]->name; + } + + /* Start each module. */ + for (i=0; i < endpt->mod_count; ++i) { + pjsip_module *mod = endpt->modules[i]; + if (mod->start_module) { + status = mod->start_module(mod); + if (status != 0) { + return status; + } + } + } + + /* Done. */ + return 0; +} + +/* + * Unregister the transaction from the hash table, and destroy the resources + * from the transaction. + */ +PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name)); + + pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED); + + /* No need to lock transaction. + * This function typically is called from the transaction callback, which + * means that transaction mutex is being held. + */ + pj_assert( pj_mutex_is_locked(tsx->mutex) ); + + /* Lock endpoint. */ + pj_mutex_lock( endpt->tsx_table_mutex ); + + /* Unregister from the hash table. */ + pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr, + tsx->transaction_key.slen, NULL); + + /* Unlock endpoint mutex. */ + pj_mutex_unlock( endpt->tsx_table_mutex ); + + /* Destroy transaction mutex. */ + pj_mutex_destroy( tsx->mutex ); + + /* Release the pool for the transaction. */ + pj_pool_release(tsx->pool); + + PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx)); +} + + +/* + * Receive transaction events from transactions and dispatch them to the + * modules. + */ +static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt) +{ + unsigned i; + + /* Dispatch event to modules. */ + for (i=0; i<endpt->mod_count; ++i) { + pjsip_module *mod = endpt->modules[i]; + if (mod && mod->tsx_handler) { + mod->tsx_handler( mod, evt ); + } + } + + /* Destroy transaction if it is terminated. */ + if (evt->type == PJSIP_EVENT_TSX_STATE && + evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED) + { + /* No need to lock mutex. Mutex is locked inside the destroy function */ + pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx ); + } +} + +/* + * Receive transaction events from transactions and put in the event queue + * to be processed later. + */ +void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ) +{ + // Need to protect this with try/catch? + endpt_do_event(endpt, evt); +} + +/* + * Get "Allow" header. + */ +PJ_DEF(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ) +{ + return endpt->allow_hdr; +} + +/* + * 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; +} + +PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt, + int url_cnt, const pj_str_t url[]) +{ + int i; + pjsip_route_hdr *hdr; + pj_str_t str_ROUTE = { "Route", 5 }; + + /* Lock endpoint mutex. */ + pj_mutex_lock(endpt->mutex); + + pj_list_init(&endpt->route_hdr_list); + + for (i=0; i<url_cnt; ++i) { + int len = url[i].slen; + char *dup = pj_pool_alloc(endpt->pool, len + 1); + pj_memcpy(dup, url[i].ptr, len); + dup[len] = '\0'; + + hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL); + if (!hdr) { + pj_mutex_unlock(endpt->mutex); + PJ_LOG(4,(THIS_FILE, "Invalid URL %s in proxy URL", dup)); + return -1; + } + + pj_assert(hdr->type == PJSIP_H_ROUTE); + pj_list_insert_before(&endpt->route_hdr_list, hdr); + } + + /* Unlock endpoint mutex. */ + pj_mutex_unlock(endpt->mutex); + + return 0; +} + +/* + * Get "Route" header list. + */ +PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt ) +{ + return &endpt->route_hdr_list; +} + + +/* + * 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_forwards_hdr *mf_hdr; + pj_lock_t *lock = NULL; + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()")); + + *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_calloc(pool, 1, sizeof(*endpt)); + endpt->pool = pool; + endpt->pf = pf; + + /* 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 mutex for the transaction table. */ + status = pj_mutex_create_recursive( endpt->pool, "mtbl%p", + &endpt->tsx_table_mutex); + if (status != PJ_SUCCESS) { + goto on_error; + } + + /* Create hash table for transaction. */ + endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT ); + if (!endpt->tsx_table) { + status = PJ_ENOMEM; + 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_transport_callback, + &endpt->transport_mgr); + if (status != PJ_SUCCESS) { + goto on_error; + } + + /* Create asynchronous DNS resolver. */ + endpt->resolver = pjsip_resolver_create(endpt->pool); + if (!endpt->resolver) { + PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error creating resolver")); + goto on_error; + } + + /* Initialize TLS ID for transaction lock. */ + status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id); + if (status != PJ_SUCCESS) { + goto on_error; + } + pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL); + + /* Initialize request headers. */ + pj_list_init(&endpt->req_hdr); + + /* Initialist "Route" header list. */ + pj_list_init(&endpt->route_hdr_list); + + /* Add "Max-Forwards" for request header. */ + mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool); + mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE; + pj_list_insert_before( &endpt->req_hdr, mf_hdr); + + /* Load and init modules. */ + status = init_modules(endpt); + if (status != PJ_SUCCESS) { + PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()")); + return status; + } + + /* Done. */ + *p_endpt = endpt; + return status; + +on_error: + if (endpt->transport_mgr) { + pjsip_tpmgr_destroy(endpt->transport_mgr); + endpt->transport_mgr = NULL; + } + if (endpt->mutex) { + pj_mutex_destroy(endpt->mutex); + endpt->mutex = NULL; + } + if (endpt->tsx_table_mutex) { + pj_mutex_destroy(endpt->tsx_table_mutex); + endpt->tsx_table_mutex = NULL; + } + pj_pool_release( endpt->pool ); + + PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init() failed")); + return status; +} + +/* + * Destroy endpoint. + */ +PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy()")); + + /* Shutdown and destroy all transports. */ + pjsip_tpmgr_destroy(endpt->transport_mgr); + + /* Delete endpoint mutex. */ + pj_mutex_destroy(endpt->mutex); + + /* Delete transaction table mutex. */ + pj_mutex_destroy(endpt->tsx_table_mutex); + + /* Finally destroy pool. */ + pj_pool_release(endpt->pool); +} + +/* + * 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; + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_pool()")); + + /* Lock endpoint mutex. */ + pj_mutex_lock(endpt->mutex); + + /* Create pool */ + pool = pj_pool_create( endpt->pf, pool_name, + initial, increment, &pool_callback); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->mutex); + + if (pool) { + PJ_LOG(5, (THIS_FILE, " pool %s created", pj_pool_getobjname(pool))); + } else { + 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_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool ) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool))); + + pj_mutex_lock(endpt->mutex); + pj_pool_release( pool ); + pj_mutex_unlock(endpt->mutex); +} + +/* + * Handle events. + */ +PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, + const pj_time_val *max_timeout) +{ + /* timeout is 'out' var. This just to make compiler happy. */ + pj_time_val timeout = { 0, 0}; + + PJ_LOG(5, (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; + pj_timer_heap_poll( endpt->timer_heap, &timeout ); + + /* 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. */ + pj_ioqueue_poll( endpt->ioqueue, &timeout); +} + +/* + * Schedule timer. + */ +PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry, + const pj_time_val *delay ) +{ + PJ_LOG(5, (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 ); +} + +/* + * Cancel the previously registered timer. + */ +PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry ) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry)); + pj_timer_heap_cancel( endpt->timer_heap, entry ); +} + +/* + * Create a new transaction. + * Endpoint must then initialize the new transaction as either UAS or UAC, and + * register it to the hash table. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt, + pjsip_transaction **p_tsx) +{ + pj_pool_t *pool; + + PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL); + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()")); + + /* Request one pool for the transaction. Mutex is locked there. */ + pool = pjsip_endpt_create_pool(endpt, "ptsx%p", + PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX); + if (pool == NULL) { + return PJ_ENOMEM; + } + + /* Create the transaction. */ + return pjsip_tsx_create(pool, endpt, p_tsx); +} + +/* + * Register the transaction to the endpoint. + * This will put the transaction to the transaction hash table. Before calling + * this function, the transaction must be INITIALIZED as either UAS or UAC, so + * that the transaction key is built. + */ +PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name)); + + pj_assert(tsx->transaction_key.slen != 0); + //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL); + + /* Lock hash table mutex. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Register the transaction to the hash table. */ + pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr, + tsx->transaction_key.slen, tsx); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); +} + +/* + * Find transaction by the key. + */ +PJ_DEF(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, + const pj_str_t *key ) +{ + pjsip_transaction *tsx; + + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()")); + + /* Start lock mutex in the endpoint. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Find the transaction in the hash table. */ + tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen ); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); + + return tsx; +} + +/* + * Create key. + */ +static void rdata_create_key( pjsip_rx_data *rdata) +{ + pjsip_role_e role; + if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { + role = PJSIP_ROLE_UAS; + } else { + role = PJSIP_ROLE_UAC; + } + pjsip_tsx_create_key(rdata->tp_info.pool, &rdata->endpt_info.key, role, + &rdata->msg_info.cseq->method, rdata); +} + +/* + * This is the callback that is called by the transport manager when it + * receives a message from the network. + */ +static void endpt_transport_callback( pjsip_endpoint *endpt, + pj_status_t status, + pjsip_rx_data *rdata ) +{ + pjsip_msg *msg = rdata->msg_info.msg; + pjsip_transaction *tsx; + pj_bool_t a_new_transaction_just_been_created = PJ_FALSE; + + PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata)); + + if (status != PJ_SUCCESS) { + const char *src_addr = pj_inet_ntoa(rdata->pkt_info.addr.sin_addr); + int port = pj_ntohs(rdata->pkt_info.addr.sin_port); + PJSIP_ENDPT_LOG_ERROR((endpt, "transport", status, + "Src.addr=%s:%d, packet:--\n" + "%s\n" + "-- end of packet. Error", + src_addr, port, rdata->msg_info.msg_buf)); + return; + } + + /* 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_sockaddr_in *addr; + const char *addr_addr; + int port = rdata->msg_info.via->sent_by.port; + pj_bool_t mismatch = PJ_FALSE; + if (port == 0) { + int type; + type = rdata->tp_info.transport->type; + port = pjsip_transport_get_default_port_for_type(type); + } + addr = &rdata->tp_info.transport->public_addr; + addr_addr = pj_inet_ntoa(addr->sin_addr); + if (pj_strcmp2(&rdata->msg_info.via->sent_by.host, addr_addr) != 0) + mismatch = PJ_TRUE; + else if (port != pj_ntohs(addr->sin_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 != pj_sockaddr_in_get_port(addr)) + mismatch = PJ_TRUE; + else { + PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by" + " but the rport parameter is correct", + rdata)); + } + } + + if (mismatch) { + pjsip_event e; + + PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA); + endpt_do_event( endpt, &e ); + return; + } + } + + /* Create key for transaction lookup. */ + rdata_create_key( rdata); + + /* Find the transaction for the received message. */ + PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s", + rdata->endpt_info.key.slen, rdata->endpt_info.key.ptr)); + + /* Start lock mutex in the endpoint. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Find the transaction in the hash table. */ + tsx = pj_hash_get( endpt->tsx_table, rdata->endpt_info.key.ptr, rdata->endpt_info.key.slen ); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); + + /* If the transaction is not found... */ + if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { + + /* + * For response message, discard the message, except if the response is + * an 2xx class response to INVITE, which in this case it must be + * passed to TU to be acked. + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + + /* Inform TU about the 200 message, only if it's INVITE. */ + if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) && + rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) + { + pjsip_event e; + + /* Should not happen for UA. Tsx theoritically lives until + * all responses are absorbed. + */ + pj_assert(0); + + PJSIP_EVENT_INIT_RX_200_MSG(e, rdata); + endpt_do_event( endpt, &e ); + + } else { + /* Just discard the response, inform TU. */ + pjsip_event e; + + PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, + PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST)); + endpt_do_event( endpt, &e ); + } + + /* + * For non-ACK request message, create a new transaction. + */ + } else if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { + + pj_status_t status; + + /* Create transaction, mutex is locked there. */ + status = pjsip_endpt_create_tsx(endpt, &tsx); + if (status != PJ_SUCCESS) { + PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status, + "Unable to create transaction")); + return; + } + + /* Initialize transaction as UAS. */ + pjsip_tsx_init_uas( tsx, rdata ); + + /* Register transaction, mutex is locked there. */ + pjsip_endpt_register_tsx( endpt, tsx ); + + a_new_transaction_just_been_created = PJ_TRUE; + } + } + + /* If transaction is found (or newly created), pass the message. + * Otherwise if it's an ACK request, pass directly to TU. + */ + if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) { + /* Dispatch message to transaction. */ + pjsip_tsx_on_rx_msg( tsx, rdata ); + + } else if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) { + /* + * This is an ACK message, but the INVITE transaction could not + * be found (possibly because the branch parameter in Via in ACK msg + * is different than the branch in original INVITE). This happens with + * SER! + */ + pjsip_event event; + + PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata); + endpt_do_event( endpt, &event ); + } + + /* + * If a new request message has just been receieved, but no modules + * seem to be able to handle the request message, then terminate the + * transaction. + * + * Ideally for cases like "unsupported method", we should be able to + * answer the request statelessly. But we can not do that since the + * endpoint shoule be able to be used as both user agent and proxy stack, + * and a proxy stack should be able to handle arbitrary methods. + */ + if (a_new_transaction_just_been_created && tsx->status_code < 100) { + /* Certainly no modules has sent any response message. + * Check that any modules has attached a module data. + */ + int i; + for (i=0; i<PJSIP_MAX_MODULE; ++i) { + if (tsx->module_data[i] != NULL) { + break; + } + } + if (i == PJSIP_MAX_MODULE) { + /* No modules have attached itself to the transaction. + * Terminate the transaction with 501/Not Implemented. + */ + pjsip_tx_data *tdata; + pj_status_t status; + + if (tsx->method.id == PJSIP_OPTIONS_METHOD) { + status = pjsip_endpt_create_response(endpt, rdata, 200, + &tdata); + } else { + status = pjsip_endpt_create_response(endpt, rdata, + PJSIP_SC_METHOD_NOT_ALLOWED, + &tdata); + } + + if (status != PJ_SUCCESS) { + PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status, + "Unable to create response")); + return; + } + + if (endpt->allow_hdr) { + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr)); + } + pjsip_tsx_on_tx_msg( tsx, tdata ); + + } else { + /* + * If a module has registered itself in the transaction but it + * hasn't responded the request, chances are the module wouldn't + * respond to the request at all. We terminate the request here + * with 500/Internal Server Error, to be safe. + */ + pjsip_tx_data *tdata; + pj_status_t status; + + status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata); + if (status != PJ_SUCCESS) { + PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status, + "Unable to create response")); + return; + } + + pjsip_tsx_on_tx_msg(tsx, tdata); + } + } +} + +/* + * Create transmit data buffer. + */ +PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt, + pjsip_tx_data **p_tdata) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tdata()")); + return pjsip_tx_data_create(endpt->transport_mgr, p_tdata); +} + +/* + * Resolve + */ +PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_resolve()")); + 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_alloc_transport( pjsip_endpoint *endpt, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + pjsip_transport **p_transport) +{ + PJ_LOG(5, (THIS_FILE, "pjsip_endpt_alloc_transport()")); + return pjsip_tpmgr_alloc_transport( endpt->transport_mgr, type, remote, + p_transport); +} + + +/* + * Report error. + */ +PJ_DEF(void) pjsip_endpt_log_error( pjsip_endpoint *endpt, + const char *sender, + pj_status_t error_code, + const char *format, + ... ) +{ + char newformat[256]; + int len; + va_list marker; + + va_start(marker, format); + + PJ_UNUSED_ARG(endpt); + + len = pj_native_strlen(format); + if (len < sizeof(newformat)-30) { + pj_str_t errstr; + + pj_native_strcpy(newformat, format); + pj_snprintf(newformat+len, sizeof(newformat)-len-1, + ": [err %d] ", error_code); + len += pj_native_strlen(newformat+len); + + errstr = pjsip_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); +} + + +/* + * Dump endpoint. + */ +PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + unsigned count; + + 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. */ + (*endpt->pf->dump_status)(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))); + + /* Transaction tables. */ + count = pj_hash_count(endpt->tsx_table); + PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count)); + + if (count && detail) { + pj_hash_iterator_t it_val; + pj_hash_iterator_t *it; + pj_time_val now; + + PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:")); + + pj_gettimeofday(&now); + it = pj_hash_first(endpt->tsx_table, &it_val); + + while (it != NULL) { + int timeout_diff; + + /* Get the transaction. No need to lock transaction's mutex + * since we already hold endpoint mutex, so that no transactions + * will be deleted. + */ + pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it); + + const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC"); + + if (tsx->timeout_timer._timer_id != -1) { + if (tsx->timeout_timer._timer_value.sec > now.sec) { + timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec; + } else { + timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec; + timeout_diff = 0 - timeout_diff; + } + } else { + timeout_diff = -1; + } + + PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds", + tsx->obj_name, role, + tsx->method.name.slen, tsx->method.name.ptr, + tsx->cseq, + pjsip_tsx_state_str(tsx->state), + timeout_diff)); + + it = pj_hash_next(endpt->tsx_table, it); + } + } + + /* Transports. + */ + pjsip_tpmgr_dump_transports( endpt->transport_mgr ); + + /* Timer. */ + PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries", + pj_timer_heap_count(endpt->timer_heap))); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->mutex); +#else + PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled.")); +#endif +} + |