1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555 |
- /*
- * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
- * Copyright (C) 2003-2008 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-ua/sip_regc.h>
- #include <pjsip/sip_endpoint.h>
- #include <pjsip/sip_parser.h>
- #include <pjsip/sip_module.h>
- #include <pjsip/sip_transaction.h>
- #include <pjsip/sip_event.h>
- #include <pjsip/sip_util.h>
- #include <pjsip/sip_auth_msg.h>
- #include <pjsip/sip_errno.h>
- #include <pj/assert.h>
- #include <pj/guid.h>
- #include <pj/lock.h>
- #include <pj/os.h>
- #include <pj/pool.h>
- #include <pj/log.h>
- #include <pj/rand.h>
- #include <pj/string.h>
- #define REFRESH_TIMER 1
- #define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
- #define THIS_FILE "sip_reg.c"
- /* Outgoing transaction timeout when server sends 100 but never replies
- * with final response. Value is in MILISECONDS!
- */
- #define REGC_TSX_TIMEOUT 33000
- #define NOEXP PJSIP_REGC_EXPIRATION_NOT_SPECIFIED
- static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
- /* Current/pending operation */
- enum regc_op
- {
- REGC_IDLE,
- REGC_REGISTERING,
- REGC_UNREGISTERING
- };
- /**
- * SIP client registration structure.
- */
- struct pjsip_regc
- {
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pj_lock_t *lock;
- pj_bool_t _delete_flag;
- pj_bool_t has_tsx;
- pj_atomic_t *busy_ctr;
- enum regc_op current_op;
- pj_bool_t add_xuid_param;
- void *token;
- pjsip_regc_cb *cb;
- pjsip_regc_tsx_cb *tsx_cb;
- pj_str_t str_srv_url;
- pjsip_uri *srv_url;
- pjsip_cid_hdr *cid_hdr;
- pjsip_cseq_hdr *cseq_hdr;
- pj_str_t from_uri;
- pjsip_from_hdr *from_hdr;
- pjsip_to_hdr *to_hdr;
- pjsip_contact_hdr contact_hdr_list;
- pjsip_contact_hdr removed_contact_hdr_list;
- pjsip_expires_hdr *expires_hdr;
- pj_uint32_t expires;
- pj_uint32_t expires_requested;
- pj_uint32_t delay_before_refresh;
- pjsip_route_hdr route_set;
- pjsip_hdr hdr_list;
- pjsip_host_port via_addr;
- const void *via_tp;
- /* Authorization sessions. */
- pjsip_auth_clt_sess auth_sess;
- /* Auto refresh registration. */
- pj_bool_t auto_reg;
- pj_time_val last_reg;
- pj_time_val next_reg;
- pj_timer_entry timer;
- /* Transport selector */
- pjsip_tpselector tp_sel;
- /* Last transport used. We acquire the transport to keep
- * it open.
- */
- pjsip_transport *last_transport;
- /* Transport being used by pending transaction, for informational purpose,
- * we don't keep this transport.
- */
- pjsip_transport *info_transport;
- };
- PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
- pjsip_regc_cb *cb,
- pjsip_regc **p_regc)
- {
- pj_pool_t *pool;
- pjsip_regc *regc;
- pj_status_t status;
- /* Verify arguments. */
- PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
- pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
- PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
- regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
- regc->pool = pool;
- regc->endpt = endpt;
- regc->token = token;
- regc->cb = cb;
- regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
- regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
- status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
- ®c->lock);
- if (status != PJ_SUCCESS) {
- pj_pool_release(pool);
- return status;
- }
- status = pj_atomic_create(pool, 0, ®c->busy_ctr);
- if (status != PJ_SUCCESS) {
- pj_lock_destroy(regc->lock);
- pj_pool_release(pool);
- return status;
- }
- status = pjsip_auth_clt_init(®c->auth_sess, endpt, regc->pool, 0);
- if (status != PJ_SUCCESS)
- return status;
- pj_list_init(®c->route_set);
- pj_list_init(®c->hdr_list);
- pj_list_init(®c->contact_hdr_list);
- pj_list_init(®c->removed_contact_hdr_list);
- /* Done */
- *p_regc = regc;
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
- {
- return pjsip_regc_destroy2(regc, PJ_TRUE);
- }
- PJ_DEF(pj_status_t) pjsip_regc_destroy2(pjsip_regc *regc, pj_bool_t force)
- {
- PJ_ASSERT_RETURN(regc, PJ_EINVAL);
- pj_lock_acquire(regc->lock);
- if (!force && regc->has_tsx) {
- pj_lock_release(regc->lock);
- return PJ_EBUSY;
- }
- if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) {
- regc->_delete_flag = 1;
- regc->cb = NULL;
- pj_lock_release(regc->lock);
- } else {
- pjsip_tpselector_dec_ref(®c->tp_sel);
- if (regc->last_transport) {
- pjsip_transport_dec_ref(regc->last_transport);
- regc->last_transport = NULL;
- }
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
- regc->timer.id = 0;
- }
- pj_atomic_destroy(regc->busy_ctr);
- pj_lock_release(regc->lock);
- pj_lock_destroy(regc->lock);
- regc->lock = NULL;
- pjsip_auth_clt_deinit(®c->auth_sess);
- pjsip_endpt_release_pool(regc->endpt, regc->pool);
- }
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
- pjsip_regc_info *info )
- {
- PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
- pj_lock_acquire(regc->lock);
- info->server_uri = regc->str_srv_url;
- info->client_uri = regc->from_uri;
- info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx);
- info->auto_reg = regc->auto_reg;
- info->interval = regc->expires;
- info->transport = regc->has_tsx? regc->info_transport :
- regc->last_transport;
-
- if (regc->has_tsx)
- info->next_reg = 0;
- else if (regc->auto_reg == 0)
- info->next_reg = 0;
- else if (regc->expires == PJSIP_REGC_EXPIRATION_NOT_SPECIFIED)
- info->next_reg = regc->expires;
- else {
- pj_time_val now, next_reg;
- next_reg = regc->next_reg;
- pj_gettimeofday(&now);
- if (PJ_TIME_VAL_GT(next_reg, now)) {
- PJ_TIME_VAL_SUB(next_reg, now);
- info->next_reg = next_reg.sec;
- } else {
- info->next_reg = 0;
- }
- }
- pj_lock_release(regc->lock);
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
- {
- return regc->pool;
- }
- static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
- {
- if (expires != regc->expires) {
- regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
- } else {
- regc->expires_hdr = NULL;
- }
- }
- static pj_status_t set_contact( pjsip_regc *regc,
- int contact_cnt,
- const pj_str_t contact[] )
- {
- const pj_str_t CONTACT = { "Contact", 7 };
- pjsip_contact_hdr *h;
- int i;
-
- /* Save existing contact list to removed_contact_hdr_list and
- * clear contact_hdr_list.
- */
- pj_list_merge_last(®c->removed_contact_hdr_list,
- ®c->contact_hdr_list);
- /* Set the expiration of Contacts in to removed_contact_hdr_list
- * zero.
- */
- h = regc->removed_contact_hdr_list.next;
- while (h != ®c->removed_contact_hdr_list) {
- h->expires = 0;
- h = h->next;
- }
- /* Process new contacts */
- for (i=0; i<contact_cnt; ++i) {
- pjsip_contact_hdr *hdr;
- pj_str_t tmp;
- pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
- hdr = (pjsip_contact_hdr*)
- pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
- if (hdr == NULL) {
- PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"",
- (int)tmp.slen, tmp.ptr));
- return PJSIP_EINVALIDURI;
- }
- /* Find the new contact in old contact list. If found, remove
- * the old header from the old header list.
- */
- h = regc->removed_contact_hdr_list.next;
- while (h != ®c->removed_contact_hdr_list) {
- int rc;
- rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
- h->uri, hdr->uri);
- if (rc == 0) {
- /* Match */
- pj_list_erase(h);
- break;
- }
- h = h->next;
- }
- /* If add_xuid_param option is enabled and Contact URI is sip/sips,
- * add xuid parameter to assist matching the Contact URI in the
- * REGISTER response later.
- */
- if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
- PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
- {
- pjsip_param *xuid_param;
- pjsip_sip_uri *sip_uri;
- xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
- xuid_param->name = XUID_PARAM_NAME;
- pj_create_unique_string(regc->pool, &xuid_param->value);
- sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
- pj_list_push_back(&sip_uri->other_param, xuid_param);
- }
- pj_list_push_back(®c->contact_hdr_list, hdr);
- }
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
- const pj_str_t *srv_url,
- const pj_str_t *from_url,
- const pj_str_t *to_url,
- int contact_cnt,
- const pj_str_t contact[],
- pj_uint32_t expires)
- {
- pj_str_t tmp;
- pj_status_t status;
- PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
- expires, PJ_EINVAL);
- /* Copy server URL. */
- pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url);
- /* Set server URL. */
- tmp = regc->str_srv_url;
- regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
- if (regc->srv_url == NULL) {
- return PJSIP_EINVALIDURI;
- }
- /* Set "From" header. */
- pj_strdup_with_null(regc->pool, ®c->from_uri, from_url);
- tmp = regc->from_uri;
- regc->from_hdr = pjsip_from_hdr_create(regc->pool);
- regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (!regc->from_hdr->uri) {
- PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
- (int)from_url->slen, from_url->ptr));
- return PJSIP_EINVALIDURI;
- }
- /* Set "To" header. */
- pj_strdup_with_null(regc->pool, &tmp, to_url);
- regc->to_hdr = pjsip_to_hdr_create(regc->pool);
- regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (!regc->to_hdr->uri) {
- PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s",
- (int)to_url->slen, to_url->ptr));
- return PJSIP_EINVALIDURI;
- }
- /* Set "Contact" header. */
- status = set_contact( regc, contact_cnt, contact);
- if (status != PJ_SUCCESS)
- return status;
- /* Set "Expires" header, if required. */
- set_expires( regc, expires);
- regc->delay_before_refresh = DELAY_BEFORE_REFRESH;
- /* Set "Call-ID" header. */
- regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
- pj_create_unique_string(regc->pool, ®c->cid_hdr->id);
- /* Set "CSeq" header. */
- regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
- regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
- pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD);
- /* Done. */
- return PJ_SUCCESS;
- }
- PJ_DEF(void) pjsip_regc_add_ref( pjsip_regc *regc )
- {
- pj_assert(regc);
- pj_atomic_inc(regc->busy_ctr);
- }
- PJ_DEF(pj_status_t) pjsip_regc_dec_ref( pjsip_regc *regc )
- {
- pj_assert(regc);
- if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
- pjsip_regc_destroy(regc);
- return PJ_EGONE;
- }
-
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
- int count,
- const pjsip_cred_info cred[] )
- {
- PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
- return pjsip_auth_clt_set_credentials(®c->auth_sess, count, cred);
- }
- PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
- const pjsip_auth_clt_pref *pref)
- {
- PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
- return pjsip_auth_clt_set_prefs(®c->auth_sess, pref);
- }
- PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
- const pjsip_route_hdr *route_set)
- {
- const pjsip_route_hdr *chdr;
- PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
- pj_list_init(®c->route_set);
- chdr = route_set->next;
- while (chdr != route_set) {
- pj_list_push_back(®c->route_set, pjsip_hdr_clone(regc->pool, chdr));
- chdr = chdr->next;
- }
- 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;
- }
- /* Release transport */
- PJ_DEF(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc)
- {
- PJ_ASSERT_RETURN(regc, PJ_EINVAL);
- if (regc->last_transport) {
- pjsip_transport_dec_ref(regc->last_transport);
- regc->last_transport = NULL;
- }
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
- const pjsip_hdr *hdr_list)
- {
- const pjsip_hdr *hdr;
- PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
- //This is "add" operation, so don't remove headers.
- //pj_list_init(®c->hdr_list);
- hdr = hdr_list->next;
- while (hdr != hdr_list) {
- pj_list_push_back(®c->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
- hdr = hdr->next;
- }
- return PJ_SUCCESS;
- }
- static pj_status_t create_request(pjsip_regc *regc,
- pjsip_tx_data **p_tdata)
- {
- pj_status_t status;
- pjsip_tx_data *tdata;
- PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
- /* Create the request. */
- status = pjsip_endpt_create_request_from_hdr( regc->endpt,
- pjsip_get_register_method(),
- regc->srv_url,
- regc->from_hdr,
- regc->to_hdr,
- NULL,
- regc->cid_hdr,
- regc->cseq_hdr->cseq,
- NULL,
- &tdata);
- if (status != PJ_SUCCESS)
- return status;
- /* Add cached authorization headers. */
- pjsip_auth_clt_init_req( ®c->auth_sess, tdata );
- /* Add Route headers from route set, ideally after Via header */
- if (!pj_list_empty(®c->route_set)) {
- pjsip_hdr *route_pos;
- const pjsip_route_hdr *route;
- route_pos = (pjsip_hdr*)
- pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
- if (!route_pos)
- route_pos = &tdata->msg->hdr;
- route = regc->route_set.next;
- while (route != ®c->route_set) {
- pjsip_hdr *new_hdr = (pjsip_hdr*)
- pjsip_hdr_clone(tdata->pool, route);
- pj_list_insert_after(route_pos, new_hdr);
- route_pos = new_hdr;
- route = route->next;
- }
- }
- /* Add additional request headers */
- if (!pj_list_empty(®c->hdr_list)) {
- const pjsip_hdr *hdr;
- hdr = regc->hdr_list.next;
- while (hdr != ®c->hdr_list) {
- pjsip_hdr *new_hdr = (pjsip_hdr*)
- pjsip_hdr_clone(tdata->pool, hdr);
- pjsip_msg_add_hdr(tdata->msg, new_hdr);
- hdr = hdr->next;
- }
- }
- /* Done. */
- *p_tdata = tdata;
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
- pjsip_tx_data **p_tdata)
- {
- pjsip_msg *msg;
- pjsip_contact_hdr *hdr;
- const pjsip_hdr *h_allow;
- pj_status_t status;
- pjsip_tx_data *tdata;
- PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
- pj_lock_acquire(regc->lock);
-
- regc->expires_requested = 1;
- status = create_request(regc, &tdata);
- if (status != PJ_SUCCESS) {
- pj_lock_release(regc->lock);
- return status;
- }
- msg = tdata->msg;
- /* Add Contact headers. */
- hdr = regc->contact_hdr_list.next;
- while (hdr != ®c->contact_hdr_list) {
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)
- pjsip_hdr_clone(tdata->pool, hdr));
- hdr = hdr->next;
- }
- /* Also add bindings which are to be removed */
- while (!pj_list_empty(®c->removed_contact_hdr_list)) {
- hdr = regc->removed_contact_hdr_list.next;
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)
- pjsip_hdr_clone(tdata->pool, hdr));
- pj_list_erase(hdr);
- }
-
- if (regc->expires_hdr)
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)
- pjsip_hdr_clone(tdata->pool,
- regc->expires_hdr));
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
- regc->timer.id = 0;
- }
- /* Add Allow header (https://github.com/pjsip/pjproject/issues/1039) */
- h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL);
- if (h_allow) {
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)
- pjsip_hdr_clone(tdata->pool, h_allow));
- }
- regc->auto_reg = autoreg;
- pj_lock_release(regc->lock);
- /* Done */
- *p_tdata = tdata;
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
- pjsip_tx_data **p_tdata)
- {
- pjsip_tx_data *tdata;
- pjsip_msg *msg;
- pjsip_hdr *hdr;
- pj_status_t status;
- PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
- pj_lock_acquire(regc->lock);
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
- regc->timer.id = 0;
- }
- regc->expires_requested = 0;
- status = create_request(regc, &tdata);
- if (status != PJ_SUCCESS) {
- pj_lock_release(regc->lock);
- return status;
- }
- msg = tdata->msg;
- /* Add Contact headers. */
- hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
- while ((void*)hdr != (void*)®c->contact_hdr_list) {
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)
- pjsip_hdr_clone(tdata->pool, hdr));
- hdr = hdr->next;
- }
- /* Also add bindings which are to be removed */
- while (!pj_list_empty(®c->removed_contact_hdr_list)) {
- hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)
- pjsip_hdr_clone(tdata->pool, hdr));
- pj_list_erase(hdr);
- }
- /* Add Expires:0 header */
- hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
- pjsip_msg_add_hdr(msg, hdr);
- pj_lock_release(regc->lock);
- *p_tdata = tdata;
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
- pjsip_tx_data **p_tdata)
- {
- pjsip_tx_data *tdata;
- pjsip_contact_hdr *hcontact;
- pjsip_hdr *hdr;
- pjsip_msg *msg;
- pj_status_t status;
- PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
- pj_lock_acquire(regc->lock);
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
- regc->timer.id = 0;
- }
- status = create_request(regc, &tdata);
- if (status != PJ_SUCCESS) {
- pj_lock_release(regc->lock);
- return status;
- }
- msg = tdata->msg;
- /* Clear removed_contact_hdr_list */
- pj_list_init(®c->removed_contact_hdr_list);
- /* Add Contact:* header */
- hcontact = pjsip_contact_hdr_create(tdata->pool);
- hcontact->star = 1;
- pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
-
- /* Add Expires:0 header */
- hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
- pjsip_msg_add_hdr(msg, hdr);
- pj_lock_release(regc->lock);
- *p_tdata = tdata;
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
- int contact_cnt,
- const pj_str_t contact[] )
- {
- pj_status_t status;
- PJ_ASSERT_RETURN(regc, PJ_EINVAL);
- pj_lock_acquire(regc->lock);
- status = set_contact( regc, contact_cnt, contact );
- pj_lock_release(regc->lock);
- return status;
- }
- PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
- pj_uint32_t expires )
- {
- PJ_ASSERT_RETURN(regc, PJ_EINVAL);
- pj_lock_acquire(regc->lock);
- set_expires( regc, expires );
- pj_lock_release(regc->lock);
- return PJ_SUCCESS;
- }
- static void cbparam_init( struct pjsip_regc_cbparam *cbparam,
- pjsip_regc *regc,
- pj_status_t status, int st_code,
- const pj_str_t *reason,
- pjsip_rx_data *rdata, pj_uint32_t expiration,
- int contact_cnt, pjsip_contact_hdr *contact[],
- pj_bool_t is_unreg)
- {
- cbparam->regc = regc;
- cbparam->token = regc->token;
- cbparam->status = status;
- cbparam->code = st_code;
- cbparam->reason = *reason;
- cbparam->rdata = rdata;
- cbparam->contact_cnt = contact_cnt;
- cbparam->expiration = (expiration != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED?
- expiration: regc->expires_requested);
- cbparam->is_unreg = is_unreg;
- if (contact_cnt) {
- pj_memcpy( cbparam->contact, contact,
- contact_cnt*sizeof(pjsip_contact_hdr*));
- }
- }
- static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
- const pj_str_t *reason,
- pjsip_rx_data *rdata, pj_uint32_t expiration,
- int contact_cnt, pjsip_contact_hdr *contact[],
- pj_bool_t is_unreg)
- {
- struct pjsip_regc_cbparam cbparam;
- if (!regc->cb)
- return;
- cbparam_init(&cbparam, regc, status, st_code, reason, rdata, expiration,
- contact_cnt, contact, is_unreg);
- (*regc->cb)(&cbparam);
- }
- static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
- {
- pjsip_regc *regc = (pjsip_regc*) entry->user_data;
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- PJ_UNUSED_ARG(timer_heap);
- /* Temporarily increase busy flag to prevent regc from being deleted
- * in pjsip_regc_send() or in the callback
- */
- pjsip_regc_add_ref(regc);
- entry->id = 0;
- status = pjsip_regc_register(regc, 1, &tdata);
- if (status == PJ_SUCCESS) {
- status = pjsip_regc_send(regc, tdata);
- }
-
- if (status != PJ_SUCCESS && regc->cb) {
- char errmsg[PJ_ERR_MSG_SIZE];
- pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
- call_callback(regc, status, 400, &reason, NULL, NOEXP, 0, NULL,
- PJ_FALSE);
- }
- /* Delete the record if user destroy regc during the callback. */
- pjsip_regc_dec_ref(regc);
- }
- static void schedule_registration ( pjsip_regc *regc, pj_uint32_t expiration )
- {
- if (regc->auto_reg && expiration > 0 && expiration != NOEXP) {
- pj_time_val delay = { 0, 0};
- pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(regc->endpt),
- ®c->timer, 0);
- delay.sec = expiration - regc->delay_before_refresh;
- if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
- delay.sec > (pj_int32_t)regc->expires)
- {
- delay.sec = regc->expires;
- }
- if (delay.sec < DELAY_BEFORE_REFRESH)
- delay.sec = DELAY_BEFORE_REFRESH;
- regc->timer.cb = ®c_refresh_timer_cb;
- regc->timer.id = REFRESH_TIMER;
- regc->timer.user_data = regc;
- pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay);
- pj_gettimeofday(®c->last_reg);
- regc->next_reg = regc->last_reg;
- regc->next_reg.sec += delay.sec;
- }
- }
- PJ_DEF(pj_status_t) pjsip_regc_set_reg_tsx_cb( pjsip_regc *regc,
- pjsip_regc_tsx_cb *tsx_cb)
- {
- PJ_ASSERT_RETURN(regc, PJ_EINVAL);
- regc->tsx_cb = tsx_cb;
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
- pjsip_host_port *via_addr,
- pjsip_transport *via_tp)
- {
- PJ_ASSERT_RETURN(regc, PJ_EINVAL);
- if (!via_addr)
- pj_bzero(®c->via_addr, sizeof(regc->via_addr));
- else {
- if (pj_strcmp(®c->via_addr.host, &via_addr->host))
- pj_strdup(regc->pool, ®c->via_addr.host, &via_addr->host);
- regc->via_addr.port = via_addr->port;
- }
- regc->via_tp = via_tp;
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t)
- pjsip_regc_set_delay_before_refresh( pjsip_regc *regc,
- pj_uint32_t delay )
- {
- PJ_ASSERT_RETURN(regc, PJ_EINVAL);
- if (delay > regc->expires)
- return PJ_ETOOBIG;
- pj_lock_acquire(regc->lock);
- if (regc->delay_before_refresh != delay)
- {
- regc->delay_before_refresh = delay;
- if (regc->timer.id != 0) {
- /* Cancel registration timer */
- pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
- regc->timer.id = 0;
- /* Schedule next registration */
- schedule_registration(regc, regc->expires);
- }
- }
- pj_lock_release(regc->lock);
- return PJ_SUCCESS;
- }
- static pj_uint32_t calculate_response_expiration(const pjsip_regc *regc,
- const pjsip_rx_data *rdata,
- unsigned *contact_cnt,
- unsigned max_contact,
- pjsip_contact_hdr *contacts[])
- {
- pj_uint32_t expiration = NOEXP;
- const pjsip_msg *msg = rdata->msg_info.msg;
- const pjsip_hdr *hdr;
- /* Enumerate all Contact headers in the response */
- *contact_cnt = 0;
- for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
- if (hdr->type == PJSIP_H_CONTACT &&
- *contact_cnt < max_contact)
- {
- contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
- ++(*contact_cnt);
- }
- }
- if (regc->current_op == REGC_REGISTERING) {
- pj_bool_t has_our_contact = PJ_FALSE;
- const pjsip_expires_hdr *expires;
- /* Get Expires header */
- expires = (const pjsip_expires_hdr*)
- pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
- /* Try to find the Contact URIs that we register, in the response
- * to get the expires value. We'll try both with comparing the URI
- * and comparing the extension param only.
- */
- if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
- unsigned i;
- for (i=0; i<*contact_cnt; ++i) {
- const pjsip_contact_hdr *our_hdr;
- our_hdr = (const pjsip_contact_hdr*)
- regc->contact_hdr_list.next;
- /* Match with our Contact header(s) */
- while ((void*)our_hdr != (void*)®c->contact_hdr_list) {
- const pjsip_uri *uri1, *uri2;
- pj_bool_t matched = PJ_FALSE;
- /* Exclude the display name when comparing the URI
- * since server may not return it.
- */
- uri1 = (const pjsip_uri*)
- pjsip_uri_get_uri(contacts[i]->uri);
- uri2 = (const pjsip_uri*)
- pjsip_uri_get_uri(our_hdr->uri);
- /* First try with exact matching, according to RFC 3261
- * Section 19.1.4 URI Comparison
- */
- if (pjsip_cfg()->regc.check_contact) {
- matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
- uri1, uri2)==0;
- }
- /* If no match is found, try with matching the extension
- * parameter only if extension parameter was added.
- */
- if (!matched && regc->add_xuid_param &&
- (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
- PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
- (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
- PJSIP_URI_SCHEME_IS_SIPS(uri2)))
- {
- const pjsip_sip_uri *sip_uri1, *sip_uri2;
- const pjsip_param *p1, *p2;
-
- sip_uri1 = (const pjsip_sip_uri*)uri1;
- sip_uri2 = (const pjsip_sip_uri*)uri2;
- p1 = pjsip_param_cfind(&sip_uri1->other_param,
- &XUID_PARAM_NAME);
- p2 = pjsip_param_cfind(&sip_uri2->other_param,
- &XUID_PARAM_NAME);
- matched = p1 && p2 &&
- pj_strcmp(&p1->value, &p2->value)==0;
- }
- if (matched) {
- has_our_contact = PJ_TRUE;
- if (contacts[i]->expires != PJSIP_EXPIRES_NOT_SPECIFIED
- && contacts[i]->expires < expiration)
- {
- /* Get the lowest expiration time. */
- expiration = contacts[i]->expires;
- }
- break;
- }
- our_hdr = our_hdr->next;
- } /* while ((void.. */
- } /* for (i=.. */
- /* If matching Contact header(s) are found but the
- * header doesn't contain expires parameter, get the
- * expiration value from the Expires header. And
- * if Expires header is not present, get the expiration
- * value from the request.
- */
- if (has_our_contact && expiration == NOEXP) {
- if (expires) {
- expiration = expires->ivalue;
- } else if (regc->expires_hdr) {
- expiration = regc->expires_hdr->ivalue;
- } else {
- /* We didn't request explicit expiration value,
- * and server doesn't specify it either. This
- * shouldn't happen unless we have a broken
- * registrar.
- */
- expiration = 3600;
- }
- }
- }
- /* If we still couldn't get matching Contact header(s), it means
- * there must be something wrong with the registrar (e.g. it may
- * have modified the URI's in the response, which is prohibited).
- */
- if (expiration==NOEXP) {
- /* If the number of Contact headers in the response matches
- * ours, they're all probably ours. Get the expiration
- * from there if this is the case, or from Expires header
- * if we don't have exact Contact header count, or
- * from the request as the last resort.
- */
- pj_size_t our_contact_cnt;
- our_contact_cnt = pj_list_size(®c->contact_hdr_list);
- if (*contact_cnt == our_contact_cnt && *contact_cnt &&
- contacts[0]->expires != PJSIP_EXPIRES_NOT_SPECIFIED)
- {
- expiration = contacts[0]->expires;
- } else if (expires)
- expiration = expires->ivalue;
- else if (regc->expires_hdr)
- expiration = regc->expires_hdr->ivalue;
- else
- expiration = 3600;
- }
- } else {
- /* Just assume that the unregistration has been successful. */
- expiration = 0;
- }
- /* Must have expiration value by now */
- pj_assert(expiration != NOEXP);
- return expiration;
- }
- static void regc_tsx_callback(void *token, pjsip_event *event)
- {
- pj_status_t status;
- pjsip_regc *regc = (pjsip_regc*) token;
- pjsip_transaction *tsx = event->body.tsx_state.tsx;
- pj_bool_t handled = PJ_TRUE;
- pj_bool_t update_contact = PJ_FALSE;
- pjsip_regc_add_ref(regc);
- pj_lock_acquire(regc->lock);
- /* Decrement pending transaction counter. */
- pj_assert(regc->has_tsx);
- regc->has_tsx = PJ_FALSE;
- /* Add reference to the transport */
- if (tsx->transport != regc->last_transport) {
- if (regc->last_transport) {
- pjsip_transport_dec_ref(regc->last_transport);
- regc->last_transport = NULL;
- }
- if (tsx->transport) {
- regc->last_transport = tsx->transport;
- pjsip_transport_add_ref(regc->last_transport);
- }
- }
- if (regc->_delete_flag == 0 && regc->tsx_cb &&
- regc->current_op == REGC_REGISTERING)
- {
- struct pjsip_regc_tsx_cb_param param;
- param.contact_cnt = -1;
- cbparam_init(¶m.cbparam, regc, PJ_SUCCESS, tsx->status_code,
- &tsx->status_text,
- (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
- event->body.tsx_state.src.rdata : NULL,
- NOEXP, 0, NULL, PJ_FALSE);
- /* Call regc tsx callback before handling any response */
- pj_lock_release(regc->lock);
- (*regc->tsx_cb)(¶m);
- pj_lock_acquire(regc->lock);
- if (param.contact_cnt >= 0) {
- /* Since we receive non-2xx response, it means that (some) contact
- * bindings haven't been established so we can safely remove these
- * contact headers. This is to avoid removing non-existent contact
- * bindings later.
- */
- if (tsx->status_code/100 != 2) {
- pjsip_contact_hdr *h;
- h = regc->contact_hdr_list.next;
- while (h != ®c->contact_hdr_list) {
- pjsip_contact_hdr *next = h->next;
- if (h->expires == PJSIP_EXPIRES_NOT_SPECIFIED) {
- pj_list_erase(h);
- }
- h = next;
- }
- }
- /* Update contact address */
- pjsip_regc_update_contact(regc, param.contact_cnt, param.contact);
- update_contact = PJ_TRUE;
- }
- }
- /* Handle 401/407 challenge (even when _delete_flag is set) */
- if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
- tsx->status_code == PJSIP_SC_UNAUTHORIZED)
- {
- pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
- pjsip_tx_data *tdata;
- pj_bool_t is_unreg;
- /* reset current op */
- is_unreg = (regc->current_op == REGC_UNREGISTERING);
- regc->current_op = REGC_IDLE;
- if (update_contact) {
- pjsip_msg *msg;
- pjsip_hdr *hdr, *ins_hdr;
- pjsip_contact_hdr *chdr;
- /* Delete Contact headers, but we shouldn't delete headers
- * which are supposed to remove contact bindings since
- * we cannot reconstruct those headers.
- */
- msg = tsx->last_tx->msg;
- hdr = msg->hdr.next;
- ins_hdr = &msg->hdr;
- while (hdr != &msg->hdr) {
- pjsip_hdr *next = hdr->next;
- if (hdr->type == PJSIP_H_CONTACT) {
- chdr = (pjsip_contact_hdr *)hdr;
- if (chdr->expires != 0) {
- pj_list_erase(hdr);
- ins_hdr = next;
- }
- }
- hdr = next;
- }
- /* Add Contact headers. */
- chdr = regc->contact_hdr_list.next;
- while (chdr != ®c->contact_hdr_list) {
- pj_list_insert_before(ins_hdr, (pjsip_hdr*)
- pjsip_hdr_clone(tsx->last_tx->pool, chdr));
- chdr = chdr->next;
- }
- /* Also add bindings which are to be removed */
- while (!pj_list_empty(®c->removed_contact_hdr_list)) {
- chdr = regc->removed_contact_hdr_list.next;
- pj_list_insert_before(ins_hdr, (pjsip_hdr*)
- pjsip_hdr_clone(tsx->last_tx->pool, chdr));
- pj_list_erase(chdr);
- }
- }
- status = pjsip_auth_clt_reinit_req( ®c->auth_sess,
- rdata,
- tsx->last_tx,
- &tdata);
- if (status == PJ_SUCCESS) {
- /* Need to unlock the regc temporarily while sending the message
- * to prevent deadlock (see ticket #2260 and #1247).
- * It should be safe to do this since the regc's refcount has been
- * incremented.
- */
- pj_lock_release(regc->lock);
- status = pjsip_regc_send(regc, tdata);
- pj_lock_acquire(regc->lock);
- }
-
- if (status != PJ_SUCCESS) {
- /* Only call callback if application is still interested
- * in it.
- */
- if (regc->_delete_flag == 0) {
- /* Should be safe to release the lock temporarily.
- * We do this to avoid deadlock.
- */
- pj_lock_release(regc->lock);
- call_callback(regc, status, tsx->status_code,
- &rdata->msg_info.msg->line.status.reason,
- rdata, NOEXP, 0, NULL, is_unreg);
- pj_lock_acquire(regc->lock);
- }
- }
- } else if (regc->_delete_flag) {
- /* User has called pjsip_regc_destroy(), so don't call callback.
- * This regc will be destroyed later in this function.
- */
- /* Just reset current op */
- regc->current_op = REGC_IDLE;
- } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
- regc->current_op == REGC_REGISTERING)
- {
- /* Handle 423 response automatically:
- * - set requested expiration to Min-Expires header, ONLY IF
- * the original request is a registration (as opposed to
- * unregistration) and the requested expiration was indeed
- * lower than Min-Expires)
- * - resend the request
- */
- pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
- pjsip_min_expires_hdr *me_hdr;
- pjsip_tx_data *tdata;
- pj_uint32_t min_exp;
- /* reset current op */
- regc->current_op = REGC_IDLE;
- /* Update requested expiration */
- me_hdr = (pjsip_min_expires_hdr*)
- pjsip_msg_find_hdr(rdata->msg_info.msg,
- PJSIP_H_MIN_EXPIRES, NULL);
- if (me_hdr) {
- min_exp = me_hdr->ivalue;
- } else {
- /* Broken server, Min-Expires doesn't exist.
- * Just guestimate then, BUT ONLY if if this is the
- * first time we received such response.
- */
- enum {
- /* Note: changing this value would require changing couple of
- * Python test scripts.
- */
- UNSPECIFIED_MIN_EXPIRES = 3601
- };
- if (!regc->expires_hdr ||
- regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
- {
- min_exp = UNSPECIFIED_MIN_EXPIRES;
- } else {
- handled = PJ_FALSE;
- PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
- "without Min-Expires header is invalid"));
- goto handle_err;
- }
- }
- if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
- /* But we already send with greater expiration time, why does
- * the server send us with 423? Oh well, just fail the request.
- */
- handled = PJ_FALSE;
- PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
- "Min-Expires header value in response"));
- goto handle_err;
- }
- set_expires(regc, min_exp);
- status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
- if (status == PJ_SUCCESS) {
- /* Need to unlock the regc temporarily while sending the message
- * to prevent deadlock (see ticket #2260 and #1247).
- * It should be safe to do this since the regc's refcount has been
- * incremented.
- */
- pj_lock_release(regc->lock);
- status = pjsip_regc_send(regc, tdata);
- pj_lock_acquire(regc->lock);
- }
- if (status != PJ_SUCCESS) {
- /* Only call callback if application is still interested
- * in it.
- */
- if (!regc->_delete_flag) {
- /* Should be safe to release the lock temporarily.
- * We do this to avoid deadlock.
- */
- pj_lock_release(regc->lock);
- call_callback(regc, status, tsx->status_code,
- &rdata->msg_info.msg->line.status.reason,
- rdata, NOEXP, 0, NULL, PJ_FALSE);
- pj_lock_acquire(regc->lock);
- }
- }
- } else {
- handled = PJ_FALSE;
- }
- handle_err:
- if (!handled) {
- pjsip_rx_data *rdata;
- pj_uint32_t expiration = NOEXP;
- unsigned contact_cnt = 0;
- pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
- pj_bool_t is_unreg;
- if (tsx->status_code/100 == 2) {
- rdata = event->body.tsx_state.src.rdata;
- /* Calculate expiration */
- expiration = calculate_response_expiration(regc, rdata,
- &contact_cnt,
- PJSIP_REGC_MAX_CONTACT,
- contact);
- #if PJSIP_REGISTER_ALLOW_EXP_REFRESH
- if (expiration == 0 && regc->current_op != REGC_UNREGISTERING) {
- /* Response contain expires contact param 0, allow client to
- * continue refresh registration.
- * Refer to: https://github.com/pjsip/pjproject/pull/2809
- */
- const pjsip_msg *msg = rdata->msg_info.msg;
- const pjsip_expires_hdr *expires;
- expires = (const pjsip_expires_hdr*) pjsip_msg_find_hdr(msg,
- PJSIP_H_EXPIRES, NULL);
- if (expires) {
- expiration = expires->ivalue;
- } else if (regc->expires_hdr && regc->expires_hdr->ivalue) {
- expiration = regc->expires_hdr->ivalue;
- } else {
- expiration = 3600;
- }
- PJ_LOG(4, (THIS_FILE, "Modify response's expiration from 0 "
- "to %d", expiration));
- }
- #endif
- /* Schedule next registration */
- schedule_registration(regc, expiration);
- } else {
- rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
- event->body.tsx_state.src.rdata : NULL;
- }
- /* Update registration */
- // if (expiration==NOEXP) expiration=-1;
- regc->expires = expiration;
- /* Mark operation as complete */
- is_unreg = (regc->current_op == REGC_UNREGISTERING);
- regc->current_op = REGC_IDLE;
- /* Call callback. */
- /* Should be safe to release the lock temporarily.
- * We do this to avoid deadlock.
- */
- pj_lock_release(regc->lock);
- call_callback(regc, PJ_SUCCESS, tsx->status_code,
- (rdata ? &rdata->msg_info.msg->line.status.reason
- : &tsx->status_text),
- rdata, expiration,
- contact_cnt, contact, is_unreg);
- pj_lock_acquire(regc->lock);
- }
- pj_lock_release(regc->lock);
- /* Delete the record if user destroy regc during the callback. */
- pjsip_regc_dec_ref(regc);
- }
- PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
- {
- pj_status_t status;
- pjsip_cseq_hdr *cseq_hdr;
- pjsip_expires_hdr *expires_hdr;
- pj_int32_t cseq;
- pjsip_regc_add_ref(regc);
- pj_lock_acquire(regc->lock);
- /* Make sure we don't have pending transaction. */
- if (regc->has_tsx) {
- PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
- "transaction pending"));
- pjsip_tx_data_dec_ref( tdata );
- pj_lock_release(regc->lock);
- pj_atomic_dec(regc->busy_ctr);
- return PJSIP_EBUSY;
- }
- /* Just regc->has_tsx check above should be enough. This assertion check
- * may cause problem, e.g: when regc_tsx_callback() invokes callback,
- * lock is released and 'has_tsx' is set to FALSE and 'current_op' has
- * not been updated to REGC_IDLE yet.
- */
- //pj_assert(regc->current_op == REGC_IDLE);
- /* Invalidate message buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
- /* Increment CSeq */
- cseq = ++regc->cseq_hdr->cseq;
- cseq_hdr = (pjsip_cseq_hdr*)
- pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq_hdr->cseq = cseq;
- /* Find Expires header */
- expires_hdr = (pjsip_expires_hdr*)
- pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
- /* Bind to transport selector */
- pjsip_tx_data_set_transport(tdata, ®c->tp_sel);
- regc->has_tsx = PJ_TRUE;
- /* Set current operation based on the value of Expires header */
- if (expires_hdr && expires_hdr->ivalue==0)
- regc->current_op = REGC_UNREGISTERING;
- else
- regc->current_op = REGC_REGISTERING;
-
- if (expires_hdr && expires_hdr->ivalue)
- regc->expires_requested = expires_hdr->ivalue;
- /* Prevent deletion of tdata, e.g: when something wrong in sending,
- * we need tdata to retrieve the transport.
- */
- pjsip_tx_data_add_ref(tdata);
- /* If via_addr is set, use this address for the Via header. */
- if (regc->via_addr.host.slen > 0) {
- tdata->via_addr = regc->via_addr;
- tdata->via_tp = regc->via_tp;
- }
- /* Need to unlock the regc temporarily while sending the message to
- * prevent deadlock (https://github.com/pjsip/pjproject/issues/1247).
- * It should be safe to do this since the regc's refcount has been
- * incremented.
- */
- pj_lock_release(regc->lock);
- /* Now send the message */
- status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
- regc, ®c_tsx_callback);
-
- /* Reacquire the lock */
- pj_lock_acquire(regc->lock);
- if (status!=PJ_SUCCESS) {
- /* On failure, regc_tsx_callback() may not be called, so we need
- * to reset regc->has_tsx here (see also ticket #1936).
- * But note that we are releasing the lock when sending the request
- * above, so there can be a race with another registration send.
- */
- if (cseq == regc->cseq_hdr->cseq) {
- regc->has_tsx = PJ_FALSE;
- }
- PJ_PERROR(4,(THIS_FILE, status, "Error sending request"));
- }
- /* Get last transport used and add reference to it */
- //if (tdata->tp_info.transport != regc->last_transport &&
- // status==PJ_SUCCESS)
- //{
- // if (regc->last_transport) {
- // pjsip_transport_dec_ref(regc->last_transport);
- // regc->last_transport = NULL;
- // }
- // if (tdata->tp_info.transport) {
- // regc->last_transport = tdata->tp_info.transport;
- // pjsip_transport_add_ref(regc->last_transport);
- // }
- //}
- // Update: don't add_ref() or use the transport info from tdata other than
- // for informational purpose (e.g: comparing the pointers to check
- // if a disconnected transport is the registration transport), see
- // pjsip_tx_data.tp_info docs about its valid period.
- // Note that the send operation may be async and transport may
- // have been destroyed here.
- regc->info_transport = status==PJ_SUCCESS? tdata->tp_info.transport :
- NULL;
- /* Release tdata */
- pjsip_tx_data_dec_ref(tdata);
- pj_lock_release(regc->lock);
- /* Delete the record if user destroy regc during the callback. */
- pjsip_regc_dec_ref(regc);
- return status;
- }
|