diff options
Diffstat (limited to 'channels/chan_sip.c')
-rw-r--r-- | channels/chan_sip.c | 1225 |
1 files changed, 675 insertions, 550 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index c99e87351..d029eaf98 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -2332,10 +2332,17 @@ 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, int t38action); +static int process_sdp_o(const char *o, struct sip_pvt *p); +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_text(const char *a, struct sip_pvt *p, struct ast_rtp *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, 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, struct ast_str **m_buf, struct ast_str **a_buf, int debug, int *min_packet_size); @@ -6809,12 +6816,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_OFFSET_TO_STR(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 */ @@ -7991,61 +8013,64 @@ 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, int t38action) { - const char *m; /* SDP media offer */ - const char *c; - const char *a; - const char *o; /* Pointer to o= line */ - char *o_copy; /* Copy of o= line */ - char *token; - 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; - int portno = -1; /*!< RTP Audio port number */ - int vportno = -1; /*!< RTP Video port number */ + + /* Host information */ + struct ast_hostent sessionhp; + struct ast_hostent audiohp; + struct ast_hostent videohp; + struct ast_hostent texthp; + struct hostent *hp = NULL; /*!< RTP Audio host IP */ + struct hostent *vhp = NULL; /*!< RTP video host IP */ + struct hostent *thp = NULL; /*!< RTP text host IP */ + int portno = -1; /*!< RTP Audio port number */ + int vportno = -1; /*!< RTP Video port number */ int tportno = -1; /*!< RTP Text port number */ - int udptlportno = -1; - 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 */ + struct sockaddr_in tsin; /*!< text 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; int tpeercapability = 0, tpeernoncodeccapability = 0; - struct sockaddr_in sin; /*!< media socket address */ - struct sockaddr_in vsin; /*!< Video socket address */ - struct sockaddr_in tsin; /*!< Text socket address */ + + struct ast_rtp *newaudiortp, *newvideortp, *newtextrtp; + 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 hostent *thp = NULL; /*!< RTP text host IP */ - struct ast_hostent audiohp; - struct ast_hostent videohp; - struct ast_hostent texthp; int codec; - int destiterator = 0; - int iterator; + + /* Others */ int sendonly = -1; + int vsendonly = -1; int numberofports; - struct ast_rtp *newaudiortp, *newvideortp, *newtextrtp; /* Buffers for codec handling */ - int newjointcapability; /* Negotiated capability */ - int newpeercapability; - int newnoncodeccapability; int numberofmediastreams = 0; + int last_rtpmap_codec = 0; + int red_data_pt[10]; /* For T.140 red */ + int red_num_gen = 0; /* For T.140 red */ + char red_fmtp[100] = "empty"; /* For T.140 red */ int debug = sip_debug_test_pvt(p); - - int found_rtpmap_codecs[SDP_MAX_RTPMAP_CODECS]; - int last_rtpmap_codec=0; + /* START UNKNOWN */ char buf[SIPBUFSIZE]; - int64_t rua_version; - - int red_data_pt[10]; - int red_num_gen = 0; - int red_pt = 0; - - char *red_cp; /* For T.140 red */ - char red_fmtp[100] = "empty"; /* For T.140 red */ + /* END UNKNOWN */ + /* Initial check */ if (!p->rtp) { ast_log(LOG_ERROR, "Got SDP but have no RTP session allocated.\n"); return -1; @@ -8084,102 +8109,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action memset(p->offered_media, 0, sizeof(p->offered_media)); - /* Store the SDP version number of remote UA. This will allow us to - distinguish between session modifications and session refreshes. If - the remote UA does not send an incremented SDP version number in a - subsequent RE-INVITE then that means its not changing media session. - The RE-INVITE may have been sent to update connected party, remote - target or to refresh the session (Session-Timers). Asterisk must not - change media session and increment its own version number in answer - SDP in this case. */ - - o = get_sdp(req, "o"); - if (ast_strlen_zero(o)) { - ast_log(LOG_WARNING, "SDP syntax error. SDP without an o= line\n"); - return -1; - } - o_copy = ast_strdupa(o); - token = strsep(&o_copy, " "); /* Skip username */ - if (!o_copy) { - ast_log(LOG_WARNING, "SDP syntax error in o= line username\n"); - return -1; - } - token = strsep(&o_copy, " "); /* Skip session-id */ - if (!o_copy) { - ast_log(LOG_WARNING, "SDP syntax error in o= line session-id\n"); - return -1; - } - token = strsep(&o_copy, " "); /* Version */ - if (!o_copy) { - ast_log(LOG_WARNING, "SDP syntax error in o= line\n"); - return -1; - } - if (!sscanf(token, "%30" SCNd64, &rua_version)) { - ast_log(LOG_WARNING, "SDP syntax error in o= line version\n"); - return -1; - } - - /* we need to check the SDP version number the other end sent us; - * our rules for deciding what to accept are a bit complex. - * - * 1) if 'ignoresdpversion' has been set for this dialog, then - * we will just accept whatever they sent and assume it is - * a modification of the session, even if it is not - * 2) otherwise, if this is the first SDP we've seen from them - * we accept it - * 3) otherwise, if the new SDP version number is higher than the - * old one, we accept it - * 4) otherwise, if this SDP is in response to us requesting a switch - * to T.38, we accept the SDP, but also generate a warning message - * that this peer should have the 'ignoresdpversion' option set, - * because it is not following the SDP offer/answer RFC; if we did - * not request a switch to T.38, then we stop parsing the SDP, as it - * has not changed from the previous version - */ - - if (ast_test_flag(&p->flags[1], SIP_PAGE2_IGNORESDPVERSION) || - (p->sessionversion_remote < 0) || - (p->sessionversion_remote < rua_version)) { - p->sessionversion_remote = rua_version; - p->session_modify = TRUE; - } else { - if (p->t38.state == T38_LOCAL_REINVITE) { - p->sessionversion_remote = rua_version; - p->session_modify = TRUE; - ast_log(LOG_WARNING, "Call %s responded to our T.38 reinvite without changing SDP version; 'ignoresdpversion' should be set for this peer.\n", p->callid); - } else { - p->session_modify = FALSE; - ast_debug(2, "Call %s responded to our reinvite without changing SDP version; ignoring SDP.\n", p->callid); - return 0; - } - } - - /* 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); - return -1; - } - - /* XXX This could block for a long time, and block the main thread! XXX */ - hp = ast_gethostbyname(host, &audiohp); - if (!hp) { - ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c); - return -1; - } - vhp = hp; /* Copy to video address as default too */ - thp = hp; /* Copy to text address as default too */ - - iterator = req->sdp_start; /* default: novideo and notext set */ p->novideo = TRUE; p->notext = TRUE; @@ -8189,23 +8119,75 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action if (p->trtp) ast_rtp_pt_clear(newtextrtp); /* Must be cleared in case no m=text line exists */ + + /* 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; + } - /* Find media streams in this SDP offer */ - while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { - int x; + /* 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 'o': + if (!process_sdp_o(value, p)) + return -1; + break; + case 'c': + if (process_sdp_c(value, &sessionhp)) { + processed = TRUE; + hp = &sessionhp.hp; + vhp = hp; + thp = 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_text(value, p, newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &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."); + } + + + + /* Scan media stream (m=) specific parameters loop */ + while (!ast_strlen_zero(nextm)) { int audio = FALSE; int video = FALSE; + int image = FALSE; int text = 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)) { 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)); @@ -8216,15 +8198,18 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } if (debug) 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)) { + (sscanf(m, "video %30d RTP/AVP %n", &x, &len) == 1 && len >= 0)) { video = TRUE; p->novideo = FALSE; 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)); @@ -8237,13 +8222,15 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action ast_verbose("Found RTP video format %d\n", codec); ast_rtp_set_m_type(newvideortp, codec); } + /* Search for text media definition */ } else if ((sscanf(m, "text %30d/%30d RTP/AVP %n", &x, &numberofports, &len) == 2 && len > 0) || - (sscanf(m, "text %30d RTP/AVP %n", &x, &len) == 1 && len > 0)) { + (sscanf(m, "text %30d RTP/AVP %n", &x, &len) == 1 && len > 0)) { text = TRUE; - p->offered_media[SDP_TEXT].offered = TRUE; p->notext = FALSE; + p->offered_media[SDP_TEXT].offered = TRUE; numberofmediastreams++; tportno = x; + /* Scan through the RTP payload types specified in a "m=" line: */ codecs = m + len; ast_copy_string(p->offered_media[SDP_TEXT].text, codecs, sizeof(p->offered_media[SDP_TEXT].text)); @@ -8256,46 +8243,105 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action ast_verbose("Found RTP text format %d\n", codec); ast_rtp_set_m_type(newtextrtp, codec); } - } 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) )) { + /* 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) )) { + 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; udptlportno = x; numberofmediastreams++; - } else + + if (p->t38.state != T38_ENABLED) { + memset(&p->t38.their_parms, 0, sizeof(p->t38.their_parms)); + + /* Remote party offers T38, we need to update state */ + if ((t38action == SDP_T38_ACCEPT) && + (p->t38.state == T38_LOCAL_REINVITE)) { + change_t38_state(p, T38_ENABLED); + } else if ((t38action == SDP_T38_INITIATE) && + p->owner && p->lastinvite) { + change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */ + } + } + } 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 (video) { - 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; + if (process_sdp_c(value, &videohp)) { + processed = TRUE; + vhp = &videohp.hp; } } else if (text) { - if (!(thp = ast_gethostbyname(host, &texthp))) { - ast_log(LOG_WARNING, "Unable to lookup RTP text host in secondary c= line, '%s'\n", c); - return -2; + if (process_sdp_c(value, &texthp)) { + processed = TRUE; + thp = &texthp.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; + } + /* Text (T.140) specific scanning */ + else if (text) { + if (process_sdp_a_text(value, p, newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &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 && tportno == -1) /* No acceptable offer found in SDP - we have no ports */ /* Do not change RTP or VRTP if this is a re-invite */ @@ -8305,386 +8351,20 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action /* We have too many fax, audio and/or video and/or text 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; - tsin.sin_family = AF_INET; - memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); - if (vhp) - memcpy(&vsin.sin_addr, vhp->h_addr, sizeof(vsin.sin_addr)); - if (thp) - memcpy(&tsin.sin_addr, thp->h_addr, sizeof(tsin.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_debug(1, "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_debug(1, "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, assumes we have audio */ - if (vportno != -1) - vsin.sin_port = htons(vportno); - - /* Setup text port number, assumes we have audio */ - if (tportno != -1) - tsin.sin_port = htons(tportno); - - /* Next, scan through each "a=xxxx:" 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[128]; - char fmtp_string[64]; - unsigned int sample_rate; - - 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, "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; - } - - if (!strcasecmp(a, "inactive")) { - if (sendonly == -1) - sendonly = 2; - continue; - } - - if (!strcasecmp(a, "sendrecv")) { - if (sendonly == -1) - sendonly = 0; - continue; - } - - if (!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; - ast_debug(1, "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; - } - - if (!strncmp(a, red_fmtp, strlen(red_fmtp))) { - /* count numbers of generations in fmtp */ - red_cp = &red_fmtp[strlen(red_fmtp)]; - strncpy(red_fmtp, a, 100); - - sscanf(red_cp, "%30u", &red_data_pt[red_num_gen]); - red_cp = strtok(red_cp, "/"); - while (red_cp && red_num_gen++ < RED_MAX_GENERATION) { - sscanf(red_cp, "%30u", &red_data_pt[red_num_gen]); - red_cp = strtok(NULL, "/"); - } - red_cp = red_fmtp; - continue; - } - - if (sscanf(a, "fmtp: %30u %63s", &codec, fmtp_string) == 2) { - struct rtpPayloadType payload; - unsigned int handled = 0; - - payload = ast_rtp_lookup_pt(newaudiortp, codec); - if (!payload.code) { - /* it wasn't found, try the video rtp */ - payload = ast_rtp_lookup_pt(newvideortp, codec); - } - if (payload.code && payload.isAstFormat) { - unsigned int bit_rate; - - switch (payload.code) { - case AST_FORMAT_SIREN7: - if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) { - if (bit_rate != 32000) { - ast_log(LOG_WARNING, "Got Siren7 offer at %d bps, but only 32000 bps supported; ignoring.\n", bit_rate); - ast_rtp_unset_m_type(newaudiortp, codec); - } else { - handled = 1; - } - } - break; - case AST_FORMAT_SIREN14: - if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) { - if (bit_rate != 48000) { - ast_log(LOG_WARNING, "Got Siren14 offer at %d bps, but only 48000 bps supported; ignoring.\n", bit_rate); - ast_rtp_unset_m_type(newaudiortp, codec); - } else { - handled = 1; - } - } - break; - } - } - - if (!handled) { - ast_debug(1, "Got unsupported a:%s in SDP offer\n", a); - } - continue; - } - - if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) { - /* We have a rtpmap to handle */ - - if (last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) { - /* Note: should really look at the '#chans' params too */ - /* Note: This should all be done in the context of the m= above */ - if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { /* Video */ - if (ast_rtp_set_rtpmap_type_rate(newvideortp, codec, "video", mimeSubtype, 0, sample_rate) != -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 if (!strncasecmp(mimeSubtype, "RED", 3)) { /* Text with Redudancy */ - if (p->trtp) { - ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); - red_pt = codec; - sprintf(red_fmtp, "fmtp:%d ", red_pt); - - if (debug) - ast_verbose("RED submimetype has payload type: %d\n", red_pt); - } - } else { /* Must be audio?? */ - if (ast_rtp_set_rtpmap_type_rate(newaudiortp, codec, "audio", mimeSubtype, - ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, - sample_rate) != -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); - } - } - } else { - if (debug) - ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); - } - - } - } - - if (udptlportno != -1) { - if (p->t38.state != T38_ENABLED) { - int found = 0, x; - - old = 0; - memset(&p->t38.their_parms, 0, sizeof(p->t38.their_parms)); - - /* 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; - ast_debug(3, "MaxBufferSize:%d\n", x); - } else if ((sscanf(a, "T38MaxBitRate:%30d", &x) == 1) || (sscanf(a, "T38FaxMaxRate:%30d", &x) == 1)) { - found = 1; - ast_debug(3, "T38MaxBitRate: %d\n", x); - switch (x) { - case 14400: - p->t38.their_parms.rate = AST_T38_RATE_14400; - break; - case 12000: - p->t38.their_parms.rate = AST_T38_RATE_12000; - break; - case 9600: - p->t38.their_parms.rate = AST_T38_RATE_9600; - break; - case 7200: - p->t38.their_parms.rate = AST_T38_RATE_7200; - break; - case 4800: - p->t38.their_parms.rate = AST_T38_RATE_4800; - break; - case 2400: - p->t38.their_parms.rate = AST_T38_RATE_2400; - break; - } - } else if ((sscanf(a, "T38FaxVersion:%30d", &x) == 1)) { - found = 1; - ast_debug(3, "FaxVersion: %d\n", x); - p->t38.their_parms.version = x; - } else if ((sscanf(a, "T38FaxMaxDatagram:%30d", &x) == 1) || (sscanf(a, "T38MaxDatagram:%30d", &x) == 1)) { - /* override the supplied value if the configuration requests it */ - if (p->t38_maxdatagram > x) { - ast_debug(1, "Overriding T38FaxMaxDatagram '%d' with '%d'\n", x, p->t38_maxdatagram); - x = p->t38_maxdatagram; - } - found = 1; - ast_debug(3, "FaxMaxDatagram: %d\n", x); - ast_udptl_set_far_max_datagram(p->udptl, x); - } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) { - found = 1; - if (sscanf(a, "T38FaxFillBitRemoval:%30d", &x) == 1) { - ast_debug(3, "FillBitRemoval: %d\n", x); - if (x == 1) { - p->t38.their_parms.fill_bit_removal = TRUE; - } - } else { - ast_debug(3, "FillBitRemoval\n"); - p->t38.their_parms.fill_bit_removal = TRUE; - } - } else if ((strncmp(a, "T38FaxTranscodingMMR", 20) == 0)) { - found = 1; - if (sscanf(a, "T38FaxTranscodingMMR:%30d", &x) == 1) { - ast_debug(3, "Transcoding MMR: %d\n", x); - if (x == 1) { - p->t38.their_parms.transcoding_mmr = TRUE; - } - } else { - ast_debug(3, "Transcoding MMR\n"); - p->t38.their_parms.transcoding_mmr = TRUE; - } - } else if ((strncmp(a, "T38FaxTranscodingJBIG", 21) == 0)) { - found = 1; - if (sscanf(a, "T38FaxTranscodingJBIG:%30d", &x) == 1) { - ast_debug(3, "Transcoding JBIG: %d\n", x); - if (x == 1) { - p->t38.their_parms.transcoding_jbig = TRUE; - } - } else { - ast_debug(3, "Transcoding JBIG\n"); - p->t38.their_parms.transcoding_jbig = TRUE; - } - } else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) { - found = 1; - ast_debug(3, "RateManagement: %s\n", s); - if (!strcasecmp(s, "localTCF")) - p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF; - else if (!strcasecmp(s, "transferredTCF")) - p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF; - } else if ((sscanf(a, "T38FaxUdpEC:%255s", s) == 1)) { - found = 1; - ast_debug(3, "UDP EC: %s\n", s); - if (!strcasecmp(s, "t38UDPRedundancy")) { - ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY); - } else if (!strcasecmp(s, "t38UDPFEC")) { - ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC); - } else { - ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE); - } - } - } - - /* Remote party offers T38, we need to update state */ - if ((t38action == SDP_T38_ACCEPT) && - (p->t38.state == T38_LOCAL_REINVITE)) { - change_t38_state(p, T38_ENABLED); - } else if ((t38action == SDP_T38_INITIATE) && - p->owner && p->lastinvite) { - change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */ - } - } - } else { + if (udptlportno == -1) { change_t38_state(p, T38_DISABLED); } + /* 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); 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[SIPBUFSIZE], s2[SIPBUFSIZE], s3[SIPBUFSIZE], s4[SIPBUFSIZE], s5[SIPBUFSIZE]; @@ -8745,25 +8425,79 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } } - /* 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)); + /* Setup audio address and 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)); + /* Setup video address and 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 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)); + /* Setup text address and port */ + if (p->trtp) { + if (tportno > 0) { + tsin.sin_family = AF_INET; + tsin.sin_port = htons(tportno); + memcpy(&tsin.sin_addr, thp->h_addr, sizeof(tsin.sin_addr)); + ast_rtp_set_peer(p->trtp, &tsin); + if (debug) + ast_verbose("Peer T.140 RTP is at port %s:%d\n", ast_inet_ntoa(vsin.sin_addr), ntohs(vsin.sin_port)); + } else { + ast_rtp_stop(p->trtp); + if (debug) + ast_verbose("Peer doesn't provide T.140\n"); + } + } + /* Setup image address and port */ + 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 remote_address = { 0, }; + ast_rtp_get_peer(p->rtp, &remote_address); + if (remote_address.sin_addr.s_addr) { + memcpy(&isin, &remote_address, sizeof(isin)); + 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)); + } + } + } + ast_udptl_set_peer(p->udptl, &isin); + if (debug) + ast_debug(1,"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_debug(1, "Peer doesn't provide T.38 UDPTL\n"); + } } /* Ok, we're going with this offer */ @@ -8835,6 +8569,397 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action return 0; } +static int process_sdp_o(const char *o, struct sip_pvt *p) +{ + char *o_copy; + char *token; + int64_t rua_version; + + /* Store the SDP version number of remote UA. This will allow us to + distinguish between session modifications and session refreshes. If + the remote UA does not send an incremented SDP version number in a + subsequent RE-INVITE then that means its not changing media session. + The RE-INVITE may have been sent to update connected party, remote + target or to refresh the session (Session-Timers). Asterisk must not + change media session and increment its own version number in answer + SDP in this case. */ + + if (ast_strlen_zero(o)) { + ast_log(LOG_WARNING, "SDP syntax error. SDP without an o= line\n"); + return FALSE; + } + + o_copy = ast_strdupa(o); + token = strsep(&o_copy, " "); /* Skip username */ + if (!o_copy) { + ast_log(LOG_WARNING, "SDP syntax error in o= line username\n"); + return FALSE; + } + token = strsep(&o_copy, " "); /* Skip session-id */ + if (!o_copy) { + ast_log(LOG_WARNING, "SDP syntax error in o= line session-id\n"); + return FALSE; + } + token = strsep(&o_copy, " "); /* Version */ + if (!o_copy) { + ast_log(LOG_WARNING, "SDP syntax error in o= line\n"); + return FALSE; + } + if (!sscanf(token, "%30" SCNd64, &rua_version)) { + ast_log(LOG_WARNING, "SDP syntax error in o= line version\n"); + return FALSE; + } + + /* we need to check the SDP version number the other end sent us; + * our rules for deciding what to accept are a bit complex. + * + * 1) if 'ignoresdpversion' has been set for this dialog, then + * we will just accept whatever they sent and assume it is + * a modification of the session, even if it is not + * 2) otherwise, if this is the first SDP we've seen from them + * we accept it + * 3) otherwise, if the new SDP version number is higher than the + * old one, we accept it + * 4) otherwise, if this SDP is in response to us requesting a switch + * to T.38, we accept the SDP, but also generate a warning message + * that this peer should have the 'ignoresdpversion' option set, + * because it is not following the SDP offer/answer RFC; if we did + * not request a switch to T.38, then we stop parsing the SDP, as it + * has not changed from the previous version + */ + + if (ast_test_flag(&p->flags[1], SIP_PAGE2_IGNORESDPVERSION) || + (p->sessionversion_remote < 0) || + (p->sessionversion_remote < rua_version)) { + p->sessionversion_remote = rua_version; + p->session_modify = TRUE; + } else { + if (p->t38.state == T38_LOCAL_REINVITE) { + p->sessionversion_remote = rua_version; + p->session_modify = TRUE; + ast_log(LOG_WARNING, "Call %s responded to our T.38 reinvite without changing SDP version; 'ignoresdpversion' should be set for this peer.\n", p->callid); + } else { + p->session_modify = FALSE; + ast_debug(2, "Call %s responded to our reinvite without changing SDP version; ignoring SDP.\n", p->callid); + return FALSE; + } + } + + return TRUE; +} + +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[128]; + char fmtp_string[64]; + unsigned int sample_rate; + int debug = sip_debug_test_pvt(p); + + if (!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; + ast_debug(1, "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 %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) { + /* We have a rtpmap to handle */ + if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) { + if (ast_rtp_set_rtpmap_type_rate(newaudiortp, codec, "audio", mimeSubtype, + ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0, sample_rate) != -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 { + ast_rtp_unset_m_type(newaudiortp, codec); + if (debug) + ast_verbose("Found unknown media description format %s for ID %d\n", mimeSubtype, codec); + } + } else { + if (debug) + ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); + } + } else if (sscanf(a, "fmtp: %30u %63s", &codec, fmtp_string) == 2) { + struct rtpPayloadType payload; + + payload = ast_rtp_lookup_pt(newaudiortp, codec); + if (payload.code && payload.isAstFormat) { + unsigned int bit_rate; + + switch (payload.code) { + case AST_FORMAT_SIREN7: + if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) { + if (bit_rate != 32000) { + ast_log(LOG_WARNING, "Got Siren7 offer at %d bps, but only 32000 bps supported; ignoring.\n", bit_rate); + ast_rtp_unset_m_type(newaudiortp, codec); + } else { + found = TRUE; + } + } + break; + case AST_FORMAT_SIREN14: + if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) { + if (bit_rate != 48000) { + ast_log(LOG_WARNING, "Got Siren14 offer at %d bps, but only 48000 bps supported; ignoring.\n", bit_rate); + ast_rtp_unset_m_type(newaudiortp, codec); + } else { + found = TRUE; + } + } + break; + } + } + } + + 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[128]; + unsigned int sample_rate; + int debug = sip_debug_test_pvt(p); + + if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) { + /* We have a rtpmap to handle */ + if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) { + /* Note: should really look at the '#chans' params too */ + if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { + if (ast_rtp_set_rtpmap_type_rate(newvideortp, codec, "video", mimeSubtype, 0, sample_rate) != -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 { + 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 (debug) + ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); + } + } + + return found; +} + +static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec) +{ + int found = FALSE; + int codec; + char mimeSubtype[128]; + unsigned int sample_rate; + char *red_cp; + int debug = sip_debug_test_pvt(p); + + if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) { + /* We have a rtpmap to handle */ + if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) { + 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); + found = TRUE; + } + } else if (!strncasecmp(mimeSubtype, "RED", 3)) { /* Text with Redudancy */ + if (p->trtp) { + ast_rtp_set_rtpmap_type(newtextrtp, codec, "text", mimeSubtype, 0); + sprintf(red_fmtp, "fmtp:%d ", codec); + if (debug) + ast_verbose("RED submimetype has payload type: %d\n", codec); + found = TRUE; + } + } + } else { + if (debug) + ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); + } + } else if (!strncmp(a, red_fmtp, strlen(red_fmtp))) { + /* count numbers of generations in fmtp */ + red_cp = &red_fmtp[strlen(red_fmtp)]; + strncpy(red_fmtp, a, 100); + + sscanf(red_cp, "%30u", &red_data_pt[*red_num_gen]); + red_cp = strtok(red_cp, "/"); + while (red_cp && (*red_num_gen)++ < RED_MAX_GENERATION) { + sscanf(red_cp, "%30u", &red_data_pt[*red_num_gen]); + red_cp = strtok(NULL, "/"); + } + red_cp = red_fmtp; + found = TRUE; + } + + return found; +} + +static int process_sdp_a_image(const char *a, struct sip_pvt *p) +{ + int found = FALSE; + char s[256]; + int x; + + if ((sscanf(a, "T38FaxMaxBuffer:%30d", &x) == 1)) { + ast_debug(3, "MaxBufferSize:%d\n", x); + found = TRUE; + } else if ((sscanf(a, "T38MaxBitRate:%30d", &x) == 1) || (sscanf(a, "T38FaxMaxRate:%30d", &x) == 1)) { + ast_debug(3, "T38MaxBitRate: %d\n", x); + switch (x) { + case 14400: + p->t38.their_parms.rate = AST_T38_RATE_14400; + break; + case 12000: + p->t38.their_parms.rate = AST_T38_RATE_12000; + break; + case 9600: + p->t38.their_parms.rate = AST_T38_RATE_9600; + break; + case 7200: + p->t38.their_parms.rate = AST_T38_RATE_7200; + break; + case 4800: + p->t38.their_parms.rate = AST_T38_RATE_4800; + break; + case 2400: + p->t38.their_parms.rate = AST_T38_RATE_2400; + break; + } + found = TRUE; + } else if ((sscanf(a, "T38FaxVersion:%30d", &x) == 1)) { + ast_debug(3, "FaxVersion: %d\n", x); + p->t38.their_parms.version = x; + found = TRUE; + } else if ((sscanf(a, "T38FaxMaxDatagram:%30d", &x) == 1) || (sscanf(a, "T38MaxDatagram:%30d", &x) == 1)) { + /* override the supplied value if the configuration requests it */ + if (p->t38_maxdatagram > x) { + ast_debug(1, "Overriding T38FaxMaxDatagram '%d' with '%d'\n", x, p->t38_maxdatagram); + x = p->t38_maxdatagram; + } + ast_debug(3, "FaxMaxDatagram: %d\n", x); + ast_udptl_set_far_max_datagram(p->udptl, x); + found = TRUE; + } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) { + if (sscanf(a, "T38FaxFillBitRemoval:%30d", &x) == 1) { + ast_debug(3, "FillBitRemoval: %d\n", x); + if (x == 1) { + p->t38.their_parms.fill_bit_removal = TRUE; + } + } else { + ast_debug(3, "FillBitRemoval\n"); + p->t38.their_parms.fill_bit_removal = TRUE; + } + found = TRUE; + } else if ((strncmp(a, "T38FaxTranscodingMMR", 20) == 0)) { + if (sscanf(a, "T38FaxTranscodingMMR:%30d", &x) == 1) { + ast_debug(3, "Transcoding MMR: %d\n", x); + if (x == 1) { + p->t38.their_parms.transcoding_mmr = TRUE; + } + } else { + ast_debug(3, "Transcoding MMR\n"); + p->t38.their_parms.transcoding_mmr = TRUE; + } + found = TRUE; + } else if ((strncmp(a, "T38FaxTranscodingJBIG", 21) == 0)) { + if (sscanf(a, "T38FaxTranscodingJBIG:%30d", &x) == 1) { + ast_debug(3, "Transcoding JBIG: %d\n", x); + if (x == 1) { + p->t38.their_parms.transcoding_jbig = TRUE; + } + } else { + ast_debug(3, "Transcoding JBIG\n"); + p->t38.their_parms.transcoding_jbig = TRUE; + } + found = TRUE; + } else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) { + ast_debug(3, "RateManagement: %s\n", s); + if (!strcasecmp(s, "localTCF")) + p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF; + else if (!strcasecmp(s, "transferredTCF")) + p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF; + found = TRUE; + } else if ((sscanf(a, "T38FaxUdpEC:%255s", s) == 1)) { + ast_debug(3, "UDP EC: %s\n", s); + if (!strcasecmp(s, "t38UDPRedundancy")) { + ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY); + } else if (!strcasecmp(s, "t38UDPFEC")) { + ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC); + } else { + ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE); + } + found = TRUE; + } + + return found; +} + + #ifdef LOW_MEMORY static void ts_ast_rtp_destroy(void *data) { |