Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	if (compactheaders)
    		var = find_alias(var, var);
    
    	snprintf(req->header[req->headers], maxlen, "%s: %s\r\n", var, value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req->len += strlen(req->header[req->headers]);
    
    	req->headers++;
    
    	if (req->headers < SIP_MAX_HEADERS)
    		req->headers++;
    	else
    		ast_log(LOG_WARNING, "Out of SIP header space... Will generate broken SIP message\n");
    
    /*! \brief Add 'Content-Length' header to SIP message */
    
    static int add_header_contentLength(struct sip_request *req, int len)
    {
    	char clen[10];
    
    	snprintf(clen, sizeof(clen), "%d", len);
    	return add_header(req, "Content-Length", clen);
    }
    
    
    /*! \brief Add content (not header) to SIP message */
    
    static int add_line(struct sip_request *req, const char *line)
    
    	if (req->lines == SIP_MAX_LINES)  {
    		ast_log(LOG_WARNING, "Out of SIP line space\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!req->lines) {
    		/* Add extra empty return */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		snprintf(req->data + req->len, sizeof(req->data) - req->len, "\r\n");
    		req->len += strlen(req->data + req->len);
    
    	if (req->len >= sizeof(req->data) - 4) {
    		ast_log(LOG_WARNING, "Out of space, can't add anymore\n");
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	req->line[req->lines] = req->data + req->len;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line);
    	req->len += strlen(req->line[req->lines]);
    
    	req->lines++;
    
    /*! \brief Copy one header field from one request to another */
    
    static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field)
    
    	const char *tmp = get_header(orig, field);
    
    	if (!ast_strlen_zero(tmp)) /* Add what we're responding to */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return add_header(req, field, tmp);
    	ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field);
    	return -1;
    }
    
    
    /*! \brief Copy all headers from one request to another */
    
    static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int start = 0;
    	int copied = 0;
    	for (;;) {
    
    		const char *tmp = __get_header(orig, field, &start);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    		/* Add what we're responding to */
    		add_header(req, field, tmp);
    		copied++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	return copied ? 0 : -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*! \brief Copy SIP VIA Headers from the request to the response
    \note	If the client indicates that it wishes to know the port we received from,
    
    	it adds ;rport without an argument to the topmost via header. We need to
    	add the port number (from our point of view) to that parameter.
    	We always add ;received=<ip address> to the topmost via header.
    	Received: RFC 3261, rport RFC 3581 */
    
    static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field)
    
    {
    	int copied = 0;
    
    	for (;;) {
    
    		const char *oh = __get_header(orig, field, &start);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (!copied) {	/* Only check for empty rport in topmost via header */
    			char *rport;
    
    			/* Find ;rport;  (empty request) */
    			rport = strstr(oh, ";rport");
    			if (rport && *(rport+6) == '=') 
    				rport = NULL;		/* We already have a parameter to rport */
    
    			if (rport && ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_ALWAYS) {
    				/* We need to add received port - rport */
    				char tmp[256], *end;
    
    				ast_copy_string(tmp, oh, sizeof(tmp));
    
    				rport = strstr(tmp, ";rport");
    
    				if (rport) {
    					end = strchr(rport + 1, ';');
    					if (end)
    						memmove(rport, end, strlen(end) + 1);
    					else
    						*rport = '\0';
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    
    				/* Add rport to first VIA header if requested */
    				/* Whoo hoo!  Now we can indicate port address translation too!  Just
    				   another RFC (RFC3581). I'll leave the original comments in for
    				   posterity.  */
    				snprintf(new, sizeof(new), "%s;received=%s;rport=%d",
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    					ntohs(p->recv.sin_port));
    			} else {
    				/* We should *always* add a received to the topmost via */
    				snprintf(new, sizeof(new), "%s;received=%s",
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			}
    			oh = new;	/* the header to copy */
    		}  /* else add the following via headers untouched */
    		add_header(req, field, oh);
    		copied++;
    
    	}
    	if (!copied) {
    
    		ast_log(LOG_NOTICE, "No header field '%s' present to copy\n", field);
    
    		return -1;
    	}
    	return 0;
    }
    
    /*! \brief Add route header into request per learned route */
    
    static void add_route(struct sip_request *req, struct sip_route *route)
    {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (!route)
    		return;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (;route ; route = route->next) {
    
    		n = strlen(route->hop);
    
    		if (rem < n+3) /* we need room for ",<route>" */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			break;
    
    		if (p != r) {	/* add a separator after fist route */
    
    			*p++ = ',';
    			--rem;
    		}
    		*p++ = '<';
    
    		ast_copy_string(p, route->hop, rem); /* cannot fail */
    		p += n;
    
    		*p++ = '>';
    		rem -= (n+2);
    	}
    	*p = '\0';
    	add_header(req, "Route", r);
    }
    
    
    /*! \brief Set destination from SIP URI */
    
    static void set_destination(struct sip_pvt *p, char *uri)
    {
    
    	char *h, *maddr, hostname[256];
    
    	int port, hn;
    	struct hostent *hp;
    
    	struct ast_hostent ahp;
    
    	int debug=sip_debug_test_pvt(p);
    
    
    	/* Parse uri to h (host) and port - uri is already just the part inside the <> */
    	/* general form we are expecting is sip[s]:username[:password]@host[:port][;...] */
    
    
    	if (debug)
    
    		ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Find and parse hostname */
    
    	h = strchr(uri, '@');
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (h)
    		++h;
    	else {
    		h = uri;
    		if (strncmp(h, "sip:", 4) == 0)
    			h += 4;
    		else if (strncmp(h, "sips:", 5) == 0)
    			h += 5;
    
    	if (hn > sizeof(hostname)) 
    		hn = sizeof(hostname);
    
    	/* XXX bug here if string has been trimmed to sizeof(hostname) */
    
    	/* Is "port" present? if not default to DEFAULT_SIP_PORT */
    
    	if (*h == ':') {
    		/* Parse port */
    		++h;
    		port = strtol(h, &h, 10);
    	}
    	else
    
    		port = DEFAULT_SIP_PORT;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Got the hostname:port - but maybe there's a "maddr=" to override address? */
    	maddr = strstr(h, "maddr=");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		maddr += 6;
    
    		if (hn > sizeof(hostname))
    			hn = sizeof(hostname);
    
    		ast_copy_string(hostname, maddr, hn);
    
    	hp = ast_gethostbyname(hostname, &ahp);
    
    	if (hp == NULL)  {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Can't find address for host '%s'\n", hostname);
    
    		return;
    	}
    	p->sa.sin_family = AF_INET;
    	memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr));
    	p->sa.sin_port = htons(port);
    
    	if (debug)
    
    		ast_verbose("set_destination: set destination to %s, port %d\n", ast_inet_ntoa(p->sa.sin_addr), port);
    
    /*! \brief Initialize SIP response, based on SIP request */
    
    static int init_resp(struct sip_request *resp, const char *msg)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Initialize a response */
    
    	memset(resp, 0, sizeof(*resp));
    	resp->method = SIP_RESPONSE;
    	resp->header[0] = resp->data;
    	snprintf(resp->header[0], sizeof(resp->data), "SIP/2.0 %s\r\n", msg);
    	resp->len = strlen(resp->header[0]);
    	resp->headers++;
    
    /*! \brief Initialize SIP request */
    
    static int init_req(struct sip_request *req, int sipmethod, const char *recip)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* Initialize a request */
    	memset(req, 0, sizeof(*req));
            req->method = sipmethod;
    	req->header[0] = req->data;
    	snprintf(req->header[0], sizeof(req->data), "%s %s SIP/2.0\r\n", sip_methods[sipmethod].text, recip);
    	req->len = strlen(req->header[0]);
    
    	req->headers++;
    
    /*! \brief Prepare SIP response packet */
    
    static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req)
    
    	copy_via_headers(p, resp, req, "Via");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (msg[0] == '2')
    		copy_all_header(resp, req, "Record-Route");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	copy_header(resp, req, "From");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ot = get_header(req, "To");
    
    	if (!strcasestr(ot, "tag=") && strncmp(msg, "100", 3)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Add the proper tag if we don't have it already.  If they have specified
    		   their tag, use it.  Otherwise, use our own tag */
    
    		if (!ast_strlen_zero(p->theirtag) && ast_test_flag(&p->flags[0], SIP_OUTGOING))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
    
    		else if (p->tag && !ast_test_flag(&p->flags[0], SIP_OUTGOING))
    
    			snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ot = newto;
    	}
    	add_header(resp, "To", ot);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	copy_header(resp, req, "Call-ID");
    	copy_header(resp, req, "CSeq");
    
    	add_header(resp, "User-Agent", global_useragent);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(resp, "Allow", ALLOWED_METHODS);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	add_header(resp, "Supported", SUPPORTED_EXTENSIONS);
    
    	if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* For registration responses, we also need expiry and
    
    Mark Spencer's avatar
    Mark Spencer committed
    		   contact info */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		char tmp[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    		snprintf(tmp, sizeof(tmp), "%d", p->expiry);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		add_header(resp, "Expires", tmp);
    
    		if (p->expiry) {	/* Only add contact if we have an expiry time */
    			char contact[256];
    			snprintf(contact, sizeof(contact), "%s;expires=%d", p->our_contact, p->expiry);
    			add_header(resp, "Contact", contact);	/* Not when we unregister */
    		}
    
    		add_header(resp, "Contact", p->our_contact);
    
    /*! \brief Initialize a SIP request message (not the initial one in a dialog) */
    
    static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request *orig = &p->initreq;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char tmp[80];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char newto[256];
    
    	int is_strict = FALSE;		/*!< Strict routing flag */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	memset(req, 0, sizeof(struct sip_request));
    
    	snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		p->ocseq++;
    
    
    	/* Check for strict or loose router */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (p->route && !ast_strlen_zero(p->route->hop) && strstr(p->route->hop,";lr") == NULL) {
    
    		is_strict = TRUE;
    
    Olle Johansson's avatar
    Olle Johansson committed
    		if (sipdebug)
    			ast_log(LOG_DEBUG, "Strict routing enforced for session %s\n", p->callid);
    	}
    
    	if (sipmethod == SIP_CANCEL)
    
    		c = p->initreq.rlPart2;	/* Use original URI */
    
    	else if (sipmethod == SIP_ACK) {
    
    		/* Use URI from Contact: in 200 OK (if INVITE) 
    		(we only have the contacturi on INVITEs) */
    		if (!ast_strlen_zero(p->okcontacturi))
    
    			c = is_strict ? p->route->hop : p->okcontacturi;
     		else
     			c = p->initreq.rlPart2;
    
    	} else if (!ast_strlen_zero(p->okcontacturi)) 
    
    Olle Johansson's avatar
    Olle Johansson committed
    		c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */
    
    	else if (!ast_strlen_zero(p->uri)) 
    
    		c = p->uri;
    
    		/* We have no URI, use To: or From:  header as URI (depending on direction) */
    
    		ast_copy_string(stripped, get_header(orig, (ast_test_flag(&p->flags[0], SIP_OUTGOING)) ? "To" : "From"),
    
    		n = get_in_brackets(stripped);
    		c = strsep(&n, ";");	/* trim ; and beyond */
    
    	init_req(req, sipmethod, c);
    
    	snprintf(tmp, sizeof(tmp), "%d %s", seqno, sip_methods[sipmethod].text);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(req, "Via", p->via);
    
    	if (p->route) {
    		set_destination(p, p->route->hop);
    
    		add_route(req, is_strict ? p->route->next : p->route);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	ot = get_header(orig, "To");
    	of = get_header(orig, "From");
    
    	/* Add tag *unless* this is a CANCEL, in which case we need to send it exactly
    	   as our original request, including tag (or presumably lack thereof) */
    
    	if (!strcasestr(ot, "tag=") && sipmethod != SIP_CANCEL) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Add the proper tag if we don't have it already.  If they have specified
    		   their tag, use it.  Otherwise, use our own tag */
    
    		if (ast_test_flag(&p->flags[0], SIP_OUTGOING) && !ast_strlen_zero(p->theirtag))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
    
    		else if (!ast_test_flag(&p->flags[0], SIP_OUTGOING))
    
    			snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
    
    		else
    			snprintf(newto, sizeof(newto), "%s", ot);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ot = newto;
    	}
    
    
    	if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		add_header(req, "From", of);
    		add_header(req, "To", ot);
    	} else {
    		add_header(req, "From", ot);
    		add_header(req, "To", of);
    	}
    
    	add_header(req, "Contact", p->our_contact);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	copy_header(req, orig, "Call-ID");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(req, "CSeq", tmp);
    
    
    	add_header(req, "User-Agent", global_useragent);
    
    	add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
    
    	if (!ast_strlen_zero(p->rpid))
    
    /*! \brief Base transmit response function */
    
    static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int seqno = 0;
    
    	if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	respprep(&resp, p, msg, req);
    
    	add_header_contentLength(&resp, 0);
    
    	/* If we are cancelling an incoming invite for some reason, add information
    		about the reason why we are doing this in clear text */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (p->method == SIP_INVITE && msg[0] != '1' && p->owner && p->owner->hangupcause) {
    		char buf[10];
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
    
    Olle Johansson's avatar
    Olle Johansson committed
    		snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
    		add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return send_response(p, &resp, reliable, seqno);
    }
    
    
    /*! \brief Transmit response, no retransmits */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return __transmit_response(p, msg, req, XMIT_UNRELIABLE);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*! \brief Transmit response, no retransmits */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported) 
    
    {
    	struct sip_request resp;
    	respprep(&resp, p, msg, req);
    	append_date(&resp);
    	add_header(&resp, "Unsupported", unsupported);
    
    	return send_response(p, &resp, XMIT_UNRELIABLE, 0);
    
    /*! \brief Transmit response, Make sure you get an ACK
    	This is only used for responses to INVITEs, where we need to make sure we get an ACK
    */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return __transmit_response(p, msg, req, XMIT_CRITICAL);
    
    /*! \brief Append date to SIP message */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void append_date(struct sip_request *req)
    {
    	char tmpdat[256];
    	struct tm tm;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	gmtime_r(&t, &tm);
    	strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T GMT", &tm);
    	add_header(req, "Date", tmpdat);
    }
    
    
    /*! \brief Append date and content length before transmitting response */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    	respprep(&resp, p, msg, req);
    	append_date(&resp);
    
    	add_header_contentLength(&resp, 0);
    
    	return send_response(p, &resp, XMIT_UNRELIABLE, 0);
    
    /*! \brief Append Accept header, content length before transmitting response */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    	respprep(&resp, p, msg, req);
    	add_header(&resp, "Accept", "application/sdp");
    
    	add_header_contentLength(&resp, 0);
    
    	return send_response(p, &resp, reliable, 0);
    
    /*! \brief Respond with authorization request */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *randdata, enum xmittype reliable, const char *header, int stale)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_request resp;
    
    	int seqno = 0;
    
    	if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) {
    
    		ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
    		return -1;
    	}
    
    	/* Stale means that they sent us correct authentication, but 
    	   based it on an old challenge (nonce) */
    
    	snprintf(tmp, sizeof(tmp), "Digest algorithm=MD5, realm=\"%s\", nonce=\"%s\"%s", global_realm, randdata, stale ? ", stale=true" : "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	respprep(&resp, p, msg, req);
    
    	add_header_contentLength(&resp, 0);
    
    	return send_response(p, &resp, reliable, seqno);
    
    /*! \brief Add text body to SIP message */
    
    static int add_text(struct sip_request *req, const char *text)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* XXX Convert \n's to \r\n's XXX */
    	add_header(req, "Content-Type", "text/plain");
    
    	add_header_contentLength(req, strlen(text));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_line(req, text);
    	return 0;
    }
    
    
    /*! \brief Add DTMF INFO tone to sip message */
    
    /* Always adds default duration 250 ms, regardless of what came in over the line */
    
    static int add_digit(struct sip_request *req, char digit)
    {
    	char tmp[256];
    
    	snprintf(tmp, sizeof(tmp), "Signal=%c\r\nDuration=250\r\n", digit);
    	add_header(req, "Content-Type", "application/dtmf-relay");
    
    	add_header_contentLength(req, strlen(tmp));
    
    	add_line(req, tmp);
    	return 0;
    }
    
    
    /*! \brief add XML encoded media control with update */
    /*! \note XML: The only way to turn 0 bits of information into a few hundred. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int add_vidupdate(struct sip_request *req)
    {
    	const char *xml_is_a_huge_waste_of_space =
    		"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n"
    		" <media_control>\r\n"
    		"  <vc_primitive>\r\n"
    		"   <to_encoder>\r\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    		"    </picture_fast_update>\r\n"
    		"   </to_encoder>\r\n"
    		"  </vc_primitive>\r\n"
    		" </media_control>\r\n";
    	add_header(req, "Content-Type", "application/media_control+xml");
    	add_header_contentLength(req, strlen(xml_is_a_huge_waste_of_space));
    	add_line(req, xml_is_a_huge_waste_of_space);
    	return 0;
    }
    
    
    /*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
    
    static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
    			     char **m_buf, size_t *m_size, char **a_buf, size_t *a_size,
    			     int debug)
    {
    	int rtp_code;
    
    	if (debug)
    		ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec));
    	if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1)
    		return;
    
    	ast_build_string(m_buf, m_size, " %d", rtp_code);
    	ast_build_string(a_buf, a_size, "a=rtpmap:%d %s/%d\r\n", rtp_code,
    
    			 ast_rtp_lookup_mime_subtype(1, codec,
    						     ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0),
    
    	if (codec == AST_FORMAT_G729A) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    		/* Indicate that we don't support VAD (G.729 annex B) */
    
    		ast_build_string(a_buf, a_size, "a=fmtp:%d annexb=no\r\n", rtp_code);
    
    	} else if (codec == AST_FORMAT_ILBC) {
    		/* Add information about us using only 20 ms packetization */
    		ast_build_string(a_buf, a_size, "a=fmtp:%d mode=20\r\n", rtp_code);
    	
    	}
    
    /*! \brief Get Max T.38 Transmission rate from T38 capabilities */
    
    static int t38_get_rate(int t38cap)
    {
    	int maxrate = (t38cap & (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400));
    	
    	if (maxrate & T38FAX_RATE_14400) {
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "T38MaxFaxRate 14400 found\n");
    		return 14400;
    	} else if (maxrate & T38FAX_RATE_12000) {
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "T38MaxFaxRate 12000 found\n");
    		return 12000;
    	} else if (maxrate & T38FAX_RATE_9600) {
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "T38MaxFaxRate 9600 found\n");
    		return 9600;
    	} else if (maxrate & T38FAX_RATE_7200) {
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "T38MaxFaxRate 7200 found\n");
    		return 7200;
    	} else if (maxrate & T38FAX_RATE_4800) {
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "T38MaxFaxRate 4800 found\n");
    		return 4800;
    	} else if (maxrate & T38FAX_RATE_2400) {
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "T38MaxFaxRate 2400 found\n");
    		return 2400;
    	} else {
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "Strange, T38MaxFaxRate NOT found in peers T38 SDP.\n");
    		return 0;
    	}
    }
    
    /*! \brief Add T.38 Session Description Protocol message */
    static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p)
    {
    	int len = 0;
    	int x = 0;
    	struct sockaddr_in udptlsin;
    	char v[256] = "";
    	char s[256] = "";
    	char o[256] = "";
    	char c[256] = "";
    	char t[256] = "";
    	char m_modem[256];
    	char a_modem[1024];
    	char *m_modem_next = m_modem;
    	size_t m_modem_left = sizeof(m_modem);
    	char *a_modem_next = a_modem;
    	size_t a_modem_left = sizeof(a_modem);
    	struct sockaddr_in udptldest = { 0, };
    	int debug;
    	
    	debug = sip_debug_test_pvt(p);
    	len = 0;
    	if (!p->udptl) {
    		ast_log(LOG_WARNING, "No way to add SDP without an UDPTL structure\n");
    		return -1;
    	}
    	
    	if (!p->sessionid) {
    		p->sessionid = getpid();
    		p->sessionversion = p->sessionid;
    	} else
    		p->sessionversion++;
    	
    	/* Our T.38 end is */
    	ast_udptl_get_us(p->udptl, &udptlsin);
    	
    	/* Determine T.38 UDPTL destination */
    	if (p->udptlredirip.sin_addr.s_addr) {
    		udptldest.sin_port = p->udptlredirip.sin_port;
    		udptldest.sin_addr = p->udptlredirip.sin_addr;
    	} else {
    		udptldest.sin_addr = p->ourip;
    		udptldest.sin_port = udptlsin.sin_port;
    	}
    	
    	if (debug) {
    
    		ast_log(LOG_DEBUG, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(udptlsin.sin_port));
    
    	}
    	
    	/* We break with the "recommendation" and send our IP, in order that our
    	   peer doesn't have to ast_gethostbyname() us */
    	
    	if (debug) {
    		ast_log(LOG_DEBUG, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
    			p->t38.capability,
    			p->t38.peercapability,
    			p->t38.jointcapability);
    	}
    	snprintf(v, sizeof(v), "v=0\r\n");
    
    	snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(udptldest.sin_addr));
    
    	snprintf(s, sizeof(s), "s=session\r\n");
    
    	snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(udptldest.sin_addr));
    
    	snprintf(t, sizeof(t), "t=0 0\r\n");
    	ast_build_string(&m_modem_next, &m_modem_left, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port));
    	
    	if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
    		ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVersion:0\r\n");
    	if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
    		ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxVersion:1\r\n");
    	if ((x = t38_get_rate(p->t38.jointcapability)))
    		ast_build_string(&a_modem_next, &a_modem_left, "a=T38MaxBitRate:%d\r\n",x);
    	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxFillBitRemoval:%d\r\n", (p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) ? 1 : 0);
    	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingMMR:%d\r\n", (p->t38.jointcapability & T38FAX_TRANSCODING_MMR) ? 1 : 0);
    	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxTranscodingJBIG:%d\r\n", (p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) ? 1 : 0);
    	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF");
    	x = ast_udptl_get_local_max_datagram(p->udptl);
    	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxBuffer:%d\r\n",x);
    	ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxMaxDatagram:%d\r\n",x);
    	if (p->t38.jointcapability != T38FAX_UDP_EC_NONE)
    		ast_build_string(&a_modem_next, &a_modem_left, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC");
    	if (p->udptl)
    		len = strlen(m_modem) + strlen(a_modem);
    	add_header(resp, "Content-Type", "application/sdp");
    	add_header_contentLength(resp, len);
    	add_line(resp, v);
    	add_line(resp, o);
    	add_line(resp, s);
    	add_line(resp, c);
    	add_line(resp, t);
    	add_line(resp, m_modem);
    	add_line(resp, a_modem);
    	
    	/* Update lastrtprx when we send our SDP */
    	p->lastrtprx = p->lastrtptx = time(NULL);
    	
    	return 0;
    }
    
    
    
    /*! \brief Add RFC 2833 DTMF offer to SDP */
    
    static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
    				char **m_buf, size_t *m_size, char **a_buf, size_t *a_size,
    				int debug)
    {
    	int rtp_code;
    
    	if (debug)
    
    		ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0));
    
    	if ((rtp_code = ast_rtp_lookup_code(p->rtp, 0, format)) == -1)
    		return;
    
    	ast_build_string(m_buf, m_size, " %d", rtp_code);
    	ast_build_string(a_buf, a_size, "a=rtpmap:%d %s/%d\r\n", rtp_code,
    
    			 ast_rtp_lookup_mime_subtype(0, format, 0),
    
    			 sample_rate);
    	if (format == AST_RTP_DTMF)
    		/* Indicate we support DTMF and FLASH... */
    		ast_build_string(a_buf, a_size, "a=fmtp:%d 0-16\r\n", rtp_code);
    }
    
    
    /*! \brief Add Session Description Protocol message */
    
    static int add_sdp(struct sip_request *resp, struct sip_pvt *p)
    
    	int alreadysent = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct sockaddr_in sin;
    
    	struct sockaddr_in dest;
    	struct sockaddr_in vdest = { 0, };
    
    	/* SDP fields */
    	char *version = 	"v=0\r\n";		/* Protocol version */
    	char *subject = 	"s=session\r\n";	/* Subject of the session */
    	char owner[256];				/* Session owner/creator */
    	char connection[256];				/* Connection data */
    	char *stime = "t=0 0\r\n"; 			/* Time the session is active */
    	char bandwidth[256] = "";			/* Max bitrate */
    
    	char m_audio[256];				/* Media declaration line for audio */
    	char m_video[256];				/* Media declaration line for video */
    	char a_audio[1024];				/* Attributes for audio */
    	char a_video[1024];				/* Attributes for video */
    
    	char *m_audio_next = m_audio;
    	char *m_video_next = m_video;
    	size_t m_audio_left = sizeof(m_audio);
    	size_t m_video_left = sizeof(m_video);
    	char *a_audio_next = a_audio;
    	char *a_video_next = a_video;
    	size_t a_audio_left = sizeof(a_audio);
    	size_t a_video_left = sizeof(a_video);
    
    	int needvideo = FALSE;
    	int debug = sip_debug_test_pvt(p);
    
    	m_video[0] = '\0';	/* Reset the video media string if it's not needed */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!p->rtp) {
    		ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
    		return -1;
    	}
    
    	if (!p->sessionid) {
    		p->sessionid = getpid();
    		p->sessionversion = p->sessionid;
    	} else
    		p->sessionversion++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_rtp_get_us(p->rtp, &sin);
    
    	if (p->vrtp)
    		ast_rtp_get_us(p->vrtp, &vsin);
    
    
    	/* Is this a re-invite to move the media out, then use the original offer from caller  */
    
    	if (p->redirip.sin_addr.s_addr) {
    		dest.sin_port = p->redirip.sin_port;
    		dest.sin_addr = p->redirip.sin_addr;
    
    		if (p->redircodecs)
    			capability = p->redircodecs;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		dest.sin_addr = p->ourip;
    		dest.sin_port = sin.sin_port;
    	}
    
    	/* Ok, let's start working with codec selection here */
    	capability = p->jointcapability;
    
    	if (option_debug > 1) {
    		char codecbuf[BUFSIZ];
    		ast_log(LOG_DEBUG, "** Our capability: %s Video flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability), ast_test_flag(&p->flags[0], SIP_NOVIDEO) ? "True" : "False");
    		ast_log(LOG_DEBUG, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec));
    	}
    
    	
    	if ((ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP))) {
    		ast_build_string(&m_audio_next, &m_audio_left, " %d", 191);
    		ast_build_string(&a_audio_next, &a_audio_left, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000);
    	}
    
    
    	/* Check if we need video in this call */
    	if((capability & AST_FORMAT_VIDEO_MASK) && !ast_test_flag(&p->flags[0], SIP_NOVIDEO)) {
    		if (p->vrtp) {
    			needvideo = TRUE;
    			if (option_debug > 1)
    				ast_log(LOG_DEBUG, "This call needs video offers! \n");
    		} else if (option_debug > 1)
    			ast_log(LOG_DEBUG, "This call needs video offers, but there's no video support enabled ! \n");
    	}
    		
    
    	/* Ok, we need video. Let's add what we need for video and set codecs.
    	   Video is handled differently than audio since we can not transcode. */
    	if (needvideo) {
    
    		/* Determine video destination */
    
    		if (p->vredirip.sin_addr.s_addr) {
    			vdest.sin_addr = p->vredirip.sin_addr;
    
    			vdest.sin_port = p->vredirip.sin_port;
    
    		} else {
    			vdest.sin_addr = p->ourip;
    			vdest.sin_port = vsin.sin_port;
    		}
    
    		ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
    
    		/* Build max bitrate string */
    		if (p->maxcallbitrate)
    			snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
    		if (debug) 
    
    			ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port));	
    
    
    		/* For video, we can't negotiate video offers. Let's compare the incoming call with what we got. */
    		if (p->prefcodec) {
    			int videocapability = (capability & p->prefcodec) & AST_FORMAT_VIDEO_MASK; /* Outbound call */
    		
    			/*! \todo XXX We need to select one codec, not many, since there's no transcoding */
    
    			/* Now, merge this video capability into capability while removing unsupported codecs */
    			if (!videocapability) {
    				needvideo = FALSE;
    				if (option_debug > 2)
    					ast_log(LOG_DEBUG, "** No compatible video codecs... Disabling video.\n");
    			} 
    
    			/* Replace video capabilities with the new videocapability */
    			capability = (capability & AST_FORMAT_AUDIO_MASK) | videocapability;
    
    			if (option_debug > 4) {
    				char codecbuf[BUFSIZ];
    				if (videocapability)
    					ast_log(LOG_DEBUG, "** Our video codec selection is: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), videocapability));
    				ast_log(LOG_DEBUG, "** Capability now set to : %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability));
    			}
    		}
    
    		ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port));	
    
    
    	/* We break with the "recommendation" and send our IP, in order that our
    	   peer doesn't have to ast_gethostbyname() us */
    
    
    	snprintf(owner, sizeof(owner), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr));
    	snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr));
    
    	ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
    
    
    	if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR))
    
    		hold = "a=recvonly\r\n";
    
    	else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE))
    		hold = "a=inactive\r\n";
    
    		hold = "a=sendrecv\r\n";
    
    	/* Now, start adding audio codecs. These are added in this order:
    		- First what was requested by the calling channel
    		- Then preferences in order from sip.conf device config for this peer/user
    		- Then other codecs in capabilities, including video
    	*/
    
    	/* Prefer the audio codec we were requested to use, first, no matter what 
    		Note that p->prefcodec can include video codecs, so mask them out
    	 */
    
    	if (capability & p->prefcodec) {
    
    		add_codec_to_sdp(p, p->prefcodec & AST_FORMAT_AUDIO_MASK, 8000,
    				 &m_audio_next, &m_audio_left,
    				 &a_audio_next, &a_audio_left,
    				 debug);
    		alreadysent |= p->prefcodec & AST_FORMAT_AUDIO_MASK;
    
    
    	/* Start by sending our preferred audio codecs */
    
    		if (!(pref_codec = ast_codec_pref_index(&p->prefs, x)))
    
    
    		if (!(capability & pref_codec))
    			continue;
    
    		if (alreadysent & pref_codec)
    			continue;
    
    
    		add_codec_to_sdp(p, pref_codec, 8000,
    				 &m_audio_next, &m_audio_left,
    				 &a_audio_next, &a_audio_left,
    				 debug);
    
    		alreadysent |= pref_codec;
    
    	/* Now send any other common audio and video codecs, and non-codec formats: */
    	for (x = 1; x <= (needvideo ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO); x <<= 1) {
    		if (!(capability & x))	/* Codec not requested */
    
    		if (alreadysent & x)	/* Already added to SDP */
    
    			continue;
    
    		if (x <= AST_FORMAT_MAX_AUDIO)
    			add_codec_to_sdp(p, x, 8000,
    					 &m_audio_next, &m_audio_left,
    					 &a_audio_next, &a_audio_left,
    					 debug);
    
    			add_codec_to_sdp(p, x, 90000,
    					 &m_video_next, &m_video_left,
    					 &a_video_next, &a_video_left,
    					 debug);
    
    	/* Now add DTMF RFC2833 telephony-event as a codec */
    
    	for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
    
    		if (!(p->noncodeccapability & x))
    			continue;
    
    		add_noncodec_to_sdp(p, x, 8000,
    				    &m_audio_next, &m_audio_left,
    				    &a_audio_next, &a_audio_left,
    				    debug);
    
    	if (option_debug > 2)
    		ast_log(LOG_DEBUG, "-- Done with adding codecs to SDP\n");
    
    
    	if(!ast_internal_timing_enabled(p->owner))
    		ast_build_string(&a_audio_next, &a_audio_left, "a=silenceSupp:off - - - -\r\n");
    
    
    	if ((m_audio_left < 2) || (m_video_left < 2) || (a_audio_left == 0) || (a_video_left == 0))
    
    		ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
    
    
    	ast_build_string(&m_audio_next, &m_audio_left, "\r\n");
    
    	if (needvideo)
    		ast_build_string(&m_video_next, &m_video_left, "\r\n");
    
    	len = strlen(version) + strlen(subject) + strlen(owner) + strlen(connection) + strlen(stime) + strlen(m_audio) + strlen(a_audio) + strlen(hold);
    	if (needvideo) /* only if video response is appropriate */
    		len += strlen(m_video) + strlen(a_video) + strlen(bandwidth) + strlen(hold);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	add_header(resp, "Content-Type", "application/sdp");
    
    	add_header_contentLength(resp, len);
    
    	add_line(resp, version);
    	add_line(resp, owner);
    	add_line(resp, subject);
    	add_line(resp, connection);
    	if (needvideo)	 	/* only if video response is appropriate */
    		add_line(resp, bandwidth);
    	add_line(resp, stime);
    
    	add_line(resp, m_audio);
    	add_line(resp, a_audio);
    
    	if (needvideo) { /* only if video response is appropriate */
    
    		add_line(resp, m_video);
    		add_line(resp, a_video);
    
    		add_line(resp, hold);	/* Repeat hold for the video stream */
    
    	/* Update lastrtprx when we send our SDP */
    
    	p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
    
    	if (option_debug > 2) {
    		char buf[BUFSIZ];
    		ast_log(LOG_DEBUG, "Done building SDP. Settling with this capability: %s\n", ast_getformatname_multiple(buf, BUFSIZ, capability));
    	}