1
/* $Id: sip_100rel.c 2394 2008-12-23 17:27:53Z bennylp $ */
3
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
* Additional permission under GNU GPL version 3 section 7:
22
* If you modify this program, or any covered work, by linking or
23
* combining it with the OpenSSL project's OpenSSL library (or a
24
* modified version of that library), containing parts covered by the
25
* terms of the OpenSSL or SSLeay licenses, Teluu Inc. (http://www.teluu.com)
26
* grants you additional permission to convey the resulting work.
27
* Corresponding Source for a non-source form of such a combination
28
* shall include the source code for the parts of OpenSSL used as well
29
* as that of the covered work.
31
#include <pjsip-ua/sip_100rel.h>
32
#include <pjsip/sip_endpoint.h>
33
#include <pjsip/sip_event.h>
34
#include <pjsip/sip_module.h>
35
#include <pjsip/sip_transaction.h>
36
#include <pj/assert.h>
43
#define THIS_FILE "sip_100rel.c"
46
PJ_DEF_DATA(const pjsip_method) pjsip_prack_method =
52
typedef struct dlg_data dlg_data;
57
static pj_status_t mod_100rel_load(pjsip_endpoint *endpt);
59
static void on_retransmit(pj_timer_heap_t *timer_heap,
60
struct pj_timer_entry *entry);
63
const pj_str_t tag_100rel = { "100rel", 6 };
64
const pj_str_t RSEQ = { "RSeq", 4 };
65
const pj_str_t RACK = { "RAck", 4 };
69
static struct mod_100rel
72
pjsip_endpoint *endpt;
76
NULL, NULL, /* prev, next. */
77
{ "mod-100rel", 10 }, /* Name. */
79
PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */
80
&mod_100rel_load, /* load() */
84
NULL, /* on_rx_request() */
85
NULL, /* on_rx_response() */
86
NULL, /* on_tx_request. */
87
NULL, /* on_tx_response() */
88
NULL, /* on_tsx_state() */
93
/* List of pending transmission (may include the final response as well) */
94
typedef struct tx_data_list_t
96
PJ_DECL_LIST_MEMBER(struct tx_data_list_t);
102
/* Below, UAS and UAC roles are of the INVITE transaction */
105
typedef struct uas_state_t
108
pj_uint32_t rseq; /* Initialized to -1 */
109
tx_data_list_t tx_data_list;
110
unsigned retransmit_count;
111
pj_timer_entry retransmit_timer;
116
typedef struct uac_state_t
119
pj_uint32_t rseq; /* Initialized to -1 */
123
/* State attached to each dialog. */
126
pjsip_inv_session *inv;
127
uas_state_t *uas_state;
128
uac_state_t *uac_state;
132
/*****************************************************************************
136
*****************************************************************************
138
static pj_status_t mod_100rel_load(pjsip_endpoint *endpt)
140
mod_100rel.endpt = endpt;
141
pjsip_endpt_add_capability(endpt, &mod_100rel.mod,
143
1, &pjsip_prack_method.name);
144
pjsip_endpt_add_capability(endpt, &mod_100rel.mod,
145
PJSIP_H_SUPPORTED, NULL,
151
static pjsip_require_hdr *find_req_hdr(pjsip_msg *msg)
153
pjsip_require_hdr *hreq;
155
hreq = (pjsip_require_hdr*)
156
pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL);
160
for (i=0; i<hreq->count; ++i) {
161
if (!pj_stricmp(&hreq->values[i], &tag_100rel)) {
166
if ((void*)hreq->next == (void*)&msg->hdr)
169
hreq = (pjsip_require_hdr*)
170
pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, hreq->next);
179
* Get PRACK method constant.
181
PJ_DEF(const pjsip_method*) pjsip_get_prack_method(void)
183
return &pjsip_prack_method;
190
PJ_DEF(pj_status_t) pjsip_100rel_init_module(pjsip_endpoint *endpt)
192
if (mod_100rel.mod.id != -1)
195
return pjsip_endpt_register_module(endpt, &mod_100rel.mod);
200
* API: attach 100rel support in invite session. Called by
203
PJ_DEF(pj_status_t) pjsip_100rel_attach(pjsip_inv_session *inv)
207
/* Check that 100rel module has been initialized */
208
PJ_ASSERT_RETURN(mod_100rel.mod.id >= 0, PJ_EINVALIDOP);
210
/* Create and attach as dialog usage */
211
dd = PJ_POOL_ZALLOC_T(inv->dlg->pool, dlg_data);
213
pjsip_dlg_add_usage(inv->dlg, &mod_100rel.mod, (void*)dd);
215
PJ_LOG(5,(dd->inv->dlg->obj_name, "100rel module attached"));
222
* Check if incoming response has reliable provisional response feature.
224
PJ_DEF(pj_bool_t) pjsip_100rel_is_reliable(pjsip_rx_data *rdata)
226
pjsip_msg *msg = rdata->msg_info.msg;
228
PJ_ASSERT_RETURN(msg->type == PJSIP_RESPONSE_MSG, PJ_FALSE);
230
return msg->line.status.code > 100 && msg->line.status.code < 200 &&
231
rdata->msg_info.require != NULL &&
232
find_req_hdr(msg) != NULL;
237
* Create PRACK request for the incoming reliable provisional response.
239
PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
240
pjsip_rx_data *rdata,
241
pjsip_tx_data **p_tdata)
244
pjsip_transaction *tsx;
246
pjsip_generic_string_hdr *rseq_hdr;
247
pjsip_generic_string_hdr *rack_hdr;
251
pjsip_tx_data *tdata;
256
dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
257
PJ_ASSERT_RETURN(dd != NULL, PJSIP_ENOTINITIALIZED);
259
tsx = pjsip_rdata_get_tsx(rdata);
260
msg = rdata->msg_info.msg;
262
/* Check our assumptions */
263
pj_assert( tsx->role == PJSIP_ROLE_UAC &&
264
tsx->method.id == PJSIP_INVITE_METHOD &&
265
msg->line.status.code > 100 &&
266
msg->line.status.code < 200);
269
/* Get the RSeq header */
270
rseq_hdr = (pjsip_generic_string_hdr*)
271
pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL);
272
if (rseq_hdr == NULL) {
273
PJ_LOG(4,(dd->inv->dlg->obj_name,
274
"Ignoring provisional response with no RSeq header"));
275
return PJSIP_EMISSINGHDR;
277
rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue);
279
/* Create new UAC state if we don't have one */
280
if (dd->uac_state == NULL) {
281
dd->uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool,
283
dd->uac_state->cseq = rdata->msg_info.cseq->cseq;
284
dd->uac_state->rseq = rseq - 1;
287
/* If this is from new INVITE transaction, reset UAC state */
288
if (rdata->msg_info.cseq->cseq != dd->uac_state->cseq) {
289
dd->uac_state->cseq = rdata->msg_info.cseq->cseq;
290
dd->uac_state->rseq = rseq - 1;
293
/* Ignore provisional response retransmission */
294
if (rseq <= dd->uac_state->rseq) {
295
/* This should have been handled before */
298
/* Ignore provisional response with out-of-order RSeq */
299
} else if (rseq != dd->uac_state->rseq + 1) {
300
PJ_LOG(4,(dd->inv->dlg->obj_name,
301
"Ignoring provisional response because RSeq jump "
302
"(expecting %u, got %u)",
303
dd->uac_state->rseq+1, rseq));
307
/* Update our RSeq */
308
dd->uac_state->rseq = rseq;
311
status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method,
313
if (status != PJ_SUCCESS)
316
/* Create RAck header */
318
rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf),
320
rseq, rdata->msg_info.cseq->cseq,
321
(int)tsx->method.name.slen,
322
tsx->method.name.ptr);
323
rack_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RACK, &rack);
324
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) rack_hdr);
334
* Send PRACK request.
336
PJ_DEF(pj_status_t) pjsip_100rel_send_prack( pjsip_inv_session *inv,
337
pjsip_tx_data *tdata)
341
dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
342
PJ_ASSERT_ON_FAIL(dd != NULL,
343
{pjsip_tx_data_dec_ref(tdata); return PJSIP_ENOTINITIALIZED; });
345
return pjsip_dlg_send_request(inv->dlg, tdata,
346
mod_100rel.mod.id, (void*) dd);
352
* Notify 100rel module that the invite session has been disconnected.
354
PJ_DEF(pj_status_t) pjsip_100rel_end_session(pjsip_inv_session *inv)
358
dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
362
/* Make sure we don't have pending transmission */
364
pj_assert(!dd->uas_state->retransmit_timer.id);
365
pj_assert(pj_list_empty(&dd->uas_state->tx_data_list));
372
static void parse_rack(const pj_str_t *rack,
373
pj_uint32_t *p_rseq, pj_int32_t *p_seq,
376
const char *p = rack->ptr, *end = p + rack->slen;
379
token.ptr = (char*)p;
380
while (p < end && pj_isdigit(*p))
382
token.slen = p - token.ptr;
383
*p_rseq = pj_strtoul(&token);
386
token.ptr = (char*)p;
387
while (p < end && pj_isdigit(*p))
389
token.slen = p - token.ptr;
390
*p_seq = pj_strtoul(&token);
394
p_method->ptr = (char*)p;
395
p_method->slen = end - p;
397
p_method->ptr = NULL;
402
/* Clear all responses in the transmission list */
403
static void clear_all_responses(dlg_data *dd)
407
tl = dd->uas_state->tx_data_list.next;
408
while (tl != &dd->uas_state->tx_data_list) {
409
pjsip_tx_data_dec_ref(tl->tdata);
412
pj_list_init(&dd->uas_state->tx_data_list);
417
* Handle incoming PRACK request.
419
PJ_DEF(pj_status_t) pjsip_100rel_on_rx_prack( pjsip_inv_session *inv,
420
pjsip_rx_data *rdata)
423
pjsip_transaction *tsx;
425
pjsip_generic_string_hdr *rack_hdr;
426
pjsip_tx_data *tdata;
432
tsx = pjsip_rdata_get_tsx(rdata);
433
pj_assert(tsx != NULL);
435
msg = rdata->msg_info.msg;
437
dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
439
/* UAC sends us PRACK while we didn't send reliable provisional
440
* response. Respond with 400 (?)
442
const pj_str_t reason = pj_str("Unexpected PRACK");
444
status = pjsip_dlg_create_response(inv->dlg, rdata, 400,
446
if (status == PJ_SUCCESS) {
447
status = pjsip_dlg_send_response(inv->dlg, tsx, tdata);
449
return PJSIP_ENOTINITIALIZED;
452
/* Always reply with 200/OK for PRACK */
453
status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
454
if (status == PJ_SUCCESS) {
455
status = pjsip_dlg_send_response(inv->dlg, tsx, tdata);
458
/* Ignore if we don't have pending transmission */
459
if (dd->uas_state == NULL || pj_list_empty(&dd->uas_state->tx_data_list)) {
460
PJ_LOG(4,(dd->inv->dlg->obj_name,
461
"PRACK ignored - no pending response"));
465
/* Find RAck header */
466
rack_hdr = (pjsip_generic_string_hdr*)
467
pjsip_msg_find_hdr_by_name(msg, &RACK, NULL);
469
/* RAck header not found */
470
PJ_LOG(4,(dd->inv->dlg->obj_name, "No RAck header"));
471
return PJSIP_EMISSINGHDR;
474
/* Parse RAck header */
475
parse_rack(&rack_hdr->hvalue, &rseq, &cseq, &method);
478
/* Match RAck against outgoing transmission */
479
if (rseq == dd->uas_state->tx_data_list.next->rseq &&
480
cseq == dd->uas_state->cseq)
483
* Yes this PRACK matches outgoing transmission.
485
tx_data_list_t *tl = dd->uas_state->tx_data_list.next;
487
if (dd->uas_state->retransmit_timer.id) {
488
pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
489
&dd->uas_state->retransmit_timer);
490
dd->uas_state->retransmit_timer.id = PJ_FALSE;
493
/* Remove from the list */
494
if (tl != &dd->uas_state->tx_data_list) {
497
/* Destroy the response */
498
pjsip_tx_data_dec_ref(tl->tdata);
501
/* Schedule next packet */
502
dd->uas_state->retransmit_count = 0;
503
if (!pj_list_empty(&dd->uas_state->tx_data_list)) {
504
on_retransmit(NULL, &dd->uas_state->retransmit_timer);
508
/* No it doesn't match */
509
PJ_LOG(4,(dd->inv->dlg->obj_name,
510
"Rx PRACK with no matching reliable response"));
519
* This is retransmit timer callback, called initially to send the response,
520
* and subsequently when the retransmission time elapses.
522
static void on_retransmit(pj_timer_heap_t *timer_heap,
523
struct pj_timer_entry *entry)
527
pjsip_tx_data *tdata;
531
PJ_UNUSED_ARG(timer_heap);
533
dd = (dlg_data*) entry->user_data;
535
entry->id = PJ_FALSE;
537
++dd->uas_state->retransmit_count;
538
if (dd->uas_state->retransmit_count >= 7) {
539
/* If a reliable provisional response is retransmitted for
540
64*T1 seconds without reception of a corresponding PRACK,
541
the UAS SHOULD reject the original request with a 5xx
544
pj_str_t reason = pj_str("Reliable response timed out");
547
/* Clear all pending responses */
548
clear_all_responses(dd);
550
/* Send 500 response */
551
status = pjsip_inv_end_session(dd->inv, 500, &reason, &tdata);
552
if (status == PJ_SUCCESS) {
553
pjsip_dlg_send_response(dd->inv->dlg,
560
pj_assert(!pj_list_empty(&dd->uas_state->tx_data_list));
561
tl = dd->uas_state->tx_data_list.next;
564
pjsip_tx_data_add_ref(tdata);
565
final = tdata->msg->line.status.code >= 200;
567
if (dd->uas_state->retransmit_count == 1) {
568
pjsip_tsx_send_msg(dd->inv->invite_tsx, tdata);
570
pjsip_tsx_retransmit_no_state(dd->inv->invite_tsx, tdata);
574
/* This is final response, which will be retransmitted by
575
* UA layer. There's no more task to do, so clear the
576
* transmission list and bail out.
578
clear_all_responses(dd);
582
/* Schedule next retransmission */
583
if (dd->uas_state->retransmit_count < 6) {
585
delay.msec = (1 << dd->uas_state->retransmit_count) *
587
pj_time_val_normalize(&delay);
594
pjsip_endpt_schedule_timer(dd->inv->dlg->endpt,
595
&dd->uas_state->retransmit_timer,
602
/* Clone response. */
603
static pjsip_tx_data *clone_tdata(dlg_data *dd,
604
const pjsip_tx_data *src)
607
const pjsip_hdr *hsrc;
611
status = pjsip_endpt_create_tdata(dd->inv->dlg->endpt, &dst);
612
if (status != PJ_SUCCESS)
615
msg = pjsip_msg_create(dst->pool, PJSIP_RESPONSE_MSG);
617
pjsip_tx_data_add_ref(dst);
619
/* Duplicate status line */
620
msg->line.status.code = src->msg->line.status.code;
621
pj_strdup(dst->pool, &msg->line.status.reason,
622
&src->msg->line.status.reason);
624
/* Duplicate all headers */
625
hsrc = src->msg->hdr.next;
626
while (hsrc != &src->msg->hdr) {
627
pjsip_hdr *h = (pjsip_hdr*) pjsip_hdr_clone(dst->pool, hsrc);
628
pjsip_msg_add_hdr(msg, h);
632
/* Duplicate message body */
634
msg->body = pjsip_msg_body_clone(dst->pool, src->msg->body);
636
PJ_LOG(5,(dd->inv->dlg->obj_name,
637
"Reliable response %s created",
638
pjsip_tx_data_get_info(dst)));
644
/* Check if any pending response in transmission list has SDP */
645
static pj_bool_t has_sdp(dlg_data *dd)
649
tl = dd->uas_state->tx_data_list.next;
650
while (tl != &dd->uas_state->tx_data_list) {
651
if (tl->tdata->msg->body)
660
/* Send response reliably */
661
PJ_DEF(pj_status_t) pjsip_100rel_tx_response(pjsip_inv_session *inv,
662
pjsip_tx_data *tdata)
664
pjsip_cseq_hdr *cseq_hdr;
665
pjsip_generic_string_hdr *rseq_hdr;
666
pjsip_require_hdr *req_hdr;
669
pjsip_tx_data *old_tdata;
672
PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG,
673
PJSIP_ENOTRESPONSEMSG);
675
status_code = tdata->msg->line.status.code;
677
/* 100 response doesn't need PRACK */
678
if (status_code == 100)
679
return pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
682
/* Get the 100rel data attached to this dialog */
683
dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
684
PJ_ASSERT_RETURN(dd != NULL, PJ_EINVALIDOP);
688
* We need to clone tdata because we may need to keep it in our
689
* retransmission list, while the original dialog may modify it
690
* if it wants to send another response.
693
tdata = clone_tdata(dd, old_tdata);
694
pjsip_tx_data_dec_ref(old_tdata);
697
/* Get CSeq header, and make sure this is INVITE response */
698
cseq_hdr = (pjsip_cseq_hdr*)
699
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
700
PJ_ASSERT_RETURN(cseq_hdr != NULL, PJ_EBUG);
701
PJ_ASSERT_RETURN(cseq_hdr->method.id == PJSIP_INVITE_METHOD,
704
/* Remove existing Require header */
705
req_hdr = find_req_hdr(tdata->msg);
707
pj_list_erase(req_hdr);
710
/* Remove existing RSeq header */
711
rseq_hdr = (pjsip_generic_string_hdr*)
712
pjsip_msg_find_hdr_by_name(tdata->msg, &RSEQ, NULL);
714
pj_list_erase(rseq_hdr);
716
/* Different treatment for provisional and final response */
717
if (status_code/100 == 2) {
719
/* RFC 3262 Section 3: UAS Behavior:
721
The UAS MAY send a final response to the initial request
722
before having received PRACKs for all unacknowledged
723
reliable provisional responses, unless the final response
724
is 2xx and any of the unacknowledged reliable provisional
725
responses contained a session description. In that case,
726
it MUST NOT send a final response until those provisional
727
responses are acknowledged.
730
if (dd->uas_state && has_sdp(dd)) {
731
/* Yes we have transmitted 1xx with SDP reliably.
732
* In this case, must queue the 2xx response.
736
tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t);
738
tl->rseq = (pj_uint32_t)-1;
739
pj_list_push_back(&dd->uas_state->tx_data_list, tl);
741
/* Will send later */
744
PJ_LOG(4,(dd->inv->dlg->obj_name,
745
"2xx response will be sent after PRACK"));
747
} else if (dd->uas_state) {
749
RFC 3262 Section 3: UAS Behavior:
751
If the UAS does send a final response when reliable
752
responses are still unacknowledged, it SHOULD NOT
753
continue to retransmit the unacknowledged reliable
754
provisional responses, but it MUST be prepared to
755
process PRACK requests for those outstanding
759
PJ_LOG(4,(dd->inv->dlg->obj_name,
760
"No SDP sent so far, sending 2xx now"));
762
/* Cancel the retransmit timer */
763
if (dd->uas_state->retransmit_timer.id) {
764
pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
765
&dd->uas_state->retransmit_timer);
766
dd->uas_state->retransmit_timer.id = PJ_FALSE;
769
/* Clear all pending responses (drop 'em) */
770
clear_all_responses(dd);
772
/* And transmit the 2xx response */
773
status=pjsip_dlg_send_response(inv->dlg,
774
inv->invite_tsx, tdata);
777
/* We didn't send any reliable provisional response */
779
/* Transmit the 2xx response */
780
status=pjsip_dlg_send_response(inv->dlg,
781
inv->invite_tsx, tdata);
784
} else if (status_code >= 300) {
787
RFC 3262 Section 3: UAS Behavior:
789
If the UAS does send a final response when reliable
790
responses are still unacknowledged, it SHOULD NOT
791
continue to retransmit the unacknowledged reliable
792
provisional responses, but it MUST be prepared to
793
process PRACK requests for those outstanding
797
/* Cancel the retransmit timer */
798
if (dd->uas_state && dd->uas_state->retransmit_timer.id) {
799
pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
800
&dd->uas_state->retransmit_timer);
801
dd->uas_state->retransmit_timer.id = PJ_FALSE;
803
/* Clear all pending responses (drop 'em) */
804
clear_all_responses(dd);
807
/* And transmit the 2xx response */
808
status=pjsip_dlg_send_response(inv->dlg,
809
inv->invite_tsx, tdata);
813
* This is provisional response.
819
/* Create UAS state if we don't have one */
820
if (dd->uas_state == NULL) {
821
dd->uas_state = PJ_POOL_ZALLOC_T(inv->dlg->pool,
823
dd->uas_state->cseq = cseq_hdr->cseq;
824
dd->uas_state->rseq = pj_rand() % 0x7FFF;
825
pj_list_init(&dd->uas_state->tx_data_list);
826
dd->uas_state->retransmit_timer.user_data = dd;
827
dd->uas_state->retransmit_timer.cb = &on_retransmit;
830
/* Check that CSeq match */
831
PJ_ASSERT_RETURN(cseq_hdr->cseq == dd->uas_state->cseq,
834
/* Add Require header */
835
req_hdr = pjsip_require_hdr_create(tdata->pool);
837
req_hdr->values[0] = tag_100rel;
838
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr);
840
/* Add RSeq header */
841
pj_ansi_snprintf(rseq_str, sizeof(rseq_str), "%u",
842
dd->uas_state->rseq);
843
rseq = pj_str(rseq_str);
844
rseq_hdr = pjsip_generic_string_hdr_create(tdata->pool,
846
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)rseq_hdr);
848
/* Create list entry for this response */
849
tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t);
851
tl->rseq = dd->uas_state->rseq++;
853
/* Add to queue if there's pending response, otherwise
854
* transmit immediately.
856
if (!pj_list_empty(&dd->uas_state->tx_data_list)) {
858
int code = tdata->msg->line.status.code;
860
/* Will send later */
861
pj_list_push_back(&dd->uas_state->tx_data_list, tl);
864
PJ_LOG(4,(dd->inv->dlg->obj_name,
865
"Reliable %d response enqueued (%d pending)",
866
code, pj_list_size(&dd->uas_state->tx_data_list)));
869
pj_list_push_back(&dd->uas_state->tx_data_list, tl);
871
dd->uas_state->retransmit_count = 0;
872
on_retransmit(NULL, &dd->uas_state->retransmit_timer);