Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	ast_mutex_init(&p->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 = 500;	/* Default SIP retransmission timer T1 (RFC 3261) */
    
    		if (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);
    
    
    	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_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_log(LOG_WARNING, "Unable to create RTP audio %s session: %s\n",
    				ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video" : "", strerror(errno));
    
    			if (p->chanvars) {
    
    				ast_variables_destroy(p->chanvars);
    				p->chanvars = NULL;
    			}
    			free(p);
    			return NULL;
    
    		ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INFO);
    
    			ast_rtp_setdtmf(p->vrtp, 0);
    		}
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (p->udptl)
    
    			ast_udptl_settos(p->udptl, global_tos_audio);
    
    		p->rtptimeout = global_rtptimeout;
    		p->rtpholdtimeout = global_rtpholdtimeout;
    		p->rtpkeepalive = global_rtpkeepalive;
    
    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);
    
    		natflags = ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE;
    
    			ast_rtp_setnat(p->rtp, natflags);
    
    			ast_rtp_setnat(p->vrtp, natflags);
    
    		if (p->udptl)
    			ast_udptl_setnat(p->udptl, natflags);
    
    		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;
    
    	if (p->udptl) {
    		p->t38.capability = global_t38_capability;
    		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_FEC)
    			p->t38.capability |= T38FAX_UDP_EC_FEC;
    		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;
    		p->t38.jointcapability = p->t38.capability;
    	}
    
    	ast_string_field_set(p, context, default_context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->next = iflist;
    	iflist = p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_debug)
    
    		ast_log(LOG_DEBUG, "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 Connect incoming SIP message to current dialog or create new dialog structure
    	Called by handle_request, sipsock_read */
    
    static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_pvt *p;
    
    	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");
    
    	if (!callid || !to || !from || !cseq)		/* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
    		return NULL;	/* Invalid packet */
    
    
    	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)))
    			ast_set_flag(req, SIP_PKT_WITH_TOTAG);	/* Used in handle_request/response */
    		gettag(req, "From", fromtag, sizeof(fromtag));
    
    
    		tag = (req->method == SIP_RESPONSE) ? totag : fromtag;
    
    
    		if (option_debug > 4 )
    			ast_log(LOG_DEBUG, "= Looking for  Call ID: %s (Checking %s) --From tag %s --To-tag %s  \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (p = iflist; p; p = p->next) {
    		/* In pedantic, we do not want packets with bad syntax to be connected to a PVT */
    
    		int found = FALSE;
    
    		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))) ;
    
    
    		if (option_debug > 4)
    			ast_log(LOG_DEBUG, "= %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 && option_debug > 4)
    				ast_log(LOG_DEBUG, "= 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 */
    
    			ast_mutex_lock(&p->lock);
    			ast_mutex_unlock(&iflock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return p;
    		}
    	}
    
    	/* Allocate new call */
    	if ((p = sip_alloc(callid, sin, 1, intended_method)))
    
    	return p;
    
    /*! \brief Parse register=> line in sip.conf and add to registry */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int sip_register(char *value, int lineno)
    {
    	struct sip_registry *reg;
    
    	char *username=NULL, *hostname=NULL, *secret=NULL, *authuser=NULL;
    	char *porta=NULL;
    	char *contact=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *stringp=NULL;
    	
    	if (!value)
    		return -1;
    
    	ast_copy_string(copy, value, sizeof(copy));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	stringp=copy;
    
    	username = stringp;
    	hostname = strrchr(stringp, '@');
    
    	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;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	username = strsep(&stringp, ":");
    
    	if (username) {
    		secret = strsep(&stringp, ":");
    		if (secret) 
    			authuser = strsep(&stringp, ":");
    	}
    
    	stringp = hostname;
    	hostname = strsep(&stringp, "/");
    
    	if (hostname) 
    		contact = strsep(&stringp, "/");
    
    		contact = "s";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	stringp=hostname;
    	hostname = strsep(&stringp, ":");
    
    	porta = strsep(&stringp, ":");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	if (porta && !atoi(porta)) {
    		ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
    		return -1;
    	}
    
    	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, contact, contact);
    
    	if (username)
    
    		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;
    	reg->portno = porta ? atoi(porta) : 0;
    
    	reg->callid_valid = FALSE;
    
    	ASTOBJ_CONTAINER_LINK(&regl, reg);	/* Add the new registry entry to the list */
    
    	ASTOBJ_UNREF(reg,sip_registry_destroy);
    
    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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Divide fields by NULL's */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int f = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	c = req->data;
    
    	/* First header starts immediately */
    	req->header[f] = c;
    	while(*c) {
    		if (*c == '\n') {
    			/* We've got a new header */
    			*c = 0;
    
    
    				ast_log(LOG_DEBUG, "Header %d: %s (%d)\n", f, req->header[f], (int) strlen(req->header[f]));
    
    			if (ast_strlen_zero(req->header[f])) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Line by itself means we're now in content */
    				c++;
    				break;
    			}
    			if (f >= SIP_MAX_HEADERS - 1) {
    
    				ast_log(LOG_WARNING, "Too many SIP headers. Ignoring.\n");
    
    			} else
    				f++;
    			req->header[f] = c + 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else if (*c == '\r') {
    			/* Ignore but eliminate \r's */
    			*c = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		c++;
    	}
    	/* Check for last header */
    
    	if (!ast_strlen_zero(req->header[f])) {
    
    			ast_log(LOG_DEBUG, "Header %d: %s (%d)\n", f, req->header[f], (int) strlen(req->header[f]));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		f++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req->headers = f;
    	/* Now we process any mime content */
    	f = 0;
    	req->line[f] = c;
    	while(*c) {
    		if (*c == '\n') {
    			/* We've got a new line */
    			*c = 0;
    
    				ast_log(LOG_DEBUG, "Line: %s (%d)\n", req->line[f], (int) strlen(req->line[f]));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (f >= SIP_MAX_LINES - 1) {
    
    				ast_log(LOG_WARNING, "Too many SDP lines. Ignoring.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else
    				f++;
    			req->line[f] = c + 1;
    		} else if (*c == '\r') {
    			/* Ignore and eliminate \r's */
    			*c = 0;
    		}
    		c++;
    	}
    	/* Check for last line */
    
    	if (!ast_strlen_zero(req->line[f])) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		f++;
    	req->lines = f;
    	if (*c) 
    		ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%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;
    
    	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 1;
    	}
    
    	/* 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=")))
    		return 0;
    
    	search += 10;
    
    	if (ast_strlen_zero(search))
    		return 0;
    
    	/* make a duplicate of the string, with two extra characters
    	   at the beginning */
    	boundary = ast_strdupa(search - 2);
    	boundary[0] = boundary[1] = '-';
    
    	/* search for the boundary marker, but stop when there are not enough
    	   lines left for it, the Content-Type header and at least one line of
    	   body */
    	for (x = 0; x < (req->lines - 2); x++) {
    		if (!strncasecmp(req->line[x], boundary, strlen(boundary)) &&
    		    !strcasecmp(req->line[x + 1], "Content-Type: application/sdp")) {
    			req->sdp_start = x + 2;
    			/* search for the end of the body part */
    			for ( ; x < req->lines; x++) {
    				if (!strncasecmp(req->line[x], boundary, strlen(boundary)))
    					break;
    			}
    			req->sdp_end = x;
    			return 1;
    		}
    	}
    
    	return 0;
    }
    
    
    /*! \brief Process SIP SDP offer, select formats and activate RTP channels
    	If offer is rejected, we will not change any properties of the call
    */
    
    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 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;
    	struct sockaddr_in sin;		/*!< media socket address */
    	struct sockaddr_in vsin;	/*!< Video 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 ast_hostent audiohp;
    	struct ast_hostent videohp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int codec;
    
    	int iterator;
    
    	struct ast_channel *bridgepeer = NULL;
    
    	struct ast_rtp *newaudiortp, *newvideortp;	/* Buffers for codec handling */
    
    	int newjointcapability;				/* Negotiated capability */
    	int newpeercapability;
    	int newnoncodeccapability;
    	int numberofmediastreams = 0;
    	int debug = sip_debug_test_pvt(p);
    		
    
    	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());
    
    
    	newvideortp = alloca(ast_rtp_alloc_size());
    	memset(newvideortp, 0, ast_rtp_alloc_size());
    
    	/* 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 */
    	
    
    	iterator = req->sdp_start;
    
    	ast_set_flag(&p->flags[0], SIP_NOVIDEO);	
    
    	while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') {
    
    		int x;
    		int audio = 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)) {
    			/* If it is not audio - is it video ? */
    
    			ast_clear_flag(&p->flags[0], SIP_NOVIDEO);	
    
    			/* 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);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		} else if (p->udptl && ((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 */
    				if (option_debug > 1)
    					ast_log(LOG_DEBUG, "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 */
    				if (option_debug > 1)
    					ast_log(LOG_DEBUG, "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 (audio) {
    					if ( !(hp = ast_gethostbyname(host, &audiohp)))
    						ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in secondary c= line, '%s'\n", c);
    				} else if (!(vhp = ast_gethostbyname(host, &videohp)))
    					ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c);
    
    	if (portno == -1 && vportno == -1 && udptlportno == -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 > 2)
    
    		/* We have too many fax, audio and/or video 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;
    
    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 (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 {
    			ast_rtp_stop(p->rtp);
    			if (debug)
    				ast_verbose("Peer doesn't provide audio\n");
    		}
    	}
    
    	/* Setup video port number */
    
    	if (vportno != -1)
    		vsin.sin_port = htons(vportno);
    
    	/* 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_log(LOG_DEBUG,"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_log(LOG_DEBUG, "Peer doesn't provide T.38 UDPTL\n");
    		}
    	}
    
    
    	/* Next, scan through each "a=rtpmap:" 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");
    
    			} else if (!strncasecmp(a, "ptime:", (size_t) 6)) {
    				if (debug)
    					ast_verbose("Got unsupported a:ptime in SDP offer \n");
    
    			if (breakout)	/* We have a match, skip to next header */
    				continue;
    		}
    		if (!strcasecmp(a, "sendonly")) {
    			sendonly = 1;
    			continue;
    
    		} else if (!strcasecmp(a, "inactive")) {
    			sendonly = 2;
    			continue;
    
    		}  else if (!strcasecmp(a, "sendrecv")) {
    			sendonly = 0;
    			continue;
    
    		} else if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) 
    			continue;
    		/* We have a rtpmap to handle */
    
    			ast_verbose("Found description format %s for ID %d\n", mimeSubtype, codec);
    
    
    		/* Note: should really look at the 'freq' and '#chans' params too */
    
    		ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype,
    					ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0);
    
    			ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0);
    
    	
    	if (udptlportno != -1) {
    		int found = 0, x;
    		
    		old = 0;
    		
    		/* Scan trough the a= lines for T38 attributes and set apropriate fileds */
    		iterator = req->sdp_start;
    		while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
    			if ((sscanf(a, "T38FaxMaxBuffer:%d", &x) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"MaxBufferSize:%d\n",x);
    			}
    			if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"T38MaxBitRate: %d\n",x);
    				switch (x) {
    				case 14400:
    					peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 12000:
    					peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 9600:
    					peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 7200:
    					peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 4800:
    					peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
    					break;
    				case 2400:
    					peert38capability |= T38FAX_RATE_2400;
    					break;
    				}
    			}
    			if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"FaxVersion: %d\n",x);
    				if (x == 0)
    					peert38capability |= T38FAX_VERSION_0;
    				else if (x == 1)
    					peert38capability |= T38FAX_VERSION_1;
    			}
    			if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"FaxMaxDatagram: %d\n",x);
    				ast_udptl_set_far_max_datagram(p->udptl, x);
    				ast_udptl_set_local_max_datagram(p->udptl, x);
    			}
    			if ((sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"FillBitRemoval: %d\n",x);
    				if (x == 1)
    					peert38capability |= T38FAX_FILL_BIT_REMOVAL;
    			}
    			if ((sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"Transcoding MMR: %d\n",x);
    				if (x == 1)
    					peert38capability |= T38FAX_TRANSCODING_MMR;
    			}
    			if ((sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"Transcoding JBIG: %d\n",x);
    				if (x == 1)
    					peert38capability |= T38FAX_TRANSCODING_JBIG;
    			}
    			if ((sscanf(a, "T38FaxRateManagement:%s", s) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"RateMangement: %s\n", s);
    				if (!strcasecmp(s, "localTCF"))
    					peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
    				else if (!strcasecmp(s, "transferredTCF"))
    					peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
    			}
    			if ((sscanf(a, "T38FaxUdpEC:%s", s) == 1)) {
    				found = 1;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG,"UDP EC: %s\n", s);
    				if (!strcasecmp(s, "t38UDPRedundancy")) {
    					peert38capability |= T38FAX_UDP_EC_REDUNDANCY;
    					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
    				} else if (!strcasecmp(s, "t38UDPFEC")) {
    					peert38capability |= T38FAX_UDP_EC_FEC;
    					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
    				} else {
    					peert38capability |= T38FAX_UDP_EC_NONE;
    					ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
    				}
    			}
    		}
    		if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */
    			p->t38.peercapability = peert38capability;
    			p->t38.jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */
    			peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400);
    			p->t38.jointcapability |= (peert38capability & p->t38.capability); /* Put the lower of our's and peer's speed */
    		}
    		if (debug)
    			ast_log(LOG_DEBUG,"Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n",
    				p->t38.capability,
    				p->t38.peercapability,
    				p->t38.jointcapability);
    	} else {
    		p->t38.state = T38_DISABLED;
    
    			ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
    	}
    
    
    	/* Now gather all of the codecs that we are asked for: */
    
    	ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability);
    	ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability);
    
    
    	newjointcapability = p->capability & (peercapability | vpeercapability);
    	newpeercapability = (peercapability | vpeercapability);
    	newnoncodeccapability = noncodeccapability & peernoncodeccapability;
    		
    		
    	if (debug) {
    		/* shame on whoever coded this.... */
    		char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ];
    
    		ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n",
    
    			    ast_getformatname_multiple(s1, BUFSIZ, p->capability),
    			    ast_getformatname_multiple(s2, BUFSIZ, newpeercapability),
    			    ast_getformatname_multiple(s3, BUFSIZ, vpeercapability),
    			    ast_getformatname_multiple(s4, BUFSIZ, newjointcapability));
    
    
    		ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
    
    			    ast_rtp_lookup_mime_multiple(s1, BUFSIZ, noncodeccapability, 0, 0),
    			    ast_rtp_lookup_mime_multiple(s2, BUFSIZ, peernoncodeccapability, 0, 0),
    			    ast_rtp_lookup_mime_multiple(s3, BUFSIZ, newnoncodeccapability, 0, 0));
    
    	}
    	if (!newjointcapability) {
    		ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
    		/* Do NOT Change current setting */
    		return -1;
    	}
    
    	/* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
    		they are acceptable */
    	p->jointcapability = newjointcapability;	/* Our joint codec profile for this call */
    	p->peercapability = newpeercapability;		/* The other sides capability in latest offer */
    	p->noncodeccapability = newnoncodeccapability;	/* DTMF capabilities */
    
    
    	ast_rtp_pt_copy(p->rtp, newaudiortp);
    	if (p->vrtp)
    		ast_rtp_pt_copy(p->vrtp, newvideortp);
    
    	if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
    		ast_clear_flag(&p->flags[0], SIP_DTMF);
    
    		if (newnoncodeccapability & AST_RTP_DTMF) {
    
    			/* XXX Would it be reasonable to drop the DSP at this point? XXX */
    
    			ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
    
    			ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
    
    	/* Setup audio port number */
    	if (p->rtp && sin.sin_port) {
    		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));
    
    	}
    
    	/* Setup video port number */
    	if (p->vrtp && vsin.sin_port) {
    		ast_rtp_set_peer(p->vrtp, &vsin);
    		if (debug) 
    
    			ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port));
    
    	}
    
    	/* Ok, we're going with this offer */
    	if (option_debug > 1) {
    		char buf[BUFSIZ];
    		ast_log(LOG_DEBUG, "We're settling with these formats: %s\n", ast_getformatname_multiple(buf, BUFSIZ, p->jointcapability));
    
    	if (!p->owner) 	/* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */
    
    	if (option_debug > 3)
    		ast_log(LOG_DEBUG, "We have an owner, now see if we need to change this call\n");
    
    	if (!(p->owner->nativeformats & p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
    
    		if (debug) {
    			char s1[BUFSIZ], s2[BUFSIZ];
    			ast_log(LOG_DEBUG, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n", 
    				ast_getformatname_multiple(s1, BUFSIZ, p->jointcapability),
    				ast_getformatname_multiple(s2, BUFSIZ, p->owner->nativeformats));
    		}
    
    		p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability);
    
    		ast_set_read_format(p->owner, p->owner->readformat);
    		ast_set_write_format(p->owner, p->owner->writeformat);
    	}
    
    	
    	bridgepeer = ast_bridged_channel(p->owner);
    
    	/* Turn on/off music on hold if we are holding/unholding */
    
    	if ((bridgepeer = ast_bridged_channel(p->owner))) {
    		if (sin.sin_addr.s_addr && !sendonly) {
    
    			ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
    
    			/* Activate a re-invite */
    			ast_queue_frame(p->owner, &ast_null_frame);
    		} else if (!sin.sin_addr.s_addr || sendonly) {
    
    			ast_queue_control_data(p->owner, AST_CONTROL_HOLD, 
    
    					       S_OR(p->mohsuggest, NULL),
    					       !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
    
    			if (sendonly)
    				ast_rtp_stop(p->rtp);
    			/* RTCP needs to go ahead, even if we're on hold!!! */
    			/* Activate a re-invite */
    			ast_queue_frame(p->owner, &ast_null_frame);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    
    	/* Manager Hold and Unhold events must be generated, if necessary */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (sin.sin_addr.s_addr && !sendonly) {
    
    		if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
    
    			append_history(p, "Unhold", "%s", req->data);
    			if (global_callevents)
    				manager_event(EVENT_FLAG_CALL, "Unhold",
    					"Channel: %s\r\n"
    					"Uniqueid: %s\r\n",
    					p->owner->name, 
    					p->owner->uniqueid);
    
    		ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD);	/* Clear both flags */
    	} else if (!sin.sin_addr.s_addr || sendonly ) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    		append_history(p, "Hold", "%s", req->data);
    
    		if (global_callevents && !ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    				"Uniqueid: %s\r\n",
    
    		if (sendonly == 1)	/* One directional hold (sendonly/recvonly) */
    
    			ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR);
    		else if (sendonly == 2)	/* Inactive stream */
    			ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE);
    
    /*! \brief Add header to SIP message */
    
    static int add_header(struct sip_request *req, const char *var, const char *value)
    
    	int maxlen = sizeof(req->data) - 4 - req->len; /* 4 bytes are for two \r\n ? */
    
    
    	if (req->headers == SIP_MAX_HEADERS) {
    		ast_log(LOG_WARNING, "Out of SIP header space\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (req->lines) {
    		ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
    		return -1;
    	}
    
    	if (maxlen <= 0) {
    
    		ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value);
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req->header[req->headers] = req->data + req->len;