summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-01-12 06:37:35 +0000
committerBenny Prijono <bennylp@teluu.com>2007-01-12 06:37:35 +0000
commit7567c6b90e8e0dd20204a579cd548ad7ae00febf (patch)
treefaadb7133a618fe5b1551b3ea89717eec860bee3 /pjsip
parent5058236717c4a44f02a1507b2833e8cff8f54083 (diff)
Workaround for ticket #50: added API to lock/bind transaction, dialog, and regc to a specific transport/listener
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@879 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/include/pjsip-ua/sip_regc.h27
-rw-r--r--pjsip/include/pjsip/sip_dialog.h28
-rw-r--r--pjsip/include/pjsip/sip_endpoint.h21
-rw-r--r--pjsip/include/pjsip/sip_errno.h8
-rw-r--r--pjsip/include/pjsip/sip_transaction.h22
-rw-r--r--pjsip/include/pjsip/sip_transport.h120
-rw-r--r--pjsip/src/pjsip-ua/sip_reg.c21
-rw-r--r--pjsip/src/pjsip/sip_dialog.c36
-rw-r--r--pjsip/src/pjsip/sip_endpoint.c3
-rw-r--r--pjsip/src/pjsip/sip_transaction.c43
-rw-r--r--pjsip/src/pjsip/sip_transport.c223
-rw-r--r--pjsip/src/pjsip/sip_util.c2
12 files changed, 483 insertions, 71 deletions
diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h
index 04f2752d..683c3d48 100644
--- a/pjsip/include/pjsip-ua/sip_regc.h
+++ b/pjsip/include/pjsip-ua/sip_regc.h
@@ -26,6 +26,7 @@
#include <pjsip/sip_types.h>
#include <pjsip/sip_auth.h>
+#include <pjsip/sip_transport.h>
/**
@@ -198,6 +199,32 @@ PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
PJ_DECL(pj_status_t) pjsip_regc_set_route_set(pjsip_regc *regc,
const pjsip_route_hdr*route_set);
+
+/**
+ * Lock/bind client registration to a specific transport/listener.
+ * This is optional, as normally transport will be selected automatically
+ * based on the destination of requests upon resolver completion.
+ * When the client registration is explicitly bound to the specific
+ * transport/listener, all UAC transactions originated by the client
+ * registration will use the specified transport/listener when sending
+ * outgoing requests.
+ *
+ * Note that this doesn't affect the Contact header set for this client
+ * registration. Application must manually update the Contact header if
+ * necessary, to adjust the address according to the transport being
+ * selected.
+ *
+ * @param regc The client registration instance.
+ * @param sel Transport selector containing the specification of
+ * transport or listener to be used by this session
+ * to send requests.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_set_transport(pjsip_regc *regc,
+ const pjsip_tpselector *sel);
+
+
/**
* Add headers to be added to outgoing REGISTER requests.
*
diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h
index b4d2a1ac..9cea4ded 100644
--- a/pjsip/include/pjsip/sip_dialog.h
+++ b/pjsip/include/pjsip/sip_dialog.h
@@ -28,6 +28,7 @@
#include <pjsip/sip_msg.h>
#include <pjsip/sip_auth.h>
#include <pjsip/sip_errno.h>
+#include <pjsip/sip_transport.h>
#include <pj/sock.h>
#include <pj/assert.h>
@@ -134,6 +135,9 @@ struct pjsip_dialog
/** Transaction counter. */
int tsx_count; /**< Number of pending transactions. */
+ /** Transport selector. */
+ pjsip_tpselector tp_sel;
+
/* Dialog usages. */
unsigned usage_cnt; /**< Number of registered usages. */
pjsip_module *usage[PJSIP_MAX_MODULE]; /**< Array of usages,
@@ -224,6 +228,30 @@ PJ_DECL(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua,
/**
+ * Lock/bind dialog to a specific transport/listener. This is optional,
+ * as normally transport will be selected automatically based on the
+ * destination of requests upon resolver completion. When the dialog is
+ * explicitly bound to the specific transport/listener, all UAC transactions
+ * originated by this dialog will use the specified transport/listener
+ * when sending outgoing requests.
+ *
+ * Note that this doesn't affect the Contact header generated by this
+ * dialog. Application must manually update the Contact header if
+ * necessary, to adjust the address according to the transport being
+ * selected.
+ *
+ * @param dlg The dialog instance.
+ * @param sel Transport selector containing the specification of
+ * transport or listener to be used by this dialog
+ * to send requests.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_set_transport(pjsip_dialog *dlg,
+ const pjsip_tpselector *sel);
+
+
+/**
* Create a new (forked) dialog on receipt on forked response in rdata.
* The new dialog will be created from original_dlg, except that it will have
* new remote tag as copied from the To header in the response. Upon return,
diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h
index 34413d7c..7f264908 100644
--- a/pjsip/include/pjsip/sip_endpoint.h
+++ b/pjsip/include/pjsip/sip_endpoint.h
@@ -352,18 +352,29 @@ PJ_DECL(pj_ioqueue_t*) pjsip_endpt_get_ioqueue(pjsip_endpoint *endpt);
/**
* Find a SIP transport suitable for sending SIP message to the specified
- * address. This function will complete asynchronously when the transport is
- * ready (for example, when TCP socket is connected), and when it completes,
- * the callback will be called with the status of the operation.
+ * address. If transport selector ("sel") is set, then the function will
+ * check if the transport selected is suitable to send requests to the
+ * specified address.
*
- * @see pjsip_transport_get
+ * @see pjsip_tpmgr_acquire_transport
+ *
+ * @param endpt The SIP endpoint instance.
+ * @param type The type of transport to be acquired.
+ * @param remote The remote address to send message to.
+ * @param addr_len Length of the remote address.
+ * @param sel Optional pointer to transport selector instance which is
+ * used to find explicit transport, if required.
+ * @param p_tp Pointer to receive the transport instance, if one is found.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
*/
PJ_DECL(pj_status_t)
pjsip_endpt_acquire_transport( pjsip_endpoint *endpt,
pjsip_transport_type_e type,
const pj_sockaddr_t *remote,
int addr_len,
- pjsip_transport **p_transport);
+ const pjsip_tpselector *sel,
+ pjsip_transport **p_tp);
/*****************************************************************************
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index 10b542c7..bd0fecaf 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -214,6 +214,14 @@ PJ_BEGIN_DECL
* transmit data has been deleted on return of pjsip_tx_data_dec_ref().
*/
#define PJSIP_EBUFDESTROYED (PJSIP_ERRNO_START_PJSIP + 63) /* 171063 */
+/**
+ * @hideinitializer
+ * Unsuitable transport selected. This error occurs when application
+ * has explicitly requested to use a particular transport/listener,
+ * but the selected transport is not suitable to send request to
+ * the specified destination.
+ */
+#define PJSIP_ETPNOTSUITABLE (PJSIP_ERRNO_START_PJSIP + 64) /* 171064 */
/************************************************************
diff --git a/pjsip/include/pjsip/sip_transaction.h b/pjsip/include/pjsip/sip_transaction.h
index e15fd13d..dfd00339 100644
--- a/pjsip/include/pjsip/sip_transaction.h
+++ b/pjsip/include/pjsip/sip_transaction.h
@@ -26,6 +26,7 @@
#include <pjsip/sip_msg.h>
#include <pjsip/sip_util.h>
+#include <pjsip/sip_transport.h>
#include <pj/timer.h>
PJ_BEGIN_DECL
@@ -96,6 +97,7 @@ struct pjsip_transaction
pj_str_t transaction_key;/**< Hash table key. */
pj_uint32_t hashed_key; /**< Key's hashed value. */
pj_str_t branch; /**< The branch Id. */
+ pjsip_tpselector tp_sel; /**< Transport selector. */
/*
* State and status.
@@ -214,6 +216,26 @@ PJ_DECL(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
/**
+ * Lock/bind transaction to a specific transport/listener. This is optional,
+ * as normally transport will be selected automatically based on the
+ * destination of the request upon resolver completion. Also it's only valid
+ * for UAC transaction (to send outgoing request), since for UAS the
+ * transport will be selected according to rules about handling incoming
+ * request (most likely it will use the transport where the request is
+ * coming from if ";rport" parameter is present in Via header).
+ *
+ * @param tsx The UAC transaction.
+ * @param sel Transport selector containing the specification of
+ * transport or listener to be used by this transaction
+ * to send requests.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx,
+ const pjsip_tpselector *sel);
+
+
+/**
* Call this function to manually feed a message to the transaction.
* For UAS transaction, application MUST call this function after
* UAS transaction has been created.
diff --git a/pjsip/include/pjsip/sip_transport.h b/pjsip/include/pjsip/sip_transport.h
index 4c9120e9..30516de7 100644
--- a/pjsip/include/pjsip/sip_transport.h
+++ b/pjsip/include/pjsip/sip_transport.h
@@ -55,6 +55,13 @@ PJ_BEGIN_DECL
*
*****************************************************************************/
+/*
+ * Forward declaration for transport factory (since it is referenced by
+ * the transport factory itself).
+ */
+typedef struct pjsip_tpfactory pjsip_tpfactory;
+
+
/**
* Flags for SIP transports.
*/
@@ -156,6 +163,76 @@ pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type);
PJ_DECL(const char*) pjsip_transport_get_type_name(pjsip_transport_type_e t);
+
+/*****************************************************************************
+ *
+ * TRANSPORT SELECTOR.
+ *
+ *****************************************************************************/
+
+/**
+ * This structure describes the type of data in pjsip_tpselector.
+ */
+typedef enum pjsip_tpselector_type
+{
+ /** Transport is not specified. */
+ PJSIP_TPSELECTOR_NONE,
+
+ /** Use the specific transport to send request. */
+ PJSIP_TPSELECTOR_TRANSPORT,
+
+ /** Use the specific listener to send request. */
+ PJSIP_TPSELECTOR_LISTENER,
+
+} pjsip_tpselector_type;
+
+
+/**
+ * This structure describes the transport/listener preference to be used
+ * when sending outgoing requests.
+ *
+ * Normally transport will be selected automatically according to rules about
+ * sending requests. But some applications (such as proxies or B2BUAs) may
+ * want to explicitly use specific transport to send requests, for example
+ * when they want to make sure that outgoing request should go from a specific
+ * network interface.
+ *
+ * The pjsip_tpselector structure is used for that purpose, i.e. to allow
+ * application specificly request that a particular transport/listener
+ * should be used to send request. This structure is used when calling
+ * pjsip_tsx_set_transport() and pjsip_dlg_set_transport().
+ */
+typedef struct pjsip_tpselector
+{
+ /** The type of data in the union */
+ pjsip_tpselector_type type;
+
+ /** Union representing the transport/listener criteria to be used. */
+ union {
+ pjsip_transport *transport;
+ pjsip_tpfactory *listener;
+ } u;
+
+} pjsip_tpselector;
+
+
+/**
+ * Add transport/listener reference in the selector to prevent the specified
+ * transport/listener from being destroyed while application still has
+ * reference to it.
+ *
+ * @param sel The transport selector.
+ */
+PJ_DECL(void) pjsip_tpselector_add_ref(pjsip_tpselector *sel);
+
+
+/**
+ * Decrement transport/listener reference in the selector.
+ * @param sel The transport selector
+ */
+PJ_DECL(void) pjsip_tpselector_dec_ref(pjsip_tpselector *sel);
+
+
/*****************************************************************************
*
* RECEIVE DATA BUFFER.
@@ -431,6 +508,14 @@ struct pjsip_tx_data
char dst_name[16]; /**< Destination address. */
int dst_port; /**< Destination port. */
} tp_info;
+
+ /**
+ * Transport selector, to specify which transport to be used.
+ * The value here must be set with pjsip_tx_data_set_transport(),
+ * to allow reference counter to be set properly.
+ */
+ pjsip_tpselector tp_sel;
+
};
@@ -499,6 +584,20 @@ PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata );
*/
PJ_DECL(char*) pjsip_tx_data_get_info( pjsip_tx_data *tdata );
+/**
+ * Set the explicit transport to be used when sending this transmit data.
+ * Application should not need to call this function, but rather use
+ * pjsip_tsx_set_transport() and pjsip_dlg_set_transport() instead (which
+ * will call this function).
+ *
+ * @param tdata The transmit buffer.
+ * @param sel Transport selector.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_tx_data_set_transport(pjsip_tx_data *tdata,
+ const pjsip_tpselector *sel);
+
/*****************************************************************************
*
@@ -706,12 +805,6 @@ PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr,
*
*****************************************************************************/
-/*
- * Forward declaration for transport factory (since it is referenced by
- * the transport factory itself).
- */
-typedef struct pjsip_tpfactory pjsip_tpfactory;
-
/**
* A transport factory is normally used for connection oriented transports
@@ -859,11 +952,26 @@ PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr);
/**
* Find transport to be used to send message to remote destination. If no
* suitable transport is found, a new one will be created.
+ *
+ * This is an internal function since normally application doesn't have access
+ * to transport manager. Application should use pjsip_endpt_acquire_transport()
+ * instead.
+ *
+ * @param mgr The transport manager instance.
+ * @param type The type of transport to be acquired.
+ * @param remote The remote address to send message to.
+ * @param addr_len Length of the remote address.
+ * @param sel Optional pointer to transport selector instance which is
+ * used to find explicit transport, if required.
+ * @param tp Pointer to receive the transport instance, if one is found.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
pjsip_transport_type_e type,
const pj_sockaddr_t *remote,
int addr_len,
+ const pjsip_tpselector *sel,
pjsip_transport **tp);
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
index 5cba23ee..d6078c73 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -76,6 +76,9 @@ struct pjsip_regc
pj_time_val last_reg;
pj_time_val next_reg;
pj_timer_entry timer;
+
+ /* Transport selector */
+ pjsip_tpselector tp_sel;
};
@@ -124,6 +127,7 @@ PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
regc->_delete_flag = 1;
regc->cb = NULL;
} else {
+ pjsip_tpselector_dec_ref(&regc->tp_sel);
pjsip_endpt_release_pool(regc->endpt, regc->pool);
}
@@ -310,6 +314,23 @@ PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
return PJ_SUCCESS;
}
+
+/*
+ * Bind client registration to a specific transport/listener.
+ */
+PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
+ const pjsip_tpselector *sel)
+{
+ PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
+
+ pjsip_tpselector_dec_ref(&regc->tp_sel);
+ pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
+ pjsip_tpselector_add_ref(&regc->tp_sel);
+
+ return PJ_SUCCESS;
+}
+
+
PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
const pjsip_hdr *hdr_list)
{
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
index 515b8616..bf8eef40 100644
--- a/pjsip/src/pjsip/sip_dialog.c
+++ b/pjsip/src/pjsip/sip_dialog.c
@@ -506,6 +506,34 @@ on_error:
/*
+ * Bind dialog to a specific transport/listener.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_transport( pjsip_dialog *dlg,
+ const pjsip_tpselector *sel)
+{
+ /* Validate */
+ PJ_ASSERT_RETURN(dlg && sel, PJ_EINVAL);
+
+ /* Start locking the dialog. */
+ pjsip_dlg_inc_lock(dlg);
+
+ /* Decrement reference counter of previous transport selector */
+ pjsip_tpselector_dec_ref(&dlg->tp_sel);
+
+ /* Copy transport selector structure .*/
+ pj_memcpy(&dlg->tp_sel, sel, sizeof(*sel));
+
+ /* Increment reference counter */
+ pjsip_tpselector_add_ref(&dlg->tp_sel);
+
+ /* Unlock dialog. */
+ pjsip_dlg_dec_lock(dlg);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
* Create forked dialog from a response.
*/
PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
@@ -1065,6 +1093,10 @@ PJ_DEF(pj_status_t) pjsip_dlg_send_request( pjsip_dialog *dlg,
if (status != PJ_SUCCESS)
goto on_error;
+ /* Set transport selector */
+ status = pjsip_tsx_set_transport(tsx, &dlg->tp_sel);
+ pj_assert(status == PJ_SUCCESS);
+
/* Attach this dialog to the transaction, so that user agent
* will dispatch events to this dialog.
*/
@@ -1086,6 +1118,10 @@ PJ_DEF(pj_status_t) pjsip_dlg_send_request( pjsip_dialog *dlg,
}
} else {
+ /* Set transport selector */
+ pjsip_tx_data_set_transport(tdata, &dlg->tp_sel);
+
+ /* Send request */
status = pjsip_endpt_send_request_stateless(dlg->endpt, tdata,
NULL, NULL);
if (status != PJ_SUCCESS)
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
index 59729987..8c112395 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -1043,10 +1043,11 @@ PJ_DEF(pj_status_t) pjsip_endpt_acquire_transport(pjsip_endpoint *endpt,
pjsip_transport_type_e type,
const pj_sockaddr_t *remote,
int addr_len,
+ const pjsip_tpselector *sel,
pjsip_transport **transport)
{
return pjsip_tpmgr_acquire_transport(endpt->transport_mgr, type,
- remote, addr_len, transport);
+ remote, addr_len, sel, transport);
}
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index 23685ff6..a0574ca4 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -931,6 +931,9 @@ static pj_status_t tsx_destroy( pjsip_transaction *tsx )
pjsip_transport_dec_ref( tsx->transport );
tsx->transport = NULL;
}
+ /* Decrement reference counter in transport selector */
+ pjsip_tpselector_dec_ref(&tsx->tp_sel);
+
/* Free last transmitted message. */
if (tsx->last_tx) {
pjsip_tx_data_dec_ref( tsx->last_tx );
@@ -1363,6 +1366,36 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
/*
+ * Bind transaction to a specific transport/listener.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx,
+ const pjsip_tpselector *sel)
+{
+ struct tsx_lock_data lck;
+
+ /* Must be UAC transaction */
+ PJ_ASSERT_RETURN(tsx && sel && tsx->role == PJSIP_ROLE_UAC, PJ_EINVAL);
+
+ /* Start locking the transaction. */
+ lock_tsx(tsx, &lck);
+
+ /* Decrement reference counter of previous transport selector */
+ pjsip_tpselector_dec_ref(&tsx->tp_sel);
+
+ /* Copy transport selector structure .*/
+ pj_memcpy(&tsx->tp_sel, sel, sizeof(*sel));
+
+ /* Increment reference counter */
+ pjsip_tpselector_add_ref(&tsx->tp_sel);
+
+ /* Unlock transaction. */
+ unlock_tsx(tsx, &lck);
+
+ return PJ_SUCCESS;
+}
+
+
+/*
* Set transaction status code and reason.
*/
static void tsx_set_status_code(pjsip_transaction *tsx,
@@ -1423,11 +1456,17 @@ PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx,
/* Dispatch to transaction. */
lock_tsx(tsx, &lck);
+
+ /* Set transport selector to tdata */
+ pjsip_tx_data_set_transport(tdata, &tsx->tp_sel);
+
+ /* Dispatch to state handler */
status = (*tsx->state_handler)(tsx, &event);
+
unlock_tsx(tsx, &lck);
- /* Will always decrement tdata reference counter
- * (consistent with other send functions.
+ /* Only decrement reference counter when it returns success.
+ * (This is the specification from the .PDF design document).
*/
if (status == PJ_SUCCESS) {
pjsip_tx_data_dec_ref(tdata);
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
index 38db3993..9a3ae6a1 100644
--- a/pjsip/src/pjsip/sip_transport.c
+++ b/pjsip/src/pjsip/sip_transport.c
@@ -259,6 +259,37 @@ PJ_DEF(const char*) pjsip_transport_get_type_name(pjsip_transport_type_e type)
return transport_names[type].name.ptr;
}
+
+/*****************************************************************************
+ *
+ * TRANSPORT SELECTOR
+ *
+ *****************************************************************************/
+
+/*
+ * Add transport/listener reference in the selector.
+ */
+PJ_DEF(void) pjsip_tpselector_add_ref(pjsip_tpselector *sel)
+{
+ if (sel->type == PJSIP_TPSELECTOR_TRANSPORT && sel->u.transport != NULL)
+ pjsip_transport_add_ref(sel->u.transport);
+ else if (sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener != NULL)
+ ; /* Hmm.. looks like we don't have reference counter for listener */
+}
+
+
+/*
+ * Decrement transport/listener reference in the selector.
+ */
+PJ_DEF(void) pjsip_tpselector_dec_ref(pjsip_tpselector *sel)
+{
+ if (sel->type == PJSIP_TPSELECTOR_TRANSPORT && sel->u.transport != NULL)
+ pjsip_transport_dec_ref(sel->u.transport);
+ else if (sel->type == PJSIP_TPSELECTOR_LISTENER && sel->u.listener != NULL)
+ ; /* Hmm.. looks like we don't have reference counter for listener */
+}
+
+
/*****************************************************************************
*
* TRANSMIT DATA BUFFER MANIPULATION.
@@ -330,6 +361,7 @@ PJ_DEF(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
PJ_LOG(5,(tdata->obj_name, "Destroying txdata %s",
pjsip_tx_data_get_info(tdata)));
+ pjsip_tpselector_dec_ref(&tdata->tp_sel);
#if defined(PJ_DEBUG) && PJ_DEBUG!=0
pj_atomic_dec( tdata->mgr->tdata_counter );
#endif
@@ -408,6 +440,24 @@ PJ_DEF(char*) pjsip_tx_data_get_info( pjsip_tx_data *tdata )
return tdata->info;
}
+PJ_DEF(pj_status_t) pjsip_tx_data_set_transport(pjsip_tx_data *tdata,
+ const pjsip_tpselector *sel)
+{
+ PJ_ASSERT_RETURN(tdata && sel, PJ_EINVAL);
+
+ pj_lock_acquire(tdata->lock);
+
+ pjsip_tpselector_dec_ref(&tdata->tp_sel);
+
+ pj_memcpy(&tdata->tp_sel, sel, sizeof(*sel));
+ pjsip_tpselector_add_ref(&tdata->tp_sel);
+
+ pj_lock_release(tdata->lock);
+
+ return PJ_SUCCESS;
+}
+
+
PJ_DEF(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata)
{
char obj_name[PJ_MAX_OBJ_NAME];
@@ -911,7 +961,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr,
pj_sockaddr_in_init(&remote, NULL, 0);
status = pjsip_tpmgr_acquire_transport(tpmgr, type, &remote,
- sizeof(remote), &tp);
+ sizeof(remote), NULL, &tp);
if (status == PJ_SUCCESS) {
pj_strdup(pool, ip_addr, &tp->local_name.host);
@@ -1200,11 +1250,9 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
pjsip_transport_type_e type,
const pj_sockaddr_t *remote,
int addr_len,
+ const pjsip_tpselector *sel,
pjsip_transport **tp)
{
- struct transport_key key;
- int key_len;
- pjsip_transport *transport;
pjsip_tpfactory *factory;
pj_status_t status;
@@ -1215,74 +1263,135 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
pj_lock_acquire(mgr->lock);
- key_len = sizeof(key.type) + addr_len;
+ /* If transport is specified, then just use it if it is suitable
+ * for the destination.
+ */
+ if (sel && sel->type == PJSIP_TPSELECTOR_TRANSPORT &&
+ sel->u.transport)
+ {
+ pjsip_transport *seltp = sel->u.transport;
+
+ /* See if the transport is (not) suitable */
+ if (seltp->key.type != type) {
+ pj_lock_release(mgr->lock);
+ return PJSIP_ETPNOTSUITABLE;
+ }
- /* First try to get exact destination. */
- key.type = type;
- pj_memcpy(&key.addr, remote, addr_len);
+ /* We could also verify that the destination address is reachable
+ * from this transport (i.e. both are equal), but if application
+ * has requested a specific transport to be used, assume that
+ * it knows what to do.
+ *
+ * In other words, I don't think destination verification is a good
+ * idea for now.
+ */
- transport = pj_hash_get(mgr->table, &key, key_len, NULL);
- if (transport == NULL) {
- unsigned flag = pjsip_transport_get_flag_from_type(type);
- const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote;
+ /* Transport looks to be suitable to use, so just use it. */
+ pjsip_transport_add_ref(seltp);
+ pj_lock_release(mgr->lock);
+ *tp = seltp;
- /* Ignore address for loop transports. */
- if (type == PJSIP_TRANSPORT_LOOP ||
- type == PJSIP_TRANSPORT_LOOP_DGRAM)
- {
- pj_sockaddr_in *addr = (pj_sockaddr_in*)&key.addr;
+ TRACE_((THIS_FILE, "Transport %s acquired", seltp->obj_name));
+ return PJ_SUCCESS;
+
+
+ } else if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER &&
+ sel->u.listener)
+ {
+ /* Application has requested that a specific listener is to
+ * be used. In this case, skip transport hash table lookup.
+ */
- pj_bzero(addr, sizeof(pj_sockaddr_in));
- key_len = sizeof(key.type) + sizeof(pj_sockaddr_in);
- transport = pj_hash_get(mgr->table, &key, key_len, NULL);
+ /* Verify that the listener type matches the destination type */
+ if (sel->u.listener->type != type) {
+ pj_lock_release(mgr->lock);
+ return PJSIP_ETPNOTSUITABLE;
}
- /* For datagram INET transports, try lookup with zero address.
+
+ /* We'll use this listener to create transport */
+ factory = sel->u.listener;
+
+ } else {
+
+ /*
+ * This is the "normal" flow, where application doesn't specify
+ * specific transport/listener to be used to send message to.
+ * In this case, lookup the transport from the hash table.
*/
- else if ((flag & PJSIP_TRANSPORT_DATAGRAM) &&
- (remote_addr->sa_family == PJ_AF_INET))
- {
- pj_sockaddr_in *addr = (pj_sockaddr_in*)&key.addr;
+ struct transport_key key;
+ int key_len;
+ pjsip_transport *transport;
+
+ key_len = sizeof(key.type) + addr_len;
+
+ /* First try to get exact destination. */
+ key.type = type;
+ pj_memcpy(&key.addr, remote, addr_len);
- pj_bzero(addr, sizeof(pj_sockaddr_in));
- addr->sin_family = PJ_AF_INET;
+ transport = pj_hash_get(mgr->table, &key, key_len, NULL);
+ if (transport == NULL) {
+ unsigned flag = pjsip_transport_get_flag_from_type(type);
+ const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote;
- key_len = sizeof(key.type) + sizeof(pj_sockaddr_in);
- transport = pj_hash_get(mgr->table, &key, key_len, NULL);
+ /* Ignore address for loop transports. */
+ if (type == PJSIP_TRANSPORT_LOOP ||
+ type == PJSIP_TRANSPORT_LOOP_DGRAM)
+ {
+ pj_sockaddr_in *addr = (pj_sockaddr_in*)&key.addr;
+
+ pj_bzero(addr, sizeof(pj_sockaddr_in));
+ key_len = sizeof(key.type) + sizeof(pj_sockaddr_in);
+ transport = pj_hash_get(mgr->table, &key, key_len, NULL);
+ }
+ /* For datagram INET transports, try lookup with zero address.
+ */
+ else if ((flag & PJSIP_TRANSPORT_DATAGRAM) &&
+ (remote_addr->sa_family == PJ_AF_INET))
+ {
+ pj_sockaddr_in *addr = (pj_sockaddr_in*)&key.addr;
+
+ pj_bzero(addr, sizeof(pj_sockaddr_in));
+ addr->sin_family = PJ_AF_INET;
+
+ key_len = sizeof(key.type) + sizeof(pj_sockaddr_in);
+ transport = pj_hash_get(mgr->table, &key, key_len, NULL);
+ }
}
- }
-
- if (transport!=NULL && !transport->is_shutdown) {
+
+ if (transport!=NULL && !transport->is_shutdown) {
+ /*
+ * Transport found!
+ */
+ pjsip_transport_add_ref(transport);
+ pj_lock_release(mgr->lock);
+ *tp = transport;
+
+ TRACE_((THIS_FILE, "Transport %s acquired", transport->obj_name));
+ return PJ_SUCCESS;
+ }
+
/*
- * Transport found!
+ * Transport not found!
+ * Find factory that can create such transport.
*/
- pjsip_transport_add_ref(transport);
- pj_lock_release(mgr->lock);
- *tp = transport;
-
- TRACE_((THIS_FILE, "Transport %s acquired", transport->obj_name));
- return PJ_SUCCESS;
- }
+ factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+ if (factory->type == type)
+ break;
+ factory = factory->next;
+ }
- /*
- * 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);
+ TRACE_((THIS_FILE, "No suitable factory was found either"));
+ return PJSIP_EUNSUPTRANSPORT;
+ }
- if (factory == &mgr->factory_list) {
- /* No factory can create the transport! */
- pj_lock_release(mgr->lock);
- TRACE_((THIS_FILE, "No suitable factory was found either"));
- return PJSIP_EUNSUPTRANSPORT;
}
- TRACE_((THIS_FILE, "%s, creating new one from factory",
- (transport?"Transport is shutdown":"No transport found")));
+
+ TRACE_((THIS_FILE, "Creating new transport from factory"));
/* Request factory to create transport. */
status = factory->create_transport(factory, mgr, mgr->endpt,
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index 0b72e568..6d9f3ff4 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -837,6 +837,7 @@ static void stateless_send_transport_cb( void *token,
cur_addr_type,
cur_addr,
cur_addr_len,
+ &tdata->tp_sel,
&stateless_data->cur_transport);
if (status != PJ_SUCCESS) {
sent = -status;
@@ -1111,6 +1112,7 @@ static void send_response_resolver_cb( pj_status_t status, void *token,
addr->entry[0].type,
&addr->entry[0].addr,
addr->entry[0].addr_len,
+ &send_state->tdata->tp_sel,
&send_state->cur_transport);
if (status != PJ_SUCCESS) {
if (send_state->app_cb) {