Newer
Older
}
if (!pkt->timer_a) {
pkt->timer_a = 2 ;
} else {
pkt->timer_a = 2 * pkt->timer_a;
}
/* For non-invites, a maximum of 4 secs */
if (INT_MAX / pkt->timer_a < pkt->timer_t1) {
/*
* Uh Oh, we will have an integer overflow.
* Recalculate previous timeout time instead.
*/
pkt->timer_a = pkt->timer_a / 2;
}
siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */
if (pkt->method != SIP_INVITE && siptimer_a > 4000) {
siptimer_a = 4000;
}
/* Reschedule re-transmit */
ast_debug(4, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n",
pkt->retrans + 1,
siptimer_a,
pkt->timer_t1,
pkt->retransid);
Joshua Colp
committed
if (sip_debug_test_pvt(pkt->owner)) {
const struct ast_sockaddr *dst = sip_real_dst(pkt->owner);
ast_verbose("Retransmitting #%d (%s) to %s:\n%s\n---\n",
pkt->retrans, sip_nat_mode(pkt->owner),
Joshua Colp
committed
append_history(pkt->owner, "ReTx", "%d %s", reschedule, ast_str_buffer(pkt->data));
xmitres = __sip_xmit(pkt->owner, pkt->data);
/* If there was no error during the network transmission, schedule the next retransmission,
* but if the next retransmission is going to be beyond our timeout period, mark the packet's
* stop_retrans value and set the next retransmit to be the exact time of timeout. This will
* allow any responses to the packet to be processed before the packet is destroyed on the next
* call to this function by the scheduler. */
if (xmitres != XMIT_ERROR) {
if (reschedule >= diff) {
pkt->retrans_stop = 1;
reschedule = diff;
}
return reschedule;
Joshua Colp
committed
}
/* At this point, either the packet's retransmission timed out, or there was a
* transmission error, either way destroy the scheduler item and this packet. */
pkt->retransid = -1; /* Kill this scheduler item */
if (pkt->method != SIP_OPTIONS && xmitres == 0) {
if (pkt->is_fatal || sipdebug) { /* Tell us if it's critical or if we're debugging */
ast_log(LOG_WARNING, "Retransmission timeout reached on transmission %s for seqno %u (%s %s) -- See https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions\n"
"Packet timed out after %dms with no response\n",
pkt->owner->callid,
pkt->seqno,
pkt->is_fatal ? "Critical" : "Non-critical",
pkt->is_resp ? "Response" : "Request",
(int) ast_tvdiff_ms(ast_tvnow(), pkt->time_sent));
} else if (pkt->method == SIP_OPTIONS && sipdebug) {
ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) -- See https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions\n", pkt->owner->callid);
if (xmitres == XMIT_ERROR) {
ast_log(LOG_WARNING, "Transmit error :: Cancelling transmission on Call ID %s\n", pkt->owner->callid);
append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
append_history(pkt->owner, "MaxRetries", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
sip_pvt_unlock(pkt->owner); /* SIP_PVT, not channel */
owner_chan = sip_pvt_lock_full(pkt->owner);
if (owner_chan) {
ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet (see https://wiki.asterisk.org/wiki/display/AST/SIP+Retransmissions).\n", pkt->owner->callid);
if (pkt->is_resp &&
(pkt->response_code >= 200) &&
(pkt->response_code < 300) &&
pkt->owner->pendinginvite &&
ast_test_flag(&pkt->owner->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED)) {
/* This is a timeout of the 2XX response to a pending INVITE. In this case terminate the INVITE
* transaction just as if we received the ACK, but immediately hangup with a BYE (sip_hangup
* will send the BYE as long as the dialog is not set as "alreadygone")
* RFC 3261 section 13.3.1.4.
* "If the server retransmits the 2xx response for 64*T1 seconds without receiving
* an ACK, the dialog is confirmed, but the session SHOULD be terminated. This is
* accomplished with a BYE, as described in Section 15." */
pkt->owner->invitestate = INV_TERMINATED;
pkt->owner->pendinginvite = 0;
} else {
/* there is nothing left to do, mark the dialog as gone */
sip_alreadygone(pkt->owner);
}
if (!ast_channel_hangupcause(owner_chan)) {
ast_channel_hangupcause_set(owner_chan, AST_CAUSE_NO_USER_RESPONSE);
}
ast_queue_hangup_with_cause(owner_chan, AST_CAUSE_NO_USER_RESPONSE);
} else {
/* If no channel owner, destroy now */
/* Let the peerpoke system expire packets when the timer expires for poke_noanswer */
if (pkt->method != SIP_OPTIONS && pkt->method != SIP_REGISTER) {
pvt_set_needdestroy(pkt->owner, "no response to critical packet");
sip_alreadygone(pkt->owner);
append_history(pkt->owner, "DialogKill", "Killing this failed dialog immediately");
}
}
} else if (pkt->owner->pendinginvite == pkt->seqno) {
ast_log(LOG_WARNING, "Timeout on %s on non-critical invite transaction.\n", pkt->owner->callid);
pkt->owner->invitestate = INV_TERMINATED;
pkt->owner->pendinginvite = 0;
check_pendings(pkt->owner);
if (owner_chan) {
ast_channel_unlock(owner_chan);
ast_channel_unref(owner_chan);
}
if (pkt->method == SIP_BYE) {
/* We're not getting answers on SIP BYE's. Tear down the call anyway. */
append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway.");
pvt_set_needdestroy(pkt->owner, "no response to BYE");
/* Unlink and destroy the packet object. */
for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
if (cur == pkt) {
/* Unlink the node from the list. */
UNLINK(cur, pkt->owner->packets, prev);
ao2_t_ref(pkt, -1, "Packet retransmission list (retransmission complete)");
break;
/*
* If the object was not in the list then we were in the process of
* stopping retransmisions while we were sending this retransmission.
*/
ao2_t_ref(pkt, -1, "Scheduled packet retransmission complete");
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
/* Run by the sched thread. */
static int __stop_retrans_pkt(const void *data)
{
struct sip_pkt *pkt = (void *) data;
AST_SCHED_DEL_UNREF(sched, pkt->retransid,
ao2_t_ref(pkt, -1, "Stop scheduled packet retransmission"));
ao2_t_ref(pkt, -1, "Stop packet retransmission action");
return 0;
}
static void stop_retrans_pkt(struct sip_pkt *pkt)
{
ao2_t_ref(pkt, +1, "Stop packet retransmission action");
if (ast_sched_add(sched, 0, __stop_retrans_pkt, pkt) < 0) {
/* Uh Oh. Expect bad behavior. */
ao2_t_ref(pkt, -1, "Failed to schedule stop packet retransmission action");
}
}
static void sip_pkt_dtor(void *vdoomed)
{
struct sip_pkt *pkt = (void *) vdoomed;
if (pkt->owner) {
dialog_unref(pkt->owner, "Retransmission packet is being destroyed");
}
ast_free(pkt->data);
}
/*!
* \internal
* \brief Transmit packet with retransmits
* \return 0 on success, -1 on failure to allocate packet
*/
static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, uint32_t seqno, int resp, struct ast_str *data, int fatal, int sipmethod)
struct sip_pkt *pkt = NULL;
int siptimer_a = DEFAULT_RETRANS;
int xmitres = 0;
if (sipmethod == SIP_INVITE) {
/* Note this is a pending invite */
p->pendinginvite = seqno;
Olle Johansson
committed
}
pkt = ao2_alloc_options(sizeof(*pkt), sip_pkt_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!pkt) {
/* copy data, add a terminator and save length */
pkt->data = ast_str_create(ast_str_strlen(data));
if (!pkt->data) {
ao2_t_ref(pkt, -1, "Failed to initialize");
ast_str_set(&pkt->data, 0, "%s%s", ast_str_buffer(data), "\0");
/* copy other parameters from the caller */
pkt->method = sipmethod;
pkt->seqno = seqno;
pkt->is_resp = resp;
pkt->is_fatal = fatal;
pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner");
/* The retransmission list owns a pkt ref */
pkt->next = p->packets;
p->packets = pkt; /* Add it to the queue */
if (resp) {
/* Parse out the response code */
if (sscanf(ast_str_buffer(pkt->data), "SIP/2.0 %30u", &respid) == 1) {
pkt->response_code = respid;
}
}
pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */
David Vossel
committed
siptimer_a = pkt->timer_t1;
pkt->time_sent = ast_tvnow(); /* time packet was sent */
pkt->retrans_stop_time = 64 * (pkt->timer_t1 ? pkt->timer_t1 : DEFAULT_TIMER_T1); /* time in ms after pkt->time_sent to stop retransmission */
if (!(p->socket.type & AST_TRANSPORT_UDP)) {
/* TCP does not need retransmits as that's built in, but with
* retrans_stop set, we must give it the full timer_H treatment */
pkt->retrans_stop = 1;
siptimer_a = pkt->retrans_stop_time;
/* Schedule retransmission */
ao2_t_ref(pkt, +1, "Schedule packet retransmission");
pkt->retransid = ast_sched_add_variable(sched, siptimer_a, retrans_pkt, pkt, 1);
if (pkt->retransid < 0) {
ao2_t_ref(pkt, -1, "Failed to schedule packet retransmission");
}
ast_debug(4, "*** SIP TIMER: Initializing retransmit timer on packet: Id #%d\n", pkt->retransid);
xmitres = __sip_xmit(pkt->owner, pkt->data); /* Send packet */
if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
ast_log(LOG_ERROR, "Serious Network Trouble; __sip_xmit returns error for pkt data\n");
/* Unlink and destroy the packet object. */
stop_retrans_pkt(pkt);
ao2_t_ref(pkt, -1, "Packet retransmission list");
/* This is odd, but since the retrans timer starts at 500ms and the do_monitor thread
* only wakes up every 1000ms by default, we have to poke the thread here to make
* sure it successfully detects this must be retransmitted in less time than
* it usually sleeps for. Otherwise it might not retransmit this packet for 1000ms. */
if (monitor_thread != AST_PTHREADT_NULL) {
pthread_kill(monitor_thread, SIGURG);
}
/*! \brief Kill a SIP dialog (called only by the scheduler)
* The scheduler has a reference to this dialog when p->autokillid != -1,
* and we are called using that reference. So if the event is not
* rescheduled, we need to call dialog_unref().
*/
static int __sip_autodestruct(const void *data)
struct sip_pvt *p = (struct sip_pvt *)data;
struct ast_channel *owner;
/* If this is a subscription, tell the phone that we got a timeout */
if (p->subscribed && p->subscribed != MWI_NOTIFICATION && p->subscribed != CALL_COMPLETION) {
struct state_notify_data data = { 0, };
data.state = AST_EXTENSION_DEACTIVATED;
transmit_state_notify(p, &data, 1, TRUE); /* Send last notification */
p->subscribed = NONE;
append_history(p, "Subscribestatus", "timeout");
ast_debug(3, "Re-scheduled destruction of SIP subscription %s\n", p->callid ? p->callid : "<unknown>");
return 10000; /* Reschedule this destruction so that we know that it's gone */
}
/* If there are packets still waiting for delivery, delay the destruction */
if (p->packets) {
if (!p->needdestroy) {
char method_str[31];
ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
append_history(p, "ReliableXmit", "timeout");
if (sscanf(p->lastmsg, "Tx: %30s", method_str) == 1 || sscanf(p->lastmsg, "Rx: %30s", method_str) == 1) {
if (p->ongoing_reinvite || method_match(SIP_CANCEL, method_str) || method_match(SIP_BYE, method_str)) {
pvt_set_needdestroy(p, "autodestruct");
}
/* They've had their chance to respond. Time to bail */
__sip_pretend_ack(p);
Russell Bryant
committed
/*
* Lock both the pvt and the channel safely so that we can queue up a frame.
*/
owner = sip_pvt_lock_full(p);
if (owner) {
ast_log(LOG_WARNING,
"Autodestruct on dialog '%s' with owner %s in place (Method: %s). Rescheduling destruction for 10000 ms\n",
p->callid, ast_channel_name(owner), sip_methods[p->method].text);
ast_queue_hangup_with_cause(owner, AST_CAUSE_PROTOCOL_ERROR);
ast_channel_unlock(owner);
ast_channel_unref(owner);
Mark Michelson
committed
sip_pvt_unlock(p);
return 10000;
}
/* Reset schedule ID */
p->autokillid = -1;
if (p->refer && !p->alreadygone) {
ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
} else {
append_history(p, "AutoDestroy", "%s", p->callid);
ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
sip_pvt_unlock(p);
dialog_unlink_all(p); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
dialog_unref(p, "autokillid complete");
static void do_cancel_destroy(struct sip_pvt *pvt)
{
if (-1 < pvt->autokillid) {
append_history(pvt, "CancelDestroy", "");
AST_SCHED_DEL_UNREF(sched, pvt->autokillid,
dialog_unref(pvt, "Stop scheduled autokillid"));
}
}
/* Run by the sched thread. */
static int __sip_cancel_destroy(const void *data)
{
struct sip_pvt *pvt = (void *) data;
sip_pvt_lock(pvt);
do_cancel_destroy(pvt);
sip_pvt_unlock(pvt);
dialog_unref(pvt, "Cancel destroy action");
}
void sip_cancel_destroy(struct sip_pvt *pvt)
if (pvt->final_destruction_scheduled) {
return;
dialog_ref(pvt, "Cancel destroy action");
if (ast_sched_add(sched, 0, __sip_cancel_destroy, pvt) < 0) {
/* Uh Oh. Expect bad behavior. */
dialog_unref(pvt, "Failed to schedule cancel destroy action");
ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
struct sip_scheddestroy_data {
struct sip_pvt *pvt;
int ms;
};
/* Run by the sched thread. */
static int __sip_scheddestroy(const void *data)
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
struct sip_scheddestroy_data *sched_data = (void *) data;
struct sip_pvt *pvt = sched_data->pvt;
int ms = sched_data->ms;
ast_free(sched_data);
sip_pvt_lock(pvt);
do_cancel_destroy(pvt);
if (pvt->do_history) {
append_history(pvt, "SchedDestroy", "%d ms", ms);
}
dialog_ref(pvt, "Schedule autokillid");
pvt->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, pvt);
if (pvt->autokillid < 0) {
/* Uh Oh. Expect bad behavior. */
dialog_unref(pvt, "Failed to schedule autokillid");
}
if (pvt->stimer) {
stop_session_timer(pvt);
sip_pvt_unlock(pvt);
dialog_unref(pvt, "Destroy action");
return 0;
}
static int sip_scheddestroy_full(struct sip_pvt *p, int ms)
{
struct sip_scheddestroy_data *sched_data;
if (ms < 0) {
if (p->timer_t1 == 0) {
p->timer_t1 = global_t1; /* Set timer T1 if not set (RFC 3261) */
}
if (p->timer_b == 0) {
p->timer_b = global_timer_b; /* Set timer B if not set (RFC 3261) */
}
ms = p->timer_t1 * 64;
if (sip_debug_test_pvt(p)) {
ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n",
p->callid, ms, sip_methods[p->method].text);
Brett Bryant
committed
sched_data = ast_malloc(sizeof(*sched_data));
if (!sched_data) {
/* Uh Oh. Expect bad behavior. */
return -1;
sched_data->pvt = p;
sched_data->ms = ms;
dialog_ref(p, "Destroy action");
if (ast_sched_add(sched, 0, __sip_scheddestroy, sched_data) < 0) {
/* Uh Oh. Expect bad behavior. */
dialog_unref(p, "Failed to schedule destroy action");
ast_free(sched_data);
return -1;
}
return 0;
}
void sip_scheddestroy(struct sip_pvt *p, int ms)
{
if (p->final_destruction_scheduled) {
return; /* already set final destruction */
sip_scheddestroy_full(p, ms);
void sip_scheddestroy_final(struct sip_pvt *p, int ms)
if (p->final_destruction_scheduled) {
return; /* already set final destruction */
if (!sip_scheddestroy_full(p, ms)) {
p->final_destruction_scheduled = 1;
/*! \brief Acknowledges receipt of a packet and stops retransmission
* called with p locked*/
int __sip_ack(struct sip_pvt *p, uint32_t seqno, int resp, int sipmethod)
struct sip_pkt *cur, *prev = NULL;
const char *msg = "Not Found"; /* used only for debugging */
int res = FALSE;
/* If we have an outbound proxy for this dialog, then delete it now since
the rest of the requests in this dialog needs to follow the routing.
If obforcing is set, we will keep the outbound proxy during the whole
dialog, regardless of what the SIP rfc says
*/
if (p->outboundproxy && !p->outboundproxy->force) {
for (cur = p->packets; cur; prev = cur, cur = cur->next) {
if (cur->seqno != seqno || cur->is_resp != resp) {
if (cur->is_resp || cur->method == sipmethod) {
res = TRUE;
msg = "Found";
if (!resp && (seqno == p->pendinginvite)) {
ast_debug(1, "Acked pending invite %u\n", p->pendinginvite);
p->pendinginvite = 0;
}
if (cur->retransid > -1) {
if (sipdebug)
ast_debug(4, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
}
/* Unlink and destroy the packet object. */
UNLINK(cur, p->packets, prev);
stop_retrans_pkt(cur);
ao2_t_ref(cur, -1, "Packet retransmission list");
ast_debug(1, "Stopping retransmission on '%s' of %s %u: Match %s\n",
p->callid, resp ? "Response" : "Request", seqno, msg);
return res;
}
/*! \brief Pretend to ack all packets
* called with p locked */
void __sip_pretend_ack(struct sip_pvt *p)
{
struct sip_pkt *cur = NULL;
while (p->packets) {
int method;
if (cur == p->packets) {
ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text);
return;
method = (cur->method) ? cur->method : find_sip_method(ast_str_buffer(cur->data));
__sip_ack(p, cur->seqno, cur->is_resp, method);
/*! \brief Acks receipt of packet, keep it around (used for provisional responses) */
int __sip_semi_ack(struct sip_pvt *p, uint32_t seqno, int resp, int sipmethod)
{
struct sip_pkt *cur;
int res = FALSE;
for (cur = p->packets; cur; cur = cur->next) {
if (cur->seqno == seqno && cur->is_resp == resp &&
(cur->is_resp || method_match(sipmethod, ast_str_buffer(cur->data)))) {
/* this is our baby */
if (cur->retransid > -1) {
if (sipdebug)
ast_debug(4, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text);
stop_retrans_pkt(cur);
Mark Spencer
committed
}
ast_debug(1, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %u: %s\n", p->callid, resp ? "Response" : "Request", seqno, res == -1 ? "Not Found" : "Found");
return res;
}
/*! \brief Copy SIP request, parse it */
static void parse_copy(struct sip_request *dst, const struct sip_request *src)
{
copy_request(dst, src);
parse_request(dst);
}
/*! \brief add a blank line if no body */
static void add_blank(struct sip_request *req)
{
if (!req->lines) {
/* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */
ast_str_append(&req->data, 0, "\r\n");
/* Run by the sched thread. */
static int send_provisional_keepalive_full(struct sip_pvt *pvt, int with_sdp)
{
const char *msg = NULL;
struct ast_channel *chan;
int res = 0;
chan = sip_pvt_lock_full(pvt);
Mark Spencer
committed
if (!pvt->last_provisional || !strncasecmp(pvt->last_provisional, "100", 3)) {
msg = "183 Session Progress";
}
if (pvt->invitestate < INV_COMPLETED) {
if (with_sdp) {
transmit_response_with_sdp(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq, XMIT_UNRELIABLE, FALSE, FALSE);
} else {
transmit_response(pvt, S_OR(msg, pvt->last_provisional), &pvt->initreq);
res = PROVIS_KEEPALIVE_TIMEOUT;
} else {
pvt->provisional_keepalive_sched_id = -1;
Mark Spencer
committed
sip_pvt_unlock(pvt);
if (chan) {
ast_channel_unlock(chan);
ast_channel_unref(chan);
dialog_unref(pvt, "Schedule provisional keepalive complete");
/* Run by the sched thread. */
static int send_provisional_keepalive(const void *data)
{
struct sip_pvt *pvt = (struct sip_pvt *) data;
Mark Spencer
committed
return send_provisional_keepalive_full(pvt, 0);
/* Run by the sched thread. */
static int send_provisional_keepalive_with_sdp(const void *data)
{
struct sip_pvt *pvt = (void *) data;
Sean Bright
committed
return send_provisional_keepalive_full(pvt, 1);
}
Sean Bright
committed
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
/* Run by the sched thread. */
static int __update_provisional_keepalive_full(struct sip_pvt *pvt, int with_sdp)
{
AST_SCHED_DEL_UNREF(sched, pvt->provisional_keepalive_sched_id,
dialog_unref(pvt, "Stop scheduled provisional keepalive for update"));
sip_pvt_lock(pvt);
if (pvt->invitestate < INV_COMPLETED) {
/* Provisional keepalive is still needed. */
dialog_ref(pvt, "Schedule provisional keepalive");
pvt->provisional_keepalive_sched_id = ast_sched_add(sched, PROVIS_KEEPALIVE_TIMEOUT,
with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive,
pvt);
if (pvt->provisional_keepalive_sched_id < 0) {
dialog_unref(pvt, "Failed to schedule provisional keepalive");
}
}
sip_pvt_unlock(pvt);
dialog_unref(pvt, "Update provisional keepalive action");
return 0;
}
/* Run by the sched thread. */
static int __update_provisional_keepalive(const void *data)
{
struct sip_pvt *pvt = (void *) data;
return __update_provisional_keepalive_full(pvt, 0);
}
/* Run by the sched thread. */
static int __update_provisional_keepalive_with_sdp(const void *data)
{
struct sip_pvt *pvt = (void *) data;
return __update_provisional_keepalive_full(pvt, 1);
}
static void update_provisional_keepalive(struct sip_pvt *pvt, int with_sdp)
{
dialog_ref(pvt, "Update provisional keepalive action");
if (ast_sched_add(sched, 0,
with_sdp ? __update_provisional_keepalive_with_sdp : __update_provisional_keepalive,
pvt) < 0) {
dialog_unref(pvt, "Failed to schedule update provisional keepalive action");
}
}
/* Run by the sched thread. */
static int __stop_provisional_keepalive(const void *data)
{
struct sip_pvt *pvt = (void *) data;
Sean Bright
committed
AST_SCHED_DEL_UNREF(sched, pvt->provisional_keepalive_sched_id,
dialog_unref(pvt, "Stop scheduled provisional keepalive"));
dialog_unref(pvt, "Stop provisional keepalive action");
return 0;
}
static void stop_provisional_keepalive(struct sip_pvt *pvt)
{
dialog_ref(pvt, "Stop provisional keepalive action");
if (ast_sched_add(sched, 0, __stop_provisional_keepalive, pvt) < 0) {
/* Uh Oh. Expect bad behavior. */
dialog_unref(pvt, "Failed to schedule stop provisional keepalive action");
}
Sean Bright
committed
}
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
static void add_required_respheader(struct sip_request *req)
{
struct ast_str *str;
int i;
if (!req->reqsipoptions) {
return;
}
str = ast_str_create(32);
for (i = 0; i < ARRAY_LEN(sip_options); ++i) {
if (!(req->reqsipoptions & sip_options[i].id)) {
continue;
}
if (ast_str_strlen(str) > 0) {
ast_str_append(&str, 0, ", ");
}
ast_str_append(&str, 0, "%s", sip_options[i].text);
}
if (ast_str_strlen(str) > 0) {
add_header(req, "Require", ast_str_buffer(str));
}
ast_free(str);
}
/*! \brief Transmit response on SIP request*/
static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno)
Kevin P. Fleming
committed
add_blank(req);
if (sip_debug_test_pvt(p)) {
const struct ast_sockaddr *dst = sip_real_dst(p);
ast_verbose("\n<--- %sTransmitting (%s) to %s --->\n%s\n<------------>\n",
reliable ? "Reliably " : "", sip_nat_mode(p),
append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", ast_str_buffer(tmp.data), sip_get_header(&tmp, "CSeq"),
(tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? REQ_OFFSET_TO_STR(&tmp, rlpart2) : sip_methods[tmp.method].text);
/* If we are sending a final response to an INVITE, stop retransmitting provisional responses */
if (p->initreq.method == SIP_INVITE && reliable == XMIT_CRITICAL) {
stop_provisional_keepalive(p);
__sip_reliable_xmit(p, seqno, 1, req->data, (reliable == XMIT_CRITICAL), req->method) :
__sip_xmit(p, req->data);
/*!
* \internal
* \brief Send SIP Request to the other part of the dialogue
* \return see \ref __sip_xmit
*/
static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, uint32_t seqno)
/* If we have an outbound proxy, reset peer address
Only do this once.
*/
if (p->outboundproxy) {
p->sa = p->outboundproxy->ip;
add_blank(req);
if (sip_debug_test_pvt(p)) {
if (ast_test_flag(&p->flags[0], SIP_NAT_FORCE_RPORT)) {
ast_verbose("%sTransmitting (NAT) to %s:\n%s\n---\n", reliable ? "Reliably " : "", ast_sockaddr_stringify(&p->recv), ast_str_buffer(req->data));
ast_verbose("%sTransmitting (no NAT) to %s:\n%s\n---\n", reliable ? "Reliably " : "", ast_sockaddr_stringify(&p->sa), ast_str_buffer(req->data));
append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", ast_str_buffer(tmp.data), sip_get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
__sip_reliable_xmit(p, seqno, 0, req->data, (reliable == XMIT_CRITICAL), req->method) :
__sip_xmit(p, req->data);
static void enable_dsp_detect(struct sip_pvt *p)
Joshua Colp
committed
{
Joshua Colp
committed
Joshua Colp
committed
return;
David Vossel
committed
}
Joshua Colp
committed
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
(ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
if (p->rtp) {
ast_rtp_instance_dtmf_mode_set(p->rtp, AST_RTP_DTMF_MODE_INBAND);
}
features |= DSP_FEATURE_DIGIT_DETECT;
Joshua Colp
committed
if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
features |= DSP_FEATURE_FAX_DETECT;
Joshua Colp
committed
}
if (!features) {
return;
Brett Bryant
committed
}
if (!(p->dsp = ast_dsp_new())) {
return;
Brett Bryant
committed
}
ast_dsp_set_features(p->dsp, features);
if (global_relaxdtmf) {
ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
}
Brett Bryant
committed
}
static void disable_dsp_detect(struct sip_pvt *p)
if (p->dsp) {
ast_dsp_free(p->dsp);
p->dsp = NULL;
/*! \brief Set an option on a SIP dialog */
static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen)
{
int res = -1;
struct sip_pvt *p = ast_channel_tech_pvt(chan);
ast_log(LOG_ERROR, "Attempt to Ref a null pointer. sip private structure is gone!\n");
return -1;
switch (option) {
case AST_OPTION_FORMAT_READ:
res = ast_rtp_instance_set_read_format(p->rtp, *(struct ast_format **) data);
break;
case AST_OPTION_FORMAT_WRITE:
res = ast_rtp_instance_set_write_format(p->rtp, *(struct ast_format **) data);
break;
case AST_OPTION_DIGIT_DETECT:
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
(ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
char *cp = (char *) data;
ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", ast_channel_name(chan));
if (*cp) {
enable_dsp_detect(p);
} else {
disable_dsp_detect(p);
}
res = 0;
}
break;
case AST_OPTION_SECURE_SIGNALING:
p->req_secure_signaling = *(unsigned int *) data;
res = 0;
break;
case AST_OPTION_SECURE_MEDIA:
ast_set2_flag(&p->flags[1], *(unsigned int *) data, SIP_PAGE2_USE_SRTP);
res = 0;
break;
return res;
}
/*! \brief Query an option on a SIP dialog */
static int sip_queryoption(struct ast_channel *chan, int option, void *data, int *datalen)
{
int res = -1;
enum ast_t38_state state = T38_STATE_UNAVAILABLE;
struct sip_pvt *p = (struct sip_pvt *) ast_channel_tech_pvt(chan);
if (!p) {
ast_debug(1, "Attempt to Ref a null pointer. Sip private structure is gone!\n");
return -1;
}
switch (option) {
case AST_OPTION_T38_STATE:
/* Make sure we got an ast_t38_state enum passed in */
if (*datalen != sizeof(enum ast_t38_state)) {
ast_log(LOG_ERROR, "Invalid datalen for AST_OPTION_T38_STATE option. Expected %d, got %d\n", (int)sizeof(enum ast_t38_state), *datalen);
/* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
switch (p->t38.state) {
case T38_LOCAL_REINVITE:
case T38_PEER_REINVITE:
state = T38_STATE_NEGOTIATING;
break;
case T38_ENABLED:
state = T38_STATE_NEGOTIATED;
break;
case T38_REJECTED:
state = T38_STATE_REJECTED;
break;
default:
state = T38_STATE_UNKNOWN;
}
}
*((enum ast_t38_state *) data) = state;
res = 0;
break;
case AST_OPTION_DIGIT_DETECT:
cp = (char *) data;
*cp = p->dsp ? 1 : 0;
ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", ast_channel_name(chan));
case AST_OPTION_SECURE_SIGNALING:
*((unsigned int *) data) = p->req_secure_signaling;
res = 0;
break;
case AST_OPTION_SECURE_MEDIA:
*((unsigned int *) data) = ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP) ? 1 : 0;
res = 0;
break;
case AST_OPTION_DEVICE_NAME:
if (p && p->outgoing_call) {
cp = (char *) data;
ast_copy_string(cp, p->dialstring, *datalen);
res = 0;
}
/* We purposely break with a return of -1 in the
* implied else case here
*/
break;
default:
break;
}
/*! \brief Locate closing quote in a string, skipping escaped quotes.
* optionally with a limit on the search.
* start must be past the first quote.
const char *find_closing_quote(const char *start, const char *lim)
char last_char = '\0';
const char *s;
for (s = start; *s && s != lim; last_char = *s++) {