Skip to content
Snippets Groups Projects
chan_sip.c 455 KiB
Newer Older
  • Learn to ignore specific revisions
  • static struct sip_peer *temp_peer(const char *name);
    
    static int sip_send_mwi_to_peer(struct sip_peer *peer);
    static int sip_scheddestroy(struct sip_pvt *p, int ms);
    
    /*----- RTP interface functions */
    static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active);
    static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan);
    static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan);
    static int sip_get_codec(struct ast_channel *chan);
    
    
    /*! \brief Definition of this channel for PBX channel registration */
    
    static const struct ast_channel_tech sip_tech = {
    
    	.description = "Session Initiation Protocol (SIP)",
    	.capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
    
    	.devicestate = sip_devicestate,
    	.call = sip_call,
    	.hangup = sip_hangup,
    	.answer = sip_answer,
    	.read = sip_read,
    	.write = sip_write,
    	.write_video = sip_write,
    	.indicate = sip_indicate,
    	.transfer = sip_transfer,
    	.fixup = sip_fixup,
    	.send_digit = sip_senddigit,
    	.bridge = ast_rtp_bridge,
    	.send_text = sip_sendtext,
    };
    
    
    /*! \brief Interface structure with callbacks used to connect to RTP module */
    static struct ast_rtp_protocol sip_rtp = {
    
    	get_rtp_info: sip_get_rtp_peer,
    	get_vrtp_info: sip_get_vrtp_peer,
    	set_rtp_peer: sip_set_rtp_peer,
    	get_codec: sip_get_codec,
    };
    
    
    
    /*! \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.
    
     * 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;
    
    	char *temp = ast_strdupa(supported);
    	unsigned int profile = 0;
    
    	int i, found;
    
    	if (!pvt || ast_strlen_zero(supported) )
    
    		ast_log(LOG_DEBUG, "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);
    
    			ast_log(LOG_DEBUG, "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;
    
    					ast_log(LOG_DEBUG, "Matched SIP option: %s\n", next);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				break;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (!found && option_debug > 2 && sipdebug)
    			ast_log(LOG_DEBUG, "Found no match for SIP option: %s (Please file bug report!)\n", next);
    
    
    	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;
    }
    
    
    /* The real destination address for a write */
    static const struct sockaddr_in *sip_real_dst(const struct sip_pvt *p)
    {
    	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;
    
    	char iabuf[INET_ADDRSTRLEN];
    
    	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 != len)
    		ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), 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)
    
    {
    	char iabuf[INET_ADDRSTRLEN];
    
    	/* 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",
    
    Olle Johansson's avatar
    Olle Johansson committed
    			 ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ourport, p->branch, rport);
    
    /*! \brief NAT fix - decide which IP address to use for ASterisk server?
     * Only used for outbound registrations */
    
    static int ast_sip_ouraddrfor(struct in_addr *them, struct in_addr *us)
    {
    
    	 * Using the localaddr structure built up with localnet statements
    	 * apply it to their address to see if we need to substitute our
    	 * externip or can get away with our internal bindaddr
    	 */
    	struct sockaddr_in theirs;
    	theirs.sin_addr = *them;
    
    	if (localaddr && externip.sin_addr.s_addr &&
    	   ast_apply_ha(localaddr, &theirs)) {
    
    		if (externexpire && time(NULL) >= externexpire) {
    
    			struct ast_hostent ahp;
    			struct hostent *hp;
    
    			time(&externexpire);
    			externexpire += externrefresh;
    			if ((hp = ast_gethostbyname(externhost, &ahp))) {
    				memcpy(&externip.sin_addr, hp->h_addr, sizeof(externip.sin_addr));
    			} else
    				ast_log(LOG_NOTICE, "Warning: Re-lookup of '%s' failed!\n", externhost);
    		}
    
    		if (option_debug) {
    			char iabuf[INET_ADDRSTRLEN];
    			ast_inet_ntoa(iabuf, sizeof(iabuf), *(struct in_addr *)&them->s_addr);
    		
    			ast_log(LOG_DEBUG, "Target address %s is not local, substituting externip\n", iabuf);
    		}
    	} else if (bindaddr.sin_addr.s_addr)
    
    	else
    		return ast_ouraddrfor(them, us);
    
    /*! \brief Append to SIP dialog history 
    
    	\return Always returns 0 */
    #define append_history(p, event, fmt , args... )	append_history_full(p, "%-15s " fmt, event, ## args)
    
    static int append_history_full(struct sip_pvt *p, const char *fmt, ...)
    	__attribute__ ((format (printf, 2, 3)));
    
    
    /*! \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)))) {
    
    	memcpy(hist->event, buf, l);
    	AST_LIST_INSERT_TAIL(p->history, hist, list);
    }
    
    
    /*! \brief Append to SIP dialog history with arg list  */
    
    static int append_history_full(struct sip_pvt *p, const char *fmt, ...)
    {
    
    Olle Johansson's avatar
    Olle Johansson committed
    	va_list ap;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	va_start(ap, fmt);
    	append_history_va(p, fmt, ap);
    	va_end(ap);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	return 0;
    
    /*! \brief Retransmit SIP message if no answer */
    
    static int retrans_pkt(void *data)
    {
    
    	struct sip_pkt *pkt=data, *prev, *cur = NULL;
    
    	char iabuf[INET_ADDRSTRLEN];
    
    	int reschedule = DEFAULT_RETRANS;
    
    	/* Lock channel */
    
    	if (pkt->retrans < MAX_RETRANS) {
    		pkt->retrans++;
    
     		if (!pkt->timer_t1) {	/* Re-schedule using timer_a and timer_t1 */
    
     				ast_log(LOG_DEBUG, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method);
    		} else {
     			int siptimer_a;
    
    
     				ast_log(LOG_DEBUG, "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;
     			if (option_debug > 3)
     				ast_log(LOG_DEBUG, "** 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 (pkt->owner && sip_debug_test_pvt(pkt->owner)) {
    
    			if (ast_test_flag(&pkt->owner->flags[0], SIP_NAT_ROUTE))
    
    				ast_verbose("Retransmitting #%d (NAT) to %s:%d:\n%s\n---\n", pkt->retrans, ast_inet_ntoa(iabuf, sizeof(iabuf), pkt->owner->recv.sin_addr), ntohs(pkt->owner->recv.sin_port), pkt->data);
    
    				ast_verbose("Retransmitting #%d (no NAT) to %s:%d:\n%s\n---\n", pkt->retrans, ast_inet_ntoa(iabuf, sizeof(iabuf), pkt->owner->sa.sin_addr), ntohs(pkt->owner->sa.sin_port), pkt->data);
    
    		append_history(pkt->owner, "ReTx", "%d %s", reschedule, pkt->data);
    
    		__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);
    
    		ast_mutex_unlock(&pkt->owner->lock);
    		return  reschedule;
    	} 
    	/* Too many retries */
    	if (pkt->owner && pkt->method != SIP_OPTIONS) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (ast_test_flag(pkt, FLAG_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, (ast_test_flag(pkt, FLAG_FATAL)) ? "Critical" : "Non-critical", (ast_test_flag(pkt, FLAG_RESPONSE)) ? "Response" : "Request");
    	} else {
    
    		if ((pkt->method == SIP_OPTIONS) && sipdebug)
    
    			ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \n", pkt->owner->callid);
    	}
    
    	append_history(pkt->owner, "MaxRetries", "%s", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)");
    
     		
    	pkt->retransid = -1;
    
    	if (ast_test_flag(pkt, FLAG_FATAL)) {
    		while(pkt->owner->owner && ast_mutex_trylock(&pkt->owner->owner->lock)) {
    			ast_mutex_unlock(&pkt->owner->lock);
    			usleep(1);
    			ast_mutex_lock(&pkt->owner->lock);
    
    		if (pkt->owner->owner) {
    
    			ast_set_flag(&pkt->owner->flags[0], SIP_ALREADYGONE);
    
    			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_mutex_unlock(&pkt->owner->owner->lock);
    		} else {
    			/* If no channel owner, destroy now */
    
    			ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY);	
    
    	/* In any case, go ahead and remove the packet */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (prev = NULL, cur = pkt->owner->packets; cur; prev = cur, cur = cur->next) {
    
    		if (cur == pkt)
    			break;
    	}
    	if (cur) {
    		if (prev)
    			prev->next = cur->next;
    		else
    			pkt->owner->packets = cur->next;
    		ast_mutex_unlock(&pkt->owner->lock);
    		free(cur);
    		pkt = NULL;
    	} else
    		ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n");
    
    		ast_mutex_unlock(&pkt->owner->lock);
    
    /*! \brief Transmit packet with retransmits 
    	\return 0 on success, -1 on failure to allocate packet 
    */
    
    static int __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)))
    
    		return -1;
    	memcpy(pkt->data, data, len);
    
    	pkt->packetlen = len;
    	pkt->next = p->packets;
    	pkt->owner = p;
    	pkt->seqno = seqno;
    
    	pkt->data[len] = '\0';
    
    	pkt->timer_t1 = p->timer_t1;	/* Set SIP timer T1 */
    
    		ast_set_flag(pkt, FLAG_FATAL);
    
    	if (pkt->timer_t1)
    		siptimer_a = pkt->timer_t1 * 2;
    
    
    	/* Schedule retransmission */
    
    	pkt->retransid = ast_sched_add_variable(sched, siptimer_a, retrans_pkt, pkt, 1);
    
    		ast_log(LOG_DEBUG, "*** SIP TIMER: Initalizing retransmit timer on packet: Id  #%d\n", pkt->retransid);
    
    	pkt->next = p->packets;
    	p->packets = pkt;
    
    
    	__sip_xmit(pkt->owner, pkt->data, pkt->packetlen);	/* Send packet */
    	if (sipmethod == SIP_INVITE) {
    
    		/* Note this is a pending invite */
    		p->pendinginvite = seqno;
    	}
    
    /*! \brief Kill a SIP dialog (called by scheduler) */
    
    static int __sip_autodestruct(void *data)
    {
    	struct sip_pvt *p = data;
    
    	/* If this is a subscription, tell the phone that we got a timeout */
    	if (p->subscribed) {
    		p->subscribed = TIMEOUT;
    
    		transmit_state_notify(p, AST_EXTENSION_DEACTIVATED, 1);	/* Send last notification */
    
    		p->subscribed = NONE;
    		append_history(p, "Subscribestatus", "timeout");
    
    		if (option_debug > 2)
    			ast_log(LOG_DEBUG, "Re-scheduled destruction of SIP subsription %s\n", p->callid ? p->callid : "<unknown>");
    
    		return 10000;	/* Reschedule this destruction so that we know that it's gone */
    	}
    
    
    	/* Reset schedule ID */
    	p->autokillid = -1;
    
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Auto destroying call '%s'\n", p->callid);
    
    	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);
    
    	} else {
    		sip_destroy(p);
    	}
    	return 0;
    }
    
    
    /*! \brief Schedule destruction of SIP call */
    
    static int sip_scheddestroy(struct sip_pvt *p, int ms)
    {
    
    		ast_verbose("Scheduling destruction of SIP dialog '%s' in %d ms (Method: %s)\n", p->callid, ms, sip_methods[p->method].text);
    
    	if (recordhistory)
    		append_history(p, "SchedDestroy", "%d ms", ms);
    
    	if (p->autokillid > -1)
    		ast_sched_del(sched, p->autokillid);
    	p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, p);
    	return 0;
    }
    
    
    /*! \brief Cancel destruction of SIP dialog */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_cancel_destroy(struct sip_pvt *p)
    {
    	if (p->autokillid > -1)
    		ast_sched_del(sched, p->autokillid);
    
    	append_history(p, "CancelDestroy", "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->autokillid = -1;
    	return 0;
    }
    
    
    /*! \brief Acknowledges receipt of a packet and stops retransmission */
    
    static int __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod, int reset)
    
    {
    	struct sip_pkt *cur, *prev = NULL;
    	int res = -1;
    
    	ast_mutex_lock(&p->lock);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (cur = p->packets; cur; prev = cur, cur = cur->next) {
    
    		if ((cur->seqno == seqno) && ((ast_test_flag(cur, FLAG_RESPONSE)) == resp) &&
    			((ast_test_flag(cur, FLAG_RESPONSE)) || 
    
    			 (!strncasecmp(msg, cur->data, strlen(msg)) && (cur->data[strlen(msg)] < 33)))) {
    
    			if (!resp && (seqno == p->pendinginvite)) {
    				ast_log(LOG_DEBUG, "Acked pending invite %d\n", p->pendinginvite);
    				p->pendinginvite = 0;
    			}
    
    			/* this is our baby */
    			if (prev)
    				prev->next = cur->next;
    			else
    				p->packets = cur->next;
    
    			if (cur->retransid > -1) {
    
    					ast_log(LOG_DEBUG, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid);
    
    				ast_sched_del(sched, cur->retransid);
    
    			if (!reset)
    				free(cur);
    
    	ast_mutex_unlock(&p->lock);
    
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Stopping retransmission on '%s' of %s %d: Match %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found");
    
    /*! \brief Pretend to ack all packets */
    
    static int __sip_pretend_ack(struct sip_pvt *p)
    {
    
    	struct sip_pkt *cur = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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 -1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		cur = p->packets;
    
    			__sip_ack(p, p->packets->seqno, (ast_test_flag(p->packets, FLAG_RESPONSE)), cur->method, FALSE);
    
    		else {	/* Unknown packet type */
    			char *c;
    
    			ast_copy_string(method, p->packets->data, sizeof(method));
    			c = ast_skip_blanks(method); /* XXX what ? */
    			*c = '\0';
    
    			__sip_ack(p, p->packets->seqno, (ast_test_flag(p->packets, FLAG_RESPONSE)), find_sip_method(method), FALSE);
    
    /*! \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)
    
    	int res = -1;
    
    	for (cur = p->packets; cur; cur = cur->next) {
    		if (cur->seqno == seqno && ast_test_flag(cur, FLAG_RESPONSE) == resp &&
    			(ast_test_flag(cur, FLAG_RESPONSE) || method_match(sipmethod, cur->data))) {
    
    			/* this is our baby */
    
    			if (cur->retransid > -1) {
    
    					ast_log(LOG_DEBUG, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, sip_methods[sipmethod].text);
    
    				ast_sched_del(sched, cur->retransid);
    
    			cur->retransid = -1;
    			res = 0;
    			break;
    		}
    	}
    
    	if (option_debug)
    		ast_log(LOG_DEBUG, "(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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void parse_copy(struct sip_request *dst, struct sip_request *src)
    {
    	memset(dst, 0, sizeof(*dst));
    	memcpy(dst->data, src->data, sizeof(dst->data));
    	dst->len = src->len;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*! \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;
    
    		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(iabuf, sizeof(iabuf), 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(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), req->data);
    
    	if (recordhistory) {
    		struct sip_request tmp;
    		parse_copy(&tmp, req);
    
    		append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"), 
    			tmp.method == SIP_RESPONSE ? 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 (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(iabuf, sizeof(iabuf), 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(iabuf, sizeof(iabuf), p->sa.sin_addr), ntohs(p->sa.sin_port), req->data);
    
    	if (recordhistory) {
    		struct sip_request tmp;
    		parse_copy(&tmp, req);
    
    		append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
    
    	res = (reliable) ?
    		__sip_reliable_xmit(p, seqno, 0, req->data, req->len, (reliable > 1), req->method) :
    		__sip_xmit(p, req->data, req->len);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*! \brief Pick out text in brackets from character string
    	\return pointer to terminated stripped string
    	\param tmp input string that will be modified */
    
    static char *get_in_brackets(char *tmp)
    
    	char *parse;
    	char *first_quote;
    	char *first_bracket;
    	char *second_bracket;
    	char last_char;
    
    	parse = tmp;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (;;) {
    
    		first_quote = strchr(parse, '"');
    		first_bracket = strchr(parse, '<');
    		if (first_quote && first_bracket && (first_quote < first_bracket)) {
    			last_char = '\0';
    			for (parse = first_quote + 1; *parse; parse++) {
    				if ((*parse == '"') && (last_char != '\\'))
    					break;
    				last_char = *parse;
    			}
    			if (!*parse) {
    				ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
    				return tmp;
    			}
    			parse++;
    			continue;
    
    		if (first_bracket) {
    			second_bracket = strchr(first_bracket + 1, '>');
    			if (second_bracket) {
    				*second_bracket = '\0';
    				return first_bracket + 1;
    			} else {
    				ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
    				return tmp;
    			}
    		}
    		return tmp;
    
    /*! \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 */
    
    static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, const char *username, const char *fullcontact, int expirey)
    
    	char port[10];
    	char ipaddr[20];
    
    	char regseconds[20];
    	time_t nowtime;
    
    	const char *fc = fullcontact ? "fullcontact" : NULL;
    
    	
    	time(&nowtime);
    	nowtime += expirey;
    	snprintf(regseconds, sizeof(regseconds), "%d", (int)nowtime);	/* Expiration time */
    	ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin->sin_addr);
    	snprintf(port, sizeof(port), "%d", ntohs(sin->sin_port));
    
    	ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr,
    		"port", port, "regseconds", regseconds,
    		"username", username, fc, fullcontact, NULL); /* note fc _can_ be NULL */
    
    /*! \brief Automatically add peer extension to dial plan */
    
    static void register_peer_exten(struct sip_peer *peer, int onoff)
    
    	char *stringp, *ext;
    
    	if (!ast_strlen_zero(global_regcontext)) {
    
    
    		ast_copy_string(multi, S_OR(peer->regexten, peer->name), sizeof(multi));
    
    		stringp = multi;
    		while((ext = strsep(&stringp, "&"))) {
    			if (onoff)
    
    				ast_add_extension(global_regcontext, 1, ext, 1, NULL, NULL, "Noop",
    
    						  ast_strdup(peer->name), free, "SIP");
    
    				ast_context_remove_extension(global_regcontext, ext, 1, NULL);
    
    /*! \brief Destroy peer object from memory */
    
    static void sip_destroy_peer(struct sip_peer *peer)
    
    	if (option_debug > 2)
    		ast_log(LOG_DEBUG, "Destroying SIP peer %s\n", peer->name);
    
    
    	/* Delete it, it needs to disappear */
    	if (peer->call)
    		sip_destroy(peer->call);
    
    
    	if (peer->mwipvt) {	/* We have an active subscription, delete it */
    		sip_destroy(peer->mwipvt);
    	}
    
    
    	if (peer->chanvars) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_variables_destroy(peer->chanvars);
    		peer->chanvars = NULL;
    	}
    
    	if (peer->expire > -1)
    		ast_sched_del(sched, peer->expire);
    	if (peer->pokeexpire > -1)
    		ast_sched_del(sched, peer->pokeexpire);
    
    	ast_free_ha(peer->ha);
    
    	if (ast_test_flag(&peer->flags[1], SIP_PAGE2_SELFDESTRUCT))
    
    	else if (ast_test_flag(&peer->flags[0], SIP_REALTIME))
    
    	clear_realm_authentication(peer->auth);
    
    	if (peer->dnsmgr)
    		ast_dnsmgr_release(peer->dnsmgr);
    
    	free(peer);
    
    /*! \brief Update peer data in database (if used) */
    
    static void update_peer(struct sip_peer *p, int expiry)
    
    	int rtcachefriends = ast_test_flag(&p->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
    	if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTUPDATE) &&
    	    (ast_test_flag(&p->flags[0], SIP_REALTIME) || rtcachefriends)) {
    
    		realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, expiry);
    
    /*! \brief  realtime_peer: Get peer from realtime storage
    
     * Checks the "sippeers" realtime family from extconfig.conf 
     * \todo Consider adding check of port address when matching here to follow the same
     * 	algorithm as for static peers. Will we break anything by adding that?
    */
    
    static struct sip_peer *realtime_peer(const char *peername, struct sockaddr_in *sin)
    {
    
    	struct sip_peer *peer = NULL;
    
    	struct ast_variable *var;
    	struct ast_variable *tmp;
    
    	char *newpeername = (char *) peername;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* First check on peer name */
    
    		var = ast_load_realtime("sippeers", "name", peername, NULL);
    
    	else if (sin) {	/* Then check on IP address for dynamic peers */
    
    		ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr);
    
    		var = ast_load_realtime("sippeers", "host", iabuf, NULL);	/* First check for fixed IP hosts */
    		if (!var)
    			var = ast_load_realtime("sippeers", "ipaddr", iabuf, NULL);	/* Then check for registred hosts */
    	
    
    	} else
    		return NULL;
    
    	if (!var)
    		return NULL;
    
    
    	for (tmp = var; tmp; tmp = tmp->next) {
    		/* If this is type=user, then skip this object. */
    
    		if (!strcasecmp(tmp->name, "type") &&
    		    !strcasecmp(tmp->value, "user")) {
    
    			ast_variables_destroy(var);
    
    			return NULL;
    
    		} else if (!newpeername && !strcasecmp(tmp->name, "name")) {
    
    	if (!newpeername) {	/* Did not find peer in realtime */
    		ast_log(LOG_WARNING, "Cannot Determine peer name ip=%s\n", iabuf);
    		ast_variables_destroy(var);
    
    	/* Peer found in realtime, now build it in memory */
    
    	peer = build_peer(newpeername, var, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
    
    	if (!peer) {
    		ast_variables_destroy(var);
    
    	if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
    
    		/* Cache peer */
    
    		ast_copy_flags(&peer->flags[1],&global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
    		if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
    
    			if (peer->expire > -1) {
    				ast_sched_del(sched, peer->expire);
    
    			peer->expire = ast_sched_add(sched, (global_rtautoclear) * 1000, expire_register, (void *)peer);
    
    		ASTOBJ_CONTAINER_LINK(&peerl,peer);
    	} else {
    
    		ast_set_flag(&peer->flags[0], SIP_REALTIME);
    
    	ast_variables_destroy(var);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return peer;
    
    /*! \brief Support routine for find_peer */
    
    static int sip_addrcmp(char *name, struct sockaddr_in *sin)
    {
    	/* We know name is the first field, so we can cast */
    
    	struct sip_peer *p = (struct sip_peer *) name;
    
    	return 	!(!inaddrcmp(&p->addr, sin) || 
    
    					(ast_test_flag(&p->flags[0], SIP_INSECURE_PORT) &&
    
    					(p->addr.sin_addr.s_addr == sin->sin_addr.s_addr)));
    }
    
    
    /*! \brief Locate peer by name or ip address 
    
     *	This is used on incoming SIP message to find matching peer on ip
    
    	or outgoing message to find matching peer on name */
    
    static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime)
    
    {
    	struct sip_peer *p = NULL;
    
    
    		p = ASTOBJ_CONTAINER_FIND(&peerl, peer);
    
    		p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp);
    
    	if (!p && realtime) {
    
    		p = realtime_peer(peer, sin);
    
    /*! \brief Remove user object from in-memory storage */
    
    static void sip_destroy_user(struct sip_user *user)
    
    	if (option_debug > 2)
    		ast_log(LOG_DEBUG, "Destroying user object from memory: %s\n", user->name);
    
    	ast_free_ha(user->ha);
    
    	if (user->chanvars) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_variables_destroy(user->chanvars);
    		user->chanvars = NULL;
    
    	if (ast_test_flag(&user->flags[0], SIP_REALTIME))
    
    /*! \brief Load user from realtime storage
    
     * Loads user from "sipusers" category in realtime (extconfig.conf)
     * Users are matched on From: user name (the domain in skipped) */
    
    static struct sip_user *realtime_user(const char *username)
    {
    	struct ast_variable *var;
    	struct ast_variable *tmp;
    
    	struct sip_user *user = NULL;
    
    
    	var = ast_load_realtime("sipusers", "name", username, NULL);
    
    	for (tmp = var; tmp; tmp = tmp->next) {
    
    		if (!strcasecmp(tmp->name, "type") &&
    
    			!strcasecmp(tmp->value, "peer")) {
    
    			ast_variables_destroy(var);
    
    			return NULL;
    
    	user = build_user(username, var, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
    
    	if (!user) {	/* No user found */
    		ast_variables_destroy(var);
    		return NULL;
    	}
    
    
    	if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
    		ast_set_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
    
    		suserobjs++;
    
    		ASTOBJ_CONTAINER_LINK(&userl,user);
    	} else {
    
    		/* Move counter from s to r... */
    		suserobjs--;
    		ruserobjs++;
    
    		ast_set_flag(&user->flags[0], SIP_REALTIME);
    
    	ast_variables_destroy(var);
    
    /*! \brief Locate user by name 
    
     * Locates user by name (From: sip uri user name part) first
     * from in-memory list (static configuration) then from 
     * realtime storage (defined in extconfig.conf) */
    
    static struct sip_user *find_user(const char *name, int realtime)
    
    	struct sip_user *u = ASTOBJ_CONTAINER_FIND(&userl, name);
    	if (!u && realtime)
    
    		u = realtime_user(name);
    
    /*! \brief Create address structure from peer reference */
    
    static int create_addr_from_peer(struct sip_pvt *r, struct sip_peer *peer)
    {
    
    	if ((peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr) &&
    	    (!peer->maxms || ((peer->lastms >= 0)  && (peer->lastms <= peer->maxms)))) {
    
    		r->sa = (peer->addr.sin_addr.s_addr) ? peer->addr : peer->defaddr;
    		r->recv = r->sa;
    
    	ast_copy_flags(&r->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
    	ast_copy_flags(&r->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    	r->capability = peer->capability;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (!ast_test_flag(&r->flags[1], SIP_PAGE2_VIDEOSUPPORT) && r->vrtp) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		ast_rtp_destroy(r->vrtp);
    		r->vrtp = NULL;
    	}
    
    	natflags = ast_test_flag(&r->flags[0], SIP_NAT) & SIP_NAT_ROUTE;
    
    			ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", natflags);
    		ast_rtp_setnat(r->rtp, natflags);
    
    			ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", natflags);
    		ast_rtp_setnat(r->vrtp, natflags);
    
    	ast_string_field_set(r, peername, peer->username);
    	ast_string_field_set(r, authname, peer->username);
    	ast_string_field_set(r, username, peer->username);
    	ast_string_field_set(r, peersecret, peer->secret);
    	ast_string_field_set(r, peermd5secret, peer->md5secret);
    	ast_string_field_set(r, tohost, peer->tohost);
    	ast_string_field_set(r, fullcontact, peer->fullcontact);
    
    	if (!r->initreq.headers && !ast_strlen_zero(peer->fromdomain)) {
    
    		char *tmpcall;
    		char *c;
    		tmpcall = ast_strdupa(r->callid);
    
    		if (tmpcall) {
    			c = strchr(tmpcall, '@');
    			if (c) {
    				*c = '\0';
    				ast_string_field_build(r, callid, "%s@%s", tmpcall, peer->fromdomain);
    			}
    
    		ast_inet_ntoa(iabuf, sizeof(iabuf),  peer->addr.sin_addr.s_addr ? peer->addr.sin_addr : peer->defaddr.sin_addr);
    
    
    		ast_string_field_set(r, tohost, iabuf);
    
    	}
    	if (!ast_strlen_zero(peer->fromdomain))
    
    		ast_string_field_set(r, fromdomain, peer->fromdomain);
    
    	if (!ast_strlen_zero(peer->fromuser))
    
    		ast_string_field_set(r, fromuser, peer->fromuser);
    
    	r->maxtime = peer->maxms;
    	r->callgroup = peer->callgroup;
    	r->pickupgroup = peer->pickupgroup;
    
    	/* Set timer T1 to RTT for this peer (if known by qualify=) */
    
    	/* Minimum is settable or default to 100 ms */
    
    	if (peer->maxms && peer->lastms)
    
    		r->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
    
    	if ((ast_test_flag(&r->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
    	    (ast_test_flag(&r->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
    
    		r->noncodeccapability |= AST_RTP_DTMF;
    	else
    		r->noncodeccapability &= ~AST_RTP_DTMF;
    
    	ast_string_field_set(r, context, peer->context);
    
    	r->rtptimeout = peer->rtptimeout;
    	r->rtpholdtimeout = peer->rtpholdtimeout;
    	r->rtpkeepalive = peer->rtpkeepalive;
    
    		ast_set_flag(&r->flags[0], SIP_CALL_LIMIT);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	r->maxcallbitrate = peer->maxcallbitrate;
    	
    
    /*! \brief create address structure from peer name
    
     *      Or, if peer not found, find it in the global DNS 
     *      returns TRUE (-1) on failure, FALSE on success */
    
    static int create_addr(struct sip_pvt *dialog, const char *opeer)