Skip to content
Snippets Groups Projects
chan_sip.c 1.16 MiB
Newer Older
  • Learn to ignore specific revisions
  • 			/* 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);
    			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);
    
    			return  reschedule;
    
    	/* 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)");
    
    	if (pkt->is_fatal) {
    		while(pkt->owner->owner && ast_channel_trylock(pkt->owner->owner)) {
    			sip_pvt_unlock(pkt->owner);	/* SIP_PVT, not channel */
    			usleep(1);
    			sip_pvt_lock(pkt->owner);
    		}
    
    		if (pkt->owner->owner && !ast_channel_hangupcause(pkt->owner->owner)) {
    			ast_channel_hangupcause_set(pkt->owner->owner, AST_CAUSE_NO_USER_RESPONSE);
    
    		if (pkt->owner->owner) {
    
    			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);
    			}
    
    			ast_queue_hangup_with_cause(pkt->owner->owner, AST_CAUSE_NO_USER_RESPONSE);
    
    			ast_channel_unlock(pkt->owner->owner);
    		} 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");
    			}
    		}
    	}
    
    	if (pkt->method == SIP_BYE) {
    		/* We're not getting answers on SIP BYE's.  Tear down the call anyway. */
    
    		sip_alreadygone(pkt->owner);
    
    		if (pkt->owner->owner) {
    
    			ast_channel_unlock(pkt->owner->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
    	}
    
    	/* Remove the packet */
    	for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
    		if (cur == pkt) {
    			UNLINK(cur, pkt->owner->packets, prev);
    			sip_pvt_unlock(pkt->owner);
    
    				pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
    
    				ast_free(pkt->data);
    
    			pkt->data = NULL;
    			ast_free(pkt);
    			return 0;
    		}
    	}
    	/* error case */
    	ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
    	sip_pvt_unlock(pkt->owner);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /*!
     * \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;
    
    	/* If the transport is something reliable (TCP or TLS) then don't really send this reliably */
    	/* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
    	/*! \todo According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
    
    	if (!(p->socket.type & AST_TRANSPORT_UDP)) {
    
    		xmitres = __sip_xmit(p, data);	/* Send packet */
    
    		if (xmitres == XMIT_ERROR) {	/* Serious network trouble, no need to try again */
    			append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
    			return AST_FAILURE;
    		} else {
    			return AST_SUCCESS;
    		}
    	}
    
    	if (!(pkt = ast_calloc(1, sizeof(*pkt)))) {
    
    		return AST_FAILURE;
    
    	/* copy data, add a terminator and save length */
    
    	if (!(pkt->data = ast_str_create(ast_str_strlen(data)))) {
    
    		ast_free(pkt);
    		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");
    	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 */
    	pkt->retransid = -1;
    
    	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 */
    
    
    	/* Schedule retransmission */
    	AST_SCHED_REPLACE_VARIABLE(pkt->retransid, sched, siptimer_a, retrans_pkt, pkt, 1);
    
    		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");
    		AST_SCHED_DEL(sched, pkt->retransid);
    		p->packets = pkt->next;
    		pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
    		ast_free(pkt->data);
    		ast_free(pkt);
    		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
    		}
    
    	/* Reset schedule ID */
    	p->autokillid = -1;
    
    	/*
    	 * 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);
    
    	} else 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 */
    		sip_pvt_lock(p);
    
    		/* dialog_unref(p, "unref dialog-- no other matching conditions"); -- unlink all now should finish off the dialog's references and free it. */
    		/* sip_destroy(p); */		/* Go ahead and destroy dialog. All attempts to recover is done */
    		/* sip_destroy also absorbs the reference */
    	}
    
    	dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it.");
    
    /*! \brief Schedule final destruction of SIP dialog.  This can not be canceled.
     *  This function is used to keep a dialog around for a period of time in order
     *  to properly respond to any retransmits. */
    void sip_scheddestroy_final(struct sip_pvt *p, int ms)
    {
    	if (p->final_destruction_scheduled) {
    		return; /* already set final destruction */
    	}
    
    	sip_scheddestroy(p, ms);
    	if (p->autokillid != -1) {
    		p->final_destruction_scheduled = 1;
    	}
    }
    
    
    /*! \brief Schedule destruction of SIP dialog */
    void sip_scheddestroy(struct sip_pvt *p, int ms)
    
    	if (p->final_destruction_scheduled) {
    		return; /* already set final destruction */
    	}
    
    
    	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);
    
    	}
    	if (sip_cancel_destroy(p)) {
    
    		ast_log(LOG_WARNING, "Unable to cancel SIP destruction.  Expect bad things.\n");
    
    	if (p->do_history) {
    
    		append_history(p, "SchedDestroy", "%d ms", ms);
    
    	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p, "setting ref as passing into ast_sched_add for __sip_autodestruct"));
    
    	if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0) {
    
    		stop_session_timer(p);
    
    /*! \brief Cancel destruction of SIP dialog.
     * Be careful as this also absorbs the reference - if you call it
     * from within the scheduler, this might be the last reference.
     */
    int sip_cancel_destroy(struct sip_pvt *p)
    
    	if (p->final_destruction_scheduled) {
    
    	if (p->autokillid > -1) {
    
    		append_history(p, "CancelDestroy", "");
    		AST_SCHED_DEL_UNREF(sched, p->autokillid, dialog_unref(p, "remove ref for autokillid"));
    
    /*! \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);
    			}
    			/* This odd section is designed to thwart a
    			 * race condition in the packet scheduler. There are
    			 * two conditions under which deleting the packet from the
    			 * scheduler can fail.
    			 *
    			 * 1. The packet has been removed from the scheduler because retransmission
    			 * is being attempted. The problem is that if the packet is currently attempting
    			 * retransmission and we are at this point in the code, then that MUST mean
    			 * that retrans_pkt is waiting on p's lock. Therefore we will relinquish the
    			 * lock temporarily to allow retransmission.
    			 *
    			 * 2. The packet has reached its maximum number of retransmissions and has
    			 * been permanently removed from the packet scheduler. If this is the case, then
    			 * the packet's retransid will be set to -1. The atomicity of the setting and checking
    			 * of the retransid to -1 is ensured since in both cases p's lock is held.
    
    			while (cur->retransid > -1 && ast_sched_del(sched, cur->retransid)) {
    				sip_pvt_unlock(p);
    				usleep(1);
    				sip_pvt_lock(p);
    
    			UNLINK(cur, p->packets, prev);
    			dialog_unref(cur->owner, "unref pkt cur->owner dialog from sip ack before freeing pkt");
    
    			if (cur->data) {
    
    				ast_free(cur->data);
    
    			ast_free(cur);
    			break;
    
    	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_SCHED_DEL(sched, cur->retransid);
    			res = TRUE;
    			break;
    
    	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");
    
    }
    
    static int send_provisional_keepalive_full(struct sip_pvt *pvt, int with_sdp)
    {
    	const char *msg = NULL;
    
    	struct ast_channel *chan;
    	int res = 0;
    
    	int old_sched_id = pvt->provisional_keepalive_sched_id;
    
    
    	chan = sip_pvt_lock_full(pvt);
    
    	/* Check that nothing has changed while we were waiting for the lock */
    	if (old_sched_id != pvt->provisional_keepalive_sched_id) {
    		/* Keepalive has been cancelled or rescheduled, clean up and leave */
    		if (chan) {
    			ast_channel_unlock(chan);
    			chan = ast_channel_unref(chan);
    		}
    		sip_pvt_unlock(pvt);
    		dialog_unref(pvt, "dialog ref for provisional keepalive");
    		return 0;
    	}
    
    	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;
    
    	if (chan) {
    		ast_channel_unlock(chan);
    		chan = ast_channel_unref(chan);
    	}
    
    	if (!res) {
    		pvt->provisional_keepalive_sched_id = -1;
    	}
    
    	sip_pvt_unlock(pvt);
    
    	if (!res) {
    		dialog_unref(pvt, "dialog ref for provisional keepalive");
    	}
    	return res;
    
    static int send_provisional_keepalive(const void *data)
    {
    
    	struct sip_pvt *pvt = (struct sip_pvt *) data;
    
    	return send_provisional_keepalive_full(pvt, 0);
    
    static int send_provisional_keepalive_with_sdp(const void *data)
    {
    	struct sip_pvt *pvt = (void *) data;
    
    	return send_provisional_keepalive_full(pvt, 1);
    }
    
    static void update_provisional_keepalive(struct sip_pvt *pvt, int with_sdp)
    {
    	AST_SCHED_DEL_UNREF(sched, pvt->provisional_keepalive_sched_id, dialog_unref(pvt, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
    
    	pvt->provisional_keepalive_sched_id = ast_sched_add(sched, PROVIS_KEEPALIVE_TIMEOUT,
    		with_sdp ? send_provisional_keepalive_with_sdp : send_provisional_keepalive, dialog_ref(pvt, "Increment refcount to pass dialog pointer to sched callback"));
    
    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) {
    		AST_SCHED_DEL_UNREF(sched, p->provisional_keepalive_sched_id, dialog_unref(p, "when you delete the provisional_keepalive_sched_id, you should dec the refcount for the stored dialog ptr"));
    
    	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);
    
    	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++) {
    		if (*s == '"' && last_char != '\\')
    			break;
    	}
    	return s;
    }
    
    /*! \brief Send message with Access-URL header, if this is an HTML URL only! */
    static int sip_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
    {
    
    	struct sip_pvt *p = ast_channel_tech_pvt(chan);
    
    	if (subclass != AST_HTML_URL)
    
    	ast_string_field_build(p, url, "<%s>;mode=active", data);
    
    	if (sip_debug_test_pvt(p))
    
    		ast_debug(1, "Send URL %s, state = %u!\n", data, ast_channel_state(chan));
    
    	switch (ast_channel_state(chan)) {
    
    	case AST_STATE_RING:
    		transmit_response(p, "100 Trying", &p->initreq);
    		break;
    	case AST_STATE_RINGING:
    		transmit_response(p, "180 Ringing", &p->initreq);
    		break;
    	case AST_STATE_UP:
    		if (!p->pendinginvite) {		/* We are up, and have no outstanding invite */
    			transmit_reinvite_with_sdp(p, FALSE, FALSE);
    		} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
    
    			ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
    		}
    
    		ast_log(LOG_WARNING, "Don't know how to send URI when state is %u!\n", ast_channel_state(chan));
    
    /*! \brief Deliver SIP call ID for the call */
    static const char *sip_get_callid(struct ast_channel *chan)
    {
    
    	return ast_channel_tech_pvt(chan) ? ((struct sip_pvt *) ast_channel_tech_pvt(chan))->callid : "";
    
    /*!
     * \internal
     * \brief Send SIP MESSAGE text within a call
     * \note Called from PBX core sendtext() application
     */
    
    static int sip_sendtext(struct ast_channel *ast, const char *text)
    {
    
    	struct sip_pvt *dialog = ast_channel_tech_pvt(ast);
    
    		return -1;
    
    	/* NOT ast_strlen_zero, because a zero-length message is specifically
    	 * allowed by RFC 3428 (See section 10, Examples) */
    
    	if(!is_method_allowed(&dialog->allowed_methods, SIP_MESSAGE)) {
    		ast_debug(2, "Trying to send MESSAGE to device that does not support it.\n");
    
    
    	debug = sip_debug_test_pvt(dialog);
    	if (debug) {
    
    		ast_verbose("Sending text %s on %s\n", text, ast_channel_name(ast));
    
    	/* Setup to send text message */
    	sip_pvt_lock(dialog);
    	destroy_msg_headers(dialog);
    	ast_string_field_set(dialog, msg_body, text);
    	transmit_message(dialog, 0, 0);
    	sip_pvt_unlock(dialog);
    
    /*! \brief Update peer object in realtime storage
    	If the Asterisk system name is set in asterisk.conf, we will use
    	that name and store that in the "regserver" field in the sippeers
    	table to facilitate multi-server setups.
    */
    
    static void realtime_update_peer(const char *peername, struct ast_sockaddr *addr, const char *defaultuser, const char *fullcontact, const char *useragent, int expirey, unsigned short deprecated_username, int lastms, const char *path)
    
    	char ipaddr[INET6_ADDRSTRLEN];
    
    	char regseconds[20];
    	char *tablename = NULL;
    	char str_lastms[20];
    
    	const char *sysname = ast_config_AST_SYSTEM_NAME;
    	char *syslabel = NULL;
    
    	time_t nowtime = time(NULL) + expirey;
    	const char *fc = fullcontact ? "fullcontact" : NULL;
    
    	int realtimeregs = ast_check_realtime("sipregs");
    
    	tablename = realtimeregs ? "sipregs" : "sippeers";
    
    	snprintf(str_lastms, sizeof(str_lastms), "%d", lastms);
    	snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime);	/* Expiration time */
    
    	ast_copy_string(ipaddr, ast_sockaddr_isnull(addr) ? "" : ast_sockaddr_stringify_addr(addr), sizeof(ipaddr));
    	ast_copy_string(port, ast_sockaddr_port(addr) ? ast_sockaddr_stringify_port(addr) : "", sizeof(port));
    
    	if (ast_strlen_zero(sysname)) {	/* No system name, disable this */
    
    		sysname = NULL;
    
    	} else if (sip_cfg.rtsave_sysname) {
    
    		syslabel = "regserver";
    
    	/* XXX IMPORTANT: Anytime you add a new parameter to be updated, you
             *  must also add it to contrib/scripts/asterisk.ldap-schema,
             *  contrib/scripts/asterisk.ldif,
             *  and to configs/res_ldap.conf.sample as described in
    
             *  bugs 15156 and 15895
    
    
    	/* This is ugly, we need something better ;-) */
    	if (sip_cfg.rtsave_path) {
    		if (fc) {
    			ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
    				"port", port, "regseconds", regseconds,
    				deprecated_username ? "username" : "defaultuser", defaultuser,
    				"useragent", useragent, "lastms", str_lastms,
    				"path", path,			/* Path data can be NULL */
    				fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */
    		} else {
    			ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
    				"port", port, "regseconds", regseconds,
    				"useragent", useragent, "lastms", str_lastms,
    				deprecated_username ? "username" : "defaultuser", defaultuser,
    				"path", path,			/* Path data can be NULL */
    				syslabel, sysname, SENTINEL); /* note syslabel _can_ be NULL */
    		}
    
    		if (fc) {
    			ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
    				"port", port, "regseconds", regseconds,
    				deprecated_username ? "username" : "defaultuser", defaultuser,
    				"useragent", useragent, "lastms", str_lastms,
    				fc, fullcontact, syslabel, sysname, SENTINEL); /* note fc and syslabel _can_ be NULL */
    		} else {
    			ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,