From 5aabb54f68f495bfd51c083bd89d4fdaa1a9ba6c Mon Sep 17 00:00:00 2001 From: Dragos Oancea <dragos@signalwire.com> Date: Fri, 4 Sep 2020 08:57:28 +0000 Subject: [PATCH] [core] eavesdrop: init L16 codec at right ptime in certain conditions. [core] eavesdrop: avoid eavesdropping on itself and return error. [core] eavesdrop: adjust buffer operations for ptime mismatch and for when ptimes are the same. [core] eavesdrop: add buffering based on LCM (Least Common Multiple) when ptime mismatch, and have audio write thread enabled when ptime eavesdropee < ptime eavesdropper. [unit-tests] add unit-tests for eavesdrop. --- src/switch_ivr_async.c | 113 +++++- tests/unit/Makefile.am | 5 + tests/unit/conf_eavesdrop/freeswitch.xml | 238 +++++++++++++ tests/unit/conf_eavesdrop/gw/eavestest.xml | 14 + tests/unit/switch_eavesdrop.c | 392 +++++++++++++++++++++ 5 files changed, 743 insertions(+), 19 deletions(-) create mode 100644 tests/unit/conf_eavesdrop/freeswitch.xml create mode 100644 tests/unit/conf_eavesdrop/gw/eavestest.xml create mode 100644 tests/unit/switch_eavesdrop.c diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index a6972e847c..07f28a36e8 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -2184,6 +2184,25 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_update_display(switch_core_ return status; } +/*Greatest Common Divisor*/ +static uint32_t switch_gcd(uint32_t x, uint32_t y) +{ + if (y == 0) { + return x; + } + + return switch_gcd(y, x % y); +} + +/*Least Common Multiple*/ +static uint32_t switch_lcm(uint32_t x, uint32_t y) +{ + uint32_t gcd = switch_gcd(x, y); + + if (gcd) return (x * y) / gcd; + + return 0; +} SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session_t *session, const char *uuid, const char *require_group, switch_eavesdrop_flag_t flags) @@ -2213,11 +2232,17 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session const char *vval; int buf_size = 0; int channels; + int lcm, buff_min_len, buffered = 1; if (!switch_channel_media_up(channel)) { goto end; } + if (tsession == session) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Eavesdrop target invalid.\n"); + goto end; + } + while(switch_channel_state_change_pending(tchannel) || !switch_channel_media_up(tchannel)) { switch_yield(10000); if (!--sanity) break; @@ -2286,8 +2311,21 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session goto end; } - - if (switch_core_codec_init(&codec, + if (tread_impl.decoded_bytes_per_packet < read_impl.decoded_bytes_per_packet) { + if (switch_core_codec_init(&codec, + "L16", + NULL, + NULL, + read_impl.actual_samples_per_second, + read_impl.microseconds_per_packet / 1000, + read_impl.number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); + goto end; + } + } else { + if (switch_core_codec_init(&codec, "L16", NULL, NULL, @@ -2298,10 +2336,10 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); goto end; + } + buffered = 0; } - switch_core_session_get_read_impl(session, &read_impl); - ep->read_impl = read_impl; ep->tread_impl = tread_impl; @@ -2440,6 +2478,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session switch_core_session_receive_message(tsession, &msg); } + lcm = switch_lcm(tread_impl.decoded_bytes_per_packet, read_impl.decoded_bytes_per_packet); + while (switch_channel_up_nosig(tchannel) && switch_channel_ready(channel)) { uint32_t len = sizeof(buf); switch_event_t *event = NULL; @@ -2569,15 +2609,24 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session channels = 1; } - tlen = tread_impl.decoded_bytes_per_packet * channels; + tlen = ep->read_impl.decoded_bytes_per_packet * channels; if (len > tlen) { len = tlen; } + if (buffered) { + buff_min_len = lcm * 2; + if (switch_buffer_inuse(ep->buffer) < buff_min_len) { + continue; + } + } else { + buff_min_len = len; + } + if (ep->buffer) { switch_buffer_lock(ep->buffer); - while (switch_buffer_inuse(ep->buffer) >= len) { + while (switch_buffer_inuse(ep->buffer) >= buff_min_len) { int tchanged = 0, changed = 0; write_frame.datalen = (uint32_t) switch_buffer_read(ep->buffer, buf, len); @@ -2592,7 +2641,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session tchanged = 1; } - if (read_impl.number_of_channels != ep->tread_impl.number_of_channels || + if (read_impl.number_of_channels != ep->read_impl.number_of_channels || read_impl.actual_samples_per_second != ep->read_impl.actual_samples_per_second) { changed = 1; } @@ -2606,6 +2655,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session ep->read_impl.number_of_channels, read_impl.actual_samples_per_second, read_impl.number_of_channels); + + tlen = read_impl.decoded_bytes_per_packet * channels; + + if (len > tlen) { + len = tlen; + } + } if (tchanged) { @@ -2615,28 +2671,44 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session ep->tread_impl.number_of_channels, tread_impl.actual_samples_per_second, tread_impl.number_of_channels); + } + - tlen = tread_impl.decoded_bytes_per_packet * channels; - - if (len > tlen) { - len = tlen; - } - - switch_core_codec_destroy(&codec); + switch_core_codec_destroy(&codec); + if (tread_impl.decoded_bytes_per_packet < read_impl.decoded_bytes_per_packet) { if (switch_core_codec_init(&codec, "L16", NULL, NULL, - tread_impl.actual_samples_per_second, - tread_impl.microseconds_per_packet / 1000, - tread_impl.number_of_channels, + read_impl.actual_samples_per_second, + read_impl.microseconds_per_packet / 1000, + read_impl.number_of_channels, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); + switch_buffer_unlock(ep->buffer); + goto end; + } + buffered = 1; + lcm = switch_lcm(tread_impl.decoded_bytes_per_packet, read_impl.decoded_bytes_per_packet); + } else { + if (switch_core_codec_init(&codec, + "L16", + NULL, + NULL, + tread_impl.actual_samples_per_second, + tread_impl.microseconds_per_packet / 1000, + tread_impl.number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot init codec\n"); switch_buffer_unlock(ep->buffer); goto end; } + if (buffered == 1) { + buffered = 0; + } } ep->read_impl = read_impl; @@ -2658,11 +2730,14 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_eavesdrop_session(switch_core_session switch_buffer_unlock(ep->buffer); switch_buffer_lock(ep->buffer); - } + if (ep->tread_impl.decoded_bytes_per_packet == ep->read_impl.decoded_bytes_per_packet) { + /* push just the number of samples worth of a packet. */ + break; + } + } switch_buffer_unlock(ep->buffer); } - } end_loop: diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 6e7e128d62..2511a143c2 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -7,8 +7,13 @@ noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_core_asr AM_LDFLAGS += -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS) $(openssl_LIBS) AM_LDFLAGS += $(FREESWITCH_LIBS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) +# "make check" will not run these. +examples = switch_eavesdrop + if HAVE_FVAD AM_CFLAGS += -DSWITCH_HAVE_FVAD endif TESTS = $(noinst_PROGRAMS) + +bin_PROGRAMS = $(examples) diff --git a/tests/unit/conf_eavesdrop/freeswitch.xml b/tests/unit/conf_eavesdrop/freeswitch.xml new file mode 100644 index 0000000000..4a65caf6d8 --- /dev/null +++ b/tests/unit/conf_eavesdrop/freeswitch.xml @@ -0,0 +1,238 @@ +<?xml version="1.0"?> +<document type="freeswitch/xml"> + <X-PRE-PROCESS cmd="exec-set" data="test=echo 1234"/> + <X-PRE-PROCESS cmd="set" data="default_password=$${test}"/> + <X-PRE-PROCESS cmd="set" data="core_video_blank_image=$${conf_dir}/freeswitch-logo.png"/> + <section name="configuration" description="Various Configuration"> + <configuration name="modules.conf" description="Modules"> + <modules> + <load module="mod_sofia"/> + <load module="mod_console"/> + <load module="mod_loopback"/> + <load module="mod_commands"/> + <load module="mod_dptools"/> + <load module="mod_dialplan_xml"/> + <load module="mod_tone_stream"/> + <load module="mod_commands"/> + <load module="mod_sndfile"/> + </modules> + </configuration> + + <configuration name="console.conf" description="Console Logger"> + <mappings> + <map name="all" value="console,debug,info,notice,warning,err,crit,alert"/> + </mappings> + <settings> + <param name="colorize" value="true"/> + <param name="loglevel" value="debug"/> + </settings> + </configuration> + + <configuration name="timezones.conf" description="Timezones"> + <timezones> + <zone name="GMT" value="GMT0" /> + </timezones> + </configuration> + + <configuration name="sofia.conf" description="SofiaSIP"> + <profiles> + <profile name="external"> + <gateways> + + <gateway name="eavestest"> + <param name="username" value="not-used"/> + <param name="password" value="not-used"/> + <param name="proxy" value="$${local_ip_v4}:61068"/> + <param name="register" value="false"/> + <param name="retry-seconds" value="30"/> + <param name="dtmf-type" value="rfc2833"/> + <variables> + <variable name="rtp_secure_media" value="false" direction="outbound"/> + </variables> + </gateway> + </gateways> + + <domains> + <domain name="all" alias="false" parse="true"/> + </domains> + + <settings> + <param name="debug" value="1"/> + <param name="shutdown-on-fail" value="true"/> + <param name="p-asserted-id-parse" value="verbatim"/> + <param name="username" value="SignalWire-STACK"/> + <param name="user-agent-string" value="SignalWire STACK Unit Test"/> + <param name="sip-trace" value="yes"/> + <param name="sip-capture" value="no"/> + <param name="rfc2833-pt" value="101"/> + <param name="sip-port" value="61068"/> + <param name="dialplan" value="XML"/> + <param name="context" value="default"/> + <param name="dtmf-duration" value="2000"/> + <param name="inbound-codec-prefs" value="PCMU"/> + <param name="outbound-codec-prefs" value="PCMU"/> + <param name="rtp-timer-name" value="soft"/> + <param name="local-network-acl" value="localnet.auto"/> + <param name="manage-presence" value="false"/> + <param name="inbound-codec-negotiation" value="generous"/> + <param name="nonce-ttl" value="60"/> + <param name="inbound-late-negotiation" value="true"/> + <param name="inbound-zrtp-passthru" value="false"/> + <param name="rtp-ip" value="$${local_ip_v4}"/> + <param name="sip-ip" value="$${local_ip_v4}"/> + <param name="ext-rtp-ip" value="$${local_ip_v4}"/> + <param name="ext-sip-ip" value="$${local_ip_v4}"/> + <param name="rtp-timeout-sec" value="300"/> + <param name="rtp-hold-timeout-sec" value="1800"/> + <param name="session-timeout" value="600"/> + <param name="minimum-session-expires" value="90"/> + <param name="tls" value="false"/> + </settings> + </profile> + + <profile name="internal"> + <gateways> + </gateways> + + <domains> + <domain name="all" alias="false" parse="true"/> + </domains> + + <settings> + <param name="debug" value="1"/> + <param name="shutdown-on-fail" value="true"/> + <param name="p-asserted-id-parse" value="verbatim"/> + <param name="username" value="SignalWire-STACK"/> + <param name="user-agent-string" value="SignalWire STACK Unit Test"/> + <param name="sip-trace" value="yes"/> + <param name="sip-capture" value="no"/> + <param name="rfc2833-pt" value="101"/> + <param name="sip-port" value="61069"/> + <param name="dialplan" value="XML"/> + <param name="context" value="default"/> + <param name="dtmf-duration" value="2000"/> + <param name="inbound-codec-prefs" value="PCMU"/> + <param name="outbound-codec-prefs" value="PCMU"/> + <param name="rtp-timer-name" value="soft"/> + <param name="local-network-acl" value="localnet.auto"/> + <param name="manage-presence" value="false"/> + <param name="inbound-codec-negotiation" value="generous"/> + <param name="nonce-ttl" value="60"/> + <param name="inbound-late-negotiation" value="true"/> + <param name="inbound-zrtp-passthru" value="false"/> + <param name="rtp-ip" value="$${local_ip_v4}"/> + <param name="sip-ip" value="$${local_ip_v4}"/> + <param name="ext-rtp-ip" value="$${local_ip_v4}"/> + <param name="ext-sip-ip" value="$${local_ip_v4}"/> + <param name="rtp-timeout-sec" value="300"/> + <param name="rtp-hold-timeout-sec" value="1800"/> + <param name="session-timeout" value="600"/> + <param name="minimum-session-expires" value="90"/> + <param name="tls" value="false"/> + </settings> + </profile> + + </profiles> + </configuration> + + <configuration name="switch.conf" description="Switch"> + <param name="rtp-start-port" value="20000"/> + <param name="rtp-end-port" value="30000"/> + <param name="threaded-system-exec" value="true"/> + </configuration> + </section> + + <section name="dialplan" description="Regex/XML Dialplan"> + <context name="default"> + <extension name="one"> + <condition field="destination_number" expression="^\+15553332220$"> + <action application="set" data="absolute_codec_string=PCMU@20i"/> + <action application="info"/> + <action application="answer"/> + <action application="park""/> + </condition> + </extension> + <extension name="two"> + <condition field="destination_number" expression="^\+15553332221$"> + <action application="set" data="absolute_codec_string=PCMU@10i"/> + <action application="info"/> + <action application="answer"/> + <action application="playback" data="{loops=-1}tone_stream://%(251,0,300)"/> + </condition> + </extension> + <extension name="three"> + <condition field="destination_number" expression="^\+15553332222$"> + <action application="set" data="absolute_codec_string=PCMU@30i"/> + <action application="info"/> + <action application="answer"/> + <action application="playback" data="{loops=-1}tone_stream://%(251,0,300)"/> + </condition> + </extension> + <extension name="four"> + <condition field="destination_number" expression="^\+15553332223$"> + <action application="set" data="absolute_codec_string=PCMU@40i"/> + <action application="info"/> + <action application="answer"/> + <action application="playback" data="{loops=-1}tone_stream://%(251,0,300)"/> + </condition> + </extension> + <extension name="five"> + <condition field="destination_number" expression="^\+15553332224$"> + <action application="set" data="absolute_codec_string=PCMU@80i"/> + <action application="info"/> + <action application="answer"/> + <action application="playback" data="{loops=-1}tone_stream://%(251,0,300)"/> + </condition> + </extension> + <extension name="six"> + <condition field="destination_number" expression="^\+15553332225$"> + <action application="set" data="absolute_codec_string=PCMU@10i"/> + <action application="info"/> + <action application="answer"/> + <action application="playback" data="{loops=-1}tone_stream://%(251,0,300)"/> + </condition> + </extension> + <extension name="seven"> + <condition field="destination_number" expression="^\+15553332226$"> + <action application="set" data="absolute_codec_string=PCMU@20i"/> + <action application="info"/> + <action application="answer"/> + <action application="record_session" data="/tmp/eaves-${uuid}.wav"/> + <action application="playback" data="{loops=-1}tone_stream://%(251,0,300)"/> + </condition> + </extension> + <extension name="eavesdropper_20ms"> + <condition field="destination_number" expression="^\+15553332230$"> + <action application="answer"/> + <action application="record_session" data="/tmp/eaves-${sip_h_X-UnitTestRecfile}.wav"/> + <action application="playback" data="silence_stream://-1,1400"/> + </condition> + </extension> + <extension name="eight"> + <condition field="destination_number" expression="^\+15553332231$"> + <action application="answer"/> + <action application="park"/> + </condition> + </extension> + + <extension name="eavesdropper_30ms"> + <condition field="destination_number" expression="^\+15553332240$"> + <action application="set" data="absolute_codec_string=PCMU@30i"/> + <action application="answer"/> + <action application="record_session" data="/tmp/eaves-${sip_h_X-UnitTestRecfile}.wav"/> + <action application="playback" data="silence_stream://-1,1400"/> + </condition> + </extension> + + <extension name="ten"> + <condition field="destination_number" expression="^\+15553332241$"> + <action application="set" data="absolute_codec_string=PCMU@30i"/> + <action application="info"/> + <action application="answer"/> + <action application="playback" data="{loops=-1}tone_stream://%(251,0,300)"/> + </condition> + </extension> + + </context> + </section> +</document> diff --git a/tests/unit/conf_eavesdrop/gw/eavestest.xml b/tests/unit/conf_eavesdrop/gw/eavestest.xml new file mode 100644 index 0000000000..a2f268424e --- /dev/null +++ b/tests/unit/conf_eavesdrop/gw/eavestest.xml @@ -0,0 +1,14 @@ +<include/> + <gateway name="eavestest"> + <param name="username" value="not-used"/> + <param name="password" value="not-used"/> + <param name="proxy" value="127.0.0.1"/> + <param name="register" value="false"/> + <param name="retry-seconds" value="30"/> + <param name="dtmf-type" value="rfc2833"/> + <variables> + <variable name="rtp_secure_media" value="false" direction="outbound"/> + </variables> + </gateway> +</include> + diff --git a/tests/unit/switch_eavesdrop.c b/tests/unit/switch_eavesdrop.c new file mode 100644 index 0000000000..867ad7a6bc --- /dev/null +++ b/tests/unit/switch_eavesdrop.c @@ -0,0 +1,392 @@ +#include <strings.h> +#include <switch.h> +#include <test/switch_test.h> + +static switch_memory_pool_t *pool = NULL; + +static switch_status_t test_detect_long_tone_in_file(const char *filepath, int rate, int freq, int ptime) { + teletone_multi_tone_t mt; + teletone_tone_map_t map; + int16_t data[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; + size_t len = (rate * ptime / 1000) /*packet len in samples */ * 8; /*length of chunk that must contain tone*/ + size_t fin = 0; + switch_status_t status; + switch_file_handle_t fh = { 0 }; + uint8_t fail = 0, gaps = 0, audio = 0; + uint32_t pos = 0; + size_t full_len = 0; + + status = switch_core_file_open(&fh, filepath, 1, rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL); + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot open file [%s]\n", filepath); + return SWITCH_STATUS_FALSE; + } + + mt.sample_rate = rate; + map.freqs[0] = (teletone_process_t)freq; + + teletone_multi_tone_init(&mt, &map); + + len = (rate * 2 / 100) /*packet len in samples */ * 8; + + while (switch_core_file_read(&fh, &data, &len) == SWITCH_STATUS_SUCCESS) { + fin += len; + /*skip silence at the beginning of the file, 1 second max. */ + if (!teletone_multi_tone_detect(&mt, data, len)) { + if ((fin > rate && !audio) || gaps > 30) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many gaps in audio or no tone detected 1st second. [%u][%d]\n", fin, gaps); + fail = 1; + break; + } + gaps++; + continue; + } else { + audio++; + } + } + + switch_core_file_close(&fh); + + if (fail) { + return SWITCH_STATUS_FALSE; + } + return SWITCH_STATUS_SUCCESS; +} + +FST_CORE_BEGIN("./conf_eavesdrop") + +{ +FST_SUITE_BEGIN(switch_eavesdrop) +{ + FST_SETUP_BEGIN() + { + fst_requires_module("mod_loopback"); + fst_requires_module("mod_sofia"); + switch_core_set_variable("link_ip", switch_core_get_variable("local_ip_v4")); + } + FST_SETUP_END() + + FST_TEARDOWN_BEGIN() + { + } + FST_TEARDOWN_END() + + FST_TEST_BEGIN(test_eavesdrop_bridged_same_ptime_20ms) + { + switch_core_session_t *session1 = NULL; + switch_core_session_t *session2 = NULL; + switch_core_session_t *session3 = NULL; + + switch_channel_t *channel1 = NULL; + switch_channel_t *channel2 = NULL; + switch_channel_t *channel3 = NULL; + + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + char eavesdrop_command[256] = { 0 }; + char rec_path[256]; + char rec_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char eaves_dialstr[256] = { 0 }; + + switch_uuid_str(rec_uuid, sizeof(rec_uuid)); + + /*parked 20 ms ptime */ + status = switch_ivr_originate(NULL, &session1, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332220", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session1); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel1 = switch_core_session_get_channel(session1); + fst_requires(channel1); + + snprintf(eaves_dialstr, sizeof(eaves_dialstr), "{ignore_early_media=true}{sip_h_X-UnitTestRecfile=%s}sofia/gateway/eavestest/+15553332230", rec_uuid); + + /*eavesdropper 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session2, &cause, eaves_dialstr, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session2); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel2 = switch_core_session_get_channel(session2); + fst_requires(channel2); + + /*milliwatt tone 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session3, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332226", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session3); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel3 = switch_core_session_get_channel(session3); + fst_requires(channel3); + + SWITCH_STANDARD_STREAM(stream); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_bridge %s %s", switch_core_session_get_uuid(session1), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session1, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command),"uuid_setvar_multi %s eavesdrop_enable_dtmf=false;eavesdrop_whisper_bleg=true;eavesdrop_whisper_aleg=false", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_transfer %s 'eavesdrop:%s' inline", switch_core_session_get_uuid(session3), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + switch_safe_free(stream.data); + + sleep(5); // it will record ~ 5 secs + + snprintf(rec_path, sizeof(rec_path), "/tmp/eaves-%s.wav", rec_uuid); + + fst_requires(switch_file_exists(rec_path, fst_pool) == SWITCH_STATUS_SUCCESS); + + fst_requires(test_detect_long_tone_in_file(rec_path, 8000, 300, 20) == SWITCH_STATUS_SUCCESS); + + unlink(rec_path); + + switch_channel_hangup(channel1, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel3, SWITCH_CAUSE_NORMAL_CLEARING); + + switch_core_session_rwunlock(session1); + switch_core_session_rwunlock(session2); + switch_core_session_rwunlock(session3); + + } + FST_TEST_END() + + FST_TEST_BEGIN(test_eavesdrop_bridged_ptime_mismatch_20ms_30ms) + { + switch_core_session_t *session1 = NULL; + switch_core_session_t *session2 = NULL; + switch_core_session_t *session3 = NULL; + + switch_channel_t *channel1 = NULL; + switch_channel_t *channel2 = NULL; + switch_channel_t *channel3 = NULL; + + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + char eavesdrop_command[256] = { 0 }; + char rec_path[256]; + char rec_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char eaves_dialstr[256] = { 0 }; + + switch_uuid_str(rec_uuid, sizeof(rec_uuid)); + + /*parked 20 ms ptime */ + status = switch_ivr_originate(NULL, &session1, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332220", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session1); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel1 = switch_core_session_get_channel(session1); + fst_requires(channel1); + + snprintf(eaves_dialstr, sizeof(eaves_dialstr), "{ignore_early_media=true}{sip_h_X-UnitTestRecfile=%s}sofia/gateway/eavestest/+15553332230", rec_uuid); + + /*eavesdropper 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session2, &cause, eaves_dialstr, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session2); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel2 = switch_core_session_get_channel(session2); + fst_requires(channel2); + + /*milliwatt tone 30 ms ptime*/ + status = switch_ivr_originate(NULL, &session3, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332222", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session3); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel3 = switch_core_session_get_channel(session3); + fst_requires(channel3); + + SWITCH_STANDARD_STREAM(stream); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_bridge %s %s", switch_core_session_get_uuid(session1), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session1, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command),"uuid_setvar_multi %s eavesdrop_enable_dtmf=false;eavesdrop_whisper_bleg=true;eavesdrop_whisper_aleg=false", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_transfer %s 'eavesdrop:%s' inline", switch_core_session_get_uuid(session3), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + switch_safe_free(stream.data); + + sleep(5); // it will record ~ 5 secs + + snprintf(rec_path, sizeof(rec_path), "/tmp/eaves-%s.wav", rec_uuid); + + fst_requires(switch_file_exists(rec_path, fst_pool) == SWITCH_STATUS_SUCCESS); + + fst_requires(test_detect_long_tone_in_file(rec_path, 8000, 300, 20) == SWITCH_STATUS_SUCCESS); + + unlink(rec_path); + + switch_channel_hangup(channel1, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel3, SWITCH_CAUSE_NORMAL_CLEARING); + + switch_core_session_rwunlock(session1); + switch_core_session_rwunlock(session2); + switch_core_session_rwunlock(session3); + + } + FST_TEST_END() + + FST_TEST_BEGIN(test_eavesdrop_bridged_ptime_mismatch_30ms_20ms) + { + switch_core_session_t *session1 = NULL; + switch_core_session_t *session2 = NULL; + switch_core_session_t *session3 = NULL; + + switch_channel_t *channel1 = NULL; + switch_channel_t *channel2 = NULL; + switch_channel_t *channel3 = NULL; + + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + char eavesdrop_command[256] = { 0 }; + char rec_path[256]; + char rec_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char eaves_dialstr[256] = { 0 }; + + switch_uuid_str(rec_uuid, sizeof(rec_uuid)); + + /*parked 30 ms ptime */ + status = switch_ivr_originate(NULL, &session1, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332231", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session1); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel1 = switch_core_session_get_channel(session1); + fst_requires(channel1); + + snprintf(eaves_dialstr, sizeof(eaves_dialstr), "{ignore_early_media=true}{sip_h_X-UnitTestRecfile=%s}sofia/gateway/eavestest/+15553332240", rec_uuid); + + /*eavesdropper 30 ms ptime*/ + status = switch_ivr_originate(NULL, &session2, &cause, eaves_dialstr, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session2); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel2 = switch_core_session_get_channel(session2); + fst_requires(channel2); + + /*milliwatt tone 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session3, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332226", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session3); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel3 = switch_core_session_get_channel(session3); + fst_requires(channel3); + + SWITCH_STANDARD_STREAM(stream); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_bridge %s %s", switch_core_session_get_uuid(session1), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session1, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command),"uuid_setvar_multi %s eavesdrop_enable_dtmf=false;eavesdrop_whisper_bleg=true;eavesdrop_whisper_aleg=false", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_transfer %s 'eavesdrop:%s' inline", switch_core_session_get_uuid(session3), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + switch_safe_free(stream.data); + + sleep(5); // it will record ~ 5 secs + + snprintf(rec_path, sizeof(rec_path), "/tmp/eaves-%s.wav", rec_uuid); + + fst_requires(switch_file_exists(rec_path, fst_pool) == SWITCH_STATUS_SUCCESS); + + fst_requires(test_detect_long_tone_in_file(rec_path, 8000, 300, 30) == SWITCH_STATUS_SUCCESS); + + unlink(rec_path); + + switch_channel_hangup(channel1, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel3, SWITCH_CAUSE_NORMAL_CLEARING); + + switch_core_session_rwunlock(session1); + switch_core_session_rwunlock(session2); + switch_core_session_rwunlock(session3); + + } + FST_TEST_END() + + FST_TEST_BEGIN(test_eavesdrop_bridged_ptime_mismatch_reneg) + { + switch_core_session_t *session1 = NULL; + switch_core_session_t *session2 = NULL; + switch_core_session_t *session3 = NULL; + + switch_channel_t *channel1 = NULL; + switch_channel_t *channel2 = NULL; + switch_channel_t *channel3 = NULL; + + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + char eavesdrop_command[256] = { 0 }; + char rec_path[256]; + char rec_uuid[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 }; + char eaves_dialstr[256] = { 0 }; + + switch_uuid_str(rec_uuid, sizeof(rec_uuid)); + + /*parked 30 ms ptime */ + status = switch_ivr_originate(NULL, &session1, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332231", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session1); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel1 = switch_core_session_get_channel(session1); + fst_requires(channel1); + + snprintf(eaves_dialstr, sizeof(eaves_dialstr), "{ignore_early_media=true}{sip_h_X-UnitTestRecfile=%s}sofia/gateway/eavestest/+15553332240", rec_uuid); + + /*eavesdropper 30 ms ptime*/ + status = switch_ivr_originate(NULL, &session2, &cause, eaves_dialstr, 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session2); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel2 = switch_core_session_get_channel(session2); + fst_requires(channel2); + + /*milliwatt tone 20 ms ptime*/ + status = switch_ivr_originate(NULL, &session3, &cause, "{ignore_early_media=true}sofia/gateway/eavestest/+15553332226", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session3); + fst_check(status == SWITCH_STATUS_SUCCESS); + channel3 = switch_core_session_get_channel(session3); + fst_requires(channel3); + + SWITCH_STANDARD_STREAM(stream); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_bridge %s %s", switch_core_session_get_uuid(session1), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session1, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command),"uuid_setvar_multi %s eavesdrop_enable_dtmf=false;eavesdrop_whisper_bleg=true;eavesdrop_whisper_aleg=false", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_transfer %s 'eavesdrop:%s' inline", switch_core_session_get_uuid(session3), switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + + sleep(2); + + // codec reneg for eavesdropper + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_media_reneg %s = PCMU@20i", switch_core_session_get_uuid(session2)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + + sleep(1); + + // codec reneg for eavesdroppee + memset(eavesdrop_command, 0, sizeof(eavesdrop_command)); + switch_snprintf(eavesdrop_command, sizeof(eavesdrop_command), "uuid_media_reneg %s = PCMU@30i", switch_core_session_get_uuid(session3)); + switch_api_execute("bgapi", eavesdrop_command, session3, &stream); + switch_safe_free(stream.data); + + sleep(2); + + snprintf(rec_path, sizeof(rec_path), "/tmp/eaves-%s.wav", rec_uuid); + + fst_requires(switch_file_exists(rec_path, fst_pool) == SWITCH_STATUS_SUCCESS); + + fst_requires(test_detect_long_tone_in_file(rec_path, 8000, 300, 30) == SWITCH_STATUS_SUCCESS); + + unlink(rec_path); + + switch_channel_hangup(channel1, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel2, SWITCH_CAUSE_NORMAL_CLEARING); + switch_channel_hangup(channel3, SWITCH_CAUSE_NORMAL_CLEARING); + + switch_core_session_rwunlock(session1); + switch_core_session_rwunlock(session2); + switch_core_session_rwunlock(session3); + + } + FST_TEST_END() + +} +FST_SUITE_END() +} +FST_CORE_END() +