Skip to content
Snippets Groups Projects
chan_sip.c 1.18 MiB
Newer Older
  • Learn to ignore specific revisions
  • 			}
    			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 */
    
    			reschedule = siptimer_a;
    
    			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);
    
    		if (sip_debug_test_pvt(pkt->owner)) {
    
    Mark Michelson's avatar
    Mark Michelson committed
    			const struct ast_sockaddr *dst = sip_real_dst(pkt->owner);
    
    Mark Michelson's avatar
    Mark Michelson committed
    			ast_verbose("Retransmitting #%d (%s) to %s:\n%s\n---\n",
    
    				pkt->retrans, sip_nat_mode(pkt->owner),
    
    Mark Michelson's avatar
    Mark Michelson committed
    				ast_sockaddr_stringify(dst),
    
    				ast_str_buffer(pkt->data));
    
    		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;
    			}
    
    			sip_pvt_unlock(pkt->owner);
    
    	/* 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 (pkt->is_fatal) {
    
    			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. */
    
    		sip_alreadygone(pkt->owner);
    
    		append_history(pkt->owner, "ByeFailure", "Remote peer doesn't respond to bye. Destroying call anyway.");
    		pvt_set_needdestroy(pkt->owner, "no response to BYE");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* 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.
    	 */
    
    
    	sip_pvt_unlock(pkt->owner);
    
    	ao2_t_ref(pkt, -1, "Scheduled packet retransmission complete");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /* 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;
    
    	unsigned respid;
    
    	if (sipmethod == SIP_INVITE) {
    		/* Note this is a pending invite */
    		p->pendinginvite = seqno;
    
    	pkt = ao2_alloc_options(sizeof(*pkt), sip_pkt_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
    	if (!pkt) {
    
    		return AST_FAILURE;
    
    	/* 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");
    
    		return AST_FAILURE;
    	}
    
    	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 */
    
    	if (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 */
    
    		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. */
    
    		p->packets = pkt->next;
    
    		stop_retrans_pkt(pkt);
    		ao2_t_ref(pkt, -1, "Packet retransmission list");
    
    		return AST_FAILURE;
    
    		/* 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);
    		}
    
    		return AST_SUCCESS;
    
    /*! \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");
    				}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		} else {
    
    			/* They've had their chance to respond. Time to bail */
    			__sip_pretend_ack(p);
    
    Steve Murphy's avatar
    Steve Murphy 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);
    
    	}
    
    	/* Reset schedule ID */
    	p->autokillid = -1;
    
    	if (p->refer && !p->alreadygone) {
    
    		ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
    
    Mark Michelson's avatar
    Mark Michelson committed
    		stop_media_flows(p);
    
    		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)
    
    	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);
    
    	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) {
    
    		ref_proxy(p, NULL);
    
    	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;
    
    		cur = p->packets;
    
    		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);
    
    	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);
    
    	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;
    
    	if (chan) {
    		ast_channel_unlock(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;
    
    	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;
    
    	return send_provisional_keepalive_full(pvt, 1);
    }
    
    /* 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;
    
    	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");
    	}
    
    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)
    
    	finalize_content(req);
    
    	add_blank(req);
    	if (sip_debug_test_pvt(p)) {
    
    Mark Michelson's avatar
    Mark Michelson committed
    		const struct ast_sockaddr *dst = sip_real_dst(p);
    
    Mark Michelson's avatar
    Mark Michelson committed
    		ast_verbose("\n<--- %sTransmitting (%s) to %s --->\n%s\n<------------>\n",
    
    			reliable ? "Reliably " : "", sip_nat_mode(p),
    
    Mark Michelson's avatar
    Mark Michelson committed
    			ast_sockaddr_stringify(dst),
    
    			ast_str_buffer(req->data));
    
    	}
    	if (p->do_history) {
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    		struct sip_request tmp = { .rlpart1 = 0, };
    
    		parse_copy(&tmp, req);
    
    		append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", ast_str_buffer(tmp.data), sip_get_header(&tmp, "CSeq"),
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    			(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);
    
    	res = (reliable) ?
    
    		 __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;
    
    	finalize_content(req);
    
    	add_blank(req);
    	if (sip_debug_test_pvt(p)) {
    
    Mark Michelson's avatar
    Mark Michelson committed
    		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));
    
    Mark Michelson's avatar
    Mark Michelson committed
    		} else {
    
    			ast_verbose("%sTransmitting (no NAT) to %s:\n%s\n---\n", reliable ? "Reliably " : "", ast_sockaddr_stringify(&p->sa), ast_str_buffer(req->data));
    
    	if (p->do_history) {
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    		struct sip_request tmp = { .rlpart1 = 0, };
    
    		parse_copy(&tmp, req);
    
    		append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", ast_str_buffer(tmp.data), sip_get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
    
    	res = (reliable) ?
    
    		__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)
    
    	int features = 0;
    
    	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;
    
    	if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
    		features |= DSP_FEATURE_FAX_DETECT;
    
    	if (!features) {
    		return;
    
    	if (!(p->dsp = ast_dsp_new())) {
    		return;
    
    	ast_dsp_set_features(p->dsp, features);
    	if (global_relaxdtmf) {
    		ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
    	}
    
    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++) {