Newer
Older
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);
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_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) {
int natflags;
/* 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;
natflags = ast_test_flag(&p->flags[0], SIP_NAT) & SIP_NAT_ROUTE;
Kevin P. Fleming
committed
if (p->rtp)
ast_rtp_setnat(p->rtp, natflags);
if (p->vrtp)
ast_rtp_setnat(p->vrtp, natflags);
if (p->udptl)
ast_udptl_setnat(p->udptl, natflags);
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;
char *username=NULL, *hostname=NULL, *secret=NULL, *authuser=NULL;
char *porta=NULL;
char *contact=NULL;
char *stringp=NULL;
if (!value)
return -1;
Kevin P. Fleming
committed
ast_copy_string(copy, value, sizeof(copy));
username = stringp;
hostname = strrchr(stringp, '@');
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);
stringp = username;
if (username) {
secret = strsep(&stringp, ":");
if (secret)
authuser = strsep(&stringp, ":");
}
stringp = hostname;
hostname = strsep(&stringp, "/");
if (hostname)
contact = strsep(&stringp, "/");
if (ast_strlen_zero(contact))
stringp=hostname;
hostname = strsep(&stringp, ":");
if (porta && !atoi(porta)) {
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->portno = porta ? atoi(porta) : 0;
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);
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
/*!
\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);
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 (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2)
continue;
/* We have a rtpmap to handle */
if (debug)
ast_verbose("Found description format %s for ID %d\n", mimeSubtype, codec);
/* Note: should really look at the 'freq' and '#chans' params too */
Kevin P. Fleming
committed
ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype,
ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0);
if (p->vrtp)
Kevin P. Fleming
committed
ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0);
}
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
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);
}
if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1)) {
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;
}
}
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;
}
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);
}
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;
}
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;
}
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;
}
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) {
ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
/* Do NOT Change current setting */
return -1;
}
/* 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);
Mark Spencer
committed
}
}
/* Setup audio port number */
if (p->rtp && sin.sin_port) {
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));
}
/* Setup video port number */
if (p->vrtp && vsin.sin_port) {
ast_rtp_set_peer(p->vrtp, &vsin);
if (debug)
Russell Bryant
committed
ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port));
}
/* Ok, we're going with this offer */
if (option_debug > 1) {
char buf[BUFSIZ];
ast_log(LOG_DEBUG, "We're settling with these formats: %s\n", ast_getformatname_multiple(buf, BUFSIZ, p->jointcapability));
if (!p->owner) /* There's no open channel owning us so we can return here. For a re-invite or so, we proceed */
if (option_debug > 3)
ast_log(LOG_DEBUG, "We have an owner, now see if we need to change this call\n");
if (!(p->owner->nativeformats & p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
if (debug) {
char s1[BUFSIZ], s2[BUFSIZ];
ast_log(LOG_DEBUG, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n",
ast_getformatname_multiple(s1, BUFSIZ, p->jointcapability),
ast_getformatname_multiple(s2, BUFSIZ, p->owner->nativeformats));
}
p->owner->nativeformats = ast_codec_choose(&p->prefs, p->jointcapability, 1) | (p->capability & vpeercapability);
ast_set_read_format(p->owner, p->owner->readformat);
ast_set_write_format(p->owner, p->owner->writeformat);
}
bridgepeer = ast_bridged_channel(p->owner);
/* Turn on/off music on hold if we are holding/unholding */
if ((bridgepeer = ast_bridged_channel(p->owner))) {
if (sin.sin_addr.s_addr && !sendonly) {
Kevin P. Fleming
committed
ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
/* Activate a re-invite */
ast_queue_frame(p->owner, &ast_null_frame);
} else if (!sin.sin_addr.s_addr || sendonly) {
Kevin P. Fleming
committed
ast_queue_control_data(p->owner, AST_CONTROL_HOLD,
S_OR(p->mohsuggest, NULL),
!ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
if (sendonly)
ast_rtp_stop(p->rtp);
/* RTCP needs to go ahead, even if we're on hold!!! */
/* Activate a re-invite */
ast_queue_frame(p->owner, &ast_null_frame);
}
Kevin P. Fleming
committed
/* Manager Hold and Unhold events must be generated, if necessary */
Olle Johansson
committed
if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
append_history(p, "Unhold", "%s", req->data);
if (global_callevents)
manager_event(EVENT_FLAG_CALL, "Unhold",
"Channel: %s\r\n"
"Uniqueid: %s\r\n",
p->owner->name,
p->owner->uniqueid);
sip_peer_hold(p, 0);
Olle Johansson
committed
ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */
} else if (!sin.sin_addr.s_addr || sendonly ) {
Kevin P. Fleming
committed
/* No address for RTP, we're on hold */
Kevin P. Fleming
committed
Olle Johansson
committed
if (global_callevents && !ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
Kevin P. Fleming
committed
manager_event(EVENT_FLAG_CALL, "Hold",
"Channel: %s\r\n"
Kevin P. Fleming
committed
p->owner->name,
p->owner->uniqueid);
}
if (sendonly == 1) /* One directional hold (sendonly/recvonly) */
Olle Johansson
committed
ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ONEDIR);
else if (sendonly == 2) /* Inactive stream */
ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_INACTIVE);
sip_peer_hold(p, 1);
Kevin P. Fleming
committed
}
/*! \brief Add header to SIP message */
Kevin P. Fleming
committed
static int add_header(struct sip_request *req, const char *var, const char *value)
int maxlen = sizeof(req->data) - 4 - req->len; /* 4 bytes are for two \r\n ? */
if (req->headers == SIP_MAX_HEADERS) {
ast_log(LOG_WARNING, "Out of SIP header space\n");
Kevin P. Fleming
committed
if (req->lines) {
ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
return -1;
}
Kevin P. Fleming
committed
ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value);
return -1;
}
req->header[req->headers] = req->data + req->len;