Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	/* Don't forward RFC2833 if we're not supposed to */
    
    	if (f && (f->frametype == AST_FRAME_DTMF) &&
    	    (ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833))
    
    	/* We already hold the channel lock */
    
    	if (!p->owner || (f && f->frametype != AST_FRAME_VOICE))
    
    	if (f && f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
    
    		if (!(f->subclass & p->jointcapability)) {
    
    			ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
    				ast_getformatname(f->subclass), p->owner->name);
    
    			return &ast_null_frame;
    		}
    
    		ast_debug(1, "Oooh, format changed to %d %s\n",
    			f->subclass, ast_getformatname(f->subclass));
    
    		p->owner->nativeformats = (p->owner->nativeformats & (AST_FORMAT_VIDEO_MASK | AST_FORMAT_TEXT_MASK)) | f->subclass;
    		ast_set_read_format(p->owner, p->owner->readformat);
    		ast_set_write_format(p->owner, p->owner->writeformat);
    	}
    
    
    	if (f && (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
    
    		f = ast_dsp_process(p->owner, p->vad, f);
    		if (f && f->frametype == AST_FRAME_DTMF) {
    			if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') {
    
    				ast_debug(1, "Fax CNG detected on %s\n", ast->name);
    
    			} else {
    				ast_debug(1, "* Detected inband DTMF '%c'\n", f->subclass);
    
    /*! \brief Read SIP RTP from channel */
    
    static struct ast_frame *sip_read(struct ast_channel *ast)
    {
    	struct ast_frame *fr;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	int faxdetected = FALSE;
    
    	fr = sip_rtp_read(ast, p, &faxdetected);
    
    
    	/* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */
    	/* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */
    	if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) {
    		if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
    			if (!p->pendinginvite) {
    
    				ast_debug(3, "Sending reinvite on SIP (%s) for T.38 negotiation.\n",ast->name);
    
    				p->t38.state = T38_LOCAL_REINVITE;
    
    				transmit_reinvite_with_sdp(p, TRUE);
    
    				ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, ast->name);
    
    			}
    		} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
    
    			ast_debug(3, "Deferring reinvite on SIP (%s) - it will be re-negotiated for T.38\n", ast->name);
    
    			ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
    		}
    	}
    
    
    	return fr;
    
    
    /*! \brief Generate 32 byte random string for callid's etc */
    static char *generate_random_string(char *buf, size_t size)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int x;
    
    		val[x] = ast_random();
    	snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]);
    
    
    	return buf;
    }
    
    /*! \brief Build SIP Call-ID value for a non-REGISTER transaction */
    static void build_callid_pvt(struct sip_pvt *pvt)
    {
    	char buf[33];
    
    
    	const char *host = S_OR(pvt->fromdomain, ast_inet_ntoa(pvt->ourip.sin_addr));
    
    	
    	ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
    
    /*! \brief Build SIP Call-ID value for a REGISTER transaction */
    
    static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain)
    {
    
    	const char *host = S_OR(fromdomain, ast_inet_ntoa(ourip));
    
    	ast_string_field_build(reg, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
    
    /*! \brief Make our SIP dialog tag */
    
    static void make_our_tag(char *tagbuf, size_t len)
    {
    
    	snprintf(tagbuf, len, "as%08lx", ast_random());
    
    /*! \brief Allocate sip_pvt structure, set defaults and link in the container.
     * Returns a reference to the object so whoever uses it later must
     * remember to release the reference.
     */
    
    static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin,
    				 int useglobal_nat, const int intended_method)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_pvt *p;
    
    	if (!(p = ast_calloc(1, sizeof(*p))))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    
    	if (ast_string_field_init(p, 512)) {
    
    	ast_mutex_init(&p->pvt_lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->initid = -1;
    
    	p->autokillid = -1;
    
    	p->prefs = default_prefs;		/* Set default codecs for this call */
    
    
    	if (intended_method != SIP_OPTIONS) {	/* Peerpoke has it's own system */
    		p->timer_t1 = global_t1;	/* Default SIP retransmission timer T1 (RFC 3261) */
    		p->timer_b = global_timer_b;	/* Default SIP transaction timer B (RFC 3261) */
    	}
    
    	if (!sin)
    		p->ourip = internip;
    	else {
    
    		ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
    
    
    	/* Copy global flags to this PVT at setup. */
    
    	ast_copy_flags(&p->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
    	ast_copy_flags(&p->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
    
    	p->do_history = recordhistory;
    
    
    	if (sip_methods[intended_method].need_rtp) {
    		p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
    
    		/* If the global videosupport flag is on, we always create a RTP interface for video */
    
    		if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT))
    
    			p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
    
     		if (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT))
     			p->trtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
    
    		if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT))
    			p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr);
    
     		if (!p->rtp|| (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp) 
    				|| (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && !p->trtp)) {
     			ast_log(LOG_WARNING, "Unable to create RTP audio %s%ssession: %s\n",
     				ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video " : "",
     				ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "and text " : "", strerror(errno));
    
    			ast_mutex_destroy(&p->pvt_lock);
    
    			if (p->chanvars) {
    
    		ast_rtp_setqos(p->rtp, global_tos_audio, global_cos_audio, "SIP RTP");
    
    		ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
    
    		ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
    
    		ast_rtp_set_rtptimeout(p->rtp, global_rtptimeout);
    		ast_rtp_set_rtpholdtimeout(p->rtp, global_rtpholdtimeout);
    		ast_rtp_set_rtpkeepalive(p->rtp, global_rtpkeepalive);
    
    			ast_rtp_setqos(p->vrtp, global_tos_video, global_cos_video, "SIP VRTP");
    
    			ast_rtp_setdtmf(p->vrtp, 0);
    
    			ast_rtp_setdtmfcompensate(p->vrtp, 0);
    
    			ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout);
    			ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout);
    			ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive);
    
    			ast_rtp_setqos(p->trtp, global_tos_text, global_cos_text, "SIP TRTP");
    
    			ast_rtp_setdtmf(p->trtp, 0);
    			ast_rtp_setdtmfcompensate(p->trtp, 0);
    		}
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (p->udptl)
    
    			ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		p->maxcallbitrate = default_maxcallbitrate;
    
    		/* Setup NAT structure according to global settings if we have an address */
    
    		ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT);
    
    		do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE);
    
    		ast_string_field_set(p, fromdomain, default_fromdomain);
    	build_via(p);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!callid)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else
    
    		ast_string_field_set(p, callid, callid);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Assign default music on hold class */
    
    	ast_string_field_set(p, mohinterpret, default_mohinterpret);
    	ast_string_field_set(p, mohsuggest, default_mohsuggest);
    
    	p->allowtransfer = global_allowtransfer;
    
    	if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
    	    (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		p->noncodeccapability |= AST_RTP_DTMF;
    
    		ast_copy_flags(&p->t38.t38support, &p->flags[1], SIP_PAGE2_T38SUPPORT);
    		set_t38_capabilities(p);
    
    		p->t38.jointcapability = p->t38.capability;
    	}
    
    	ast_string_field_set(p, context, default_context);
    
    	dialoglist_lock();
    
    	dialoglist_unlock();
    
    	ast_debug(1, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : "(No Call-ID)", sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP");
    
    /*! \brief find or create a dialog structure for an incoming SIP message.
     * Connect incoming SIP message to current dialog or create new dialog structure
     * Returns a reference to the sip_pvt object, remember to give it back once done.
    
     *     Called by handle_incoming(), sipsock_read
    
    static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method)
    
    	struct sip_pvt *p = NULL;
    
    	char *tag = "";	/* note, tag is never NULL */
    
    	char totag[128];
    	char fromtag[128];
    
    	const char *callid = get_header(req, "Call-ID");
    
    	const char *from = get_header(req, "From");
    	const char *to = get_header(req, "To");
    	const char *cseq = get_header(req, "Cseq");
    
    
    	/* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
    	/* get_header always returns non-NULL so we must use ast_strlen_zero() */
    	if (ast_strlen_zero(callid) || ast_strlen_zero(to) ||
    			ast_strlen_zero(from) || ast_strlen_zero(cseq))
    
    
    	if (pedanticsipchecking) {
    
    		/* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy
    		   we need more to identify a branch - so we have to check branch, from
    		   and to tags to identify a call leg.
    
    		   For Asterisk to behave correctly, you need to turn on pedanticsipchecking
    
    		if (gettag(req, "To", totag, sizeof(totag)))
    
    			req->has_to_tag = 1;	/* Used in handle_request/response */
    
    		gettag(req, "From", fromtag, sizeof(fromtag));
    
    
    		tag = (req->method == SIP_RESPONSE) ? totag : fromtag;
    
    		ast_debug(5, "= Looking for  Call ID: %s (Checking %s) --From tag %s --To-tag %s  \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag);
    
    
    		/* All messages must always have From: tag */
    		if (ast_strlen_zero(fromtag)) {
    
    			ast_debug(5, "%s request has no from tag, dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
    
    			return NULL;
    		}
    		/* reject requests that must always have a To: tag */
    		if (ast_strlen_zero(totag) && (req->method == SIP_ACK || req->method == SIP_BYE || req->method == SIP_INFO )) {
    
    			ast_debug(5, "%s must have a to tag. dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
    
    	dialoglist_lock();
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		/* In pedantic, we do not want packets with bad syntax to be connected to a PVT */
    
    		int found = FALSE;
    
    		if (ast_strlen_zero(p->callid))
    			continue;
    
    		if (req->method == SIP_REGISTER)
    			found = (!strcmp(p->callid, callid));
    		else 
    			found = (!strcmp(p->callid, callid) && 
    			(!pedanticsipchecking || !tag || ast_strlen_zero(p->theirtag) || !strcmp(p->theirtag, tag))) ;
    
    		ast_debug(5, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag);
    
    
    		/* If we get a new request within an existing to-tag - check the to tag as well */
    		if (pedanticsipchecking && found  && req->method != SIP_RESPONSE) {	/* SIP Request */
    			if (p->tag[0] == '\0' && totag[0]) {
    				/* We have no to tag, but they have. Wrong dialog */
    
    				found = FALSE;
    
    			} else if (totag[0]) {			/* Both have tags, compare them */
    				if (strcmp(totag, p->tag)) {
    
    					found = FALSE;		/* This is not our packet */
    
    			if (!found)
    				ast_debug(5, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n", p->callid, totag, sip_methods[req->method].text);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Found the call */
    
    			dialoglist_unlock();
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return p;
    		}
    	}
    
    	dialoglist_unlock();
    
    	
    	/* See if the method is capable of creating a dialog */
    
    	if (sip_methods[intended_method].can_create == CAN_CREATE_DIALOG) {
    		if (intended_method == SIP_REFER) {
    			/* We do support REFER, but not outside of a dialog yet */
    			transmit_response_using_temp(callid, sin, 1, intended_method, req, "603 Declined (no dialog)");
    		} else if (intended_method == SIP_NOTIFY) {
    			/* We do not support out-of-dialog NOTIFY either,
    		   	like voicemail notification, so cancel that early */
    			transmit_response_using_temp(callid, sin, 1, intended_method, req, "489 Bad event");
    		} else {
    			/* Ok, time to create a new SIP dialog object, a pvt */
    
    			if ((p = sip_alloc(callid, sin, 1, intended_method)))  {
    
    				/* Ok, we've created a dialog, let's go and process it */
    
    			} else {
    				/* We have a memory or file/socket error (can't allocate RTP sockets or something) so we're not
    					getting a dialog from sip_alloc. 
    	
    					Without a dialog we can't retransmit and handle ACKs and all that, but at least
    					send an error message.
    	
    					Sorry, we apologize for the inconvienience
    				*/
    				transmit_response_using_temp(callid, sin, 1, intended_method, req, "500 Server internal error");
    
    				ast_debug(4, "Failed allocating SIP dialog, sending 500 Server internal error and giving up\n");
    
    		return p; /* can be NULL */
    
    	} else if( sip_methods[intended_method].can_create == CAN_CREATE_DIALOG_UNSUPPORTED_METHOD) {
    		/* A method we do not support, let's take it on the volley */
    		transmit_response_using_temp(callid, sin, 1, intended_method, req, "501 Method Not Implemented");
    
    		ast_debug(2, "Got a request with unsupported SIP method.\n");
    
    	} else if (intended_method != SIP_RESPONSE && intended_method != SIP_ACK) {
    
    		/* This is a request outside of a dialog that we don't know about */
    		transmit_response_using_temp(callid, sin, 1, intended_method, req, "481 Call leg/transaction does not exist");
    
    		ast_debug(2, "That's odd...  Got a request in unknown dialog. Callid %s\n", callid ? callid : "<unknown>");
    
    	/* We do not respond to responses for dialogs that we don't know about, we just drop
    	   the session quickly */
    
    	if (intended_method == SIP_RESPONSE)
    		ast_debug(2, "That's odd...  Got a response on a call we dont know about. Callid %s\n", callid ? callid : "<unknown>");
    
    	return p;
    
    /*! \brief Parse register=> line in sip.conf and add to registry */
    
    static int sip_register(const char *value, int lineno)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_registry *reg;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	int portnum = 0;
    	char username[256] = "";
    	char *hostname=NULL, *secret=NULL, *authuser=NULL;
    
    	char *porta=NULL;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!value)
    		return -1;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	ast_copy_string(username, value, sizeof(username));
    	/* First split around the last '@' then parse the two components. */
    	hostname = strrchr(username, '@'); /* allow @ in the first part */
    
    	if (hostname)
    		*hostname++ = '\0';
    
    	if (ast_strlen_zero(username) || ast_strlen_zero(hostname)) {
    
    		ast_log(LOG_WARNING, "Format for registration is user[:secret[:authuser]]@host[:port][/contact] at line %d\n", lineno);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* split user[:secret[:authuser]] */
    	secret = strchr(username, ':');
    	if (secret) {
    		*secret++ = '\0';
    		authuser = strchr(secret, ':');
    		if (authuser)
    			*authuser++ = '\0';
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* split host[:port][/contact] */
    
    	callback = strchr(hostname, '/');
    	if (callback)
    		*callback++ = '\0';
    	if (ast_strlen_zero(callback))
    		callback = "s";
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	porta = strchr(hostname, ':');
    	if (porta) {
    		*porta++ = '\0';
    		portnum = atoi(porta);
    		if (portnum == 0) {
    			ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
    			return -1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (!(reg = ast_calloc(1, sizeof(*reg)))) {
    		ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	if (ast_string_field_init(reg, 256)) {
    
    		ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry strings\n");
    
    	regobjs++;
    	ASTOBJ_INIT(reg);
    
    	ast_string_field_set(reg, callback, callback);
    
    		ast_string_field_set(reg, username, username);
    
    	if (hostname)
    
    		ast_string_field_set(reg, hostname, hostname);
    
    	if (authuser)
    
    		ast_string_field_set(reg, authuser, authuser);
    
    		ast_string_field_set(reg, secret, secret);
    
    	reg->expire = -1;
    
    	reg->timeout =  -1;
    	reg->refresh = default_expiry;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	reg->portno = portnum;
    
    	reg->callid_valid = FALSE;
    
    	ASTOBJ_CONTAINER_LINK(&regl, reg);	/* Add the new registry entry to the list */
    
    	registry_unref(reg);	/* release the reference given by ASTOBJ_INIT. The container has another reference */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /*! \brief  Parse multiline SIP headers into one header
    	This is enabled if pedanticsipchecking is enabled */
    
    static int lws2sws(char *msgbuf, int len) 
    
    	int h = 0, t = 0; 
    	int lws = 0; 
    
    	for (; h < len;) { 
    		/* Eliminate all CRs */ 
    		if (msgbuf[h] == '\r') { 
    			h++; 
    			continue; 
    		} 
    		/* Check for end-of-line */ 
    		if (msgbuf[h] == '\n') { 
    
    			/* Check for end-of-message */ 
    
    				break; 
    			/* Check for a continuation line */ 
    			if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') { 
    				/* Merge continuation line */ 
    				h++; 
    				continue; 
    			} 
    			/* Propagate LF and start new line */ 
    			msgbuf[t++] = msgbuf[h++]; 
    			lws = 0;
    
    		if (msgbuf[h] == ' ' || msgbuf[h] == '\t') { 
    			if (lws) { 
    				h++; 
    				continue; 
    			} 
    			msgbuf[t++] = msgbuf[h++]; 
    			lws = 1; 
    
    			continue; 
    		} 
    		msgbuf[t++] = msgbuf[h++]; 
    
    /*! \brief Parse a SIP message 
    	\note this function is used both on incoming and outgoing packets
    */
    
    static void parse_request(struct sip_request *req)
    
    	char *c = req->data, **dst = req->header;
    	int i = 0, lim = SIP_MAX_HEADERS - 1;
    
    	req->header[0] = c;
    	req->headers = -1;	/* mark that we are working on the header */
    	for (; *c; c++) {
    		if (*c == '\r')		/* remove \r */
    			*c = '\0';
    		else if (*c == '\n') { /* end of this line */
    			*c = '\0';
    
    			if (sipdebug)
    				ast_debug(4, "%7s %2d [%3d]: %s\n",
    
    					req->headers < 0 ? "Header" : "Body",
    
    					i, (int)strlen(dst[i]), dst[i]);
    
    			if (ast_strlen_zero(dst[i]) && req->headers < 0) {
    				req->headers = i;	/* record number of header lines */
    				dst = req->line;	/* start working on the body */
    				i = 0;
    				lim = SIP_MAX_LINES - 1;
    			} else {	/* move to next line, check for overflows */
    				if (i++ >= lim)
    					break;
    
    			dst[i] = c + 1; /* record start of next line */
    
    	/* Check for last header without CRLF. The RFC for SDP requires CRLF,
    	   but since some devices send without, we'll be generous in what we accept.
    	*/
    	if (!ast_strlen_zero(dst[i])) {
    
    		if (sipdebug)
    			ast_debug(4, "%7s %2d [%3d]: %s\n",
    
    				req->headers < 0 ? "Header" : "Body",
    				i, (int)strlen(dst[i]), dst[i]);
    		i++;
    	}
    
    	/* update count of header or body lines */
    	if (req->headers >= 0)	/* we are in the body */
    		req->lines = i;
    	else {			/* no body */
    		req->headers = i;
    		req->lines = 0;
    		req->line[0] = "";
    
    
    	if (*c)
    		ast_log(LOG_WARNING, "Too many lines, skipping <%s>\n", c);
    
    	/* Split up the first line parts */
    	determine_firstline_parts(req);
    
    /*!
      \brief Determine whether a SIP message contains an SDP in its body
      \param req the SIP request to process
      \return 1 if SDP found, 0 if not found
    
      Also updates req->sdp_start and req->sdp_end to indicate where the SDP
      lives in the message body.
    */
    static int find_sdp(struct sip_request *req)
    {
    	const char *content_type;
    	const char *search;
    	char *boundary;
    	unsigned int x;
    
    	int boundaryisquoted = FALSE;
    
    	int found_application_sdp = FALSE;
    	int found_end_of_headers = FALSE;
    
    
    	content_type = get_header(req, "Content-Type");
    
    	/* if the body contains only SDP, this is easy */
    	if (!strcasecmp(content_type, "application/sdp")) {
    		req->sdp_start = 0;
    		req->sdp_end = req->lines;
    
    		return req->lines ? 1 : 0;
    
    	}
    
    	/* if it's not multipart/mixed, there cannot be an SDP */
    	if (strncasecmp(content_type, "multipart/mixed", 15))
    		return 0;
    
    	/* if there is no boundary marker, it's invalid */
    
    	if ((search = strcasestr(content_type, ";boundary=")))
    		search += 10;
    	else if ((search = strcasestr(content_type, "; boundary=")))
    		search += 11;
    	else
    
    		return 0;
    
    	if (ast_strlen_zero(search))
    		return 0;
    
    
    	/* If the boundary is quoted with ", remove quote */
    	if (*search == '\"')  {
    		search++;
    		boundaryisquoted = TRUE;
    	}
    
    
    	/* make a duplicate of the string, with two extra characters
    	   at the beginning */
    	boundary = ast_strdupa(search - 2);
    	boundary[0] = boundary[1] = '-';
    
    	/* Remove final quote */
    	if (boundaryisquoted)
    		boundary[strlen(boundary) - 1] = '\0';
    
    
    	/* search for the boundary marker, the empty line delimiting headers from
    	   sdp part and the end boundry if it exists */
    
    	for (x = 0; x < (req->lines ); x++) {
    		if(!strncasecmp(req->line[x], boundary, strlen(boundary))){
    			if(found_application_sdp && found_end_of_headers){
    				req->sdp_end = x-1;
    				return 1;
    			}
    			found_application_sdp = FALSE;
    		}
    		if(!strcasecmp(req->line[x], "Content-Type: application/sdp"))
    			found_application_sdp = TRUE;
    		
    		if(strlen(req->line[x]) == 0 ){
    			if(found_application_sdp && !found_end_of_headers){
    				req->sdp_start = x;
    				found_end_of_headers = TRUE;
    
    	if(found_application_sdp && found_end_of_headers) {
    		req->sdp_end = x;
    		return TRUE;
    	}
    	return FALSE;
    
    /*! \brief Process SIP SDP offer, select formats and activate RTP channels
    	If offer is rejected, we will not change any properties of the call
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
     	Return 0 on success, a negative value on errors.
    	Must be called after find_sdp().
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int process_sdp(struct sip_pvt *p, struct sip_request *req)
    {
    
    Olle Johansson's avatar
    Olle Johansson committed
    	const char *m;		/* SDP media offer */
    
    	const char *c;
    	const char *a;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char host[258];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int len = -1;
    
    	int portno = -1;		/*!< RTP Audio port number */
    	int vportno = -1;		/*!< RTP Video port number */
    
    	int tportno = -1;		/*!< RTP Text port number */
    
    	int udptlportno = -1;
    	int peert38capability = 0;
    	char s[256];
    	int old = 0;
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    	/* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */	
    
    	int peercapability = 0, peernoncodeccapability = 0;
    
    	int vpeercapability = 0, vpeernoncodeccapability = 0;
    
    	int tpeercapability = 0, tpeernoncodeccapability = 0;
    
    	struct sockaddr_in sin;		/*!< media socket address */
    	struct sockaddr_in vsin;	/*!< Video socket address */
    
    	struct sockaddr_in tsin;	/*!< Text socket address */
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    
    	const char *codecs;
    
    	struct hostent *hp;		/*!< RTP Audio host IP */
    	struct hostent *vhp = NULL;	/*!< RTP video host IP */
    
    	struct hostent *thp = NULL;	/*!< RTP text host IP */
    
    	struct ast_hostent audiohp;
    	struct ast_hostent videohp;
    
    	struct ast_hostent texthp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int codec;
    
    	int iterator;
    
    	int sendonly = -1;
    
    	struct ast_rtp *newaudiortp, *newvideortp, *newtextrtp;	/* Buffers for codec handling */
    
    	int newjointcapability;				/* Negotiated capability */
    	int newpeercapability;
    	int newnoncodeccapability;
    	int numberofmediastreams = 0;
    	int debug = sip_debug_test_pvt(p);
    		
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	int found_rtpmap_codecs[32];
    	int last_rtpmap_codec=0;
    
    
    	if (!p->rtp) {
    		ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n");
    		return -1;
    	}
    
    
    	/* Initialize the temporary RTP structures we use to evaluate the offer from the peer */
    
    	newaudiortp = alloca(ast_rtp_alloc_size());
    	memset(newaudiortp, 0, ast_rtp_alloc_size());
    
    	ast_rtp_new_init(newaudiortp);
    
    
    	newvideortp = alloca(ast_rtp_alloc_size());
    	memset(newvideortp, 0, ast_rtp_alloc_size());
    
    	ast_rtp_new_init(newvideortp);
    
    	newtextrtp = alloca(ast_rtp_alloc_size());
    	memset(newtextrtp, 0, ast_rtp_alloc_size());
    	ast_rtp_new_init(newtextrtp);
    	ast_rtp_pt_clear(newtextrtp);
    
    
    	/* Update our last rtprx when we receive an SDP, too */
    
    	p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	m = get_sdp(req, "m");
    
    	destiterator = req->sdp_start;
    
    	c = get_sdp_iterate(&destiterator, req, "c");
    
    	if (ast_strlen_zero(m) || ast_strlen_zero(c)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c);
    		return -1;
    	}
    
    
    	/* Check for IPv4 address (not IPv6 yet) */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sscanf(c, "IN IP4 %256s", host) != 1) {
    		ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* XXX This could block for a long time, and block the main thread! XXX */
    
    	hp = ast_gethostbyname(host, &audiohp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!hp) {
    		ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
    		return -1;
    	}
    
    	vhp = hp;	/* Copy to video address as default too */
    
    	thp = hp;	/* Copy to text address as default too */
    
    	iterator = req->sdp_start;
    
    	/* default: novideo and notext set */
    	p->novideo = TRUE;
    	p->notext = TRUE;
    
    	if (p->vrtp)
    		ast_rtp_pt_clear(newvideortp);  /* Must be cleared in case no m=video line exists */
     
    	if (p->trtp)
    		ast_rtp_pt_clear(newtextrtp);  /* Must be cleared in case no m=text line exists */
    
    
    	/* Find media streams in this SDP offer */
    
    	while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') {
    
    		int video = FALSE;
    		int text = FALSE;
    
    
    		numberofports = 1;
    		if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
    
    		    (sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) {
    
    			/* Found audio stream in this media definition */
    
    			/* Scan through the RTP payload types specified in a "m=" line: */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
    
    				if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
    					ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
    					return -1;
    				}
    
    				if (debug)
    
    					ast_verbose("Found RTP audio format %d\n", codec);
    
    				ast_rtp_set_m_type(newaudiortp, codec);
    
    		} else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
    		    (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) {
    
    			/* Scan through the RTP payload types specified in a "m=" line: */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
    
    				if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
    					ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
    					return -1;
    				}
    
    				if (debug)
    
    					ast_verbose("Found RTP video format %d\n", codec);
    
    				ast_rtp_set_m_type(newvideortp, codec);
    
    		} else if ((sscanf(m, "text %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
    		    (sscanf(m, "text %d RTP/AVP %n", &x, &len) == 1)) {
    			text = TRUE;
    
    			numberofmediastreams++;
    			tportno = x;
    			/* Scan through the RTP payload types specified in a "m=" line: */
    			for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
    				if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
    					ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
    					return -1;
    				}
    				if (debug)
    					ast_verbose("Found RTP text format %d\n", codec);
    				ast_rtp_set_m_type(newtextrtp, codec);
    			}
    
    		} else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1) || 
    		 (sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1) )) {
    
    				ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
    
    			
    			if (p->owner && p->lastinvite) {
    				p->t38.state = T38_PEER_REINVITE; /* T38 Offered in re-invite from remote party */
    
    				ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>" );
    
    			} else {
    				p->t38.state = T38_PEER_DIRECT; /* T38 Offered directly from peer in first invite */
    
    				ast_debug(2, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
    
    		} else 
    			ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %s\n", m);
    		if (numberofports > 1)
    			ast_log(LOG_WARNING, "SDP offered %d ports for media, not supported by Asterisk. Will try anyway...\n", numberofports);
    		
    
    		/* Check for Media-description-level-address for audio */
    
    		c = get_sdp_iterate(&destiterator, req, "c");
    		if (!ast_strlen_zero(c)) {
    			if (sscanf(c, "IN IP4 %256s", host) != 1) {
    				ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c);
    			} else {
    				/* XXX This could block for a long time, and block the main thread! XXX */
    
    					if ( !(hp = ast_gethostbyname(host, &audiohp))) {
    
    						ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in secondary c= line, '%s'\n", c);
    
    					if (!(vhp = ast_gethostbyname(host, &videohp))) {
    
    						ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c);
    
    					if (!(thp = ast_gethostbyname(host, &texthp))) {
    
    						ast_log(LOG_WARNING, "Unable to lookup RTP text host in secondary c= line, '%s'\n", c);
    
    	if (portno == -1 && vportno == -1 && udptlportno == -1  && tportno == -1)
    
    		/* No acceptable offer found in SDP  - we have no ports */
    		/* Do not change RTP or VRTP if this is a re-invite */
    		return -2;
    
    
    	if (numberofmediastreams > 3)
    		/* We have too many fax, audio and/or video and/or text media streams, fail this offer */
    
    	/* RTP addresses and ports for audio and video */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	sin.sin_family = AF_INET;
    
    	tsin.sin_family = AF_INET;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
    
    	if (vhp)
    		memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr));
    
    	if (thp)
    		memcpy(&tsin.sin_addr, thp->h_addr, sizeof(tsin.sin_addr));
    
    	/* Setup UDPTL port number */
    	if (p->udptl) {
    		if (udptlportno > 0) {
    			sin.sin_port = htons(udptlportno);
    			ast_udptl_set_peer(p->udptl, &sin);
    			if (debug)
    
    				ast_debug(1,"Peer T.38 UDPTL is at port %s:%d\n",ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
    
    		} else {
    			ast_udptl_stop(p->udptl);
    			if (debug)
    
    				ast_debug(1, "Peer doesn't provide T.38 UDPTL\n");
    
    		
    	if (p->rtp) {
    		if (portno > 0) {
    			sin.sin_port = htons(portno);
    			ast_rtp_set_peer(p->rtp, &sin);
    			if (debug)
    				ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
    		} else {
    			if (udptlportno > 0) {
    				if (debug)
    					ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session. Callid %s\n", p->callid);
    			} else {
    				ast_rtp_stop(p->rtp);
    				if (debug)
    					ast_verbose("Peer doesn't provide audio. Callid %s\n", p->callid);
    			}
    		}
    	}
    
    	/* Setup video port number, assumes we have audio */
    
    	if (vportno != -1)
    		vsin.sin_port = htons(vportno);
    
    
    	/* Setup text port number, assumes we have audio */
    	if (tportno != -1)
    		tsin.sin_port = htons(tportno);
    
    	/* Next, scan through each "a=xxxx:" line, noting each
    
    	 * specified RTP payload type (with corresponding MIME subtype):
    	 */
    
    	/* XXX This needs to be done per media stream, since it's media stream specific */
    
    	iterator = req->sdp_start;
    
    	while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
    
    		char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
    
    		if (option_debug > 1) {
    			int breakout = FALSE;
    		
    
    			/* If we're debugging, check for unsupported sdp options */
    
    			if (!strncasecmp(a, "rtcp:", (size_t) 5)) {
    
    				if (debug)
    					ast_verbose("Got unsupported a:rtcp in SDP offer \n");
    
    			} else if (!strncasecmp(a, "fmtp:", (size_t) 5)) {
    				/* Format parameters:  Not supported */
    				/* Note: This is used for codec parameters, like bitrate for
    					G722 and video formats for H263 and H264 
    					See RFC2327 for an example */
    				if (debug)
    					ast_verbose("Got unsupported a:fmtp in SDP offer \n");
    
    			} else if (!strncasecmp(a, "framerate:", (size_t) 10)) {
    				/* Video stuff:  Not supported */
    				if (debug)
    					ast_verbose("Got unsupported a:framerate in SDP offer \n");
    
    			} else if (!strncasecmp(a, "maxprate:", (size_t) 9)) {
    				/* Video stuff:  Not supported */
    				if (debug)
    					ast_verbose("Got unsupported a:maxprate in SDP offer \n");
    
    			} else if (!strncasecmp(a, "crypto:", (size_t) 7)) {
    				/* SRTP stuff, not yet supported */
    				if (debug)
    					ast_verbose("Got unsupported a:crypto in SDP offer \n");
    
    			if (breakout)	/* We have a match, skip to next header */
    				continue;
    		}
    		if (!strcasecmp(a, "sendonly")) {
    
    			if (sendonly == -1)
    				sendonly = 1;
    
    			if (sendonly == -1)
    				sendonly = 2;
    
    		}  else if (!strcasecmp(a, "sendrecv")) {
    
    			if (sendonly == -1)
    				sendonly = 0;
    
    		} else if (strlen(a) > 5 && !strncasecmp(a, "ptime", 5)) {
    			char *tmp = strrchr(a, ':');
    			long int framing = 0;
    			if (tmp) {
    				tmp++;
    				framing = strtol(tmp, NULL, 10);
    				if (framing == LONG_MIN || framing == LONG_MAX) {
    					framing = 0;
    
    					ast_debug(1, "Can't read framing from SDP: %s\n", a);
    
    				}
    			}
    			if (framing && last_rtpmap_codec) {
    
    				if (p->autoframing) {
    
    					struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp);
    					int codec_n;
    					int format = 0;
    					for (codec_n = 0; codec_n < last_rtpmap_codec; codec_n++) {
    						format = ast_rtp_codec_getformat(found_rtpmap_codecs[codec_n]);
    						if (!format)	/* non-codec or not found */
    							continue;
    
    						ast_debug(1, "Setting framing for %d to %ld\n", format, framing);
    
    						ast_codec_pref_setsize(pref, format, framing);
    					}
    					ast_rtp_codec_setpref(p->rtp, pref);
    				}
    			}
    			memset(&found_rtpmap_codecs, 0, sizeof(found_rtpmap_codecs));
    			last_rtpmap_codec = 0;
    
    		} else if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) == 2) {
    			/* We have a rtpmap to handle */
    
    			/* Note: should really look at the 'freq' and '#chans' params too */
    
    			/* Note: This should all be done in the context of the m= above */