aboutsummaryrefslogtreecommitdiffstats
path: root/channels/chan_sip.c
diff options
context:
space:
mode:
Diffstat (limited to 'channels/chan_sip.c')
-rw-r--r--channels/chan_sip.c869
1 files changed, 490 insertions, 379 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index e87d7a154..0477aa0ad 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1353,10 +1353,15 @@ static int sip_sipredirect(struct sip_pvt *p, const char *dest);
/*--- Codec handling / SDP */
static void try_suggested_sip_codec(struct sip_pvt *p);
-static const char* get_sdp_iterate(int* start, struct sip_request *req, const char *name);
-static const char *get_sdp(struct sip_request *req, const char *name);
+static const char *get_sdp_iterate(int* start, struct sip_request *req, const char *name);
+static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value);
static int find_sdp(struct sip_request *req);
static int process_sdp(struct sip_pvt *p, struct sip_request *req);
+static int process_sdp_c(const char *c, struct ast_hostent *hp);
+static int process_sdp_a_sendonly(const char *a, int *sendonly);
+static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp *newaudiortp, int *last_rtpmap_codec);
+static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp *newvideortp, int *last_rtpmap_codec);
+static int process_sdp_a_image(const char *a, struct sip_pvt *p);
static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate,
char **m_buf, size_t *m_size, char **a_buf, size_t *a_size,
int debug, int *min_packet_size);
@@ -4383,12 +4388,27 @@ static const char *get_sdp_iterate(int *start, struct sip_request *req, const ch
return "";
}
-/*! \brief Get a line from an SDP message body */
-static const char *get_sdp(struct sip_request *req, const char *name)
+/*! \brief Fetches the next valid SDP line between the 'start' line
+ * and the 'stop' line. Returns the type ('a', 'c', ...) and
+ * matching line in reference 'start' is updated with the next line number.
+ */
+static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value)
{
- int dummy = 0;
+ char type = '\0';
+ const char *line = NULL;
+
+ if (stop > req->sdp_end || stop < req->sdp_start) stop = req->sdp_end;
+
+ while (*start < stop) {
+ line = req->line[(*start)++];
+ if (line[1] == '=') {
+ type = line[0];
+ *value = ast_skip_blanks(line + 2);
+ break;
+ }
+ }
- return get_sdp_iterate(&dummy, req, name);
+ return type;
}
/*! \brief Get a specific line from the message body */
@@ -5298,44 +5318,50 @@ static int get_ip_and_port_from_sdp(struct sip_request *req, const enum media_ty
*/
static int process_sdp(struct sip_pvt *p, struct sip_request *req)
{
- const char *m; /* SDP media offer */
- const char *c;
- const char *a;
- char host[258];
+ /* Iterators for SDP parsing */
+ int start = req->sdp_start;
+ int next = start;
+ int iterator = start;
+
+ /* Temporary vars for SDP parsing */
+ char type = '\0';
+ const char *value = NULL;
+ const char *m = NULL; /* SDP media offer */
+ const char *nextm = NULL;
int len = -1;
+
+ /* Host information */
+ struct ast_hostent audiohp;
+ struct ast_hostent videohp;
+ struct ast_hostent sessionhp;
+ struct hostent *hp = NULL; /*!< RTP Audio host IP */
+ struct hostent *vhp = NULL; /*!< RTP video host IP */
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;
+ int udptlportno = -1; /*!< UDPTL Image port number */
+ struct sockaddr_in sin; /*!< media socket address */
+ struct sockaddr_in vsin; /*!< video socket address */
+ struct sockaddr_in isin; /*!< image socket address */
/* Peer capability is the capability in the SDP, non codec is RFC2833 DTMF (101) */
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 ast_rtp *newaudiortp, *newvideortp; /*!< Buffers for codec handling */
+ int newjointcapability; /*!< Negotiated capability */
+ int newpeercapability;
+ int newnoncodeccapability;
const char *codecs;
- struct hostent *hp; /*!< RTP Audio host IP */
- struct hostent *vhp = NULL; /*!< RTP video host IP */
- struct ast_hostent audiohp;
- struct ast_hostent videohp;
int codec;
- int destiterator = 0;
- int iterator;
+
+ /* Others */
int sendonly = -1;
+ int vsendonly = -1;
int numberofports;
- struct ast_rtp *newaudiortp, *newvideortp; /* Buffers for codec handling */
- int newjointcapability; /* Negotiated capability */
- int newpeercapability;
- int newnoncodeccapability;
int numberofmediastreams = 0;
+ int last_rtpmap_codec = 0;
int debug = sip_debug_test_pvt(p);
-
- int found_rtpmap_codecs[SDP_MAX_RTPMAP_CODECS];
- int last_rtpmap_codec=0;
+ /* Initial check */
if (!p->rtp) {
ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n");
return -1;
@@ -5360,52 +5386,75 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
ast_rtp_new_init(newvideortp);
ast_rtp_pt_clear(newvideortp);
+
/* Update our last rtprx when we receive an SDP, too */
p->lastrtprx = p->lastrtptx = time(NULL); /* XXX why both ? */
+ ast_set_flag(&p->flags[0], SIP_NOVIDEO);
+
memset(p->offered_media, 0, sizeof(p->offered_media));
- /* Try to find first media stream */
- m = get_sdp(req, "m");
- 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);
+ /* Scan for the first media stream (m=) line to limit scanning of globals */
+ nextm = get_sdp_iterate(&next, req, "m");
+ if (ast_strlen_zero(nextm)) {
+ ast_log(LOG_WARNING, "Insufficient information for SDP (m= not found)\n");
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;
+ /* Scan session level SDP parameters (lines before first media stream) */
+ while ((type = get_sdp_line(&iterator, next - 1, req, &value)) != '\0') {
+ int processed = FALSE;
+ switch (type) {
+ case 'c':
+ if (process_sdp_c(value, &sessionhp)) {
+ processed = TRUE;
+ hp = &sessionhp.hp;
+ vhp = hp;
+ }
+ break;
+ case 'a':
+ if (process_sdp_a_sendonly(value, &sendonly)) {
+ processed = TRUE;
+ vsendonly = sendonly;
+ }
+ else if (process_sdp_a_audio(value, p, newaudiortp, &last_rtpmap_codec))
+ processed = TRUE;
+ else if (process_sdp_a_video(value, p, newvideortp, &last_rtpmap_codec))
+ processed = TRUE;
+ else if (process_sdp_a_image(value, p))
+ processed = TRUE;
+ break;
+ }
+
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Processing session-level SDP %c=%s... %s\n", type, value, (processed == TRUE)? "OK." : "UNSUPPORTED.");
}
- 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;
+ /* Scan media stream (m=) specific parameters loop */
+ while (!ast_strlen_zero(nextm)) {
int audio = FALSE;
+ int video = FALSE;
+ int image = FALSE;
+ int x;
numberofports = 1;
len = -1;
+ start = next;
+ m = nextm;
+ iterator = next;
+ nextm = get_sdp_iterate(&next, req, "m");
+
+ /* Search for audio media definition */
if ((sscanf(m, "audio %30d/%30d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
(sscanf(m, "audio %30d RTP/AVP %n", &x, &len) == 1 && len > 0)) {
+ /* Found audio stream in this media definition */
audio = TRUE;
p->offered_media[SDP_AUDIO].offered = TRUE;
numberofmediastreams++;
- /* Found audio stream in this media definition */
portno = x;
+
/* Scan through the RTP payload types specified in a "m=" line: */
codecs = m + len;
ast_copy_string(p->offered_media[SDP_AUDIO].text, codecs, sizeof(p->offered_media[SDP_AUDIO].text));
@@ -5418,13 +5467,16 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
ast_verbose("Found RTP audio format %d\n", codec);
ast_rtp_set_m_type(newaudiortp, codec);
}
+ /* Search for video media definition */
} else if ((sscanf(m, "video %30d/%30d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) ||
- (sscanf(m, "video %30d RTP/AVP %n", &x, &len) == 1 && len >= 0)) {
- /* If it is not audio - is it video ? */
+ (sscanf(m, "video %30d RTP/AVP %n", &x, &len) == 1 && len >= 0)) {
+ /* Found video stream in this media definition */
+ video = TRUE;
ast_clear_flag(&p->flags[0], SIP_NOVIDEO);
p->offered_media[SDP_VIDEO].offered = TRUE;
numberofmediastreams++;
vportno = x;
+
/* Scan through the RTP payload types specified in a "m=" line: */
codecs = m + len;
ast_copy_string(p->offered_media[SDP_VIDEO].text, codecs, sizeof(p->offered_media[SDP_VIDEO].text));
@@ -5437,8 +5489,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
ast_verbose("Found RTP video format %d\n", codec);
ast_rtp_set_m_type(newvideortp, codec);
}
+ /* Search for image media definition */
} else if (p->udptl && ((sscanf(m, "image %30d udptl t38%n", &x, &len) == 1 && len > 0) ||
(sscanf(m, "image %30d UDPTL t38%n", &x, &len) == 1 && len >= 0))) {
+ /* Found image stream in this media definition */
+ image = TRUE;
if (debug)
ast_verbose("Got T.38 offer in SDP in dialog %s\n", p->callid);
p->offered_media[SDP_IMAGE].offered = TRUE;
@@ -5446,7 +5501,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
numberofmediastreams++;
if (p->owner && p->lastinvite) {
- if(p->t38.state != T38_LOCAL_REINVITE) {
+ if (p->t38.state != T38_LOCAL_REINVITE) {
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>" );
@@ -5457,32 +5512,73 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
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
+ } else {
ast_log(LOG_WARNING, "Unsupported SDP media type in offer: %s\n", m);
+ continue;
+ }
+
+ /* Check for number of ports */
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 */
+
+ /* Media stream specific parameters */
+ while ((type = get_sdp_line(&iterator, next - 1, req, &value)) != '\0') {
+ int processed = FALSE;
+
+ switch (type) {
+ case 'c':
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);
- return -2;
+ if (process_sdp_c(value, &audiohp)) {
+ processed = TRUE;
+ hp = &audiohp.hp;
}
- } else if (!(vhp = ast_gethostbyname(host, &videohp))) {
- ast_log(LOG_WARNING, "Unable to lookup RTP video host in secondary c= line, '%s'\n", c);
- return -2;
}
+ else if (video) {
+ if (process_sdp_c(value, &videohp)) {
+ processed = TRUE;
+ vhp = &videohp.hp;
+ }
+ }
+ break;
+ case 'a':
+ /* Audio specific scanning */
+ if (audio) {
+ if (process_sdp_a_sendonly(value, &sendonly))
+ processed = TRUE;
+ else if (process_sdp_a_audio(value, p, newaudiortp, &last_rtpmap_codec))
+ processed = TRUE;
+ }
+ /* Video specific scanning */
+ else if (video) {
+ if (process_sdp_a_sendonly(value, &vsendonly))
+ processed = TRUE;
+ else if (process_sdp_a_video(value, p, newvideortp, &last_rtpmap_codec))
+ processed = TRUE;
+ }
+ /* Image (T.38 FAX) specific scanning */
+ else if (image) {
+ if (process_sdp_a_image(value, p))
+ processed = TRUE;
+ }
+ break;
}
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Processing media-level (%s) SDP %c=%s... %s\n",
+ (audio == TRUE)? "audio" : (video == TRUE)? "video" : "image",
+ type, value,
+ (processed == TRUE)? "OK." : "UNSUPPORTED.");
}
}
+
+ /* Sanity checks */
+ if (!hp) {
+ ast_log(LOG_WARNING, "Insufficient information in SDP (c=)...\n");
+ return -1;
+ }
+
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 */
@@ -5492,309 +5588,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
/* We have too many fax, audio and/or video media streams, fail this offer */
return -3;
- /* RTP addresses and ports for audio and video */
- sin.sin_family = AF_INET;
- vsin.sin_family = AF_INET;
- memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
- if (vhp)
- memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr));
-
- /* Setup UDPTL port number */
- if (p->udptl) {
- if (udptlportno > 0) {
- sin.sin_port = htons(udptlportno);
- if (ast_test_flag(&p->flags[0], SIP_NAT) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) {
- struct sockaddr_in peer;
- ast_rtp_get_peer(p->rtp, &peer);
- if (peer.sin_addr.s_addr) {
- memcpy(&sin.sin_addr, &peer.sin_addr, sizeof(sin.sin_addr));
- if (debug) {
- ast_log(LOG_DEBUG, "Peer T.38 UDPTL is set behind NAT and with destination, destination address now %s\n", ast_inet_ntoa(sin.sin_addr));
- }
- }
- }
- ast_udptl_set_peer(p->udptl, &sin);
- if (debug)
- 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");
- }
- }
-
-
- if (p->rtp) {
- if (portno > 0) {
- sin.sin_port = htons(portno);
- ast_rtp_set_peer(p->rtp, &sin);
- if (debug)
- ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
- } else {
- if (udptlportno > 0) {
- if (debug)
- ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session. Callid %s\n", p->callid);
- } else {
- ast_rtp_stop(p->rtp);
- if (debug)
- ast_verbose("Peer doesn't provide audio. Callid %s\n", p->callid);
- }
- }
- }
- /* Setup video port number */
- if (vportno != -1)
- vsin.sin_port = htons(vportno);
-
- /* Next, scan through each "a=rtpmap:" line, noting each
- * specified RTP payload type (with corresponding MIME subtype):
- */
- /* XXX This needs to be done per media stream, since it's media stream specific */
- iterator = req->sdp_start;
- while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
- char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
- if (option_debug > 1) {
- int breakout = FALSE;
-
- /* If we're debugging, check for unsupported sdp options */
- 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;
- }
- if (breakout) /* We have a match, skip to next header */
- continue;
- }
- if (!strcasecmp(a, "sendonly")) {
- if (sendonly == -1)
- sendonly = 1;
- continue;
- } else if (!strcasecmp(a, "inactive")) {
- if (sendonly == -1)
- sendonly = 2;
- continue;
- } else if (!strcasecmp(a, "sendrecv")) {
- if (sendonly == -1)
- sendonly = 0;
- continue;
- } else if (strlen(a) > 5 && !strncasecmp(a, "ptime", 5)) {
- char *tmp = strrchr(a, ':');
- long int framing = 0;
- if (tmp) {
- tmp++;
- framing = strtol(tmp, NULL, 10);
- if (framing == LONG_MIN || framing == LONG_MAX) {
- framing = 0;
- if (option_debug)
- ast_log(LOG_DEBUG, "Can't read framing from SDP: %s\n", a);
- }
- }
- if (framing && p->autoframing) {
- struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp);
- int codec_n;
- int format = 0;
- for (codec_n = 0; codec_n < MAX_RTP_PT; codec_n++) {
- format = ast_rtp_codec_getformat(codec_n);
- if (!format) /* non-codec or not found */
- continue;
- if (option_debug)
- ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format, framing);
- ast_codec_pref_setsize(pref, format, framing);
- }
- ast_rtp_codec_setpref(p->rtp, pref);
- }
- continue;
- } else if (sscanf(a, "rtpmap: %30u %[^/]/", &codec, mimeSubtype) == 2) {
- /* We have a rtpmap to handle */
- int found = FALSE;
- /* We should propably check if this is an audio or video codec
- so we know where to look */
-
- if (last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) {
- /* Note: should really look at the 'freq' and '#chans' params too */
- 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++;
- found = TRUE;
-
- } else if (p->vrtp) {
- 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++;
- found = TRUE;
- }
- }
- } else {
- if (debug)
- ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec);
- }
-
- if (!found) {
- /* Remove this codec since it's an unknown media type for us */
- /* XXX This is buggy since the media line for audio and video can have the
- same numbers. We need to check as described above, but for testing this works... */
- ast_rtp_unset_m_type(newaudiortp, codec);
- ast_rtp_unset_m_type(newvideortp, 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:%30d", &x) == 1)) {
- found = 1;
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "MaxBufferSize:%d\n",x);
- } else if ((sscanf(a, "T38MaxBitRate:%30d", &x) == 1) || (sscanf(a, "T38FaxMaxRate:%30d", &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;
- }
- } else if ((sscanf(a, "T38FaxVersion:%30d", &x) == 1)) {
- found = 1;
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "FaxVersion: %d\n",x);
- if (x == 0)
- peert38capability |= T38FAX_VERSION_0;
- else if (x == 1)
- peert38capability |= T38FAX_VERSION_1;
- } else if ((sscanf(a, "T38FaxMaxDatagram:%30d", &x) == 1) || (sscanf(a, "T38MaxDatagram:%30d", &x) == 1)) {
- found = 1;
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "FaxMaxDatagram: %d\n",x);
- ast_udptl_set_far_max_datagram(p->udptl, x);
- ast_udptl_set_local_max_datagram(p->udptl, x);
- } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) {
- found = 1;
- if ((sscanf(a, "T38FaxFillBitRemoval:%30d", &x) == 1)) {
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "FillBitRemoval: %d\n",x);
- if (x == 1)
- peert38capability |= T38FAX_FILL_BIT_REMOVAL;
- } else {
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "FillBitRemoval\n");
- peert38capability |= T38FAX_FILL_BIT_REMOVAL;
- }
- } else if ((strncmp(a, "T38FaxTranscodingMMR", 20) == 0)) {
- found = 1;
- if ((sscanf(a, "T38FaxTranscodingMMR:%30d", &x) == 1)) {
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "Transcoding MMR: %d\n",x);
- if (x == 1)
- peert38capability |= T38FAX_TRANSCODING_MMR;
- } else {
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "Transcoding MMR\n");
- peert38capability |= T38FAX_TRANSCODING_MMR;
- }
- } else if ((strncmp(a, "T38FaxTranscodingJBIG", 21) == 0)) {
- found = 1;
- if ((sscanf(a, "T38FaxTranscodingJBIG:%30d", &x) == 1)) {
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "Transcoding JBIG: %d\n",x);
- if (x == 1)
- peert38capability |= T38FAX_TRANSCODING_JBIG;
- } else {
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "Transcoding JBIG\n");
- peert38capability |= T38FAX_TRANSCODING_JBIG;
- }
- } else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) {
- found = 1;
- if (option_debug > 2)
- ast_log(LOG_DEBUG, "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;
- 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 {
+ if (udptlportno == -1) {
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: */
ast_rtp_get_current_formats(newaudiortp, &peercapability, &peernoncodeccapability);
ast_rtp_get_current_formats(newvideortp, &vpeercapability, &vpeernoncodeccapability);
@@ -5834,7 +5634,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
/* 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->jointcapability = newjointcapability; /* Our joint codec profile */
p->peercapability = newpeercapability; /* The other sides capability in latest offer */
p->jointnoncodeccapability = newnoncodeccapability; /* DTMF capabilities */
@@ -5856,19 +5656,66 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
}
/* Setup audio port number */
- if (p->rtp && sin.sin_port) {
- ast_rtp_set_peer(p->rtp, &sin);
- if (debug)
- ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ if (p->rtp) {
+ if (portno > 0) {
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(portno);
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ ast_rtp_set_peer(p->rtp, &sin);
+ if (debug)
+ ast_verbose("Peer audio RTP is at port %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ } else if (udptlportno > 0) {
+ if (debug)
+ ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session.\n");
+ } else {
+ ast_rtp_stop(p->rtp);
+ if (debug)
+ ast_verbose("Peer doesn't provide audio\n");
+ }
}
/* Setup video port number */
- if (p->vrtp && vsin.sin_port) {
- ast_rtp_set_peer(p->vrtp, &vsin);
- if (debug)
- ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port));
+ if (p->vrtp) {
+ if (vportno > 0) {
+ vsin.sin_family = AF_INET;
+ vsin.sin_port = htons(vportno);
+ memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr));
+ ast_rtp_set_peer(p->vrtp, &vsin);
+ if (debug)
+ ast_verbose("Peer video RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port));
+ } else {
+ ast_rtp_stop(p->vrtp);
+ if (debug)
+ ast_verbose("Peer doesn't provide video\n");
+ }
+ }
+
+ /* Setup image port number */
+ if (p->udptl) {
+ if (udptlportno > 0) {
+ isin.sin_family = AF_INET;
+ isin.sin_port = htons(udptlportno);
+ if (ast_test_flag(&p->flags[0], SIP_NAT) && ast_test_flag(&p->flags[1], SIP_PAGE2_UDPTL_DESTINATION)) {
+ struct sockaddr_in peer;
+ ast_rtp_get_peer(p->rtp, &peer);
+ if (peer.sin_addr.s_addr) {
+ memcpy(&isin.sin_addr, &peer.sin_addr, sizeof(isin.sin_addr));
+ if (debug)
+ ast_log(LOG_DEBUG, "Peer T.38 UDPTL is set behind NAT and with destination, destination address now %s\n", ast_inet_ntoa(isin.sin_addr));
+ }
+ } else
+ memcpy(&isin.sin_addr, hp->h_addr, sizeof(isin.sin_addr));
+ ast_udptl_set_peer(p->udptl, &isin);
+ if (debug)
+ ast_log(LOG_DEBUG,"Peer T.38 UDPTL is at port %s:%d\n",ast_inet_ntoa(isin.sin_addr), ntohs(isin.sin_port));
+ } else {
+ ast_udptl_stop(p->udptl);
+ if (debug)
+ ast_log(LOG_DEBUG, "Peer doesn't provide T.38 UDPTL\n");
+ }
}
+
/* Ok, we're going with this offer */
if (option_debug > 1) {
char buf[SIPBUFSIZE];
@@ -5892,7 +5739,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
ast_set_read_format(p->owner, p->owner->readformat);
ast_set_write_format(p->owner, p->owner->writeformat);
}
-
+
+ /* sendonly processing */
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 */
@@ -5913,9 +5761,272 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
change_hold_state(p, req, FALSE, sendonly);
else if (!sin.sin_addr.s_addr || (sendonly && sendonly != -1))
change_hold_state(p, req, TRUE, sendonly);
+
return 0;
}
+
+static int process_sdp_c(const char *c, struct ast_hostent *ast_hp)
+{
+ char host[258];
+ struct hostent *hp;
+
+ /* Check for Media-description-level-address */
+ if (sscanf(c, "IN IP4 %255s", host) != 1) {
+ ast_log(LOG_WARNING, "Invalid host in c= line, '%s'\n", c);
+ return FALSE;
+ } else {
+ if (!(hp = ast_gethostbyname(host, ast_hp))) {
+ ast_log(LOG_WARNING, "Unable to lookup RTP Audio host in c= line, '%s'\n", c);
+ return FALSE;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int process_sdp_a_sendonly(const char *a, int *sendonly)
+{
+ int found = FALSE;
+
+ if (!strcasecmp(a, "sendonly")) {
+ if (*sendonly == -1)
+ *sendonly = 1;
+ found = TRUE;
+ } else if (!strcasecmp(a, "inactive")) {
+ if (*sendonly == -1)
+ *sendonly = 2;
+ found = TRUE;
+ } else if (!strcasecmp(a, "sendrecv")) {
+ if (*sendonly == -1)
+ *sendonly = 0;
+ found = TRUE;
+ }
+ return found;
+}
+
+static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp *newaudiortp, int *last_rtpmap_codec)
+{
+ int found = FALSE;
+ int codec;
+ char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
+ int debug = sip_debug_test_pvt(p);
+
+ if (strlen(a) > 5 && !strncasecmp(a, "ptime", 5)) {
+ char *tmp = strrchr(a, ':');
+ long int framing = 0;
+ if (tmp) {
+ tmp++;
+ framing = strtol(tmp, NULL, 10);
+ if (framing == LONG_MIN || framing == LONG_MAX) {
+ framing = 0;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Can't read framing from SDP: %s\n", a);
+ }
+ }
+ if (framing && p->autoframing) {
+ struct ast_codec_pref *pref = ast_rtp_codec_getpref(p->rtp);
+ int codec_n;
+ int format = 0;
+ for (codec_n = 0; codec_n < MAX_RTP_PT; codec_n++) {
+ format = ast_rtp_codec_getformat(codec_n);
+ if (!format) /* non-codec or not found */
+ continue;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Setting framing for %d to %ld\n", format, framing);
+ ast_codec_pref_setsize(pref, format, framing);
+ }
+ ast_rtp_codec_setpref(p->rtp, pref);
+ }
+ found = TRUE;
+ } else if (sscanf(a, "rtpmap: %30u %[^/]/", &codec, mimeSubtype) == 2) {
+ /* We have a rtpmap to handle */
+ if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) {
+ /* Note: should really look at the 'freq' and '#chans' params too */
+ 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);
+ (*last_rtpmap_codec)++;
+ found = TRUE;
+ }
+ } else {
+ if (debug)
+ ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec);
+ }
+
+ if (!found) {
+ /* Remove this codec since it's an unknown media type for us */
+ ast_rtp_unset_m_type(newaudiortp, codec);
+ if (debug)
+ ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec);
+ }
+ }
+
+ return found;
+}
+
+static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp *newvideortp, int *last_rtpmap_codec)
+{
+ int found = FALSE;
+ int codec;
+ char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */
+ int debug = sip_debug_test_pvt(p);
+
+ if (sscanf(a, "rtpmap: %30u %[^/]/", &codec, mimeSubtype) == 2) {
+ /* We have a rtpmap to handle */
+ if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) {
+ /* Note: should really look at the 'freq' and '#chans' params too */
+ if (p->vrtp && 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);
+ (*last_rtpmap_codec)++;
+ found = TRUE;
+ }
+ } else {
+ if (debug)
+ ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec);
+ }
+
+ if (!found) {
+ /* Remove this codec since it's an unknown media type for us */
+ ast_rtp_unset_m_type(newvideortp, codec);
+ if (debug)
+ ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec);
+ }
+ }
+
+ return found;
+}
+
+static int process_sdp_a_image(const char *a, struct sip_pvt *p)
+{
+ int found = FALSE;
+ int peert38capability = 0;
+ char s[256];
+ int x;
+ int debug = sip_debug_test_pvt(p);
+
+ /* Scan trough the a= lines for T38 attributes and set apropriate fileds */
+ if ((sscanf(a, "T38FaxMaxBuffer:%30d", &x) == 1)) {
+ found = TRUE;
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "MaxBufferSize:%d\n",x);
+ } else if ((sscanf(a, "T38MaxBitRate:%30d", &x) == 1) || (sscanf(a, "T38FaxMaxRate:%30d", &x) == 1)) {
+ found = TRUE;
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG,"T38MaxBitRate: %d\n",x);
+ switch (x) {
+ case 14400:
+ peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 12000:
+ peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 9600:
+ peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 7200:
+ peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 4800:
+ peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
+ break;
+ case 2400:
+ peert38capability |= T38FAX_RATE_2400;
+ break;
+ }
+ } else if ((sscanf(a, "T38FaxVersion:%30d", &x) == 1)) {
+ found = TRUE;
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "FaxVersion: %d\n",x);
+ if (x == 0)
+ peert38capability |= T38FAX_VERSION_0;
+ else if (x == 1)
+ peert38capability |= T38FAX_VERSION_1;
+ } else if ((sscanf(a, "T38FaxMaxDatagram:%30d", &x) == 1) || (sscanf(a, "T38MaxDatagram:%30d", &x) == 1)) {
+ found = TRUE;
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "FaxMaxDatagram: %d\n",x);
+ ast_udptl_set_far_max_datagram(p->udptl, x);
+ ast_udptl_set_local_max_datagram(p->udptl, x);
+ } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) {
+ found = TRUE;
+ if ((sscanf(a, "T38FaxFillBitRemoval:%30d", &x) == 1)) {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "FillBitRemoval: %d\n",x);
+ if (x == 1)
+ peert38capability |= T38FAX_FILL_BIT_REMOVAL;
+ } else {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "FillBitRemoval\n");
+ peert38capability |= T38FAX_FILL_BIT_REMOVAL;
+ }
+ } else if ((strncmp(a, "T38FaxTranscodingMMR", 20) == 0)) {
+ found = TRUE;
+ if ((sscanf(a, "T38FaxTranscodingMMR:%30d", &x) == 1)) {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Transcoding MMR: %d\n",x);
+ if (x == 1)
+ peert38capability |= T38FAX_TRANSCODING_MMR;
+ } else {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Transcoding MMR\n");
+ peert38capability |= T38FAX_TRANSCODING_MMR;
+ }
+ } else if ((strncmp(a, "T38FaxTranscodingJBIG", 21) == 0)) {
+ found = TRUE;
+ if ((sscanf(a, "T38FaxTranscodingJBIG:%30d", &x) == 1)) {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Transcoding JBIG: %d\n",x);
+ if (x == 1)
+ peert38capability |= T38FAX_TRANSCODING_JBIG;
+ } else {
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "Transcoding JBIG\n");
+ peert38capability |= T38FAX_TRANSCODING_JBIG;
+ }
+ } else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) {
+ found = TRUE;
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "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 = TRUE;
+ 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);
+
+ return found;
+}
+
+
+
#ifdef LOW_MEMORY
static void ts_ast_rtp_destroy(void *data)
{