Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			ast_log(LOG_DEBUG, "SIP REGISTER attempt failed for %s : %s\n",
    
    		ASTOBJ_UNREF(peer, sip_destroy_peer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Get referring dnis */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_request *req;
    	
    	req = oreq;
    	if (!req)
    		req = &p->initreq;
    
    	ast_copy_string(tmp, get_header(req, "Diversion"), sizeof(tmp));
    
    	if (ast_strlen_zero(tmp))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (strncmp(c, "sip:", 4)) {
    		ast_log(LOG_WARNING, "Huh?  Not an RDNIS SIP header (%s)?\n", c);
    		return -1;
    	}
    	c += 4;
    
    	a = c;
    	strsep(&a, "@;");	/* trim anything after @ or ; */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_verbose("RDNIS is %s\n", c);
    
    	ast_string_field_set(p, rdnis, c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 0;
    }
    
    /*! \brief Find out who the call is for 
    	We use the INVITE uri to find out
    */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
    
    	char tmp[256] = "", *uri, *a;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sip_request *req;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	req = oreq;
    	if (!req)
    		req = &p->initreq;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (req->rlPart2)
    
    		ast_copy_string(tmp, req->rlPart2, sizeof(tmp));
    
    	if (strncmp(uri, "sip:", 4)) {
    		ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", uri);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    
    	/* Now find the From: caller ID and name */
    	ast_copy_string(tmpf, get_header(req, "From"), sizeof(tmpf));
    	if (!ast_strlen_zero(tmpf)) {
    		if (pedanticsipchecking)
    			ast_uri_decode(tmpf);
    		from = get_in_brackets(tmpf);
    	} else {
    		from = NULL;
    	}
    	
    
    	if (!ast_strlen_zero(from)) {
    		if (strncmp(from, "sip:", 4)) {
    			ast_log(LOG_WARNING, "Huh?  Not a SIP header (%s)?\n", from);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    
    		if ((a = strchr(from, '@')))
    			*a++ = '\0';
    		else
    			a = from;	/* just a domain */
    
    		from = strsep(&from, ";");	/* Remove userinfo options */
    		a = strsep(&a, ";");		/* Remove URI options */
    
    		ast_string_field_set(p, fromdomain, a);
    
    	/* Skip any options and find the domain */
    
    	/* Get the target domain */
    	if ((a = strchr(uri, '@'))) {
    
    	} else {	/* No username part */
    		a = uri;
    		uri = "s";	/* Set extension to "s" */
    
    	colon = strchr(a, ':'); /* Remove :port */
    	if (colon)
    		*colon = '\0';
    
    
    	uri = strsep(&uri, ";");	/* Remove userinfo options */
    	a = strsep(&a, ";");		/* Remove URI options */
    
    
    
    	if (!AST_LIST_EMPTY(&domain_list)) {
    		char domain_context[AST_MAX_EXTENSION];
    
    		domain_context[0] = '\0';
    		if (!check_sip_domain(p->domain, domain_context, sizeof(domain_context))) {
    
    			if (!allow_external_domains && (req->method == SIP_INVITE || req->method == SIP_REFER)) {
    
    				ast_log(LOG_DEBUG, "Got SIP %s to non-local domain '%s'; refusing request.\n", sip_methods[req->method].text, p->domain);
    				return -2;
    			}
    		}
    		/* If we have a context defined, overwrite the original context */
    		if (!ast_strlen_zero(domain_context))
    
    			ast_string_field_set(p, context, domain_context);
    
    		ast_verbose("Looking for %s in %s (domain %s)\n", uri, p->context, p->domain);
    
    
    	/* Check the dialplan for the username part of the request URI,
    	   the domain will be stored in the SIPDOMAIN variable
    		Return 0 if we have a matching extension */
    
    	if (ast_exists_extension(NULL, p->context, uri, 1, from) ||
    		!strcmp(uri, ast_pickup_ext())) {
    
    			ast_string_field_set(p, exten, uri);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	/* Return 1 for pickup extension or overlap dialling support (if we support it) */
    	if((ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP) && 
     	    ast_canmatch_extension(NULL, p->context, uri, 1, from)) ||
    	    !strncmp(uri, ast_pickup_ext(), strlen(uri))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 1;
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    /*! \brief Lock interface lock and find matching pvt lock  
    	- Their tag is fromtag, our tag is to-tag
    	- This means that in some transactions, totag needs to be their tag :-)
    	  depending upon the direction
    */
    
    static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag) 
    
    	struct sip_pvt *sip_pvt_ptr;
    
    
    
    	if (option_debug > 3 && totag)
    		ast_log(LOG_DEBUG, "Looking for callid %s (fromtag %s totag %s)\n", callid, fromtag ? fromtag : "<no fromtag>", totag ? totag : "<no totag>");
    
    
    	/* Search interfaces and find the match */
    
    	for (sip_pvt_ptr = iflist; sip_pvt_ptr; sip_pvt_ptr = sip_pvt_ptr->next) {
    
    		if (!strcmp(sip_pvt_ptr->callid, callid)) {
    
    			int match = 1;
    			char *ourtag = sip_pvt_ptr->tag;
    
    
    			/* Go ahead and lock it (and its owner) before returning */
    			ast_mutex_lock(&sip_pvt_ptr->lock);
    
    
    			/* Check if tags match. If not, this is not the call we want
    			   (With a forking SIP proxy, several call legs share the
    			   call id, but have different tags)
    			*/
    			if (pedanticsipchecking && (strcmp(fromtag, sip_pvt_ptr->theirtag) || strcmp(totag, ourtag)))
    				match = 0;
    
    			if (!match) {
    				ast_mutex_unlock(&sip_pvt_ptr->lock);
    				break;
    			}
    
    			if (option_debug > 3 && totag)				 
    
    				ast_log(LOG_DEBUG, "Matched %s call - their tag is %s Our tag is %s\n",
    					ast_test_flag(&sip_pvt_ptr->flags[0], SIP_OUTGOING) ? "OUTGOING": "INCOMING",
    					sip_pvt_ptr->theirtag, sip_pvt_ptr->tag);
    
    			/* deadlock avoidance... */
    
    			while (sip_pvt_ptr->owner && ast_channel_trylock(sip_pvt_ptr->owner)) {
    
    				ast_mutex_unlock(&sip_pvt_ptr->lock);
    				usleep(1);
    				ast_mutex_lock(&sip_pvt_ptr->lock);
    
    	if (option_debug > 3 && !sip_pvt_ptr)
    		ast_log(LOG_DEBUG, "Found no match for callid %s to-tag %s from-tag %s\n", callid, totag, fromtag);
    
    /*! \brief Call transfer support (the REFER method) 
     * 	Extracts Refer headers into pvt dialog structure */
    static int get_refer_info(struct sip_pvt *transferer, struct sip_request *outgoing_req)
    
    	char *h_refer_to = NULL; 
    	char *h_referred_by = NULL;
    	char *refer_to;
    	const char *p_refer_to;
    	char *referred_by_uri = NULL;
    	char *ptr;
    
    	struct sip_request *req = NULL;
    
    	const char *transfer_context = NULL;
    	struct sip_refer *referdata;
    
    
    	referdata = transferer->refer;
    
    	if (!(p_refer_to = get_header(req, "Refer-To"))) {
    
    		ast_log(LOG_WARNING, "Refer-To Header missing. Skipping transfer.\n");
    		return -2;	/* Syntax error */
    
    	refer_to = get_in_brackets(h_refer_to);
    
    	if (strncasecmp(refer_to, "sip:", 4)) {
    		ast_log(LOG_WARNING, "Can't transfer to non-sip: URI.  (Refer-to: %s)?\n", refer_to);
    		return -3;
    
    	refer_to += 4;			/* Skip sip: */
    
    	/* Get referred by header if it exists */
    	if ((p_referred_by = get_header(req, "Referred-By"))) {
    		char *lessthan;
    		h_referred_by = ast_strdupa(p_referred_by);
    		if (pedanticsipchecking)
    			ast_uri_decode(h_referred_by);
    
    		/* Store referrer's caller ID name */
    		ast_copy_string(referdata->referred_by_name, h_referred_by, sizeof(referdata->referred_by_name));
    		if ((lessthan = strchr(referdata->referred_by_name, '<'))) {
    			*(lessthan - 1) = '\0';	/* Space */
    		}
    
    		referred_by_uri = get_in_brackets(h_referred_by);
    		if(strncasecmp(referred_by_uri, "sip:", 4)) {
    			ast_log(LOG_WARNING, "Huh?  Not a sip: header (Referred-by: %s). Skipping.\n", referred_by_uri);
    			referred_by_uri = (char *) NULL;
    		} else {
    			referred_by_uri += 4;		/* Skip sip: */
    		}
    	}
    
    	/* Check for arguments in the refer_to header */
    	if ((ptr = strchr(refer_to, '?'))) { /* Search for arguments */
    
    		if (!strncasecmp(ptr, "REPLACES=", 9)) {
    
    			char *to = NULL, *from = NULL;
    
    
    			/* This is an attended transfer */
    			referdata->attendedtransfer = 1;
    			strncpy(referdata->replaces_callid, ptr+9, sizeof(referdata->replaces_callid));
    			ast_uri_decode(referdata->replaces_callid);
    
    			if ((ptr = strchr(referdata->replaces_callid, ';'))) 	/* Find options */ {
    				*ptr++ = '\0';
    
    			if (ptr) {
    				/* Find the different tags before we destroy the string */
    				to = strcasestr(ptr, "to-tag=");
    				from = strcasestr(ptr, "from-tag=");
    			}
    
    
    			/* Grab the to header */
    			if (to) {
    				ptr = to + 7;
    				if ((to = strchr(ptr, '&')))
    					*to = '\0';
    				if ((to = strchr(ptr, ';')))
    					*to = '\0';
    				ast_copy_string(referdata->replaces_callid_totag, ptr, sizeof(referdata->replaces_callid_totag));
    			}
    
    			if (from) {
    				ptr = from + 9;
    				if ((to = strchr(ptr, '&')))
    					*to = '\0';
    				if ((to = strchr(ptr, ';')))
    					*to = '\0';
    				ast_copy_string(referdata->replaces_callid_fromtag, ptr, sizeof(referdata->replaces_callid_fromtag));
    			}
    
    			if (option_debug > 1) {
    				if (!pedanticsipchecking)
    					ast_log(LOG_DEBUG,"Attended transfer: Will use Replace-Call-ID : %s (No check of from/to tags)\n", referdata->replaces_callid );
    				else
    					ast_log(LOG_DEBUG,"Attended transfer: Will use Replace-Call-ID : %s F-tag: %s T-tag: %s\n", referdata->replaces_callid, referdata->replaces_callid_fromtag ? referdata->replaces_callid_fromtag : "<none>", referdata->replaces_callid_totag ? referdata->replaces_callid_totag : "<none>" );
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if ((ptr = strchr(refer_to, '@'))) {	/* Separate domain */
    		char *urioption;
    
    
    		*ptr++ = '\0';
    		if ((urioption = strchr(ptr, ';')))
    			*urioption++ = '\0';
    
    		/* Save the domain for the dial plan */
    		strncpy(referdata->refer_to_domain, ptr, sizeof(referdata->refer_to_domain));
    		if (urioption)
    			strncpy(referdata->refer_to_urioption, urioption, sizeof(referdata->refer_to_urioption));
    	}
    
    	if ((ptr = strchr(refer_to, ';'))) 	/* Remove options */
    		*ptr = '\0';
    	ast_copy_string(referdata->refer_to, refer_to, sizeof(referdata->refer_to));
    	
    
    	if (referred_by_uri) {
    		if ((ptr = strchr(referred_by_uri, ';'))) 	/* Remove options */
    			*ptr = '\0';
    		ast_copy_string(referdata->referred_by, referred_by_uri, sizeof(referdata->referred_by));
    	} else {
    		referdata->referred_by[0] = '\0';
    	}
    
    
    	/* Determine transfer context */
    	if (transferer->owner)	/* Mimic behaviour in res_features.c */
    		transfer_context = pbx_builtin_getvar_helper(transferer->owner, "TRANSFER_CONTEXT");
    
    	/* By default, use the context in the channel sending the REFER */
    	if (ast_strlen_zero(transfer_context)) {
    
    		transfer_context = S_OR(transferer->owner->macrocontext,
    					S_OR(transferer->context, default_context));
    
    	}
    
    	strncpy(referdata->refer_to_context, transfer_context, sizeof(referdata->refer_to_context));
    	
    	/* Either an existing extension or the parking extension */
    	if (ast_exists_extension(NULL, transfer_context, refer_to, 1, NULL) ) {
    		if (sip_debug_test_pvt(transferer)) {
    			ast_verbose("SIP transfer to extension %s@%s by %s\n", refer_to, transfer_context, referred_by_uri);
    
    		/* We are ready to transfer to the extension */
    
    		return 0;
    
    	} 
    	if (sip_debug_test_pvt(transferer))
    		ast_verbose("Failed SIP Transfer to non-existing extension %s in context %s\n n", refer_to, transfer_context);
    
    	/* Failure, we can't find this extension */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    /*! \brief Call transfer support (old way, deprecated by the IETF)--*/
    
    static int get_also_info(struct sip_pvt *p, struct sip_request *oreq)
    {
    
    	struct sip_request *req = oreq ? oreq : &p->initreq;
    	struct sip_refer *referdata = p->refer;
    
    	const char *transfer_context = NULL;
    
    	ast_copy_string(tmp, get_header(req, "Also"), sizeof(tmp));
    
    	if (strncmp(c, "sip:", 4)) {
    
    		ast_log(LOG_WARNING, "Huh?  Not a SIP header in Also: transfer (%s)?\n", c);
    
    		return -1;
    	}
    	c += 4;
    
    	if ((a = strchr(c, '@'))) {	/* Separate Domain */
    
    		ast_copy_string(referdata->refer_to_domain, a, sizeof(referdata->refer_to_domain));
    	}
    	
    	if ((a = strchr(c, ';'))) 	/* Remove arguments */
    		*a = '\0';
    
    		ast_verbose("Looking for %s in %s\n", c, p->context);
    
    	if (p->owner)	/* Mimic behaviour in res_features.c */
    		transfer_context = pbx_builtin_getvar_helper(p->owner, "TRANSFER_CONTEXT");
    
    	/* By default, use the context in the channel sending the REFER */
    
    	if (ast_strlen_zero(transfer_context)) {
    		transfer_context = S_OR(p->owner->macrocontext,
    					S_OR(p->context, default_context));
    
    	}
    	if (ast_exists_extension(NULL, transfer_context, c, 1, NULL)) {
    		/* This is a blind transfer */
    
    			ast_log(LOG_DEBUG,"SIP Bye-also transfer to Extension %s@%s \n", c, transfer_context);
    		ast_copy_string(referdata->refer_to, c, sizeof(referdata->refer_to));
    		ast_copy_string(referdata->referred_by, "", sizeof(referdata->referred_by));
    		ast_copy_string(referdata->refer_contact, "", sizeof(referdata->refer_contact));
    		referdata->refer_call = NULL;
    		/* Set new context */
    		ast_string_field_set(p, context, transfer_context);
    
    		return 0;
    	} else if (ast_canmatch_extension(NULL, p->context, c, 1, NULL)) {
    		return 1;
    	}
    
    	return -1;
    }
    
    /*! \brief check Via: header for hostname, port and rport request/answer */
    
    static void check_via(struct sip_pvt *p, struct sip_request *req)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *c, *pt;
    	struct hostent *hp;
    
    	struct ast_hostent ahp;
    
    	ast_copy_string(via, get_header(req, "Via"), sizeof(via));
    
    
    	/* Check for rport */
    	c = strstr(via, ";rport");
    	if (c && (c[6] != '='))	/* rport query, not answer */
    
    		ast_set_flag(&p->flags[0], SIP_NAT_ROUTE);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	c = strchr(via, ';');
    	if (c) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		*c = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    	c = strchr(via, ' ');
    	if (c) {
    		*c = '\0';
    
    		if (strcasecmp(via, "SIP/2.0/UDP")) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Don't know how to respond via '%s'\n", via);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		pt = strchr(c, ':');
    
    			*pt++ = '\0';	/* remember port pointer */
    
    		hp = ast_gethostbyname(c, &ahp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!hp) {
    			ast_log(LOG_WARNING, "'%s' is not a valid host\n", c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		memset(&p->sa, 0, sizeof(p->sa));
    		p->sa.sin_family = AF_INET;
    		memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr));
    
    		p->sa.sin_port = htons(pt ? atoi(pt) : DEFAULT_SIP_PORT);
    
    			const struct sockaddr_in *dst = sip_real_dst(p);
    
    			ast_verbose("Sending to %s : %d (%s)\n", ast_inet_ntoa(dst->sin_addr), ntohs(dst->sin_port), sip_nat_mode(p));
    
    /*! \brief  Get caller id name from SIP headers */
    
    static char *get_calleridname(const char *input, char *output, size_t outputsize)
    
    	const char *end = strchr(input,'<');	/* first_bracket */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	const char *tmp = strchr(input,'"');	/* first quote */
    
    	int bytes = 0;
    	int maxbytes = outputsize - 1;
    
    
    	if (!end || end == input)	/* we require a part in brackets */
    
    Olle Johansson's avatar
    Olle Johansson committed
    		return NULL;
    
    
    	/* move away from "<" */
    	end--;
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    
    	/* we found "name" */
    	if (tmp && tmp < end) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		end = strchr(tmp+1, '"');
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (!end)
    			return NULL;
    
    		/* protect the output buffer */
    
    	} else {
    		/* we didn't find "name" */
    		/* clear the empty characters in the begining*/
    
    		input = ast_skip_blanks(input);
    
    		/* clear the empty characters in the end */
    
    		while(*end && *end < 33 && end > input)
    
    		if (end >= input) {
    
    			/* protect the output buffer */
    
    /*! \brief  Get caller id number from Remote-Party-ID header field 
    
     *	Returns true if number should be restricted (privacy setting found)
     *	output is set to NULL if no number found
    
    static int get_rpid_num(const char *input, char *output, int maxlen)
    
    {
    	char *start;
    	char *end;
    
    	start = strchr(input,':');
    	if (!start) {
    
    	output[maxlen-1] = '\0';
    
    	end = strchr(output,'@');
    	if (end)
    		*end = '\0';
    
    	else
    		output[0] = '\0';
    
    	if (strstr(input,"privacy=full") || strstr(input,"privacy=uri"))
    
    		return AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
    
    /*! \brief  Check if matching user or peer is defined 
     	Match user on From: user name and peer on IP/port
    	This is used on first invite (not re-invites) and subscribe requests 
    
        \return 0 on success, non-zero on failure
    
    static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
    					      int sipmethod, char *uri, enum xmittype reliable,
    					      struct sockaddr_in *sin, struct sip_peer **authpeer)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct sip_peer *peer;
    
    	char from[256], *c;
    	char *of;
    	char rpid_num[50];
    	const char *rpid;
    
    	enum check_auth_result res = AUTH_SUCCESSFUL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *t;
    
    	int debug=sip_debug_test_addr(sin);
    
    	struct ast_variable *tmpvar = NULL, *v = NULL;
    
    	char *uri2 = ast_strdupa(uri);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Terminate URI */
    
    	while (*t && *t > 32 && *t != ';')
    
    Mark Spencer's avatar
    Mark Spencer committed
    		t++;
    	*t = '\0';
    
    	ast_copy_string(from, get_header(req, "From"), sizeof(from));	/* XXX bug in original code, overwrote string */
    
    	if (pedanticsipchecking)
    
    	/* XXX here tries to map the username for invite things */
    
    	memset(calleridname, 0, sizeof(calleridname));
    
    	get_calleridname(from, calleridname, sizeof(calleridname));
    
    		ast_string_field_set(p, cid_name, calleridname);
    
    	memset(rpid_num, 0, sizeof(rpid_num));
    
    	if (!ast_strlen_zero(rpid)) 
    
    		p->callingpres = get_rpid_num(rpid, rpid_num, sizeof(rpid_num));
    
    	if (ast_strlen_zero(p->exten)) {
    
    		if (!strncmp(t, "sip:", 4))
    			t+= 4;
    
    		ast_string_field_set(p, exten, t);
    
    		t = strchr(p->exten, '@');
    		if (t)
    			*t = '\0';
    
    		if (ast_strlen_zero(p->our_contact))
    
    	/* save the URI part of the From header */
    
    	ast_string_field_set(p, from, of);
    
    	if (strncmp(of, "sip:", 4)) {
    		ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
    	} else
    
    Mark Spencer's avatar
    Mark Spencer committed
    		of += 4;
    	/* Get just the username part */
    
    	if ((c = strchr(of, '@'))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		*c = '\0';
    
    		if ((c = strchr(of, ':')))
    			*c = '\0';
    
    		if (ast_is_shrinkable_phonenumber(tmp))
    			ast_shrink_phone_number(tmp);
    		ast_string_field_set(p, cid_num, tmp);
    
    	if (ast_strlen_zero(of))
    
    	if (!authpeer)	/* If we are looking for a peer, don't check the user objects (or realtime) */
    
    	/* Find user based on user name in the from header */
    
    		ast_copy_flags(&p->flags[0], &user->flags[0], SIP_FLAGS_TO_COPY);
    		ast_copy_flags(&p->flags[1], &user->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* copy channel vars */
    		for (v = user->chanvars ; v ; v = v->next) {
    
    			if ((tmpvar = ast_variable_new(v->name, v->value))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				tmpvar->next = p->chanvars; 
    				p->chanvars = tmpvar;
    
    		p->prefs = user->prefs;
    
    		/* replace callerid if rpid found, and not restricted */
    
    		if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) {
    
    			if (*calleridname)
    
    				ast_string_field_set(p, cid_name, calleridname);
    
    			if (ast_is_shrinkable_phonenumber(tmp))
    				ast_shrink_phone_number(tmp);
    			ast_string_field_set(p, cid_num, tmp);
    
    		
    		usenatroute = ast_test_flag(&p->flags[0], SIP_NAT_ROUTE);
    
    
    		if (p->rtp) {
    
    				ast_log(LOG_DEBUG, "Setting NAT on RTP to %s\n", usenatroute ? "On" : "Off");
    
    			ast_rtp_setnat(p->rtp, usenatroute);
    
    		}
    		if (p->vrtp) {
    
    				ast_log(LOG_DEBUG, "Setting NAT on VRTP to %s\n", usenatroute ? "On" : "Off");
    
    			ast_rtp_setnat(p->vrtp, usenatroute);
    
    		if (p->udptl) {
    			if (option_debug)
    				ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %s\n", usenatroute ? "On" : "Off");
    			ast_udptl_setnat(p->udptl, usenatroute);
    		}
    
    		if (!(res = check_auth(p, req, user->name, user->secret, user->md5secret, sipmethod, uri2, reliable, ast_test_flag(req, SIP_PKT_IGNORE)))) {
    
    			sip_cancel_destroy(p);
    
    			ast_copy_flags(&p->flags[0], &user->flags[0], SIP_FLAGS_TO_COPY);
    
    			ast_copy_flags(&p->flags[1], &user->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    			/* Copy SIP extensions profile from INVITE */
    			if (p->sipoptions)
    				user->sipoptions = p->sipoptions;
    
    
    			/* If we have a call limit, set flag */
    
    				ast_set_flag(&p->flags[0], SIP_CALL_LIMIT);
    
    			if (!ast_strlen_zero(user->context))
    
    				ast_string_field_set(p, context, user->context);
    			if (!ast_strlen_zero(user->cid_num) && !ast_strlen_zero(p->cid_num)) {
    
    				if (ast_is_shrinkable_phonenumber(tmp))
    					ast_shrink_phone_number(tmp);
    				ast_string_field_set(p, cid_num, tmp);
    
    			if (!ast_strlen_zero(user->cid_name) && !ast_strlen_zero(p->cid_num))
    				ast_string_field_set(p, cid_name, user->cid_name);
    			ast_string_field_set(p, username, user->name);
    
    			ast_string_field_set(p, peername, user->name);
    
    			ast_string_field_set(p, peersecret, user->secret);
    			ast_string_field_set(p, peermd5secret, user->md5secret);
    			ast_string_field_set(p, subscribecontext, user->subscribecontext);
    			ast_string_field_set(p, accountcode, user->accountcode);
    			ast_string_field_set(p, language, user->language);
    
    			ast_string_field_set(p, mohsuggest, user->mohsuggest);
    			ast_string_field_set(p, mohinterpret, user->mohinterpret);
    
    			p->allowtransfer = user->allowtransfer;
    
    			p->amaflags = user->amaflags;
    			p->callgroup = user->callgroup;
    			p->pickupgroup = user->pickupgroup;
    
    			if (user->callingpres)	/* User callingpres setting will override RPID header */
    				p->callingpres = user->callingpres;
    
    			
    			/* Set default codec settings for this call */
    			p->capability = user->capability;		/* User codec choice */
    			p->jointcapability = user->capability;		/* Our codecs */
    			if (p->peercapability)				/* AND with peer's codecs */
    
    				p->jointcapability &= p->peercapability;
    
    			if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
    			    (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
    
    				p->noncodeccapability |= AST_RTP_DTMF;
    			else
    				p->noncodeccapability &= ~AST_RTP_DTMF;
    
    			if (p->t38.peercapability)
    				p->t38.jointcapability &= p->t38.peercapability;
    
    			p->maxcallbitrate = user->maxcallbitrate;
    			/* If we do not support video, remove video from call structure */
    			if (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && p->vrtp) {
    				ast_rtp_destroy(p->vrtp);
    				p->vrtp = NULL;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		if (user && debug)
    
    			ast_verbose("Found user '%s'\n", user->name);
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (user) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_verbose("Found user '%s', but fails host access\n", user->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (!user) {
    
    		/* If we didn't find a user match, check for peers */
    
    		if (sipmethod == SIP_SUBSCRIBE)
    			/* For subscribes, match on peer name only */
    			peer = find_peer(of, NULL, 1);
    		else
    			/* Look for peer based on the IP address we received data from */
    			/* If peer is registered from this IP address or have this as a default
    			   IP address, this call is from the peer 
    			*/
    			peer = find_peer(NULL, &p->recv, 1);
    
    
    		if (peer) {
    
    			if (debug)
    
    				ast_verbose("Found peer '%s'\n", peer->name);
    
    			ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
    			ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    
    			/* Copy SIP extensions profile to peer */
    			if (p->sipoptions)
    				peer->sipoptions = p->sipoptions;
    
    
    			/* replace callerid if rpid found, and not restricted */
    
    			if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) {
    
    				if (*calleridname)
    
    					ast_string_field_set(p, cid_name, calleridname);
    
    				if (ast_is_shrinkable_phonenumber(tmp))
    					ast_shrink_phone_number(tmp);
    				ast_string_field_set(p, cid_num, tmp);
    
    			usenatroute = ast_test_flag(&p->flags[0], SIP_NAT_ROUTE);
    
    				ast_log(LOG_DEBUG, "Setting NAT on RTP to %s\n", usenatroute ? "On" : "Off");
    
    				ast_rtp_setnat(p->rtp, usenatroute);
    
    				ast_log(LOG_DEBUG, "Setting NAT on VRTP to %s\n", usenatroute ? "On" : "Off");
    
    				ast_rtp_setnat(p->vrtp, usenatroute);
    
    			if (p->udptl) {
    				ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %s\n", usenatroute ? "On" : "Off");
    				ast_udptl_setnat(p->udptl, usenatroute);
    			}
    
    			ast_string_field_set(p, peersecret, peer->secret);
    			ast_string_field_set(p, peermd5secret, peer->md5secret);
    			ast_string_field_set(p, subscribecontext, peer->subscribecontext);
    
    			ast_string_field_set(p, mohinterpret, peer->mohinterpret);
    			ast_string_field_set(p, mohsuggest, peer->mohsuggest);
    
    			if (peer->callingpres)	/* Peer calling pres setting will override RPID */
    				p->callingpres = peer->callingpres;
    
    			if (peer->maxms && peer->lastms)
    				p->timer_t1 = peer->lastms;
    
    			if (ast_test_flag(&peer->flags[0], SIP_INSECURE_INVITE)) {
    
    				/* Pretend there is no required authentication */
    
    				ast_string_field_free(p, peersecret);
    				ast_string_field_free(p, peermd5secret);
    
    			if (!(res = check_auth(p, req, peer->name, p->peersecret, p->peermd5secret, sipmethod, uri2, reliable, ast_test_flag(req, SIP_PKT_IGNORE)))) {
    
    				ast_copy_flags(&p->flags[0], &peer->flags[0], SIP_FLAGS_TO_COPY);
    				ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    				/* If we have a call limit, set flag */
    
    					ast_set_flag(&p->flags[0], SIP_CALL_LIMIT);
    
    				ast_string_field_set(p, peername, peer->name);
    				ast_string_field_set(p, authname, peer->name);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* copy channel vars */
    				for (v = peer->chanvars ; v ; v = v->next) {
    
    					if ((tmpvar = ast_variable_new(v->name, v->value))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						tmpvar->next = p->chanvars; 
    						p->chanvars = tmpvar;
    					}
    				}
    
    				if (authpeer) {
    					(*authpeer) = ASTOBJ_REF(peer);	/* Add a ref to the object here, to keep it in memory a bit longer if it is realtime */
    				}
    
    
    				if (!ast_strlen_zero(peer->username)) {
    
    					ast_string_field_set(p, username, peer->username);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					/* Use the default username for authentication on outbound calls */
    
    					/* XXX this takes the name from the caller... can we override ? */
    
    					ast_string_field_set(p, authname, peer->username);
    
    				if (!ast_strlen_zero(peer->cid_num) && !ast_strlen_zero(p->cid_num)) {
    
    					if (ast_is_shrinkable_phonenumber(tmp))
    						ast_shrink_phone_number(tmp);
    					ast_string_field_set(p, cid_num, tmp);
    
    				}
    				if (!ast_strlen_zero(peer->cid_name) && !ast_strlen_zero(p->cid_name)) 
    
    					ast_string_field_set(p, cid_name, peer->cid_name);
    				ast_string_field_set(p, fullcontact, peer->fullcontact);
    
    				if (!ast_strlen_zero(peer->context))
    
    					ast_string_field_set(p, context, peer->context);
    				ast_string_field_set(p, peersecret, peer->secret);
    				ast_string_field_set(p, peermd5secret, peer->md5secret);
    				ast_string_field_set(p, language, peer->language);
    				ast_string_field_set(p, accountcode, peer->accountcode);
    
    				p->amaflags = peer->amaflags;
    
    				p->callgroup = peer->callgroup;
    				p->pickupgroup = peer->pickupgroup;
    
    				p->capability = peer->capability;
    
    				p->jointcapability = peer->capability;
    
    				if (p->peercapability)
    					p->jointcapability &= p->peercapability;
    
    Olle Johansson's avatar
    Olle Johansson committed
    				p->maxcallbitrate = peer->maxcallbitrate;
    
    				if (!ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && p->vrtp) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    					ast_rtp_destroy(p->vrtp);
    					p->vrtp = NULL;
    				}
    
    				if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
    				    (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
    
    					p->noncodeccapability |= AST_RTP_DTMF;
    				else
    					p->noncodeccapability &= ~AST_RTP_DTMF;
    
    				if (p->t38.peercapability)
    					p->t38.jointcapability &= p->t38.peercapability;
    
    			ASTOBJ_UNREF(peer, sip_destroy_peer);
    
    				ast_verbose("Found no matching peer or user for '%s:%d'\n", ast_inet_ntoa(p->recv.sin_addr), ntohs(p->recv.sin_port));
    
    			if (!global_allowguest) {
    				if (global_alwaysauthreject)
    					res = AUTH_FAKE_AUTH; /* reject with fake authorization request */
    				else
    					res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */
    			}
    
    		ASTOBJ_UNREF(user, sip_destroy_user);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief  Find user 
    	If we get a match, this will add a reference pointer to the user object in ASTOBJ, that needs to be unreferenced
    */
    
    static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod, char *uri, enum xmittype reliable, struct sockaddr_in *sin)
    
    	return check_user_full(p, req, sipmethod, uri, reliable, sin, NULL);
    
    /*! \brief  Get text out of a SIP MESSAGE packet */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int get_msg_text(char *buf, int len, struct sip_request *req)
    {
    	int x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int y;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	y = len - strlen(buf) - 5;
    	if (y < 0)
    		y = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=0;x<req->lines;x++) {
    
    		strncat(buf, req->line[x], y); /* safe */
    
    		y -= strlen(req->line[x]) + 1;
    		if (y < 0)
    			y = 0;
    		if (y != 0)
    
    			strcat(buf, "\n"); /* safe */
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief  Receive SIP MESSAGE method messages
    \note	We only handle messages within current calls currently 
    	Reference: RFC 3428 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void receive_message(struct sip_pvt *p, struct sip_request *req)
    {
    	char buf[1024];
    	struct ast_frame f;
    
    	const char *content_type = get_header(req, "Content-Type");
    
    
    	if (strcmp(content_type, "text/plain")) { /* No text/plain attachment */
    		transmit_response(p, "415 Unsupported Media Type", req); /* Good enough, or? */
    
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (get_msg_text(buf, sizeof(buf), req)) {
    		ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid);
    
    		transmit_response(p, "202 Accepted", req);
    
    		ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p->owner) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_verbose("Message received: '%s'\n", buf);
    
    		memset(&f, 0, sizeof(f));
    		f.frametype = AST_FRAME_TEXT;
    		f.subclass = 0;
    		f.offset = 0;
    		f.data = buf;
    		f.datalen = strlen(buf);
    		ast_queue_frame(p->owner, &f);
    
    		transmit_response(p, "202 Accepted", req); /* We respond 202 accepted, since we relay the message */
    	} else { /* Message outside of a call, we do not support that */
    		ast_log(LOG_WARNING,"Received message to %s from %s, dropped it...\n  Content-Type:%s\n  Message: %s\n", get_header(req,"To"), get_header(req,"From"), content_type, buf);
    		transmit_response(p, "405 Method Not Allowed", req); /* Good enough, or? */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
    
    /*! \brief  CLI Command to show calls within limits set by call_limit */
    
    static int sip_show_inuse(int fd, int argc, char *argv[])
    {
    
    #define FORMAT  "%-25.25s %-15.15s %-15.15s \n"
    #define FORMAT2 "%-25.25s %-15.15s %-15.15s \n"
    
    	char iused[40];
    
    	int showall = FALSE;
    
    	if (argc < 3) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return RESULT_SHOWUSAGE;
    
    
    	if (argc == 4 && !strcmp(argv[3],"all")) 
    
    			showall = TRUE;
    
    	
    	ast_cli(fd, FORMAT, "* User name", "In use", "Limit");
    
    	ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do {
    
    		ASTOBJ_RDLOCK(iterator);
    
    		if (iterator->call_limit)
    			snprintf(ilimits, sizeof(ilimits), "%d", iterator->call_limit);
    
    			ast_copy_string(ilimits, "N/A", sizeof(ilimits));
    
    		snprintf(iused, sizeof(iused), "%d", iterator->inUse);
    
    			ast_cli(fd, FORMAT2, iterator->name, iused, ilimits);
    		ASTOBJ_UNLOCK(iterator);
    	} while (0) );
    
    	ast_cli(fd, FORMAT, "* Peer name", "In use", "Limit");
    
    	ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
    		ASTOBJ_RDLOCK(iterator);
    
    		if (iterator->call_limit)
    			snprintf(ilimits, sizeof(ilimits), "%d", iterator->call_limit);
    
    			ast_copy_string(ilimits, "N/A", sizeof(ilimits));
    
    		snprintf(iused, sizeof(iused), "%d/%d", iterator->inUse, iterator->inRinging);
    
    			ast_cli(fd, FORMAT2, iterator->name, iused, ilimits);
    
    		ASTOBJ_UNLOCK(iterator);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return RESULT_SUCCESS;
    #undef FORMAT
    #undef FORMAT2
    }
    
    /*! \brief Convert transfer mode to text string */
    static char *transfermode2str(enum transfermodes mode)
    {
    	if (mode == TRANSFER_OPENFORALL)
    		return "open";
    	else if (mode == TRANSFER_CLOSED)
    		return "closed";
    	return "strict";
    }
    
    
    /*! \brief  Convert NAT setting to text string */