Newer
Older
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);
if (req->headers < SIP_MAX_HEADERS)
req->headers++;
else
ast_log(LOG_WARNING, "Out of SIP header space... Will generate broken SIP message\n");
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, 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 */
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)
{
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, const struct sip_request *orig, const char *field)
const char *oh = __get_header(orig, field, &start);
if (ast_strlen_zero(oh))
break;
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
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';
/* 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",
Russell Bryant
committed
tmp, ast_inet_ntoa(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",
Russell Bryant
committed
oh, ast_inet_ntoa(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)
{
Olle Johansson
committed
char r[BUFSIZ*2], *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];
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);
Russell Bryant
committed
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)
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, const 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 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)
char stripped[80];
const char *c;
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);
}
c = p->initreq.rlPart2; /* Use original URI */
/* 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 */
char *n;
/* 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));
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);
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, const 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, const char *msg, const 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, 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
*/
static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const 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, const char *msg, const 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, const char *msg, const 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, const struct sip_request *req, const char *randdata, enum xmittype reliable, const char *header, int stale)
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 algorithm=MD5, 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;
}
/*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
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,
Kevin P. Fleming
committed
ast_rtp_lookup_mime_subtype(1, codec,
ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0),
Kevin P. Fleming
committed
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);
} 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);
}
Kevin P. Fleming
committed
}
Kevin P. Fleming
committed
/*! \brief Get Max T.38 Transmission rate from T38 capabilities */
5579
5580
5581
5582
5583
5584
5585
5586
5587
5588
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
5623
5624
5625
5626
5627
5628
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
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) {
Russell Bryant
committed
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");
Russell Bryant
committed
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");
Russell Bryant
committed
snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", ast_inet_ntoa(udptldest.sin_addr));
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
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 */
Kevin P. Fleming
committed
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)
Kevin P. Fleming
committed
ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0));
Kevin P. Fleming
committed
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,
Kevin P. Fleming
committed
ast_rtp_lookup_mime_subtype(0, format, 0),
Kevin P. Fleming
committed
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)
struct sockaddr_in vsin;
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 */
Kevin P. Fleming
committed
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);
Kevin P. Fleming
committed
int x;
int capability;
int needvideo = FALSE;
int debug = sip_debug_test_pvt(p);
m_video[0] = '\0'; /* Reset the video media string if it's not needed */
if (!p->rtp) {
ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
return -1;
}
/* Set RTP Session ID and version */
if (!p->sessionid) {
p->sessionid = getpid();
p->sessionversion = p->sessionid;
} else
p->sessionversion++;
/* Get our addresses */
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;
} 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)
Russell Bryant
committed
ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(vsin.sin_port));
5845
5846
5847
5848
5849
5850
5851
5852
5853
5854
5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
/* 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));
}
}
if (debug)
Russell Bryant
committed
ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip), ntohs(sin.sin_port));
/* Start building generic SDP headers */
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 */
Russell Bryant
committed
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));
Kevin P. Fleming
committed
ast_build_string(&m_audio_next, &m_audio_left, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
Olle Johansson
committed
if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR))
Olle Johansson
committed
else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE))
hold = "a=inactive\r\n";
Olle Johansson
committed
else
Olle Johansson
committed
/* 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
*/
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;
Kevin P. Fleming
committed
/* Start by sending our preferred audio codecs */
Kevin P. Fleming
committed
for (x = 0; x < 32; x++) {
int pref_codec;
Kevin P. Fleming
committed
if (!(pref_codec = ast_codec_pref_index(&p->prefs, x)))
Kevin P. Fleming
committed
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 */
Kevin P. Fleming
committed
continue;
if (alreadysent & x) /* Already added to SDP */
Kevin P. Fleming
committed
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
Kevin P. Fleming
committed
add_codec_to_sdp(p, x, 90000,
&m_video_next, &m_video_left,
&a_video_next, &a_video_left,
debug);
Kevin P. Fleming
committed
/* Now add DTMF RFC2833 telephony-event as a codec */
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 (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");
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");
if (needvideo)
ast_build_string(&m_video_next, &m_video_left, "\r\n");
Kevin P. Fleming
committed
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);
Kevin P. Fleming
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);
Kevin P. Fleming
committed
add_line(resp, m_audio);
add_line(resp, a_audio);
Olle Johansson
committed
add_line(resp, hold);
if (needvideo) { /* only if video response is appropriate */
Kevin P. Fleming
committed
add_line(resp, m_video);
add_line(resp, a_video);
add_line(resp, hold); /* Repeat hold for the video stream */
Kevin P. Fleming
committed
/* Update lastrtprx when we send our SDP */
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
Kevin P. Fleming
committed
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));
}