Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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
     * 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;
    
    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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		cur = p->packets;
    
    		method = (cur->method) ? cur->method : find_sip_method(cur->data);
    		__sip_ack(p, cur->seqno, ast_test_flag(cur, FLAG_RESPONSE), 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 */
    
    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
    }
    
    /* 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 */
    		snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n");
    		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("%sTransmitting (%s) to %s:%d:\n%s\n---\n",
    			reliable ? "Reliably " : "", sip_nat_mode(p),
    
    			ntohs(dst->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.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 (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);
    
    	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 
    	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 *username, const char *fullcontact, int expirey)
    
    	char regseconds[20];
    
    
    	char *sysname = ast_config_AST_SYSTEM_NAME;
    	char *syslabel = NULL;
    
    
    	time_t nowtime = time(NULL) + expirey;
    
    	const char *fc = fullcontact ? "fullcontact" : NULL;
    
    	
    	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 (ast_test_flag(&global_flags[1], SIP_PAGE2_RTSAVE_SYSNAME))
    
    	if (fc)
    		ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr,
    			"port", port, "regseconds", regseconds,
    
    			"username", username, fc, fullcontact, syslabel, sysname, NULL); /* note fc and syslabel _can_ be NULL */
    
    	else
    		ast_update_realtime("sippeers", "name", peername, "ipaddr", ipaddr,
    			"port", port, "regseconds", regseconds,
    
    			"username", username, 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, "SIP");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		else
    			ast_context_remove_extension(context, 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 */
    
    	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 *newpeername, struct sockaddr_in *sin)
    
    	struct sip_peer *peer;
    	struct ast_variable *var = NULL;
    
    	struct ast_variable *tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* First check on peer name */
    
    		var = ast_load_realtime("sippeers", "name", newpeername, NULL);
    
    	else if (sin) {	/* Then check on IP address for dynamic peers */
    
    		ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
    		var = ast_load_realtime("sippeers", "host", ipaddr, NULL);	/* First check for fixed IP hosts */
    
    			var = ast_load_realtime("sippeers", "ipaddr", ipaddr, NULL);	/* Then check for registred hosts */
    
    	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", ipaddr);
    
    		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.
     *  return -1 on error, 0 on success.
     */
    
    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;
    	}
    
    	if (ast_test_flag(&r->flags[1], SIP_PAGE2_T38SUPPORT)) {
    		r->t38.capability = global_t38_capability;
    		if (r->udptl) {
    			if (ast_udptl_get_error_correction_scheme(r->udptl) == UDPTL_ERROR_CORRECTION_FEC )
    				r->t38.capability |= T38FAX_UDP_EC_FEC;
    			else if (ast_udptl_get_error_correction_scheme(r->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY )
    				r->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
    			else if (ast_udptl_get_error_correction_scheme(r->udptl) == UDPTL_ERROR_CORRECTION_NONE )
    				r->t38.capability |= T38FAX_UDP_EC_NONE;
    			r->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
    			if (option_debug > 1)
    				ast_log(LOG_DEBUG,"Our T38 capability (%d)\n", r->t38.capability);
    		}
    		r->t38.jointcapability = r->t38.capability;
    	} else if (r->udptl) {
    		ast_udptl_destroy(r->udptl);
    		r->udptl = NULL;
    	}
    
    	natflags = ast_test_flag(&r->flags[0], SIP_NAT) & SIP_NAT_ROUTE;
    
    			ast_log(LOG_DEBUG, "Setting NAT on RTP to %s\n", natflags ? "On" : "Off");
    
    		ast_rtp_setnat(r->rtp, natflags);
    
    		ast_rtp_setdtmf(r->rtp, ast_test_flag(&r->flags[0], SIP_DTMF) != SIP_DTMF_INFO);
    
    			ast_log(LOG_DEBUG, "Setting NAT on VRTP to %s\n", natflags ? "On" : "Off");
    
    		ast_rtp_setnat(r->vrtp, natflags);
    
    	if (r->udptl) {
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %s\n", natflags ? "On" : "Off");
    		ast_udptl_setnat(r->udptl, 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);
    
    		c = strchr(tmpcall, '@');
    		if (c) {
    			*c = '\0';
    			ast_string_field_build(r, callid, "%s@%s", tmpcall, peer->fromdomain);
    
    	if (ast_strlen_zero(r->tohost))
    		ast_string_field_set(r, tohost, ast_inet_ntoa(r->sa.sin_addr));
    
    	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;
    
    	r->allowtransfer = peer->allowtransfer;
    
    	/* 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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct hostent *hp;
    
    	struct ast_hostent ahp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_peer *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *port;
    
    	char host[MAXHOSTNAMELEN], *hostn;
    
    	ast_copy_string(peer, opeer, sizeof(peer));
    
    	dialog->sa.sin_family = AF_INET;
    	dialog->timer_t1 = 500; /* Default SIP retransmission timer T1 (RFC 3261) */
    
    	p = find_peer(peer, NULL, 1);
    
    		int res = create_addr_from_peer(dialog, p);
    		ASTOBJ_UNREF(p, sip_destroy_peer);
    		return res;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    	hostn = peer;
    	portno = port ? atoi(port) : DEFAULT_SIP_PORT;
    	if (srvlookup) {
    		char service[MAXHOSTNAMELEN];
    		int tportno;
    		int ret;
    
    		snprintf(service, sizeof(service), "_sip._udp.%s", peer);
    		ret = ast_get_srv(NULL, host, sizeof(host), &tportno, service);
    		if (ret > 0) {
    			hostn = host;
    			portno = tportno;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	hp = ast_gethostbyname(hostn, &ahp);
    	if (!hp) {
    		ast_log(LOG_WARNING, "No such host: %s\n", peer);
    		return -1;
    	}
    	ast_string_field_set(dialog, tohost, peer);
    	memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr));
    	dialog->sa.sin_port = htons(portno);
    	dialog->recv = dialog->sa;
    	return 0;
    
    /*! \brief Scheduled congestion on a call */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int auto_congest(void *nothing)
    {
    	struct sip_pvt *p = nothing;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->initid = -1;
    	if (p->owner) {
    
    		/* XXX fails on possible deadlock */
    
    		if (!ast_channel_trylock(p->owner)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
    
    			append_history(p, "Cong", "Auto-congesting (timer)");
    
    			ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
    
    			ast_channel_unlock(p->owner);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /*! \brief Initiate SIP call from PBX 
    
     *      used from the dial() application      */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_call(struct ast_channel *ast, char *dest, int timeout)
    {
    	int res;
    	struct sip_pvt *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct varshead *headp;
    	struct ast_var_t *current;
    
    	const char *referer = NULL;   /* SIP refererer */	
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
    		return -1;
    	}
    
    	/* Check whether there is vxml_url, distinctive ring variables */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	headp=&ast->varshead;
    	AST_LIST_TRAVERSE(headp,current,entries) {
    
    		/* Check whether there is a VXML_URL variable */
    
    		if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) {
    			p->options->vxml_url = ast_var_value(current);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) {
    			p->options->uri_options = ast_var_value(current);
    
    		} else if (!p->options->distinctive_ring && !strcasecmp(ast_var_name(current), "ALERT_INFO")) {
    
    			/* Check whether there is a ALERT_INFO variable */
    
    			p->options->distinctive_ring = ast_var_value(current);
    		} else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
    
    			/* Check whether there is a variable with a name starting with SIPADDHEADER */
    
    		} else if (!strcasecmp(ast_var_name(current),"SIPTRANSFER")) {
    			/* This is a transfered call */
    			p->options->transfer = 1;
    		} else if (!strcasecmp(ast_var_name(current),"SIPTRANSFER_REFERER")) {
    			/* This is the referer */
    			referer = ast_var_value(current);
    		} else if (!strcasecmp(ast_var_name(current),"SIPTRANSFER_REPLACES")) {
    			/* We're replacing a call. */
    			p->options->replaces = ast_var_value(current);
    
    		} else if (!strcasecmp(ast_var_name(current),"T38CALL")) {
    			p->t38.state = T38_LOCAL_DIRECT;
    			if (option_debug)
    				ast_log(LOG_DEBUG,"T38State change to %d on channel %s\n", p->t38.state, ast->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = 0;
    
    	ast_set_flag(&p->flags[0], SIP_OUTGOING);
    
    
    	if (p->options->transfer) {
    		char buf[BUFSIZ/2];
    
    		if (referer) {
    			if (sipdebug && option_debug > 2)
    				ast_log(LOG_DEBUG, "Call for %s transfered by %s\n", p->username, referer);
    			snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer);
    		} else {
    			snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name);
    		}
    		ast_string_field_set(p, cid_name, buf);
    	} 
    
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Outgoing Call for %s\n", p->username);
    
    	res = update_call_counter(p, INC_CALL_RINGING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ( res != -1 ) {
    
    		p->callingpres = ast->cid.cid_pres;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		p->jointcapability = p->capability;
    
    		p->t38.jointcapability = p->t38.capability;
    		if (option_debug)
    			ast_log(LOG_DEBUG,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability);
    
    		transmit_invite(p, SIP_INVITE, 1, 2);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (p->maxtime) {
    			/* Initialize auto-congest time */
    
    			p->initid = ast_sched_add(sched, p->maxtime * 4, auto_congest, p);
    
    		} else {
    			p->initid = ast_sched_add(sched, SIP_TRANS_TIMEOUT, auto_congest, p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*! \brief Destroy registry object
    	Objects created with the register= statement in static configuration */
    
    static void sip_registry_destroy(struct sip_registry *reg)
    {
    	/* Really delete */
    
    	if (option_debug > 2)
    		ast_log(LOG_DEBUG, "Destroying registry entry for %s@%s\n", reg->username, reg->hostname);
    
    
    	if (reg->call) {
    		/* Clear registry before destroying to ensure
    		   we don't get reentered trying to grab the registry lock */
    		reg->call->registry = NULL;
    
    		if (option_debug > 2)
    			ast_log(LOG_DEBUG, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
    
    		sip_destroy(reg->call);
    	}
    	if (reg->expire > -1)
    		ast_sched_del(sched, reg->expire);
    	if (reg->timeout > -1)
    		ast_sched_del(sched, reg->timeout);
    
    	ast_string_field_free_all(reg);
    
    /*! \brief Execute destruction of SIP dialog structure, release memory */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void __sip_destroy(struct sip_pvt *p, int lockowner)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_pvt *cur, *prev = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_pkt *cp;
    
    	if (sip_debug_test_pvt(p) || option_debug > 2)
    		ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
    
    	/* Remove link from peer to subscription of MWI */
    	if (p->relatedpeer && p->relatedpeer->mwipvt)
    
    		p->relatedpeer->mwipvt = NULL;
    
    	if (dumphistory)
    		sip_dump_history(p);
    
    
    	if (p->stateid > -1)
    		ast_extension_state_del(p->stateid, NULL);
    
    	if (p->initid > -1)
    		ast_sched_del(sched, p->initid);
    	if (p->autokillid > -1)
    		ast_sched_del(sched, p->autokillid);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (p->rtp)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_rtp_destroy(p->rtp);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (p->vrtp)
    
    	if (p->udptl)
    		ast_udptl_destroy(p->udptl);
    
    	if (p->refer)
    		free(p->refer);
    
    	if (p->route) {
    		free_old_route(p->route);
    		p->route = NULL;
    	}
    
    		if (p->registry->call == p)
    			p->registry->call = NULL;
    
    		ASTOBJ_UNREF(p->registry, sip_registry_destroy);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Unlink us from the owner if we have one */
    	if (p->owner) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (lockowner)
    
    			ast_channel_lock(p->owner);
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (lockowner)
    
    			ast_channel_unlock(p->owner);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		struct sip_history *hist;
    		while( (hist = AST_LIST_REMOVE_HEAD(p->history, list)) )
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (prev = NULL, cur = iflist; cur; prev = cur, cur = cur->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (cur == p) {
    
    			UNLINK(cur, iflist, prev);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    		}
    	}
    	if (!cur) {
    
    		ast_log(LOG_WARNING, "Trying to destroy \"%s\", not found in dialog list?!?! \n", p->callid);
    		return;
    	} 
    
    	while((cp = p->packets)) {
    		p->packets = p->packets->next;
    
    		if (cp->retransid > -1)
    
    			ast_sched_del(sched, cp->retransid);
    		free(cp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (p->chanvars) {
    		ast_variables_destroy(p->chanvars);
    		p->chanvars = NULL;
    	}
    
    	ast_mutex_destroy(&p->lock);
    
    /*! \brief  update_call_counter: Handle call_limit for SIP users 
    
     * Setting a call-limit will cause calls above the limit not to be accepted.
     *
     * Remember that for a type=friend, there's one limit for the user and
     * another for the peer, not a combined call limit.
     * This will cause unexpected behaviour in subscriptions, since a "friend"
     * is *two* devices in Asterisk, not one.
     *
     * Thought: For realtime, we should propably update storage with inuse counter... 
    
     *
     * \return 0 if call is ok (no call limit, below treshold)
     *	-1 on rejection of call
     *		
    
    static int update_call_counter(struct sip_pvt *fup, int event)
    
    	int *inuse, *call_limit, *inringing = NULL;
    
    	int outgoing = ast_test_flag(&fup->flags[0], SIP_OUTGOING);
    
    	struct sip_user *u = NULL;
    	struct sip_peer *p = NULL;
    
    	if (option_debug > 2)
    		ast_log(LOG_DEBUG, "Updating call counter for %s call\n", outgoing ? "outgoing" : "incoming");
    
    	/* Test if we need to check call limits, in order to avoid 
    	   realtime lookups if we do not need it */
    
    	if (!ast_test_flag(&fup->flags[0], SIP_CALL_LIMIT))
    
    	ast_copy_string(name, fup->username, sizeof(name));
    
    	if (!outgoing)	/* Only check users for incoming calls */
    		u = find_user(name, 1);
    
    
    		if (!p)
    			p = find_peer(fup->peername, NULL, 1);
    
    			inringing = &p->inRinging;
    
    			ast_copy_string(name, fup->peername, sizeof(name));
    
    			if (option_debug > 1)
    
    				ast_log(LOG_DEBUG, "%s is not a local device, no call limit\n", name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	switch(event) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* incoming and outgoing affects the inUse counter */
    
    				if (ast_test_flag(&fup->flags[0], SIP_INC_COUNT))
    
    Olle Johansson's avatar
    Olle Johansson committed
    					(*inuse)--;
    
    			} else {
    
    			if (inringing) {
    				if (ast_test_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING)) {
    					if (*inringing > 0)
    						(*inringing)--;
    					else
    						ast_log(LOG_WARNING, "Inringing for peer '%s' < 0?\n", fup->peername);
    					ast_clear_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING);
    				}
    			}
    
    				ast_log(LOG_DEBUG, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
    			}
    
    		case INC_CALL_RINGING:
    
    		case INC_CALL_LIMIT:
    			if (*call_limit > 0 ) {
    				if (*inuse >= *call_limit) {
    					ast_log(LOG_ERROR, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
    
    						ASTOBJ_UNREF(u, sip_destroy_user);
    
    						ASTOBJ_UNREF(p, sip_destroy_peer);
    
    			if (inringing && (event == INC_CALL_RINGING)) {
    				if (!ast_test_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING)) {
    					(*inringing)++;
    					ast_set_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING);
    				}
    			}
    			/* Continue */
    
    			ast_set_flag(&fup->flags[0], SIP_INC_COUNT);
    
    				ast_log(LOG_DEBUG, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *inuse, *call_limit);
    			}
    
    		case DEC_CALL_RINGING:
    			if (inringing) {
    				if (ast_test_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING)) {
    					if (*inringing > 0)
    						(*inringing)--;
    					else
    
    						ast_log(LOG_WARNING, "Inringing for peer '%s' < 0?\n", p->name);
    
    					ast_clear_flag(&fup->flags[1], SIP_PAGE2_INC_RINGING);
    				}
    			}
    			break;
    
    		default:
    
    			ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event);
    
    	if (p)
    		ast_device_state_changed("SIP/%s", p->name);
    
    		ASTOBJ_UNREF(u, sip_destroy_user);
    
    		ASTOBJ_UNREF(p, sip_destroy_peer);
    
    /*! \brief Destroy SIP call structure */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void sip_destroy(struct sip_pvt *p)
    {
    
    	if (option_debug > 2)
    		ast_log(LOG_DEBUG, "Destroying SIP dialog %s\n", p->callid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	__sip_destroy(p, 1);
    
    /*! \brief Convert SIP hangup causes to Asterisk hangup causes */
    
    	/* Possible values taken from causes.h */
    
    
    	switch(cause) {
    
    		case 401:	/* Unauthorized */
    			return AST_CAUSE_CALL_REJECTED;
    
    		case 403:	/* Not found */
    
    			return AST_CAUSE_CALL_REJECTED;
    
    		case 404:	/* Not found */
    
    			return AST_CAUSE_UNALLOCATED;
    
    		case 405:	/* Method not allowed */
    			return AST_CAUSE_INTERWORKING;
    		case 407:	/* Proxy authentication required */
    			return AST_CAUSE_CALL_REJECTED;
    
    		case 408:	/* No reaction */
    
    			return AST_CAUSE_NO_USER_RESPONSE;
    
    		case 409:	/* Conflict */
    			return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;