blob: 3333317b5378a9d90bcdb44a597200ff45e870f2 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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.
10 *
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.
15 *
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
19 */
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>
30#include <pj/guid.h>
31#include <pj/lock.h>
32#include <pj/os.h>
33#include <pj/pool.h>
34#include <pj/log.h>
35#include <pj/rand.h>
36#include <pj/string.h>
37
38
39#define REFRESH_TIMER 1
40#define DELAY_BEFORE_REFRESH PJSIP_REGISTER_CLIENT_DELAY_BEFORE_REFRESH
41#define THIS_FILE "sip_reg.c"
42
43/* Outgoing transaction timeout when server sends 100 but never replies
44 * with final response. Value is in MILISECONDS!
45 */
46#define REGC_TSX_TIMEOUT 33000
47
48enum { NOEXP = 0x1FFFFFFF };
49
50static const pj_str_t XUID_PARAM_NAME = { "x-uid", 5 };
51
52
53/* Current/pending operation */
54enum regc_op
55{
56 REGC_IDLE,
57 REGC_REGISTERING,
58 REGC_UNREGISTERING
59};
60
61/**
62 * SIP client registration structure.
63 */
64struct pjsip_regc
65{
66 pj_pool_t *pool;
67 pjsip_endpoint *endpt;
68 pj_lock_t *lock;
69 pj_bool_t _delete_flag;
70 pj_bool_t has_tsx;
71 pj_atomic_t *busy_ctr;
72 enum regc_op current_op;
73
74 pj_bool_t add_xuid_param;
75
76 void *token;
77 pjsip_regc_cb *cb;
78
79 pj_str_t str_srv_url;
80 pjsip_uri *srv_url;
81 pjsip_cid_hdr *cid_hdr;
82 pjsip_cseq_hdr *cseq_hdr;
83 pj_str_t from_uri;
84 pjsip_from_hdr *from_hdr;
85 pjsip_to_hdr *to_hdr;
86 pjsip_contact_hdr contact_hdr_list;
87 pjsip_contact_hdr removed_contact_hdr_list;
88 pjsip_expires_hdr *expires_hdr;
89 pj_uint32_t expires;
90 pj_uint32_t delay_before_refresh;
91 pjsip_route_hdr route_set;
92 pjsip_hdr hdr_list;
93 pjsip_host_port via_addr;
94 const void *via_tp;
95
96 /* Authorization sessions. */
97 pjsip_auth_clt_sess auth_sess;
98
99 /* Auto refresh registration. */
100 pj_bool_t auto_reg;
101 pj_time_val last_reg;
102 pj_time_val next_reg;
103 pj_timer_entry timer;
104
105 /* Transport selector */
106 pjsip_tpselector tp_sel;
107
108 /* Last transport used. We acquire the transport to keep
109 * it open.
110 */
111 pjsip_transport *last_transport;
112};
113
114
115PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
116 pjsip_regc_cb *cb,
117 pjsip_regc **p_regc)
118{
119 pj_pool_t *pool;
120 pjsip_regc *regc;
121 pj_status_t status;
122
123 /* Verify arguments. */
124 PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL);
125
126 pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
127 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
128
129 regc = PJ_POOL_ZALLOC_T(pool, pjsip_regc);
130
131 regc->pool = pool;
132 regc->endpt = endpt;
133 regc->token = token;
134 regc->cb = cb;
135 regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
136 regc->add_xuid_param = pjsip_cfg()->regc.add_xuid_param;
137
138 status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
139 &regc->lock);
140 if (status != PJ_SUCCESS) {
141 pj_pool_release(pool);
142 return status;
143 }
144
145 status = pj_atomic_create(pool, 0, &regc->busy_ctr);
146 if (status != PJ_SUCCESS) {
147 pj_lock_destroy(regc->lock);
148 pj_pool_release(pool);
149 return status;
150 }
151
152 status = pjsip_auth_clt_init(&regc->auth_sess, endpt, regc->pool, 0);
153 if (status != PJ_SUCCESS)
154 return status;
155
156 pj_list_init(&regc->route_set);
157 pj_list_init(&regc->hdr_list);
158 pj_list_init(&regc->contact_hdr_list);
159 pj_list_init(&regc->removed_contact_hdr_list);
160
161 /* Done */
162 *p_regc = regc;
163 return PJ_SUCCESS;
164}
165
166
167PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc)
168{
169 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
170
171 pj_lock_acquire(regc->lock);
172 if (regc->has_tsx || pj_atomic_get(regc->busy_ctr) != 0) {
173 regc->_delete_flag = 1;
174 regc->cb = NULL;
175 pj_lock_release(regc->lock);
176 } else {
177 pjsip_tpselector_dec_ref(&regc->tp_sel);
178 if (regc->last_transport) {
179 pjsip_transport_dec_ref(regc->last_transport);
180 regc->last_transport = NULL;
181 }
182 if (regc->timer.id != 0) {
183 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
184 regc->timer.id = 0;
185 }
186 pj_atomic_destroy(regc->busy_ctr);
187 pj_lock_release(regc->lock);
188 pj_lock_destroy(regc->lock);
189 regc->lock = NULL;
190 pjsip_endpt_release_pool(regc->endpt, regc->pool);
191 }
192
193 return PJ_SUCCESS;
194}
195
196
197PJ_DEF(pj_status_t) pjsip_regc_get_info( pjsip_regc *regc,
198 pjsip_regc_info *info )
199{
200 PJ_ASSERT_RETURN(regc && info, PJ_EINVAL);
201
202 pj_lock_acquire(regc->lock);
203
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;
210
211 if (regc->has_tsx)
212 info->next_reg = 0;
213 else if (regc->auto_reg == 0)
214 info->next_reg = 0;
215 else if (regc->expires < 0)
216 info->next_reg = regc->expires;
217 else {
218 pj_time_val now, next_reg;
219
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;
224 }
225
226 pj_lock_release(regc->lock);
227
228 return PJ_SUCCESS;
229}
230
231
232PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
233{
234 return regc->pool;
235}
236
237static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
238{
239 if (expires != regc->expires) {
240 regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires);
241 } else {
242 regc->expires_hdr = NULL;
243 }
244}
245
246
247static pj_status_t set_contact( pjsip_regc *regc,
248 int contact_cnt,
249 const pj_str_t contact[] )
250{
251 const pj_str_t CONTACT = { "Contact", 7 };
252 pjsip_contact_hdr *h;
253 int i;
254
255 /* Save existing contact list to removed_contact_hdr_list and
256 * clear contact_hdr_list.
257 */
258 pj_list_merge_last(&regc->removed_contact_hdr_list,
259 &regc->contact_hdr_list);
260
261 /* Set the expiration of Contacts in to removed_contact_hdr_list
262 * zero.
263 */
264 h = regc->removed_contact_hdr_list.next;
265 while (h != &regc->removed_contact_hdr_list) {
266 h->expires = 0;
267 h = h->next;
268 }
269
270 /* Process new contacts */
271 for (i=0; i<contact_cnt; ++i) {
272 pjsip_contact_hdr *hdr;
273 pj_str_t tmp;
274
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);
278 if (hdr == NULL) {
279 PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"",
280 (int)tmp.slen, tmp.ptr));
281 return PJSIP_EINVALIDURI;
282 }
283
284 /* Find the new contact in old contact list. If found, remove
285 * the old header from the old header list.
286 */
287 h = regc->removed_contact_hdr_list.next;
288 while (h != &regc->removed_contact_hdr_list) {
289 int rc;
290
291 rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
292 h->uri, hdr->uri);
293 if (rc == 0) {
294 /* Match */
295 pj_list_erase(h);
296 break;
297 }
298
299 h = h->next;
300 }
301
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.
305 */
306 if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) ||
307 PJSIP_URI_SCHEME_IS_SIPS(hdr->uri)))
308 {
309 pjsip_param *xuid_param;
310 pjsip_sip_uri *sip_uri;
311
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);
315
316 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri);
317 pj_list_push_back(&sip_uri->other_param, xuid_param);
318 }
319
320 pj_list_push_back(&regc->contact_hdr_list, hdr);
321 }
322
323 return PJ_SUCCESS;
324}
325
326
327PJ_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,
331 int contact_cnt,
332 const pj_str_t contact[],
333 pj_uint32_t expires)
334{
335 pj_str_t tmp;
336 pj_status_t status;
337
338 PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url &&
339 expires, PJ_EINVAL);
340
341 /* Copy server URL. */
342 pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
343
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;
349 }
350
351 /* Set "From" header. */
352 pj_strdup_with_null(regc->pool, &regc->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;
361 }
362
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;
371 }
372
373
374 /* Set "Contact" header. */
375 status = set_contact( regc, contact_cnt, contact);
376 if (status != PJ_SUCCESS)
377 return status;
378
379 /* Set "Expires" header, if required. */
380 set_expires( regc, expires);
381 regc->delay_before_refresh = DELAY_BEFORE_REFRESH;
382
383 /* Set "Call-ID" header. */
384 regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
385 pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
386
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( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
391
392 /* Done. */
393 return PJ_SUCCESS;
394}
395
396PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
397 int count,
398 const pjsip_cred_info cred[] )
399{
400 PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL);
401 return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
402}
403
404PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
405 const pjsip_auth_clt_pref *pref)
406{
407 PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
408 return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
409}
410
411PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
412 const pjsip_route_hdr *route_set)
413{
414 const pjsip_route_hdr *chdr;
415
416 PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL);
417
418 pj_list_init(&regc->route_set);
419
420 chdr = route_set->next;
421 while (chdr != route_set) {
422 pj_list_push_back(&regc->route_set, pjsip_hdr_clone(regc->pool, chdr));
423 chdr = chdr->next;
424 }
425
426 return PJ_SUCCESS;
427}
428
429
430/*
431 * Bind client registration to a specific transport/listener.
432 */
433PJ_DEF(pj_status_t) pjsip_regc_set_transport( pjsip_regc *regc,
434 const pjsip_tpselector *sel)
435{
436 PJ_ASSERT_RETURN(regc && sel, PJ_EINVAL);
437
438 pjsip_tpselector_dec_ref(&regc->tp_sel);
439 pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
440 pjsip_tpselector_add_ref(&regc->tp_sel);
441
442 return PJ_SUCCESS;
443}
444
445/* Release transport */
446PJ_DEF(pj_status_t) pjsip_regc_release_transport(pjsip_regc *regc)
447{
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;
452 }
453 return PJ_SUCCESS;
454}
455
456
457PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc,
458 const pjsip_hdr *hdr_list)
459{
460 const pjsip_hdr *hdr;
461
462 PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL);
463
464 //This is "add" operation, so don't remove headers.
465 //pj_list_init(&regc->hdr_list);
466
467 hdr = hdr_list->next;
468 while (hdr != hdr_list) {
469 pj_list_push_back(&regc->hdr_list, pjsip_hdr_clone(regc->pool, hdr));
470 hdr = hdr->next;
471 }
472
473 return PJ_SUCCESS;
474}
475
476static pj_status_t create_request(pjsip_regc *regc,
477 pjsip_tx_data **p_tdata)
478{
479 pj_status_t status;
480 pjsip_tx_data *tdata;
481
482 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
483
484 /* Create the request. */
485 status = pjsip_endpt_create_request_from_hdr( regc->endpt,
486 pjsip_get_register_method(),
487 regc->srv_url,
488 regc->from_hdr,
489 regc->to_hdr,
490 NULL,
491 regc->cid_hdr,
492 regc->cseq_hdr->cseq,
493 NULL,
494 &tdata);
495 if (status != PJ_SUCCESS)
496 return status;
497
498 /* Add cached authorization headers. */
499 pjsip_auth_clt_init_req( &regc->auth_sess, tdata );
500
501 /* Add Route headers from route set, ideally after Via header */
502 if (!pj_list_empty(&regc->route_set)) {
503 pjsip_hdr *route_pos;
504 const pjsip_route_hdr *route;
505
506 route_pos = (pjsip_hdr*)
507 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
508 if (!route_pos)
509 route_pos = &tdata->msg->hdr;
510
511 route = regc->route_set.next;
512 while (route != &regc->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);
516 route_pos = new_hdr;
517 route = route->next;
518 }
519 }
520
521 /* Add additional request headers */
522 if (!pj_list_empty(&regc->hdr_list)) {
523 const pjsip_hdr *hdr;
524
525 hdr = regc->hdr_list.next;
526 while (hdr != &regc->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);
530 hdr = hdr->next;
531 }
532 }
533
534 /* Done. */
535 *p_tdata = tdata;
536 return PJ_SUCCESS;
537}
538
539
540PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
541 pjsip_tx_data **p_tdata)
542{
543 pjsip_msg *msg;
544 pjsip_contact_hdr *hdr;
545 const pjsip_hdr *h_allow;
546 pj_status_t status;
547 pjsip_tx_data *tdata;
548
549 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
550
551 pj_lock_acquire(regc->lock);
552
553 status = create_request(regc, &tdata);
554 if (status != PJ_SUCCESS) {
555 pj_lock_release(regc->lock);
556 return status;
557 }
558
559 msg = tdata->msg;
560
561 /* Add Contact headers. */
562 hdr = regc->contact_hdr_list.next;
563 while (hdr != &regc->contact_hdr_list) {
564 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
565 pjsip_hdr_shallow_clone(tdata->pool, hdr));
566 hdr = hdr->next;
567 }
568
569 /* Also add bindings which are to be removed */
570 while (!pj_list_empty(&regc->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));
574 pj_list_erase(hdr);
575 }
576
577
578 if (regc->expires_hdr)
579 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
580 pjsip_hdr_shallow_clone(tdata->pool,
581 regc->expires_hdr));
582
583 if (regc->timer.id != 0) {
584 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
585 regc->timer.id = 0;
586 }
587
588 /* Add Allow header (http://trac.pjsip.org/repos/ticket/1039) */
589 h_allow = pjsip_endpt_get_capability(regc->endpt, PJSIP_H_ALLOW, NULL);
590 if (h_allow) {
591 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
592 pjsip_hdr_shallow_clone(tdata->pool, h_allow));
593
594 }
595
596 regc->auto_reg = autoreg;
597
598 pj_lock_release(regc->lock);
599
600 /* Done */
601 *p_tdata = tdata;
602 return PJ_SUCCESS;
603}
604
605
606PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc,
607 pjsip_tx_data **p_tdata)
608{
609 pjsip_tx_data *tdata;
610 pjsip_msg *msg;
611 pjsip_hdr *hdr;
612 pj_status_t status;
613
614 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
615
616 pj_lock_acquire(regc->lock);
617
618 if (regc->timer.id != 0) {
619 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
620 regc->timer.id = 0;
621 }
622
623 status = create_request(regc, &tdata);
624 if (status != PJ_SUCCESS) {
625 pj_lock_release(regc->lock);
626 return status;
627 }
628
629 msg = tdata->msg;
630
631 /* Add Contact headers. */
632 hdr = (pjsip_hdr*)regc->contact_hdr_list.next;
633 while ((void*)hdr != (void*)&regc->contact_hdr_list) {
634 pjsip_msg_add_hdr(msg, (pjsip_hdr*)
635 pjsip_hdr_shallow_clone(tdata->pool, hdr));
636 hdr = hdr->next;
637 }
638
639 /* Also add bindings which are to be removed */
640 while (!pj_list_empty(&regc->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));
644 pj_list_erase(hdr);
645 }
646
647 /* Add Expires:0 header */
648 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
649 pjsip_msg_add_hdr(msg, hdr);
650
651 pj_lock_release(regc->lock);
652
653 *p_tdata = tdata;
654 return PJ_SUCCESS;
655}
656
657PJ_DEF(pj_status_t) pjsip_regc_unregister_all(pjsip_regc *regc,
658 pjsip_tx_data **p_tdata)
659{
660 pjsip_tx_data *tdata;
661 pjsip_contact_hdr *hcontact;
662 pjsip_hdr *hdr;
663 pjsip_msg *msg;
664 pj_status_t status;
665
666 PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);
667
668 pj_lock_acquire(regc->lock);
669
670 if (regc->timer.id != 0) {
671 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
672 regc->timer.id = 0;
673 }
674
675 status = create_request(regc, &tdata);
676 if (status != PJ_SUCCESS) {
677 pj_lock_release(regc->lock);
678 return status;
679 }
680
681 msg = tdata->msg;
682
683 /* Clear removed_contact_hdr_list */
684 pj_list_init(&regc->removed_contact_hdr_list);
685
686 /* Add Contact:* header */
687 hcontact = pjsip_contact_hdr_create(tdata->pool);
688 hcontact->star = 1;
689 pjsip_msg_add_hdr(msg, (pjsip_hdr*)hcontact);
690
691 /* Add Expires:0 header */
692 hdr = (pjsip_hdr*) pjsip_expires_hdr_create(tdata->pool, 0);
693 pjsip_msg_add_hdr(msg, hdr);
694
695 pj_lock_release(regc->lock);
696
697 *p_tdata = tdata;
698 return PJ_SUCCESS;
699}
700
701
702PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
703 int contact_cnt,
704 const pj_str_t contact[] )
705{
706 pj_status_t status;
707
708 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
709
710 pj_lock_acquire(regc->lock);
711 status = set_contact( regc, contact_cnt, contact );
712 pj_lock_release(regc->lock);
713
714 return status;
715}
716
717
718PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
719 pj_uint32_t expires )
720{
721 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
722
723 pj_lock_acquire(regc->lock);
724 set_expires( regc, expires );
725 pj_lock_release(regc->lock);
726
727 return PJ_SUCCESS;
728}
729
730
731static 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[])
735{
736 struct pjsip_regc_cbparam cbparam;
737
738
739 if (!regc->cb)
740 return;
741
742 cbparam.regc = regc;
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;
750 if (contact_cnt) {
751 pj_memcpy( cbparam.contact, contact,
752 contact_cnt*sizeof(pjsip_contact_hdr*));
753 }
754
755 (*regc->cb)(&cbparam);
756}
757
758static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
759 struct pj_timer_entry *entry)
760{
761 pjsip_regc *regc = (pjsip_regc*) entry->user_data;
762 pjsip_tx_data *tdata;
763 pj_status_t status;
764
765 PJ_UNUSED_ARG(timer_heap);
766
767 /* Temporarily increase busy flag to prevent regc from being deleted
768 * in pjsip_regc_send() or in the callback
769 */
770 pj_atomic_inc(regc->busy_ctr);
771
772 entry->id = 0;
773 status = pjsip_regc_register(regc, 1, &tdata);
774 if (status == PJ_SUCCESS) {
775 status = pjsip_regc_send(regc, tdata);
776 }
777
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);
782 }
783
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);
787 }
788}
789
790static void schedule_registration ( pjsip_regc *regc, pj_int32_t expiration )
791{
792 if (regc->auto_reg && expiration > 0) {
793 pj_time_val delay = { 0, 0};
794
795 pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(regc->endpt),
796 &regc->timer, 0);
797
798 delay.sec = expiration - regc->delay_before_refresh;
799 if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
800 delay.sec > (pj_int32_t)regc->expires)
801 {
802 delay.sec = regc->expires;
803 }
804 if (delay.sec < DELAY_BEFORE_REFRESH)
805 delay.sec = DELAY_BEFORE_REFRESH;
806 regc->timer.cb = &regc_refresh_timer_cb;
807 regc->timer.id = REFRESH_TIMER;
808 regc->timer.user_data = regc;
809 pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
810 pj_gettimeofday(&regc->last_reg);
811 regc->next_reg = regc->last_reg;
812 regc->next_reg.sec += delay.sec;
813 }
814}
815
816PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
817 pjsip_host_port *via_addr,
818 pjsip_transport *via_tp)
819{
820 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
821
822 if (!via_addr)
823 pj_bzero(&regc->via_addr, sizeof(regc->via_addr));
824 else {
825 if (pj_strcmp(&regc->via_addr.host, &via_addr->host))
826 pj_strdup(regc->pool, &regc->via_addr.host, &via_addr->host);
827 regc->via_addr.port = via_addr->port;
828 }
829 regc->via_tp = via_tp;
830
831 return PJ_SUCCESS;
832}
833
834PJ_DEF(pj_status_t)
835pjsip_regc_set_delay_before_refresh( pjsip_regc *regc,
836 pj_uint32_t delay )
837{
838 PJ_ASSERT_RETURN(regc, PJ_EINVAL);
839
840 if (delay > regc->expires)
841 return PJ_ETOOBIG;
842
843 pj_lock_acquire(regc->lock);
844
845 if (regc->delay_before_refresh != delay)
846 {
847 regc->delay_before_refresh = delay;
848
849 if (regc->timer.id != 0) {
850 /* Cancel registration timer */
851 pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
852 regc->timer.id = 0;
853
854 /* Schedule next registration */
855 schedule_registration(regc, regc->expires);
856 }
857 }
858
859 pj_lock_release(regc->lock);
860
861 return PJ_SUCCESS;
862}
863
864
865static pj_int32_t calculate_response_expiration(const pjsip_regc *regc,
866 const pjsip_rx_data *rdata,
867 unsigned *contact_cnt,
868 unsigned max_contact,
869 pjsip_contact_hdr *contacts[])
870{
871 pj_int32_t expiration = NOEXP;
872 const pjsip_msg *msg = rdata->msg_info.msg;
873 const pjsip_hdr *hdr;
874
875 /* Enumerate all Contact headers in the response */
876 *contact_cnt = 0;
877 for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
878 if (hdr->type == PJSIP_H_CONTACT &&
879 *contact_cnt < max_contact)
880 {
881 contacts[*contact_cnt] = (pjsip_contact_hdr*)hdr;
882 ++(*contact_cnt);
883 }
884 }
885
886 if (regc->current_op == REGC_REGISTERING) {
887 pj_bool_t has_our_contact = PJ_FALSE;
888 const pjsip_expires_hdr *expires;
889
890 /* Get Expires header */
891 expires = (const pjsip_expires_hdr*)
892 pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
893
894 /* Try to find the Contact URIs that we register, in the response
895 * to get the expires value. We'll try both with comparing the URI
896 * and comparing the extension param only.
897 */
898 if (pjsip_cfg()->regc.check_contact || regc->add_xuid_param) {
899 unsigned i;
900 for (i=0; i<*contact_cnt; ++i) {
901 const pjsip_contact_hdr *our_hdr;
902
903 our_hdr = (const pjsip_contact_hdr*)
904 regc->contact_hdr_list.next;
905
906 /* Match with our Contact header(s) */
907 while ((void*)our_hdr != (void*)&regc->contact_hdr_list) {
908
909 const pjsip_uri *uri1, *uri2;
910 pj_bool_t matched = PJ_FALSE;
911
912 /* Exclude the display name when comparing the URI
913 * since server may not return it.
914 */
915 uri1 = (const pjsip_uri*)
916 pjsip_uri_get_uri(contacts[i]->uri);
917 uri2 = (const pjsip_uri*)
918 pjsip_uri_get_uri(our_hdr->uri);
919
920 /* First try with exact matching, according to RFC 3261
921 * Section 19.1.4 URI Comparison
922 */
923 if (pjsip_cfg()->regc.check_contact) {
924 matched = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR,
925 uri1, uri2)==0;
926 }
927
928 /* If no match is found, try with matching the extension
929 * parameter only if extension parameter was added.
930 */
931 if (!matched && regc->add_xuid_param &&
932 (PJSIP_URI_SCHEME_IS_SIP(uri1) ||
933 PJSIP_URI_SCHEME_IS_SIPS(uri1)) &&
934 (PJSIP_URI_SCHEME_IS_SIP(uri2) ||
935 PJSIP_URI_SCHEME_IS_SIPS(uri2)))
936 {
937 const pjsip_sip_uri *sip_uri1, *sip_uri2;
938 const pjsip_param *p1, *p2;
939
940 sip_uri1 = (const pjsip_sip_uri*)uri1;
941 sip_uri2 = (const pjsip_sip_uri*)uri2;
942
943 p1 = pjsip_param_cfind(&sip_uri1->other_param,
944 &XUID_PARAM_NAME);
945 p2 = pjsip_param_cfind(&sip_uri2->other_param,
946 &XUID_PARAM_NAME);
947 matched = p1 && p2 &&
948 pj_strcmp(&p1->value, &p2->value)==0;
949
950 }
951
952 if (matched) {
953 has_our_contact = PJ_TRUE;
954
955 if (contacts[i]->expires >= 0 &&
956 contacts[i]->expires < expiration)
957 {
958 /* Get the lowest expiration time. */
959 expiration = contacts[i]->expires;
960 }
961
962 break;
963 }
964
965 our_hdr = our_hdr->next;
966
967 } /* while ((void.. */
968
969 } /* for (i=.. */
970
971 /* If matching Contact header(s) are found but the
972 * header doesn't contain expires parameter, get the
973 * expiration value from the Expires header. And
974 * if Expires header is not present, get the expiration
975 * value from the request.
976 */
977 if (has_our_contact && expiration == NOEXP) {
978 if (expires) {
979 expiration = expires->ivalue;
980 } else if (regc->expires_hdr) {
981 expiration = regc->expires_hdr->ivalue;
982 } else {
983 /* We didn't request explicit expiration value,
984 * and server doesn't specify it either. This
985 * shouldn't happen unless we have a broken
986 * registrar.
987 */
988 expiration = 3600;
989 }
990 }
991
992 }
993
994 /* If we still couldn't get matching Contact header(s), it means
995 * there must be something wrong with the registrar (e.g. it may
996 * have modified the URI's in the response, which is prohibited).
997 */
998 if (expiration==NOEXP) {
999 /* If the number of Contact headers in the response matches
1000 * ours, they're all probably ours. Get the expiration
1001 * from there if this is the case, or from Expires header
1002 * if we don't have exact Contact header count, or
1003 * from the request as the last resort.
1004 */
1005 pj_size_t our_contact_cnt;
1006
1007 our_contact_cnt = pj_list_size(&regc->contact_hdr_list);
1008
1009 if (*contact_cnt == our_contact_cnt && *contact_cnt &&
1010 contacts[0]->expires >= 0)
1011 {
1012 expiration = contacts[0]->expires;
1013 } else if (expires)
1014 expiration = expires->ivalue;
1015 else if (regc->expires_hdr)
1016 expiration = regc->expires_hdr->ivalue;
1017 else
1018 expiration = 3600;
1019 }
1020
1021 } else {
1022 /* Just assume that the unregistration has been successful. */
1023 expiration = 0;
1024 }
1025
1026 /* Must have expiration value by now */
1027 pj_assert(expiration != NOEXP);
1028
1029 return expiration;
1030}
1031
1032static void regc_tsx_callback(void *token, pjsip_event *event)
1033{
1034 pj_status_t status;
1035 pjsip_regc *regc = (pjsip_regc*) token;
1036 pjsip_transaction *tsx = event->body.tsx_state.tsx;
1037 pj_bool_t handled = PJ_TRUE;
1038
1039 pj_atomic_inc(regc->busy_ctr);
1040 pj_lock_acquire(regc->lock);
1041
1042 /* Decrement pending transaction counter. */
1043 pj_assert(regc->has_tsx);
1044 regc->has_tsx = PJ_FALSE;
1045
1046 /* Add reference to the transport */
1047 if (tsx->transport != regc->last_transport) {
1048 if (regc->last_transport) {
1049 pjsip_transport_dec_ref(regc->last_transport);
1050 regc->last_transport = NULL;
1051 }
1052
1053 if (tsx->transport) {
1054 regc->last_transport = tsx->transport;
1055 pjsip_transport_add_ref(regc->last_transport);
1056 }
1057 }
1058
1059 /* Handle 401/407 challenge (even when _delete_flag is set) */
1060 if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
1061 tsx->status_code == PJSIP_SC_UNAUTHORIZED)
1062 {
1063 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1064 pjsip_tx_data *tdata;
1065
1066 /* reset current op */
1067 regc->current_op = REGC_IDLE;
1068
1069 status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
1070 rdata,
1071 tsx->last_tx,
1072 &tdata);
1073
1074 if (status == PJ_SUCCESS) {
1075 status = pjsip_regc_send(regc, tdata);
1076 }
1077
1078 if (status != PJ_SUCCESS) {
1079
1080 /* Only call callback if application is still interested
1081 * in it.
1082 */
1083 if (regc->_delete_flag == 0) {
1084 /* Should be safe to release the lock temporarily.
1085 * We do this to avoid deadlock.
1086 */
1087 pj_lock_release(regc->lock);
1088 call_callback(regc, status, tsx->status_code,
1089 &rdata->msg_info.msg->line.status.reason,
1090 rdata, -1, 0, NULL);
1091 pj_lock_acquire(regc->lock);
1092 }
1093 }
1094
1095 } else if (regc->_delete_flag) {
1096
1097 /* User has called pjsip_regc_destroy(), so don't call callback.
1098 * This regc will be destroyed later in this function.
1099 */
1100
1101 /* Just reset current op */
1102 regc->current_op = REGC_IDLE;
1103
1104 } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
1105 regc->current_op == REGC_REGISTERING)
1106 {
1107 /* Handle 423 response automatically:
1108 * - set requested expiration to Min-Expires header, ONLY IF
1109 * the original request is a registration (as opposed to
1110 * unregistration) and the requested expiration was indeed
1111 * lower than Min-Expires)
1112 * - resend the request
1113 */
1114 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
1115 pjsip_min_expires_hdr *me_hdr;
1116 pjsip_tx_data *tdata;
1117 pj_int32_t min_exp;
1118
1119 /* reset current op */
1120 regc->current_op = REGC_IDLE;
1121
1122 /* Update requested expiration */
1123 me_hdr = (pjsip_min_expires_hdr*)
1124 pjsip_msg_find_hdr(rdata->msg_info.msg,
1125 PJSIP_H_MIN_EXPIRES, NULL);
1126 if (me_hdr) {
1127 min_exp = me_hdr->ivalue;
1128 } else {
1129 /* Broken server, Min-Expires doesn't exist.
1130 * Just guestimate then, BUT ONLY if if this is the
1131 * first time we received such response.
1132 */
1133 enum {
1134 /* Note: changing this value would require changing couple of
1135 * Python test scripts.
1136 */
1137 UNSPECIFIED_MIN_EXPIRES = 3601
1138 };
1139 if (!regc->expires_hdr ||
1140 regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
1141 {
1142 min_exp = UNSPECIFIED_MIN_EXPIRES;
1143 } else {
1144 handled = PJ_FALSE;
1145 PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
1146 "without Min-Expires header is invalid"));
1147 goto handle_err;
1148 }
1149 }
1150
1151 if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
1152 /* But we already send with greater expiration time, why does
1153 * the server send us with 423? Oh well, just fail the request.
1154 */
1155 handled = PJ_FALSE;
1156 PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
1157 "Min-Expires header value in response"));
1158 goto handle_err;
1159 }
1160
1161 set_expires(regc, min_exp);
1162
1163 status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
1164 if (status == PJ_SUCCESS) {
1165 status = pjsip_regc_send(regc, tdata);
1166 }
1167
1168 if (status != PJ_SUCCESS) {
1169 /* Only call callback if application is still interested
1170 * in it.
1171 */
1172 if (!regc->_delete_flag) {
1173 /* Should be safe to release the lock temporarily.
1174 * We do this to avoid deadlock.
1175 */
1176 pj_lock_release(regc->lock);
1177 call_callback(regc, status, tsx->status_code,
1178 &rdata->msg_info.msg->line.status.reason,
1179 rdata, -1, 0, NULL);
1180 pj_lock_acquire(regc->lock);
1181 }
1182 }
1183
1184 } else {
1185 handled = PJ_FALSE;
1186 }
1187
1188handle_err:
1189 if (!handled) {
1190 pjsip_rx_data *rdata;
1191 pj_int32_t expiration = NOEXP;
1192 unsigned contact_cnt = 0;
1193 pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
1194
1195 if (tsx->status_code/100 == 2) {
1196
1197 rdata = event->body.tsx_state.src.rdata;
1198
1199 /* Calculate expiration */
1200 expiration = calculate_response_expiration(regc, rdata,
1201 &contact_cnt,
1202 PJSIP_REGC_MAX_CONTACT,
1203 contact);
1204
1205 /* Schedule next registration */
1206 schedule_registration(regc, expiration);
1207
1208 } else {
1209 rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ?
1210 event->body.tsx_state.src.rdata : NULL;
1211 }
1212
1213 /* Update registration */
1214 if (expiration==NOEXP) expiration=-1;
1215 regc->expires = expiration;
1216
1217 /* Mark operation as complete */
1218 regc->current_op = REGC_IDLE;
1219
1220 /* Call callback. */
1221 /* Should be safe to release the lock temporarily.
1222 * We do this to avoid deadlock.
1223 */
1224 pj_lock_release(regc->lock);
1225 call_callback(regc, PJ_SUCCESS, tsx->status_code,
1226 (rdata ? &rdata->msg_info.msg->line.status.reason
1227 : &tsx->status_text),
1228 rdata, expiration,
1229 contact_cnt, contact);
1230 pj_lock_acquire(regc->lock);
1231 }
1232
1233 pj_lock_release(regc->lock);
1234
1235 /* Delete the record if user destroy regc during the callback. */
1236 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
1237 pjsip_regc_destroy(regc);
1238 }
1239}
1240
1241PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
1242{
1243 pj_status_t status;
1244 pjsip_cseq_hdr *cseq_hdr;
1245 pjsip_expires_hdr *expires_hdr;
1246 pj_uint32_t cseq;
1247
1248 pj_atomic_inc(regc->busy_ctr);
1249 pj_lock_acquire(regc->lock);
1250
1251 /* Make sure we don't have pending transaction. */
1252 if (regc->has_tsx) {
1253 PJ_LOG(4,(THIS_FILE, "Unable to send request, regc has another "
1254 "transaction pending"));
1255 pjsip_tx_data_dec_ref( tdata );
1256 pj_lock_release(regc->lock);
1257 pj_atomic_dec(regc->busy_ctr);
1258 return PJSIP_EBUSY;
1259 }
1260
1261 pj_assert(regc->current_op == REGC_IDLE);
1262
1263 /* Invalidate message buffer. */
1264 pjsip_tx_data_invalidate_msg(tdata);
1265
1266 /* Increment CSeq */
1267 cseq = ++regc->cseq_hdr->cseq;
1268 cseq_hdr = (pjsip_cseq_hdr*)
1269 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
1270 cseq_hdr->cseq = cseq;
1271
1272 /* Find Expires header */
1273 expires_hdr = (pjsip_expires_hdr*)
1274 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_EXPIRES, NULL);
1275
1276 /* Bind to transport selector */
1277 pjsip_tx_data_set_transport(tdata, &regc->tp_sel);
1278
1279 regc->has_tsx = PJ_TRUE;
1280
1281 /* Set current operation based on the value of Expires header */
1282 if (expires_hdr && expires_hdr->ivalue==0)
1283 regc->current_op = REGC_UNREGISTERING;
1284 else
1285 regc->current_op = REGC_REGISTERING;
1286
1287 /* Prevent deletion of tdata, e.g: when something wrong in sending,
1288 * we need tdata to retrieve the transport.
1289 */
1290 pjsip_tx_data_add_ref(tdata);
1291
1292 /* If via_addr is set, use this address for the Via header. */
1293 if (regc->via_addr.host.slen > 0) {
1294 tdata->via_addr = regc->via_addr;
1295 tdata->via_tp = regc->via_tp;
1296 }
1297
1298 /* Need to unlock the regc temporarily while sending the message to
1299 * prevent deadlock (https://trac.pjsip.org/repos/ticket/1247).
1300 * It should be safe to do this since the regc's refcount has been
1301 * incremented.
1302 */
1303 pj_lock_release(regc->lock);
1304
1305 /* Now send the message */
1306 status = pjsip_endpt_send_request(regc->endpt, tdata, REGC_TSX_TIMEOUT,
1307 regc, &regc_tsx_callback);
1308 if (status!=PJ_SUCCESS) {
1309 PJ_LOG(4,(THIS_FILE, "Error sending request, status=%d", status));
1310 }
1311
1312 /* Reacquire the lock */
1313 pj_lock_acquire(regc->lock);
1314
1315 /* Get last transport used and add reference to it */
1316 if (tdata->tp_info.transport != regc->last_transport &&
1317 status==PJ_SUCCESS)
1318 {
1319 if (regc->last_transport) {
1320 pjsip_transport_dec_ref(regc->last_transport);
1321 regc->last_transport = NULL;
1322 }
1323
1324 if (tdata->tp_info.transport) {
1325 regc->last_transport = tdata->tp_info.transport;
1326 pjsip_transport_add_ref(regc->last_transport);
1327 }
1328 }
1329
1330 /* Release tdata */
1331 pjsip_tx_data_dec_ref(tdata);
1332
1333 pj_lock_release(regc->lock);
1334
1335 /* Delete the record if user destroy regc during the callback. */
1336 if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
1337 pjsip_regc_destroy(regc);
1338 }
1339
1340 return status;
1341}
1342
1343