From 7567c6b90e8e0dd20204a579cd548ad7ae00febf Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Fri, 12 Jan 2007 06:37:35 +0000 Subject: 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 --- pjsip/include/pjsip-ua/sip_regc.h | 27 ++++ pjsip/include/pjsip/sip_dialog.h | 28 +++++ pjsip/include/pjsip/sip_endpoint.h | 21 +++- pjsip/include/pjsip/sip_errno.h | 8 ++ pjsip/include/pjsip/sip_transaction.h | 22 ++++ pjsip/include/pjsip/sip_transport.h | 120 +++++++++++++++++- pjsip/src/pjsip-ua/sip_reg.c | 21 ++++ pjsip/src/pjsip/sip_dialog.c | 36 ++++++ pjsip/src/pjsip/sip_endpoint.c | 3 +- pjsip/src/pjsip/sip_transaction.c | 43 ++++++- pjsip/src/pjsip/sip_transport.c | 223 +++++++++++++++++++++++++--------- pjsip/src/pjsip/sip_util.c | 2 + 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 #include +#include /** @@ -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 #include #include +#include #include #include @@ -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, @@ -223,6 +227,30 @@ PJ_DECL(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, pjsip_dialog **p_dlg); +/** + * 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 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 #include +#include #include 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. @@ -213,6 +215,26 @@ PJ_DECL(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user, pjsip_transaction **p_tsx ); +/** + * 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 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(®c->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(®c->tp_sel); + pj_memcpy(®c->tp_sel, sel, sizeof(*sel)); + pjsip_tpselector_add_ref(®c->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 @@ -505,6 +505,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. */ @@ -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 ); @@ -1362,6 +1365,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. */ @@ -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) { -- cgit v1.2.3