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/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 + 6 files changed, 268 insertions(+), 60 deletions(-) (limited to 'pjsip/src') 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