Newer
Older
if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { /* Video */
if(ast_rtp_set_rtpmap_type(newvideortp, codec, "video", mimeSubtype, 0) != -1) {
if (debug)
ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec);
found_rtpmap_codecs[last_rtpmap_codec] = codec;
last_rtpmap_codec++;
} else {
ast_rtp_unset_m_type(newvideortp, codec);
if (debug)
ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec);
}
} else if (!strncasecmp(mimeSubtype, "T140",4)) { /* Text */
if (p->trtp) {
/* ast_verbose("Adding t140 mimeSubtype to textrtp struct\n"); */
ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0);
}
} else { /* Must be audio?? */
if(ast_rtp_set_rtpmap_type(newaudiortp, codec, "audio", mimeSubtype,
ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0) != -1) {
if (debug)
ast_verbose("Found audio description format %s for ID %d\n", mimeSubtype, codec);
found_rtpmap_codecs[last_rtpmap_codec] = codec;
last_rtpmap_codec++;
} else {
ast_rtp_unset_m_type(newaudiortp, codec);
if (debug)
ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec);
}
}
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;
ast_debug(3, "MaxBufferSize:%d\n",x);
} else if ((sscanf(a, "T38MaxBitRate:%d", &x) == 1)) {
found = 1;
ast_debug(3,"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;
ast_debug(3, "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;
ast_debug(3, "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;
ast_debug(3, "FillBitRemoval: %d\n",x);
if (x == 1)
peert38capability |= T38FAX_FILL_BIT_REMOVAL;
} else if ((sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1)) {
found = 1;
ast_debug(3, "Transcoding MMR: %d\n",x);
if (x == 1)
peert38capability |= T38FAX_TRANSCODING_MMR;
}
if ((sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1)) {
found = 1;
ast_debug(3, "Transcoding JBIG: %d\n",x);
if (x == 1)
peert38capability |= T38FAX_TRANSCODING_JBIG;
} else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) {
found = 1;
ast_debug(3, "RateManagement: %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:%255s", s) == 1)) {
found = 1;
ast_debug(3, "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_debug(1, "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;
ast_debug(3, "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);
ast_rtp_get_current_formats(newtextrtp, &tpeercapability, &tpeernoncodeccapability);
newjointcapability = p->capability & (peercapability | vpeercapability | tpeercapability);
newpeercapability = (peercapability | vpeercapability | tpeercapability);
newnoncodeccapability = p->noncodeccapability & peernoncodeccapability;
if (debug) {
/* shame on whoever coded this.... */
char s1[BUFSIZ], s2[BUFSIZ], s3[BUFSIZ], s4[BUFSIZ], s5[BUFSIZ];
ast_verbose("Capabilities: us - %s, peer - audio=%s/video=%s/text=%s, combined - %s\n",
Kevin P. Fleming
committed
ast_getformatname_multiple(s1, BUFSIZ, p->capability),
ast_getformatname_multiple(s2, BUFSIZ, peercapability),
Kevin P. Fleming
committed
ast_getformatname_multiple(s3, BUFSIZ, vpeercapability),
ast_getformatname_multiple(s4, BUFSIZ, tpeercapability),
ast_getformatname_multiple(s5, BUFSIZ, newjointcapability));
ast_verbose("Non-codec capabilities (dtmf): us - %s, peer - %s, combined - %s\n",
ast_rtp_lookup_mime_multiple(s1, BUFSIZ, p->noncodeccapability, 0, 0),
Kevin P. Fleming
committed
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 {
ast_debug(3, "Have T.38 but no audio codecs, accepting offer anyway\n");
}
/* 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->jointnoncodeccapability = newnoncodeccapability; /* DTMF capabilities */
ast_rtp_pt_copy(p->rtp, newaudiortp);
if (p->vrtp)
ast_rtp_pt_copy(p->vrtp, newvideortp);
if (p->trtp)
ast_rtp_pt_copy(p->trtp, newtextrtp);
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);
/* Since RFC2833 is now negotiated we need to change some properties of the RTP stream */
ast_rtp_setdtmf(p->rtp, 1);
ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE));
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));
}
/* Setup text port number */
if (p->trtp && tsin.sin_port) {
ast_rtp_set_peer(p->trtp, &tsin);
if (debug)
ast_verbose("Peer text RTP is at port %s:%d\n", ast_inet_ntoa(tsin.sin_addr), ntohs(tsin.sin_port));
}
/* Ok, we're going with this offer */
ast_debug(2, "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 */
ast_debug(4, "We have an owner, now see if we need to change this call\n");
if (!(p->owner->nativeformats & p->jointcapability) && (p->jointcapability & AST_FORMAT_AUDIO_MASK)) {
if (debug) {
char s1[BUFSIZ], s2[BUFSIZ];
ast_debug(1, "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) | (p->capability & tpeercapability);
ast_set_read_format(p->owner, p->owner->readformat);
ast_set_write_format(p->owner, p->owner->writeformat);
}
Joshua Colp
committed
if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) && sin.sin_addr.s_addr && (!sendonly || sendonly == -1)) {
ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
/* Activate a re-invite */
ast_queue_frame(p->owner, &ast_null_frame);
Joshua Colp
committed
/* Queue Manager Unhold event */
append_history(p, "Unhold", "%s", req->data);
if (global_callevents)
Olle Johansson
committed
manager_event(EVENT_FLAG_CALL, "Hold",
"Status: Off\r\n"
Joshua Colp
committed
"Channel: %s\r\n"
"Uniqueid: %s\r\n",
p->owner->name,
p->owner->uniqueid);
if (global_notifyhold)
sip_peer_hold(p, FALSE);
ast_clear_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */
} else if (!sin.sin_addr.s_addr || (sendonly && sendonly != -1)) {
int already_on_hold = ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD);
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);
Joshua Colp
committed
/* Queue Manager Hold event */
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",
Olle Johansson
committed
"Status: On\r\n"
Joshua Colp
committed
"Channel: %s\r\n"
"Uniqueid: %s\r\n",
p->owner->name,
p->owner->uniqueid);
Kevin P. Fleming
committed
}
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);
ast_set_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD_ACTIVE);
if (global_notifyhold && !already_on_hold)
Olle Johansson
committed
sip_peer_hold(p, TRUE);
Kevin P. Fleming
committed
}
/*! \brief Add header to SIP message */
Kevin P. Fleming
committed
static int add_header(struct sip_request *req, const char *var, const char *value)
int maxlen = sizeof(req->data) - 4 - req->len; /* 4 bytes are for two \r\n ? */
if (req->headers == SIP_MAX_HEADERS) {
ast_log(LOG_WARNING, "Out of SIP header space\n");
Kevin P. Fleming
committed
if (req->lines) {
ast_log(LOG_WARNING, "Can't add more headers when lines have been added\n");
return -1;
}
Kevin P. Fleming
committed
ast_log(LOG_WARNING, "Out of space, can't add anymore (%s:%s)\n", var, value);
return -1;
}
req->header[req->headers] = req->data + req->len;
Kevin P. Fleming
committed
if (compactheaders)
var = find_alias(var, var);
Kevin P. Fleming
committed
snprintf(req->header[req->headers], maxlen, "%s: %s\r\n", var, value);
Kevin P. Fleming
committed
/*! \brief Add 'Content-Length' header to SIP message */
static int add_header_contentLength(struct sip_request *req, int len)
{
char clen[10];
snprintf(clen, sizeof(clen), "%d", len);
return add_header(req, "Content-Length", clen);
}
/*! \brief Add content (not header) to SIP message */
Mark Spencer
committed
static int add_line(struct sip_request *req, const char *line)
if (req->lines == SIP_MAX_LINES) {
ast_log(LOG_WARNING, "Out of SIP line space\n");
if (!req->lines) {
/* Add extra empty return */
Russell Bryant
committed
ast_copy_string(req->data + req->len, "\r\n", sizeof(req->data) - req->len);
if (req->len >= sizeof(req->data) - 4) {
ast_log(LOG_WARNING, "Out of space, can't add anymore\n");
return -1;
}
snprintf(req->line[req->lines], sizeof(req->data) - req->len, "%s", line);
req->len += strlen(req->line[req->lines]);
/*! \brief Copy one header field from one request to another */
static int copy_header(struct sip_request *req, const struct sip_request *orig, const char *field)
const char *tmp = get_header(orig, field);
if (!ast_strlen_zero(tmp)) /* Add what we're responding to */
return add_header(req, field, tmp);
ast_log(LOG_NOTICE, "No field '%s' present to copy\n", field);
return -1;
}
/*! \brief Copy all headers from one request to another */
static int copy_all_header(struct sip_request *req, const struct sip_request *orig, const char *field)
{
int start = 0;
int copied = 0;
for (;;) {
const char *tmp = __get_header(orig, field, &start);
if (ast_strlen_zero(tmp))
/* Add what we're responding to */
add_header(req, field, tmp);
copied++;
/*! \brief Copy SIP VIA Headers from the request to the response
\note If the client indicates that it wishes to know the port we received from,
it adds ;rport without an argument to the topmost via header. We need to
add the port number (from our point of view) to that parameter.
We always add ;received=<ip address> to the topmost via header.
Received: RFC 3261, rport RFC 3581 */
static int copy_via_headers(struct sip_pvt *p, struct sip_request *req, const struct sip_request *orig, const char *field)
const char *oh = __get_header(orig, field, &start);
if (ast_strlen_zero(oh))
break;
if (!copied) { /* Only check for empty rport in topmost via header */
char leftmost[256], *others, *rport;
/* Only work on leftmost value */
ast_copy_string(leftmost, oh, sizeof(leftmost));
others = strchr(leftmost, ',');
if (others)
*others++ = '\0';
rport = strstr(leftmost, ";rport");
if (rport && *(rport+6) == '=')
rport = NULL; /* We already have a parameter to rport */
/* Check rport if NAT=yes or NAT=rfc3581 (which is the default setting) */
if (rport && ((ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_ALWAYS) || (ast_test_flag(&p->flags[0], SIP_NAT) == SIP_NAT_RFC3581))) {
rport = strstr(leftmost, ";rport");
if (rport) {
end = strchr(rport + 1, ';');
if (end)
memmove(rport, end, strlen(end) + 1);
else
*rport = '\0';
/* Add rport to first VIA header if requested */
snprintf(new, sizeof(new), "%s;received=%s;rport=%d%s%s",
leftmost, ast_inet_ntoa(p->recv.sin_addr),
ntohs(p->recv.sin_port),
others ? "," : "", others ? others : "");
} else {
/* We should *always* add a received to the topmost via */
snprintf(new, sizeof(new), "%s;received=%s%s%s",
leftmost, ast_inet_ntoa(p->recv.sin_addr),
others ? "," : "", others ? others : "");
}
oh = new; /* the header to copy */
} /* else add the following via headers untouched */
add_header(req, field, oh);
copied++;
ast_log(LOG_NOTICE, "No header field '%s' present to copy\n", field);
return -1;
}
return 0;
}
/*! \brief Add route header into request per learned route */
static void add_route(struct sip_request *req, struct sip_route *route)
{
Olle Johansson
committed
char r[BUFSIZ*2], *p;
Kevin P. Fleming
committed
int n, rem = sizeof(r);
if (rem < n+3) /* we need room for ",<route>" */
if (p != r) { /* add a separator after fist route */
*p++ = ',';
--rem;
}
*p++ = '<';
ast_copy_string(p, route->hop, rem); /* cannot fail */
p += n;
*p++ = '>';
rem -= (n+2);
}
*p = '\0';
add_header(req, "Route", r);
}
/*! \brief Set destination from SIP URI */
static void set_destination(struct sip_pvt *p, char *uri)
{
char *h, *maddr, hostname[256];
int port, hn;
struct hostent *hp;
/* Parse uri to h (host) and port - uri is already just the part inside the <> */
Olle Johansson
committed
/* general form we are expecting is sip[s]:username[:password][;parameter]@host[:port][;...] */
ast_verbose("set_destination: Parsing <%s> for address/port to send to\n", uri);
if (strncasecmp(h, "sip:", 4) == 0)
else if (strncasecmp(h, "sips:", 5) == 0)
Kevin P. Fleming
committed
hn = strcspn(h, ":;>") + 1;
if (hn > sizeof(hostname))
hn = sizeof(hostname);
Kevin P. Fleming
committed
ast_copy_string(hostname, h, hn);
/* XXX bug here if string has been trimmed to sizeof(hostname) */
Kevin P. Fleming
committed
h += hn - 1;
/* Is "port" present? if not default to STANDARD_SIP_PORT */
if (*h == ':') {
/* Parse port */
++h;
port = strtol(h, &h, 10);
}
else
port = STANDARD_SIP_PORT;
/* Got the hostname:port - but maybe there's a "maddr=" to override address? */
maddr = strstr(h, "maddr=");
Kevin P. Fleming
committed
hn = strspn(maddr, "0123456789.") + 1;
if (hn > sizeof(hostname))
hn = sizeof(hostname);
hp = ast_gethostbyname(hostname, &ahp);
ast_log(LOG_WARNING, "Can't find address for host '%s'\n", hostname);
return;
}
p->sa.sin_family = AF_INET;
memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr));
p->sa.sin_port = htons(port);
Russell Bryant
committed
ast_verbose("set_destination: set destination to %s, port %d\n", ast_inet_ntoa(p->sa.sin_addr), port);
/*! \brief Initialize SIP response, based on SIP request */
static int init_resp(struct sip_request *resp, const char *msg)
memset(resp, 0, sizeof(*resp));
resp->method = SIP_RESPONSE;
resp->header[0] = resp->data;
snprintf(resp->header[0], sizeof(resp->data), "SIP/2.0 %s\r\n", msg);
resp->len = strlen(resp->header[0]);
resp->headers++;
/*! \brief Initialize SIP request */
static int init_req(struct sip_request *req, int sipmethod, const char *recip)
/* Initialize a request */
memset(req, 0, sizeof(*req));
req->method = sipmethod;
req->header[0] = req->data;
snprintf(req->header[0], sizeof(req->data), "%s %s SIP/2.0\r\n", sip_methods[sipmethod].text, recip);
req->len = strlen(req->header[0]);
/*! \brief Prepare SIP response packet */
static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, const struct sip_request *req)
char newto[256];
const char *ot;
Mark Spencer
committed
init_resp(resp, msg);
copy_via_headers(p, resp, req, "Via");
if (msg[0] == '1' || msg[0] == '2')
if (!strcasestr(ot, "tag=") && strncmp(msg, "100", 3)) {
/* Add the proper tag if we don't have it already. If they have specified
their tag, use it. Otherwise, use our own tag */
if (!ast_strlen_zero(p->theirtag) && ast_test_flag(&p->flags[0], SIP_OUTGOING))
snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
else if (p->tag && !ast_test_flag(&p->flags[0], SIP_OUTGOING))
Kevin P. Fleming
committed
snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
Kevin P. Fleming
committed
ast_copy_string(newto, ot, sizeof(newto));
ot = newto;
}
add_header(resp, "To", ot);
copy_header(resp, req, "Call-ID");
copy_header(resp, req, "CSeq");
if (!ast_strlen_zero(global_useragent))
add_header(resp, "User-Agent", global_useragent);
add_header(resp, "Supported", SUPPORTED_EXTENSIONS);
if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) {
/* For registration responses, we also need expiry and
if (p->expiry) { /* Only add contact if we have an expiry time */
snprintf(contact, sizeof(contact), "%s;expires=%d", p->our_contact, p->expiry);
add_header(resp, "Contact", contact); /* Not when we unregister */
}
} else if (msg[0] != '4' && !ast_strlen_zero(p->our_contact)) {
add_header(resp, "Contact", p->our_contact);
Kevin P. Fleming
committed
Joshua Colp
committed
if (!ast_strlen_zero(p->url)) {
ast_string_field_set(p, url, NULL);
Kevin P. Fleming
committed
/*! \brief Initialize a SIP request message (not the initial one in a dialog) */
static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, int seqno, int newbranch)
char stripped[80];
const char *c;
const char *ot, *of;
int is_strict = FALSE; /*!< Strict routing flag */
int is_outbound = ast_test_flag(&p->flags[0], SIP_OUTGOING); /* Session direction */
memset(req, 0, sizeof(struct sip_request));
snprintf(p->lastmsg, sizeof(p->lastmsg), "Tx: %s", sip_methods[sipmethod].text);
if (!seqno) {
seqno = p->ocseq;
}
Mark Spencer
committed
if (newbranch) {
Tilghman Lesher
committed
p->branch ^= ast_random();
build_via(p);
Mark Spencer
committed
}
/* Check for strict or loose router */
if (p->route && !ast_strlen_zero(p->route->hop) && strstr(p->route->hop,";lr") == NULL) {
ast_debug(1, "Strict routing enforced for session %s\n", p->callid);
c = p->initreq.rlPart2; /* Use original URI */
/* Use URI from Contact: in 200 OK (if INVITE)
(we only have the contacturi on INVITEs) */
if (!ast_strlen_zero(p->okcontacturi))
c = is_strict ? p->route->hop : p->okcontacturi;
else
c = p->initreq.rlPart2;
} else if (!ast_strlen_zero(p->okcontacturi))
c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */
char *n;
/* We have no URI, use To: or From: header as URI (depending on direction) */
ast_copy_string(stripped, get_header(orig, is_outbound ? "To" : "From"),
sizeof(stripped));
n = get_in_brackets(stripped);
Olle Johansson
committed
c = remove_uri_parameters(n);
init_req(req, sipmethod, c);
snprintf(tmp, sizeof(tmp), "%d %s", seqno, sip_methods[sipmethod].text);
if (p->route) {
set_destination(p, p->route->hop);
add_route(req, is_strict ? p->route->next : p->route);
add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
ot = get_header(orig, "To");
of = get_header(orig, "From");
/* Add tag *unless* this is a CANCEL, in which case we need to send it exactly
as our original request, including tag (or presumably lack thereof) */
if (!strcasestr(ot, "tag=") && sipmethod != SIP_CANCEL) {
/* Add the proper tag if we don't have it already. If they have specified
their tag, use it. Otherwise, use our own tag */
if (is_outbound && !ast_strlen_zero(p->theirtag))
snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->theirtag);
else if (!is_outbound)
Kevin P. Fleming
committed
snprintf(newto, sizeof(newto), "%s;tag=%s", ot, p->tag);
else
snprintf(newto, sizeof(newto), "%s", ot);
if (is_outbound) {
add_header(req, "From", of);
add_header(req, "To", ot);
} else {
add_header(req, "From", ot);
add_header(req, "To", of);
}
/* Do not add Contact for MESSAGE, BYE and Cancel requests */
if (sipmethod != SIP_BYE && sipmethod != SIP_CANCEL && sipmethod != SIP_MESSAGE)
add_header(req, "Contact", p->our_contact);
if (!ast_strlen_zero(global_useragent))
add_header(req, "User-Agent", global_useragent);
Kevin P. Fleming
committed
if (!ast_strlen_zero(p->rpid))
Kevin P. Fleming
committed
add_header(req, "Remote-Party-ID", p->rpid);
Kevin P. Fleming
committed
Joshua Colp
committed
if (!ast_strlen_zero(p->url)) {
ast_string_field_set(p, url, NULL);
Kevin P. Fleming
committed
/*! \brief Base transmit response function */
static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
Kevin P. Fleming
committed
if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) {
ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
return -1;
}
add_header_contentLength(&resp, 0);
Kevin P. Fleming
committed
/* If we are cancelling an incoming invite for some reason, add information
about the reason why we are doing this in clear text */
if (p->method == SIP_INVITE && msg[0] != '1' && p->owner && p->owner->hangupcause) {
char buf[10];
add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
Kevin P. Fleming
committed
}
return send_response(p, &resp, reliable, seqno);
}
Kevin P. Fleming
committed
static int temp_pvt_init(void *data)
{
struct sip_pvt *p = data;
p->do_history = 0; /* XXX do we need it ? isn't already all 0 ? */
Kevin P. Fleming
committed
return ast_string_field_init(p, 512);
}
Russell Bryant
committed
static void temp_pvt_cleanup(void *data)
{
struct sip_pvt *p = data;
ast_string_field_free_memory(p);
Russell Bryant
committed
Tilghman Lesher
committed
ast_free(data);
Russell Bryant
committed
}
/*! \brief Transmit response, no retransmits, using a temporary pvt structure */
static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg)
{
struct sip_pvt *p = NULL;
if (!(p = ast_threadstorage_get(&ts_temp_pvt, sizeof(*p)))) {
ast_log(LOG_NOTICE, "Failed to get temporary pvt\n");
return -1;
}
/* XXX the structure may be dirty from previous usage.
* Here we should state clearly how we should reinitialize it
* before using it.
* E.g. certainly the threadstorage should be left alone,
* but other thihngs such as flags etc. maybe need cleanup ?
*/
/* Initialize the bare minimum */
p->method = intended_method;
if (!sin)
p->ourip = internip;
else {
ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
p->branch = ast_random();
make_our_tag(p->tag, sizeof(p->tag));
p->ocseq = INITIAL_CSEQ;
if (useglobal_nat && sin) {
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);
}
ast_string_field_set(p, fromdomain, default_fromdomain);
build_via(p);
ast_string_field_set(p, callid, callid);
/* Use this temporary pvt structure to send the message */
__transmit_response(p, msg, req, XMIT_UNRELIABLE);
/* Free the string fields, but not the pool space */
ast_string_field_init(p, 0);
/*! \brief Transmit response, no retransmits */
static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req)
return __transmit_response(p, msg, req, XMIT_UNRELIABLE);
/*! \brief Transmit response, no retransmits */
static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported)
{
struct sip_request resp;
respprep(&resp, p, msg, req);
append_date(&resp);
add_header(&resp, "Unsupported", unsupported);
add_header_contentLength(&resp, 0);
return send_response(p, &resp, XMIT_UNRELIABLE, 0);
}
/*! \brief Transmit response, Make sure you get an ACK
This is only used for responses to INVITEs, where we need to make sure we get an ACK
*/
static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req)
return __transmit_response(p, msg, req, XMIT_CRITICAL);
/*! \brief Append date to SIP message */
static void append_date(struct sip_request *req)
{
char tmpdat[256];
struct tm tm;
time_t t = time(NULL);
gmtime_r(&t, &tm);
strftime(tmpdat, sizeof(tmpdat), "%a, %d %b %Y %T GMT", &tm);
add_header(req, "Date", tmpdat);
}
/*! \brief Append date and content length before transmitting response */
static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req)
{
struct sip_request resp;
respprep(&resp, p, msg, req);
append_date(&resp);
add_header_contentLength(&resp, 0);
return send_response(p, &resp, XMIT_UNRELIABLE, 0);
/*! \brief Append Accept header, content length before transmitting response */
static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
{
struct sip_request resp;
respprep(&resp, p, msg, req);
add_header(&resp, "Accept", "application/sdp");
add_header_contentLength(&resp, 0);
return send_response(p, &resp, reliable, 0);
/*! \brief Respond with authorization request */
static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *randdata, enum xmittype reliable, const char *header, int stale)
Kevin P. Fleming
committed
if (reliable && (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1)) {
ast_log(LOG_WARNING, "Unable to determine sequence number from '%s'\n", get_header(req, "CSeq"));
return -1;
}
Kevin P. Fleming
committed
/* Stale means that they sent us correct authentication, but
based it on an old challenge (nonce) */
snprintf(tmp, sizeof(tmp), "Digest algorithm=MD5, realm=\"%s\", nonce=\"%s\"%s", global_realm, randdata, stale ? ", stale=true" : "");
Mark Spencer
committed
add_header(&resp, header, tmp);
add_header_contentLength(&resp, 0);
append_history(p, "AuthChal", "Auth challenge sent for %s - nc %d", p->username, p->noncecount);
return send_response(p, &resp, reliable, seqno);
/*! \brief Add text body to SIP message */
Mark Spencer
committed
static int add_text(struct sip_request *req, const char *text)
{
/* XXX Convert \n's to \r\n's XXX */
add_header(req, "Content-Type", "text/plain");
add_header_contentLength(req, strlen(text));
/*! \brief Add DTMF INFO tone to sip message
Mode = 0 for application/dtmf-relay (Cisco)
1 for application/dtmf
*/
static int add_digit(struct sip_request *req, char digit, unsigned int duration, int mode)
6921
6922
6923
6924
6925
6926
6927
6928
6929
6930
6931
6932
6933
6934
6935
6936
6937
6938
6939
6940
6941
6942
int event;
if (mode) {
/* Application/dtmf short version used by some implementations */
if (digit == '*')
event = 10;
else if (digit == '#')
event = 11;
else if ((digit >= 'A') && (digit <= 'D'))
event = 12 + digit - 'A';
else
event = atoi(&digit);
snprintf(tmp, sizeof(tmp), "%d\r\n", event);
add_header(req, "Content-Type", "application/dtmf");
add_header_contentLength(req, strlen(tmp));
add_line(req, tmp);
} else {
/* Application/dtmf-relay as documented by Cisco */
snprintf(tmp, sizeof(tmp), "Signal=%c\r\nDuration=%u\r\n", digit, duration);
add_header(req, "Content-Type", "application/dtmf-relay");
add_header_contentLength(req, strlen(tmp));
add_line(req, tmp);
}
/*! \brief add XML encoded media control with update
\note XML: The only way to turn 0 bits of information into a few hundred. (markster) */
static int add_vidupdate(struct sip_request *req)
{
const char *xml_is_a_huge_waste_of_space =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n"
" <media_control>\r\n"
" <vc_primitive>\r\n"
" <to_encoder>\r\n"
" <picture_fast_update>\r\n"
" </picture_fast_update>\r\n"
" </to_encoder>\r\n"
" </vc_primitive>\r\n"
" </media_control>\r\n";
add_header(req, "Content-Type", "application/media_control+xml");
add_header_contentLength(req, strlen(xml_is_a_huge_waste_of_space));
add_line(req, xml_is_a_huge_waste_of_space);
return 0;
}
/*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
Kevin P. Fleming
committed
static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
struct ast_str **m_buf, struct ast_str **a_buf,
int debug, int *min_packet_size)
Kevin P. Fleming
committed
{
int rtp_code;
struct ast_format_list fmt;
Kevin P. Fleming
committed
if (debug)
ast_verbose("Adding codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec));
if ((rtp_code = ast_rtp_lookup_code(p->rtp, 1, codec)) == -1)
return;
if (p->rtp) {
struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp);
fmt = ast_codec_pref_getsize(pref, codec);
} else /* I dont see how you couldn't have p->rtp, but good to check for and error out if not there like earlier code */
return;
ast_str_append(m_buf, 0, " %d", rtp_code);
ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code,
Kevin P. Fleming
committed
ast_rtp_lookup_mime_subtype(1, codec,
ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0),
Kevin P. Fleming
committed
sample_rate);
if (codec == AST_FORMAT_G729A) {
/* Indicate that we don't support VAD (G.729 annex B) */
ast_str_append(a_buf, 0, "a=fmtp:%d annexb=no\r\n", rtp_code);
} else if (codec == AST_FORMAT_G723_1) {
/* Indicate that we don't support VAD (G.723.1 annex A) */
ast_str_append(a_buf, 0, "a=fmtp:%d annexa=no\r\n", rtp_code);
} else if (codec == AST_FORMAT_ILBC) {
/* Add information about us using only 20/30 ms packetization */
ast_str_append(a_buf, 0, "a=fmtp:%d mode=%d\r\n", rtp_code, fmt.cur_ms);