Newer
Older
* specified RTP payload type (with corresponding MIME subtype):
*/
iterator = 0;
while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
if (!strcasecmp(a, "sendonly")) {
sendonly=1;
continue;
}
if (!strcasecmp(a, "sendrecv")) {
sendonly=0;
}
if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue;
if (debug)
ast_verbose("Found description format %s\n", mimeSubtype);
/* Note: should really look at the 'freq' and '#chans' params too */
ast_rtp_set_rtpmap_type(p->rtp, codec, "audio", mimeSubtype);
if (p->vrtp)
ast_rtp_set_rtpmap_type(p->vrtp, codec, "video", mimeSubtype);
/* Now gather all of the codecs that were asked for: */
if (p->vrtp)
ast_rtp_get_current_formats(p->vrtp,
&vpeercapability, &vpeernoncodeccapability);
p->jointcapability = p->capability & (peercapability | vpeercapability);
p->peercapability = (peercapability | vpeercapability);
p->noncodeccapability = noncodeccapability & peernoncodeccapability;
if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
Mark Spencer
committed
if (p->noncodeccapability & 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);
Mark Spencer
committed
} else {
ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
Mark Spencer
committed
}
}
/* shame on whoever coded this.... */
const unsigned slen=512;
char s1[slen], s2[slen], s3[slen], s4[slen];
ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n",
ast_getformatname_multiple(s1, slen, p->capability),
ast_getformatname_multiple(s2, slen, peercapability),
ast_getformatname_multiple(s3, slen, vpeercapability),
ast_getformatname_multiple(s4, slen, p->jointcapability));
ast_verbose("Non-codec capabilities: us - %s, peer - %s, combined - %s\n",
ast_rtp_lookup_mime_multiple(s1, slen, noncodeccapability, 0),
ast_rtp_lookup_mime_multiple(s2, slen, peernoncodeccapability, 0),
ast_rtp_lookup_mime_multiple(s3, slen, p->noncodeccapability, 0));
if (!p->jointcapability) {
Mark Spencer
committed
ast_log(LOG_NOTICE, "No compatible codecs!\n");
if (!p->owner) /* There's no open channel owning us */
return 0;
if (!(p->owner->nativeformats & p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
const unsigned slen=512;
char s1[slen], s2[slen];
ast_log(LOG_DEBUG, "Oooh, we need to change our formats since our peer supports only %s and not %s\n",
ast_getformatname_multiple(s1, slen, p->jointcapability),
ast_getformatname_multiple(s2, slen, 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);
}
if ((bridgepeer=ast_bridged_channel(p->owner))) {
/* We have a bridge */
/* Turn on/off music on hold if we are holding/unholding */
if (sin.sin_addr.s_addr && !sendonly) {
ast_moh_stop(bridgepeer);
Kevin P. Fleming
committed
/* Activate a re-invite */
ast_queue_frame(p->owner, &ast_null_frame);
} else {
/* No address for RTP, we're on hold */
Kevin P. Fleming
committed
ast_moh_start(bridgepeer, NULL);
if (sendonly)
ast_rtp_stop(p->rtp);
/* Activate a re-invite */
ast_queue_frame(p->owner, &ast_null_frame);
Kevin P. Fleming
committed
/* Manager Hold and Unhold events must be generated, if necessary */
if (sin.sin_addr.s_addr && !sendonly) {
append_history(p, "Unhold", "%s", req->data);
Kevin P. Fleming
committed
if (global_callevents && ast_test_flag(&p->flags[0], SIP_CALL_ONHOLD)) {
Kevin P. Fleming
committed
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[0], SIP_CALL_ONHOLD);
Kevin P. Fleming
committed
/* No address for RTP, we're on hold */
Kevin P. Fleming
committed
if (global_callevents && !ast_test_flag(&p->flags[0], SIP_CALL_ONHOLD)) {
Kevin P. Fleming
committed
manager_event(EVENT_FLAG_CALL, "Hold",
"Channel: %s\r\n"
Kevin P. Fleming
committed
p->owner->name,
p->owner->uniqueid);
}
ast_set_flag(&p->flags[0], SIP_CALL_ONHOLD);
Kevin P. Fleming
committed
}
/*! \brief Add header to SIP message */
Kevin P. Fleming
committed
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");
Kevin P. Fleming
committed
if (req->lines) {
ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
return -1;
}
Kevin P. Fleming
committed
ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value);
return -1;
}
req->header[req->headers] = req->data + req->len;
Kevin P. Fleming
committed
if (compactheaders)
var = find_alias(var, var);
Kevin P. Fleming
committed
snprintf(req->header[req->headers], maxlen, "%s: %s\r\n", var, value);
Kevin P. Fleming
committed
/*! \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 */
Mark Spencer
committed
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");
if (!req->lines) {
/* Add extra empty return */
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;
}
snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line);
req->len += strlen(req->line[req->lines]);
/*! \brief Copy one header field from one request to another */
static int copy_header(struct sip_request *req, struct sip_request *orig, char *field)
{
const char *tmp = get_header(orig, field);
if (!ast_strlen_zero(tmp)) /* Add what we're responding to */
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, struct sip_request *orig, char *field)
{
int start = 0;
int copied = 0;
for (;;) {
const char *tmp = __get_header(orig, field, &start);
if (ast_strlen_zero(tmp))
/* Add what we're responding to */
add_header(req, field, tmp);
copied++;
/*! \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, struct sip_request *orig, char *field)
{
int copied = 0;
const char *oh = __get_header(orig, field, &start);
if (ast_strlen_zero(oh))
break;
4235
4236
4237
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
if (!copied) { /* Only check for empty rport in topmost via header */
char iabuf[INET_ADDRSTRLEN];
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';
/* 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",
tmp, ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr),
ntohs(p->recv.sin_port));
} else {
/* We should *always* add a received to the topmost via */
snprintf(new, sizeof(new), "%s;received=%s",
oh, ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr));
}
oh = new; /* the header to copy */
} /* else add the following via headers untouched */
add_header(req, field, oh);
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)
{
char r[256], *p;
Kevin P. Fleming
committed
int n, rem = sizeof(r);
if (rem < n+3) /* we need room for ",<route>" */
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];
char iabuf[INET_ADDRSTRLEN];
int port, hn;
struct hostent *hp;
/* 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][;...] */
ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
if (h)
++h;
else {
h = uri;
if (strncmp(h, "sip:", 4) == 0)
h += 4;
else if (strncmp(h, "sips:", 5) == 0)
h += 5;
Kevin P. Fleming
committed
hn = strcspn(h, ":;>") + 1;
if (hn > sizeof(hostname))
hn = sizeof(hostname);
Kevin P. Fleming
committed
ast_copy_string(hostname, h, hn);
/* XXX bug here if string has been trimmed to sizeof(hostname) */
Kevin P. Fleming
committed
h += hn - 1;
/* Is "port" present? if not default to DEFAULT_SIP_PORT */
if (*h == ':') {
/* Parse port */
++h;
port = strtol(h, &h, 10);
}
else
/* Got the hostname:port - but maybe there's a "maddr=" to override address? */
maddr = strstr(h, "maddr=");
Kevin P. Fleming
committed
hn = strspn(maddr, "0123456789.") + 1;
if (hn > sizeof(hostname))
hn = sizeof(hostname);
hp = ast_gethostbyname(hostname, &ahp);
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);
Mark Spencer
committed
ast_verbose("set_destination: set destination to %s, port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), port);
/*! \brief Initialize SIP response, based on SIP request */
static int init_resp(struct sip_request *resp, const char *msg)
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)
/* 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]);
/*! \brief Prepare SIP response packet */
static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, struct sip_request *req)
char newto[256];
const char *ot;
Mark Spencer
committed
init_resp(resp, msg);
copy_via_headers(p, resp, req, "Via");
if (msg[0] == '2')
copy_all_header(resp, req, "Record-Route");
if (!strcasestr(ot, "tag=") && strncmp(msg, "100", 3)) {
/* 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))
snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
else if (p->tag && !ast_test_flag(&p->flags[0], SIP_OUTGOING))
Kevin P. Fleming
committed
snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
Kevin P. Fleming
committed
ast_copy_string(newto, ot, sizeof(newto));
ot = newto;
}
add_header(resp, "To", ot);
copy_header(resp, req, "Call-ID");
copy_header(resp, req, "CSeq");
add_header(resp, "User-Agent", global_useragent);
add_header(resp, "Supported", SUPPORTED_EXTENSIONS);
if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) {
/* For registration responses, we also need expiry and
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 */
}
Kevin P. Fleming
committed
} else if (p->our_contact[0]) {
add_header(resp, "Contact", p->our_contact);
/*! \brief Initialize a SIP request response packet */
static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch)
char stripped[80];
const char *c;
char *n;
const char *ot, *of;
int is_strict = FALSE; /*!< Strict routing flag */
memset(req, 0, sizeof(struct sip_request));
snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text);
if (!seqno) {
seqno = p->ocseq;
}
Mark Spencer
committed
if (newbranch) {
Tilghman Lesher
committed
p->branch ^= ast_random();
build_via(p);
Mark Spencer
committed
}
/* Check for strict or loose router */
if (p->route && !ast_strlen_zero(p->route->hop) && strstr(p->route->hop,";lr") == NULL) {
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)) {
c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */
Mark Spencer
committed
} else if (!ast_strlen_zero(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"),
sizeof(stripped));
c = get_in_brackets(stripped);
n = strchr(c, ';');
if (n)
*n = '\0';
}
init_req(req, sipmethod, c);
snprintf(tmp, sizeof(tmp), "%d %s", seqno, sip_methods[sipmethod].text);
if (p->route) {
set_destination(p, p->route->hop);
add_route(req, is_strict ? p->route->next : p->route);
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) {
/* 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))
snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
else if (!ast_test_flag(&p->flags[0], SIP_OUTGOING))
Kevin P. Fleming
committed
snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
else
snprintf(newto, sizeof(newto), "%s", ot);
if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
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);
add_header(req, "User-Agent", global_useragent);
add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
Kevin P. Fleming
committed
if (!ast_strlen_zero(p->rpid))
Kevin P. Fleming
committed
add_header(req, "Remote-Party-ID", p->rpid);
/*! \brief Base transmit response function */
static int __transmit_response(struct sip_pvt *p, const char *msg, struct sip_request *req, enum xmittype reliable)
Kevin P. Fleming
committed
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;
}
add_header_contentLength(&resp, 0);
Kevin P. Fleming
committed
/* If we are cancelling an incoming invite for some reason, add information
about the reason why we are doing this in clear text */
if (p->method == SIP_INVITE && msg[0] != '1' && p->owner && p->owner->hangupcause) {
char buf[10];
add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
Kevin P. Fleming
committed
}
return send_response(p, &resp, reliable, seqno);
}
/*! \brief Transmit response, no retransmits */
static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req)
{
return __transmit_response(p, msg, req, XMIT_UNRELIABLE);
/*! \brief Transmit response, no retransmits */
static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, 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
*/
static int transmit_response_reliable(struct sip_pvt *p, const char *msg, struct sip_request *req)
return __transmit_response(p, msg, req, XMIT_CRITICAL);
/*! \brief Append date to SIP message */
static void append_date(struct sip_request *req)
{
char tmpdat[256];
struct tm tm;
time_t t = time(NULL);
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 */
static int transmit_response_with_date(struct sip_pvt *p, char *msg, struct sip_request *req)
{
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 */
static int transmit_response_with_allow(struct sip_pvt *p, char *msg, struct sip_request *req, enum xmittype reliable)
{
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 */
static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, struct sip_request *req, const char *randdata, enum xmittype reliable, const char *header, int stale)
{
struct sip_request resp;
char tmp[256];
Kevin P. Fleming
committed
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;
}
Kevin P. Fleming
committed
/* Stale means that they sent us correct authentication, but
based it on an old challenge (nonce) */
snprintf(tmp, sizeof(tmp), "Digest realm=\"%s\", nonce=\"%s\"%s", global_realm, randdata, stale ? ", stale=true" : "");
Mark Spencer
committed
add_header(&resp, header, tmp);
add_header_contentLength(&resp, 0);
return send_response(p, &resp, reliable, seqno);
/*! \brief Add text body to SIP message */
Mark Spencer
committed
static int add_text(struct sip_request *req, const char *text)
{
/* XXX Convert \n's to \r\n's XXX */
add_header(req, "Content-Type", "text/plain");
add_header_contentLength(req, strlen(text));
/*! \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. */
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"
" <picture_fast_update>\r\n"
" </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;
}
Kevin P. Fleming
committed
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),
sample_rate);
if (codec == AST_FORMAT_G729A)
/* Indicate that we don't support VAD (G.729 annex B) */
Russell Bryant
committed
ast_build_string(a_buf, a_size, "a=fmtp:%d annexb=no\r\n", rtp_code);
Kevin P. Fleming
committed
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
}
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));
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),
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)
Kevin P. Fleming
committed
int pref_codec;
struct sockaddr_in vsin;
char v[256];
char s[256];
char o[256];
char c[256];
char t[256];
Kevin P. Fleming
committed
char m_audio[256];
char m_video[256];
char a_audio[1024];
char a_video[1024];
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);
char iabuf[INET_ADDRSTRLEN];
Kevin P. Fleming
committed
int x;
int capability;
Mark Spencer
committed
struct sockaddr_in vdest = { 0, };
Kevin P. Fleming
committed
int debug;
debug = sip_debug_test_pvt(p);
if (!p->rtp) {
ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
return -1;
}
capability = p->jointcapability;
if (!p->sessionid) {
p->sessionid = getpid();
p->sessionversion = p->sessionid;
} else
p->sessionversion++;
if (p->vrtp)
ast_rtp_get_us(p->vrtp, &vsin);
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;
} else {
dest.sin_addr = p->ourip;
dest.sin_port = sin.sin_port;
}
/* Determine video destination */
if (p->vrtp) {
if (p->vredirip.sin_addr.s_addr) {
vdest.sin_port = p->vredirip.sin_port;
vdest.sin_addr = p->vredirip.sin_addr;
} else {
vdest.sin_addr = p->ourip;
vdest.sin_port = vsin.sin_port;
}
}
if (debug) {
Mark Spencer
committed
ast_verbose("We're at %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ntohs(sin.sin_port));
Mark Spencer
committed
ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->ourip), ntohs(vsin.sin_port));
Kevin P. Fleming
committed
/* We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us */
Mark Spencer
committed
snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", p->sessionid, p->sessionversion, ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
Mark Spencer
committed
snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(iabuf, sizeof(iabuf), dest.sin_addr));
if ((p->vrtp) &&
(!ast_test_flag(&p->flags[0], SIP_NOVIDEO)) &&
(capability & VIDEO_CODEC_MASK)) /* only if video response is appropriate */
Kevin P. Fleming
committed
ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
ast_build_string(&m_video_next, &m_video_left, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
Olle Johansson
committed
if (ast_test_flag(&p->flags[0], SIP_CALL_ONHOLD))
hold = "a=recvonly";
Olle Johansson
committed
else
hold = "a=sendrecv";
Olle Johansson
committed
/* Prefer the codec we were requested to use, first, no matter what */
Kevin P. Fleming
committed
if (p->prefcodec <= AST_FORMAT_MAX_AUDIO)
add_codec_to_sdp(p, p->prefcodec, 8000,
&m_audio_next, &m_audio_left,
&a_audio_next, &a_audio_left,
debug);
else
add_codec_to_sdp(p, p->prefcodec, 90000,
&m_video_next, &m_video_left,
&a_video_next, &a_video_left,
debug);
alreadysent |= p->prefcodec;
}
Kevin P. Fleming
committed
/* Start by sending our preferred codecs */
Kevin P. Fleming
committed
for (x = 0; x < 32; x++) {
if (!(pref_codec = ast_codec_pref_index(&p->prefs, x)))
Kevin P. Fleming
committed
if (!(capability & pref_codec))
continue;
if (alreadysent & pref_codec)
continue;
if (pref_codec <= AST_FORMAT_MAX_AUDIO)
add_codec_to_sdp(p, pref_codec, 8000,
&m_audio_next, &m_audio_left,
&a_audio_next, &a_audio_left,
debug);
else
add_codec_to_sdp(p, pref_codec, 90000,
&m_video_next, &m_video_left,
&a_video_next, &a_video_left,
debug);
alreadysent |= pref_codec;
/* Now send any other common codecs, and non-codec formats: */
for (x = 1;
x <= ((ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && p->vrtp) ? AST_FORMAT_MAX_VIDEO : AST_FORMAT_MAX_AUDIO);
x <<= 1) {
Kevin P. Fleming
committed
if (!(capability & x))
continue;
if (alreadysent & x)
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);
else
add_codec_to_sdp(p, x, 90000,
&m_video_next, &m_video_left,
&a_video_next, &a_video_left,
debug);
Kevin P. Fleming
committed
for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
Kevin P. Fleming
committed
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);
Kevin P. Fleming
committed
if(!ast_internal_timing_enabled(p->owner))
ast_build_string(&a_audio_next, &a_audio_left, "a=silenceSupp:off - - - -\r\n");
Kevin P. Fleming
committed
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");
Kevin P. Fleming
committed
ast_build_string(&m_audio_next, &m_audio_left, "\r\n");
ast_build_string(&m_video_next, &m_video_left, "\r\n");
Olle Johansson
committed
len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m_audio) + strlen(a_audio) + strlen(hold);
if ((p->vrtp) &&
(!ast_test_flag(&p->flags[0], SIP_NOVIDEO)) &&
(capability & VIDEO_CODEC_MASK)) /* only if video response is appropriate */
Olle Johansson
committed
len += strlen(m_video) + strlen(a_video) + strlen(b) + strlen(hold);
Kevin P. Fleming
committed
add_header(resp, "Content-Type", "application/sdp");
add_header_contentLength(resp, len);
if ((p->vrtp) &&
(!ast_test_flag(&p->flags[0], SIP_NOVIDEO)) &&
(capability & VIDEO_CODEC_MASK)) /* only if video response is appropriate */
Kevin P. Fleming
committed
add_line(resp, m_audio);
add_line(resp, a_audio);
Olle Johansson
committed
add_line(resp, hold);
if ((p->vrtp) &&
(!ast_test_flag(&p->flags[0], SIP_NOVIDEO)) &&
(capability & VIDEO_CODEC_MASK)) { /* only if video response is appropriate */
Kevin P. Fleming
committed
add_line(resp, m_video);
add_line(resp, a_video);
Olle Johansson
committed
add_line(resp, hold);
Kevin P. Fleming
committed
/* Update lastrtprx when we send our SDP */
time(&p->lastrtprx);
Kevin P. Fleming
committed
/*! \brief copy SIP request (mostly used to save request for responses) */
static void copy_request(struct sip_request *dst, struct sip_request *src)
{
long offset;
int x;
offset = ((void *)dst) - ((void *)src);
/* First copy stuff */
memcpy(dst, src, sizeof(*dst));
/* Now fix pointer arithmetic */
for (x=0; x < src->headers; x++)
for (x=0; x < src->lines; x++)
/*! \brief Used for 200 OK and 183 early media */
static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, enum xmittype reliable)
Kevin P. Fleming
committed
if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
return -1;
}
if (p->rtp) {
Olle Johansson
committed
try_suggested_sip_codec(p);
add_sdp(&resp, p);
} else {
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
}
return send_response(p, &resp, reliable, seqno);
/*! \brief Parse first line of incoming SIP request */
static int determine_firstline_parts(struct sip_request *req)
char *e = ast_skip_blanks(req->header[0]); /* there shouldn't be any */
if (!*e)
req->rlPart1 = e; /* method or protocol */
e = ast_skip_nonblanks(e);
if (*e)
*e++ = '\0';
e = ast_skip_blanks(e);
if ( !*e )