Newer
Older
/*! \brief Get a line from an SDP message body */
static const char *get_sdp(struct sip_request *req, const char *name)
{
int dummy = 0;
return get_sdp_iterate(&dummy, req, name);
}
/*! \brief Get a specific line from the message body */
static char *get_body(struct sip_request *req, char *name)
{
int x;
int len = strlen(name);
char *r;
for (x = 0; x < req->lines; x++) {
r = get_body_by_line(req->line[x], name, len);
if (r[0] != '\0')
return r;
}
return "";
}
Olle Johansson
committed
/*! \brief Find compressed SIP alias */
static const char *find_alias(const char *name, const char *_default)
{
/*! \brief Structure for conversion between compressed SIP and "normal" SIP */
static const struct cfalias {
char * const fullname;
char * const shortname;
} aliases[] = {
{ "Content-Type", "c" },
{ "Content-Encoding", "e" },
{ "From", "f" },
{ "Call-ID", "i" },
{ "Contact", "m" },
{ "Content-Length", "l" },
{ "Subject", "s" },
{ "To", "t" },
{ "Supported", "k" },
{ "Refer-To", "r" },
{ "Referred-By", "b" },
{ "Allow-Events", "u" },
{ "Event", "o" },
{ "Via", "v" },
{ "Accept-Contact", "a" },
{ "Reject-Contact", "j" },
{ "Request-Disposition", "d" },
{ "Session-Expires", "x" },
{ "Identity", "y" },
{ "Identity-Info", "n" },
Olle Johansson
committed
for (x=0; x<sizeof(aliases) / sizeof(aliases[0]); x++)
if (!strcasecmp(aliases[x].fullname, name))
return aliases[x].shortname;
return _default;
}
static const char *__get_header(const struct sip_request *req, const char *name, int *start)
{
int pass;
/*
* Technically you can place arbitrary whitespace both before and after the ':' in
* a header, although RFC3261 clearly says you shouldn't before, and place just
* one afterwards. If you shouldn't do it, what absolute idiot decided it was
* a good idea to say you can do it, and if you can do it, why in the hell would.
* you say you shouldn't.
* Anyways, pedanticsipchecking controls whether we allow spaces before ':',
* and we always allow spaces after that for compatibility.
*/
for (pass = 0; name && pass < 2;pass++) {
int x, len = strlen(name);
for (x=*start; x<req->headers; x++) {
if (!strncasecmp(req->header[x], name, len)) {
char *r = req->header[x] + len; /* skip name */
if (pedanticsipchecking)
r = ast_skip_blanks(r);
if (*r == ':') {
return ast_skip_blanks(r+1);
}
}
}
if (pass == 0) /* Try aliases */
name = find_alias(name, NULL);
}
/* Don't return NULL, so get_header is always a valid pointer */
return "";
}
/*! \brief Get header from SIP request
\return Always return something, so don't check for NULL because it won't happen :-)
*/
static const char *get_header(const struct sip_request *req, const char *name)
{
int start = 0;
return __get_header(req, name, &start);
}
/*! \brief Read RTP from network */
static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect)
/* Retrieve audio/etc from channel. Assumes p->lock is already held. */
struct ast_frame *f;
if (!p->rtp) {
/* We have no RTP allocated for this channel */
return &ast_null_frame;
}
switch(ast->fdno) {
case 0:
f = ast_rtp_read(p->rtp); /* RTP Audio */
break;
case 1:
f = ast_rtcp_read(p->rtp); /* RTCP Control Channel */
break;
case 2:
f = ast_rtp_read(p->vrtp); /* RTP Video */
break;
case 3:
f = ast_rtcp_read(p->vrtp); /* RTCP Control Channel for video */
case 5:
f = ast_udptl_read(p->udptl); /* UDPTL for T.38 */
break;
default:
f = &ast_null_frame;
/* Don't forward RFC2833 if we're not supposed to */
if (f && (f->frametype == AST_FRAME_DTMF) &&
(ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_RFC2833))
return &ast_null_frame;
/* We already hold the channel lock */
if (f->frametype == AST_FRAME_VOICE) {
if (f->subclass != (p->owner->nativeformats & AST_FORMAT_AUDIO_MASK)) {
if (option_debug)
ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
p->owner->nativeformats = (p->owner->nativeformats & AST_FORMAT_VIDEO_MASK) | f->subclass;
Mark Spencer
committed
ast_set_read_format(p->owner, p->owner->readformat);
ast_set_write_format(p->owner, p->owner->writeformat);
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
f = ast_dsp_process(p->owner, p->vad, f);
if (f && f->frametype == AST_FRAME_DTMF) {
if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') {
if (option_debug)
ast_log(LOG_DEBUG, "Fax CNG detected on %s\n", ast->name);
*faxdetect = 1;
} else if (option_debug) {
ast_log(LOG_DEBUG, "* Detected inband DTMF '%c'\n", f->subclass);
}
}
/*! \brief Read SIP RTP from channel */
static struct ast_frame *sip_read(struct ast_channel *ast)
{
struct ast_frame *fr;
struct sip_pvt *p = ast->tech_pvt;
sip_pvt_lock(p);
fr = sip_rtp_read(ast, p, &faxdetected);
p->lastrtprx = time(NULL);
/* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */
/* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */
if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) {
if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
if (!p->pendinginvite) {
if (option_debug > 2)
ast_log(LOG_DEBUG, "Sending reinvite on SIP (%s) for T.38 negotiation.\n",ast->name);
p->t38.state = T38_LOCAL_REINVITE;
transmit_reinvite_with_sdp(p, TRUE);
if (option_debug > 1)
ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, ast->name);
}
} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
if (option_debug > 2)
ast_log(LOG_DEBUG, "Deferring reinvite on SIP (%s) - it will be re-negotiated for T.38\n", ast->name);
ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
}
}
sip_pvt_unlock(p);
Olle Johansson
committed
/*! \brief Generate 32 byte random string for callid's etc */
static char *generate_random_string(char *buf, size_t size)
Tilghman Lesher
committed
long val[4];
for (x=0; x<4; x++)
Tilghman Lesher
committed
val[x] = ast_random();
snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]);
Olle Johansson
committed
return buf;
}
/*! \brief Build SIP Call-ID value for a non-REGISTER transaction */
static void build_callid_pvt(struct sip_pvt *pvt)
{
char buf[33];
Russell Bryant
committed
const char *host = S_OR(pvt->fromdomain, ast_inet_ntoa(pvt->ourip));
Olle Johansson
committed
ast_string_field_build(pvt, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
}
/*! \brief Build SIP Call-ID value for a REGISTER transaction */
static void build_callid_registry(struct sip_registry *reg, struct in_addr ourip, const char *fromdomain)
{
Olle Johansson
committed
char buf[33];
Russell Bryant
committed
const char *host = S_OR(fromdomain, ast_inet_ntoa(ourip));
Olle Johansson
committed
ast_string_field_build(reg, callid, "%s@%s", generate_random_string(buf, sizeof(buf)), host);
/*! \brief Make our SIP dialog tag */
Kevin P. Fleming
committed
static void make_our_tag(char *tagbuf, size_t len)
{
Tilghman Lesher
committed
snprintf(tagbuf, len, "as%08lx", ast_random());
Kevin P. Fleming
committed
}
/*! \brief Allocate SIP_PVT structure and set defaults */
static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *sin,
int useglobal_nat, const int intended_method)
if (!(p = ast_calloc(1, sizeof(*p))))
Kevin P. Fleming
committed
if (ast_string_field_init(p, 512)) {
free(p);
return NULL;
}
ast_mutex_init(&p->pvt_lock);
Kevin P. Fleming
committed
p->method = intended_method;
Kevin P. Fleming
committed
p->subscribed = NONE;
p->stateid = -1;
Olle Johansson
committed
p->prefs = default_prefs; /* Set default codecs for this call */
if (intended_method != SIP_OPTIONS) /* Peerpoke has it's own system */
p->timer_t1 = SIP_TIMER_T1; /* Default SIP retransmission timer T1 (RFC 3261) */
p->sa = *sin;
if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip))
p->ourip = __ourip;
p->ourip = __ourip;
/* Copy global flags to this PVT at setup. */
ast_copy_flags(&p->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&p->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
Kevin P. Fleming
committed
ast_set2_flag(&p->flags[0], !recordhistory, SIP_NO_HISTORY);
Tilghman Lesher
committed
p->branch = ast_random();
Kevin P. Fleming
committed
make_our_tag(p->tag, sizeof(p->tag));
p->ocseq = INITIAL_CSEQ;
Kevin P. Fleming
committed
if (sip_methods[intended_method].need_rtp) {
p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
/* If the global videosupport flag is on, we always create a RTP interface for video */
if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT))
Kevin P. Fleming
committed
p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT))
p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr);
if (!p->rtp || (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp)) {
ast_log(LOG_WARNING, "Unable to create RTP audio %s session: %s\n",
ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video" : "", strerror(errno));
ast_mutex_destroy(&p->pvt_lock);
Kevin P. Fleming
committed
ast_variables_destroy(p->chanvars);
p->chanvars = NULL;
}
free(p);
return NULL;
ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INFO);
ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
Kevin P. Fleming
committed
ast_rtp_settos(p->rtp, global_tos_audio);
ast_rtp_set_rtptimeout(p->rtp, global_rtptimeout);
ast_rtp_set_rtpholdtimeout(p->rtp, global_rtpholdtimeout);
ast_rtp_set_rtpkeepalive(p->rtp, global_rtpkeepalive);
Kevin P. Fleming
committed
ast_rtp_settos(p->vrtp, global_tos_video);
ast_rtp_setdtmf(p->vrtp, 0);
ast_rtp_setdtmfcompensate(p->vrtp, 0);
ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout);
ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout);
ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive);
ast_udptl_settos(p->udptl, global_tos_audio);
Kevin P. Fleming
committed
Mark Spencer
committed
if (useglobal_nat && sin) {
/* Setup NAT structure according to global settings if we have an address */
ast_copy_flags(&p->flags[0], &global_flags[0], SIP_NAT);
p->recv = *sin;
do_setnat(p, ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE);
Kevin P. Fleming
committed
if (p->method != SIP_REGISTER)
ast_string_field_set(p, fromdomain, default_fromdomain);
build_via(p);
build_callid_pvt(p);
ast_string_field_set(p, callid, callid);
Kevin P. Fleming
committed
ast_string_field_set(p, mohinterpret, default_mohinterpret);
ast_string_field_set(p, mohsuggest, default_mohsuggest);
Mark Spencer
committed
p->capability = global_capability;
p->allowtransfer = global_allowtransfer;
if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) ||
(ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
if (p->udptl) {
p->t38.capability = global_t38_capability;
if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_REDUNDANCY)
p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_FEC)
p->t38.capability |= T38FAX_UDP_EC_FEC;
else if (ast_udptl_get_error_correction_scheme(p->udptl) == UDPTL_ERROR_CORRECTION_NONE)
p->t38.capability |= T38FAX_UDP_EC_NONE;
p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
p->t38.jointcapability = p->t38.capability;
}
ast_string_field_set(p, context, default_context);
Kevin P. Fleming
committed
/* Add to active dialog list */
Olle Johansson
committed
p->next = dialoglist;
dialoglist = p;
Kevin P. Fleming
committed
ast_log(LOG_DEBUG, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : "(No Call-ID)", sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP");
/*! \brief Connect incoming SIP message to current dialog or create new dialog structure
Called by handle_request, sipsock_read */
Kevin P. Fleming
committed
static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method)
char *tag = ""; /* note, tag is never NULL */
char totag[128];
char fromtag[128];
const char *callid = get_header(req, "Call-ID");
Olle Johansson
committed
const char *from = get_header(req, "From");
const char *to = get_header(req, "To");
const char *cseq = get_header(req, "Cseq");
/* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
/* get_header always returns non-NULL so we must use ast_strlen_zero() */
if (ast_strlen_zero(callid) || ast_strlen_zero(to) ||
ast_strlen_zero(from) || ast_strlen_zero(cseq))
Olle Johansson
committed
return NULL; /* Invalid packet */
/* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy
we need more to identify a branch - so we have to check branch, from
and to tags to identify a call leg.
For Asterisk to behave correctly, you need to turn on pedanticsipchecking
in sip.conf
*/
if (gettag(req, "To", totag, sizeof(totag)))
ast_set_flag(req, SIP_PKT_WITH_TOTAG); /* Used in handle_request/response */
gettag(req, "From", fromtag, sizeof(fromtag));
tag = (req->method == SIP_RESPONSE) ? totag : fromtag;
if (option_debug > 4 )
ast_log(LOG_DEBUG, "= Looking for Call ID: %s (Checking %s) --From tag %s --To-tag %s \n", callid, req->method==SIP_RESPONSE ? "To" : "From", fromtag, totag);
Olle Johansson
committed
/* All messages must always have From: tag */
if (ast_strlen_zero(fromtag)) {
if (option_debug > 4 )
ast_log(LOG_DEBUG, "%s request has no from tag, dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
return NULL;
}
/* reject requests that must always have a To: tag */
if (ast_strlen_zero(totag) && (req->method == SIP_ACK || req->method == SIP_BYE || req->method == SIP_INFO )) {
if (option_debug > 4)
ast_log(LOG_DEBUG, "%s must have a to tag. dropping callid: %s from: %s\n", sip_methods[req->method].text , callid, from );
return NULL;
}
Olle Johansson
committed
for (p = dialoglist; p; p = p->next) {
/* In pedantic, we do not want packets with bad syntax to be connected to a PVT */
if (req->method == SIP_REGISTER)
found = (!strcmp(p->callid, callid));
else
found = (!strcmp(p->callid, callid) &&
(!pedanticsipchecking || !tag || ast_strlen_zero(p->theirtag) || !strcmp(p->theirtag, tag))) ;
if (option_debug > 4)
ast_log(LOG_DEBUG, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag);
/* If we get a new request within an existing to-tag - check the to tag as well */
if (pedanticsipchecking && found && req->method != SIP_RESPONSE) { /* SIP Request */
if (p->tag[0] == '\0' && totag[0]) {
/* We have no to tag, but they have. Wrong dialog */
} else if (totag[0]) { /* Both have tags, compare them */
if (strcmp(totag, p->tag)) {
found = FALSE; /* This is not our packet */
}
}
if (!found && option_debug > 4)
ast_log(LOG_DEBUG, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n", p->callid, totag, sip_methods[req->method].text);
}
if (found) {
sip_pvt_lock(p);
/* See if the method is capable of creating a dialog */
if (sip_methods[intended_method].can_create == CAN_CREATE_DIALOG) {
if (intended_method == SIP_REFER) {
/* We do support REFER, but not outside of a dialog yet */
transmit_response_using_temp(callid, sin, 1, intended_method, req, "603 Declined (no dialog)");
} else if (intended_method == SIP_NOTIFY) {
/* We do not support out-of-dialog NOTIFY either,
like voicemail notification, so cancel that early */
transmit_response_using_temp(callid, sin, 1, intended_method, req, "489 Bad event");
} else {
/* Ok, time to create a new SIP dialog object, a pvt */
Olle Johansson
committed
if ((p = sip_alloc(callid, sin, 1, intended_method))) {
/* Ok, we've created a dialog, let's go and process it */
sip_pvt_lock(p);
Olle Johansson
committed
} else {
/* We have a memory or file/socket error (can't allocate RTP sockets or something) so we're not
getting a dialog from sip_alloc.
Without a dialog we can't retransmit and handle ACKs and all that, but at least
send an error message.
Sorry, we apologize for the inconvienience
*/
transmit_response_using_temp(callid, sin, 1, intended_method, req, "500 Server internal error");
if (option_debug > 3)
ast_log(LOG_DEBUG, "Failed allocating SIP dialog, sending 500 Server internal error and giving up\n");
}
}
return p;
} else if( sip_methods[intended_method].can_create == CAN_CREATE_DIALOG_UNSUPPORTED_METHOD) {
/* A method we do not support, let's take it on the volley */
transmit_response_using_temp(callid, sin, 1, intended_method, req, "501 Method Not Implemented");
if (option_debug > 1 )
ast_log(LOG_DEBUG, "Got a request with unsupported SIP method.\n");
} else if (intended_method != SIP_RESPONSE && intended_method != SIP_ACK) {
/* This is a request outside of a dialog that we don't know about */
transmit_response_using_temp(callid, sin, 1, intended_method, req, "481 Call leg/transaction does not exist");
if (option_debug > 1)
ast_log(LOG_DEBUG, "That's odd... Got a request in unknown dialog. Callid %s\n", callid ? callid : "<unknown>");
/* We do not respond to responses for dialogs that we don't know about, we just drop
the session quickly */
if (option_debug > 1 && intended_method == SIP_RESPONSE)
ast_log(LOG_DEBUG, "That's odd... Got a response on a call we dont know about. Callid %s\n", callid ? callid : "<unknown>");
/*! \brief Parse register=> line in sip.conf and add to registry */
static int sip_register(char *value, int lineno)
{
struct sip_registry *reg;
int portnum = 0;
char username[256] = "";
char *hostname=NULL, *secret=NULL, *authuser=NULL;
Olle Johansson
committed
char *callback=NULL;
ast_copy_string(username, value, sizeof(username));
/* First split around the last '@' then parse the two components. */
hostname = strrchr(username, '@'); /* allow @ in the first part */
if (hostname)
*hostname++ = '\0';
if (ast_strlen_zero(username) || ast_strlen_zero(hostname)) {
ast_log(LOG_WARNING, "Format for registration is user[:secret[:authuser]]@host[:port][/contact] at line %d\n", lineno);
/* split user[:secret[:authuser]] */
secret = strchr(username, ':');
if (secret) {
*secret++ = '\0';
authuser = strchr(secret, ':');
if (authuser)
*authuser++ = '\0';
Olle Johansson
committed
callback = strchr(hostname, '/');
if (callback)
*callback++ = '\0';
if (ast_strlen_zero(callback))
callback = "s";
porta = strchr(hostname, ':');
if (porta) {
*porta++ = '\0';
portnum = atoi(porta);
if (portnum == 0) {
ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno);
return -1;
}
if (!(reg = ast_calloc(1, sizeof(*reg)))) {
ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n");
if (ast_string_field_init(reg, 256)) {
ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry strings\n");
free(reg);
return -1;
}
regobjs++;
ASTOBJ_INIT(reg);
Olle Johansson
committed
ast_string_field_set(reg, callback, callback);
ast_string_field_set(reg, username, username);
ast_string_field_set(reg, hostname, hostname);
ast_string_field_set(reg, authuser, authuser);
ast_string_field_set(reg, secret, secret);
Olle Johansson
committed
reg->expiry = default_expiry;
reg->timeout = -1;
reg->refresh = default_expiry;
reg->ocseq = INITIAL_CSEQ;
ASTOBJ_CONTAINER_LINK(®l, reg); /* Add the new registry entry to the list */
/*! \brief Parse multiline SIP headers into one header
This is enabled if pedanticsipchecking is enabled */
static int lws2sws(char *msgbuf, int len)
int h = 0, t = 0;
int lws = 0;
for (; h < len;) {
/* Eliminate all CRs */
if (msgbuf[h] == '\r') {
h++;
continue;
}
/* Check for end-of-line */
if (msgbuf[h] == '\n') {
if (h + 1 == len)
break;
/* Check for a continuation line */
if (msgbuf[h + 1] == ' ' || msgbuf[h + 1] == '\t') {
/* Merge continuation line */
h++;
continue;
}
/* Propagate LF and start new line */
msgbuf[t++] = msgbuf[h++];
lws = 0;
continue;
}
if (msgbuf[h] == ' ' || msgbuf[h] == '\t') {
if (lws) {
h++;
continue;
}
msgbuf[t++] = msgbuf[h++];
lws = 1;
continue;
}
msgbuf[t++] = msgbuf[h++];
}
msgbuf[t] = '\0';
return t;
}
Olle Johansson
committed
/*! \brief Parse a SIP message
\note this function is used both on incoming and outgoing packets
*/
static void parse_request(struct sip_request *req)
char *c = req->data, **dst = req->header;
int i = 0, lim = SIP_MAX_HEADERS - 1;
req->header[0] = c;
req->headers = -1; /* mark that we are working on the header */
for (; *c; c++) {
if (*c == '\r') /* remove \r */
*c = '\0';
else if (*c == '\n') { /* end of this line */
*c = '\0';
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "%7s %2d [%3d]: %s\n",
req->headers < 0 ? "Header" : "Body",
i, (int)strlen(dst[i]), dst[i]);
if (ast_strlen_zero(dst[i]) && req->headers < 0) {
req->headers = i; /* record number of header lines */
dst = req->line; /* start working on the body */
i = 0;
lim = SIP_MAX_LINES - 1;
} else { /* move to next line, check for overflows */
if (i++ >= lim)
break;
dst[i] = c + 1; /* record start of next line */
}
/* update count of header or body lines */
if (req->headers >= 0) /* we are in the body */
req->lines = i;
else { /* no body */
req->headers = i;
req->lines = 0;
req->line[0] = "";
if (*c)
ast_log(LOG_WARNING, "Too many lines, skipping <%s>\n", c);
/* Split up the first line parts */
determine_firstline_parts(req);
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
/*!
\brief Determine whether a SIP message contains an SDP in its body
\param req the SIP request to process
\return 1 if SDP found, 0 if not found
Also updates req->sdp_start and req->sdp_end to indicate where the SDP
lives in the message body.
*/
static int find_sdp(struct sip_request *req)
{
const char *content_type;
const char *search;
char *boundary;
unsigned int x;
content_type = get_header(req, "Content-Type");
/* if the body contains only SDP, this is easy */
if (!strcasecmp(content_type, "application/sdp")) {
req->sdp_start = 0;
req->sdp_end = req->lines;
return 1;
}
/* if it's not multipart/mixed, there cannot be an SDP */
if (strncasecmp(content_type, "multipart/mixed", 15))
return 0;
/* if there is no boundary marker, it's invalid */
if (!(search = strcasestr(content_type, ";boundary=")))
return 0;
search += 10;
if (ast_strlen_zero(search))
return 0;
/* make a duplicate of the string, with two extra characters
at the beginning */
boundary = ast_strdupa(search - 2);
boundary[0] = boundary[1] = '-';
/* search for the boundary marker, but stop when there are not enough
lines left for it, the Content-Type header and at least one line of
body */
for (x = 0; x < (req->lines - 2); x++) {
if (!strncasecmp(req->line[x], boundary, strlen(boundary)) &&
!strcasecmp(req->line[x + 1], "Content-Type: application/sdp")) {
Olle Johansson
committed
x += 2;
req->sdp_start = x;
/* search for the end of the body part */
for ( ; x < req->lines; x++) {
if (!strncasecmp(req->line[x], boundary, strlen(boundary)))
break;
}
req->sdp_end = x;
return 1;
}
}
return 0;
}
/*! \brief Process SIP SDP offer, select formats and activate RTP channels
If offer is rejected, we will not change any properties of the call
Return 0 on success, a negative value on errors.
Must be called after find_sdp().
*/
static int process_sdp(struct sip_pvt *p, struct sip_request *req)
{
int portno = -1; /*!< RTP Audio port number */
int vportno = -1; /*!< RTP Video port number */
int udptlportno = -1;
int peert38capability = 0;
char s[256];
int old = 0;
/* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */
Joshua Colp
committed
int peercapability = 0, peernoncodeccapability = 0;
int vpeercapability = 0, vpeernoncodeccapability = 0;
struct sockaddr_in sin; /*!< media socket address */
struct sockaddr_in vsin; /*!< Video socket address */
struct hostent *hp; /*!< RTP Audio host IP */
struct hostent *vhp = NULL; /*!< RTP video host IP */
struct ast_hostent audiohp;
struct ast_hostent videohp;
int destiterator = 0;
int sendonly = 0;
int numberofports;
struct ast_channel *bridgepeer = NULL;
struct ast_rtp *newaudiortp, *newvideortp; /* Buffers for codec handling */
int newjointcapability; /* Negotiated capability */
int newpeercapability;
int newnoncodeccapability;
int numberofmediastreams = 0;
int debug = sip_debug_test_pvt(p);
int found_rtpmap_codecs[32];
int last_rtpmap_codec=0;
if (!p->rtp) {
ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n");
return -1;
}
/* Initialize the temporary RTP structures we use to evaluate the offer from the peer */
newaudiortp = alloca(ast_rtp_alloc_size());
memset(newaudiortp, 0, ast_rtp_alloc_size());
Joshua Colp
committed
ast_rtp_pt_clear(newaudiortp);
newvideortp = alloca(ast_rtp_alloc_size());
memset(newvideortp, 0, ast_rtp_alloc_size());
Joshua Colp
committed
ast_rtp_pt_clear(newvideortp);
/* Update our last rtprx when we receive an SDP, too */
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
/* Try to find first media stream */
destiterator = req->sdp_start;
c = get_sdp_iterate(&destiterator, req, "c");
if (ast_strlen_zero(m) || ast_strlen_zero(c)) {
ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c);
return -1;
}
/* Check for IPv4 address (not IPv6 yet) */
if (sscanf(c, "IN IP4 %256s", host) != 1) {
ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
return -1;
}
/* XXX This could block for a long time, and block the main thread! XXX */
hp = ast_gethostbyname(host, &audiohp);
if (!hp) {
ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
return -1;
}
vhp = hp; /* Copy to video address as default too */
iterator = req->sdp_start;
ast_set_flag(&p->flags[0], SIP_NOVIDEO);
/* Find media streams in this SDP offer */
while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') {
int x;
int audio = FALSE;
numberofports = 1;
if ((sscanf(m, "audio %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
(sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) {
audio = TRUE;
Olle Johansson
committed
numberofmediastreams++;
/* Found audio stream in this media definition */
portno = x;
/* Scan through the RTP payload types specified in a "m=" line: */
for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
return -1;
}
ast_verbose("Found RTP audio format %d\n", codec);
ast_rtp_set_m_type(newaudiortp, codec);
} else if ((sscanf(m, "video %d/%d RTP/AVP %n", &x, &numberofports, &len) == 2) ||
(sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) {
/* If it is not audio - is it video ? */
ast_clear_flag(&p->flags[0], SIP_NOVIDEO);
Olle Johansson
committed
numberofmediastreams++;
vportno = x;
/* Scan through the RTP payload types specified in a "m=" line: */
for (codecs = m + len; !ast_strlen_zero(codecs); codecs = ast_skip_blanks(codecs + len)) {
if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
return -1;
}
ast_verbose("Found RTP video format %d\n", codec);
ast_rtp_set_m_type(newvideortp, codec);
} else if (p->udptl && ( (sscanf(m, "image %d udptl t38%n", &x, &len) == 1) ||
(sscanf(m, "image %d UDPTL t38%n", &x, &len) == 1) )) {
if (debug)
ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
udptlportno = x;
Olle Johansson
committed
numberofmediastreams++;
if (p->owner && p->lastinvite) {
p->t38.state = T38_PEER_REINVITE; /* T38 Offered in re-invite from remote party */
if (option_debug > 1)
ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>" );
} else {
p->t38.state = T38_PEER_DIRECT; /* T38 Offered directly from peer in first invite */
if (option_debug > 1)
ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
}
} else
ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %s\n", m);
if (numberofports > 1)
ast_log(LOG_WARNING, "SDP offered %d ports for media, not supported by Asterisk. Will try anyway...\n", numberofports);
/* Check for Media-description-level-address for audio */
c = get_sdp_iterate(&destiterator, req, "c");
if (!ast_strlen_zero(c)) {
if (sscanf(c, "IN IP4 %256s", host) != 1) {
ast_log(LOG_WARNING, "Invalid secondary host in c= line, '%s'\n", c);
} else {
/* XXX This could block for a long time, and block the main thread! XXX */
if (audio) {
if ( !(hp = ast_gethostbyname(host, &audiohp)))
ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in secondary c= line, '%s'\n", c);
} else if (!(vhp = ast_gethostbyname(host, &videohp)))
ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c);
if (portno == -1 && vportno == -1 && udptlportno == -1)
/* No acceptable offer found in SDP - we have no ports */
/* Do not change RTP or VRTP if this is a re-invite */
return -2;
if (numberofmediastreams > 2)
Olle Johansson
committed
/* We have too many fax, audio and/or video media streams, fail this offer */
return -3;
/* RTP addresses and ports for audio and video */
vsin.sin_family = AF_INET;
memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
if (vhp)
memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr));
/* Setup UDPTL port number */
if (p->udptl) {
if (udptlportno > 0) {
sin.sin_port = htons(udptlportno);
ast_udptl_set_peer(p->udptl, &sin);
if (debug)
Russell Bryant
committed
ast_log(LOG_DEBUG,"Peer T.38 UDPTL is at port %s:%d\n",ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
} else {
ast_udptl_stop(p->udptl);
if (debug)
ast_log(LOG_DEBUG, "Peer doesn't provide T.38 UDPTL\n");
}
}
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
if (p->rtp) {
if (portno > 0) {
sin.sin_port = htons(portno);
ast_rtp_set_peer(p->rtp, &sin);
if (debug)
ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
} else {
if (udptlportno > 0) {
if (debug)
ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session. Callid %s\n", p->callid);
} else {
ast_rtp_stop(p->rtp);
if (debug)
ast_verbose("Peer doesn't provide audio. Callid %s\n", p->callid);
}
}
}
/* Setup video port number */
if (vportno != -1)
vsin.sin_port = htons(vportno);
/* Next, scan through each "a=rtpmap:" line, noting each
* specified RTP payload type (with corresponding MIME subtype):
*/
/* XXX This needs to be done per media stream, since it's media stream specific */
iterator = req->sdp_start;
while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
if (option_debug > 1) {
int breakout = FALSE;
/* If we're debugging, check for unsupported sdp options */
Olle Johansson
committed
if (!strncasecmp(a, "rtcp:", (size_t) 5)) {
if (debug)
ast_verbose("Got unsupported a:rtcp in SDP offer \n");
breakout = TRUE;
} else if (!strncasecmp(a, "fmtp:", (size_t) 5)) {
/* Format parameters: Not supported */
/* Note: This is used for codec parameters, like bitrate for
G722 and video formats for H263 and H264
See RFC2327 for an example */
if (debug)
ast_verbose("Got unsupported a:fmtp in SDP offer \n");
breakout = TRUE;
} else if (!strncasecmp(a, "framerate:", (size_t) 10)) {
/* Video stuff: Not supported */
if (debug)
ast_verbose("Got unsupported a:framerate in SDP offer \n");
breakout = TRUE;
} else if (!strncasecmp(a, "maxprate:", (size_t) 9)) {
/* Video stuff: Not supported */
if (debug)
ast_verbose("Got unsupported a:maxprate in SDP offer \n");
breakout = TRUE;
} else if (!strncasecmp(a, "crypto:", (size_t) 7)) {
/* SRTP stuff, not yet supported */
if (debug)
ast_verbose("Got unsupported a:crypto in SDP offer \n");
breakout = TRUE;