diff options
Diffstat (limited to 'pjsip/src/pjsip/sip_transport.c')
-rw-r--r-- | pjsip/src/pjsip/sip_transport.c | 1686 |
1 files changed, 843 insertions, 843 deletions
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index a32fb683..de1d13d9 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1,843 +1,843 @@ -/* $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_transport.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_private.h>
-#include <pjsip/sip_errno.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/ioqueue.h>
-#include <pj/hash.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/assert.h>
-#include <pj/lock.h>
-
-
-#define THIS_FILE "transport"
-
-/*
- * Transport manager.
- */
-struct pjsip_tpmgr
-{
- pj_hash_table_t *table;
- pj_lock_t *lock;
- pjsip_endpoint *endpt;
- pjsip_tpfactory factory_list;
- void (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*);
-};
-
-
-
-/*****************************************************************************
- *
- * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
- *
- *****************************************************************************/
-
-/*
- * Transport names.
- */
-const struct
-{
- pjsip_transport_type_e type;
- pj_uint16_t port;
- pj_str_t name;
- unsigned flag;
-} transport_names[] =
-{
- { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}, 0},
- { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM},
- { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE},
- { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE},
- { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE}
-};
-
-
-/*
- * Get transport type from name.
- */
-PJ_DEF(pjsip_transport_type_e)
-pjsip_transport_get_type_from_name(const pj_str_t *name)
-{
- unsigned i;
-
- for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
- if (pj_stricmp(name, &transport_names[i].name) == 0) {
- return transport_names[i].type;
- }
- }
-
- pj_assert(!"Invalid transport name");
- return PJSIP_TRANSPORT_UNSPECIFIED;
-}
-
-
-/*
- * Get the transport type for the specified flags.
- */
-PJ_DEF(pjsip_transport_type_e)
-pjsip_transport_get_type_from_flag(unsigned flag)
-{
- unsigned i;
-
- for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
- if (transport_names[i].flag == flag) {
- return transport_names[i].type;
- }
- }
-
- pj_assert(!"Invalid transport type");
- return PJSIP_TRANSPORT_UNSPECIFIED;
-}
-
-PJ_DEF(unsigned)
-pjsip_transport_get_flag_from_type( pjsip_transport_type_e type )
-{
- PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 0);
- return transport_names[type].flag;
-}
-
-/*
- * Get the default SIP port number for the specified type.
- */
-PJ_DEF(int)
-pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
-{
- PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 5060);
- return transport_names[type].port;
-}
-
-
-/*****************************************************************************
- *
- * TRANSMIT DATA BUFFER MANIPULATION.
- *
- *****************************************************************************/
-
-/*
- * Create new transmit buffer.
- */
-PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
- pjsip_tx_data **p_tdata )
-{
- pj_pool_t *pool;
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
-
- PJ_LOG(5, ("", "pjsip_tx_data_create"));
-
- pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p",
- PJSIP_POOL_LEN_TDATA,
- PJSIP_POOL_INC_TDATA );
- if (!pool)
- return PJ_ENOMEM;
-
- tdata = pj_pool_zalloc(pool, sizeof(pjsip_tx_data));
- tdata->pool = pool;
- tdata->mgr = mgr;
- pj_snprintf(tdata->obj_name, PJ_MAX_OBJ_NAME, "tdta%p", tdata);
-
- status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
- if (status != PJ_SUCCESS) {
- pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
- return status;
- }
-
- //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock);
- status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock);
- if (status != PJ_SUCCESS) {
- pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
- return status;
- }
-
- pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key));
-
- *p_tdata = tdata;
- return PJ_SUCCESS;
-}
-
-
-/*
- * Add reference to tx buffer.
- */
-PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
-{
- pj_atomic_inc(tdata->ref_cnt);
-}
-
-/*
- * Decrease transport data reference, destroy it when the reference count
- * reaches zero.
- */
-PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
-{
- pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
- if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
- PJ_LOG(5,(tdata->obj_name, "destroying txdata"));
- pj_atomic_destroy( tdata->ref_cnt );
- pj_lock_destroy( tdata->lock );
- pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
- }
-}
-
-/*
- * Invalidate the content of the print buffer to force the message to be
- * re-printed when sent.
- */
-PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
-{
- tdata->buf.cur = tdata->buf.start;
-}
-
-PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata )
-{
- return tdata->buf.cur != tdata->buf.start;
-}
-
-
-
-/*****************************************************************************
- *
- * TRANSPORT KEY
- *
- *****************************************************************************/
-
-/*
- * Transport key for indexing in the hash table.
- */
-typedef struct transport_key
-{
- pj_uint8_t type;
- pj_uint8_t zero;
- pj_uint16_t port;
- pj_uint32_t addr;
-} transport_key;
-
-
-/*****************************************************************************
- *
- * TRANSPORT
- *
- *****************************************************************************/
-
-static void transport_send_callback(pjsip_transport *transport,
- void *token,
- pj_ssize_t size)
-{
- pjsip_tx_data *tdata = token;
-
- PJ_UNUSED_ARG(transport);
-
- /* Mark pending off so that app can resend/reuse txdata from inside
- * the callback.
- */
- tdata->is_pending = 0;
-
- /* Call callback, if any. */
- if (tdata->cb) {
- (*tdata->cb)(tdata->token, tdata, size);
- }
-
- /* Decrement reference count. */
- pjsip_tx_data_dec_ref(tdata);
-}
-
-/*
- * Send a SIP message using the specified transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr,
- pjsip_tx_data *tdata,
- const pj_sockaddr_in *addr,
- void *token,
- void (*cb)(void *token,
- pjsip_tx_data *tdata,
- pj_ssize_t))
-{
- pj_status_t status;
-
- PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL);
-
- /* Is it currently being sent? */
- if (tdata->is_pending) {
- pj_assert(!"Invalid operation step!");
- return PJSIP_EPENDINGTX;
- }
-
- /* Allocate buffer if necessary. */
- if (tdata->buf.start == NULL) {
- tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN);
- tdata->buf.cur = tdata->buf.start;
- tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
- }
-
- /* Do we need to reprint? */
- if (!pjsip_tx_data_is_valid(tdata)) {
- pj_ssize_t size;
-
- size = pjsip_msg_print( tdata->msg, tdata->buf.start,
- tdata->buf.end - tdata->buf.start);
- if (size < 0) {
- return PJSIP_EMSGTOOLONG;
- }
- pj_assert(size != 0);
- tdata->buf.cur += size;
- tdata->buf.cur[size] = '\0';
- }
-
- /* Save callback data. */
- tdata->token = token;
- tdata->cb = cb;
-
- /* Add reference counter. */
- pjsip_tx_data_add_ref(tdata);
-
- /* Mark as pending. */
- tdata->is_pending = 1;
-
- /* Send to transport. */
- status = (*tr->send_msg)(tr, tdata, addr, (void*)tdata,
- &transport_send_callback);
-
- if (status != PJ_EPENDING) {
- tdata->is_pending = 0;
- pjsip_tx_data_dec_ref(tdata);
- }
-
- return status;
-}
-
-static void transport_idle_callback(pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
-{
- pjsip_transport *tp = entry->user_data;
- pj_assert(tp != NULL);
-
- PJ_UNUSED_ARG(timer_heap);
-
- entry->id = PJ_FALSE;
- pjsip_transport_unregister(tp->tpmgr, tp);
-}
-
-/*
- * Add ref.
- */
-PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp )
-{
- PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
-
- if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) {
- pj_lock_acquire(tp->tpmgr->lock);
- /* Verify again. */
- if (pj_atomic_get(tp->ref_cnt) == 1) {
- if (tp->idle_timer.id != PJ_FALSE) {
- pjsip_endpt_cancel_timer(tp->tpmgr->endpt, &tp->idle_timer);
- tp->idle_timer.id = PJ_FALSE;
- }
- }
- pj_lock_release(tp->tpmgr->lock);
- }
-
- return PJ_SUCCESS;
-}
-
-/*
- * Dec ref.
- */
-PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp )
-{
- PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL);
-
- pj_assert(pj_atomic_get(tp->ref_cnt) > 0);
-
- if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) {
- pj_lock_acquire(tp->tpmgr->lock);
- /* Verify again. */
- if (pj_atomic_get(tp->ref_cnt) == 0) {
- pj_time_val delay = { PJSIP_TRANSPORT_IDLE_TIME, 0 };
-
- pj_assert(tp->idle_timer.id == 0);
- tp->idle_timer.id = PJ_TRUE;
- pjsip_endpt_schedule_timer(tp->tpmgr->endpt, &tp->idle_timer,
- &delay);
- }
- pj_lock_release(tp->tpmgr->lock);
- }
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Register a transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
- pjsip_transport *tp )
-{
- transport_key key;
-
- /* Init. */
- tp->tpmgr = mgr;
- pj_memset(&tp->idle_timer, 0, sizeof(tp->idle_timer));
- tp->idle_timer.user_data = tp;
- tp->idle_timer.cb = &transport_idle_callback;
-
- /*
- * Register to hash table.
- */
- key.type = (pj_uint8_t)tp->type;
- key.zero = 0;
- key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
- key.port = pj_ntohs(tp->rem_addr.sin_port);
-
- pj_lock_acquire(mgr->lock);
- pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), tp);
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Unregister transport.
- */
-PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr,
- pjsip_transport *tp)
-{
- transport_key key;
-
- PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY);
-
- pj_lock_acquire(tp->lock);
- pj_lock_acquire(mgr->lock);
-
- /*
- * Unregister timer, if any.
- */
- pj_assert(tp->idle_timer.id == PJ_FALSE);
- if (tp->idle_timer.id != PJ_FALSE) {
- pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer);
- tp->idle_timer.id = PJ_FALSE;
- }
-
- /*
- * Unregister from hash table.
- */
- key.type = (pj_uint8_t)tp->type;
- key.zero = 0;
- key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr);
- key.port = pj_ntohs(tp->rem_addr.sin_port);
-
- pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), NULL);
-
- pj_lock_release(mgr->lock);
-
- /* Destroy. */
- return tp->destroy(tp);
-}
-
-
-
-/*****************************************************************************
- *
- * TRANSPORT FACTORY
- *
- *****************************************************************************/
-
-
-PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf)
-{
- pjsip_tpfactory *p;
- pj_status_t status;
-
- pj_lock_acquire(mgr->lock);
-
- /* Check that no factory with the same type has been registered. */
- status = PJ_SUCCESS;
- for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) {
- if (p->type == tpf->type) {
- status = PJSIP_ETYPEEXISTS;
- break;
- }
- if (p == tpf) {
- status = PJ_EEXISTS;
- break;
- }
- }
-
- if (status != PJ_SUCCESS) {
- pj_lock_release(mgr->lock);
- return status;
- }
-
- pj_list_insert_before(&mgr->factory_list, tpf);
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/**
- * Unregister factory.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr,
- pjsip_tpfactory *tpf)
-{
- pj_lock_acquire(mgr->lock);
-
- pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf);
- pj_list_erase(tpf);
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/*****************************************************************************
- *
- * TRANSPORT MANAGER
- *
- *****************************************************************************/
-
-/*
- * Create a new transport manager.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
- pjsip_endpoint *endpt,
- void (*cb)(pjsip_endpoint*,
- pj_status_t,
- pjsip_rx_data *),
- pjsip_tpmgr **p_mgr)
-{
- pjsip_tpmgr *mgr;
- pj_status_t status;
-
- PJ_ASSERT_RETURN(pool && endpt && cb && p_mgr, PJ_EINVAL);
-
- PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_create()"));
-
- mgr = pj_pool_zalloc(pool, sizeof(*mgr));
- mgr->endpt = endpt;
- mgr->msg_cb = cb;
- pj_list_init(&mgr->factory_list);
-
- mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE);
- if (!mgr->table)
- return PJ_ENOMEM;
-
- status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock);
- if (status != PJ_SUCCESS)
- return status;
-
- *p_mgr = mgr;
- return PJ_SUCCESS;
-}
-
-/*
- * pjsip_tpmgr_destroy()
- *
- * Destroy transport manager.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr )
-{
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
- PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()"));
-
- pj_lock_acquire(mgr->lock);
-
- itr = pj_hash_first(mgr->table, &itr_val);
- while (itr != NULL) {
- pj_hash_iterator_t *next;
- pjsip_transport *transport;
-
- transport = pj_hash_this(mgr->table, itr);
-
- next = pj_hash_next(mgr->table, itr);
-
- pj_atomic_set(transport->ref_cnt, 0);
- pjsip_transport_unregister(mgr, transport);
-
- itr = next;
- }
-
- pj_lock_release(mgr->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * pjsip_tpmgr_receive_packet()
- *
- * Called by tranports when they receive a new packet.
- */
-PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
- pjsip_rx_data *rdata)
-{
- pjsip_transport *tr = rdata->tp_info.transport;
- pj_str_t s;
-
- char *current_pkt;
- pj_size_t remaining_len;
- pj_size_t total_processed = 0;
-
- /* Check size. */
- pj_assert(rdata->pkt_info.len > 0);
- if (rdata->pkt_info.len <= 0)
- return -1;
-
- current_pkt = rdata->pkt_info.packet;
- remaining_len = rdata->pkt_info.len;
-
- /* Must NULL terminate buffer. This is the requirement of the
- * parser etc.
- */
- current_pkt[remaining_len] = '\0';
-
- /* Process all message fragments. */
- while (total_processed < remaining_len) {
-
- pjsip_msg *msg;
- pj_size_t msg_fragment_size = 0;
-
- /* Initialize default fragment size. */
- msg_fragment_size = remaining_len;
-
- /* Null terminate packet. */
-
- /* Clear and init msg_info in rdata.
- * Endpoint might inspect the values there when we call the callback
- * to report some errors.
- */
- pj_memset(&rdata->msg_info, 0, sizeof(rdata->msg_info));
- pj_list_init(&rdata->msg_info.parse_err);
- rdata->msg_info.msg_buf = current_pkt;
- rdata->msg_info.len = remaining_len;
-
- /* For TCP transport, check if the whole message has been received. */
- if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) {
- pj_status_t msg_status;
- msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE,
- &msg_fragment_size);
- if (msg_status != PJ_SUCCESS) {
- if (remaining_len == PJSIP_MAX_PKT_LEN) {
- mgr->msg_cb(mgr->endpt, PJSIP_ERXOVERFLOW, rdata);
- /* Exhaust all data. */
- return rdata->pkt_info.len;
- } else {
- /* Not enough data in packet. */
- return total_processed;
- }
- }
- }
-
- /* Update msg_info. */
- rdata->msg_info.len = msg_fragment_size;
-
- /* Parse the message. */
- rdata->msg_info.msg = msg =
- pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata);
- if (msg == NULL) {
- mgr->msg_cb(mgr->endpt, PJSIP_EINVALIDMSG, rdata);
- goto finish_process_fragment;
- }
-
- /* Perform basic header checking. */
- if (rdata->msg_info.call_id.ptr == NULL ||
- rdata->msg_info.from == NULL ||
- rdata->msg_info.to == NULL ||
- rdata->msg_info.via == NULL ||
- rdata->msg_info.cseq == NULL)
- {
- mgr->msg_cb(mgr->endpt, PJSIP_EMISSINGHDR, rdata);
- goto finish_process_fragment;
- }
-
- /* If message is received from address that's different from sent-by,
- * MUST add received parameter to the via.
- */
- s = pj_str(pj_inet_ntoa(rdata->pkt_info.addr.sin_addr));
- if (pj_strcmp(&s, &rdata->msg_info.via->sent_by.host) != 0) {
- pj_strdup(rdata->tp_info.pool,
- &rdata->msg_info.via->recvd_param, &s);
- }
-
- /* RFC 3581:
- * If message contains "rport" param, put the received port there.
- */
- if (rdata->msg_info.via->rport_param == 0) {
- rdata->msg_info.via->rport_param =
- pj_ntohs(rdata->pkt_info.addr.sin_port);
- }
-
- /* Drop response message if it has more than one Via.
- */
- if (msg->type == PJSIP_RESPONSE_MSG) {
- pjsip_hdr *hdr;
- hdr = (pjsip_hdr*)rdata->msg_info.via->next;
- if (hdr != &msg->hdr) {
- hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
- if (hdr) {
- mgr->msg_cb(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata);
- goto finish_process_fragment;
- }
- }
- }
-
- /* Call the transport manager's upstream message callback.
- */
- mgr->msg_cb(mgr->endpt, PJ_SUCCESS, rdata);
-
-
-finish_process_fragment:
- total_processed += msg_fragment_size;
- current_pkt += msg_fragment_size;
- remaining_len -= msg_fragment_size;
-
- } /* while (rdata->pkt_info.len > 0) */
-
-
- return total_processed;
-}
-
-
-/*
- * pjsip_tpmgr_alloc_transport()
- *
- * Get transport suitable to communicate to remote. Create a new one
- * if necessary.
- */
-PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr,
- pjsip_transport_type_e type,
- const pj_sockaddr_in *remote,
- pjsip_transport **p_transport)
-{
- transport_key key;
- pjsip_transport *transport;
- pjsip_tpfactory *factory;
- pj_status_t status;
-
- pj_lock_acquire(mgr->lock);
-
- /* First try to get exact destination. */
- key.type = (pj_uint8_t)type;
- key.zero = 0;
- key.addr = pj_ntohl(remote->sin_addr.s_addr);
- key.port = pj_ntohs(remote->sin_port);
-
- transport = pj_hash_get(mgr->table, &key, sizeof(key));
- if (transport != NULL) {
- unsigned flag = pjsip_transport_get_flag_from_type(type);
-
- /* For datagram transports, try lookup with zero address. */
- if (flag & PJSIP_TRANSPORT_DATAGRAM) {
- key.addr = 0;
- key.port = 0;
-
- transport = pj_hash_get(mgr->table, &key, sizeof(key));
- }
- }
-
- if (transport != NULL) {
- /*
- * Transport found!
- */
- pjsip_transport_add_ref(transport);
- pj_lock_release(mgr->lock);
- *p_transport = transport;
- return PJ_SUCCESS;
- }
-
- /*
- * Transport not found!
- * Find factory that can create such transport.
- */
- factory = mgr->factory_list.next;
- while (factory != &mgr->factory_list) {
- if (factory->type == type)
- break;
- factory = factory->next;
- }
-
- if (factory == &mgr->factory_list) {
- /* No factory can create the transport! */
- pj_lock_release(mgr->lock);
- return PJSIP_EUNSUPTRANSPORT;
- }
-
- /* Request factory to create transport. */
- status = factory->create_transport(factory, mgr, mgr->endpt,
- remote, p_transport);
-
- pj_lock_release(mgr->lock);
- return status;
-}
-
-/**
- * Dump transport info.
- */
-PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr)
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- pj_hash_iterator_t itr_val;
- pj_hash_iterator_t *itr;
-
- pj_lock_acquire(mgr->lock);
-
- itr = pj_hash_first(mgr->table, &itr_val);
- if (itr) {
- PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
-
- do {
- char src_addr[128], dst_addr[128];
- int src_port, dst_port;
- pjsip_transport *t;
-
- t = pj_hash_this(mgr->table, itr);
- pj_native_strcpy(src_addr, pj_inet_ntoa(t->local_addr.sin_addr));
- src_port = pj_ntohs(t->local_addr.sin_port);
-
- pj_native_strcpy(dst_addr, pj_inet_ntoa(t->rem_addr.sin_addr));
- dst_port = pj_ntohs(t->rem_addr.sin_port);
-
- PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)",
- t->type_name,
- t->obj_name,
- src_addr, src_port,
- dst_addr, dst_port,
- pj_atomic_get(t->ref_cnt)));
-
- itr = pj_hash_next(mgr->table, itr);
- } while (itr);
- }
-
- pj_lock_release(mgr->lock);
-#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_transport.h> +#include <pjsip/sip_endpoint.h> +#include <pjsip/sip_parser.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_private.h> +#include <pjsip/sip_errno.h> +#include <pj/os.h> +#include <pj/log.h> +#include <pj/ioqueue.h> +#include <pj/hash.h> +#include <pj/string.h> +#include <pj/pool.h> +#include <pj/assert.h> +#include <pj/lock.h> + + +#define THIS_FILE "transport" + +/* + * Transport manager. + */ +struct pjsip_tpmgr +{ + pj_hash_table_t *table; + pj_lock_t *lock; + pjsip_endpoint *endpt; + pjsip_tpfactory factory_list; + void (*msg_cb)(pjsip_endpoint*, pj_status_t, pjsip_rx_data*); +}; + + + +/***************************************************************************** + * + * GENERAL TRANSPORT (NAMES, TYPES, ETC.) + * + *****************************************************************************/ + +/* + * Transport names. + */ +const struct +{ + pjsip_transport_type_e type; + pj_uint16_t port; + pj_str_t name; + unsigned flag; +} transport_names[] = +{ + { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}, 0}, + { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}, PJSIP_TRANSPORT_DATAGRAM}, + { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}, PJSIP_TRANSPORT_RELIABLE}, + { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}, PJSIP_TRANSPORT_RELIABLE | PJSIP_TRANSPORT_SECURE}, + { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}, PJSIP_TRANSPORT_RELIABLE} +}; + + +/* + * Get transport type from name. + */ +PJ_DEF(pjsip_transport_type_e) +pjsip_transport_get_type_from_name(const pj_str_t *name) +{ + unsigned i; + + for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) { + if (pj_stricmp(name, &transport_names[i].name) == 0) { + return transport_names[i].type; + } + } + + pj_assert(!"Invalid transport name"); + return PJSIP_TRANSPORT_UNSPECIFIED; +} + + +/* + * Get the transport type for the specified flags. + */ +PJ_DEF(pjsip_transport_type_e) +pjsip_transport_get_type_from_flag(unsigned flag) +{ + unsigned i; + + for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) { + if (transport_names[i].flag == flag) { + return transport_names[i].type; + } + } + + pj_assert(!"Invalid transport type"); + return PJSIP_TRANSPORT_UNSPECIFIED; +} + +PJ_DEF(unsigned) +pjsip_transport_get_flag_from_type( pjsip_transport_type_e type ) +{ + PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 0); + return transport_names[type].flag; +} + +/* + * Get the default SIP port number for the specified type. + */ +PJ_DEF(int) +pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type) +{ + PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(transport_names), 5060); + return transport_names[type].port; +} + + +/***************************************************************************** + * + * TRANSMIT DATA BUFFER MANIPULATION. + * + *****************************************************************************/ + +/* + * Create new transmit buffer. + */ +PJ_DEF(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr, + pjsip_tx_data **p_tdata ) +{ + pj_pool_t *pool; + pjsip_tx_data *tdata; + pj_status_t status; + + PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL); + + PJ_LOG(5, ("", "pjsip_tx_data_create")); + + pool = pjsip_endpt_create_pool( mgr->endpt, "tdta%p", + PJSIP_POOL_LEN_TDATA, + PJSIP_POOL_INC_TDATA ); + if (!pool) + return PJ_ENOMEM; + + tdata = pj_pool_zalloc(pool, sizeof(pjsip_tx_data)); + tdata->pool = pool; + tdata->mgr = mgr; + pj_snprintf(tdata->obj_name, PJ_MAX_OBJ_NAME, "tdta%p", tdata); + + status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt); + if (status != PJ_SUCCESS) { + pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool ); + return status; + } + + //status = pj_lock_create_simple_mutex(pool, "tdta%p", &tdata->lock); + status = pj_lock_create_null_mutex(pool, "tdta%p", &tdata->lock); + if (status != PJ_SUCCESS) { + pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool ); + return status; + } + + pj_ioqueue_op_key_init(&tdata->op_key.key, sizeof(tdata->op_key)); + + *p_tdata = tdata; + return PJ_SUCCESS; +} + + +/* + * Add reference to tx buffer. + */ +PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ) +{ + pj_atomic_inc(tdata->ref_cnt); +} + +/* + * Decrease transport data reference, destroy it when the reference count + * reaches zero. + */ +PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) +{ + pj_assert( pj_atomic_get(tdata->ref_cnt) > 0); + if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) { + PJ_LOG(5,(tdata->obj_name, "destroying txdata")); + pj_atomic_destroy( tdata->ref_cnt ); + pj_lock_destroy( tdata->lock ); + pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool ); + } +} + +/* + * Invalidate the content of the print buffer to force the message to be + * re-printed when sent. + */ +PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ) +{ + tdata->buf.cur = tdata->buf.start; +} + +PJ_DEF(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata ) +{ + return tdata->buf.cur != tdata->buf.start; +} + + + +/***************************************************************************** + * + * TRANSPORT KEY + * + *****************************************************************************/ + +/* + * Transport key for indexing in the hash table. + */ +typedef struct transport_key +{ + pj_uint8_t type; + pj_uint8_t zero; + pj_uint16_t port; + pj_uint32_t addr; +} transport_key; + + +/***************************************************************************** + * + * TRANSPORT + * + *****************************************************************************/ + +static void transport_send_callback(pjsip_transport *transport, + void *token, + pj_ssize_t size) +{ + pjsip_tx_data *tdata = token; + + PJ_UNUSED_ARG(transport); + + /* Mark pending off so that app can resend/reuse txdata from inside + * the callback. + */ + tdata->is_pending = 0; + + /* Call callback, if any. */ + if (tdata->cb) { + (*tdata->cb)(tdata->token, tdata, size); + } + + /* Decrement reference count. */ + pjsip_tx_data_dec_ref(tdata); +} + +/* + * Send a SIP message using the specified transport. + */ +PJ_DEF(pj_status_t) pjsip_transport_send( pjsip_transport *tr, + pjsip_tx_data *tdata, + const pj_sockaddr_in *addr, + void *token, + void (*cb)(void *token, + pjsip_tx_data *tdata, + pj_ssize_t)) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(tr && tdata && addr, PJ_EINVAL); + + /* Is it currently being sent? */ + if (tdata->is_pending) { + pj_assert(!"Invalid operation step!"); + return PJSIP_EPENDINGTX; + } + + /* Allocate buffer if necessary. */ + if (tdata->buf.start == NULL) { + tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN); + tdata->buf.cur = tdata->buf.start; + tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN; + } + + /* Do we need to reprint? */ + if (!pjsip_tx_data_is_valid(tdata)) { + pj_ssize_t size; + + size = pjsip_msg_print( tdata->msg, tdata->buf.start, + tdata->buf.end - tdata->buf.start); + if (size < 0) { + return PJSIP_EMSGTOOLONG; + } + pj_assert(size != 0); + tdata->buf.cur += size; + tdata->buf.cur[size] = '\0'; + } + + /* Save callback data. */ + tdata->token = token; + tdata->cb = cb; + + /* Add reference counter. */ + pjsip_tx_data_add_ref(tdata); + + /* Mark as pending. */ + tdata->is_pending = 1; + + /* Send to transport. */ + status = (*tr->send_msg)(tr, tdata, addr, (void*)tdata, + &transport_send_callback); + + if (status != PJ_EPENDING) { + tdata->is_pending = 0; + pjsip_tx_data_dec_ref(tdata); + } + + return status; +} + +static void transport_idle_callback(pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry) +{ + pjsip_transport *tp = entry->user_data; + pj_assert(tp != NULL); + + PJ_UNUSED_ARG(timer_heap); + + entry->id = PJ_FALSE; + pjsip_transport_unregister(tp->tpmgr, tp); +} + +/* + * Add ref. + */ +PJ_DEF(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp ) +{ + PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL); + + if (pj_atomic_inc_and_get(tp->ref_cnt) == 1) { + pj_lock_acquire(tp->tpmgr->lock); + /* Verify again. */ + if (pj_atomic_get(tp->ref_cnt) == 1) { + if (tp->idle_timer.id != PJ_FALSE) { + pjsip_endpt_cancel_timer(tp->tpmgr->endpt, &tp->idle_timer); + tp->idle_timer.id = PJ_FALSE; + } + } + pj_lock_release(tp->tpmgr->lock); + } + + return PJ_SUCCESS; +} + +/* + * Dec ref. + */ +PJ_DEF(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp ) +{ + PJ_ASSERT_RETURN(tp != NULL, PJ_EINVAL); + + pj_assert(pj_atomic_get(tp->ref_cnt) > 0); + + if (pj_atomic_dec_and_get(tp->ref_cnt) == 0) { + pj_lock_acquire(tp->tpmgr->lock); + /* Verify again. */ + if (pj_atomic_get(tp->ref_cnt) == 0) { + pj_time_val delay = { PJSIP_TRANSPORT_IDLE_TIME, 0 }; + + pj_assert(tp->idle_timer.id == 0); + tp->idle_timer.id = PJ_TRUE; + pjsip_endpt_schedule_timer(tp->tpmgr->endpt, &tp->idle_timer, + &delay); + } + pj_lock_release(tp->tpmgr->lock); + } + + return PJ_SUCCESS; +} + + +/** + * Register a transport. + */ +PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, + pjsip_transport *tp ) +{ + transport_key key; + + /* Init. */ + tp->tpmgr = mgr; + pj_memset(&tp->idle_timer, 0, sizeof(tp->idle_timer)); + tp->idle_timer.user_data = tp; + tp->idle_timer.cb = &transport_idle_callback; + + /* + * Register to hash table. + */ + key.type = (pj_uint8_t)tp->type; + key.zero = 0; + key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr); + key.port = pj_ntohs(tp->rem_addr.sin_port); + + pj_lock_acquire(mgr->lock); + pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), tp); + pj_lock_release(mgr->lock); + + return PJ_SUCCESS; +} + + +/** + * Unregister transport. + */ +PJ_DEF(pj_status_t) pjsip_transport_unregister( pjsip_tpmgr *mgr, + pjsip_transport *tp) +{ + transport_key key; + + PJ_ASSERT_RETURN(pj_atomic_get(tp->ref_cnt) == 0, PJSIP_EBUSY); + + pj_lock_acquire(tp->lock); + pj_lock_acquire(mgr->lock); + + /* + * Unregister timer, if any. + */ + pj_assert(tp->idle_timer.id == PJ_FALSE); + if (tp->idle_timer.id != PJ_FALSE) { + pjsip_endpt_cancel_timer(mgr->endpt, &tp->idle_timer); + tp->idle_timer.id = PJ_FALSE; + } + + /* + * Unregister from hash table. + */ + key.type = (pj_uint8_t)tp->type; + key.zero = 0; + key.addr = pj_ntohl(tp->rem_addr.sin_addr.s_addr); + key.port = pj_ntohs(tp->rem_addr.sin_port); + + pj_hash_set(tp->pool, mgr->table, &key, sizeof(key), NULL); + + pj_lock_release(mgr->lock); + + /* Destroy. */ + return tp->destroy(tp); +} + + + +/***************************************************************************** + * + * TRANSPORT FACTORY + * + *****************************************************************************/ + + +PJ_DEF(pj_status_t) pjsip_tpmgr_register_tpfactory( pjsip_tpmgr *mgr, + pjsip_tpfactory *tpf) +{ + pjsip_tpfactory *p; + pj_status_t status; + + pj_lock_acquire(mgr->lock); + + /* Check that no factory with the same type has been registered. */ + status = PJ_SUCCESS; + for (p=mgr->factory_list.next; p!=&mgr->factory_list; p=p->next) { + if (p->type == tpf->type) { + status = PJSIP_ETYPEEXISTS; + break; + } + if (p == tpf) { + status = PJ_EEXISTS; + break; + } + } + + if (status != PJ_SUCCESS) { + pj_lock_release(mgr->lock); + return status; + } + + pj_list_insert_before(&mgr->factory_list, tpf); + + pj_lock_release(mgr->lock); + + return PJ_SUCCESS; +} + + +/** + * Unregister factory. + */ +PJ_DEF(pj_status_t) pjsip_tpmgr_unregister_tpfactory( pjsip_tpmgr *mgr, + pjsip_tpfactory *tpf) +{ + pj_lock_acquire(mgr->lock); + + pj_assert(pj_list_find_node(&mgr->factory_list, tpf) == tpf); + pj_list_erase(tpf); + + pj_lock_release(mgr->lock); + + return PJ_SUCCESS; +} + + +/***************************************************************************** + * + * TRANSPORT MANAGER + * + *****************************************************************************/ + +/* + * Create a new transport manager. + */ +PJ_DEF(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool, + pjsip_endpoint *endpt, + void (*cb)(pjsip_endpoint*, + pj_status_t, + pjsip_rx_data *), + pjsip_tpmgr **p_mgr) +{ + pjsip_tpmgr *mgr; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && endpt && cb && p_mgr, PJ_EINVAL); + + PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_create()")); + + mgr = pj_pool_zalloc(pool, sizeof(*mgr)); + mgr->endpt = endpt; + mgr->msg_cb = cb; + pj_list_init(&mgr->factory_list); + + mgr->table = pj_hash_create(pool, PJSIP_TPMGR_HTABLE_SIZE); + if (!mgr->table) + return PJ_ENOMEM; + + status = pj_lock_create_recursive_mutex(pool, "tmgr%p", &mgr->lock); + if (status != PJ_SUCCESS) + return status; + + *p_mgr = mgr; + return PJ_SUCCESS; +} + +/* + * pjsip_tpmgr_destroy() + * + * Destroy transport manager. + */ +PJ_DEF(pj_status_t) pjsip_tpmgr_destroy( pjsip_tpmgr *mgr ) +{ + pj_hash_iterator_t itr_val; + pj_hash_iterator_t *itr; + + PJ_LOG(5, (THIS_FILE, "pjsip_tpmgr_destroy()")); + + pj_lock_acquire(mgr->lock); + + itr = pj_hash_first(mgr->table, &itr_val); + while (itr != NULL) { + pj_hash_iterator_t *next; + pjsip_transport *transport; + + transport = pj_hash_this(mgr->table, itr); + + next = pj_hash_next(mgr->table, itr); + + pj_atomic_set(transport->ref_cnt, 0); + pjsip_transport_unregister(mgr, transport); + + itr = next; + } + + pj_lock_release(mgr->lock); + + return PJ_SUCCESS; +} + + +/* + * pjsip_tpmgr_receive_packet() + * + * Called by tranports when they receive a new packet. + */ +PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, + pjsip_rx_data *rdata) +{ + pjsip_transport *tr = rdata->tp_info.transport; + pj_str_t s; + + char *current_pkt; + pj_size_t remaining_len; + pj_size_t total_processed = 0; + + /* Check size. */ + pj_assert(rdata->pkt_info.len > 0); + if (rdata->pkt_info.len <= 0) + return -1; + + current_pkt = rdata->pkt_info.packet; + remaining_len = rdata->pkt_info.len; + + /* Must NULL terminate buffer. This is the requirement of the + * parser etc. + */ + current_pkt[remaining_len] = '\0'; + + /* Process all message fragments. */ + while (total_processed < remaining_len) { + + pjsip_msg *msg; + pj_size_t msg_fragment_size = 0; + + /* Initialize default fragment size. */ + msg_fragment_size = remaining_len; + + /* Null terminate packet. */ + + /* Clear and init msg_info in rdata. + * Endpoint might inspect the values there when we call the callback + * to report some errors. + */ + pj_memset(&rdata->msg_info, 0, sizeof(rdata->msg_info)); + pj_list_init(&rdata->msg_info.parse_err); + rdata->msg_info.msg_buf = current_pkt; + rdata->msg_info.len = remaining_len; + + /* For TCP transport, check if the whole message has been received. */ + if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) { + pj_status_t msg_status; + msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, + &msg_fragment_size); + if (msg_status != PJ_SUCCESS) { + if (remaining_len == PJSIP_MAX_PKT_LEN) { + mgr->msg_cb(mgr->endpt, PJSIP_ERXOVERFLOW, rdata); + /* Exhaust all data. */ + return rdata->pkt_info.len; + } else { + /* Not enough data in packet. */ + return total_processed; + } + } + } + + /* Update msg_info. */ + rdata->msg_info.len = msg_fragment_size; + + /* Parse the message. */ + rdata->msg_info.msg = msg = + pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata); + if (msg == NULL) { + mgr->msg_cb(mgr->endpt, PJSIP_EINVALIDMSG, rdata); + goto finish_process_fragment; + } + + /* Perform basic header checking. */ + if (rdata->msg_info.call_id.ptr == NULL || + rdata->msg_info.from == NULL || + rdata->msg_info.to == NULL || + rdata->msg_info.via == NULL || + rdata->msg_info.cseq == NULL) + { + mgr->msg_cb(mgr->endpt, PJSIP_EMISSINGHDR, rdata); + goto finish_process_fragment; + } + + /* If message is received from address that's different from sent-by, + * MUST add received parameter to the via. + */ + s = pj_str(pj_inet_ntoa(rdata->pkt_info.addr.sin_addr)); + if (pj_strcmp(&s, &rdata->msg_info.via->sent_by.host) != 0) { + pj_strdup(rdata->tp_info.pool, + &rdata->msg_info.via->recvd_param, &s); + } + + /* RFC 3581: + * If message contains "rport" param, put the received port there. + */ + if (rdata->msg_info.via->rport_param == 0) { + rdata->msg_info.via->rport_param = + pj_ntohs(rdata->pkt_info.addr.sin_port); + } + + /* Drop response message if it has more than one Via. + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + pjsip_hdr *hdr; + hdr = (pjsip_hdr*)rdata->msg_info.via->next; + if (hdr != &msg->hdr) { + hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr); + if (hdr) { + mgr->msg_cb(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata); + goto finish_process_fragment; + } + } + } + + /* Call the transport manager's upstream message callback. + */ + mgr->msg_cb(mgr->endpt, PJ_SUCCESS, rdata); + + +finish_process_fragment: + total_processed += msg_fragment_size; + current_pkt += msg_fragment_size; + remaining_len -= msg_fragment_size; + + } /* while (rdata->pkt_info.len > 0) */ + + + return total_processed; +} + + +/* + * pjsip_tpmgr_alloc_transport() + * + * Get transport suitable to communicate to remote. Create a new one + * if necessary. + */ +PJ_DEF(pj_status_t) pjsip_tpmgr_alloc_transport( pjsip_tpmgr *mgr, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + pjsip_transport **p_transport) +{ + transport_key key; + pjsip_transport *transport; + pjsip_tpfactory *factory; + pj_status_t status; + + pj_lock_acquire(mgr->lock); + + /* First try to get exact destination. */ + key.type = (pj_uint8_t)type; + key.zero = 0; + key.addr = pj_ntohl(remote->sin_addr.s_addr); + key.port = pj_ntohs(remote->sin_port); + + transport = pj_hash_get(mgr->table, &key, sizeof(key)); + if (transport != NULL) { + unsigned flag = pjsip_transport_get_flag_from_type(type); + + /* For datagram transports, try lookup with zero address. */ + if (flag & PJSIP_TRANSPORT_DATAGRAM) { + key.addr = 0; + key.port = 0; + + transport = pj_hash_get(mgr->table, &key, sizeof(key)); + } + } + + if (transport != NULL) { + /* + * Transport found! + */ + pjsip_transport_add_ref(transport); + pj_lock_release(mgr->lock); + *p_transport = transport; + return PJ_SUCCESS; + } + + /* + * Transport not found! + * Find factory that can create such transport. + */ + factory = mgr->factory_list.next; + while (factory != &mgr->factory_list) { + if (factory->type == type) + break; + factory = factory->next; + } + + if (factory == &mgr->factory_list) { + /* No factory can create the transport! */ + pj_lock_release(mgr->lock); + return PJSIP_EUNSUPTRANSPORT; + } + + /* Request factory to create transport. */ + status = factory->create_transport(factory, mgr, mgr->endpt, + remote, p_transport); + + pj_lock_release(mgr->lock); + return status; +} + +/** + * Dump transport info. + */ +PJ_DEF(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + pj_hash_iterator_t itr_val; + pj_hash_iterator_t *itr; + + pj_lock_acquire(mgr->lock); + + itr = pj_hash_first(mgr->table, &itr_val); + if (itr) { + PJ_LOG(3, (THIS_FILE, " Dumping transports:")); + + do { + char src_addr[128], dst_addr[128]; + int src_port, dst_port; + pjsip_transport *t; + + t = pj_hash_this(mgr->table, itr); + pj_native_strcpy(src_addr, pj_inet_ntoa(t->local_addr.sin_addr)); + src_port = pj_ntohs(t->local_addr.sin_port); + + pj_native_strcpy(dst_addr, pj_inet_ntoa(t->rem_addr.sin_addr)); + dst_port = pj_ntohs(t->rem_addr.sin_port); + + PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)", + t->type_name, + t->obj_name, + src_addr, src_port, + dst_addr, dst_port, + pj_atomic_get(t->ref_cnt))); + + itr = pj_hash_next(mgr->table, itr); + } while (itr); + } + + pj_lock_release(mgr->lock); +#endif +} + |