diff --git a/UPGRADE.txt b/UPGRADE.txt index 8097c4bae75c987d45bdd9c298b7270cf3ac0aec..dda6d66d8d44f19e827eacb35189b804285bef88 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -18,6 +18,17 @@ === =========================================================== +T.38 changes in 1.6.0.11, 1.6.1.2, 1.6.2.0: + +Beginning with each of these releases in their respective branches, +Asterisk's internal methods of negotiating T.38 (FAX over IP) sessions +changed in non-backwards-compatible ways. Any applications that previously +used AST_CONTROL_T38 control frames will have to be upgraded to use +AST_CONTROL_T38_PARAMETERS control frames instead; app_fax.c is a good +example of how to generate and respond to these frames. These changes were +made to solve significant T.38 interoperability problems between Asterisk +and various SIP/T.38 endpoints identified by many users of Asterisk. + From 1.6.2 to 1.6.3: * Asterisk-addons no longer exists as an independent package. Those modules diff --git a/apps/app_fax.c b/apps/app_fax.c index 6c132c16219a54058d22d4e7cccf430fe643af0c..c2156f0df84c6a5a0af62212ffd851a266581289 100644 --- a/apps/app_fax.c +++ b/apps/app_fax.c @@ -471,11 +471,12 @@ static int transmit_audio(fax_session *s) if (fr && fr->frametype == AST_FRAME_DTMF && fr->subclass == 'f') { struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REQUEST_NEGOTIATE, .version = 0, - .max_datagram = 400, + .max_ifp = 800, .rate = AST_T38_RATE_9600, .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, .fill_bit_removal = 1, .transcoding_mmr = 1, + .transcoding_jbig = 1, }; ast_debug(1, "Fax tone detected. Requesting T38\n"); ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters)); @@ -509,20 +510,18 @@ static int transmit_audio(fax_session *s) res = 1; break; } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) { + struct ast_control_t38_parameters our_parameters = { .request_response = AST_T38_NEGOTIATED, + .version = 0, + .max_ifp = 800, + .rate = AST_T38_RATE_9600, + .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF, + .fill_bit_removal = 1, + .transcoding_mmr = 1, + .transcoding_jbig = 1, + }; ast_debug(1, "T38 request received, accepting\n"); - if (parameters->version > 0) { - /* Only T.38 Version 0 is supported at this time */ - parameters->version = 0; - } - if (parameters->max_datagram > 400) { - /* Limit incoming datagram size to our default */ - /* TODO: this need to come from the udptl stack, not be hardcoded */ - parameters->max_datagram = 400; - } - /* we only support bit rates up to 9.6kbps */ - parameters->rate = AST_T38_RATE_9600; /* Complete T38 switchover */ - ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, parameters, sizeof(*parameters)); + ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &our_parameters, sizeof(our_parameters)); /* Do not break audio loop, wait until channel driver finally acks switchover with AST_T38_NEGOTIATED */ } @@ -593,7 +592,7 @@ static int transmit_t38(fax_session *s) return -1; } - t38_set_max_datagram_size(t38state, s->t38parameters.max_datagram); + t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp); if (s->t38parameters.fill_bit_removal) { t38_set_fill_bit_removal(t38state, TRUE); diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 7297da8f0ea2e8cbf009014810a5e3016d8eaf14..29e0f959513e9b51a6be1ca4314bcd9a24266971 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1511,10 +1511,10 @@ struct sip_auth { #define SIP_PAGE2_SUBSCRIBEMWIONLY (1 << 18) /*!< GP: Only issue MWI notification if subscribed to */ #define SIP_PAGE2_IGNORESDPVERSION (1 << 19) /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */ -#define SIP_PAGE2_T38SUPPORT (7 << 20) /*!< GDP: T38 Fax Passthrough Support */ -#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T38 Fax Passthrough Support (no error correction) */ -#define SIP_PAGE2_T38SUPPORT_UDPTL_FEC (2 << 20) /*!< GDP: T38 Fax Passthrough Support (FEC error correction) */ -#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (4 << 20) /*!< GDP: T38 Fax Passthrough Support (redundancy error correction) */ +#define SIP_PAGE2_T38SUPPORT (7 << 20) /*!< GDP: T.38 Fax Support */ +#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T.38 Fax Support (no error correction) */ +#define SIP_PAGE2_T38SUPPORT_UDPTL_FEC (2 << 20) /*!< GDP: T.38 Fax Support (FEC error correction) */ +#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (4 << 20) /*!< GDP: T.38 Fax Support (redundancy error correction) */ #define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< D: Call hold states: */ #define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< D: Active hold */ @@ -1538,36 +1538,6 @@ struct sip_auth { /*@}*/ -/*! \name SIPflagsT38 - T.38 set of flags */ - -/*@{*/ -#define T38FAX_FILL_BIT_REMOVAL (1 << 0) /*!< Default: 0 (unset)*/ -#define T38FAX_TRANSCODING_MMR (1 << 1) /*!< Default: 0 (unset)*/ -#define T38FAX_TRANSCODING_JBIG (1 << 2) /*!< Default: 0 (unset)*/ -/* Rate management */ -#define T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF (0 << 3) -#define T38FAX_RATE_MANAGEMENT_LOCAL_TCF (1 << 3) /*!< Unset for transferredTCF (UDPTL), set for localTCF (TPKT) */ -/* UDP Error correction */ -#define T38FAX_UDP_EC_NONE (0 << 4) /*!< two bits, if unset NO t38UDPEC field in T38 SDP*/ -#define T38FAX_UDP_EC_FEC (1 << 4) /*!< Set for t38UDPFEC */ -#define T38FAX_UDP_EC_REDUNDANCY (2 << 4) /*!< Set for t38UDPRedundancy */ -/* T38 Spec version */ -#define T38FAX_VERSION (3 << 6) /*!< two bits, 2 values so far, up to 4 values max */ -#define T38FAX_VERSION_0 (0 << 6) /*!< Version 0 */ -#define T38FAX_VERSION_1 (1 << 6) /*!< Version 1 */ -/* Maximum Fax Rate */ -#define T38FAX_RATE_2400 (1 << 8) /*!< 2400 bps t38FaxRate */ -#define T38FAX_RATE_4800 (1 << 9) /*!< 4800 bps t38FaxRate */ -#define T38FAX_RATE_7200 (1 << 10) /*!< 7200 bps t38FaxRate */ -#define T38FAX_RATE_9600 (1 << 11) /*!< 9600 bps t38FaxRate */ -#define T38FAX_RATE_12000 (1 << 12) /*!< 12000 bps t38FaxRate */ -#define T38FAX_RATE_14400 (1 << 13) /*!< 14400 bps t38FaxRate */ - -/*!< This is default: NO MMR and JBIG transcoding, NO fill bit removal, transferredTCF TCF, UDP FEC, Version 0 and 9600 max fax rate */ -static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_RATE_4800 | T38FAX_RATE_7200 | T38FAX_RATE_9600; -/*@}*/ - /*! \brief debugging state * We store separately the debugging requests from the config file * and requests from the CLI. Debugging is enabled if either is set @@ -1598,11 +1568,9 @@ enum t38state { /*! \brief T.38 channel settings (at some point we need to make this alloc'ed */ struct t38properties { - struct ast_flags t38support; /*!< Flag for udptl, rtp or tcp support for this session */ - int capability; /*!< Our T38 capability */ - int peercapability; /*!< Peers T38 capability */ - int jointcapability; /*!< Supported T38 capability at both ends */ enum t38state state; /*!< T.38 state */ + struct ast_control_t38_parameters our_parms; + struct ast_control_t38_parameters their_parms; }; /*! \brief Parameters to know status of transfer */ @@ -4190,7 +4158,7 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int sip_pvt_lock(p); /* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */ - if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT)) { + if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) { switch (p->t38.state) { case T38_LOCAL_REINVITE: case T38_PEER_REINVITE: @@ -4922,57 +4890,12 @@ static void do_setnat(struct sip_pvt *p) } } -/*! \brief Helper function which interprets T.38 capabilities and fills a parameters structure in */ -static void fill_t38_parameters(int capabilities, struct ast_control_t38_parameters *parameters, struct sip_pvt *p) -{ - if (capabilities & T38FAX_VERSION_0) { - parameters->version = 0; - } else if (capabilities & T38FAX_VERSION_1) { - parameters->version = 1; - } - - if (capabilities & T38FAX_RATE_14400) { - parameters->rate = AST_T38_RATE_14400; - } else if (capabilities & T38FAX_RATE_12000) { - parameters->rate = AST_T38_RATE_12000; - } else if (capabilities & T38FAX_RATE_9600) { - parameters->rate = AST_T38_RATE_9600; - } else if (capabilities & T38FAX_RATE_7200) { - parameters->rate = AST_T38_RATE_7200; - } else if (capabilities & T38FAX_RATE_4800) { - parameters->rate = AST_T38_RATE_4800; - } else if (capabilities & T38FAX_RATE_2400) { - parameters->rate = AST_T38_RATE_2400; - } - - if (capabilities & T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF) { - parameters->rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF; - } else if (capabilities & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) { - parameters->rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF; - } - - if (capabilities & T38FAX_FILL_BIT_REMOVAL) { - parameters->fill_bit_removal = 1; - } - - if (capabilities & T38FAX_TRANSCODING_MMR) { - parameters->transcoding_mmr = 1; - } - - if (capabilities & T38FAX_TRANSCODING_JBIG) { - parameters->transcoding_jbig = 1; - } - - parameters->max_datagram = ast_udptl_get_far_max_datagram(p->udptl); -} - /*! \brief Change the T38 state on a SIP dialog */ static void change_t38_state(struct sip_pvt *p, int state) { int old = p->t38.state; struct ast_channel *chan = p->owner; - enum ast_control_t38 message = 0; - struct ast_control_t38_parameters parameters = { 0, }; + struct ast_control_t38_parameters parameters = { .request_response = 0 }; /* Don't bother changing if we are already in the state wanted */ if (old == state) @@ -4987,21 +4910,21 @@ static void change_t38_state(struct sip_pvt *p, int state) /* Given the state requested and old state determine what control frame we want to queue up */ if (state == T38_PEER_REINVITE) { - message = parameters.request_response = AST_T38_REQUEST_NEGOTIATE; - fill_t38_parameters(p->t38.peercapability, ¶meters, p); + parameters = p->t38.their_parms; + parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl); + parameters.request_response = AST_T38_REQUEST_NEGOTIATE; } else if (state == T38_ENABLED) { - message = parameters.request_response = AST_T38_NEGOTIATED; - fill_t38_parameters(p->t38.jointcapability, ¶meters, p); + parameters = p->t38.their_parms; + parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl); + parameters.request_response = AST_T38_NEGOTIATED; } else if (state == T38_DISABLED && old == T38_ENABLED) - message = parameters.request_response = AST_T38_TERMINATED; + parameters.request_response = AST_T38_TERMINATED; else if (state == T38_DISABLED && old == T38_LOCAL_REINVITE) - message = parameters.request_response = AST_T38_REFUSED; + parameters.request_response = AST_T38_REFUSED; /* Woot we got a message, create a control frame and send it on! */ if (parameters.request_response) ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, ¶meters, sizeof(parameters)); - if (message) - ast_queue_control_data(chan, AST_CONTROL_T38, &message, sizeof(message)); if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT) && !p->outgoing_call) { /* fax detection is enabled and this is an incoming call */ @@ -5029,19 +4952,14 @@ static void change_t38_state(struct sip_pvt *p, int state) /*! \brief Set the global T38 capabilities on a SIP dialog structure */ static void set_t38_capabilities(struct sip_pvt *p) { - p->t38.capability = global_t38_capability; if (p->udptl) { if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY) { ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY); - p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY; } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_FEC) { ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC); - p->t38.capability |= T38FAX_UDP_EC_FEC; } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL) { ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE); - p->t38.capability |= T38FAX_UDP_EC_NONE; } - p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF; } } @@ -5139,9 +5057,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) /* t38pt_udptl was enabled in the peer and not in [general] */ dialog->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr); } - ast_copy_flags(&dialog->t38.t38support, &peer->flags[1], SIP_PAGE2_T38SUPPORT); set_t38_capabilities(dialog); - dialog->t38.jointcapability = dialog->t38.capability; } else if (dialog->udptl) { ast_udptl_destroy(dialog->udptl); dialog->udptl = NULL; @@ -5441,9 +5357,6 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) } else { int xmitres; - p->t38.jointcapability = p->t38.capability; - ast_debug(2, "Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability); - sip_pvt_lock(p); xmitres = transmit_invite(p, SIP_INVITE, 1, 2); sip_pvt_unlock(p); @@ -6321,15 +6234,10 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame) we simply forget the frames if we get modem frames before the bridge is up. Fax will re-transmit. */ - if (ast->_state == AST_STATE_UP) { - if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->t38.state == T38_DISABLED) { - if (!p->pendinginvite) { - change_t38_state(p, T38_LOCAL_REINVITE); - transmit_reinvite_with_sdp(p, TRUE, FALSE); - } - } else if (p->udptl && p->t38.state == T38_ENABLED) { - res = ast_udptl_write(p->udptl, frame); - } + if ((ast->_state == AST_STATE_UP) && + p->udptl && + (p->t38.state == T38_ENABLED)) { + res = ast_udptl_write(p->udptl, frame); } sip_pvt_unlock(p); } @@ -6459,66 +6367,34 @@ static int sip_transfer(struct ast_channel *ast, const char *dest) } /*! \brief Helper function which updates T.38 capability information and triggers a reinvite */ -static void interpret_t38_parameters(struct sip_pvt *p, enum ast_control_t38 request_response, const struct ast_control_t38_parameters *parameters) +static void interpret_t38_parameters(struct sip_pvt *p, const struct ast_control_t38_parameters *parameters) { - if (parameters) { - if (!parameters->version) { - p->t38.capability = p->t38.jointcapability |= T38FAX_VERSION_0; - } else if (parameters->version == 1) { - p->t38.capability = p->t38.jointcapability |= T38FAX_VERSION_1; - } - - if (parameters->rate == AST_T38_RATE_14400) { - p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; - } else if (parameters->rate == AST_T38_RATE_12000) { - p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; - } else if (parameters->rate == AST_T38_RATE_9600) { - p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; - } else if (parameters->rate == AST_T38_RATE_7200) { - p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; - } else if (parameters->rate == AST_T38_RATE_4800) { - p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_4800 | T38FAX_RATE_2400; - } else if (parameters->rate == AST_T38_RATE_2400) { - p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_2400; - } - - if (parameters->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF) { - p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF; - } else if (parameters->rate_management == AST_T38_RATE_MANAGEMENT_LOCAL_TCF) { - p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF; - } - - if (parameters->fill_bit_removal) { - p->t38.capability = p->t38.jointcapability |= T38FAX_FILL_BIT_REMOVAL; - } else { - p->t38.capability = p->t38.jointcapability &= ~T38FAX_FILL_BIT_REMOVAL; - } - - if (parameters->transcoding_mmr) { - p->t38.capability = p->t38.jointcapability |= T38FAX_TRANSCODING_MMR; - } else { - p->t38.capability = p->t38.jointcapability &= ~T38FAX_TRANSCODING_MMR; - } - - if (parameters->transcoding_jbig) { - p->t38.capability = p->t38.jointcapability |= T38FAX_TRANSCODING_JBIG; - } else { - p->t38.capability = p->t38.jointcapability &= ~T38FAX_TRANSCODING_JBIG; - } - - if (p->udptl && request_response == AST_T38_REQUEST_NEGOTIATE) { - ast_udptl_set_local_max_datagram(p->udptl, parameters->max_datagram ? parameters->max_datagram : 400); - } - } - - switch (request_response) { + switch (parameters->request_response) { case AST_T38_NEGOTIATED: case AST_T38_REQUEST_NEGOTIATE: /* Request T38 */ if (p->t38.state == T38_PEER_REINVITE) { AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr")); + p->t38.our_parms = *parameters; + /* modify our parameters to conform to the peer's parameters, + * based on the rules in the ITU T.38 recommendation + */ + if (!p->t38.their_parms.fill_bit_removal) { + p->t38.our_parms.fill_bit_removal = FALSE; + } + if (!p->t38.their_parms.transcoding_mmr) { + p->t38.our_parms.transcoding_mmr = FALSE; + } + if (!p->t38.their_parms.transcoding_jbig) { + p->t38.our_parms.transcoding_jbig = FALSE; + } + p->t38.our_parms.version = MIN(p->t38.our_parms.version, p->t38.their_parms.version); + p->t38.our_parms.rate_management = p->t38.their_parms.rate_management; + ast_udptl_set_local_max_ifp(p->udptl, p->t38.our_parms.max_ifp); change_t38_state(p, T38_ENABLED); transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL); - } else if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) { + } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) { + p->t38.our_parms = *parameters; + ast_udptl_set_local_max_ifp(p->udptl, p->t38.our_parms.max_ifp); change_t38_state(p, T38_LOCAL_REINVITE); if (!p->pendinginvite) { transmit_reinvite_with_sdp(p, TRUE, FALSE); @@ -6626,19 +6502,12 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data } else res = -1; break; - case AST_CONTROL_T38: /* T38 control frame */ - if (datalen != sizeof(enum ast_control_t38)) { - ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38. Expected %d, got %d\n", (int)sizeof(enum ast_control_t38), (int)datalen); - } else { - interpret_t38_parameters(p, *((enum ast_control_t38 *) data), NULL); - } - break; case AST_CONTROL_T38_PARAMETERS: if (datalen != sizeof(struct ast_control_t38_parameters)) { - ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38_PARAMETERS. Expected %d, got %d\n", (int)sizeof(struct ast_control_t38_parameters), (int)datalen); + ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38_PARAMETERS. Expected %d, got %d\n", (int) sizeof(struct ast_control_t38_parameters), (int) datalen); } else { const struct ast_control_t38_parameters *parameters = data; - interpret_t38_parameters(p, parameters->request_response, parameters); + interpret_t38_parameters(p, parameters); } break; case AST_CONTROL_SRCUPDATE: @@ -7062,7 +6931,7 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p if (f && p->dsp) { f = ast_dsp_process(p->owner, p->dsp, f); if (f && f->frametype == AST_FRAME_DTMF) { - if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && f->subclass == 'f') { + if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && f->subclass == 'f') { ast_debug(1, "Fax CNG detected on %s\n", ast->name); *faxdetect = 1; } else { @@ -7087,7 +6956,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast) /* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */ /* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */ - if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) { + if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) { if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) { if (!p->pendinginvite) { ast_debug(3, "Sending reinvite on SIP (%s) for T.38 negotiation.\n", ast->name); @@ -7261,9 +7130,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) p->noncodeccapability |= AST_RTP_DTMF; if (p->udptl) { - ast_copy_flags(&p->t38.t38support, &p->flags[1], SIP_PAGE2_T38SUPPORT); set_t38_capabilities(p); - p->t38.jointcapability = p->t38.capability; } ast_string_field_set(p, context, sip_cfg.default_context); ast_string_field_set(p, parkinglot, default_parkinglot); @@ -8050,7 +7917,6 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action int vportno = -1; /*!< RTP Video port number */ int tportno = -1; /*!< RTP Text port number */ int udptlportno = -1; - int peert38capability = 0; char s[256]; int old = 0; @@ -8590,6 +8456,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action 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; @@ -8602,110 +8469,92 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action ast_debug(3, "T38MaxBitRate: %d\n", x); switch (x) { case 14400: - peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; + p->t38.their_parms.rate = AST_T38_RATE_14400; break; case 12000: - peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; + p->t38.their_parms.rate = AST_T38_RATE_12000; break; case 9600: - peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; + p->t38.their_parms.rate = AST_T38_RATE_9600; break; case 7200: - peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400; + p->t38.their_parms.rate = AST_T38_RATE_7200; break; case 4800: - peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400; + p->t38.their_parms.rate = AST_T38_RATE_4800; break; case 2400: - peert38capability |= T38FAX_RATE_2400; + p->t38.their_parms.rate = AST_T38_RATE_2400; break; } } else if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) { found = 1; ast_debug(3, "FaxVersion: %d\n", x); - if (x == 0) - peert38capability |= T38FAX_VERSION_0; - else if (x == 1) - peert38capability |= T38FAX_VERSION_1; + p->t38.their_parms.version = x; } else if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1) || (sscanf(a, "T38MaxDatagram:%d", &x) == 1)) { found = 1; ast_debug(3, "FaxMaxDatagram: %d\n", x); ast_udptl_set_far_max_datagram(p->udptl, x); - if (!ast_udptl_get_local_max_datagram(p->udptl)) { - ast_udptl_set_local_max_datagram(p->udptl, x); - } } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) { found = 1; - if(sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1) { - ast_debug(3, "FillBitRemoval: %d\n", x); - if(x == 1) - peert38capability |= T38FAX_FILL_BIT_REMOVAL; + if (sscanf(a, "T38FaxFillBitRemoval:%d", &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"); - peert38capability |= T38FAX_FILL_BIT_REMOVAL; + 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:%d", &x) == 1) { - ast_debug(3, "Transcoding MMR: %d\n", x); - if(x == 1) - peert38capability |= T38FAX_TRANSCODING_MMR; + if (sscanf(a, "T38FaxTranscodingMMR:%d", &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"); - peert38capability |= T38FAX_TRANSCODING_MMR; + 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:%d", &x) == 1) { - ast_debug(3, "Transcoding JBIG: %d\n", x); - if(x == 1) - peert38capability |= T38FAX_TRANSCODING_JBIG; + if (sscanf(a, "T38FaxTranscodingJBIG:%d", &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"); - peert38capability |= T38FAX_TRANSCODING_JBIG; + 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")) - peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF; + p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF; else if (!strcasecmp(s, "transferredTCF")) - peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF; + 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")) { - peert38capability |= T38FAX_UDP_EC_REDUNDANCY; ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY); } else if (!strcasecmp(s, "t38UDPFEC")) { - peert38capability |= T38FAX_UDP_EC_FEC; ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC); } else { - peert38capability |= T38FAX_UDP_EC_NONE; ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE); } } } - if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */ - p->t38.peercapability = peert38capability; - p->t38.jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */ - peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400); - p->t38.jointcapability |= (peert38capability & p->t38.capability); /* Put the lower of our's and peer's speed */ - } - if (debug) - ast_debug(1, "Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n", - p->t38.capability, - p->t38.peercapability, - p->t38.jointcapability); /* Remote party offers T38, we need to update state */ - if (t38action == SDP_T38_ACCEPT) { - if (p->t38.state == T38_LOCAL_REINVITE) - change_t38_state(p, T38_ENABLED); - } else if (t38action == SDP_T38_INITIATE) { - if (p->owner && p->lastinvite) { - change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */ - } + 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 { change_t38_state(p, T38_DISABLED); @@ -8744,7 +8593,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } if (!newjointcapability) { /* If T.38 was not negotiated either, totally bail out... */ - if (!p->t38.jointcapability || !udptlportno) { + if ((p->t38.state == T38_DISABLED) || !udptlportno) { ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n"); /* Do NOT Change current setting */ return -1; @@ -9885,30 +9734,22 @@ static void add_tcodec_to_sdp(const struct sip_pvt *p, int codec, /*! \brief Get Max T.38 Transmission rate from T38 capabilities */ -static int t38_get_rate(int t38cap) +static unsigned int t38_get_rate(enum ast_control_t38_rate rate) { - 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, "T38MaxBitRate 14400 found\n"); - return 14400; - } else if (maxrate & T38FAX_RATE_12000) { - ast_debug(2, "T38MaxBitRate 12000 found\n"); - return 12000; - } else if (maxrate & T38FAX_RATE_9600) { - ast_debug(2, "T38MaxBitRate 9600 found\n"); - return 9600; - } else if (maxrate & T38FAX_RATE_7200) { - ast_debug(2, "T38MaxBitRate 7200 found\n"); - return 7200; - } else if (maxrate & T38FAX_RATE_4800) { - ast_debug(2, "T38MaxBitRate 4800 found\n"); - return 4800; - } else if (maxrate & T38FAX_RATE_2400) { - ast_debug(2, "T38MaxBitRate 2400 found\n"); + switch (rate) { + case AST_T38_RATE_2400: return 2400; - } else { - ast_debug(2, "Strange, T38MaxBitRate NOT found in peers T38 SDP.\n"); + case AST_T38_RATE_4800: + return 4800; + case AST_T38_RATE_7200: + return 7200; + case AST_T38_RATE_9600: + return 9600; + case AST_T38_RATE_12000: + return 12000; + case AST_T38_RATE_14400: + return 14400; + default: return 0; } } @@ -10224,35 +10065,38 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int /* 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, "m=image %d udptl t38", 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); - if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL) + ast_str_append(&a_modem, 0, "a=T38Faxversion:%d\r\n", p->t38.our_parms.version); + ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n", t38_get_rate(p->t38.our_parms.rate)); + if (p->t38.our_parms.fill_bit_removal) { ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval\r\n"); - if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR) + } + if (p->t38.our_parms.transcoding_mmr) { ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR\r\n"); - if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG) + } + if (p->t38.our_parms.transcoding_jbig) { ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG\r\n"); - 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_REDUNDANCY) - ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPRedundancy\r\n"); - else if (p->t38.jointcapability & T38FAX_UDP_EC_FEC) + } + switch (p->t38.our_parms.rate_management) { + case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF: + ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:transferredTCF\r\n"); + break; + case AST_T38_RATE_MANAGEMENT_LOCAL_TCF: + ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:localTCF\r\n"); + break; + } + ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n", ast_udptl_get_local_max_datagram(p->udptl)); + switch (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) { + case SIP_PAGE2_T38SUPPORT_UDPTL: + break; + case SIP_PAGE2_T38SUPPORT_UDPTL_FEC: ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPFEC\r\n"); + break; + case SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY: + ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPRedundancy\r\n"); + break; + } } if (needaudio) @@ -10335,7 +10179,6 @@ static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct s } respprep(&resp, p, msg, req); if (p->udptl) { - ast_udptl_offered_from_local(p->udptl, 0); add_sdp(&resp, p, 0, 0, 1); } else ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid); @@ -10862,7 +10705,6 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init) if (sdp) { memset(p->offered_media, 0, sizeof(p->offered_media)); if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) { - 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_sdp(&req, p, FALSE, FALSE, TRUE); } else if (p->rtp) { @@ -14132,7 +13974,6 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->udptl) { set_t38_capabilities(p); - p->t38.jointcapability = p->t38.capability; } /* Copy SIP extensions profile to peer */ @@ -14231,8 +14072,6 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, else p->noncodeccapability &= ~AST_RTP_DTMF; p->jointnoncodeccapability = p->noncodeccapability; - if (p->t38.peercapability) - p->t38.jointcapability &= p->t38.peercapability; if (!dialog_initialize_rtp(p)) { if (p->rtp) { ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs); @@ -20394,9 +20233,6 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int /* If T38 is needed but not present, then make it magically appear */ if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && !p->udptl && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) { set_t38_capabilities(p); - p->t38.jointcapability = p->t38.capability; - set_t38_capabilities(p); - p->t38.jointcapability = p->t38.capability; } /* We have a succesful authentication, process the SDP portion if there is one */ diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index e64800754b9c3b2db33eb612822c838f89b61e1d..db90e1845999b967c3f4c2d2dcf8370ff5b418b2 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -319,7 +319,7 @@ enum ast_control_frame_type { AST_CONTROL_HOLD = 16, /*!< Indicate call is placed on hold */ AST_CONTROL_UNHOLD = 17, /*!< Indicate call is left from hold */ AST_CONTROL_VIDUPDATE = 18, /*!< Indicate video frame update */ - AST_CONTROL_T38 = 19, /*!< T38 state change request/notification */ + _XXX_AST_CONTROL_T38 = 19, /*!< T38 state change request/notification \deprecated This is no longer supported. Use AST_CONTROL_T38_PARAMETERS instead. */ AST_CONTROL_SRCUPDATE = 20, /*!< Indicate source of media has changed */ AST_CONTROL_TRANSFER = 21, /*!< Indicate status of a transfer request */ AST_CONTROL_CONNECTED_LINE = 22,/*!< Indicate connected line has changed */ @@ -350,14 +350,14 @@ enum ast_control_t38_rate_management { }; struct ast_control_t38_parameters { - enum ast_control_t38 request_response; /*!< Request or response of the T38 control frame */ - unsigned int version; /*!< Supported T.38 version */ - unsigned int max_datagram; /*!< Maximum datagram size supported */ - enum ast_control_t38_rate rate; /*!< Maximum fax rate supported */ - enum ast_control_t38_rate_management rate_management; /*!< Rate management setting */ - unsigned int fill_bit_removal:1; /*!< Set if fill bit removal should be used */ - unsigned int transcoding_mmr:1; /*!< Set if MMR transcoding should be used */ - unsigned int transcoding_jbig:1; /*!< Set if JBIG transcoding should be used */ + enum ast_control_t38 request_response; /*!< Request or response of the T38 control frame */ + unsigned int version; /*!< Supported T.38 version */ + unsigned int max_ifp; /*!< Maximum IFP size supported */ + enum ast_control_t38_rate rate; /*!< Maximum fax rate supported */ + enum ast_control_t38_rate_management rate_management; /*!< Rate management setting */ + unsigned int fill_bit_removal:1; /*!< Set if fill bit removal can be used */ + unsigned int transcoding_mmr:1; /*!< Set if MMR transcoding can be used */ + unsigned int transcoding_jbig:1; /*!< Set if JBIG transcoding can be used */ }; enum ast_control_transfer { diff --git a/include/asterisk/udptl.h b/include/asterisk/udptl.h index 4d6b890687fe1372767180515e0ec2497edf6136..72b9af4aaab46a4b75a8bcb1410b1570c61aa861 100644 --- a/include/asterisk/udptl.h +++ b/include/asterisk/udptl.h @@ -33,7 +33,7 @@ #include "asterisk/channel.h" -enum { +enum ast_t38_ec_modes { UDPTL_ERROR_CORRECTION_NONE, UDPTL_ERROR_CORRECTION_FEC, UDPTL_ERROR_CORRECTION_REDUNDANCY @@ -60,11 +60,11 @@ struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context * struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr in); -void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them); +void ast_udptl_set_peer(struct ast_udptl *udptl, const struct sockaddr_in *them); -void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them); +void ast_udptl_get_peer(const struct ast_udptl *udptl, struct sockaddr_in *them); -void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us); +void ast_udptl_get_us(const struct ast_udptl *udptl, struct sockaddr_in *us); void ast_udptl_destroy(struct ast_udptl *udptl); @@ -78,37 +78,33 @@ int ast_udptl_write(struct ast_udptl *udptl, struct ast_frame *f); struct ast_frame *ast_udptl_read(struct ast_udptl *udptl); -int ast_udptl_fd(struct ast_udptl *udptl); +int ast_udptl_fd(const struct ast_udptl *udptl); -int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos); +int ast_udptl_setqos(struct ast_udptl *udptl, unsigned int tos, unsigned int cos); -void ast_udptl_set_m_type(struct ast_udptl* udptl, int pt); +void ast_udptl_set_m_type(struct ast_udptl *udptl, unsigned int pt); -void ast_udptl_set_udptlmap_type(struct ast_udptl* udptl, int pt, - char* mimeType, char* mimeSubtype); +void ast_udptl_set_udptlmap_type(struct ast_udptl *udptl, unsigned int pt, + char *mimeType, char *mimeSubtype); -int ast_udptl_lookup_code(struct ast_udptl* udptl, int isAstFormat, int code); +int ast_udptl_get_error_correction_scheme(const struct ast_udptl *udptl); -void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local); +void ast_udptl_set_error_correction_scheme(struct ast_udptl *udptl, enum ast_t38_ec_modes ec); -int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl); +void ast_udptl_set_local_max_ifp(struct ast_udptl *udptl, unsigned int max_ifp); -void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec); +unsigned int ast_udptl_get_local_max_datagram(const struct ast_udptl *udptl); -int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl); +void ast_udptl_set_far_max_datagram(struct ast_udptl *udptl, unsigned int max_datagram); -void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram); +unsigned int ast_udptl_get_far_max_datagram(const struct ast_udptl *udptl); -int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl); - -void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram); - -void ast_udptl_get_current_formats(struct ast_udptl* udptl, - int* astFormats, int* nonAstFormats); +unsigned int ast_udptl_get_far_max_ifp(const struct ast_udptl *udptl); void ast_udptl_setnat(struct ast_udptl *udptl, int nat); -int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc); +int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, + struct ast_frame **fo, struct ast_channel **rc); int ast_udptl_proto_register(struct ast_udptl_protocol *proto); diff --git a/main/channel.c b/main/channel.c index dce0747d5778eb1adcd30103c0140f1f38bf76b4..1052d0fe8fde1b60181ae75a32e75b724fc98d67 100644 --- a/main/channel.c +++ b/main/channel.c @@ -3488,11 +3488,11 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con case AST_CONTROL_TAKEOFFHOOK: case AST_CONTROL_ANSWER: case AST_CONTROL_HANGUP: - case AST_CONTROL_T38: case AST_CONTROL_CONNECTED_LINE: case AST_CONTROL_REDIRECTING: case AST_CONTROL_TRANSFER: case AST_CONTROL_T38_PARAMETERS: + case _XXX_AST_CONTROL_T38: break; case AST_CONTROL_CONGESTION: @@ -3585,7 +3585,9 @@ int ast_indicate_data(struct ast_channel *chan, int _condition, /* Handle conditions that we have tones for. */ switch (condition) { - case AST_CONTROL_T38: + case _XXX_AST_CONTROL_T38: + /* deprecated T.38 control frame */ + return -1; case AST_CONTROL_T38_PARAMETERS: /* there is no way to provide 'default' behavior for these * control frames, so we need to return failure, but there @@ -5503,7 +5505,6 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct case AST_CONTROL_HOLD: case AST_CONTROL_UNHOLD: case AST_CONTROL_VIDUPDATE: - case AST_CONTROL_T38: case AST_CONTROL_SRCUPDATE: ast_indicate_data(other, f->subclass, f->data.ptr, f->datalen); if (jb_in_use) { diff --git a/main/frame.c b/main/frame.c index 09c11dbdafc25fc07f726c04c536029c72380728..fa443407c63713ba0cd37dd88e7bbd67e37d2c48 100644 --- a/main/frame.c +++ b/main/frame.c @@ -839,24 +839,6 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix) case AST_CONTROL_UNHOLD: strcpy(subclass, "Unhold"); break; - case AST_CONTROL_T38: - if (f->datalen != sizeof(enum ast_control_t38)) { - message = "Invalid"; - } else { - enum ast_control_t38 state = *((enum ast_control_t38 *) f->data.ptr); - if (state == AST_T38_REQUEST_NEGOTIATE) - message = "Negotiation Requested"; - else if (state == AST_T38_REQUEST_TERMINATE) - message = "Negotiation Request Terminated"; - else if (state == AST_T38_NEGOTIATED) - message = "Negotiated"; - else if (state == AST_T38_TERMINATED) - message = "Terminated"; - else if (state == AST_T38_REFUSED) - message = "Refused"; - } - snprintf(subclass, sizeof(subclass), "T38/%s", message); - break; case AST_CONTROL_T38_PARAMETERS: if (f->datalen != sizeof(struct ast_control_t38_parameters *)) { message = "Invalid"; diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 138b7c03cf5b6b1b1d9d27646738106abe08e8d5..573502811c58b81a7e817ddb19f5889d35ab427a 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -853,7 +853,6 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a if ((fr->subclass == AST_CONTROL_HOLD) || (fr->subclass == AST_CONTROL_UNHOLD) || (fr->subclass == AST_CONTROL_VIDUPDATE) || - (fr->subclass == AST_CONTROL_T38) || (fr->subclass == AST_CONTROL_SRCUPDATE) || (fr->subclass == AST_CONTROL_T38_PARAMETERS)) { /* If we are going on hold, then break callback mode and P2P bridging */ @@ -1073,7 +1072,6 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct if ((fr->subclass == AST_CONTROL_HOLD) || (fr->subclass == AST_CONTROL_UNHOLD) || (fr->subclass == AST_CONTROL_VIDUPDATE) || - (fr->subclass == AST_CONTROL_T38) || (fr->subclass == AST_CONTROL_SRCUPDATE) || (fr->subclass == AST_CONTROL_T38_PARAMETERS)) { if (fr->subclass == AST_CONTROL_HOLD) { diff --git a/main/udptl.c b/main/udptl.c index 7f7a2b76aa9fc638df7bf92792ef6bf06dfdf252..86a198202aa1a7bca49c546da8492fd114f9d451 100644 --- a/main/udptl.c +++ b/main/udptl.c @@ -81,7 +81,7 @@ static struct sockaddr_in udptldebugaddr; /*!< Debug packets to/from this host #ifdef SO_NO_CHECK static int nochecksums; #endif -static int udptlfectype; +static enum ast_t38_ec_modes udptlfectype; static int udptlfecentries; static int udptlfecspan; static int udptlmaxdatagram; @@ -100,10 +100,10 @@ typedef struct { typedef struct { int buf_len; uint8_t buf[LOCAL_FAX_MAX_DATAGRAM]; - int fec_len[MAX_FEC_ENTRIES]; + unsigned int fec_len[MAX_FEC_ENTRIES]; uint8_t fec[MAX_FEC_ENTRIES][LOCAL_FAX_MAX_DATAGRAM]; - int fec_span; - int fec_entries; + unsigned int fec_span; + unsigned int fec_entries; } udptl_fec_rx_buffer_t; /*! \brief Structure for an UDPTL session */ @@ -122,35 +122,51 @@ struct ast_udptl { struct io_context *io; void *data; ast_udptl_callback callback; - int udptl_offered_from_local; /*! This option indicates the error correction scheme used in transmitted UDPTL - packets. */ - int error_correction_scheme; + * packets and expected in received UDPTL packets. + */ + enum ast_t38_ec_modes error_correction_scheme; /*! This option indicates the number of error correction entries transmitted in - UDPTL packets. */ - int error_correction_entries; + * UDPTL packets and expected in received UDPTL packets. + */ + unsigned int error_correction_entries; /*! This option indicates the span of the error correction entries in transmitted - UDPTL packets (FEC only). */ - int error_correction_span; - - /*! This option indicates the maximum size of a UDPTL packet that can be accepted by - the remote device. */ - int far_max_datagram_size; - - /*! This option indicates the maximum size of a UDPTL packet that we are prepared to - accept. */ - int local_max_datagram_size; + * UDPTL packets (FEC only). + */ + unsigned int error_correction_span; + + /*! The maximum size UDPTL packet that can be accepted by + * the remote device. + */ + unsigned int far_max_datagram; + + /*! The maximum size UDPTL packet that we are prepared to + * accept. + */ + unsigned int local_max_datagram; + + /*! The maximum IFP that can be submitted for sending + * to the remote device. Calculated from far_max_datagram, + * error_correction_scheme and error_correction_entries. + */ + unsigned int far_max_ifp; + + /*! The maximum IFP that the local endpoint is prepared + * to accept. Along with error_correction_scheme and + * error_correction_entries, used to calculate local_max_datagram. + */ + unsigned int local_max_ifp; int verbose; struct sockaddr_in far; - int tx_seq_no; - int rx_seq_no; - int rx_expected_seq_no; + unsigned int tx_seq_no; + unsigned int rx_seq_no; + unsigned int rx_expected_seq_no; udptl_fec_tx_buffer_t tx[UDPTL_BUF_MASK + 1]; udptl_fec_rx_buffer_t rx[UDPTL_BUF_MASK + 1]; @@ -158,23 +174,20 @@ struct ast_udptl { static AST_RWLIST_HEAD_STATIC(protos, ast_udptl_protocol); -static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len); -static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uint8_t *ifp, int ifp_len); - -static inline int udptl_debug_test_addr(struct sockaddr_in *addr) +static inline int udptl_debug_test_addr(const struct sockaddr_in *addr) { if (udptldebug == 0) return 0; if (udptldebugaddr.sin_addr.s_addr) { - if (((ntohs(udptldebugaddr.sin_port) != 0) - && (udptldebugaddr.sin_port != addr->sin_port)) - || (udptldebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) + if (((ntohs(udptldebugaddr.sin_port) != 0) && + (udptldebugaddr.sin_port != addr->sin_port)) || + (udptldebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) return 0; } return 1; } -static int decode_length(uint8_t *buf, int limit, int *len, int *pvalue) +static int decode_length(uint8_t *buf, unsigned int limit, unsigned int *len, unsigned int *pvalue) { if (*len >= limit) return -1; @@ -199,15 +212,16 @@ static int decode_length(uint8_t *buf, int limit, int *len, int *pvalue) } /*- End of function --------------------------------------------------------*/ -static int decode_open_type(uint8_t *buf, int limit, int *len, const uint8_t **p_object, int *p_num_octets) +static int decode_open_type(uint8_t *buf, unsigned int limit, unsigned int *len, const uint8_t **p_object, unsigned int *p_num_octets) { - int octet_cnt; - int octet_idx; - int length; - int i; + unsigned int octet_cnt; + unsigned int octet_idx; + unsigned int length; + unsigned int i; const uint8_t **pbuf; for (octet_idx = 0, *p_num_octets = 0; ; octet_idx += octet_cnt) { + octet_cnt = 0; if ((length = decode_length(buf, limit, len, &octet_cnt)) < 0) return -1; if (octet_cnt > 0) { @@ -229,9 +243,9 @@ static int decode_open_type(uint8_t *buf, int limit, int *len, const uint8_t **p } /*- End of function --------------------------------------------------------*/ -static int encode_length(uint8_t *buf, int *len, int value) +static unsigned int encode_length(uint8_t *buf, unsigned int *len, unsigned int value) { - int multiplier; + unsigned int multiplier; if (value < 0x80) { /* 1 octet */ @@ -257,10 +271,10 @@ static int encode_length(uint8_t *buf, int *len, int value) } /*- End of function --------------------------------------------------------*/ -static int encode_open_type(uint8_t *buf, int buflen, int *len, const uint8_t *data, int num_octets) +static int encode_open_type(uint8_t *buf, unsigned int buflen, unsigned int *len, const uint8_t *data, unsigned int num_octets) { - int enclen; - int octet_idx; + unsigned int enclen; + unsigned int octet_idx; uint8_t zero_byte; /* If open type is of zero length, add a single zero byte (10.1) */ @@ -289,7 +303,7 @@ static int encode_open_type(uint8_t *buf, int buflen, int *len, const uint8_t *d } /*- End of function --------------------------------------------------------*/ -static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len) +static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len) { int stat1; int stat2; @@ -301,16 +315,16 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len) int x; int limit; int which; - int ptr; - int count; + unsigned int ptr; + unsigned int count; int total_count; int seq_no; const uint8_t *ifp; const uint8_t *data; - int ifp_len; + unsigned int ifp_len; int repaired[16]; const uint8_t *bufs[16]; - int lengths[16]; + unsigned int lengths[16]; int span; int entries; int ifp_no; @@ -497,7 +511,7 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len) } /*- End of function --------------------------------------------------------*/ -static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uint8_t *ifp, int ifp_len) +static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, unsigned int buflen, uint8_t *ifp, unsigned int ifp_len) { uint8_t fec[LOCAL_FAX_MAX_DATAGRAM * 2]; int i; @@ -507,7 +521,7 @@ static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uin int entries; int span; int m; - int len; + unsigned int len; int limit; int high_tide; @@ -612,7 +626,7 @@ static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uin return len; } -int ast_udptl_fd(struct ast_udptl *udptl) +int ast_udptl_fd(const struct ast_udptl *udptl) { return udptl->fd; } @@ -695,15 +709,67 @@ struct ast_frame *ast_udptl_read(struct ast_udptl *udptl) return &udptl->f[0]; } -void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local) +static void calculate_local_max_datagram(struct ast_udptl *udptl) { - if (udptl) - udptl->udptl_offered_from_local = local; - else - ast_log(LOG_WARNING, "udptl structure is null\n"); + unsigned int new_max = 200; + + /* calculate the amount of space required to receive an IFP + * using the current error correction mode, and ensure that our + * local max datagram size is at least that big + */ + switch (udptl->error_correction_scheme) { + case UDPTL_ERROR_CORRECTION_NONE: + /* only need room for sequence number and length indicators */ + new_max = 6 + udptl->local_max_ifp; + break; + case UDPTL_ERROR_CORRECTION_REDUNDANCY: + /* need room for sequence number, length indicators and the + * configured number of redundant packets + */ + new_max = 6 + udptl->local_max_ifp + 2 + (udptl->error_correction_entries * udptl->local_max_ifp); + break; + case UDPTL_ERROR_CORRECTION_FEC: + /* need room for sequence number, length indicators and a + * a single IFP of the maximum size expected + */ + new_max = 6 + udptl->local_max_ifp + 4 + udptl->local_max_ifp; + break; + } + /* add 25% of extra space for insurance, but no larger than LOCAL_FAX_MAX_DATAGRAM */ + udptl->local_max_datagram = MIN(new_max * 1.25, LOCAL_FAX_MAX_DATAGRAM); +} + +static void calculate_far_max_ifp(struct ast_udptl *udptl) +{ + unsigned new_max = 40; + + /* calculate the maximum IFP the local endpoint should + * generate based on the far end's maximum datagram size + * and the current error correction mode + */ + switch (udptl->error_correction_scheme) { + case UDPTL_ERROR_CORRECTION_NONE: + /* only need room for sequence number and length indicators */ + new_max = udptl->far_max_datagram - 6; + break; + case UDPTL_ERROR_CORRECTION_REDUNDANCY: + /* need room for sequence number, length indicators and the + * configured number of redundant packets + */ + new_max = (udptl->far_max_datagram - 8) / (udptl->error_correction_entries + 1); + break; + case UDPTL_ERROR_CORRECTION_FEC: + /* need room for sequence number, length indicators and a + * a single IFP of the maximum size expected + */ + new_max = (udptl->far_max_datagram - 10) / 2; + break; + } + /* subtract 25% of space for insurance */ + udptl->far_max_ifp = new_max * 0.75; } -int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl) +int ast_udptl_get_error_correction_scheme(const struct ast_udptl *udptl) { if (udptl) return udptl->error_correction_scheme; @@ -713,60 +779,75 @@ int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl) } } -void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec) +void ast_udptl_set_error_correction_scheme(struct ast_udptl *udptl, enum ast_t38_ec_modes ec) { if (udptl) { + udptl->error_correction_scheme = ec; switch (ec) { case UDPTL_ERROR_CORRECTION_FEC: udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC; + if (udptl->error_correction_entries == 0) { + udptl->error_correction_entries = 3; + } + if (udptl->error_correction_span == 0) { + udptl->error_correction_span = 3; + } break; case UDPTL_ERROR_CORRECTION_REDUNDANCY: udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY; - break; - case UDPTL_ERROR_CORRECTION_NONE: - udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE; + if (udptl->error_correction_entries == 0) { + udptl->error_correction_entries = 3; + } break; default: - ast_log(LOG_WARNING, "error correction parameter invalid\n"); + /* nothing to do */ + break; }; + calculate_local_max_datagram(udptl); + calculate_far_max_ifp(udptl); } else ast_log(LOG_WARNING, "udptl structure is null\n"); } -int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl) +unsigned int ast_udptl_get_local_max_datagram(const struct ast_udptl *udptl) { if (udptl) - return udptl->local_max_datagram_size; + return udptl->local_max_datagram; else { ast_log(LOG_WARNING, "udptl structure is null\n"); - return -1; + return 0; } } -int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl) +unsigned int ast_udptl_get_far_max_datagram(const struct ast_udptl *udptl) { if (udptl) - return udptl->far_max_datagram_size; + return udptl->far_max_datagram; else { ast_log(LOG_WARNING, "udptl structure is null\n"); - return -1; + return 0; } } -void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram) +void ast_udptl_set_far_max_datagram(struct ast_udptl *udptl, unsigned int max_datagram) { - if (udptl) - udptl->local_max_datagram_size = max_datagram; - else + if (udptl) { + udptl->far_max_datagram = max_datagram; + calculate_far_max_ifp(udptl); + } else { ast_log(LOG_WARNING, "udptl structure is null\n"); + } } -void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram) +void ast_udptl_set_local_max_ifp(struct ast_udptl *udptl, unsigned int max_ifp) { - if (udptl) - udptl->far_max_datagram_size = max_datagram; - else - ast_log(LOG_WARNING, "udptl structure is null\n"); + udptl->local_max_ifp = max_ifp; + calculate_local_max_datagram(udptl); +} + +unsigned int ast_udptl_get_far_max_ifp(const struct ast_udptl *udptl) +{ + return udptl->far_max_ifp; } struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr addr) @@ -780,20 +861,13 @@ struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struc if (!(udptl = ast_calloc(1, sizeof(*udptl)))) return NULL; - if (udptlfectype == 2) - udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC; - else if (udptlfectype == 1) - udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY; - else - udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE; + udptl->error_correction_scheme = udptlfectype; udptl->error_correction_span = udptlfecspan; udptl->error_correction_entries = udptlfecentries; - udptl->far_max_datagram_size = udptlmaxdatagram; - udptl->local_max_datagram_size = udptlmaxdatagram; + udptl->far_max_datagram = udptlmaxdatagram; + udptl->local_max_datagram = udptlmaxdatagram; - memset(&udptl->rx, 0, sizeof(udptl->rx)); - memset(&udptl->tx, 0, sizeof(udptl->tx)); for (i = 0; i <= UDPTL_BUF_MASK; i++) { udptl->rx[i].buf_len = -1; udptl->tx[i].buf_len = -1; @@ -852,18 +926,18 @@ struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context * return ast_udptl_new_with_bindaddr(sched, io, callbackmode, ia); } -int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos) +int ast_udptl_setqos(struct ast_udptl *udptl, unsigned int tos, unsigned int cos) { return ast_netsock_set_qos(udptl->fd, tos, cos, "UDPTL"); } -void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them) +void ast_udptl_set_peer(struct ast_udptl *udptl, const struct sockaddr_in *them) { udptl->them.sin_port = them->sin_port; udptl->them.sin_addr = them->sin_addr; } -void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them) +void ast_udptl_get_peer(const struct ast_udptl *udptl, struct sockaddr_in *them) { memset(them, 0, sizeof(*them)); them->sin_family = AF_INET; @@ -871,7 +945,7 @@ void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them) them->sin_addr = udptl->them.sin_addr; } -void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us) +void ast_udptl_get_us(const struct ast_udptl *udptl, struct sockaddr_in *us) { memcpy(us, &udptl->us, sizeof(udptl->us)); } @@ -893,10 +967,10 @@ void ast_udptl_destroy(struct ast_udptl *udptl) int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f) { - int seq; - int len; + unsigned int seq; + unsigned int len; int res; - uint8_t buf[LOCAL_FAX_MAX_DATAGRAM * 2]; + uint8_t buf[s->far_max_datagram]; /* If we have no peer, return immediately */ if (s->them.sin_addr.s_addr == INADDR_ANY) @@ -906,11 +980,16 @@ int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f) if (f->datalen == 0) return 0; - if (f->frametype != AST_FRAME_MODEM) { - ast_log(LOG_WARNING, "UDPTL can only send T.38 data\n"); + if ((f->frametype != AST_FRAME_MODEM) || + (f->subclass != AST_MODEM_T38)) { + ast_log(LOG_WARNING, "UDPTL can only send T.38 data.\n"); return -1; } + if (f->datalen > s->far_max_ifp) { + ast_log(LOG_WARNING, "UDPTL asked to send %d bytes of IFP when far end only prepared to accept %d bytes; data loss may occur.\n", f->datalen, s->far_max_ifp); + } + /* Save seq_no for debug output because udptl_build_packet increments it */ seq = s->tx_seq_no & 0xFFFF; @@ -1175,7 +1254,7 @@ static void __ast_udptl_reload(int reload) udptlstart = 4500; udptlend = 4999; - udptlfectype = 0; + udptlfectype = UDPTL_ERROR_CORRECTION_NONE; udptlfecentries = 0; udptlfecspan = 0; udptlmaxdatagram = 0; @@ -1216,9 +1295,9 @@ static void __ast_udptl_reload(int reload) } if ((s = ast_variable_retrieve(cfg, "general", "T38FaxUdpEC"))) { if (strcmp(s, "t38UDPFEC") == 0) - udptlfectype = 2; + udptlfectype = UDPTL_ERROR_CORRECTION_FEC; else if (strcmp(s, "t38UDPRedundancy") == 0) - udptlfectype = 1; + udptlfectype = UDPTL_ERROR_CORRECTION_REDUNDANCY; } if ((s = ast_variable_retrieve(cfg, "general", "T38FaxMaxDatagram"))) { udptlmaxdatagram = atoi(s);