blob: 5381dbc356ad9774b5a5cdb993814027df820d7d [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id: sip_timer.c 4537 2013-06-19 06:47:43Z riza $ */
2/*
3 * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsip-ua/sip_timer.h>
20#include <pjsip/print_util.h>
21#include <pjsip/sip_endpoint.h>
22#include <pj/log.h>
23#include <pj/math.h>
24#include <pj/os.h>
25#include <pj/pool.h>
26
27#define THIS_FILE "sip_timer.c"
28
29
30/* Constant of Session Timers */
31#define ABS_MIN_SE 90 /* Absolute Min-SE, in seconds */
32
33
34/* String definitions */
35static const pj_str_t STR_SE = {"Session-Expires", 15};
36static const pj_str_t STR_SHORT_SE = {"x", 1};
37static const pj_str_t STR_MIN_SE = {"Min-SE", 6};
38static const pj_str_t STR_REFRESHER = {"refresher", 9};
39static const pj_str_t STR_UAC = {"uac", 3};
40static const pj_str_t STR_UAS = {"uas", 3};
41static const pj_str_t STR_TIMER = {"timer", 5};
42
43
44/* Enumeration of refresher */
45enum timer_refresher {
46 TR_UNKNOWN,
47 TR_UAC,
48 TR_UAS
49};
50
51/* Structure definition of Session Timers */
52struct pjsip_timer
53{
54 pj_bool_t active; /**< Active/inactive flag */
55 pjsip_timer_setting setting; /**< Session Timers setting */
56 enum timer_refresher refresher; /**< Session refresher */
57 pj_time_val last_refresh; /**< Timestamp of last
58 refresh */
59 pj_timer_entry timer; /**< Timer entry */
60 pj_bool_t use_update; /**< Use UPDATE method to
61 refresh the session */
62 pj_bool_t with_sdp; /**< SDP in UPDATE? */
63 pjsip_role_e role; /**< Role in last INVITE/
64 UPDATE transaction. */
65
66};
67
68/* External global vars */
69extern pj_bool_t pjsip_use_compact_form;
70
71/* Local functions & vars */
72static void stop_timer(pjsip_inv_session *inv);
73static void start_timer(pjsip_inv_session *inv);
74static pj_bool_t is_initialized;
75const pjsip_method pjsip_update_method = { PJSIP_OTHER_METHOD, {"UPDATE", 6}};
76/*
77 * Session-Expires header vptr.
78 */
79static int se_hdr_print(pjsip_sess_expires_hdr *hdr,
80 char *buf, pj_size_t size);
81static pjsip_sess_expires_hdr* se_hdr_clone(pj_pool_t *pool,
82 const pjsip_sess_expires_hdr *hdr);
83static pjsip_sess_expires_hdr* se_hdr_shallow_clone(
84 pj_pool_t *pool,
85 const pjsip_sess_expires_hdr* hdr);
86
87static pjsip_hdr_vptr se_hdr_vptr =
88{
89 (pjsip_hdr_clone_fptr) &se_hdr_clone,
90 (pjsip_hdr_clone_fptr) &se_hdr_shallow_clone,
91 (pjsip_hdr_print_fptr) &se_hdr_print,
92};
93
94/*
95 * Min-SE header vptr.
96 */
97static int min_se_hdr_print(pjsip_min_se_hdr *hdr,
98 char *buf, pj_size_t size);
99static pjsip_min_se_hdr* min_se_hdr_clone(pj_pool_t *pool,
100 const pjsip_min_se_hdr *hdr);
101static pjsip_min_se_hdr* min_se_hdr_shallow_clone(
102 pj_pool_t *pool,
103 const pjsip_min_se_hdr* hdr);
104
105static pjsip_hdr_vptr min_se_hdr_vptr =
106{
107 (pjsip_hdr_clone_fptr) &min_se_hdr_clone,
108 (pjsip_hdr_clone_fptr) &min_se_hdr_shallow_clone,
109 (pjsip_hdr_print_fptr) &min_se_hdr_print,
110};
111
112/*
113 * Session-Expires header vptr.
114 */
115static int se_hdr_print(pjsip_sess_expires_hdr *hdr,
116 char *buf, pj_size_t size)
117{
118 char *p = buf;
119 char *endbuf = buf+size;
120 pj_ssize_t printed;
121 const pjsip_parser_const_t *pc = pjsip_parser_const();
122 const pj_str_t *hname = pjsip_use_compact_form? &hdr->sname : &hdr->name;
123
124 /* Print header name and value */
125 if ((endbuf - p) < (hname->slen + 16))
126 return -1;
127
128 copy_advance(p, (*hname));
129 *p++ = ':';
130 *p++ = ' ';
131
132 printed = pj_utoa(hdr->sess_expires, p);
133 p += printed;
134
135 /* Print 'refresher' param */
136 if (hdr->refresher.slen)
137 {
138 if ((endbuf - p) < (STR_REFRESHER.slen + 2 + hdr->refresher.slen))
139 return -1;
140
141 *p++ = ';';
142 copy_advance(p, STR_REFRESHER);
143 *p++ = '=';
144 copy_advance(p, hdr->refresher);
145 }
146
147 /* Print generic params */
148 printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
149 &pc->pjsip_TOKEN_SPEC,
150 &pc->pjsip_TOKEN_SPEC, ';');
151 if (printed < 0)
152 return (int)printed;
153
154 p += printed;
155 return (int)(p - buf);
156}
157
158static pjsip_sess_expires_hdr* se_hdr_clone(pj_pool_t *pool,
159 const pjsip_sess_expires_hdr *hsrc)
160{
161 pjsip_sess_expires_hdr *hdr = pjsip_sess_expires_hdr_create(pool);
162 hdr->sess_expires = hsrc->sess_expires;
163 pj_strdup(pool, &hdr->refresher, &hsrc->refresher);
164 pjsip_param_clone(pool, &hdr->other_param, &hsrc->other_param);
165 return hdr;
166}
167
168static pjsip_sess_expires_hdr* se_hdr_shallow_clone(
169 pj_pool_t *pool,
170 const pjsip_sess_expires_hdr* hsrc)
171{
172 pjsip_sess_expires_hdr *hdr = PJ_POOL_ALLOC_T(pool,pjsip_sess_expires_hdr);
173 pj_memcpy(hdr, hsrc, sizeof(*hdr));
174 pjsip_param_shallow_clone(pool, &hdr->other_param, &hsrc->other_param);
175 return hdr;
176}
177
178/*
179 * Min-SE header vptr.
180 */
181static int min_se_hdr_print(pjsip_min_se_hdr *hdr,
182 char *buf, pj_size_t size)
183{
184 char *p = buf;
185 char *endbuf = buf+size;
186 pj_ssize_t printed;
187 const pjsip_parser_const_t *pc = pjsip_parser_const();
188
189 /* Print header name and value */
190 if ((endbuf - p) < (hdr->name.slen + 16))
191 return -1;
192
193 copy_advance(p, hdr->name);
194 *p++ = ':';
195 *p++ = ' ';
196
197 printed = pj_utoa(hdr->min_se, p);
198 p += printed;
199
200 /* Print generic params */
201 printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
202 &pc->pjsip_TOKEN_SPEC,
203 &pc->pjsip_TOKEN_SPEC, ';');
204 if (printed < 0)
205 return (int)printed;
206
207 p += printed;
208 return (int)(p - buf);
209}
210
211static pjsip_min_se_hdr* min_se_hdr_clone(pj_pool_t *pool,
212 const pjsip_min_se_hdr *hsrc)
213{
214 pjsip_min_se_hdr *hdr = pjsip_min_se_hdr_create(pool);
215 hdr->min_se = hsrc->min_se;
216 pjsip_param_clone(pool, &hdr->other_param, &hsrc->other_param);
217 return hdr;
218}
219
220static pjsip_min_se_hdr* min_se_hdr_shallow_clone(
221 pj_pool_t *pool,
222 const pjsip_min_se_hdr* hsrc)
223{
224 pjsip_min_se_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_min_se_hdr);
225 pj_memcpy(hdr, hsrc, sizeof(*hdr));
226 pjsip_param_shallow_clone(pool, &hdr->other_param, &hsrc->other_param);
227 return hdr;
228}
229
230
231/*
232 * Parse Session-Expires header.
233 */
234static pjsip_hdr *parse_hdr_se(pjsip_parse_ctx *ctx)
235{
236 pjsip_sess_expires_hdr *hdr = pjsip_sess_expires_hdr_create(ctx->pool);
237 const pjsip_parser_const_t *pc = pjsip_parser_const();
238 pj_str_t token;
239
240 pj_scan_get(ctx->scanner, &pc->pjsip_DIGIT_SPEC, &token);
241 hdr->sess_expires = pj_strtoul(&token);
242
243 while (*ctx->scanner->curptr == ';') {
244 pj_str_t pname, pvalue;
245
246 pj_scan_get_char(ctx->scanner);
247 pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
248
249 if (pj_stricmp(&pname, &STR_REFRESHER)==0) {
250 hdr->refresher = pvalue;
251 } else {
252 pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
253 param->name = pname;
254 param->value = pvalue;
255 pj_list_push_back(&hdr->other_param, param);
256 }
257 }
258 pjsip_parse_end_hdr_imp( ctx->scanner );
259 return (pjsip_hdr*)hdr;
260}
261
262/*
263 * Parse Min-SE header.
264 */
265static pjsip_hdr *parse_hdr_min_se(pjsip_parse_ctx *ctx)
266{
267 pjsip_min_se_hdr *hdr = pjsip_min_se_hdr_create(ctx->pool);
268 const pjsip_parser_const_t *pc = pjsip_parser_const();
269 pj_str_t token;
270
271 pj_scan_get(ctx->scanner, &pc->pjsip_DIGIT_SPEC, &token);
272 hdr->min_se = pj_strtoul(&token);
273
274 while (*ctx->scanner->curptr == ';') {
275 pj_str_t pname, pvalue;
276 pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
277
278 pj_scan_get_char(ctx->scanner);
279 pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
280
281 param->name = pname;
282 param->value = pvalue;
283 pj_list_push_back(&hdr->other_param, param);
284 }
285 pjsip_parse_end_hdr_imp( ctx->scanner );
286 return (pjsip_hdr*)hdr;
287}
288
289
290/* Add "Session-Expires" and "Min-SE" headers. Note that "Min-SE" header
291 * can only be added to INVITE/UPDATE request and 422 response.
292 */
293static void add_timer_headers(pjsip_inv_session *inv, pjsip_tx_data *tdata,
294 pj_bool_t add_se, pj_bool_t add_min_se)
295{
296 pjsip_timer *timer = inv->timer;
297
298 /* Add Session-Expires header */
299 if (add_se) {
300 pjsip_sess_expires_hdr *hdr;
301
302 hdr = pjsip_sess_expires_hdr_create(tdata->pool);
303 hdr->sess_expires = timer->setting.sess_expires;
304 if (timer->refresher != TR_UNKNOWN)
305 hdr->refresher = (timer->refresher == TR_UAC? STR_UAC : STR_UAS);
306
307 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hdr);
308 }
309
310 /* Add Min-SE header */
311 if (add_min_se) {
312 pjsip_min_se_hdr *hdr;
313
314 hdr = pjsip_min_se_hdr_create(tdata->pool);
315 hdr->min_se = timer->setting.min_se;
316
317 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hdr);
318 }
319}
320
321/* Timer callback. When the timer is fired, it can be time to refresh
322 * the session if UA is the refresher, otherwise it is time to end
323 * the session.
324 */
325static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
326{
327 pjsip_inv_session *inv = (pjsip_inv_session*) entry->user_data;
328 pjsip_tx_data *tdata = NULL;
329 pj_status_t status;
330 pj_bool_t as_refresher;
331
332 pj_assert(inv);
333
334 inv->timer->timer.id = 0;
335
336 PJ_UNUSED_ARG(timer_heap);
337
338 /* Lock dialog. */
339 pjsip_dlg_inc_lock(inv->dlg);
340
341 /* Check our role */
342 as_refresher =
343 (inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) ||
344 (inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS);
345
346 /* Do action based on role, refresher or refreshee */
347 if (as_refresher) {
348 pj_time_val now;
349
350 /* As refresher, reshedule the refresh request on the following:
351 * - msut not send re-INVITE if another INVITE or SDP negotiation
352 * is in progress.
353 * - must not send UPDATE with SDP if SDP negotiation is in progress
354 */
355 pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg);
356 if ( (!inv->timer->use_update && (
357 inv->invite_tsx != NULL ||
358 neg_state != PJMEDIA_SDP_NEG_STATE_DONE)
359 )
360 ||
361 (inv->timer->use_update && inv->timer->with_sdp &&
362 neg_state != PJMEDIA_SDP_NEG_STATE_DONE
363 )
364 )
365 {
366 pj_time_val delay = {1, 0};
367
368 inv->timer->timer.id = 1;
369 pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer,
370 &delay);
371 pjsip_dlg_dec_lock(inv->dlg);
372 return;
373 }
374
375 /* Refresher, refresh the session */
376 if (inv->timer->use_update) {
377 const pjmedia_sdp_session *offer = NULL;
378
379 if (inv->timer->with_sdp) {
380 pjmedia_sdp_neg_get_active_local(inv->neg, &offer);
381 }
382 status = pjsip_inv_update(inv, NULL, offer, &tdata);
383 } else {
384 /* Create re-INVITE without modifying session */
385 pjsip_msg_body *body;
386 const pjmedia_sdp_session *offer = NULL;
387
388 pj_assert(pjmedia_sdp_neg_get_state(inv->neg) ==
389 PJMEDIA_SDP_NEG_STATE_DONE);
390
391 status = pjsip_inv_invite(inv, &tdata);
392 if (status == PJ_SUCCESS)
393 status = pjmedia_sdp_neg_send_local_offer(inv->pool_prov,
394 inv->neg, &offer);
395 if (status == PJ_SUCCESS)
396 status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
397 if (status == PJ_SUCCESS) {
398 status = pjsip_create_sdp_body(tdata->pool,
399 (pjmedia_sdp_session*)offer, &body);
400 tdata->msg->body = body;
401 }
402 }
403
404 pj_gettimeofday(&now);
405 PJ_LOG(4, (inv->pool->obj_name,
406 "Refreshing session after %ds (expiration period=%ds)",
407 (now.sec-inv->timer->last_refresh.sec),
408 inv->timer->setting.sess_expires));
409 } else {
410
411 pj_time_val now;
412
413 /* Refreshee, terminate the session */
414 status = pjsip_inv_end_session(inv, PJSIP_SC_REQUEST_TIMEOUT,
415 NULL, &tdata);
416
417 pj_gettimeofday(&now);
418 PJ_LOG(3, (inv->pool->obj_name,
419 "No session refresh received after %ds "
420 "(expiration period=%ds), stopping session now!",
421 (now.sec-inv->timer->last_refresh.sec),
422 inv->timer->setting.sess_expires));
423 }
424
425 /* Unlock dialog. */
426 pjsip_dlg_dec_lock(inv->dlg);
427
428 /* Send message, if any */
429 if (tdata && status == PJ_SUCCESS) {
430 status = pjsip_inv_send_msg(inv, tdata);
431 }
432
433 /* Print error message, if any */
434 if (status != PJ_SUCCESS) {
435 PJ_PERROR(2, (inv->pool->obj_name, status,
436 "Error in %s session timer",
437 (as_refresher? "refreshing" : "terminating")));
438 }
439}
440
441/* Start Session Timers */
442static void start_timer(pjsip_inv_session *inv)
443{
444 const pj_str_t UPDATE = { "UPDATE", 6 };
445 pjsip_timer *timer = inv->timer;
446 pj_time_val delay = {0};
447
448 pj_assert(inv->timer->active == PJ_TRUE);
449
450 stop_timer(inv);
451
452 inv->timer->use_update =
453 (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL,
454 &UPDATE) == PJSIP_DIALOG_CAP_SUPPORTED);
455 if (!inv->timer->use_update) {
456 /* INVITE always needs SDP */
457 inv->timer->with_sdp = PJ_TRUE;
458 }
459
460 pj_timer_entry_init(&timer->timer,
461 1, /* id */
462 inv, /* user data */
463 timer_cb); /* callback */
464
465 /* Set delay based on role, refresher or refreshee */
466 if ((timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) ||
467 (timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS))
468 {
469 /* Next refresh, the delay is half of session expire */
470 delay.sec = timer->setting.sess_expires / 2;
471 } else {
472 /* Send BYE if no refresh received until this timer fired, delay
473 * is the minimum of 32 seconds and one third of the session interval
474 * before session expiration.
475 */
476 delay.sec = timer->setting.sess_expires -
477 timer->setting.sess_expires/3;
478 delay.sec = PJ_MAX((long)timer->setting.sess_expires-32, delay.sec);
479 }
480
481 /* Schedule the timer */
482 pjsip_endpt_schedule_timer(inv->dlg->endpt, &timer->timer, &delay);
483
484 /* Update last refresh time */
485 pj_gettimeofday(&timer->last_refresh);
486}
487
488/* Stop Session Timers */
489static void stop_timer(pjsip_inv_session *inv)
490{
491 if (inv->timer->timer.id != 0) {
492 pjsip_endpt_cancel_timer(inv->dlg->endpt, &inv->timer->timer);
493 inv->timer->timer.id = 0;
494 }
495}
496
497/* Deinitialize Session Timers */
498static void pjsip_timer_deinit_module(pjsip_endpoint *endpt)
499{
500 PJ_TODO(provide_initialized_flag_for_each_endpoint);
501 PJ_UNUSED_ARG(endpt);
502 is_initialized = PJ_FALSE;
503}
504
505/*
506 * Initialize Session Timers support in PJSIP.
507 */
508PJ_DEF(pj_status_t) pjsip_timer_init_module(pjsip_endpoint *endpt)
509{
510 pj_status_t status;
511
512 PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
513
514 if (is_initialized)
515 return PJ_SUCCESS;
516
517 /* Register Session-Expires header parser */
518 status = pjsip_register_hdr_parser( STR_SE.ptr, STR_SHORT_SE.ptr,
519 &parse_hdr_se);
520 if (status != PJ_SUCCESS)
521 return status;
522
523 /* Register Min-SE header parser */
524 status = pjsip_register_hdr_parser( STR_MIN_SE.ptr, NULL,
525 &parse_hdr_min_se);
526 if (status != PJ_SUCCESS)
527 return status;
528
529 /* Register 'timer' capability to endpoint */
530 status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED,
531 NULL, 1, &STR_TIMER);
532 if (status != PJ_SUCCESS)
533 return status;
534
535 /* Register deinit module to be executed when PJLIB shutdown */
536 if (pjsip_endpt_atexit(endpt, &pjsip_timer_deinit_module) != PJ_SUCCESS) {
537 /* Failure to register this function may cause this module won't
538 * work properly when the stack is restarted (without quitting
539 * application).
540 */
541 pj_assert(!"Failed to register Session Timer deinit.");
542 PJ_LOG(1, (THIS_FILE, "Failed to register Session Timer deinit."));
543 }
544
545 is_initialized = PJ_TRUE;
546
547 return PJ_SUCCESS;
548}
549
550
551/*
552 * Initialize Session Timers setting with default values.
553 */
554PJ_DEF(pj_status_t) pjsip_timer_setting_default(pjsip_timer_setting *setting)
555{
556 pj_bzero(setting, sizeof(pjsip_timer_setting));
557
558 setting->sess_expires = PJSIP_SESS_TIMER_DEF_SE;
559 setting->min_se = ABS_MIN_SE;
560
561 return PJ_SUCCESS;
562}
563
564/*
565 * Initialize Session Timers in an INVITE session.
566 */
567PJ_DEF(pj_status_t) pjsip_timer_init_session(
568 pjsip_inv_session *inv,
569 const pjsip_timer_setting *setting)
570{
571 pjsip_timer_setting *s;
572
573 pj_assert(is_initialized);
574 PJ_ASSERT_RETURN(inv, PJ_EINVAL);
575
576 /* Allocate and/or reset Session Timers structure */
577 if (!inv->timer)
578 inv->timer = PJ_POOL_ZALLOC_T(inv->pool, pjsip_timer);
579 else
580 pj_bzero(inv->timer, sizeof(pjsip_timer));
581
582 s = &inv->timer->setting;
583
584 /* Init Session Timers setting */
585 if (setting) {
586 PJ_ASSERT_RETURN(setting->min_se >= ABS_MIN_SE,
587 PJ_ETOOSMALL);
588 PJ_ASSERT_RETURN(setting->sess_expires >= setting->min_se,
589 PJ_EINVAL);
590
591 pj_memcpy(s, setting, sizeof(*s));
592 } else {
593 pjsip_timer_setting_default(s);
594 }
595
596 return PJ_SUCCESS;
597}
598
599
600/*
601 * Create Session-Expires header.
602 */
603PJ_DEF(pjsip_sess_expires_hdr*) pjsip_sess_expires_hdr_create(
604 pj_pool_t *pool)
605{
606 pjsip_sess_expires_hdr *hdr = PJ_POOL_ZALLOC_T(pool,
607 pjsip_sess_expires_hdr);
608
609 pj_assert(is_initialized);
610
611 hdr->type = PJSIP_H_OTHER;
612 hdr->name = STR_SE;
613 hdr->sname = STR_SHORT_SE;
614 hdr->vptr = &se_hdr_vptr;
615 pj_list_init(hdr);
616 pj_list_init(&hdr->other_param);
617 return hdr;
618}
619
620
621/*
622 * Create Min-SE header.
623 */
624PJ_DEF(pjsip_min_se_hdr*) pjsip_min_se_hdr_create(pj_pool_t *pool)
625{
626 pjsip_min_se_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_min_se_hdr);
627
628 pj_assert(is_initialized);
629
630 hdr->type = PJSIP_H_OTHER;
631 hdr->name = STR_MIN_SE;
632 hdr->vptr = &min_se_hdr_vptr;
633 pj_list_init(hdr);
634 pj_list_init(&hdr->other_param);
635 return hdr;
636}
637
638
639/*
640 * This function generates headers for Session Timers for intial and
641 * refresh INVITE or UPDATE.
642 */
643PJ_DEF(pj_status_t) pjsip_timer_update_req(pjsip_inv_session *inv,
644 pjsip_tx_data *tdata)
645{
646 PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL);
647
648 /* Check if Session Timers is supported */
649 if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0)
650 return PJ_SUCCESS;
651
652 pj_assert(is_initialized);
653
654 /* Make sure Session Timers is initialized */
655 if (inv->timer == NULL)
656 pjsip_timer_init_session(inv, NULL);
657
658 /* If refresher role (i.e: ours or peer) has been set/negotiated,
659 * better to keep it.
660 */
661 if (inv->timer->refresher != TR_UNKNOWN) {
662 pj_bool_t as_refresher;
663
664 /* Check our refresher role */
665 as_refresher =
666 (inv->timer->refresher==TR_UAC && inv->timer->role==PJSIP_ROLE_UAC) ||
667 (inv->timer->refresher==TR_UAS && inv->timer->role==PJSIP_ROLE_UAS);
668
669 /* Update transaction role */
670 inv->timer->role = PJSIP_ROLE_UAC;
671
672 /* Update refresher role */
673 inv->timer->refresher = as_refresher? TR_UAC : TR_UAS;
674 }
675
676 /* Add Session Timers headers */
677 add_timer_headers(inv, tdata, PJ_TRUE, PJ_TRUE);
678
679 return PJ_SUCCESS;
680}
681
682/*
683 * This function will handle Session Timers part of INVITE/UPDATE
684 * responses with code:
685 * - 422 (Session Interval Too Small)
686 * - 2xx final response
687 */
688PJ_DEF(pj_status_t) pjsip_timer_process_resp(pjsip_inv_session *inv,
689 const pjsip_rx_data *rdata,
690 pjsip_status_code *st_code)
691{
692 const pjsip_msg *msg;
693
694 PJ_ASSERT_ON_FAIL(inv && rdata,
695 {if(st_code)*st_code=PJSIP_SC_INTERNAL_SERVER_ERROR;return PJ_EINVAL;});
696
697 /* Check if Session Timers is supported */
698 if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0)
699 return PJ_SUCCESS;
700
701 pj_assert(is_initialized);
702
703 msg = rdata->msg_info.msg;
704 pj_assert(msg->type == PJSIP_RESPONSE_MSG);
705
706 /* Only process response of INVITE or UPDATE */
707 if (rdata->msg_info.cseq->method.id != PJSIP_INVITE_METHOD &&
708 pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_update_method))
709 {
710 return PJ_SUCCESS;
711 }
712
713 if (msg->line.status.code == PJSIP_SC_SESSION_TIMER_TOO_SMALL) {
714 /* Our Session-Expires is too small, let's update it based on
715 * Min-SE header in the response.
716 */
717 pjsip_tx_data *tdata;
718 pjsip_min_se_hdr *min_se_hdr;
719 pjsip_hdr *hdr;
720 pjsip_via_hdr *via;
721
722 /* Get Min-SE value from response */
723 min_se_hdr = (pjsip_min_se_hdr*)
724 pjsip_msg_find_hdr_by_name(msg, &STR_MIN_SE, NULL);
725 if (min_se_hdr == NULL) {
726 /* Response 422 should contain Min-SE header */
727 return PJ_SUCCESS;
728 }
729
730 /* Session Timers should have been initialized here */
731 pj_assert(inv->timer);
732
733 /* Update Min-SE */
734 inv->timer->setting.min_se = PJ_MAX(min_se_hdr->min_se,
735 inv->timer->setting.min_se);
736
737 /* Update Session Timers setting */
738 if (inv->timer->setting.sess_expires < inv->timer->setting.min_se)
739 inv->timer->setting.sess_expires = inv->timer->setting.min_se;
740
741 /* Prepare to restart the request */
742
743 /* Get the original INVITE request. */
744 tdata = inv->invite_req;
745
746 /* Remove branch param in Via header. */
747 via = (pjsip_via_hdr*) pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
748 pj_assert(via);
749 via->branch_param.slen = 0;
750
751 /* Restore strict route set.
752 * See http://trac.pjsip.org/repos/ticket/492
753 */
754 pjsip_restore_strict_route_set(tdata);
755
756 /* Must invalidate the message! */
757 pjsip_tx_data_invalidate_msg(tdata);
758
759 pjsip_tx_data_add_ref(tdata);
760
761 /* Update Session Timers headers */
762 hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_name(tdata->msg,
763 &STR_MIN_SE, NULL);
764 if (hdr != NULL) pj_list_erase(hdr);
765
766 hdr = (pjsip_hdr*) pjsip_msg_find_hdr_by_names(tdata->msg, &STR_SE,
767 &STR_SHORT_SE, NULL);
768 if (hdr != NULL) pj_list_erase(hdr);
769
770 add_timer_headers(inv, tdata, PJ_TRUE, PJ_TRUE);
771
772 /* Restart UAC */
773 pjsip_inv_uac_restart(inv, PJ_FALSE);
774 pjsip_inv_send_msg(inv, tdata);
775
776 return PJ_SUCCESS;
777
778 } else if (msg->line.status.code/100 == 2) {
779
780 pjsip_sess_expires_hdr *se_hdr;
781
782 /* Find Session-Expires header */
783 se_hdr = (pjsip_sess_expires_hdr*) pjsip_msg_find_hdr_by_names(
784 msg, &STR_SE,
785 &STR_SHORT_SE, NULL);
786 if (se_hdr == NULL) {
787 /* Remote doesn't support/want Session Timers, check if local
788 * require or force to use Session Timers.
789 */
790 if (inv->options & PJSIP_INV_REQUIRE_TIMER) {
791 if (st_code)
792 *st_code = PJSIP_SC_EXTENSION_REQUIRED;
793 pjsip_timer_end_session(inv);
794 return PJSIP_ERRNO_FROM_SIP_STATUS(
795 PJSIP_SC_EXTENSION_REQUIRED);
796 }
797
798 if ((inv->options & PJSIP_INV_ALWAYS_USE_TIMER) == 0) {
799 /* Session Timers not forced */
800 pjsip_timer_end_session(inv);
801 return PJ_SUCCESS;
802 }
803 }
804
805 /* Make sure Session Timers is initialized */
806 if (inv->timer == NULL)
807 pjsip_timer_init_session(inv, NULL);
808
809 /* Session expiration period specified by remote is lower than our
810 * Min-SE.
811 */
812 if (se_hdr &&
813 se_hdr->sess_expires < inv->timer->setting.min_se)
814 {
815 /* See ticket #954, instead of returning non-PJ_SUCCESS (which
816 * may cause disconnecting call/dialog), let's just accept the
817 * SE and update our local SE, as long as it isn't less than 90s.
818 */
819 if (se_hdr->sess_expires >= ABS_MIN_SE) {
820 PJ_LOG(3, (inv->pool->obj_name,
821 "Peer responds with bad Session-Expires, %ds, "
822 "which is less than Min-SE specified in request, "
823 "%ds. Well, let's just accept and use it.",
824 se_hdr->sess_expires, inv->timer->setting.min_se));
825
826 inv->timer->setting.sess_expires = se_hdr->sess_expires;
827 inv->timer->setting.min_se = se_hdr->sess_expires;
828 }
829
830 //if (st_code)
831 // *st_code = PJSIP_SC_SESSION_TIMER_TOO_SMALL;
832 //pjsip_timer_end_session(inv);
833 //return PJSIP_ERRNO_FROM_SIP_STATUS(
834 // PJSIP_SC_SESSION_TIMER_TOO_SMALL);
835 }
836
837 /* Update SE. Session-Expires in response cannot be lower than Min-SE.
838 * Session-Expires in response can only be equal or lower than in
839 * request.
840 */
841 if (se_hdr &&
842 se_hdr->sess_expires <= inv->timer->setting.sess_expires &&
843 se_hdr->sess_expires >= inv->timer->setting.min_se)
844 {
845 /* Good SE from remote, update local SE */
846 inv->timer->setting.sess_expires = se_hdr->sess_expires;
847 }
848
849 /* Set the refresher */
850 if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAC) == 0)
851 inv->timer->refresher = TR_UAC;
852 else if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAS) == 0)
853 inv->timer->refresher = TR_UAS;
854 else
855 /* UAS should set the refresher, however, there is a case that
856 * UAS doesn't support/want Session Timers but the UAC insists
857 * to use Session Timers.
858 */
859 inv->timer->refresher = TR_UAC;
860
861 /* Remember our role in this transaction */
862 inv->timer->role = PJSIP_ROLE_UAC;
863
864 /* Finally, set active flag and start the Session Timers */
865 inv->timer->active = PJ_TRUE;
866 start_timer(inv);
867
868 } else if (pjsip_method_cmp(&rdata->msg_info.cseq->method,
869 &pjsip_update_method) == 0 &&
870 msg->line.status.code >= 400 && msg->line.status.code < 600)
871 {
872 /* This is to handle error response to previous UPDATE that was
873 * sent without SDP. In this case, retry sending UPDATE but
874 * with SDP this time.
875 * Note: the additional expressions are to check that the
876 * UPDATE was really the one sent by us, not by other
877 * call components (e.g. to change codec)
878 */
879 if (inv->timer->timer.id == 0 && inv->timer->use_update &&
880 inv->timer->with_sdp == PJ_FALSE)
881 {
882 inv->timer->with_sdp = PJ_TRUE;
883 timer_cb(NULL, &inv->timer->timer);
884 }
885 }
886
887 return PJ_SUCCESS;
888}
889
890/*
891 * Handle incoming INVITE or UPDATE request.
892 */
893PJ_DEF(pj_status_t) pjsip_timer_process_req(pjsip_inv_session *inv,
894 const pjsip_rx_data *rdata,
895 pjsip_status_code *st_code)
896{
897 pjsip_min_se_hdr *min_se_hdr;
898 pjsip_sess_expires_hdr *se_hdr;
899 const pjsip_msg *msg;
900 unsigned min_se;
901
902 PJ_ASSERT_ON_FAIL(inv && rdata,
903 {if(st_code)*st_code=PJSIP_SC_INTERNAL_SERVER_ERROR;return PJ_EINVAL;});
904
905 /* Check if Session Timers is supported */
906 if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0)
907 return PJ_SUCCESS;
908
909 pj_assert(is_initialized);
910
911 msg = rdata->msg_info.msg;
912 pj_assert(msg->type == PJSIP_REQUEST_MSG);
913
914 /* Only process INVITE or UPDATE request */
915 if (msg->line.req.method.id != PJSIP_INVITE_METHOD &&
916 pjsip_method_cmp(&rdata->msg_info.cseq->method, &pjsip_update_method))
917 {
918 return PJ_SUCCESS;
919 }
920
921 /* Find Session-Expires header */
922 se_hdr = (pjsip_sess_expires_hdr*) pjsip_msg_find_hdr_by_names(
923 msg, &STR_SE, &STR_SHORT_SE, NULL);
924 if (se_hdr == NULL) {
925 /* Remote doesn't support/want Session Timers, check if local
926 * require or force to use Session Timers. Note that Supported and
927 * Require headers negotiation should have been verified by invite
928 * session.
929 */
930 if ((inv->options &
931 (PJSIP_INV_REQUIRE_TIMER | PJSIP_INV_ALWAYS_USE_TIMER)) == 0)
932 {
933 /* Session Timers not forced/required */
934 pjsip_timer_end_session(inv);
935 return PJ_SUCCESS;
936 }
937 }
938
939 /* Make sure Session Timers is initialized */
940 if (inv->timer == NULL)
941 pjsip_timer_init_session(inv, NULL);
942
943 /* Find Min-SE header */
944 min_se_hdr = (pjsip_min_se_hdr*) pjsip_msg_find_hdr_by_name(msg,
945 &STR_MIN_SE, NULL);
946 /* Update Min-SE */
947 min_se = inv->timer->setting.min_se;
948 if (min_se_hdr)
949 min_se = PJ_MAX(min_se_hdr->min_se, min_se);
950
951 /* Validate SE. Session-Expires cannot be lower than Min-SE
952 * (or 90 seconds if Min-SE is not set).
953 */
954 if (se_hdr && se_hdr->sess_expires < min_se) {
955 if (st_code)
956 *st_code = PJSIP_SC_SESSION_TIMER_TOO_SMALL;
957 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_SESSION_TIMER_TOO_SMALL);
958 }
959
960 /* Update SE. Note that there is a case that SE is not available in the
961 * request (which means remote doesn't want/support it), but local insists
962 * to use Session Timers.
963 */
964 if (se_hdr) {
965 /* Update SE as specified by peer. */
966 inv->timer->setting.sess_expires = se_hdr->sess_expires;
967 } else if (inv->timer->setting.sess_expires < min_se) {
968 /* There is no SE in the request (remote support Session Timers but
969 * doesn't want to use it, it just specify Min-SE) and local SE is
970 * lower than Min-SE specified by remote.
971 */
972 inv->timer->setting.sess_expires = min_se;
973 }
974
975 /* Set the refresher */
976 if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAC) == 0)
977 inv->timer->refresher = TR_UAC;
978 else if (se_hdr && pj_stricmp(&se_hdr->refresher, &STR_UAS) == 0)
979 inv->timer->refresher = TR_UAS;
980 else {
981 /* If refresher role (i.e: ours or peer) has been set/negotiated,
982 * better to keep it.
983 */
984 if (inv->timer->refresher != TR_UNKNOWN) {
985 pj_bool_t as_refresher;
986
987 /* Check our refresher role */
988 as_refresher =
989 (inv->timer->refresher==TR_UAC && inv->timer->role==PJSIP_ROLE_UAC) ||
990 (inv->timer->refresher==TR_UAS && inv->timer->role==PJSIP_ROLE_UAS);
991
992 /* Update refresher role */
993 inv->timer->refresher = as_refresher? TR_UAS : TR_UAC;
994 } else {
995 /* If UAC support timer (currently check the existance of
996 * Session-Expires header in the request), set UAC as refresher.
997 */
998 inv->timer->refresher = se_hdr? TR_UAC : TR_UAS;
999 }
1000 }
1001
1002 /* Remember our role in this transaction */
1003 inv->timer->role = PJSIP_ROLE_UAS;
1004
1005 /* Set active flag */
1006 inv->timer->active = PJ_TRUE;
1007
1008 return PJ_SUCCESS;
1009}
1010
1011/*
1012 * Handle outgoing response with status code 2xx & 422.
1013 */
1014PJ_DEF(pj_status_t) pjsip_timer_update_resp(pjsip_inv_session *inv,
1015 pjsip_tx_data *tdata)
1016{
1017 pjsip_msg *msg;
1018
1019 /* Check if Session Timers is supported */
1020 if ((inv->options & PJSIP_INV_SUPPORT_TIMER) == 0)
1021 return PJ_SUCCESS;
1022
1023 pj_assert(is_initialized);
1024 PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL);
1025
1026 msg = tdata->msg;
1027
1028 if (msg->line.status.code/100 == 2)
1029 {
1030 if (inv->timer && inv->timer->active) {
1031 /* Add Session-Expires header and start the timer */
1032 add_timer_headers(inv, tdata, PJ_TRUE, PJ_FALSE);
1033
1034 /* Add 'timer' to Require header (see ticket #1560). */
1035 if (inv->timer->refresher == TR_UAC) {
1036 pjsip_require_hdr *req_hdr;
1037 pj_bool_t req_hdr_has_timer = PJ_FALSE;
1038
1039 req_hdr = (pjsip_require_hdr*)
1040 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_REQUIRE,
1041 NULL);
1042 if (req_hdr == NULL) {
1043 req_hdr = pjsip_require_hdr_create(tdata->pool);
1044 PJ_ASSERT_RETURN(req_hdr, PJ_ENOMEM);
1045 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr);
1046 } else {
1047 unsigned i;
1048 for (i = 0; i < req_hdr->count; ++i) {
1049 if (pj_stricmp(&req_hdr->values[i], &STR_TIMER)) {
1050 req_hdr_has_timer = PJ_TRUE;
1051 break;
1052 }
1053 }
1054 }
1055 if (!req_hdr_has_timer)
1056 req_hdr->values[req_hdr->count++] = STR_TIMER;
1057 }
1058
1059 /* Finally, start timer. */
1060 start_timer(inv);
1061 }
1062 }
1063 else if (msg->line.status.code == PJSIP_SC_SESSION_TIMER_TOO_SMALL)
1064 {
1065 /* Add Min-SE header */
1066 add_timer_headers(inv, tdata, PJ_FALSE, PJ_TRUE);
1067 }
1068
1069 return PJ_SUCCESS;
1070}
1071
1072
1073/*
1074 * End the Session Timers.
1075 */
1076PJ_DEF(pj_status_t) pjsip_timer_end_session(pjsip_inv_session *inv)
1077{
1078 PJ_ASSERT_RETURN(inv, PJ_EINVAL);
1079
1080 if (inv->timer) {
1081 /* Reset active flag */
1082 inv->timer->active = PJ_FALSE;
1083
1084 /* Stop Session Timers */
1085 stop_timer(inv);
1086 }
1087
1088 return PJ_SUCCESS;
1089}