Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	if (peer->outboundproxy)
    
    	peer->outboundproxy = NULL;
    
    	/* Delete it, it needs to disappear */
    	if (peer->call)
    
    		peer->call = sip_destroy(peer->call);
    
    	if (peer->mwipvt) 	/* We have an active subscription, delete it */
    
    		peer->mwipvt = sip_destroy(peer->mwipvt);
    
    	if (peer->chanvars) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_variables_destroy(peer->chanvars);
    		peer->chanvars = NULL;
    	}
    
    	/* If the schedule delete fails, that means the schedule is currently
    	 * running, which means we should wait for that thread to complete.
    	 * Otherwise, there's a crashable race condition.
    	 *
    	 * NOTE: once peer is refcounted, this probably is no longer necessary.
    	 */
    	while (peer->expire > -1 && ast_sched_del(sched, peer->expire))
    		usleep(1);
    	while (peer->pokeexpire > -1 && ast_sched_del(sched, peer->pokeexpire))
    		usleep(1);
    
    
    	ast_free_ha(peer->ha);
    
    	if (peer->selfdestruct)
    
    	else if (peer->is_realtime) {
    
    		ast_debug(3,"-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
    
    	clear_realm_authentication(peer->auth);
    
    	if (peer->dnsmgr)
    		ast_dnsmgr_release(peer->dnsmgr);
    
    /*! \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 (sip_cfg.peer_rtupdate &&
    
    	    (p->is_realtime || rtcachefriends)) {
    
    		realtime_update_peer(p->name, &p->addr, p->username, rtcachefriends ? p->fullcontact : NULL, expiry);
    
    static struct ast_variable *get_insecure_variable_from_config(struct ast_config *config)
    {
    
    	struct ast_flags flags = {0};
    	char *cat = NULL;
    	const char *insecure;
    	while ((cat = ast_category_browse(config, cat))) {
    		insecure = ast_variable_retrieve(config, cat, "insecure");
    		set_insecure_flags(&flags, insecure, -1);
    		if (ast_test_flag(&flags, SIP_INSECURE_PORT)) {
    			var = ast_category_root(config, cat);
    			break;
    		}
    	}
    	return var;
    }
    
    static const char *get_name_from_variable(struct ast_variable *var, const char *newpeername)
    {
    	struct ast_variable *tmp;
    	for (tmp = var; tmp; tmp = tmp->next) {
    		if (!newpeername && !strcasecmp(tmp->name, "name"))
    			newpeername = tmp->value;
    	}
    	return newpeername;
    }
    
    /*! \brief  realtime_peer: Get peer from realtime storage
    
     * Checks the "sippeers" realtime family from extconfig.conf 
    
     * Checks the "sipregs" realtime family from extconfig.conf if it's configured.
    
    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 *varregs = NULL;
    
    	struct ast_variable *tmp;
    
    	struct ast_config *peerlist = NULL;
    
    	char portstring[6]; /*up to 5 digits plus null terminator*/
    	char *cat = NULL;
    	unsigned short portnum;
    
    	int realtimeregs = ast_check_realtime("sipregs");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* First check on peer name */
    
    	if (newpeername) {
    		if (realtimeregs)
    			varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
    
    
    		var = ast_load_realtime("sippeers", "name", newpeername, "host", "dynamic", NULL);
    
    			var = ast_load_realtime("sippeers", "name", newpeername, "host", ast_inet_ntoa(sin->sin_addr), NULL);
    
    		if (!var) {
    			var = ast_load_realtime("sippeers", "name", newpeername, NULL);
    			/*!\note
    			 * If this one loaded something, then we need to ensure that the host
    			 * field matched.  The only reason why we can't have this as a criteria
    			 * is because we only have the IP address and the host field might be
    			 * set as a name (and the reverse PTR might not match).
    			 */
    			if (var) {
    				for (tmp = var; tmp; tmp = tmp->next) {
    					if (!strcasecmp(var->name, "host")) {
    						struct in_addr sin2 = { 0, };
    						struct ast_dnsmgr_entry *dnsmgr = NULL;
    						if ((ast_dnsmgr_lookup(tmp->value, &sin2, &dnsmgr) < 0) || (memcmp(&sin2, &sin->sin_addr, sizeof(sin2)) != 0)) {
    							/* No match */
    							ast_variables_destroy(var);
    							var = NULL;
    
    	}
    
    	if (!var && sin) {	/* Then check on IP address for dynamic peers */
    
    		ast_copy_string(ipaddr, ast_inet_ntoa(sin->sin_addr), sizeof(ipaddr));
    
    		portnum = ntohs(sin->sin_port);
    		sprintf(portstring, "%u", portnum);
    		var = ast_load_realtime("sippeers", "host", ipaddr, "port", portstring, NULL);	/* First check for fixed IP hosts */
    
    				newpeername = get_name_from_variable(var, newpeername);
    
    				varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
    
    				varregs = ast_load_realtime("sipregs", "ipaddr", ipaddr, "port", portstring, NULL); /* Then check for registered hosts */
    
    				var = ast_load_realtime("sippeers", "ipaddr", ipaddr, "port", portstring, NULL); /* Then check for registered hosts */
    
    				newpeername = get_name_from_variable(varregs, newpeername);
    
    				var = ast_load_realtime("sippeers", "name", newpeername, NULL);
    			}
    		}
    
    		if (!var) { /*We couldn't match on ipaddress and port, so we need to check if port is insecure*/
    
    			peerlist = ast_load_realtime_multientry("sippeers", "host", ipaddr, NULL);
    			if (peerlist) {
    				var = get_insecure_variable_from_config(peerlist);
    				if(var) {
    					if (realtimeregs) {
    						newpeername = get_name_from_variable(var, newpeername);
    						varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
    					}
    				} else { /*var wasn't found in the list of "hosts", so try "ipaddr"*/
    					peerlist = NULL;
    					cat = NULL;
    					peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, NULL);
    					if(peerlist) {
    						var = get_insecure_variable_from_config(peerlist);
    						if(var) {
    							if (realtimeregs) {
    								newpeername = get_name_from_variable(var, newpeername);
    								varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
    							}
    						}
    					}
    				}
    			} else {
    
    				if (realtimeregs) {
    
    					peerlist = ast_load_realtime_multientry("sipregs", "ipaddr", ipaddr, NULL);
    					if (peerlist) {
    						varregs = get_insecure_variable_from_config(peerlist);
    						if (varregs) {
    							newpeername = get_name_from_variable(varregs, newpeername);
    							var = ast_load_realtime("sippeers", "name", newpeername, NULL);
    						}
    					}
    				} else {
    					peerlist = ast_load_realtime_multientry("sippeers", "ipaddr", ipaddr, NULL);
    					if (peerlist) {
    						var = get_insecure_variable_from_config(peerlist);
    						if (var) {
    							newpeername = get_name_from_variable(var, newpeername);
    							varregs = ast_load_realtime("sipregs", "name", newpeername, NULL);
    						}
    					}
    				}
    			}
    		}
    
    	if (!var) {
    		if (peerlist)
    			ast_config_destroy(peerlist);
    
    		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")) {
    
    			if(peerlist)
    				ast_config_destroy(peerlist);
    			else {
    				ast_variables_destroy(var);
    				ast_variables_destroy(varregs);
    			}
    
    			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);
    
    		if(peerlist)
    			ast_config_destroy(peerlist);
    		else
    			ast_variables_destroy(var);
    
    	/* Peer found in realtime, now build it in memory */
    
    	peer = build_peer(newpeername, var, varregs, !ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS));
    
    	if (!peer) {
    
    		if(peerlist)
    			ast_config_destroy(peerlist);
    		else {
    			ast_variables_destroy(var);
    			ast_variables_destroy(varregs);
    		}
    
    	ast_debug(3,"-REALTIME- loading peer from database to memory. Name: %s. Peer objects: %d\n", peer->name, rpeerobjs);
    
    	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)) {
    
    			peer->expire = ast_sched_replace(peer->expire, sched, 
    				global_rtautoclear * 1000, expire_register, (void *) peer);
    
    		ASTOBJ_CONTAINER_LINK(&peerl,peer);
    	} else {
    
    	if (peerlist)
    		ast_config_destroy(peerlist);
    	else {
    		ast_variables_destroy(var);
    		ast_variables_destroy(varregs);
    	}
    
    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 
    	\note Avoid using this function in new functions if there's a way to avoid it, i
    	since it causes a database lookup or a traversal of the in-memory peer list.
    */
    
    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)
    
    	ast_debug(3, "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;
    
    /*! \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_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 Set nat mode on the various data sockets */
    static void do_setnat(struct sip_pvt *p, int natflags)
    {
    	const char *mode = natflags ? "On" : "Off";
    
    	if (p->rtp) {
    
    		ast_debug(1, "Setting NAT on RTP to %s\n", mode);
    
    		ast_rtp_setnat(p->rtp, natflags);
    	}
    	if (p->vrtp) {
    
    		ast_debug(1, "Setting NAT on VRTP to %s\n", mode);
    
    		ast_rtp_setnat(p->vrtp, natflags);
    	}
    	if (p->udptl) {
    
    		ast_debug(1, "Setting NAT on UDPTL to %s\n", mode);
    
    		ast_udptl_setnat(p->udptl, natflags);
    	}
    
    		ast_debug(1, "Setting NAT on TRTP to %s\n", mode);
    
    		ast_rtp_setnat(p->trtp, natflags);
    	}
    
    Joshua Colp's avatar
    Joshua Colp committed
    /*! \brief Set the global T38 capabilities on a SIP dialog structure */
    
    static void set_t38_capabilities(struct sip_pvt *p)
    {
    	p->t38.capability = global_t38_capability;
    	if (p->udptl) {
    		if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_FEC )
    			p->t38.capability |= T38FAX_UDP_EC_FEC;
    		else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY )
    			p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
    		else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_NONE )
    			p->t38.capability |= T38FAX_UDP_EC_NONE;
    		p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
    	}
    }
    
    
    /*! \brief Create address structure from peer reference.
    
     *	This function copies data from peer to the dialog, so we don't have to look up the peer
     *	again from memory or database during the life time of the dialog.
     *
     * \return -1 on error, 0 on success.
    
    static int create_addr_from_peer(struct sip_pvt *dialog, 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)))) {
    
    		dialog->sa = (peer->addr.sin_addr.s_addr) ? peer->addr : peer->defaddr;
    		dialog->recv = dialog->sa;
    	} else 
    
    	ast_copy_flags(&dialog->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
    	ast_copy_flags(&dialog->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    	dialog->capability = peer->capability;
    
    	if ((!ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) || !(dialog->capability & AST_FORMAT_VIDEO_MASK)) && dialog->vrtp) {
    
    		ast_rtp_destroy(dialog->vrtp);
    		dialog->vrtp = NULL;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    	if (!ast_test_flag(&dialog->flags[1], SIP_PAGE2_TEXTSUPPORT) && dialog->trtp) {
    		ast_rtp_destroy(dialog->trtp);
    		dialog->trtp = NULL;
    	}
    
    	dialog->prefs = peer->prefs;
    	if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_T38SUPPORT)) {
    
    		ast_copy_flags(&dialog->t38.t38support, &peer->flags[1], SIP_PAGE2_T38SUPPORT);
    		set_t38_capabilities(dialog);
    
    		dialog->t38.jointcapability = dialog->t38.capability;
    	} else if (dialog->udptl) {
    		ast_udptl_destroy(dialog->udptl);
    		dialog->udptl = NULL;
    
    	do_setnat(dialog, ast_test_flag(&dialog->flags[0], SIP_NAT) & SIP_NAT_ROUTE);
    
    	if (dialog->rtp) { /* Audio */
    
    		ast_rtp_setdtmf(dialog->rtp, ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
    
    		ast_rtp_setdtmfcompensate(dialog->rtp, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
    
    		ast_rtp_set_rtptimeout(dialog->rtp, peer->rtptimeout);
    		ast_rtp_set_rtpholdtimeout(dialog->rtp, peer->rtpholdtimeout);
    		ast_rtp_set_rtpkeepalive(dialog->rtp, peer->rtpkeepalive);
    		/* Set Frame packetization */
    		ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs);
    		dialog->autoframing = peer->autoframing;
    
    	if (dialog->vrtp) { /* Video */
    
    		ast_rtp_setdtmf(dialog->vrtp, 0);
    		ast_rtp_setdtmfcompensate(dialog->vrtp, 0);
    
    		ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout);
    		ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout);
    		ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive);
    
    	if (dialog->trtp) { /* Realtime text */
    
    		ast_rtp_setdtmf(dialog->trtp, 0);
    		ast_rtp_setdtmfcompensate(dialog->trtp, 0);
    		ast_rtp_set_rtptimeout(dialog->trtp, peer->rtptimeout);
    		ast_rtp_set_rtpholdtimeout(dialog->trtp, peer->rtpholdtimeout);
    		ast_rtp_set_rtpkeepalive(dialog->trtp, peer->rtpkeepalive);
    	}
    
    	ast_string_field_set(dialog, peername, peer->name);
    
    	ast_string_field_set(dialog, authname, peer->username);
    	ast_string_field_set(dialog, username, peer->username);
    	ast_string_field_set(dialog, peersecret, peer->secret);
    	ast_string_field_set(dialog, peermd5secret, peer->md5secret);
    
    	ast_string_field_set(dialog, mohsuggest, peer->mohsuggest);
    	ast_string_field_set(dialog, mohinterpret, peer->mohinterpret);
    
    	ast_string_field_set(dialog, tohost, peer->tohost);
    	ast_string_field_set(dialog, fullcontact, peer->fullcontact);
    
    	ast_string_field_set(dialog, context, peer->context);
    
    	dialog->outboundproxy = obproxy_get(dialog, peer);
    
    	dialog->callgroup = peer->callgroup;
    	dialog->pickupgroup = peer->pickupgroup;
    	dialog->allowtransfer = peer->allowtransfer;
    	dialog->jointnoncodeccapability = dialog->noncodeccapability;
    	dialog->rtptimeout = peer->rtptimeout;
    	dialog->maxcallbitrate = peer->maxcallbitrate;
    
    	if (ast_strlen_zero(dialog->tohost))
    		ast_string_field_set(dialog, tohost, ast_inet_ntoa(dialog->sa.sin_addr));
    
    	if (!ast_strlen_zero(peer->fromdomain)) {
    
    		ast_string_field_set(dialog, fromdomain, peer->fromdomain);
    
    		if (!dialog->initreq.headers) {
    			char *c;
    			char *tmpcall = ast_strdupa(dialog->callid);
    
    			c = strchr(tmpcall, '@');
    			if (c) {
    				*c = '\0';
    				ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain);
    			}
    		}
    	}
    	if (!ast_strlen_zero(peer->fromuser)) 
    
    		ast_string_field_set(dialog, fromuser, peer->fromuser);
    
    	if (!ast_strlen_zero(peer->language))
    		ast_string_field_set(dialog, language, peer->language);
    
    	/* Set timer T1 to RTT for this peer (if known by qualify=) */
    
    	/* Minimum is settable or default to 100 ms */
    
    	/* If there is a maxms and lastms from a qualify use that over a manual T1
    	   value. Otherwise, use the peer's T1 value. */
    
    	if (peer->maxms && peer->lastms)
    
    		dialog->timer_t1 = peer->lastms < global_t1min ? global_t1min : peer->lastms;
    
    	else
    		dialog->timer_t1 = peer->timer_t1;
    
    	/* Set timer B to control transaction timeouts, the peer setting is the default and overrides
    	   the known timer */
    	if (peer->timer_b)
    		dialog->timer_b = peer->timer_b;
    	else
    		dialog->timer_b = 64 * dialog->timer_t1;
    
    	if ((ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
    	    (ast_test_flag(&dialog->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
    		dialog->noncodeccapability |= AST_RTP_DTMF;
    
    		ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	
    
    /*! \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;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	struct sip_peer *peer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *port;
    
    	char host[MAXHOSTNAMELEN], *hostn;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	char peername[256];
    
    Olle Johansson's avatar
    Olle Johansson committed
    	ast_copy_string(peername, opeer, sizeof(peername));
    	port = strchr(peername, ':');
    
    	dialog->sa.sin_family = AF_INET;
    
    	dialog->timer_t1 = global_t1; /* Default SIP retransmission timer T1 (RFC 3261) */
    	dialog->timer_b = global_timer_b; /* Default SIP transaction timer B (RFC 3261) */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	peer = find_peer(peername, NULL, 1);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (peer) {
    		int res = create_addr_from_peer(dialog, peer);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    	ast_string_field_set(dialog, tohost, peername);
    
    
    	/* Get the outbound proxy information */
    	dialog->outboundproxy = obproxy_get(dialog, NULL);
    
    	/* If we have an outbound proxy, don't bother with DNS resolution at all */
    	if (dialog->outboundproxy)
    		return 0;
    
    	/* Let's see if we can find the host in DNS. First try DNS SRV records,
       	   then hostname lookup */
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    	hostn = peername;
    
    	portno = port ? atoi(port) : STANDARD_SIP_PORT;
    
    Olle Johansson's avatar
    Olle Johansson committed
    		char service[MAXHOSTNAMELEN];
    		int tportno;
    		int ret;
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    		snprintf(service, sizeof(service), "_sip._udp.%s", peername);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		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) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		ast_log(LOG_WARNING, "No such host: %s\n", peername);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		return -1;
    	}
    	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.
     * Only called by the scheduler, must return the reference when done.
     */
    
    static int auto_congest(const void *arg)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct sip_pvt *p = (struct sip_pvt *)arg;
    
    	p->initid = -1;	/* event gone, will not be rescheduled */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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 = ast->tech_pvt;	/* chan is locked, so the reference cannot go away */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct varshead *headp;
    	struct ast_var_t *current;
    
    	const char *referer = NULL;   /* SIP referrer */	
    
    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->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 referrer */
    
    			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;
    
    			ast_debug(1,"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)
    				ast_debug(3, "Call for %s transfered by %s\n", p->username, referer);
    
    			snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer);
    
    			snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name);
    		ast_string_field_set(p, cid_name, buf);
    	} 
    
    	ast_debug(1, "Outgoing Call for %s\n", p->username);
    
    	res = update_call_counter(p, INC_CALL_RINGING);
    
    	if (res == -1)
    		return res;
    
    	p->callingpres = ast->cid.cid_pres;
    	p->jointcapability = ast_translate_available_formats(p->capability, p->prefcodec);
    
    	p->jointnoncodeccapability = p->noncodeccapability;
    
    
    	/* If there are no audio formats left to offer, punt */
    	if (!(p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
    		ast_log(LOG_WARNING, "No audio format found to offer. Cancelling call to %s\n", p->username);
    		res = -1;
    	} else {
    
    		p->t38.jointcapability = p->t38.capability;
    
    		ast_debug(2,"Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability);
    
    		if (xmitres == XMIT_ERROR)
    			return -1;
    		p->invitestate = INV_CALLING;
    	
    		/* Initialize auto-congest time */
    
    		p->initid = ast_sched_replace(p->initid, sched, p->timer_b, 
    
    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 */
    
    	ast_debug(3, "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;
    
    		ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
    
    		reg->call = 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_memory(reg);
    
    /*! \brief Execute destruction of SIP dialog structure, release memory */
    
    static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_pvt *cur, *prev = NULL;
    
    	struct sip_pkt *cp;
    
    		ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
    
    	if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
    
    		update_call_counter(p, DEC_CALL_LIMIT);
    
    		ast_debug(2, "This call did not properly clean up call limits. Call ID %s\n", p->callid);
    
    	/* Remove link from peer to subscription of MWI */
    
    	if (p->relatedpeer && p->relatedpeer->mwipvt) 
    
    		p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt);
    
    	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->waitid > -1)
    		ast_sched_del(sched, p->waitid);
    
    	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->trtp)
    		ast_rtp_destroy(p->trtp);
    
    	if (p->udptl)
    		ast_udptl_destroy(p->udptl);
    
    	if (p->route) {
    		free_old_route(p->route);
    		p->route = NULL;
    	}
    
    		if (p->registry->call == p)
    			p->registry->call = NULL;
    
    		p->registry = registry_unref(p->registry);
    
    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);
    
    		ast_debug(1, "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)) ) {
    
    			p->history_entries--;
    		}
    
    	/* Lock dialog list before removing ourselves from the list */
    
    		dialoglist_lock();
    
    	for (prev = NULL, cur = dialoglist; cur; prev = cur, cur = cur->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (cur == p) {
    
    		dialoglist_unlock();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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);
    		dialog_unref(cp->owner);
    		ast_free(cp);
    	}
    
    	if (p->chanvars) {
    		ast_variables_destroy(p->chanvars);
    		p->chanvars = NULL;
    	}
    
    	ast_mutex_destroy(&p->pvt_lock);
    
    	ast_string_field_free_memory(p);
    
    /*! \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 probably update storage with inuse counter... 
    
     * \return 0 if call is ok (no call limit, below threshold)
    
    static int update_call_counter(struct sip_pvt *fup, int event)
    
    	int *inuse = NULL, *call_limit = NULL, *inringing = NULL;
    
    	int outgoing = fup->outgoing_call;
    
    	struct sip_user *u = NULL;
    	struct sip_peer *p = NULL;
    
    	ast_debug(3, "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_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD))
    
    	ast_copy_string(name, fup->username, sizeof(name));
    
    	/* Check the list of users only for incoming calls */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (global_limitonpeers == FALSE && !outgoing && (u = find_user(name, 1)))  {
    
    		inuse = &u->inUse;
    		call_limit = &u->call_limit;
    		inringing = NULL;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	} else if ( (p = find_peer(ast_strlen_zero(fup->peername) ? name : fup->peername, NULL, 1) ) ) { /* Try to find peer */
    
    		inuse = &p->inUse;
    		call_limit = &p->call_limit;
    		inringing = &p->inRinging;
    		ast_copy_string(name, fup->peername, sizeof(name));
    
    		ast_debug(2, "%s is not a local device, no call limit\n", name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	switch(event) {
    
    	/* incoming and outgoing affects the inUse counter */
    	case DEC_CALL_LIMIT:
    
    		/* Decrement inuse count if applicable */
    
    		if (inuse && ast_test_flag(&fup->flags[0], SIP_INC_COUNT)) {
    
    			ast_atomic_fetchadd_int(inuse, -1);
    
    			ast_clear_flag(&fup->flags[0], SIP_INC_COUNT);
    		} else
    
    		/* Decrement ringing count if applicable */
    
    		if (inringing && ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
    
    			ast_atomic_fetchadd_int(inringing, -1);
    
    			ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
    
    		/* Decrement onhold count if applicable */
    
    		if (ast_test_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD) && global_notifyhold) {
    			ast_clear_flag(&fup->flags[1], SIP_PAGE2_CALL_ONHOLD);
    
    		if (sipdebug)
    			ast_debug(2, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
    
    		break;
    
    	case INC_CALL_RINGING:
    	case INC_CALL_LIMIT:
    
    		/* If call limit is active and we have reached the limit, reject the call */
    
    		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);
    				if (u)
    
    		}
    		if (inringing && (event == INC_CALL_RINGING)) {
    
    			if (!ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
    
    				ast_atomic_fetchadd_int(inringing, +1);
    
    				ast_set_flag(&fup->flags[0], SIP_INC_RINGING);
    
    		ast_atomic_fetchadd_int(inuse, +1);
    
    		ast_set_flag(&fup->flags[0], SIP_INC_COUNT);
    
    		if (sipdebug) {
    			ast_debug(2, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *inuse, *call_limit);
    
    		if (inringing && ast_test_flag(&fup->flags[0], SIP_INC_RINGING)) {
    
    			ast_atomic_fetchadd_int(inringing, -1);
    
    			ast_clear_flag(&fup->flags[0], SIP_INC_RINGING);
    
    		}
    		break;
    
    	default:
    		ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event);
    
    		ast_device_state_changed("SIP/%s", p->name);
    
    /*! \brief Destroy SIP call structure.
     * Make it return NULL so the caller can do things like
     *	foo = sip_destroy(foo);
     * and reduce the chance of bugs due to dangling pointers.
     */
    static struct sip_pvt * sip_destroy(struct sip_pvt *p)
    
    	ast_debug(3, "Destroying SIP dialog %s\n", p->callid);
    
    	__sip_destroy(p, TRUE, TRUE);
    
    /*! \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;
    		case 410:	/* Gone */
    			return AST_CAUSE_UNALLOCATED;
    		case 411:	/* Length required */
    			return AST_CAUSE_INTERWORKING;
    		case 413:	/* Request entity too large */
    			return AST_CAUSE_INTERWORKING;
    		case 414:	/* Request URI too large */
    			return AST_CAUSE_INTERWORKING;
    		case 415:	/* Unsupported media type */
    			return AST_CAUSE_INTERWORKING;
    		case 420:	/* Bad extension */