Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	/* if it's actually an IP address and not a name,
               there's no need for a managed lookup */
    
    	if (!inet_aton(proxy->name, &proxy->ip.sin_addr)) {
    
    		/* Ok, not an IP address, then let's check if it's a domain or host */
    		/* XXX Todo - if we have proxy port, don't do SRV */
    		if (ast_get_ip_or_srv(&proxy->ip, proxy->name, global_srvlookup ? "_sip._udp" : NULL) < 0) {
    			ast_log(LOG_WARNING, "Unable to locate host '%s'\n", proxy->name);
    			return FALSE;
    		}
    	}
    	proxy->last_dnsupdate = time(NULL);
    	return TRUE;
    }
    
    /*! \brief Allocate and initialize sip proxy */
    static struct sip_proxy *proxy_allocate(char *name, char *port, int force)
    {
    	struct sip_proxy *proxy;
    
    	if (!proxy)
    		return NULL;
    	proxy->force = force;
    	ast_copy_string(proxy->name, name, sizeof(proxy->name));
    
    	proxy->ip.sin_port = htons((!ast_strlen_zero(port) ? atoi(port) : STANDARD_SIP_PORT));
    
    	proxy_update(proxy);
    	return proxy;
    }
    
    /*! \brief Get default outbound proxy or global proxy */
    static struct sip_proxy *obproxy_get(struct sip_pvt *dialog, struct sip_peer *peer)
    {
    	if (peer && peer->outboundproxy) {
    
    		if (sipdebug)
    			ast_debug(1, "OBPROXY: Applying peer OBproxy to this call\n");
    
    		append_history(dialog, "OBproxy", "Using peer obproxy %s", peer->outboundproxy->name);
    		return peer->outboundproxy;
    	}
    	if (global_outboundproxy.name[0]) {
    
    		if (sipdebug)
    			ast_debug(1, "OBPROXY: Applying global OBproxy to this call\n");
    
    		append_history(dialog, "OBproxy", "Using global obproxy %s", global_outboundproxy.name);
    		return &global_outboundproxy;
    	}
    
    	if (sipdebug)
    		ast_debug(1, "OBPROXY: Not applying OBproxy to this call\n");
    
    /*! \brief returns true if 'name' (with optional trailing whitespace)
     * matches the sip method 'id'.
     * Strictly speaking, SIP methods are case SENSITIVE, but we do
     * a case-insensitive comparison to be more tolerant.
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
     * following Jon Postel's rule: Be gentle in what you accept, strict with what you send
    
     */
    static int method_match(enum sipmethod id, const char *name)
    {
    	int len = strlen(sip_methods[id].text);
    	int l_name = name ? strlen(name) : 0;
    	/* true if the string is long enough, and ends with whitespace, and matches */
    	return (l_name >= len && name[len] < 33 &&
    		!strncasecmp(sip_methods[id].text, name, len));
    }
    
    /*! \brief  find_sip_method: Find SIP method from header */
    static int find_sip_method(const char *msg)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (i = 1; i < (sizeof(sip_methods) / sizeof(sip_methods[0])) && !res; i++) {
    
    		if (method_match(i, msg))
    
    /*! \brief Parse supported header in incoming packet */
    
    static unsigned int parse_sip_options(struct sip_pvt *pvt, const char *supported)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	char *next, *sep;
    
    	int i, found;
    
    	if (ast_strlen_zero(supported) )
    
    	if (sipdebug)
    		ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", supported);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (next = temp; next; next = sep) {
    
    		found = FALSE;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if ( (sep = strchr(next, ',')) != NULL)
    			*sep++ = '\0';
    		next = ast_skip_blanks(next);
    
    		if (sipdebug)
    			ast_debug(3, "Found SIP option: -%s-\n", next);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		for (i=0; i < (sizeof(sip_options) / sizeof(sip_options[0])); i++) {
    
    			if (!strcasecmp(next, sip_options[i].text)) {
    				profile |= sip_options[i].id;
    
    				found = TRUE;
    
    				if (sipdebug)
    					ast_debug(3, "Matched SIP option: %s\n", next);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				break;
    
    				ast_debug(3, "Found private SIP option, not supported: %s\n", next);
    
    				ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next);
    
    	if (pvt)
    		pvt->sipoptions = profile;
    
    /*! \brief See if we pass debug IP filter */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static inline int sip_debug_test_addr(const struct sockaddr_in *addr) 
    
    James Golovich's avatar
    James Golovich committed
    {
    
    		return 0;
    	if (debugaddr.sin_addr.s_addr) {
    		if (((ntohs(debugaddr.sin_port) != 0)
    			&& (debugaddr.sin_port != addr->sin_port))
    			|| (debugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
    			return 0;
    	}
    	return 1;
    }
    
    
    /*! \brief The real destination address for a write */
    
    static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
    {
    
    	if (p->outboundproxy)
    		return &p->outboundproxy->ip;
    
    
    	return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? &p->recv : &p->sa;
    }
    
    
    static const char *sip_nat_mode(const struct sip_pvt *p)
    {
    	return ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE ? "NAT" : "no NAT";
    }
    
    
    /*! \brief Test PVT for debugging output */
    
    James Golovich's avatar
    James Golovich committed
    static inline int sip_debug_test_pvt(struct sip_pvt *p) 
    {
    
    		return 0;
    
    	return sip_debug_test_addr(sip_real_dst(p));
    
    /*! \brief Transmit SIP message */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int __sip_xmit(struct sip_pvt *p, char *data, int len)
    {
    	int res;
    
    	const struct sockaddr_in *dst = sip_real_dst(p);
    
    	res = sendto(sipsock, data, len, 0, (const struct sockaddr *)dst, sizeof(struct sockaddr_in));
    
    	if (res == -1) {
    		switch (errno) {
    			case EBADF: 		/* Bad file descriptor - seems like this is generated when the host exist, but doesn't accept the UDP packet */
    			case EHOSTUNREACH: 	/* Host can't be reached */
    
    			case ENETDOWN: 		/* Interface down */
    
    				res = XMIT_ERROR;	/* Don't bother with trying to transmit again */
    
    	if (res != len)
    
    		ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), res, strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*! \brief Build a Via header for a request */
    
    static void build_via(struct sip_pvt *p)
    
    	/* Work around buggy UNIDEN UIP200 firmware */
    
    	const char *rport = ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_RFC3581 ? ";rport" : "";
    
    
    	/* z9hG4bK is a magic cookie.  See RFC 3261 section 8.1.1.7 */
    
    	ast_string_field_build(p, via, "SIP/2.0/UDP %s:%d;branch=z9hG4bK%08x%s",
    
    			ast_inet_ntoa(p->ourip.sin_addr),
    			ntohs(p->ourip.sin_port), p->branch, rport);
    
    /*! \brief NAT fix - decide which IP address to use for Asterisk server?
    
     *
     * Using the localaddr structure built up with localnet statements in sip.conf
     * apply it to their address to see if we need to substitute our
     * externip or can get away with our internal bindaddr
    
     * 'us' is always overwritten.
    
    static void ast_sip_ouraddrfor(struct in_addr *them, struct sockaddr_in *us)
    {
    	struct sockaddr_in theirs;
    	/* Set want_remap to non-zero if we want to remap 'us' to an externally
    	 * reachable IP address and port. This is done if:
    	 * 1. we have a localaddr list (containing 'internal' addresses marked
    	 *    as 'deny', so ast_apply_ha() will return AST_SENSE_DENY on them,
    	 *    and AST_SENSE_ALLOW on 'external' ones);
    	 * 2. either stunaddr or externip is set, so we know what to use as the
    	 *    externally visible address;
    	 * 3. the remote address, 'them', is external;
    	 * 4. the address returned by ast_ouraddrfor() is 'internal' (AST_SENSE_DENY
    	 *    when passed to ast_apply_ha() so it does need to be remapped.
    	 *    This fourth condition is checked later.
    	 */
    
    	*us = internip;		/* starting guess for the internal address */
    	/* now ask the system what would it use to talk to 'them' */
    	ast_ouraddrfor(them, &us->sin_addr);
    
    	want_remap = localaddr &&
    		(externip.sin_addr.s_addr || stunaddr.sin_addr.s_addr) &&
    		ast_apply_ha(localaddr, &theirs) == AST_SENSE_ALLOW ;
    
    
    	if (want_remap &&
    	    (!global_matchexterniplocally || !ast_apply_ha(localaddr, us)) ) {
    		/* if we used externhost or stun, see if it is time to refresh the info */
    
    		if (externexpire && time(NULL) >= externexpire) {
    
    			if (stunaddr.sin_addr.s_addr) {
    				ast_stun_request(sipsock, &stunaddr, NULL, &externip);
    			} else {
    				if (ast_parse_arg(externhost, PARSE_INADDR, &externip))
    					ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
    			}
    
    			externexpire = time(NULL) + externrefresh;
    
    		if (externip.sin_addr.s_addr)
    			*us = externip;
    		else
    			ast_log(LOG_WARNING, "stun failed\n");
    
    		ast_debug(1, "Target address %s is not local, substituting externip\n", 
    			ast_inet_ntoa(*(struct in_addr *)&them->s_addr));
    
    	} else if (bindaddr.sin_addr.s_addr) {
    		/* no remapping, but we bind to a specific address, so use it. */
    		*us = bindaddr;
    	}
    
    /*! \brief Append to SIP dialog history with arg list  */
    
    static void append_history_va(struct sip_pvt *p, const char *fmt, va_list ap)
    
    	char buf[80], *c = buf; /* max history length */
    	struct sip_history *hist;
    	int l;
    
    	vsnprintf(buf, sizeof(buf), fmt, ap);
    	strsep(&c, "\r\n"); /* Trim up everything after \r or \n */
    	l = strlen(buf) + 1;
    
    	if (!(hist = ast_calloc(1, sizeof(*hist) + l)))
    
    	if (!p->history && !(p->history = ast_calloc(1, sizeof(*p->history)))) {
    
    	if (p->history_entries == MAX_HISTORY_ENTRIES) {
    		struct sip_history *oldest;
    		oldest = AST_LIST_REMOVE_HEAD(p->history, list);
    		p->history_entries--;
    		ast_free(oldest);
    	}
    
    	p->history_entries++;
    
    /*! \brief Append to SIP dialog history with arg list  */
    
    static void append_history_full(struct sip_pvt *p, const char *fmt, ...)
    
    Olle Johansson's avatar
    Olle Johansson committed
    	va_list ap;
    
    
    	if (!p->do_history && !recordhistory && !dumphistory)
    		return;
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    	va_start(ap, fmt);
    	append_history_va(p, fmt, ap);
    	va_end(ap);
    
    /*! \brief Retransmit SIP message if no answer (Called from scheduler) */
    
    static int retrans_pkt(const void *data)
    
    	struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL;
    
    	int reschedule = DEFAULT_RETRANS;
    
    	/* Lock channel PVT */
    
    	sip_pvt_lock(pkt->owner);
    
    	if (pkt->retrans < MAX_RETRANS) {
    		pkt->retrans++;
    
     		if (!pkt->timer_t1) {	/* Re-schedule using timer_a and timer_t1 */
    
    			if (sipdebug)
     				ast_debug(4, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method);
    
     			if (sipdebug)
     				ast_debug(4, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method);
    
     			if (!pkt->timer_a)
     				pkt->timer_a = 2 ;
     			else
     				pkt->timer_a = 2 * pkt->timer_a;
     
     			/* For non-invites, a maximum of 4 secs */
     			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)) {
    
    			const struct sockaddr_in *dst = sip_real_dst(pkt->owner);
    			ast_verbose("Retransmitting #%d (%s) to %s:%d:\n%s\n---\n",
    				pkt->retrans, sip_nat_mode(pkt->owner),
    
    				ntohs(dst->sin_port), pkt->data);
    
    		append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data);
    
    		xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
    
    		sip_pvt_unlock(pkt->owner);
    
    		if (xmitres == XMIT_ERROR)
    
    			ast_log(LOG_WARNING, "Network error on retransmit in dialog %s\n", pkt->owner->callid);
    		else 
    
    	if (pkt->owner && 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, "Maximum retries exceeded on transmission %s for seqno %d (%s %s)\n",
    				pkt->owner->callid, pkt->seqno,
    				pkt->is_fatal ? "Critical" : "Non-critical", pkt->is_resp ? "Response" : "Request");
    
    	} else if (pkt->method == SIP_OPTIONS && sipdebug) {
    			ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \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)");
    	} else 
    		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 */
    
    			sip_pvt_lock(pkt->owner);
    
    		if (pkt->owner->owner && !pkt->owner->owner->hangupcause) 
    
    			pkt->owner->owner->hangupcause = AST_CAUSE_NO_USER_RESPONSE;
    
    		if (pkt->owner->owner) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    			sip_alreadygone(pkt->owner);
    
    			ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet.\n", pkt->owner->callid);
    			ast_queue_hangup(pkt->owner->owner);
    
    			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) {
    
    				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. */
    		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.");
    
    	for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
    		if (cur == pkt) {
    			UNLINK(cur, pkt->owner->packets, prev);
    
    	/* error case */
    	ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
    	sip_pvt_unlock(pkt->owner);
    	return 0;
    
    /*! \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, int seqno, int resp, char *data, int len, int fatal, int sipmethod)
    
    {
    	struct sip_pkt *pkt;
    
    	int siptimer_a = DEFAULT_RETRANS;
    
    	if (!(pkt = ast_calloc(1, sizeof(*pkt) + len + 1)))
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* copy data, add a terminator and save length */
    
    	memcpy(pkt->data, data, len);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	pkt->data[len] = '\0';
    
    	pkt->packetlen = len;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* copy other parameters from the caller */
    	pkt->method = sipmethod;
    
    	pkt->seqno = seqno;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	pkt->is_resp = resp;
    	pkt->is_fatal = fatal;
    	pkt->owner = dialog_ref(p);
    
    	pkt->next = p->packets;
    	p->packets = pkt;
    
    	pkt->timer_t1 = p->timer_t1;	/* Set SIP timer T1 */
    	if (pkt->timer_t1)
    		siptimer_a = pkt->timer_t1 * 2;
    
    
    	/* Schedule retransmission */
    	pkt->retransid = ast_sched_replace_variable(pkt->retransid, sched, 
    		siptimer_a, retrans_pkt, pkt, 1);
    	if (sipdebug)
    		ast_debug(4, "*** SIP TIMER: Initializing retransmit timer on packet: Id  #%d\n", pkt->retransid);
    
    		/* Note this is a pending invite */
    		p->pendinginvite = seqno;
    	}
    
    
    	xmitres = __sip_xmit(pkt->owner, pkt->data, pkt->packetlen);	/* 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_sched_del(sched, pkt->retransid);	/* No more retransmission */
    
    /*! \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;
    
    	/* If this is a subscription, tell the phone that we got a timeout */
    	if (p->subscribed) {
    
    		transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 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) {
    		ast_debug(3, "Re-scheduled destruction of SIP call %s\n", p->callid ? p->callid : "<unknown>");
    		append_history(p, "ReliableXmit", "timeout");
    		return 10000;
    
    	if (p->subscribed == MWI_NOTIFICATION)
    		if (p->relatedpeer)
    
    			unref_peer(p->relatedpeer);	/* Remove link to peer. If it's realtime, make sure it's gone from memory) */
    
    	/* Reset schedule ID */
    	p->autokillid = -1;
    
    
    	if (p->owner) {
    
    		ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
    
    		dialog_unref(p);
    
    	} else if (p->refer) {
    
    		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);
    
    		dialog_unref(p);
    
    	} else {
    		append_history(p, "AutoDestroy", "%s", p->callid);
    
    		ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
    
    		sip_destroy(p);		/* Go ahead and destroy dialog. All attempts to recover is done */
    
    		/* sip_destroy also absorbs the reference */
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Schedule destruction of SIP dialog */
    
    static void sip_scheddestroy(struct sip_pvt *p, int ms)
    
    		if (p->timer_t1 == 0) {
    			p->timer_t1 = global_t1;	/* Set timer T1 if not set (RFC 3261) */
    			p->timer_b = global_timer_b;  /* Set timer B if not set (RFC 3261) */
    		}
    
    		ms = p->timer_t1 * 64;
    	}
    
    		ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text);
    
    	sip_cancel_destroy(p);
    
    	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(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.
     */
    
    static void sip_cancel_destroy(struct sip_pvt *p)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	if (p->autokillid > -1) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_sched_del(sched, p->autokillid);
    
    		append_history(p, "CancelDestroy", "");
    		p->autokillid = -1;
    
    		dialog_unref(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    
    /*! \brief Acknowledges receipt of a packet and stops retransmission */
    
    static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
    
    	struct sip_pkt *cur, *prev = NULL;
    
    	const char *msg = "Not Found";	/* used only for debugging */
    
    
    	/* 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)
    		p->outboundproxy = 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) {
    
    			if (!resp && (seqno == p->pendinginvite)) {
    
    				ast_debug(1, "Acked pending invite %d\n", p->pendinginvite);
    
    			if (cur->retransid > -1) {
    
    				if (sipdebug)
    					ast_debug(4, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
    
    				ast_sched_del(sched, cur->retransid);
    
    				cur->retransid = -1;
    
    			UNLINK(cur, p->packets, prev);
    			dialog_unref(cur->owner);
    			ast_free(cur);
    
    	ast_debug(1, "Stopping retransmission on '%s' of %s %d: Match %s\n",
    		p->callid, resp ? "Response" : "Request", seqno, msg);
    
    /*! \brief Pretend to ack all packets
     * maybe the lock on p is not strictly necessary but there might be a race */
    
    static 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(cur->data);
    		__sip_ack(p, cur->seqno, cur->is_resp, method);
    	}
    
    /*! \brief Acks receipt of packet, keep it around (used for provisional responses) */
    
    static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
    
    	struct sip_pkt *cur;
    
    	int res = -1;
    
    	for (cur = p->packets; cur; cur = cur->next) {
    
    		if (cur->seqno == seqno && cur->is_resp == resp &&
    			(cur->is_resp || method_match(sipmethod, 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);
    
    				cur->retransid = -1;
    
    	ast_debug(1, "(Provisional) Stopping retransmission (but retaining packet) on '%s' %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found");
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    /*! \brief Copy SIP request, parse it */
    
    static void parse_copy(struct sip_request *dst, const struct sip_request *src)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	memset(dst, 0, sizeof(*dst));
    	memcpy(dst->data, src->data, sizeof(dst->data));
    	dst->len = src->len;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \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_copy_string(req->data + req->len, "\r\n", sizeof(req->data) - req->len);
    
    		req->len += strlen(req->data + req->len);
    	}
    }
    
    
    /*! \brief Transmit response on SIP request*/
    
    static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res;
    
    		const struct sockaddr_in *dst = sip_real_dst(p);
    
    
    		ast_verbose("\n<--- %sTransmitting (%s) to %s:%d --->\n%s\n<------------>\n",
    
    			reliable ? "Reliably " : "", sip_nat_mode(p),
    
    			ntohs(dst->sin_port), req->data);
    
    		append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"), 
    
    			(tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? tmp.rlPart2 : sip_methods[tmp.method].text);
    
    		 __sip_reliable_xmit(p, seqno, 1, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (res > 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*! \brief Send SIP Request to the other part of the dialogue */
    
    static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable, int seqno)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res;
    
    	/* If we have an outbound proxy, reset peer address 
    		Only do this once.
    	*/
    	if (p->outboundproxy) {
    		p->sa = p->outboundproxy->ip;
    	}
    
    
    		if (ast_test_flag(&p->flags[0], SIP_NAT_ROUTE))
    
    			ast_verbose("%sTransmitting (NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port), req->data);
    
    			ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), req->data);
    
    		append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
    
    		__sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable == XMIT_CRITICAL), req->method) :
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*! \brief Locate closing quote in a string, skipping escaped quotes.
     * optionally with a limit on the search.
     * start must be past the first quote.
     */
    static 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 Pick out text in brackets from character string
    	\return pointer to terminated stripped string
    
    	\param tmp input string that will be modified
    	Examples:
    
    	"foo" <bar>	valid input, returns bar
    	foo		returns the whole string
    	< "foo ... >	returns the string between brackets
    	< "foo...	bogus (missing closing bracket), returns the whole string
    			XXX maybe should still skip the opening bracket
    
    static char *get_in_brackets(char *tmp)
    
    
    	/*
    	 * Skip any quoted text until we find the part in brackets.
    
    	* On any error give up and return the full string.
    	*/
    	while ( (first_bracket = strchr(parse, '<')) ) {
    		char *first_quote = strchr(parse, '"');
    
    
    		if (!first_quote || first_quote > first_bracket)
    			break; /* no need to look at quoted part */
    		/* the bracket is within quotes, so ignore it */
    		parse = find_closing_quote(first_quote + 1, NULL);
    		if (!*parse) { /* not found, return full string ? */
    			/* XXX or be robust and return in-bracket part ? */
    			ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
    			break;
    
    		parse++;
    	}
    	if (first_bracket) {
    		char *second_bracket = strchr(first_bracket + 1, '>');
    		if (second_bracket) {
    			*second_bracket = '\0';
    			tmp = first_bracket + 1;
    		} else {
    			ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
    
    /*! \brief * parses a URI in its components.
     *
     * \note 
    
     * - If scheme is specified, drop it from the top.
    
     * - If a component is not requested, do not split around it.
    
     * This means that if we don't have domain, we cannot split
     * name:pass and domain:port.
     * It is safe to call with ret_name, pass, domain, port
     * pointing all to the same place.
    
     * Init pointers to empty string so we never get NULL dereferencing.
    
     * Overwrites the string.
     * return 0 on success, other values on error.
    
     * general form we are expecting is sip[s]:username[:password][;parameter]@host[:port][;...] 
    
     */
    static int parse_uri(char *uri, char *scheme,
    	char **ret_name, char **pass, char **domain, char **port, char **options)
    {
    	char *name = NULL;
    	int error = 0;
    
    	/* init field as required */
    
    	if (scheme) {
    		int l = strlen(scheme);
    
    		if (!strncasecmp(uri, scheme, l))
    
    			ast_log(LOG_NOTICE, "Missing scheme '%s' in '%s'\n", scheme, uri);
    
    	if (!domain) {
    		/* if we don't want to split around domain, keep everything as a name,
    		 * so we need to do nothing here, except remember why.
    		 */
    	} else {
    
    		/* store the result in a temp. variable to avoid it being
    		 * overwritten if arguments point to the same place.
    		 */
    
    			/* domain-only URI, according to the SIP RFC. */
    
    
    		/* Remove options in domain and name */
    		dom = strsep(&dom, ";");
    		name = strsep(&name, ";");
    
    
    		if (port && (c = strchr(dom, ':'))) { /* Remove :port */
    			*c++ = '\0';
    			*port = c;
    		}
    
    		if (pass && (c = strchr(name, ':'))) {	/* user:password */
    
    			*c++ = '\0';
    			*pass = c;
    		}
    		*domain = dom;
    	}
    	if (ret_name)	/* same as for domain, store the result only at the end */
    		*ret_name = name;
    	if (options)
    
    		*options = uri ? uri : "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*! \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 = chan->tech_pvt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (subclass != AST_HTML_URL)
    		return -1;
    
    	ast_string_field_build(p, url, "<%s>;mode=active", data);
    
    	if (sip_debug_test_pvt(p))
    		ast_debug(1, "Send URL %s, state = %d!\n", data, chan->_state);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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);
    		} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
    			ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);	
    		}	
    		break;
    	default:
    		ast_log(LOG_WARNING, "Don't know how to send URI when state is %d!\n", chan->_state);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /*! \brief Send SIP MESSAGE text within a call
    	Called from PBX core sendtext() application */
    
    static int sip_sendtext(struct ast_channel *ast, const char *text)
    
    	int debug = sip_debug_test_pvt(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_verbose("Sending text %s on %s\n", text, ast->name);
    	if (!p)
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    	if (debug)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_verbose("Really sending text %s on %s\n", text, ast->name);
    	transmit_message_with_text(p, text);
    	return 0;	
    
    /*! \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 sockaddr_in *sin, const char *defaultuser, const char *fullcontact, int expirey)
    
    	char regseconds[20];
    
    	const char *sysname = ast_config_AST_SYSTEM_NAME;
    
    	time_t nowtime = time(NULL) + expirey;
    
    	const char *fc = fullcontact ? "fullcontact" : NULL;
    
    
    	int realtimeregs = ast_check_realtime("sipregs");
    
    	tablename = realtimeregs ? "sipregs" : "sippeers";
    
    	
    	snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime);	/* Expiration time */
    
    	ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
    
    	snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
    
    	if (ast_strlen_zero(sysname))	/* No system name, disable this */
    		sysname = NULL;
    
    	else if (sip_cfg.rtsave_sysname)
    
    		ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
    
    			"port", port, "regseconds", regseconds,
    
    			"defaultuser", defaultuser, fc, fullcontact, syslabel, sysname, NULL); /* note fc and syslabel _can_ be NULL */
    
    		ast_update_realtime(tablename, "name", peername, "ipaddr", ipaddr,
    
    			"port", port, "regseconds", regseconds,
    
    			"defaultuser", defaultuser, syslabel, sysname, NULL); /* note syslabel _can_ be NULL */
    
    /*! \brief Automatically add peer extension to dial plan */
    
    static void register_peer_exten(struct sip_peer *peer, int onoff)
    
    
    	/* XXX note that global_regcontext is both a global 'enable' flag and
    	 * the name of the global regexten context, if not specified
    	 * individually.
    	 */
    	if (ast_strlen_zero(global_regcontext))
    		return;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
    	stringp = multi;
    	while ((ext = strsep(&stringp, "&"))) {
    		if ((context = strchr(ext, '@'))) {
    			*context++ = '\0';	/* split ext@context */
    			if (!ast_context_find(context)) {
    				ast_log(LOG_WARNING, "Context %s must exist in regcontext= in sip.conf!\n", context);
    				continue;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		} else {
    			context = global_regcontext;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (onoff)
    			ast_add_extension(context, 1, ext, 1, NULL, NULL, "Noop",
    
    				 ast_strdup(peer->name), ast_free_ptr, "SIP");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		else
    			ast_context_remove_extension(context, ext, 1, NULL);
    	}
    
    /*! Destroy mailbox subscriptions */
    
    static void destroy_mailbox(struct sip_mailbox *mailbox)
    {
    	if (mailbox->mailbox)
    		ast_free(mailbox->mailbox);
    	if (mailbox->context)
    		ast_free(mailbox->context);
    	if (mailbox->event_sub)
    		ast_event_unsubscribe(mailbox->event_sub);
    	ast_free(mailbox);
    }
    
    
    /*! Destroy all peer-related mailbox subscriptions */
    
    static void clear_peer_mailboxes(struct sip_peer *peer)
    {
    	struct sip_mailbox *mailbox;
    
    	while ((mailbox = AST_LIST_REMOVE_HEAD(&peer->mailboxes, entry)))
    		destroy_mailbox(mailbox);
    }
    
    
    /*! \brief Destroy peer object from memory */
    
    static void sip_destroy_peer(struct sip_peer *peer)
    
    	ast_debug(3, "Destroying SIP peer %s\n", peer->name);