Newer
Older
if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size))
*min_packet_size = fmt.cur_ms;
/* Our first codec packetization processed cannot be zero */
if ((*min_packet_size)==0 && fmt.cur_ms)
*min_packet_size = fmt.cur_ms;
Kevin P. Fleming
committed
}
/*! \brief Add video codec offer to SDP offer/answer body in INVITE or 200 OK */
/* This is different to the audio one now so we can add more caps later */
static void add_vcodec_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)
{
int rtp_code;
if (!p->vrtp)
return;
if (debug)
ast_verbose("Adding video codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec));
if ((rtp_code = ast_rtp_lookup_code(p->vrtp, 1, codec)) == -1)
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,
ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate);
/* Add fmtp code here */
}
/*! \brief Add text codec offer to SDP offer/answer body in INVITE or 200 OK */
static void add_tcodec_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)
{
int rtp_code;
if (!p->trtp)
return;
if (debug)
ast_verbose("Adding text codec 0x%x (%s) to SDP\n", codec, ast_getformatname(codec));
if ((rtp_code = ast_rtp_lookup_code(p->trtp, 1, codec)) == -1)
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,
ast_rtp_lookup_mime_subtype(1, codec, 0), sample_rate);
/* Add fmtp code here */
}
Kevin P. Fleming
committed
/*! \brief Get Max T.38 Transmission rate from T38 capabilities */
static int t38_get_rate(int t38cap)
{
int maxrate = (t38cap & (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400));
if (maxrate & T38FAX_RATE_14400) {
ast_debug(2, "T38MaxFaxRate 14400 found\n");
return 14400;
} else if (maxrate & T38FAX_RATE_12000) {
ast_debug(2, "T38MaxFaxRate 12000 found\n");
return 12000;
} else if (maxrate & T38FAX_RATE_9600) {
ast_debug(2, "T38MaxFaxRate 9600 found\n");
return 9600;
} else if (maxrate & T38FAX_RATE_7200) {
ast_debug(2, "T38MaxFaxRate 7200 found\n");
return 7200;
} else if (maxrate & T38FAX_RATE_4800) {
ast_debug(2, "T38MaxFaxRate 4800 found\n");
return 4800;
} else if (maxrate & T38FAX_RATE_2400) {
ast_debug(2, "T38MaxFaxRate 2400 found\n");
return 2400;
} else {
ast_debug(2, "Strange, T38MaxFaxRate NOT found in peers T38 SDP.\n");
return 0;
}
}
/*! \brief Add T.38 Session Description Protocol message */
static int add_t38_sdp(struct sip_request *resp, struct sip_pvt *p)
{
int len = 0;
int x = 0;
struct sockaddr_in udptlsin;
struct ast_str *m_modem = ast_str_alloca(1024);
struct ast_str *a_modem = ast_str_alloca(1024);
struct sockaddr_in udptldest = { 0, };
int debug;
debug = sip_debug_test_pvt(p);
len = 0;
if (!p->udptl) {
ast_log(LOG_WARNING, "No way to add SDP without an UDPTL structure\n");
return -1;
}
if (!p->sessionid) {
p->sessionid = (int)ast_random();
p->sessionversion = p->sessionid;
} else
p->sessionversion++;
/* Our T.38 end is */
ast_udptl_get_us(p->udptl, &udptlsin);
/* Determine T.38 UDPTL destination */
if (p->udptlredirip.sin_addr.s_addr) {
udptldest.sin_port = p->udptlredirip.sin_port;
udptldest.sin_addr = p->udptlredirip.sin_addr;
} else {
udptldest.sin_addr = p->ourip.sin_addr;
udptldest.sin_port = udptlsin.sin_port;
}
ast_debug(1, "T.38 UDPTL is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(udptlsin.sin_port));
/* We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us */
if (debug) {
ast_debug(1, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
p->t38.capability,
p->t38.peercapability,
p->t38.jointcapability);
}
ast_str_append(&m_modem, 0, "v=0\r\n");
Dwayne M. Hubbard
committed
ast_str_append(&m_modem, 0, "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner , p->sessionid, p->sessionversion, ast_inet_ntoa(udptldest.sin_addr));
ast_str_append(&m_modem, 0, "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
ast_str_append(&m_modem, 0, "c=IN IP4 %s\r\n", ast_inet_ntoa(udptldest.sin_addr));
ast_str_append(&m_modem, 0, "t=0 0\r\n");
ast_str_append(&m_modem, 0, "m=image %d udptl t38\r\n", ntohs(udptldest.sin_port));
if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
ast_str_append(&a_modem, 0, "a=T38FaxVersion:0\r\n");
if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
ast_str_append(&a_modem, 0, "a=T38FaxVersion:1\r\n");
if ((x = t38_get_rate(p->t38.jointcapability)))
ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n",x);
ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval:%d\r\n", (p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) ? 1 : 0);
ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR:%d\r\n", (p->t38.jointcapability & T38FAX_TRANSCODING_MMR) ? 1 : 0);
ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG:%d\r\n", (p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) ? 1 : 0);
ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF");
x = ast_udptl_get_local_max_datagram(p->udptl);
ast_str_append(&a_modem, 0, "a=T38FaxMaxBuffer:%d\r\n",x);
ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n",x);
if (p->t38.jointcapability != T38FAX_UDP_EC_NONE)
ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:%s\r\n", (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY) ? "t38UDPRedundancy" : "t38UDPFEC");
len = m_modem->used + a_modem->used;
add_header(resp, "Content-Type", "application/sdp");
add_header_contentLength(resp, len);
add_line(resp, m_modem->str);
add_line(resp, a_modem->str);
/* Update lastrtprx when we send our SDP */
p->lastrtprx = p->lastrtptx = time(NULL);
return 0;
}
/*! \brief Add RFC 2833 DTMF offer to SDP */
Kevin P. Fleming
committed
static void add_noncodec_to_sdp(const struct sip_pvt *p, int format, int sample_rate,
struct ast_str **m_buf, struct ast_str **a_buf,
Kevin P. Fleming
committed
int debug)
{
int rtp_code;
if (debug)
Kevin P. Fleming
committed
ast_verbose("Adding non-codec 0x%x (%s) to SDP\n", format, ast_rtp_lookup_mime_subtype(0, format, 0));
Kevin P. Fleming
committed
if ((rtp_code = ast_rtp_lookup_code(p->rtp, 0, format)) == -1)
return;
ast_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(0, format, 0),
Kevin P. Fleming
committed
sample_rate);
if (format == AST_RTP_DTMF) /* Indicate we support DTMF and FLASH... */
ast_str_append(a_buf, 0, "a=fmtp:%d 0-16\r\n", rtp_code);
Kevin P. Fleming
committed
}
/*! \brief Set all IP media addresses for this call
\note called from add_sdp()
*/
static void get_our_media_address(struct sip_pvt *p, int needvideo,
struct sockaddr_in *sin, struct sockaddr_in *vsin, struct sockaddr_in *tsin,
struct sockaddr_in *dest, struct sockaddr_in *vdest)
{
/* First, get our address */
ast_rtp_get_us(p->rtp, sin);
if (p->vrtp)
ast_rtp_get_us(p->vrtp, vsin);
if (p->trtp)
ast_rtp_get_us(p->trtp, tsin);
/* Now, try to figure out where we want them to send data */
/* Is this a re-invite to move the media out, then use the original offer from caller */
if (p->redirip.sin_addr.s_addr) { /* If we have a redirection IP, use it */
dest->sin_port = p->redirip.sin_port;
dest->sin_addr = p->redirip.sin_addr;
} else {
dest->sin_addr = p->ourip.sin_addr;
dest->sin_port = sin->sin_port;
}
if (needvideo) {
/* Determine video destination */
if (p->vredirip.sin_addr.s_addr) {
vdest->sin_addr = p->vredirip.sin_addr;
vdest->sin_port = p->vredirip.sin_port;
} else {
vdest->sin_addr = p->ourip.sin_addr;
vdest->sin_port = vsin->sin_port;
}
}
}
#define SDP_SAMPLE_RATE(x) (x == AST_FORMAT_G722) ? 16000 : 8000
/*! \brief Add Session Description Protocol message */
Olle Johansson
committed
static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p)
struct sockaddr_in vsin;
struct sockaddr_in tsin;
struct sockaddr_in dest;
struct sockaddr_in vdest = { 0, };
struct sockaddr_in tdest = { 0, };
/* SDP fields */
char *version = "v=0\r\n"; /* Protocol version */
Dwayne M. Hubbard
committed
char subject[256]; /* Subject of the session */
char owner[256]; /* Session owner/creator */
char connection[256]; /* Connection data */
char *stime = "t=0 0\r\n"; /* Time the session is active */
char bandwidth[256] = ""; /* Max bitrate */
struct ast_str *m_audio = ast_str_alloca(256); /* Media declaration line for audio */
struct ast_str *m_video = ast_str_alloca(256); /* Media declaration line for video */
struct ast_str *m_text = ast_str_alloca(256); /* Media declaration line for text */
struct ast_str *a_audio = ast_str_alloca(1024); /* Attributes for audio */
struct ast_str *a_video = ast_str_alloca(1024); /* Attributes for video */
struct ast_str *a_text = ast_str_alloca(1024); /* Attributes for text */
Kevin P. Fleming
committed
int x;
int capability;
int needvideo = FALSE;
int needtext = FALSE;
int debug = sip_debug_test_pvt(p);
int min_audio_packet_size = 0;
int min_video_packet_size = 0;
int min_text_packet_size = 0;
char codecbuf[BUFSIZ];
char buf[BUFSIZ];
Dwayne M. Hubbard
committed
/* Set the SDP session name */
snprintf(subject, sizeof(subject), "s=%s\r\n", ast_strlen_zero(global_sdpsession) ? "-" : global_sdpsession);
if (!p->rtp) {
ast_log(LOG_WARNING, "No way to add SDP without an RTP structure\n");
Olle Johansson
committed
return AST_FAILURE;
/* XXX We should not change properties in the SIP dialog until
we have acceptance of the offer if this is a re-invite */
/* Set RTP Session ID and version */
if (!p->sessionid) {
p->sessionid = (int)ast_random();
p->sessionversion = p->sessionid;
} else
p->sessionversion++;
Olle Johansson
committed
capability = p->jointcapability;
/* XXX note, Video and Text are negated - 'true' means 'no' */
ast_debug(1, "** Our capability: %s Video flag: %s Text flag: %s\n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), capability),
p->novideo ? "True" : "False", p->notext ? "True" : "False");
ast_debug(1, "** Our prefcodec: %s \n", ast_getformatname_multiple(codecbuf, sizeof(codecbuf), p->prefcodec));
#ifdef WHEN_WE_HAVE_T38_FOR_OTHER_TRANSPORTS
if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_RTP)) {
ast_str_append(&m_audio, 0, " %d", 191);
ast_str_append(&a_audio, 0, "a=rtpmap:%d %s/%d\r\n", 191, "t38", 8000);
#endif
/* Check if we need video in this call */
if ((capability & AST_FORMAT_VIDEO_MASK) && !p->novideo) {
if (p->vrtp) {
needvideo = TRUE;
ast_debug(2, "This call needs video offers!\n");
} else
ast_debug(2, "This call needs video offers, but there's no video support enabled!\n");
}
/* Get our media addresses */
get_our_media_address(p, needvideo, &sin, &vsin, &tsin, &dest, &vdest);
ast_verbose("Audio is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(sin.sin_port));
/* Ok, we need video. Let's add what we need for video and set codecs.
Video is handled differently than audio since we can not transcode. */
if (needvideo) {
ast_str_append(&m_video, 0, "m=video %d RTP/AVP", ntohs(vdest.sin_port));
/* Build max bitrate string */
if (p->maxcallbitrate)
snprintf(bandwidth, sizeof(bandwidth), "b=CT:%d\r\n", p->maxcallbitrate);
if (debug)
ast_verbose("Video is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(vsin.sin_port));
/* Check if we need text in this call */
if((capability & AST_FORMAT_TEXT_MASK) && !p->notext) {
if (sipdebug_text)
ast_verbose("We think we can do text\n");
if (sipdebug_text)
ast_verbose("And we have a text rtp object\n");
ast_debug(2, "This call needs text offers! \n");
} else
ast_debug(2, "This call needs text offers, but there's no text support enabled ! \n");
}
/* Ok, we need text. Let's add what we need for text and set codecs.
Text is handled differently than audio since we can not transcode. */
if (needtext) {
if (sipdebug_text)
ast_verbose("Lets set up the text sdp\n");
/* Determine text destination */
if (p->tredirip.sin_addr.s_addr) {
tdest.sin_addr = p->tredirip.sin_addr;
tdest.sin_port = p->tredirip.sin_port;
} else {
tdest.sin_addr = p->ourip.sin_addr;
tdest.sin_port = tsin.sin_port;
}
ast_str_append(&m_text, 0, "m=text %d RTP/AVP", ntohs(tdest.sin_port));
if (debug) /* XXX should I use tdest below ? */
ast_verbose("Text is at %s port %d\n", ast_inet_ntoa(p->ourip.sin_addr), ntohs(tsin.sin_port));
/* Start building generic SDP headers */
Kevin P. Fleming
committed
/* We break with the "recommendation" and send our IP, in order that our
peer doesn't have to ast_gethostbyname() us */
Dwayne M. Hubbard
committed
snprintf(owner, sizeof(owner), "o=%s %d %d IN IP4 %s\r\n", ast_strlen_zero(global_sdpowner) ? "-" : global_sdpowner, p->sessionid, p->sessionversion, ast_inet_ntoa(dest.sin_addr));
Russell Bryant
committed
snprintf(connection, sizeof(connection), "c=IN IP4 %s\r\n", ast_inet_ntoa(dest.sin_addr));
ast_str_append(&m_audio, 0, "m=audio %d RTP/AVP", ntohs(dest.sin_port));
Kevin P. Fleming
committed
if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_ONEDIR)
else if (ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD) == SIP_PAGE2_CALL_ONHOLD_INACTIVE)
Olle Johansson
committed
hold = "a=inactive\r\n";
Olle Johansson
committed
else
Olle Johansson
committed
/* Now, start adding audio codecs. These are added in this order:
- First what was requested by the calling channel
- Then preferences in order from sip.conf device config for this peer/user
- Then other codecs in capabilities, including video
*/
/* Prefer the audio codec we were requested to use, first, no matter what
Note that p->prefcodec can include video codecs, so mask them out
*/
int codec = p->prefcodec & AST_FORMAT_AUDIO_MASK;
add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
&m_audio, &a_audio,
debug, &min_audio_packet_size);
Kevin P. Fleming
committed
/* Start by sending our preferred audio codecs */
Kevin P. Fleming
committed
for (x = 0; x < 32; x++) {
if (!(codec = ast_codec_pref_index(&p->prefs, x)))
Kevin P. Fleming
committed
Kevin P. Fleming
committed
continue;
Kevin P. Fleming
committed
continue;
add_codec_to_sdp(p, codec, SDP_SAMPLE_RATE(codec),
&m_audio, &a_audio,
debug, &min_audio_packet_size);
/* Now send any other common audio and video codecs, and non-codec formats: */
for (x = 1; x <= (needtext ? AST_FORMAT_TEXT_MASK : (needvideo ? AST_FORMAT_VIDEO_MASK : AST_FORMAT_AUDIO_MASK)); x <<= 1) {
if (!(capability & x)) /* Codec not requested */
Kevin P. Fleming
committed
continue;
if (alreadysent & x) /* Already added to SDP */
Kevin P. Fleming
committed
continue;
if (x & AST_FORMAT_AUDIO_MASK)
add_codec_to_sdp(p, x, SDP_SAMPLE_RATE(x),
&m_audio, &a_audio, debug, &min_audio_packet_size);
else if (x & AST_FORMAT_VIDEO_MASK)
add_vcodec_to_sdp(p, x, 90000,
&m_video, &a_video, debug, &min_video_packet_size);
else if (x & AST_FORMAT_TEXT_MASK)
add_tcodec_to_sdp(p, x, 1000,
&m_text, &a_text, debug, &min_text_packet_size);
Kevin P. Fleming
committed
/* Now add DTMF RFC2833 telephony-event as a codec */
for (x = 1; x <= AST_RTP_MAX; x <<= 1) {
if (!(p->jointnoncodeccapability & x))
Kevin P. Fleming
committed
continue;
add_noncodec_to_sdp(p, x, 8000, &m_audio, &a_audio, debug);
Kevin P. Fleming
committed
ast_debug(3, "-- Done with adding codecs to SDP\n");
if (!p->owner || !ast_internal_timing_enabled(p->owner))
ast_str_append(&a_audio, 0, "a=silenceSupp:off - - - -\r\n");
Kevin P. Fleming
committed
ast_str_append(&a_audio, 0, "a=ptime:%d\r\n", min_audio_packet_size);
/* XXX don't think you can have ptime for video */
ast_str_append(&a_video, 0, "a=ptime:%d\r\n", min_video_packet_size);
/* XXX don't think you can have ptime for text */
if (min_text_packet_size)
ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 ||
m_text->len - m_text->used < 2 || a_text->len - a_text->used < 2 ||
a_audio->len - a_audio->used < 2 || a_video->len - a_video->used < 2)
ast_log(LOG_WARNING, "SIP SDP may be truncated due to undersized buffer!!\n");
ast_str_append(&m_audio, 0, "\r\n");
ast_str_append(&m_video, 0, "\r\n");
ast_str_append(&m_text, 0, "\r\n");
len = strlen(version) + strlen(subject) + strlen(owner) +
strlen(connection) + strlen(stime) + m_audio->used + a_audio->used + strlen(hold);
if (needvideo) /* only if video response is appropriate */
len += m_video->used + a_video->used + strlen(bandwidth) + strlen(hold);
if (needtext) /* only if text response is appropriate */
len += m_text->used + a_text->used + strlen(hold);
Kevin P. Fleming
committed
add_header(resp, "Content-Type", "application/sdp");
add_header_contentLength(resp, len);
add_line(resp, version);
add_line(resp, owner);
add_line(resp, subject);
add_line(resp, connection);
if (needvideo) /* only if video response is appropriate */
add_line(resp, bandwidth);
add_line(resp, stime);
add_line(resp, m_audio->str);
add_line(resp, a_audio->str);
Olle Johansson
committed
add_line(resp, hold);
if (needvideo) { /* only if video response is appropriate */
add_line(resp, m_video->str);
add_line(resp, a_video->str);
add_line(resp, hold); /* Repeat hold for the video stream */
if (needtext) { /* only if text response is appropriate */
add_line(resp, m_text->str);
add_line(resp, a_text->str);
add_line(resp, hold); /* Repeat hold for the text stream */
}
Kevin P. Fleming
committed
/* Update lastrtprx when we send our SDP */
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
Kevin P. Fleming
committed
ast_debug(3, "Done building SDP. Settling with this capability: %s\n", ast_getformatname_multiple(buf, BUFSIZ, capability));
Olle Johansson
committed
return AST_SUCCESS;
/*! \brief Used for 200 OK and 183 early media */
static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans)
{
struct sip_request resp;
int seqno;
if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
return -1;
}
respprep(&resp, p, msg, req);
if (p->udptl) {
ast_udptl_offered_from_local(p->udptl, 0);
add_t38_sdp(&resp, p);
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
if (retrans && !p->pendinginvite)
p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */
return send_response(p, &resp, retrans, seqno);
}
/*! \brief copy SIP request (mostly used to save request for responses) */
static void copy_request(struct sip_request *dst, const struct sip_request *src)
{
long offset;
int x;
offset = ((void *)dst) - ((void *)src);
/* First copy stuff */
memcpy(dst, src, sizeof(*dst));
/* Now fix pointer arithmetic */
for (x=0; x < src->headers; x++)
for (x=0; x < src->lines; x++)
dst->rlPart1 += offset;
dst->rlPart2 += offset;
Olle Johansson
committed
/*! \brief Used for 200 OK and 183 early media
\return Will return XMIT_ERROR for network errors.
Olle Johansson
committed
*/
static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable)
Kevin P. Fleming
committed
if (sscanf(get_header(req, "CSeq"), "%d ", &seqno) != 1) {
ast_log(LOG_WARNING, "Unable to get seqno from '%s'\n", get_header(req, "CSeq"));
return -1;
}
if (p->rtp) {
if (!p->autoframing && !ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
ast_debug(1, "Setting framing from config on incoming call\n");
ast_rtp_codec_setpref(p->rtp, &p->prefs);
}
Olle Johansson
committed
try_suggested_sip_codec(p);
add_sdp(&resp, p);
ast_log(LOG_ERROR, "Can't add SDP to response, since we have no RTP session allocated. Call-ID %s\n", p->callid);
if (reliable && !p->pendinginvite)
p->pendinginvite = seqno; /* Buggy clients sends ACK on RINGING too */
return send_response(p, &resp, reliable, seqno);
/*! \brief Parse first line of incoming SIP request */
static int determine_firstline_parts(struct sip_request *req)
char *e = ast_skip_blanks(req->header[0]); /* there shouldn't be any */
if (!*e)
req->rlPart1 = e; /* method or protocol */
e = ast_skip_nonblanks(e);
if (*e)
*e++ = '\0';
e = ast_skip_blanks(e);
if ( !*e )
if (!strcasecmp(req->rlPart1, "SIP/2.0") ) { /* We have a response */
if (strlen(e) < 3) /* status code is 3 digits */
req->rlPart2 = e;
} else { /* We have a request */
if ( *e == '<' ) { /* XXX the spec says it must not be in <> ! */
Olle Johansson
committed
ast_debug(3, "Oops. Bogus uri in <> %s\n", e);
req->rlPart2 = e; /* URI */
e = ast_skip_nonblanks(e);
if (*e)
*e++ = '\0';
e = ast_skip_blanks(e);
if (strcasecmp(e, "SIP/2.0") ) {
Olle Johansson
committed
ast_debug(3, "Skipping packet - Bad request protocol %s\n", e);
/*! \brief Transmit reinvite with SDP
\note A re-invite is basically a new INVITE with the same CALL-ID and TAG as the
INVITE that opened the SIP dialogue
We reinvite so that the audio stream (RTP) go directly between
the SIP UAs. SIP Signalling stays with * in the path.
If t38version is TRUE, we send T38 SDP for re-invite from audio/video to
T38 UDPTL transmission on the channel
static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version)
struct sip_request req;
reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
Mark Spencer
committed
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
Russell Bryant
committed
if (sipdebug)
add_header(&req, "X-asterisk-Info", "SIP re-invite (External RTP bridge)");
if (t38version)
add_t38_sdp(&req, p);
else
add_sdp(&req, p);
/* Use this as the basis */
initialize_initreq(p, &req);
p->lastinvite = p->ocseq;
ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Change direction of this dialog */
return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
}
Olle Johansson
committed
/* \brief Remove URI parameters at end of URI, not in username part though */
static char *remove_uri_parameters(char *uri)
{
char *atsign;
atsign = strchr(uri, '@'); /* First, locate the at sign */
if (!atsign)
atsign = uri; /* Ok hostname only, let's stick with the rest */
atsign = strchr(atsign, ';'); /* Locate semi colon */
if (atsign)
*atsign = '\0'; /* Kill at the semi colon */
return uri;
}
/*! \brief Check Contact: URI of SIP message */
static void extract_uri(struct sip_pvt *p, struct sip_request *req)
{
char *c;
Kevin P. Fleming
committed
ast_copy_string(stripped, get_header(req, "Contact"), sizeof(stripped));
c = get_in_brackets(stripped);
Olle Johansson
committed
/* Cut the URI at the at sign after the @, not in the username part */
Olle Johansson
committed
c = remove_uri_parameters(c);
if (!ast_strlen_zero(c))
ast_string_field_set(p, uri, c);
Olle Johansson
committed
/*! \brief Build contact header - the contact header we send out */
static void build_contact(struct sip_pvt *p)
{
/* Construct Contact: header */
if (ntohs(p->ourip.sin_port) != STANDARD_SIP_PORT)
ast_string_field_build(p, our_contact, "<sip:%s%s%s:%d>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr), ntohs(p->ourip.sin_port));
ast_string_field_build(p, our_contact, "<sip:%s%s%s>", p->exten, ast_strlen_zero(p->exten) ? "" : "@", ast_inet_ntoa(p->ourip.sin_addr));
/*! \brief Build the Remote Party-ID & From using callingpres options */
Kevin P. Fleming
committed
static void build_rpid(struct sip_pvt *p)
{
const char *privacy=NULL;
const char *screen=NULL;
Kevin P. Fleming
committed
char buf[256];
const char *clid = default_callerid;
const char *clin = NULL;
const char *fromdomain;
if (!ast_strlen_zero(p->rpid) || !ast_strlen_zero(p->rpid_from))
Kevin P. Fleming
committed
return;
if (p->owner && p->owner->cid.cid_num)
clid = p->owner->cid.cid_num;
if (p->owner && p->owner->cid.cid_name)
clin = p->owner->cid.cid_name;
if (ast_strlen_zero(clin))
Kevin P. Fleming
committed
clin = clid;
switch (p->callingpres) {
case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
privacy = "off";
screen = "no";
break;
case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
privacy = "off";
Kevin P. Fleming
committed
break;
case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
privacy = "off";
Kevin P. Fleming
committed
break;
case AST_PRES_ALLOWED_NETWORK_NUMBER:
privacy = "off";
screen = "yes";
break;
case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
privacy = "full";
screen = "no";
break;
case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
privacy = "full";
Kevin P. Fleming
committed
break;
case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
privacy = "full";
Kevin P. Fleming
committed
break;
case AST_PRES_PROHIB_NETWORK_NUMBER:
privacy = "full";
Kevin P. Fleming
committed
break;
case AST_PRES_NUMBER_NOT_AVAILABLE:
Kevin P. Fleming
committed
break;
default:
ast_log(LOG_WARNING, "Unsupported callingpres (%d)\n", p->callingpres);
if ((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)
privacy = "full";
else
privacy = "off";
screen = "no";
break;
}
fromdomain = S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr));
Kevin P. Fleming
committed
snprintf(buf, sizeof(buf), "\"%s\" <sip:%s@%s>", clin, clid, fromdomain);
if (send_pres_tags)
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ";privacy=%s;screen=%s", privacy, screen);
ast_string_field_set(p, rpid, buf);
Kevin P. Fleming
committed
ast_string_field_build(p, rpid_from, "\"%s\" <sip:%s@%s>;tag=%s", clin,
fromdomain, p->tag);
Kevin P. Fleming
committed
}
/*! \brief Initiate new SIP request to peer/user */
static void initreqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod)
struct ast_str *invite = ast_str_alloca(256);
char tmp_n[BUFSIZ/2]; /* build a local copy of 'n' if needed */
char tmp_l[BUFSIZ/2]; /* build a local copy of 'l' if needed */
const char *l = NULL; /* XXX what is this, exactly ? */
const char *n = NULL; /* XXX what is this, exactly ? */
if (ast_test_flag(&p->flags[0], SIP_USEREQPHONE)) {
const char *s = p->username; /* being a string field, cannot be NULL */
/* Test p->username against allowed characters in AST_DIGIT_ANY
If it matches the allowed characters list, then sipuser = ";user=phone"
If not, then sipuser = ""
*/
/* + is allowed in first position in a tel: uri */
if (*s == '+')
s++;
for (; *s; s++) {
if (!strchr(AST_DIGIT_ANYNUM, *s) )
/* If we have only digits, add ;user=phone to the uri */
if (*s)
urioptions = ";user=phone";
snprintf(p->lastmsg, sizeof(p->lastmsg), "Init: %s", sip_methods[sipmethod].text);
if (p->owner) {
l = p->owner->cid.cid_num;
n = p->owner->cid.cid_name;
}
/* if we are not sending RPID and user wants his callerid restricted */
if (!ast_test_flag(&p->flags[0], SIP_SENDRPID) &&
((p->callingpres & AST_PRES_RESTRICTION) != AST_PRES_ALLOWED)) {
Martin Pycko
committed
l = CALLERID_UNKNOWN;
n = l;
}
l = default_callerid;
if (ast_strlen_zero(n))
Mark Spencer
committed
/* Allow user to be overridden */
if (!ast_strlen_zero(p->fromuser))
Mark Spencer
committed
l = p->fromuser;
else /* Save for any further attempts */
ast_string_field_set(p, fromuser, l);
/* Allow user to be overridden */
if (!ast_strlen_zero(p->fromname))
n = p->fromname;
else /* Save for any further attempts */
ast_string_field_set(p, fromname, n);
if (pedanticsipchecking) {
ast_uri_encode(n, tmp_n, sizeof(tmp_n), 0);
n = tmp_n;
ast_uri_encode(l, tmp_l, sizeof(tmp_l), 0);
l = tmp_l;
if (ntohs(p->ourip.sin_port) != STANDARD_SIP_PORT && ast_strlen_zero(p->fromdomain))
snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s:%d>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)), ntohs(p->ourip.sin_port), p->tag);
Mark Spencer
committed
else
snprintf(from, sizeof(from), "\"%s\" <sip:%s@%s>;tag=%s", n, l, S_OR(p->fromdomain, ast_inet_ntoa(p->ourip.sin_addr)), p->tag);
/* If we're calling a registered SIP peer, use the fullcontact to dial to the peer */
if (!ast_strlen_zero(p->fullcontact)) {
/* If we have full contact, trust it */
ast_str_append(&invite, 0, "%s", p->fullcontact);
/* Otherwise, use the username while waiting for registration */
ast_str_append(&invite, 0, "sip:");
if (!ast_strlen_zero(p->username)) {
n = p->username;
if (pedanticsipchecking) {
ast_uri_encode(n, tmp_n, sizeof(tmp_n), 0);
n = tmp_n;
ast_str_append(&invite, 0, "%s@", n);
ast_str_append(&invite, 0, "%s", p->tohost);
if (ntohs(p->sa.sin_port) != STANDARD_SIP_PORT)
ast_str_append(&invite, 0, ":%d", ntohs(p->sa.sin_port));
ast_str_append(&invite, 0, "%s", urioptions);
/* If custom URI options have been provided, append them */
if (p->options && p->options->uri_options)
ast_str_append(&invite, 0, ";%s", p->options->uri_options);
/* This is the request URI, which is the next hop of the call
which may or may not be the destination of the call
*/
ast_string_field_set(p, uri, invite->str);
if (!ast_strlen_zero(p->todnid)) {
/*! \todo Need to add back the VXML URL here at some point, possibly use build_string for all this junk */
if (!strchr(p->todnid, '@')) {
/* We have no domain in the dnid */
snprintf(to, sizeof(to), "<sip:%s@%s>%s%s", p->todnid, p->tohost, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
} else {
snprintf(to, sizeof(to), "<sip:%s>%s%s", p->todnid, ast_strlen_zero(p->theirtag) ? "" : ";tag=", p->theirtag);
}
} else {
if (sipmethod == SIP_NOTIFY && !ast_strlen_zero(p->theirtag)) {
/* If this is a NOTIFY, use the From: tag in the subscribe (RFC 3265) */
snprintf(to, sizeof(to), "<%s%s>;tag=%s", (strncasecmp(p->uri, "sip:", 4) ? "" : "sip:"), p->uri, p->theirtag);
} else if (p->options && p->options->vxml_url) {
/* If there is a VXML URL append it to the SIP URL */
snprintf(to, sizeof(to), "<%s>;%s", p->uri, p->options->vxml_url);
} else
snprintf(to, sizeof(to), "<%s>", p->uri);
}
init_req(req, sipmethod, p->uri);
/* now tmp_n is available so reuse it to build the CSeq */
snprintf(tmp_n, sizeof(tmp_n), "%d %s", ++p->ocseq, sip_methods[sipmethod].text);
add_header(req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
/* SLD: FIXME?: do Route: here too? I think not cos this is the first request.
* OTOH, then we won't have anything in p->route anyway */
Kevin P. Fleming
committed
/* Build Remote Party-ID and From */
if (ast_test_flag(&p->flags[0], SIP_SENDRPID) && (sipmethod == SIP_INVITE)) {
Kevin P. Fleming
committed
build_rpid(p);
Kevin P. Fleming
committed
add_header(req, "From", p->rpid_from);
Kevin P. Fleming
committed
add_header(req, "From", from);
Kevin P. Fleming
committed
add_header(req, "To", to);
ast_string_field_set(p, exten, l);
add_header(req, "Contact", p->our_contact);
add_header(req, "Call-ID", p->callid);
add_header(req, "CSeq", tmp_n);
if (!ast_strlen_zero(global_useragent))
add_header(req, "User-Agent", global_useragent);
if (!ast_strlen_zero(p->rpid))
Kevin P. Fleming
committed
add_header(req, "Remote-Party-ID", p->rpid);
/*! \brief Build REFER/INVITE/OPTIONS message and transmit it
\param init 0 = Prepare request within dialog, 1= prepare request, new branch, 2= prepare new request and new dialog. do_proxy_auth calls this with init!=2
\param p sip_pvt structure
\param sdp unknown
\param sipmethod unknown
*/
Kevin P. Fleming
committed
static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
req.method = sipmethod;
if (init) {/* Bump branch even on initial requests */
Tilghman Lesher
committed
p->branch ^= ast_random();
build_via(p);
}
if (init > 1)
initreqprep(&req, p, sipmethod);
else
reqprep(&req, p, sipmethod, 0, 1);
Kevin P. Fleming
committed
if (p->options && p->options->auth)
add_header(&req, p->options->authheader, p->options->auth);
if (sipmethod == SIP_REFER) { /* Call transfer */
if (p->refer) {
char buf[BUFSIZ];
if (!ast_strlen_zero(p->refer->refer_to))
add_header(&req, "Refer-To", p->refer->refer_to);
if (!ast_strlen_zero(p->refer->referred_by)) {
snprintf(buf, sizeof(buf), "%s <%s>", p->refer->referred_by_name, p->refer->referred_by);
add_header(&req, "Referred-By", buf);
}
}
/* This new INVITE is part of an attended transfer. Make sure that the
other end knows and replace the current call with this new call */
if (p->options && !ast_strlen_zero(p->options->replaces)) {
add_header(&req, "Replaces", p->options->replaces);
}
add_header(&req, "Allow", ALLOWED_METHODS);
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
if (p->options && p->options->addsipheaders && p->owner) {
struct ast_channel *chan = p->owner; /* The owner channel */
struct varshead *headp;
ast_channel_lock(chan);
headp = &chan->varshead;
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
7978
7979
if (!headp)
ast_log(LOG_WARNING,"No Headp for the channel...ooops!\n");
else {
const struct ast_var_t *current;
AST_LIST_TRAVERSE(headp, current, entries) {
/* SIPADDHEADER: Add SIP header to outgoing call */
if (!strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
char *content, *end;
const char *header = ast_var_value(current);
char *headdup = ast_strdupa(header);
/* Strip of the starting " (if it's there) */
if (*headdup == '"')
headdup++;
if ((content = strchr(headdup, ':'))) {
*content++ = '\0';
content = ast_skip_blanks(content); /* Skip white space */
/* Strip the ending " (if it's there) */
end = content + strlen(content) -1;
if (*end == '"')
*end = '\0';
add_header(&req, headdup, content);
if (sipdebug)
ast_debug(1, "Adding SIP Header \"%s\" with content :%s: \n", headdup, content);
ast_channel_unlock(chan);
if (sdp) {
if (p->udptl && p->t38.state == T38_LOCAL_DIRECT) {
ast_udptl_offered_from_local(p->udptl, 1);
ast_debug(1, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
add_t38_sdp(&req, p);
add_sdp(&req, p);
add_header_contentLength(&req, 0);
Olle Johansson
committed
if (!p->initreq.headers)
initialize_initreq(p, &req);