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 					    &regc->lock);
142     if (status != PJ_SUCCESS) {
143 	pj_pool_release(pool);
144 	return status;
145     }
146 
147     status = pj_atomic_create(pool, 0, &regc->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(&regc->auth_sess, endpt, regc->pool, 0);
155     if (status != PJ_SUCCESS)
156 	return status;
157 
158     pj_list_init(&regc->route_set);
159     pj_list_init(&regc->hdr_list);
160     pj_list_init(&regc->contact_hdr_list);
161     pj_list_init(&regc->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(&regc->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, &regc->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(&regc->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(&regc->removed_contact_hdr_list,
272 		       &regc->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 != &regc->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 != &regc->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(&regc->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, &regc->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, &regc->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, &regc->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( &regc->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(&regc->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(&regc->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(&regc->route_set);
449 
450     chdr = route_set->next;
451     while (chdr != route_set) {
452 	pj_list_push_back(&regc->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(&regc->tp_sel);
469     pj_memcpy(&regc->tp_sel, sel, sizeof(*sel));
470     pjsip_tpselector_add_ref(&regc->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(&regc->hdr_list);
496 
497     hdr = hdr_list->next;
498     while (hdr != hdr_list) {
499 	pj_list_push_back(&regc->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( &regc->auth_sess, tdata );
530 
531     /* Add Route headers from route set, ideally after Via header */
532     if (!pj_list_empty(&regc->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 != &regc->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(&regc->hdr_list)) {
553 	const pjsip_hdr *hdr;
554 
555 	hdr = regc->hdr_list.next;
556 	while (hdr != &regc->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 != &regc->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(&regc->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, &regc->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, &regc->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*)&regc->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(&regc->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, &regc->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(&regc->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                                        &regc->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 = &regc_refresh_timer_cb;
852         regc->timer.id = REFRESH_TIMER;
853         regc->timer.user_data = regc;
854         pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
855         pj_gettimeofday(&regc->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(&regc->via_addr, sizeof(regc->via_addr));
878     else {
879         if (pj_strcmp(&regc->via_addr.host, &via_addr->host))
880             pj_strdup(regc->pool, &regc->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, &regc->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*)&regc->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(&regc->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(&param.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)(&param);
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 != &regc->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 != &regc->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(&regc->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( &regc->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, &regc->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, &regc_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