1
/* $Id: sip_reg.c 4319 2013-01-16 10:20:55Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjsip-ua/sip_regc.h>
21
#include <pjsip/sip_endpoint.h>
22
#include <pjsip/sip_parser.h>
23
#include <pjsip/sip_module.h>
24
#include <pjsip/sip_transaction.h>
25
#include <pjsip/sip_event.h>
26
#include <pjsip/sip_util.h>
27
#include <pjsip/sip_auth_msg.h>
28
#include <pjsip/sip_errno.h>
29
#include <pj/assert.h>
36
#include <pj/string.h>
39
#define REFRESH_TIMER 1
40
#define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
41
#define THIS_FILE "sip_reg.c"
43
/* Outgoing transaction timeout when server sends 100 but never replies
44
* with final response. Value is in MILISECONDS!
46
#define REGC_TSX_TIMEOUT 33000
48
enum { NOEXP = 0x1FFFFFFF };
50
static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
53
/* Current/pending operation */
62
* SIP client registration structure.
67
pjsip_endpoint *endpt;
69
pj_bool_t _delete_flag;
71
pj_atomic_t *busy_ctr;
72
enum regc_op current_op;
74
pj_bool_t add_xuid_param;
81
pjsip_cid_hdr *cid_hdr;
82
pjsip_cseq_hdr *cseq_hdr;
84
pjsip_from_hdr *from_hdr;
86
pjsip_contact_hdr contact_hdr_list;
87
pjsip_contact_hdr removed_contact_hdr_list;
88
pjsip_expires_hdr *expires_hdr;
90
pj_uint32_t delay_before_refresh;
91
pjsip_route_hdr route_set;
93
pjsip_host_port via_addr;
96
/* Authorization sessions. */
97
pjsip_auth_clt_sess auth_sess;
99
/* Auto refresh registration. */
101
pj_time_val last_reg;
102
pj_time_val next_reg;
103
pj_timer_entry timer;
105
/* Transport selector */
106
pjsip_tpselector tp_sel;
108
/* Last transport used. We acquire the transport to keep
111
pjsip_transport *last_transport;
115
PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
123
/* Verify arguments. */
124
PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
126
pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
127
PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
129
regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
135
regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
136
regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
138
status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
140
if (status != PJ_SUCCESS) {
141
pj_pool_release(pool);
145
status = pj_atomic_create(pool, 0, ®c->busy_ctr);
146
if (status != PJ_SUCCESS) {
147
pj_lock_destroy(regc->lock);
148
pj_pool_release(pool);
152
status = pjsip_auth_clt_init(®c->auth_sess, endpt, regc->pool, 0);
153
if (status != PJ_SUCCESS)
156
pj_list_init(®c->route_set);
157
pj_list_init(®c->hdr_list);
158
pj_list_init(®c->contact_hdr_list);
159
pj_list_init(®c->removed_contact_hdr_list);
167
PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
169
PJ_ASSERT_RETURN(regc, PJ_EINVAL);
171
pj_lock_acquire(regc->lock);
172
if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) {
173
regc->_delete_flag = 1;
175
pj_lock_release(regc->lock);
177
pjsip_tpselector_dec_ref(®c->tp_sel);
178
if (regc->last_transport) {
179
pjsip_transport_dec_ref(regc->last_transport);
180
regc->last_transport = NULL;
182
if (regc->timer.id != 0) {
183
pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
186
pj_atomic_destroy(regc->busy_ctr);
187
pj_lock_release(regc->lock);
188
pj_lock_destroy(regc->lock);
190
pjsip_endpt_release_pool(regc->endpt, regc->pool);
197
PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
198
pjsip_regc_info *info )
200
PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
202
pj_lock_acquire(regc->lock);
204
info->server_uri = regc->str_srv_url;
205
info->client_uri = regc->from_uri;
206
info->is_busy = (pj_atomic_get(regc->busy_ctr) || regc->has_tsx);
207
info->auto_reg = regc->auto_reg;
208
info->interval = regc->expires;
209
info->transport = regc->last_transport;
213
else if (regc->auto_reg == 0)
215
else if (regc->expires < 0)
216
info->next_reg = regc->expires;
218
pj_time_val now, next_reg;
220
next_reg = regc->next_reg;
221
pj_gettimeofday(&now);
222
PJ_TIME_VAL_SUB(next_reg, now);
223
info->next_reg = next_reg.sec;
226
pj_lock_release(regc->lock);
232
PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
237
static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
239
if (expires != regc->expires) {
240
regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
242
regc->expires_hdr = NULL;
247
static pj_status_t set_contact( pjsip_regc *regc,
249
const pj_str_t contact[] )
251
const pj_str_t CONTACT = { "Contact", 7 };
252
pjsip_contact_hdr *h;
255
/* Save existing contact list to removed_contact_hdr_list and
256
* clear contact_hdr_list.
258
pj_list_merge_last(®c->removed_contact_hdr_list,
259
®c->contact_hdr_list);
261
/* Set the expiration of Contacts in to removed_contact_hdr_list
264
h = regc->removed_contact_hdr_list.next;
265
while (h != ®c->removed_contact_hdr_list) {
270
/* Process new contacts */
271
for (i=0; i<contact_cnt; ++i) {
272
pjsip_contact_hdr *hdr;
275
pj_strdup_with_null(regc->pool, &tmp, &contact[i]);
276
hdr = (pjsip_contact_hdr*)
277
pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL);
279
PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"",
280
(int)tmp.slen, tmp.ptr));
281
return PJSIP_EINVALIDURI;
284
/* Find the new contact in old contact list. If found, remove
285
* the old header from the old header list.
287
h = regc->removed_contact_hdr_list.next;
288
while (h != ®c->removed_contact_hdr_list) {
291
rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
302
/* If add_xuid_param option is enabled and Contact URI is sip/sips,
303
* add xuid parameter to assist matching the Contact URI in the
304
* REGISTER response later.
306
if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
307
PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
309
pjsip_param *xuid_param;
310
pjsip_sip_uri *sip_uri;
312
xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param);
313
xuid_param->name = XUID_PARAM_NAME;
314
pj_create_unique_string(regc->pool, &xuid_param->value);
316
sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
317
pj_list_push_back(&sip_uri->other_param, xuid_param);
320
pj_list_push_back(®c->contact_hdr_list, hdr);
327
PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
328
const pj_str_t *srv_url,
329
const pj_str_t *from_url,
330
const pj_str_t *to_url,
332
const pj_str_t contact[],
338
PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
341
/* Copy server URL. */
342
pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url);
344
/* Set server URL. */
345
tmp = regc->str_srv_url;
346
regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
347
if (regc->srv_url == NULL) {
348
return PJSIP_EINVALIDURI;
351
/* Set "From" header. */
352
pj_strdup_with_null(regc->pool, ®c->from_uri, from_url);
353
tmp = regc->from_uri;
354
regc->from_hdr = pjsip_from_hdr_create(regc->pool);
355
regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
356
PJSIP_PARSE_URI_AS_NAMEADDR);
357
if (!regc->from_hdr->uri) {
358
PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s",
359
from_url->slen, from_url->ptr));
360
return PJSIP_EINVALIDURI;
363
/* Set "To" header. */
364
pj_strdup_with_null(regc->pool, &tmp, to_url);
365
regc->to_hdr = pjsip_to_hdr_create(regc->pool);
366
regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
367
PJSIP_PARSE_URI_AS_NAMEADDR);
368
if (!regc->to_hdr->uri) {
369
PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
370
return PJSIP_EINVALIDURI;
374
/* Set "Contact" header. */
375
status = set_contact( regc, contact_cnt, contact);
376
if (status != PJ_SUCCESS)
379
/* Set "Expires" header, if required. */
380
set_expires( regc, expires);
381
regc->delay_before_refresh = DELAY_BEFORE_REFRESH;
383
/* Set "Call-ID" header. */
384
regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
385
pj_create_unique_string(regc->pool, ®c->cid_hdr->id);
387
/* Set "CSeq" header. */
388
regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
389
regc->cseq_hdr->cseq = pj_rand() % 0xFFFF;
390
pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD);
396
PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
398
const pjsip_cred_info cred[] )
400
PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
401
return pjsip_auth_clt_set_credentials(®c->auth_sess, count, cred);
404
PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
405
const pjsip_auth_clt_pref *pref)
407
PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
408
return pjsip_auth_clt_set_prefs(®c->auth_sess, pref);
411
PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
412
const pjsip_route_hdr *route_set)
414
const pjsip_route_hdr *chdr;
416
PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
418
pj_list_init(®c->route_set);
420
chdr = route_set->next;
421
while (chdr != route_set) {
422
pj_list_push_back(®c->route_set, pjsip_hdr_clone(regc->pool, chdr));
431
* Bind client registration to a specific transport/listener.
433
PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
434
const pjsip_tpselector *sel)
436
PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
438
pjsip_tpselector_dec_ref(®c->tp_sel);
439
pj_memcpy(®c->tp_sel, sel, sizeof(*sel));
440
pjsip_tpselector_add_ref(®c->tp_sel);
445
/* Release transport */
446
PJ_DEF(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc)
448
PJ_ASSERT_RETURN(regc, PJ_EINVAL);
449
if (regc->last_transport) {
450
pjsip_transport_dec_ref(regc->last_transport);
451
regc->last_transport = NULL;
457
PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
458
const pjsip_hdr *hdr_list)
460
const pjsip_hdr *hdr;
462
PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
464
//This is "add" operation, so don't remove headers.
465
//pj_list_init(®c->hdr_list);
467
hdr = hdr_list->next;
468
while (hdr != hdr_list) {
469
pj_list_push_back(®c->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
476
static pj_status_t create_request(pjsip_regc *regc,
477
pjsip_tx_data **p_tdata)
480
pjsip_tx_data *tdata;
482
PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
484
/* Create the request. */
485
status = pjsip_endpt_create_request_from_hdr( regc->endpt,
486
pjsip_get_register_method(),
492
regc->cseq_hdr->cseq,
495
if (status != PJ_SUCCESS)
498
/* Add cached authorization headers. */
499
pjsip_auth_clt_init_req( ®c->auth_sess, tdata );
501
/* Add Route headers from route set, ideally after Via header */
502
if (!pj_list_empty(®c->route_set)) {
503
pjsip_hdr *route_pos;
504
const pjsip_route_hdr *route;
506
route_pos = (pjsip_hdr*)
507
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
509
route_pos = &tdata->msg->hdr;
511
route = regc->route_set.next;
512
while (route != ®c->route_set) {
513
pjsip_hdr *new_hdr = (pjsip_hdr*)
514
pjsip_hdr_shallow_clone(tdata->pool, route);
515
pj_list_insert_after(route_pos, new_hdr);
521
/* Add additional request headers */
522
if (!pj_list_empty(®c->hdr_list)) {
523
const pjsip_hdr *hdr;
525
hdr = regc->hdr_list.next;
526
while (hdr != ®c->hdr_list) {
527
pjsip_hdr *new_hdr = (pjsip_hdr*)
528
pjsip_hdr_shallow_clone(tdata->pool, hdr);
529
pjsip_msg_add_hdr(tdata->msg, new_hdr);
540
PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
541
pjsip_tx_data **p_tdata)
544
pjsip_contact_hdr *hdr;
545
const pjsip_hdr *h_allow;
547
pjsip_tx_data *tdata;
549
PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
551
pj_lock_acquire(regc->lock);
553
status = create_request(regc, &tdata);
554
if (status != PJ_SUCCESS) {
555
pj_lock_release(regc->lock);
561
/* Add Contact headers. */
562
hdr = regc->contact_hdr_list.next;
563
while (hdr != ®c->contact_hdr_list) {
564
pjsip_msg_add_hdr(msg, (pjsip_hdr*)
565
pjsip_hdr_shallow_clone(tdata->pool, hdr));
569
/* Also add bindings which are to be removed */
570
while (!pj_list_empty(®c->removed_contact_hdr_list)) {
571
hdr = regc->removed_contact_hdr_list.next;
572
pjsip_msg_add_hdr(msg, (pjsip_hdr*)
573
pjsip_hdr_clone(tdata->pool, hdr));
578
if (regc->expires_hdr)
579
pjsip_msg_add_hdr(msg, (pjsip_hdr*)
580
pjsip_hdr_shallow_clone(tdata->pool,
583
if (regc->timer.id != 0) {
584
pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
588
/* Add Allow header (http://trac.pjsip.org/repos/ticket/1039) */
589
h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL);
591
pjsip_msg_add_hdr(msg, (pjsip_hdr*)
592
pjsip_hdr_shallow_clone(tdata->pool, h_allow));
596
regc->auto_reg = autoreg;
598
pj_lock_release(regc->lock);
606
PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
607
pjsip_tx_data **p_tdata)
609
pjsip_tx_data *tdata;
614
PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
616
pj_lock_acquire(regc->lock);
618
if (regc->timer.id != 0) {
619
pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
623
status = create_request(regc, &tdata);
624
if (status != PJ_SUCCESS) {
625
pj_lock_release(regc->lock);
631
/* Add Contact headers. */
632
hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
633
while ((void*)hdr != (void*)®c->contact_hdr_list) {
634
pjsip_msg_add_hdr(msg, (pjsip_hdr*)
635
pjsip_hdr_shallow_clone(tdata->pool, hdr));
639
/* Also add bindings which are to be removed */
640
while (!pj_list_empty(®c->removed_contact_hdr_list)) {
641
hdr = (pjsip_hdr*)regc->removed_contact_hdr_list.next;
642
pjsip_msg_add_hdr(msg, (pjsip_hdr*)
643
pjsip_hdr_clone(tdata->pool, hdr));
647
/* Add Expires:0 header */
648
hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
649
pjsip_msg_add_hdr(msg, hdr);
651
pj_lock_release(regc->lock);
657
PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
658
pjsip_tx_data **p_tdata)
660
pjsip_tx_data *tdata;
661
pjsip_contact_hdr *hcontact;
666
PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
668
pj_lock_acquire(regc->lock);
670
if (regc->timer.id != 0) {
671
pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
675
status = create_request(regc, &tdata);
676
if (status != PJ_SUCCESS) {
677
pj_lock_release(regc->lock);
683
/* Clear removed_contact_hdr_list */
684
pj_list_init(®c->removed_contact_hdr_list);
686
/* Add Contact:* header */
687
hcontact = pjsip_contact_hdr_create(tdata->pool);
689
pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
691
/* Add Expires:0 header */
692
hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
693
pjsip_msg_add_hdr(msg, hdr);
695
pj_lock_release(regc->lock);
702
PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
704
const pj_str_t contact[] )
708
PJ_ASSERT_RETURN(regc, PJ_EINVAL);
710
pj_lock_acquire(regc->lock);
711
status = set_contact( regc, contact_cnt, contact );
712
pj_lock_release(regc->lock);
718
PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
719
pj_uint32_t expires )
721
PJ_ASSERT_RETURN(regc, PJ_EINVAL);
723
pj_lock_acquire(regc->lock);
724
set_expires( regc, expires );
725
pj_lock_release(regc->lock);
731
static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code,
732
const pj_str_t *reason,
733
pjsip_rx_data *rdata, pj_int32_t expiration,
734
int contact_cnt, pjsip_contact_hdr *contact[])
736
struct pjsip_regc_cbparam cbparam;
743
cbparam.token = regc->token;
744
cbparam.status = status;
745
cbparam.code = st_code;
746
cbparam.reason = *reason;
747
cbparam.rdata = rdata;
748
cbparam.contact_cnt = contact_cnt;
749
cbparam.expiration = expiration;
751
pj_memcpy( cbparam.contact, contact,
752
contact_cnt*sizeof(pjsip_contact_hdr*));
755
(*regc->cb)(&cbparam);
758
static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
759
struct pj_timer_entry *entry)
761
pjsip_regc *regc = (pjsip_regc*) entry->user_data;
762
pjsip_tx_data *tdata;
765
PJ_UNUSED_ARG(timer_heap);
767
/* Temporarily increase busy flag to prevent regc from being deleted
768
* in pjsip_regc_send() or in the callback
770
pj_atomic_inc(regc->busy_ctr);
773
status = pjsip_regc_register(regc, 1, &tdata);
774
if (status == PJ_SUCCESS) {
775
status = pjsip_regc_send(regc, tdata);
778
if (status != PJ_SUCCESS && regc->cb) {
779
char errmsg[PJ_ERR_MSG_SIZE];
780
pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg));
781
call_callback(regc, status, 400, &reason, NULL, -1, 0, NULL);
784
/* Delete the record if user destroy regc during the callback. */
785
if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
786
pjsip_regc_destroy(regc);
790
static void schedule_registration ( pjsip_regc *regc, pj_int32_t expiration )
792
if (regc->auto_reg && expiration > 0) {
793
pj_time_val delay = { 0, 0};
795
delay.sec = expiration - regc->delay_before_refresh;
796
if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
797
delay.sec > (pj_int32_t)regc->expires)
799
delay.sec = regc->expires;
801
if (delay.sec < DELAY_BEFORE_REFRESH)
802
delay.sec = DELAY_BEFORE_REFRESH;
803
regc->timer.cb = ®c_refresh_timer_cb;
804
regc->timer.id = REFRESH_TIMER;
805
regc->timer.user_data = regc;
806
pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay);
807
pj_gettimeofday(®c->last_reg);
808
regc->next_reg = regc->last_reg;
809
regc->next_reg.sec += delay.sec;
813
PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
814
pjsip_host_port *via_addr,
815
pjsip_transport *via_tp)
817
PJ_ASSERT_RETURN(regc, PJ_EINVAL);
820
pj_bzero(®c->via_addr, sizeof(regc->via_addr));
822
if (pj_strcmp(®c->via_addr.host, &via_addr->host))
823
pj_strdup(regc->pool, ®c->via_addr.host, &via_addr->host);
824
regc->via_addr.port = via_addr->port;
826
regc->via_tp = via_tp;
832
pjsip_regc_set_delay_before_refresh( pjsip_regc *regc,
835
PJ_ASSERT_RETURN(regc, PJ_EINVAL);
837
if (delay > regc->expires)
840
if (regc->delay_before_refresh != delay)
842
regc->delay_before_refresh = delay;
844
if (regc->timer.id != 0) {
845
/* Cancel registration timer */
846
pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
849
/* Schedule next registration */
850
schedule_registration(regc, regc->expires);
858
static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
859
const pjsip_rx_data *rdata,
860
unsigned *contact_cnt,
861
unsigned max_contact,
862
pjsip_contact_hdr *contacts[])
864
pj_int32_t expiration = NOEXP;
865
const pjsip_msg *msg = rdata->msg_info.msg;
866
const pjsip_hdr *hdr;
868
/* Enumerate all Contact headers in the response */
870
for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
871
if (hdr->type == PJSIP_H_CONTACT &&
872
*contact_cnt < max_contact)
874
contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
879
if (regc->current_op == REGC_REGISTERING) {
880
pj_bool_t has_our_contact = PJ_FALSE;
881
const pjsip_expires_hdr *expires;
883
/* Get Expires header */
884
expires = (const pjsip_expires_hdr*)
885
pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
887
/* Try to find the Contact URIs that we register, in the response
888
* to get the expires value. We'll try both with comparing the URI
889
* and comparing the extension param only.
891
if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
893
for (i=0; i<*contact_cnt; ++i) {
894
const pjsip_contact_hdr *our_hdr;
896
our_hdr = (const pjsip_contact_hdr*)
897
regc->contact_hdr_list.next;
899
/* Match with our Contact header(s) */
900
while ((void*)our_hdr != (void*)®c->contact_hdr_list) {
902
const pjsip_uri *uri1, *uri2;
903
pj_bool_t matched = PJ_FALSE;
905
/* Exclude the display name when comparing the URI
906
* since server may not return it.
908
uri1 = (const pjsip_uri*)
909
pjsip_uri_get_uri(contacts[i]->uri);
910
uri2 = (const pjsip_uri*)
911
pjsip_uri_get_uri(our_hdr->uri);
913
/* First try with exact matching, according to RFC 3261
914
* Section 19.1.4 URI Comparison
916
if (pjsip_cfg()->regc.check_contact) {
917
matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
921
/* If no match is found, try with matching the extension
922
* parameter only if extension parameter was added.
924
if (!matched && regc->add_xuid_param &&
925
(PJSIP_URI_SCHEME_IS_SIP(uri1) ||
926
PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
927
(PJSIP_URI_SCHEME_IS_SIP(uri2) ||
928
PJSIP_URI_SCHEME_IS_SIPS(uri2)))
930
const pjsip_sip_uri *sip_uri1, *sip_uri2;
931
const pjsip_param *p1, *p2;
933
sip_uri1 = (const pjsip_sip_uri*)uri1;
934
sip_uri2 = (const pjsip_sip_uri*)uri2;
936
p1 = pjsip_param_cfind(&sip_uri1->other_param,
938
p2 = pjsip_param_cfind(&sip_uri2->other_param,
940
matched = p1 && p2 &&
941
pj_strcmp(&p1->value, &p2->value)==0;
946
has_our_contact = PJ_TRUE;
948
if (contacts[i]->expires >= 0 &&
949
contacts[i]->expires < expiration)
951
/* Get the lowest expiration time. */
952
expiration = contacts[i]->expires;
958
our_hdr = our_hdr->next;
960
} /* while ((void.. */
964
/* If matching Contact header(s) are found but the
965
* header doesn't contain expires parameter, get the
966
* expiration value from the Expires header. And
967
* if Expires header is not present, get the expiration
968
* value from the request.
970
if (has_our_contact && expiration == NOEXP) {
972
expiration = expires->ivalue;
973
} else if (regc->expires_hdr) {
974
expiration = regc->expires_hdr->ivalue;
976
/* We didn't request explicit expiration value,
977
* and server doesn't specify it either. This
978
* shouldn't happen unless we have a broken
987
/* If we still couldn't get matching Contact header(s), it means
988
* there must be something wrong with the registrar (e.g. it may
989
* have modified the URI's in the response, which is prohibited).
991
if (expiration==NOEXP) {
992
/* If the number of Contact headers in the response matches
993
* ours, they're all probably ours. Get the expiration
994
* from there if this is the case, or from Expires header
995
* if we don't have exact Contact header count, or
996
* from the request as the last resort.
998
unsigned our_contact_cnt;
1000
our_contact_cnt = pj_list_size(®c->contact_hdr_list);
1002
if (*contact_cnt == our_contact_cnt && *contact_cnt &&
1003
contacts[0]->expires >= 0)
1005
expiration = contacts[0]->expires;
1007
expiration = expires->ivalue;
1008
else if (regc->expires_hdr)
1009
expiration = regc->expires_hdr->ivalue;
1015
/* Just assume that the unregistration has been successful. */
1019
/* Must have expiration value by now */
1020
pj_assert(expiration != NOEXP);
1025
static void regc_tsx_callback(void *token, pjsip_event *event)
1028
pjsip_regc *regc = (pjsip_regc*) token;
1029
pjsip_transaction *tsx = event->body.tsx_state.tsx;
1030
pj_bool_t handled = PJ_TRUE;
1032
pj_atomic_inc(regc->busy_ctr);
1033
pj_lock_acquire(regc->lock);
1035
/* Decrement pending transaction counter. */
1036
pj_assert(regc->has_tsx);
1037
regc->has_tsx = PJ_FALSE;
1039
/* Add reference to the transport */
1040
if (tsx->transport != regc->last_transport) {
1041
if (regc->last_transport) {
1042
pjsip_transport_dec_ref(regc->last_transport);
1043
regc->last_transport = NULL;
1046
if (tsx->transport) {
1047
regc->last_transport = tsx->transport;
1048
pjsip_transport_add_ref(regc->last_transport);
1052
/* Handle 401/407 challenge (even when _delete_flag is set) */
1053
if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
1054
tsx->status_code == PJSIP_SC_UNAUTHORIZED)
1056
pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1057
pjsip_tx_data *tdata;
1059
/* reset current op */
1060
regc->current_op = REGC_IDLE;
1062
status = pjsip_auth_clt_reinit_req( ®c->auth_sess,
1067
if (status == PJ_SUCCESS) {
1068
status = pjsip_regc_send(regc, tdata);
1071
if (status != PJ_SUCCESS) {
1073
/* Only call callback if application is still interested
1076
if (regc->_delete_flag == 0) {
1077
/* Should be safe to release the lock temporarily.
1078
* We do this to avoid deadlock.
1080
pj_lock_release(regc->lock);
1081
call_callback(regc, status, tsx->status_code,
1082
&rdata->msg_info.msg->line.status.reason,
1083
rdata, -1, 0, NULL);
1084
pj_lock_acquire(regc->lock);
1088
} else if (regc->_delete_flag) {
1090
/* User has called pjsip_regc_destroy(), so don't call callback.
1091
* This regc will be destroyed later in this function.
1094
/* Just reset current op */
1095
regc->current_op = REGC_IDLE;
1097
} else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
1098
regc->current_op == REGC_REGISTERING)
1100
/* Handle 423 response automatically:
1101
* - set requested expiration to Min-Expires header, ONLY IF
1102
* the original request is a registration (as opposed to
1103
* unregistration) and the requested expiration was indeed
1104
* lower than Min-Expires)
1105
* - resend the request
1107
pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1108
pjsip_min_expires_hdr *me_hdr;
1109
pjsip_tx_data *tdata;
1112
/* reset current op */
1113
regc->current_op = REGC_IDLE;
1115
/* Update requested expiration */
1116
me_hdr = (pjsip_min_expires_hdr*)
1117
pjsip_msg_find_hdr(rdata->msg_info.msg,
1118
PJSIP_H_MIN_EXPIRES, NULL);
1120
min_exp = me_hdr->ivalue;
1122
/* Broken server, Min-Expires doesn't exist.
1123
* Just guestimate then, BUT ONLY if if this is the
1124
* first time we received such response.
1127
/* Note: changing this value would require changing couple of
1128
* Python test scripts.
1130
UNSPECIFIED_MIN_EXPIRES = 3601
1132
if (!regc->expires_hdr ||
1133
regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
1135
min_exp = UNSPECIFIED_MIN_EXPIRES;
1138
PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
1139
"without Min-Expires header is invalid"));
1144
if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
1145
/* But we already send with greater expiration time, why does
1146
* the server send us with 423? Oh well, just fail the request.
1149
PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
1150
"Min-Expires header value in response"));
1154
set_expires(regc, min_exp);
1156
status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
1157
if (status == PJ_SUCCESS) {
1158
status = pjsip_regc_send(regc, tdata);
1161
if (status != PJ_SUCCESS) {
1162
/* Only call callback if application is still interested
1165
if (!regc->_delete_flag) {
1166
/* Should be safe to release the lock temporarily.
1167
* We do this to avoid deadlock.
1169
pj_lock_release(regc->lock);
1170
call_callback(regc, status, tsx->status_code,
1171
&rdata->msg_info.msg->line.status.reason,
1172
rdata, -1, 0, NULL);
1173
pj_lock_acquire(regc->lock);
1183
pjsip_rx_data *rdata;
1184
pj_int32_t expiration = NOEXP;
1185
unsigned contact_cnt = 0;
1186
pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
1188
if (tsx->status_code/100 == 2) {
1190
rdata = event->body.tsx_state.src.rdata;
1192
/* Calculate expiration */
1193
expiration = calculate_response_expiration(regc, rdata,
1195
PJSIP_REGC_MAX_CONTACT,
1198
/* Schedule next registration */
1199
schedule_registration(regc, expiration);
1202
rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
1203
event->body.tsx_state.src.rdata : NULL;
1206
/* Update registration */
1207
if (expiration==NOEXP) expiration=-1;
1208
regc->expires = expiration;
1210
/* Mark operation as complete */
1211
regc->current_op = REGC_IDLE;
1213
/* Call callback. */
1214
/* Should be safe to release the lock temporarily.
1215
* We do this to avoid deadlock.
1217
pj_lock_release(regc->lock);
1218
call_callback(regc, PJ_SUCCESS, tsx->status_code,
1219
(rdata ? &rdata->msg_info.msg->line.status.reason
1220
: &tsx->status_text),
1222
contact_cnt, contact);
1223
pj_lock_acquire(regc->lock);
1226
pj_lock_release(regc->lock);
1228
/* Delete the record if user destroy regc during the callback. */
1229
if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
1230
pjsip_regc_destroy(regc);
1234
PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
1237
pjsip_cseq_hdr *cseq_hdr;
1238
pjsip_expires_hdr *expires_hdr;
1241
pj_atomic_inc(regc->busy_ctr);
1242
pj_lock_acquire(regc->lock);
1244
/* Make sure we don't have pending transaction. */
1245
if (regc->has_tsx) {
1246
PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
1247
"transaction pending"));
1248
pjsip_tx_data_dec_ref( tdata );
1249
pj_lock_release(regc->lock);
1250
pj_atomic_dec(regc->busy_ctr);
1254
pj_assert(regc->current_op == REGC_IDLE);
1256
/* Invalidate message buffer. */
1257
pjsip_tx_data_invalidate_msg(tdata);
1259
/* Increment CSeq */
1260
cseq = ++regc->cseq_hdr->cseq;
1261
cseq_hdr = (pjsip_cseq_hdr*)
1262
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
1263
cseq_hdr->cseq = cseq;
1265
/* Find Expires header */
1266
expires_hdr = (pjsip_expires_hdr*)
1267
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
1269
/* Bind to transport selector */
1270
pjsip_tx_data_set_transport(tdata, ®c->tp_sel);
1272
regc->has_tsx = PJ_TRUE;
1274
/* Set current operation based on the value of Expires header */
1275
if (expires_hdr && expires_hdr->ivalue==0)
1276
regc->current_op = REGC_UNREGISTERING;
1278
regc->current_op = REGC_REGISTERING;
1280
/* Prevent deletion of tdata, e.g: when something wrong in sending,
1281
* we need tdata to retrieve the transport.
1283
pjsip_tx_data_add_ref(tdata);
1285
/* If via_addr is set, use this address for the Via header. */
1286
if (regc->via_addr.host.slen > 0) {
1287
tdata->via_addr = regc->via_addr;
1288
tdata->via_tp = regc->via_tp;
1291
/* Need to unlock the regc temporarily while sending the message to
1292
* prevent deadlock (https://trac.pjsip.org/repos/ticket/1247).
1293
* It should be safe to do this since the regc's refcount has been
1296
pj_lock_release(regc->lock);
1298
/* Now send the message */
1299
status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
1300
regc, ®c_tsx_callback);
1301
if (status!=PJ_SUCCESS) {
1302
PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
1305
/* Reacquire the lock */
1306
pj_lock_acquire(regc->lock);
1308
/* Get last transport used and add reference to it */
1309
if (tdata->tp_info.transport != regc->last_transport &&
1312
if (regc->last_transport) {
1313
pjsip_transport_dec_ref(regc->last_transport);
1314
regc->last_transport = NULL;
1317
if (tdata->tp_info.transport) {
1318
regc->last_transport = tdata->tp_info.transport;
1319
pjsip_transport_add_ref(regc->last_transport);
1324
pjsip_tx_data_dec_ref(tdata);
1326
pj_lock_release(regc->lock);
1328
/* Delete the record if user destroy regc during the callback. */
1329
if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
1330
pjsip_regc_destroy(regc);