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