Newer
Older
struct sip_pvt *p = ast->tech_pvt;
ast_mutex_lock(&p->lock);
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_t38_sdp(p);
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);
}
}
ast_mutex_unlock(&p->lock);
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->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 = 500; /* 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
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));
Kevin P. Fleming
committed
ast_mutex_destroy(&p->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);
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_udptl_settos(p->udptl, global_tos_audio);
Kevin P. Fleming
committed
p->rtptimeout = global_rtptimeout;
p->rtpholdtimeout = global_rtpholdtimeout;
p->rtpkeepalive = global_rtpkeepalive;
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 */
ast_mutex_lock(&iflock);
ast_mutex_unlock(&iflock);
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");
if (!callid || !to || !from || !cseq) /* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
return NULL; /* Invalid packet */
/* 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);
ast_mutex_lock(&iflock);
for (p = iflist; 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) {
ast_mutex_lock(&p->lock);
ast_mutex_unlock(&iflock);
ast_mutex_unlock(&iflock);
Olle Johansson
committed
/* Allocate new call */
if ((p = sip_alloc(callid, sin, 1, intended_method)))
ast_mutex_lock(&p->lock);
/*! \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;
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';
/* split host[:port][/contact] */
contact = strchr(hostname, '/');
if (contact)
*contact++ = '\0';
if (ast_strlen_zero(contact))
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);
ast_string_field_set(reg, contact, contact);
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);
reg->expire = -1;
reg->timeout = -1;
reg->refresh = default_expiry;
reg->ocseq = INITIAL_CSEQ;
ASTOBJ_CONTAINER_LINK(®l, reg); /* Add the new registry entry to the list */
ASTOBJ_UNREF(reg,sip_registry_destroy);
/*! \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)
c = req->data;
/* First header starts immediately */
req->header[f] = c;
while(*c) {
if (*c == '\n') {
/* We've got a new header */
*c = 0;
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "Header %d: %s (%d)\n", f, req->header[f], (int) strlen(req->header[f]));
if (ast_strlen_zero(req->header[f])) {
/* Line by itself means we're now in content */
c++;
break;
}
if (f >= SIP_MAX_HEADERS - 1) {
ast_log(LOG_WARNING, "Too many SIP headers. Ignoring.\n");
} else
f++;
req->header[f] = c + 1;
} else if (*c == '\r') {
/* Ignore but eliminate \r's */
*c = 0;
if (!ast_strlen_zero(req->header[f])) {
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "Header %d: %s (%d)\n", f, req->header[f], (int) strlen(req->header[f]));
}
req->headers = f;
/* Now we process any mime content */
f = 0;
req->line[f] = c;
while(*c) {
if (*c == '\n') {
/* We've got a new line */
*c = 0;
Russell Bryant
committed
if (sipdebug && option_debug > 3)
ast_log(LOG_DEBUG, "Line: %s (%d)\n", req->line[f], (int) strlen(req->line[f]));
ast_log(LOG_WARNING, "Too many SDP lines. Ignoring.\n");
} else
f++;
req->line[f] = c + 1;
} else if (*c == '\r') {
/* Ignore and eliminate \r's */
*c = 0;
}
c++;
}
/* Check for last line */
if (!ast_strlen_zero(req->line[f]))
f++;
req->lines = f;
if (*c)
ast_log(LOG_WARNING, "Odd content, extra stuff left over ('%s')\n", c);
/* Split up the first line parts */
determine_firstline_parts(req);
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
/*!
\brief Determine whether a SIP message contains an SDP in its body
\param req the SIP request to process
\return 1 if SDP found, 0 if not found
Also updates req->sdp_start and req->sdp_end to indicate where the SDP
lives in the message body.
*/
static int find_sdp(struct sip_request *req)
{
const char *content_type;
const char *search;
char *boundary;
unsigned int x;
content_type = get_header(req, "Content-Type");
/* if the body contains only SDP, this is easy */
if (!strcasecmp(content_type, "application/sdp")) {
req->sdp_start = 0;
req->sdp_end = req->lines;
return 1;
}
/* if it's not multipart/mixed, there cannot be an SDP */
if (strncasecmp(content_type, "multipart/mixed", 15))
return 0;
/* if there is no boundary marker, it's invalid */
if (!(search = strcasestr(content_type, ";boundary=")))
return 0;
search += 10;
if (ast_strlen_zero(search))
return 0;
/* make a duplicate of the string, with two extra characters
at the beginning */
boundary = ast_strdupa(search - 2);
boundary[0] = boundary[1] = '-';
/* search for the boundary marker, but stop when there are not enough
lines left for it, the Content-Type header and at least one line of
body */
for (x = 0; x < (req->lines - 2); x++) {
if (!strncasecmp(req->line[x], boundary, strlen(boundary)) &&
!strcasecmp(req->line[x + 1], "Content-Type: application/sdp")) {
req->sdp_start = x + 2;
/* search for the end of the body part */
for ( ; x < req->lines; x++) {
if (!strncasecmp(req->line[x], boundary, strlen(boundary)))
break;
}
req->sdp_end = x;
return 1;
}
}
return 0;
}
/*! \brief Process SIP SDP offer, select formats and activate RTP channels
If offer is rejected, we will not change any properties of the call
*/
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))) {
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));
if (p->rtp) {
if (portno > 0) {
sin.sin_port = htons(portno);
ast_rtp_set_peer(p->rtp, &sin);
if (debug)
Russell Bryant
committed
ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
} else {
ast_rtp_stop(p->rtp);
if (debug)
ast_verbose("Peer doesn't provide audio\n");
}
}
/* Setup video port number */
if (vportno != -1)
vsin.sin_port = htons(vportno);
/* Setup UDPTL port number */
if (p->udptl) {
if (udptlportno > 0) {
sin.sin_port = htons(udptlportno);
ast_udptl_set_peer(p->udptl, &sin);
if (debug)
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");
}
}
/* 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;
} else if (!strncasecmp(a, "ptime:", (size_t) 6)) {
if (debug)
ast_verbose("Got unsupported a:ptime in SDP offer \n");
breakout = TRUE;
}
if (breakout) /* We have a match, skip to next header */
continue;
}
if (!strcasecmp(a, "sendonly")) {
sendonly = 1;
continue;
Olle Johansson
committed
} else if (!strcasecmp(a, "inactive")) {
sendonly = 2;
continue;
} else if (!strcasecmp(a, "sendrecv")) {
sendonly = 0;
continue;
} else if (strlen(a) > 5 && !strncasecmp(a, "ptime", 5)) {
char *tmp = strrchr(a, ':');
long int framing = 0;
if (tmp) {
tmp++;
framing = strtol(tmp, NULL, 10);
if (framing == LONG_MIN || framing == LONG_MAX) {
framing = 0;
if (option_debug)
ast_log(LOG_DEBUG, "Can't read framing from SDP: %s\n", a);
}
}
if (framing && last_rtpmap_codec) {
if (p->autoframing || global_autoframing) {
struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp);
int codec_n;
int format = 0;
for (codec_n = 0; codec_n < last_rtpmap_codec; codec_n++) {
format = ast_rtp_codec_getformat(found_rtpmap_codecs[codec_n]);
if (!format) /* non-codec or not found */
continue;
if (option_debug)
ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format, framing);
ast_codec_pref_setsize(pref, format, framing);
}
ast_rtp_codec_setpref(p->rtp, pref);
}
}
memset(&found_rtpmap_codecs, 0, sizeof(found_rtpmap_codecs));
last_rtpmap_codec = 0;
continue;
} else if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) == 2) {
/* We have a rtpmap to handle */
if (debug)
ast_verbose("Found description format %s for ID %d\n", mimeSubtype, codec);
found_rtpmap_codecs[last_rtpmap_codec] = codec;
last_rtpmap_codec++;
/* Note: should really look at the 'freq' and '#chans' params too */
ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype,
Kevin P. Fleming
committed
ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0);
if (p->vrtp)
ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0);
}
}
if (udptlportno != -1) {
int found = 0, x;
old = 0;
/* Scan trough the a= lines for T38 attributes and set apropriate fileds */
iterator = req->sdp_start;
while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
if ((sscanf(a, "T38FaxMaxBuffer:%d", &x) == 1)) {
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG, "MaxBufferSize:%d\n",x);
} else if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1)) {
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG,"T38MaxBitRate: %d\n",x);
switch (x) {
case 14400:
peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
break;
case 12000:
peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
break;
case 9600:
peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
break;
case 7200:
peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
break;
case 4800:
peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
break;
case 2400:
peert38capability |= T38FAX_RATE_2400;
break;
}
} else if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) {
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG, "FaxVersion: %d\n",x);
if (x == 0)
peert38capability |= T38FAX_VERSION_0;
else if (x == 1)
peert38capability |= T38FAX_VERSION_1;
} else if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1)) {
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG, "FaxMaxDatagram: %d\n",x);
ast_udptl_set_far_max_datagram(p->udptl, x);
ast_udptl_set_local_max_datagram(p->udptl, x);
} else if ((sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1)) {
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG, "FillBitRemoval: %d\n",x);
if (x == 1)
peert38capability |= T38FAX_FILL_BIT_REMOVAL;
} else if ((sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1)) {
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG, "Transcoding MMR: %d\n",x);
if (x == 1)
peert38capability |= T38FAX_TRANSCODING_MMR;
}
if ((sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1)) {
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG, "Transcoding JBIG: %d\n",x);
if (x == 1)
peert38capability |= T38FAX_TRANSCODING_JBIG;
} else if ((sscanf(a, "T38FaxRateManagement:%s", s) == 1)) {
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG, "RateMangement: %s\n", s);
if (!strcasecmp(s, "localTCF"))
peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
else if (!strcasecmp(s, "transferredTCF"))
peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERED_TCF;
} else if ((sscanf(a, "T38FaxUdpEC:%s", s) == 1)) {
found = 1;
if (option_debug > 2)
ast_log(LOG_DEBUG, "UDP EC: %s\n", s);
if (!strcasecmp(s, "t38UDPRedundancy")) {
peert38capability |= T38FAX_UDP_EC_REDUNDANCY;
ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
} else if (!strcasecmp(s, "t38UDPFEC")) {
peert38capability |= T38FAX_UDP_EC_FEC;
ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
} else {
peert38capability |= T38FAX_UDP_EC_NONE;
ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
}
}
}
if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */
p->t38.peercapability = peert38capability;
p->t38.jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */
peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400);
p->t38.jointcapability |= (peert38capability & p->t38.capability); /* Put the lower of our's and peer's speed */
}
if (debug)
ast_log(LOG_DEBUG, "Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n",
p->t38.capability,
p->t38.peercapability,
p->t38.jointcapability);
} else {
p->t38.state = T38_DISABLED;
if (option_debug > 2)
ast_log(LOG_DEBUG, "T38 state changed to %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
}
/* Now gather all of the codecs that we are asked for: */
Joshua Colp
committed
ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability);
ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability);
newjointcapability = p->capability & (peercapability | vpeercapability);
newpeercapability = (peercapability | vpeercapability);
newnoncodeccapability = noncodeccapability & peernoncodeccapability;
if (debug) {
/* shame on whoever coded this.... */
char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ];
ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s, combined - %s\n",
Kevin P. Fleming
committed
ast_getformatname_multiple(s1, BUFSIZ, p->capability),
ast_getformatname_multiple(s2, BUFSIZ, newpeercapability),
ast_getformatname_multiple(s3, BUFSIZ, vpeercapability),
ast_getformatname_multiple(s4, BUFSIZ, newjointcapability));
ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
Kevin P. Fleming
committed
ast_rtp_lookup_mime_multiple(s1, BUFSIZ, noncodeccapability, 0, 0),
ast_rtp_lookup_mime_multiple(s2, BUFSIZ, peernoncodeccapability, 0, 0),
ast_rtp_lookup_mime_multiple(s3, BUFSIZ, newnoncodeccapability, 0, 0));
}
if (!newjointcapability) {
/* If T.38 was not negotiated either, totally bail out... */
if (!p->t38.jointcapability) {
ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
/* Do NOT Change current setting */
return -1;
} else {
if (option_debug > 2)
ast_log(LOG_DEBUG, "Have T.38 but no audio codecs, accepting offer anyway\n");
return 0;
}
}
/* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
they are acceptable */
p->jointcapability = newjointcapability; /* Our joint codec profile for this call */
p->peercapability = newpeercapability; /* The other sides capability in latest offer */
p->noncodeccapability = newnoncodeccapability; /* DTMF capabilities */
ast_rtp_pt_copy(p->rtp, newaudiortp);
if (p->vrtp)
ast_rtp_pt_copy(p->vrtp, newvideortp);
if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
if (newnoncodeccapability & AST_RTP_DTMF) {
Mark Spencer
committed
/* 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);