summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorroot <[email protected]>2024-09-03 07:01:58 +0000
committerroot <[email protected]>2024-09-03 07:01:58 +0000
commit6f1ac6b36b28d082cebf8e4c3eeedd592c1946f9 (patch)
tree5c664bc282e5c01b634430531e43dae44dc50538
parenta8206cffc0ba55c6cb2b0b1054860ee28ec4a0b8 (diff)
add socks_decoder, stratum_decoder and session_flags
-rw-r--r--decoders/CMakeLists.txt3
-rw-r--r--decoders/session_flags/CMakeLists.txt15
-rw-r--r--decoders/session_flags/fet.cpp123
-rw-r--r--decoders/session_flags/fet.h7
-rw-r--r--decoders/session_flags/mesa_sts/CMakeLists.txt15
-rw-r--r--decoders/session_flags/mesa_sts/include/mesa_sts.h57
-rw-r--r--decoders/session_flags/mesa_sts/src/approximateEntropy.c72
-rw-r--r--decoders/session_flags/mesa_sts/src/binaryDerivate.c30
-rw-r--r--decoders/session_flags/mesa_sts/src/blockFrequency.c36
-rw-r--r--decoders/session_flags/mesa_sts/src/bytesToBitSequence.c31
-rw-r--r--decoders/session_flags/mesa_sts/src/cephes.c348
-rw-r--r--decoders/session_flags/mesa_sts/src/cusum.c95
-rw-r--r--decoders/session_flags/mesa_sts/src/dfft.c1462
-rw-r--r--decoders/session_flags/mesa_sts/src/discreteFourierTransform.c60
-rw-r--r--decoders/session_flags/mesa_sts/src/frequency.c38
-rw-r--r--decoders/session_flags/mesa_sts/src/genutils.c667
-rw-r--r--decoders/session_flags/mesa_sts/src/include/cephes.h13
-rw-r--r--decoders/session_flags/mesa_sts/src/include/config.h52
-rw-r--r--decoders/session_flags/mesa_sts/src/include/decls.h24
-rw-r--r--decoders/session_flags/mesa_sts/src/include/defs.h73
-rw-r--r--decoders/session_flags/mesa_sts/src/include/externs.h21
-rw-r--r--decoders/session_flags/mesa_sts/src/include/generators.h78
-rw-r--r--decoders/session_flags/mesa_sts/src/include/genutils.h47
-rw-r--r--decoders/session_flags/mesa_sts/src/include/matrix.h18
-rw-r--r--decoders/session_flags/mesa_sts/src/include/stat_fncs.h166
-rw-r--r--decoders/session_flags/mesa_sts/src/include/utilities.h4
-rw-r--r--decoders/session_flags/mesa_sts/src/linearComplexity.c135
-rw-r--r--decoders/session_flags/mesa_sts/src/longestRunOfOnes.c125
-rw-r--r--decoders/session_flags/mesa_sts/src/matrix.c170
-rw-r--r--decoders/session_flags/mesa_sts/src/mesa_sts.c114
-rw-r--r--decoders/session_flags/mesa_sts/src/nonOverlappingTemplateMatchings.c281
-rw-r--r--decoders/session_flags/mesa_sts/src/overlappingTemplateMatchings.c97
-rw-r--r--decoders/session_flags/mesa_sts/src/pokerDetect.c79
-rw-r--r--decoders/session_flags/mesa_sts/src/randomExcursions.c116
-rw-r--r--decoders/session_flags/mesa_sts/src/randomExcursionsVariant.c61
-rw-r--r--decoders/session_flags/mesa_sts/src/rank.c76
-rw-r--r--decoders/session_flags/mesa_sts/src/runs.c46
-rw-r--r--decoders/session_flags/mesa_sts/src/runsDistribution.c82
-rw-r--r--decoders/session_flags/mesa_sts/src/selfCorrelation.c25
-rw-r--r--decoders/session_flags/mesa_sts/src/serial.c65
-rw-r--r--decoders/session_flags/mesa_sts/src/universal.c88
-rw-r--r--decoders/session_flags/mesa_sts/src/utilities.c45
-rw-r--r--decoders/session_flags/mesa_sts/src/version.map6
-rw-r--r--decoders/session_flags/mesa_sts/test/CMakeLists.txt6
-rw-r--r--decoders/session_flags/mesa_sts/test/gtest_mesa_sts.cpp256
-rw-r--r--decoders/session_flags/mesa_sts/test/pcap/202202161604_win_wifi_30M_pure_wechat_wechat3.5.0.46_voice-call_120s_2_multinat.pcapbin0 -> 4634693 bytes
-rw-r--r--decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_1.pcapbin0 -> 20193 bytes
-rw-r--r--decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_2_dd.pcapbin0 -> 22352 bytes
-rw-r--r--decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_3_ee.pcapbin0 -> 109341 bytes
-rw-r--r--decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_1.pcapbin0 -> 109689 bytes
-rw-r--r--decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_2_dd.pcapbin0 -> 104057 bytes
-rw-r--r--decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_3_ee.pcapbin0 -> 170134 bytes
-rw-r--r--decoders/session_flags/mesa_sts/test/pcap/xingongsuo_kouling_http_C2S.pcapbin0 -> 4254494 bytes
-rw-r--r--decoders/session_flags/onlinemean.c37
-rw-r--r--decoders/session_flags/onlinemean.h30
-rw-r--r--decoders/session_flags/session_flags.cpp824
-rw-r--r--decoders/session_flags/session_flags_internal.h153
-rw-r--r--decoders/session_flags/session_flags_plugin.cpp404
-rw-r--r--decoders/session_flags/tunneling.cpp217
-rw-r--r--decoders/session_flags/tunneling.h18
-rw-r--r--decoders/socks/CMakeLists.txt13
-rw-r--r--decoders/socks/socks_decoder.cpp527
-rw-r--r--decoders/socks/socks_decoder_internal.h56
-rw-r--r--decoders/stratum/CMakeLists.txt14
-rw-r--r--decoders/stratum/stratum_decoder.cpp311
-rw-r--r--include/stellar/session_flags.h55
-rw-r--r--include/stellar/socks_decoder.h44
-rw-r--r--include/stellar/stratum_decoder.h18
-rw-r--r--test/CMakeLists.txt3
-rw-r--r--test/decoders/session_flags/CMakeLists.txt56
-rw-r--r--test/decoders/session_flags/dummy.c167
-rw-r--r--test/decoders/session_flags/dummy.h18
-rw-r--r--test/decoders/session_flags/pcap/1-dtls.192.168.44.32.pcapbin0 -> 6889 bytes
-rw-r--r--test/decoders/session_flags/pcap/192.168.56.19.56554.json20
-rw-r--r--test/decoders/session_flags/pcap/192.168.56.19.56555-47.93.59.84.443_869284834001082715.pcapngbin0 -> 624232 bytes
-rw-r--r--test/decoders/session_flags/pcap/192.168.56.31.58530.json17
-rw-r--r--test/decoders/session_flags/pcap/192.168.56.31.58530.pcapngbin0 -> 30876 bytes
-rw-r--r--test/decoders/session_flags/pcap/192.168.64.61.62275.asymmetric.pcapngbin0 -> 3880 bytes
-rw-r--r--test/decoders/session_flags/pcap/192.168.64.61.62275.json14
-rw-r--r--test/decoders/session_flags/pcap/192.168.64.67.53813.json14
-rw-r--r--test/decoders/session_flags/pcap/192.168.64.67.53813.pcapngbin0 -> 11924 bytes
-rw-r--r--test/decoders/session_flags/pcap/1_result.json11
-rw-r--r--test/decoders/session_flags/pcap/bilibili-dtls.pcapbin0 -> 7890746 bytes
-rw-r--r--test/decoders/session_flags/pcap/bilibili.json23
-rw-r--r--test/decoders/session_flags/pcap/dns_asymmetric.json14
-rw-r--r--test/decoders/session_flags/pcap/dns_asymmetric.pcapbin0 -> 1285 bytes
-rw-r--r--test/decoders/session_flags/pcap/douyin.json20
-rw-r--r--test/decoders/session_flags/pcap/douyin.pcapbin0 -> 14679479 bytes
-rw-r--r--test/decoders/session_flags/pcap/ftp-data.pcapbin0 -> 94339386 bytes
-rw-r--r--test/decoders/session_flags/pcap/https_download.json26
-rw-r--r--test/decoders/session_flags/pcap/https_download.pcapbin0 -> 46783462 bytes
-rw-r--r--test/decoders/session_flags/pcap/mix-quic-ssl-quic-mail.pcapbin0 -> 100712305 bytes
-rw-r--r--test/decoders/session_flags/pcap/mix.json65
-rw-r--r--test/decoders/session_flags/pcap/quic_asymmetric.json17
-rw-r--r--test/decoders/session_flags/pcap/quic_asymmetric.pcapngbin0 -> 31324 bytes
-rw-r--r--test/decoders/session_flags/pcap/quic_asymmetric_1.json14
-rw-r--r--test/decoders/session_flags/pcap/quic_asymmetric_1.pcapbin0 -> 28128290 bytes
-rw-r--r--test/decoders/session_flags/pcap/quic_online_streaming.json26
-rw-r--r--test/decoders/session_flags/pcap/quic_online_streaming.pcapbin0 -> 31717195 bytes
-rw-r--r--test/decoders/session_flags/pcap/ssh-interactive.json29
-rw-r--r--test/decoders/session_flags/pcap/ssh-interactive.pcapbin0 -> 40259 bytes
-rw-r--r--test/decoders/session_flags/pcap/telegram_mtproto_ipv4_key_1.pcapbin0 -> 20193 bytes
-rw-r--r--test/decoders/session_flags/pcap/telegram_mtproto_ipv6_key_1.pcapbin0 -> 109689 bytes
-rw-r--r--test/decoders/session_flags/pcap/tencent_meeting_video.json23
-rw-r--r--test/decoders/session_flags/pcap/tencent_meeting_video.pcapngbin0 -> 9304196 bytes
-rw-r--r--test/decoders/session_flags/pcap/tencent_meeting_voice.json20
-rw-r--r--test/decoders/session_flags/pcap/tencent_meeting_voice.pcapngbin0 -> 656916 bytes
-rw-r--r--test/decoders/session_flags/pcap/tls1.2.pcapngbin0 -> 7784 bytes
-rw-r--r--test/decoders/session_flags/pcap/tls1.2_no_reuse.pcapbin0 -> 30340 bytes
-rw-r--r--test/decoders/session_flags/pcap/tls_over_tls_1.2_no_reuse.pcapbin0 -> 35421 bytes
-rw-r--r--test/decoders/session_flags/pcap/unidirectional.json14
-rw-r--r--test/decoders/session_flags/pcap/unidirectional.pcapbin0 -> 8128 bytes
-rw-r--r--test/decoders/session_flags/pcap/wechat_voice_call.json20
-rw-r--r--test/decoders/session_flags/pcap/wechat_voice_call.pcapbin0 -> 13484391 bytes
-rw-r--r--test/decoders/session_flags/pcap/youtube-quic.pcapbin0 -> 21495595 bytes
-rw-r--r--test/decoders/session_flags/pcap/youtube.json20
-rw-r--r--test/decoders/session_flags/plugin_test_main.cpp135
-rw-r--r--test/decoders/session_flags/session_flags_pcap_test.cpp630
-rw-r--r--test/decoders/session_flags/session_flags_static_test.cpp494
-rw-r--r--test/decoders/session_flags/session_flags_test_plugin.cpp210
-rw-r--r--test/decoders/session_flags/test_based_on_stellar/CMakeLists.txt37
-rw-r--r--test/decoders/session_flags/test_based_on_stellar/env/log.toml4
-rw-r--r--test/decoders/session_flags/test_based_on_stellar/env/session_flags.toml9
-rw-r--r--test/decoders/session_flags/test_based_on_stellar/env/spec.toml9
-rw-r--r--test/decoders/session_flags/test_based_on_stellar/env/stellar.toml64
-rw-r--r--test/decoders/socks/CMakeLists.txt38
-rw-r--r--test/decoders/socks/dummy.c151
-rw-r--r--test/decoders/socks/dummy.h14
-rw-r--r--test/decoders/socks/pcap/result.json7
-rw-r--r--test/decoders/socks/pcap/socks4.pcapbin0 -> 2946 bytes
-rw-r--r--test/decoders/socks/pcap/socks4_nest_socks4a.pcapbin0 -> 4668 bytes
-rw-r--r--test/decoders/socks/pcap/socks4a_domain.pcapbin0 -> 3044 bytes
-rw-r--r--test/decoders/socks/pcap/socks5_auth_failed.pcapbin0 -> 1220 bytes
-rw-r--r--test/decoders/socks/pcap/socks5_auth_success.pcapbin0 -> 3469 bytes
-rw-r--r--test/decoders/socks/pcap/socks5_no_auth.pcapbin0 -> 3201 bytes
-rw-r--r--test/decoders/socks/plugin_test_main.cpp135
-rw-r--r--test/decoders/socks/socks_decoder_pcap_gtest.cpp246
-rw-r--r--test/decoders/socks/socks_decoder_test_plugin.cpp88
-rw-r--r--test/decoders/socks/test_based_on_stellar/CMakeLists.txt37
-rw-r--r--test/decoders/socks/test_based_on_stellar/env/log.toml4
-rw-r--r--test/decoders/socks/test_based_on_stellar/env/spec.toml9
-rw-r--r--test/decoders/socks/test_based_on_stellar/env/stellar.toml64
-rw-r--r--test/decoders/stratum/CMakeLists.txt41
-rw-r--r--test/decoders/stratum/dummy.c86
-rw-r--r--test/decoders/stratum/pcap/01-bch_f2pool_cpuminer_1.pcapngbin0 -> 4028 bytes
-rw-r--r--test/decoders/stratum/pcap/02-eth-antpool.pcapngbin0 -> 100368 bytes
-rw-r--r--test/decoders/stratum/pcap/03-xmr_f2pool_nanominer_1.pcapngbin0 -> 5020 bytes
-rw-r--r--test/decoders/stratum/pcap/04-zec-antpool.pcapngbin0 -> 7148 bytes
-rw-r--r--test/decoders/stratum/pcap/stratum_result.json11
-rw-r--r--test/decoders/stratum/plugin_test_main.cpp135
-rw-r--r--test/decoders/stratum/stratum_decoder_pcap_gtest.cpp179
-rw-r--r--test/decoders/stratum/stratum_test_plugin.cpp72
-rw-r--r--test/decoders/stratum/test_based_on_stellar/CMakeLists.txt37
-rw-r--r--test/decoders/stratum/test_based_on_stellar/env/log.toml4
-rw-r--r--test/decoders/stratum/test_based_on_stellar/env/spec.toml9
-rw-r--r--test/decoders/stratum/test_based_on_stellar/env/stellar.toml64
-rw-r--r--vendors/CMakeLists.txt39
-rw-r--r--vendors/colm-0.14.7.tar.gzbin0 -> 1768110 bytes
-rw-r--r--vendors/hyperscan-5.4.2.tar.gzbin0 -> 17359965 bytes
-rw-r--r--vendors/ragel-7.0.4.tar.gzbin0 -> 557265 bytes
160 files changed, 11861 insertions, 1 deletions
diff --git a/decoders/CMakeLists.txt b/decoders/CMakeLists.txt
index 2d71d23..8b3f7a9 100644
--- a/decoders/CMakeLists.txt
+++ b/decoders/CMakeLists.txt
@@ -1,2 +1,5 @@
add_subdirectory(http)
add_subdirectory(lpi)
+add_subdirectory(socks)
+add_subdirectory(stratum)
+add_subdirectory(session_flags) \ No newline at end of file
diff --git a/decoders/session_flags/CMakeLists.txt b/decoders/session_flags/CMakeLists.txt
new file mode 100644
index 0000000..3302328
--- /dev/null
+++ b/decoders/session_flags/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_subdirectory(mesa_sts)
+add_definitions(-fPIC)
+
+set(SESSION_FLAGS_SRC session_flags_plugin.cpp session_flags.cpp fet.cpp tunneling.cpp onlinemean.c)
+
+add_library(session_flags STATIC ${SESSION_FLAGS_SRC})
+add_library(session_flags_dyn SHARED ${SESSION_FLAGS_SRC})
+set_target_properties(session_flags PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/version.map")
+target_include_directories(session_flags PUBLIC ${CMAKE_SOURCE_DIR}/deps/)
+include_directories(${CMAKE_BINARY_DIR}/vendors/cjson/src/cjson/include)
+target_link_libraries(session_flags toml cjson-static hyperscan_static hyperscan_runtime_static libmesa_sts)
+set_target_properties(session_flags PROPERTIES PREFIX "")
+
+set_target_properties(session_flags_dyn PROPERTIES PREFIX "")
+target_link_libraries(session_flags_dyn toml cjson-static hyperscan_static hyperscan_runtime_static libmesa_sts) \ No newline at end of file
diff --git a/decoders/session_flags/fet.cpp b/decoders/session_flags/fet.cpp
new file mode 100644
index 0000000..61327d4
--- /dev/null
+++ b/decoders/session_flags/fet.cpp
@@ -0,0 +1,123 @@
+/*ref: https://github.com/apernet/OpenGFW/blob/1dce82745d0bc8b3813aea147954ba3a2294ef30/analyzer/tcp/fet.go
+https://www.usenix.org/system/files/usenixsecurity23-wu-mingshi.pdf
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include "fet.h"
+
+bool isPrintable(unsigned char b) {
+ return b >= 0x20 && b <= 0x7e;
+}
+
+int popCount(unsigned char b) {
+ int count = 0;
+ while (b != 0) {
+ count += (int)(b & 1);
+ b >>= 1;
+ }
+ return count;
+}
+
+float averagePopCount(const unsigned char* bytes, int bytes_len) {
+ if (bytes_len == 0) {
+ return 0;
+ }
+ int total = 0;
+ for (int i = 0; i < bytes_len; i++) {
+ total += popCount(bytes[i]);
+ }
+ return (float)total / (float)bytes_len;
+}
+
+bool isFirstSixPrintable(const unsigned char* bytes, int bytes_len) {
+ if (bytes_len < 6) {
+ return false;
+ }
+ for (int i = 0; i < 6; i++) {
+ if (!isPrintable(bytes[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+float printablePercentage(const unsigned char* bytes, int bytes_len) {
+ if (bytes_len == 0) {
+ return 0;
+ }
+ int count = 0;
+ for (int i = 0; i < bytes_len; i++) {
+ if (isPrintable(bytes[i])) {
+ count++;
+ }
+ }
+ return (float)count / (float)bytes_len;
+}
+
+int contiguousPrintable(const unsigned char* bytes, int bytes_len) {
+ if (bytes_len == 0) {
+ return 0;
+ }
+ int maxCount = 0;
+ int current = 0;
+ for (int i = 0; i < bytes_len; i++) {
+ if (isPrintable(bytes[i])) {
+ current++;
+ } else {
+ if (current > maxCount) {
+ maxCount = current;
+ }
+ current = 0;
+ }
+ }
+ if (current > maxCount) {
+ maxCount = current;
+ }
+ return maxCount;
+}
+
+bool isTLS(const unsigned char* bytes, int bytes_len) {
+ if (bytes_len < 3) {
+ return false;
+ }
+ // "We observe that the GFW exempts any connection whose first
+ // three bytes match the following regular expression:
+ // [\x16-\x17]\x03[\x00-\x09]" - from the paper in Section 4.3
+ if (bytes[0] >= 0x16 && bytes[0] <= 0x17 &&
+ bytes[1] == 0x03 && bytes[2] <= 0x09) {
+ return true;
+ }
+
+ return false;
+}
+
+bool isHTTP(const unsigned char* bytes, int bytes_len) {
+ if (bytes_len < 3) {
+ return false;
+ }
+
+ // HTTP request
+ bool result = memcmp(bytes, "GET", 3) == 0 || memcmp(bytes, "HEA", 3) == 0 || memcmp(bytes, "POS", 3) == 0 ||
+ memcmp(bytes, "PUT", 3) == 0 || memcmp(bytes, "DEL", 3) == 0 || memcmp(bytes, "CON", 3) == 0 ||
+ memcmp(bytes, "OPT", 3) == 0 || memcmp(bytes, "TRA", 3) == 0 || memcmp(bytes, "PAT", 3) == 0;
+
+ return result;
+}
+
+int is_data_fet(unsigned char* data, int data_len, struct fet_detail* detail) {
+ if (data_len == 0) {
+ return 0;
+ }
+ float ex1 = averagePopCount(data, data_len);
+ bool ex2 = isFirstSixPrintable(data, data_len);
+ float ex3 = printablePercentage(data, data_len);
+ int ex4 = contiguousPrintable(data, data_len);
+ bool ex5 = isTLS(data, data_len);
+ bool ex6 = isHTTP(data, data_len);
+ bool exempt = (ex1 <= 3.4 || ex1 >= 4.6) || ex2 || ex3 > 0.5 || ex4 > 20 || ex5 || ex6;
+
+ detail->is_tls = ex5;
+
+ return !exempt;
+} \ No newline at end of file
diff --git a/decoders/session_flags/fet.h b/decoders/session_flags/fet.h
new file mode 100644
index 0000000..d752ec3
--- /dev/null
+++ b/decoders/session_flags/fet.h
@@ -0,0 +1,7 @@
+#pragma once
+
+struct fet_detail {
+ bool is_tls;
+};
+
+int is_data_fet(unsigned char* data, int data_len, struct fet_detail* detail); \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/CMakeLists.txt b/decoders/session_flags/mesa_sts/CMakeLists.txt
new file mode 100644
index 0000000..fc6f629
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/CMakeLists.txt
@@ -0,0 +1,15 @@
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/)
+
+file(GLOB STS_SRC
+ "src/*.c"
+)
+
+#add_compile_options(-Wno-error=unused-parameter -Wno-unused-but-set-variable -Wno-error=misleading-indentation -Wno-unused-variable -Wno-maybe-uninitialized -Wno-error=unused-function)
+add_library(libmesa_sts ${STS_SRC})
+target_include_directories(libmesa_sts PUBLIC ${CMAKE_SOURCE_DIR}/decoders/session_flags/mesa_sts/include)
+target_compile_options(libmesa_sts PRIVATE -Wno-error=unused-parameter -Wno-unused-but-set-variable -Wno-error=misleading-indentation -Wno-unused-variable -Wno-maybe-uninitialized -Wno-error=unused-function)
+set_target_properties(libmesa_sts PROPERTIES LINK_FLAGS
+ "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/version.map")
+
+add_subdirectory(test)
diff --git a/decoders/session_flags/mesa_sts/include/mesa_sts.h b/decoders/session_flags/mesa_sts/include/mesa_sts.h
new file mode 100644
index 0000000..ee56102
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/include/mesa_sts.h
@@ -0,0 +1,57 @@
+#ifndef _MESA_STS_H_
+#define _MESA_STS_H_
+
+#define STS_RANDOM_JUDGE_NUM 15
+
+#define STS_SET_FLAG(flag, idx) (flag |= (1 << idx))
+#define STS_TEST_FLAG(flag, idx) (flag & (1 << idx))
+
+enum sts_random_judge_list_idx
+{
+ STS_FREQUENCY = 0,
+ STS_BLOCK_FREQUENCY,
+ STS_CUMULATIVE_SUMS,
+ STS_RUNS,
+ STS_LONGEST_RUN,
+ STS_RANK,
+ STS_NON_OVERLAPPING_TEMPLATE_MATCHING,
+ STS_OVERLAPPING_TEMPLATE_MATCHING,
+ STS_UNIVERSAL,
+ STS_RANDOM_EXCURSIONS,
+ STS_RANDOM_EXCURSIONS_VARIANT,
+ STS_POKER_DETECT,
+ STS_RUNS_DISTRIBUTION,
+ STS_SELF_CORRELATION,
+ STS_BINARY_DERIVATE,
+ STS_RANDOM_IDX_MAX
+};
+
+struct sts_result {
+ unsigned char frequency;
+ unsigned char block_frequency;
+ unsigned char cumulative_sums;
+ unsigned char runs;
+ unsigned char longest_run;
+ unsigned char rank;
+ unsigned char non_overlapping_template_matching;
+ unsigned char overlapping_template_matching;
+ unsigned char universal;
+ unsigned char random_excursions;
+ unsigned char random_excursions_variant;
+ unsigned char poker_detect;
+ unsigned char runs_distribution;
+ unsigned char self_correlation;
+ unsigned char binary_derivative;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int mesa_statistical_test_suite(void* data,unsigned int datalen, struct sts_result* result, unsigned int random_judge_switch_flag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MESA_STS_H_ */
diff --git a/decoders/session_flags/mesa_sts/src/approximateEntropy.c b/decoders/session_flags/mesa_sts/src/approximateEntropy.c
new file mode 100644
index 0000000..b386056
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/approximateEntropy.c
@@ -0,0 +1,72 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ A P P R O X I M A T E E N T R O P Y T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+ApproximateEntropy(int m, int n, BitSequence *epsilon)
+{
+ int i, j, k, r, blockSize, seqLength, powLen, index;
+ double sum, numOfBlocks, ApEn[2], apen, chi_squared, p_value;
+ unsigned int *P;
+
+ seqLength = n;
+ r = 0;
+
+ for ( blockSize=m; blockSize<=m+1; blockSize++ ) {
+ if ( blockSize == 0 ) {
+ ApEn[0] = 0.00;
+ r++;
+ }
+ else {
+ numOfBlocks = (double)seqLength;
+ powLen = (int)pow(2, blockSize+1)-1;
+ if ( (P = (unsigned int*)calloc(powLen,sizeof(unsigned int)))== NULL ) {
+ return 0;
+ }
+ for ( i=1; i<powLen-1; i++ )
+ P[i] = 0;
+ for ( i=0; i<numOfBlocks; i++ ) { /* COMPUTE FREQUENCY */
+ k = 1;
+ for ( j=0; j<blockSize; j++ ) {
+ k <<= 1;
+ if ( (int)epsilon[(i+j) % seqLength] == 1 )
+ k++;
+ }
+ P[k-1]++;
+ }
+ /* DISPLAY FREQUENCY */
+ sum = 0.0;
+ index = (int)pow(2, blockSize)-1;
+ for ( i=0; i<(int)pow(2, blockSize); i++ ) {
+ if ( P[index] > 0 )
+ sum += P[index]*log(P[index]/numOfBlocks);
+ index++;
+ }
+ sum /= numOfBlocks;
+ ApEn[r] = sum;
+ r++;
+ free(P);
+ }
+ }
+ apen = ApEn[0] - ApEn[1];
+
+ chi_squared = 2.0*seqLength*(log(2) - apen);
+ p_value = cephes_igamc(pow(2, m-1), chi_squared/2.0);
+
+ if ( m > (int)(log(seqLength)/log(2)-5) ) {
+ return 0;
+ }
+
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+} \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/binaryDerivate.c b/decoders/session_flags/mesa_sts/src/binaryDerivate.c
new file mode 100644
index 0000000..6d2dc4a
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/binaryDerivate.c
@@ -0,0 +1,30 @@
+#include <math.h>
+#include "include/stat_fncs.h"
+
+int BinaryDerivate(int k, int n, BitSequence *epsilon, int epsilon_l)
+{
+ int i = 0, j = 0;
+ int Sn_k = 0;
+ int n_k = n - k;
+ double V = 0.0, p_value = 0.0, sqrt2 = 1.41421356237309504880;
+
+ for (i = 0; i < k; ++i) {
+ for (j = 0; j < epsilon_l - 1; ++j) {
+ epsilon[j] = epsilon[j] ^ epsilon[j + 1];
+ }
+ }
+
+ for (i = 0; i < n_k; ++i) {
+ Sn_k += (2 * (int)epsilon[i]) - 1;
+ }
+
+ V = fabs(Sn_k) / sqrt(n_k);
+
+ p_value = erfc(fabs(V) / sqrt2);
+
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+} \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/blockFrequency.c b/decoders/session_flags/mesa_sts/src/blockFrequency.c
new file mode 100644
index 0000000..ba4ad34
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/blockFrequency.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ B L O C K F R E Q U E N C Y T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+BlockFrequency(int M, int n, BitSequence *epsilon)
+{
+ int i, j, N, blockSum;
+ double p_value, sum, pi, v, chi_squared;
+
+ N = n/M; /* # OF SUBSTRING BLOCKS */
+ sum = 0.0;
+
+ for ( i=0; i<N; i++ ) {
+ blockSum = 0;
+ for ( j=0; j<M; j++ )
+ blockSum += epsilon[j+i*M];
+ pi = (double)blockSum/(double)M;
+ v = pi - 0.5;
+ sum += v*v;
+ }
+ chi_squared = 4.0 * M * sum;
+ p_value = cephes_igamc(N/2.0, chi_squared/2.0);
+
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/decoders/session_flags/mesa_sts/src/bytesToBitSequence.c b/decoders/session_flags/mesa_sts/src/bytesToBitSequence.c
new file mode 100644
index 0000000..5953702
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/bytesToBitSequence.c
@@ -0,0 +1,31 @@
+#include "include/stat_fncs.h"
+
+static unsigned char _compute(unsigned char b, unsigned char factor)
+{
+ if ((factor & b) == factor) {
+ return 0x01;
+ } else {
+ return 0x00;
+ }
+}
+
+int BytesToBitSequence(unsigned char *in, int inl, BitSequence *outbuf, int bufsize)
+{
+ int j = 0, i = 0;
+ if (bufsize < inl * 8) {
+ return 0;
+ }
+
+ for (i = 0; i < inl; ++i) {
+ j = i * 8;
+ outbuf[j] = (BitSequence) (_compute(in[i], 0x80));
+ outbuf[j + 1] = (BitSequence) (_compute(in[i], 0x40));
+ outbuf[j + 2] = (BitSequence) (_compute(in[i], 0x20));
+ outbuf[j + 3] = (BitSequence) (_compute(in[i], 0x10));
+ outbuf[j + 4] = (BitSequence) (_compute(in[i], 0x08));
+ outbuf[j + 5] = (BitSequence) (_compute(in[i], 0x04));
+ outbuf[j + 6] = (BitSequence) (_compute(in[i], 0x02));
+ outbuf[j + 7] = (BitSequence) (_compute(in[i], 0x01));
+ }
+ return 1;
+}
diff --git a/decoders/session_flags/mesa_sts/src/cephes.c b/decoders/session_flags/mesa_sts/src/cephes.c
new file mode 100644
index 0000000..a419f27
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/cephes.c
@@ -0,0 +1,348 @@
+#include <stdio.h>
+#include <math.h>
+#include "include/cephes.h"
+
+static const double rel_error = 1E-12;
+
+double MACHEP = 1.11022302462515654042E-16; // 2**-53
+double MAXLOG = 7.09782712893383996732224E2; // log(MAXNUM)
+double MAXNUM = 1.7976931348623158E308; // 2**1024*(1-MACHEP)
+double PI = 3.14159265358979323846; // pi, duh!
+
+static double big = 4.503599627370496e15;
+static double biginv = 2.22044604925031308085e-16;
+
+int sgngam = 0;
+
+double
+cephes_igamc(double a, double x)
+{
+ double ans, ax, c, yc, r, t, y, z;
+ double pk, pkm1, pkm2, qk, qkm1, qkm2;
+
+ if ( (x <= 0) || ( a <= 0) )
+ return( 1.0 );
+
+ if ( (x < 1.0) || (x < a) )
+ return( 1.e0 - cephes_igam(a,x) );
+
+ ax = a * log(x) - x - cephes_lgam(a);
+
+ if ( ax < -MAXLOG ) {
+ printf("igamc: UNDERFLOW\n");
+ return 0.0;
+ }
+ ax = exp(ax);
+
+ /* continued fraction */
+ y = 1.0 - a;
+ z = x + y + 1.0;
+ c = 0.0;
+ pkm2 = 1.0;
+ qkm2 = x;
+ pkm1 = x + 1.0;
+ qkm1 = z * x;
+ ans = pkm1/qkm1;
+
+ do {
+ c += 1.0;
+ y += 1.0;
+ z += 2.0;
+ yc = y * c;
+ pk = pkm1 * z - pkm2 * yc;
+ qk = qkm1 * z - qkm2 * yc;
+ if ( qk != 0 ) {
+ r = pk/qk;
+ t = fabs( (ans - r)/r );
+ ans = r;
+ }
+ else
+ t = 1.0;
+ pkm2 = pkm1;
+ pkm1 = pk;
+ qkm2 = qkm1;
+ qkm1 = qk;
+ if ( fabs(pk) > big ) {
+ pkm2 *= biginv;
+ pkm1 *= biginv;
+ qkm2 *= biginv;
+ qkm1 *= biginv;
+ }
+ } while ( t > MACHEP );
+
+ return ans*ax;
+}
+
+double
+cephes_igam(double a, double x)
+{
+ double ans, ax, c, r;
+
+ if ( (x <= 0) || ( a <= 0) )
+ return 0.0;
+
+ if ( (x > 1.0) && (x > a ) )
+ return 1.e0 - cephes_igamc(a,x);
+
+ /* Compute x**a * exp(-x) / gamma(a) */
+ ax = a * log(x) - x - cephes_lgam(a);
+ if ( ax < -MAXLOG ) {
+ printf("igam: UNDERFLOW\n");
+ return 0.0;
+ }
+ ax = exp(ax);
+
+ /* power series */
+ r = a;
+ c = 1.0;
+ ans = 1.0;
+
+ do {
+ r += 1.0;
+ c *= x/r;
+ ans += c;
+ } while ( c/ans > MACHEP );
+
+ return ans * ax/a;
+}
+
+union A_Array {
+ double d[5];
+ unsigned short us[20];
+};
+
+
+union BC_Array {
+ double d[6];
+ unsigned short us[24];
+};
+
+/* A[]: Stirling's formula expansion of log gamma
+ * B[], C[]: log gamma function between 2 and 3
+ */
+
+/**/
+static union A_Array A = {
+ .us = {
+ 0x6661,0x2733,0x9850,0x3f4a,
+ 0xe943,0xb580,0x7fbd,0xbf43,
+ 0x5ebb,0x20dc,0x019f,0x3f4a,
+ 0xa5a1,0x16b0,0xc16c,0xbf66,
+ 0x554b,0x5555,0x5555,0x3fb5
+ }
+};
+static union BC_Array B = {
+ .us = {
+ 0x6761,0x8ff3,0x8901,0xc095,
+ 0xb93e,0x355b,0xf234,0xc0e2,
+ 0x89e5,0xf890,0x3d73,0xc114,
+ 0xdb51,0xf994,0xbc82,0xc131,
+ 0xf20b,0x0219,0x4589,0xc13a,
+ 0x055e,0x5418,0x0c67,0xc12a
+ }
+};
+static union BC_Array C = {
+ /*0x0000,0x0000,0x0000,0x3ff0,*/
+ .us = {
+ 0x12b2,0x1cf3,0xfd0d,0xc075,
+ 0xd757,0x7b89,0xaa0d,0xc0d0,
+ 0x4c9b,0xb974,0xeb84,0xc10a,
+ 0x0043,0x7195,0x6286,0xc131,
+ 0xf34c,0x892f,0x5255,0xc143,
+ 0xe14a,0x6a11,0xce4b,0xc13e
+ }
+};
+
+#define MAXLGM 2.556348e305
+
+
+/* Logarithm of gamma function */
+double
+cephes_lgam(double x)
+{
+ double p, q, u, w, z;
+ int i;
+
+ sgngam = 1;
+
+ if ( x < -34.0 ) {
+ q = -x;
+ w = cephes_lgam(q); /* note this modifies sgngam! */
+ p = floor(q);
+ if ( p == q ) {
+lgsing:
+ goto loverf;
+ }
+ i = (int)p;
+ if ( (i & 1) == 0 )
+ sgngam = -1;
+ else
+ sgngam = 1;
+ z = q - p;
+ if ( z > 0.5 ) {
+ p += 1.0;
+ z = p - q;
+ }
+ z = q * sin( PI * z );
+ if ( z == 0.0 )
+ goto lgsing;
+ /* z = log(PI) - log( z ) - w;*/
+ z = log(PI) - log( z ) - w;
+ return z;
+ }
+
+ if ( x < 13.0 ) {
+ z = 1.0;
+ p = 0.0;
+ u = x;
+ while ( u >= 3.0 ) {
+ p -= 1.0;
+ u = x + p;
+ z *= u;
+ }
+ while ( u < 2.0 ) {
+ if ( u == 0.0 )
+ goto lgsing;
+ z /= u;
+ p += 1.0;
+ u = x + p;
+ }
+ if ( z < 0.0 ) {
+ sgngam = -1;
+ z = -z;
+ }
+ else
+ sgngam = 1;
+ if ( u == 2.0 )
+ return( log(z) );
+ p -= 2.0;
+ x = x + p;
+ p = x * cephes_polevl( x, (double *)B.d, 5 ) / cephes_p1evl( x, (double *)C.d, 6);
+
+ return log(z) + p;
+ }
+
+ if ( x > MAXLGM ) {
+loverf:
+ printf("lgam: OVERFLOW\n");
+
+ return sgngam * MAXNUM;
+ }
+
+ q = ( x - 0.5 ) * log(x) - x + log( sqrt( 2*PI ) );
+ if ( x > 1.0e8 )
+ return q;
+
+ p = 1.0/(x*x);
+ if ( x >= 1000.0 )
+ q += (( 7.9365079365079365079365e-4 * p
+ - 2.7777777777777777777778e-3) *p
+ + 0.0833333333333333333333) / x;
+ else
+ q += cephes_polevl( p, (double *)A.d, 4 ) / x;
+
+ return q;
+}
+
+double
+cephes_polevl(double x, double *coef, int N)
+{
+ double ans;
+ int i;
+ double *p;
+
+ p = coef;
+ ans = *p++;
+ i = N;
+
+ do
+ ans = ans * x + *p++;
+ while ( --i );
+
+ return ans;
+}
+
+double
+cephes_p1evl(double x, double *coef, int N)
+{
+ double ans;
+ double *p;
+ int i;
+
+ p = coef;
+ ans = x + *p++;
+ i = N-1;
+
+ do
+ ans = ans * x + *p++;
+ while ( --i );
+
+ return ans;
+}
+
+double
+cephes_erf(double x)
+{
+ static const double two_sqrtpi = 1.128379167095512574;
+ double sum = x, term = x, xsqr = x * x;
+ int j = 1;
+
+ if ( fabs(x) > 2.2 )
+ return 1.0 - cephes_erfc(x);
+
+ do {
+ term *= xsqr/j;
+ sum -= term/(2*j+1);
+ j++;
+ term *= xsqr/j;
+ sum += term/(2*j+1);
+ j++;
+ } while ( fabs(term)/sum > rel_error );
+
+ return two_sqrtpi*sum;
+}
+
+double
+cephes_erfc(double x)
+{
+ static const double one_sqrtpi = 0.564189583547756287;
+ double a = 1, b = x, c = x, d = x*x + 0.5;
+ double q1, q2 = b/d, n = 1.0, t;
+
+ if ( fabs(x) < 2.2 )
+ return 1.0 - cephes_erf(x);
+ if ( x < 0 )
+ return 2.0 - cephes_erfc(-x);
+
+ do {
+ t = a*n + b*x;
+ a = b;
+ b = t;
+ t = c*n + d*x;
+ c = d;
+ d = t;
+ n += 0.5;
+ q1 = q2;
+ q2 = b/d;
+ } while ( fabs(q1-q2)/q2 > rel_error );
+
+ return one_sqrtpi*exp(-x*x)*q2;
+}
+
+
+double
+cephes_normal(double x)
+{
+ double arg, result, sqrt2=1.414213562373095048801688724209698078569672;
+
+ if (x > 0) {
+ arg = x/sqrt2;
+ result = 0.5 * ( 1 + erf(arg) );
+ }
+ else {
+ arg = -x/sqrt2;
+ result = 0.5 * (erfc(arg) );
+ }
+
+ return( result);
+}
diff --git a/decoders/session_flags/mesa_sts/src/cusum.c b/decoders/session_flags/mesa_sts/src/cusum.c
new file mode 100644
index 0000000..d9be4e1
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/cusum.c
@@ -0,0 +1,95 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ C U M U L A T I V E S U M S T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+CumulativeSums(int n, BitSequence *epsilon)
+{
+ int S, sup, inf, z=1, zrev, k;
+ double sum1, sum2, p_value;
+
+ S = 0;
+ sup = 0;
+ inf = 0;
+ for ( k=0; k<n; k++ ) {
+ epsilon[k] ? S++ : S--;
+ if ( S > sup )
+ sup++;
+ if ( S < inf )
+ inf--;
+ z = (sup > -inf) ? sup : -inf;
+ zrev = (sup-S > S-inf) ? sup-S : S-inf;
+ }
+
+ // forward
+ sum1 = 0.0;
+ for ( k=(-n/z+1)/4; k<=(n/z-1)/4; k++ ) {
+ sum1 += cephes_normal(((4*k+1)*z)/sqrt(n));
+ sum1 -= cephes_normal(((4*k-1)*z)/sqrt(n));
+ }
+ sum2 = 0.0;
+ for ( k=(-n/z-3)/4; k<=(n/z-1)/4; k++ ) {
+ sum2 += cephes_normal(((4*k+3)*z)/sqrt(n));
+ sum2 -= cephes_normal(((4*k+1)*z)/sqrt(n));
+ }
+
+ p_value = 1.0 - sum1 + sum2;
+
+// fprintf(stats[TEST_CUSUM], "\t\t CUMULATIVE SUMS (FORWARD) TEST\n");
+// fprintf(stats[TEST_CUSUM], "\t\t-------------------------------------------\n");
+// fprintf(stats[TEST_CUSUM], "\t\tCOMPUTATIONAL INFORMATION:\n");
+// fprintf(stats[TEST_CUSUM], "\t\t-------------------------------------------\n");
+// fprintf(stats[TEST_CUSUM], "\t\t(a) The maximum partial sum = %d\n", z);
+// fprintf(stats[TEST_CUSUM], "\t\t-------------------------------------------\n");
+
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+// fprintf(stats[TEST_CUSUM], "\t\tWARNING: P_VALUE IS OUT OF RANGE\n");
+ return 0;
+ }
+
+// fprintf(stats[TEST_CUSUM], "%s\t\tp_value = %f\n\n", p_value < ALPHA ? "FAILURE" : "SUCCESS", p_value);
+// fprintf(results[TEST_CUSUM], "%f\n", p_value);
+ if (p_value < ALPHA) {
+ return 0;
+ }
+
+ // backwards
+ sum1 = 0.0;
+ for ( k=(-n/zrev+1)/4; k<=(n/zrev-1)/4; k++ ) {
+ sum1 += cephes_normal(((4*k+1)*zrev)/sqrt(n));
+ sum1 -= cephes_normal(((4*k-1)*zrev)/sqrt(n));
+ }
+ sum2 = 0.0;
+ for ( k=(-n/zrev-3)/4; k<=(n/zrev-1)/4; k++ ) {
+ sum2 += cephes_normal(((4*k+3)*zrev)/sqrt(n));
+ sum2 -= cephes_normal(((4*k+1)*zrev)/sqrt(n));
+ }
+ p_value = 1.0 - sum1 + sum2;
+
+// fprintf(stats[TEST_CUSUM], "\t\t CUMULATIVE SUMS (REVERSE) TEST\n");
+// fprintf(stats[TEST_CUSUM], "\t\t-------------------------------------------\n");
+// fprintf(stats[TEST_CUSUM], "\t\tCOMPUTATIONAL INFORMATION:\n");
+// fprintf(stats[TEST_CUSUM], "\t\t-------------------------------------------\n");
+// fprintf(stats[TEST_CUSUM], "\t\t(a) The maximum partial sum = %d\n", zrev);
+// fprintf(stats[TEST_CUSUM], "\t\t-------------------------------------------\n");
+
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+// fprintf(stats[TEST_CUSUM], "\t\tWARNING: P_VALUE IS OUT OF RANGE\n");
+ return 0;
+ }
+
+// fprintf(stats[TEST_CUSUM], "%s\t\tp_value = %f\n\n", p_value < ALPHA ? "FAILURE" : "SUCCESS", p_value); fflush(stats[TEST_CUSUM]);
+// fprintf(results[TEST_CUSUM], "%f\n", p_value); fflush(results[TEST_CUSUM]);
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+} \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/dfft.c b/decoders/session_flags/mesa_sts/src/dfft.c
new file mode 100644
index 0000000..71c0efd
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/dfft.c
@@ -0,0 +1,1462 @@
+/* Notes from RFB:
+
+Looks like the user-level routines are:
+
+Real FFT
+
+void __ogg_fdrffti(int n, double *wsave, int *ifac)
+void __ogg_fdrfftf(int n, double *r, double *wsave, int *ifac)
+void __ogg_fdrfftb(int n, double *r, double *wsave, int *ifac)
+
+__ogg_fdrffti == initialization
+__ogg_fdrfftf == forward transform
+__ogg_fdrfftb == backward transform
+
+Parameters are
+n == length of sequence
+r == sequence to be transformed (input)
+== transformed sequence (output)
+wsave == work array of length 2n (allocated by caller)
+ifac == work array of length 15 (allocated by caller)
+
+Cosine quarter-wave FFT
+
+void __ogg_fdcosqi(int n, double *wsave, int *ifac)
+void __ogg_fdcosqf(int n, double *x, double *wsave, int *ifac)
+void __ogg_fdcosqb(int n, double *x, double *wsave, int *ifac)
+*/
+
+/********************************************************************
+* *
+* THIS FILE IS PART OF THE OggSQUISH SOFTWARE CODEC SOURCE CODE. *
+* *
+********************************************************************
+
+file: fft.c
+function: Fast discrete Fourier and cosine transforms and inverses
+author: Monty <[email protected]>
+modifications by: Monty
+last modification date: Jul 1 1996
+
+********************************************************************/
+
+/* These Fourier routines were originally based on the Fourier
+routines of the same names from the NETLIB bihar and fftpack
+fortran libraries developed by Paul N. Swarztrauber at the National
+Center for Atmospheric Research in Boulder, CO USA. They have been
+reimplemented in C and optimized in a few ways for OggSquish. */
+
+/* As the original fortran libraries are public domain, the C Fourier
+routines in this file are hereby released to the public domain as
+well. The C routines here produce output exactly equivalent to the
+original fortran routines. Of particular interest are the facts
+that (like the original fortran), these routines can work on
+arbitrary length vectors that need not be powers of two in
+length. */
+
+#include <math.h>
+#define STIN static
+
+static void drfti1(int n, double *wa, int *ifac)
+{
+ static const int ntryh[4] = { 4,2,3,5 };
+ static double tpi = 6.28318530717958647692528676655900577;
+ double arg, argh, argld, fi;
+ int ntry=0,i,j=-1;
+ int k1, l1, l2, ib;
+ int ld, ii, ip, is, nq, nr;
+ int ido, ipm, nfm1;
+ int nl=n;
+ int nf=0;
+
+L101:
+ j++;
+ if ( j < 4 )
+ ntry = ntryh[j];
+ else
+ ntry += 2;
+
+L104:
+ nq = nl/ntry;
+ nr = nl-ntry*nq;
+ if ( nr != 0 )
+ goto L101;
+
+ nf++;
+ ifac[nf+1] = ntry;
+ nl = nq;
+ if ( ntry != 2 )
+ goto L107;
+ if ( nf == 1 )
+ goto L107;
+
+ for ( i=1; i<nf; i++ ) {
+ ib = nf-i+1;
+ ifac[ib+1] = ifac[ib];
+ }
+ ifac[2] = 2;
+
+L107:
+ if( nl != 1 )
+ goto L104;
+ ifac[0] = n;
+ ifac[1] = nf;
+ argh = tpi/n;
+ is = 0;
+ nfm1 = nf-1;
+ l1 = 1;
+
+ if ( nfm1 == 0 )
+ return;
+
+ for ( k1=0; k1<nfm1; k1++ ) {
+ ip = ifac[k1+2];
+ ld = 0;
+ l2 = l1*ip;
+ ido = n/l2;
+ ipm = ip-1;
+
+ for ( j=0; j<ipm; j++ ) {
+ ld += l1;
+ i = is;
+ argld = (double)ld*argh;
+ fi = 0.0;
+ for ( ii=2; ii<ido; ii+=2 ) {
+ fi += 1.0;
+ arg = fi*argld;
+ wa[i++] = cos(arg);
+ wa[i++] = sin(arg);
+ }
+ is += ido;
+ }
+ l1 = l2;
+ }
+}
+
+void __ogg_fdrffti(int n, double *wsave, int *ifac)
+{
+ if ( n == 1 )
+ return;
+ drfti1(n, wsave+n, ifac);
+}
+
+#ifdef LBBBBBBBBBBBBB
+
+void __ogg_fdcosqi(int n, double *wsave, int *ifac)
+{
+ static double pih = 1.57079632679489661923132169163975;
+ static int k;
+ static double fk, dt;
+
+ dt = pih/n;
+ fk = 0.0;
+ for ( k=0; k<n; k++ ) {
+ fk += 1.0;
+ wsave[k] = cos(fk*dt);
+ }
+
+ __ogg_fdrffti(n, wsave+n,ifac);
+}
+
+#endif
+
+STIN void dradf2(int ido, int l1, double *cc, double *ch, double *wa1)
+{
+ int i , k;
+ double ti2 , tr2;
+ int t0 , t1, t2, t3, t4, t5, t6;
+
+ t1 = 0;
+ t0 = (t2 = l1*ido);
+ t3 = ido<<1;
+ for ( k=0; k<l1; k++ ) {
+ ch[t1<<1] = cc[t1]+cc[t2];
+ ch[(t1<<1)+t3-1] = cc[t1]-cc[t2];
+ t1 += ido;
+ t2 += ido;
+ }
+
+ if ( ido < 2 )
+ return;
+ if ( ido == 2 )
+ goto L105;
+
+ t1 = 0;
+ t2 = t0;
+ for ( k=0; k<l1; k++ ) {
+ t3 = t2;
+ t4 = (t1<<1)+(ido<<1);
+ t5 = t1;
+ t6 = t1+t1;
+ for ( i=2; i<ido; i+=2 ) {
+ t3 += 2;
+ t4 -= 2;
+ t5 += 2;
+ t6 += 2;
+ tr2 = wa1[i-2]*cc[t3-1]+wa1[i-1]*cc[t3];
+ ti2 = wa1[i-2]*cc[t3]-wa1[i-1]*cc[t3-1];
+ ch[t6] = cc[t5]+ti2;
+ ch[t4] = ti2-cc[t5];
+ ch[t6-1] = cc[t5-1]+tr2;
+ ch[t4-1] = cc[t5-1]-tr2;
+ }
+ t1 += ido;
+ t2 += ido;
+ }
+
+ if ( ido%2 == 1 )
+ return;
+
+L105:
+ t3 = (t2 = (t1 = ido)-1);
+ t2 += t0;
+ for ( k=0; k<l1; k++ ) {
+ ch[t1] = -cc[t2];
+ ch[t1-1] = cc[t3];
+ t1 += ido<<1;
+ t2 += ido;
+ t3 += ido;
+ }
+}
+
+STIN void dradf4(int ido, int l1, double *cc, double *ch, double *wa1,
+ double *wa2, double *wa3)
+{
+ static double hsqt2 = .70710678118654752440084436210485;
+ int i, k, t0, t1, t2, t3, t4, t5, t6;
+ double ci2, ci3, ci4, cr2, cr3, cr4;
+ double ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+
+ t0 = l1*ido;
+ t1 = t0;
+ t4 = t1<<1;
+ t2 = t1+(t1<<1);
+ t3 = 0;
+
+ for ( k=0; k<l1; k++ ) {
+ tr1 = cc[t1]+cc[t2];
+ tr2 = cc[t3]+cc[t4];
+ ch[t5 = t3<<2] = tr1+tr2;
+ ch[(ido<<2)+t5-1] = tr2-tr1;
+ ch[(t5 += (ido<<1))-1] = cc[t3]-cc[t4];
+ ch[t5] = cc[t2]-cc[t1];
+
+ t1 += ido;
+ t2 += ido;
+ t3 += ido;
+ t4 += ido;
+ }
+
+ if ( ido < 2 )
+ return;
+ if ( ido == 2 )
+ goto L105;
+
+ t1 = 0;
+ for ( k=0; k<l1; k++ ) {
+ t2 = t1;
+ t4 = t1<<2;
+ t5 = (t6 = ido<<1)+t4;
+ for ( i=2; i<ido; i+=2 ) {
+ t3 = (t2 += 2);
+ t4 += 2;
+ t5 -= 2;
+
+ t3 += t0;
+ cr2 = wa1[i-2]*cc[t3-1]+wa1[i-1]*cc[t3];
+ ci2 = wa1[i-2]*cc[t3]-wa1[i-1]*cc[t3-1];
+ t3 += t0;
+ cr3 = wa2[i-2]*cc[t3-1]+wa2[i-1]*cc[t3];
+ ci3 = wa2[i-2]*cc[t3]-wa2[i-1]*cc[t3-1];
+ t3 += t0;
+ cr4 = wa3[i-2]*cc[t3-1]+wa3[i-1]*cc[t3];
+ ci4 = wa3[i-2]*cc[t3]-wa3[i-1]*cc[t3-1];
+
+ tr1 = cr2+cr4;
+ tr4 = cr4-cr2;
+ ti1 = ci2+ci4;
+ ti4 = ci2-ci4;
+ ti2 = cc[t2]+ci3;
+ ti3 = cc[t2]-ci3;
+ tr2 = cc[t2-1]+cr3;
+ tr3 = cc[t2-1]-cr3;
+
+
+ ch[t4-1] = tr1+tr2;
+ ch[t4] = ti1+ti2;
+
+ ch[t5-1] = tr3-ti4;
+ ch[t5] = tr4-ti3;
+
+ ch[t4+t6-1] = ti4+tr3;
+ ch[t4+t6] = tr4+ti3;
+
+ ch[t5+t6-1] = tr2-tr1;
+ ch[t5+t6] = ti1-ti2;
+ }
+ t1 += ido;
+ }
+ if ( ido%2 == 1 )
+ return;
+
+L105:
+
+ t2 = (t1 = t0+ido-1)+(t0<<1);
+ t3 = ido<<2;
+ t4 = ido;
+ t5 = ido<<1;
+ t6 = ido;
+
+ for ( k=0; k<l1; k++ ) {
+ ti1 = -hsqt2*(cc[t1]+cc[t2]);
+ tr1 = hsqt2*(cc[t1]-cc[t2]);
+ ch[t4-1] = tr1+cc[t6-1];
+ ch[t4+t5-1] = cc[t6-1]-tr1;
+ ch[t4] = ti1-cc[t1+t0];
+ ch[t4+t5] = ti1+cc[t1+t0];
+ t1 += ido;
+ t2 += ido;
+ t4 += t3;
+ t6 += ido;
+ }
+}
+
+STIN void dradfg(int ido, int ip, int l1, int idl1, double *cc, double *c1,
+ double *c2, double *ch, double *ch2, double *wa)
+{
+ static double tpi = 6.28318530717958647692528676655900577;
+ int idij, ipph, i, j, k, l, ic, ik, is;
+ int t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10;
+ double dc2, ai1, ai2, ar1, ar2, ds2;
+ int nbd;
+ double dcp, arg, dsp, ar1h, ar2h;
+ int idp2, ipp2;
+
+ arg = tpi/(double)ip;
+ dcp = cos(arg);
+ dsp = sin(arg);
+ ipph = (ip+1)>>1;
+ ipp2 = ip;
+ idp2 = ido;
+ nbd = (ido-1)>>1;
+ t0 = l1*ido;
+ t10 = ip*ido;
+
+ if ( ido == 1 )
+ goto L119;
+ for ( ik=0; ik<idl1; ik++ )
+ ch2[ik] = c2[ik];
+
+ t1 = 0;
+ for ( j=1; j<ip; j++ ) {
+ t1 += t0;
+ t2 = t1;
+ for ( k=0; k<l1; k++ ) {
+ ch[t2] = c1[t2];
+ t2 += ido;
+ }
+ }
+
+ is = -ido;
+ t1 = 0;
+ if ( nbd > l1 ) {
+ for ( j=1; j<ip; j++ ) {
+ t1 += t0;
+ is += ido;
+ t2 = -ido+t1;
+ for ( k=0; k<l1; k++ ) {
+ idij = is-1;
+ t2 += ido;
+ t3 = t2;
+ for ( i=2; i<ido; i+=2 ) {
+ idij += 2;
+ t3 += 2;
+ ch[t3-1] = wa[idij-1]*c1[t3-1]+wa[idij]*c1[t3];
+ ch[t3] = wa[idij-1]*c1[t3]-wa[idij]*c1[t3-1];
+ }
+ }
+ }
+ }
+ else {
+ for ( j=1; j<ip; j++ ) {
+ is += ido;
+ idij = is-1;
+ t1 += t0;
+ t2 = t1;
+ for ( i=2; i<ido; i+=2 ) {
+ idij += 2;
+ t2 += 2;
+ t3 = t2;
+ for ( k=0; k<l1; k++ ) {
+ ch[t3-1] = wa[idij-1]*c1[t3-1]+wa[idij]*c1[t3];
+ ch[t3] = wa[idij-1]*c1[t3]-wa[idij]*c1[t3-1];
+ t3 += ido;
+ }
+ }
+ }
+ }
+
+ t1 = 0;
+ t2 = ipp2*t0;
+ if( nbd < l1 ) {
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1;
+ t4 = t2;
+ for ( i=2; i<ido; i+=2 ) {
+ t3 += 2;
+ t4 += 2;
+ t5 = t3-ido;
+ t6 = t4-ido;
+ for ( k=0; k<l1; k++ ) {
+ t5 += ido;
+ t6 += ido;
+ c1[t5-1] = ch[t5-1]+ch[t6-1];
+ c1[t6-1] = ch[t5]-ch[t6];
+ c1[t5] = ch[t5]+ch[t6];
+ c1[t6] = ch[t6-1]-ch[t5-1];
+ }
+ }
+ }
+ }
+ else {
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1;
+ t4 = t2;
+ for ( k=0; k<l1; k++ ) {
+ t5 = t3;
+ t6 = t4;
+ for ( i=2; i<ido; i+=2 ) {
+ t5 += 2;
+ t6 += 2;
+ c1[t5-1] = ch[t5-1]+ch[t6-1];
+ c1[t6-1] = ch[t5]-ch[t6];
+ c1[t5] = ch[t5]+ch[t6];
+ c1[t6] = ch[t6-1]-ch[t5-1];
+ }
+ t3 += ido;
+ t4 += ido;
+ }
+ }
+ }
+
+L119:
+ for ( ik=0; ik<idl1; ik++ )
+ c2[ik] = ch2[ik];
+
+ t1 = 0;
+ t2 = ipp2*idl1;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1-ido;
+ t4 = t2-ido;
+ for ( k=0; k<l1; k++ ) {
+ t3 += ido;
+ t4 += ido;
+ c1[t3] = ch[t3]+ch[t4];
+ c1[t4] = ch[t4]-ch[t3];
+ }
+ }
+
+ ar1 = 1.0;
+ ai1 = 0.0;
+ t1 = 0;
+ t2 = ipp2*idl1;
+ t3 = (ip-1)*idl1;
+ for ( l=1; l<ipph; l++ ) {
+ t1 += idl1;
+ t2 -= idl1;
+ ar1h = dcp*ar1-dsp*ai1;
+ ai1 = dcp*ai1+dsp*ar1;
+ ar1 = ar1h;
+ t4 = t1;
+ t5 = t2;
+ t6 = t3;
+ t7 = idl1;
+
+ for ( ik=0; ik<idl1; ik++ ) {
+ ch2[t4++] = c2[ik]+ar1*c2[t7++];
+ ch2[t5++] = ai1*c2[t6++];
+ }
+
+ dc2 = ar1;
+ ds2 = ai1;
+ ar2 = ar1;
+ ai2 = ai1;
+
+ t4 = idl1;
+ t5 = (ipp2-1)*idl1;
+ for ( j=2; j<ipph; j++ ) {
+ t4 += idl1;
+ t5 -= idl1;
+
+ ar2h = dc2*ar2-ds2*ai2;
+ ai2 = dc2*ai2+ds2*ar2;
+ ar2 = ar2h;
+
+ t6 = t1;
+ t7 = t2;
+ t8 = t4;
+ t9 = t5;
+ for ( ik=0; ik<idl1; ik++ ) {
+ ch2[t6++] += ar2*c2[t8++];
+ ch2[t7++] += ai2*c2[t9++];
+ }
+ }
+ }
+
+ t1 = 0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += idl1;
+ t2 = t1;
+ for ( ik=0; ik<idl1; ik++ )
+ ch2[ik] += c2[t2++];
+ }
+
+ if ( ido < l1 )
+ goto L132;
+
+ t1 = 0;
+ t2 = 0;
+ for ( k=0; k<l1; k++ ) {
+ t3 = t1;
+ t4 = t2;
+ for ( i=0; i<ido; i++ )
+ cc[t4++] = ch[t3++];
+ t1 += ido;
+ t2 += t10;
+ }
+
+ goto L135;
+
+L132:
+ for ( i=0; i<ido; i++ ) {
+ t1 = i;
+ t2 = i;
+ for ( k=0; k<l1; k++ ) {
+ cc[t2] = ch[t1];
+ t1 += ido;
+ t2 += t10;
+ }
+ }
+
+L135:
+ t1 = 0;
+ t2 = ido<<1;
+ t3 = 0;
+ t4 = ipp2*t0;
+ for ( j=1; j<ipph; j++ ) {
+
+ t1 += t2;
+ t3 += t0;
+ t4 -= t0;
+
+ t5 = t1;
+ t6 = t3;
+ t7 = t4;
+
+ for ( k=0; k<l1; k++ ) {
+ cc[t5-1] = ch[t6];
+ cc[t5] = ch[t7];
+ t5 += t10;
+ t6 += ido;
+ t7 += ido;
+ }
+ }
+
+ if ( ido == 1 )
+ return;
+ if ( nbd < l1 )
+ goto L141;
+
+ t1 = -ido;
+ t3 = 0;
+ t4 = 0;
+ t5 = ipp2*t0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t2;
+ t3 += t2;
+ t4 += t0;
+ t5 -= t0;
+ t6 = t1;
+ t7 = t3;
+ t8 = t4;
+ t9 = t5;
+ for ( k=0; k<l1; k++ ) {
+ for ( i=2; i<ido; i+=2 ) {
+ ic = idp2-i;
+ cc[i+t7-1] = ch[i+t8-1]+ch[i+t9-1];
+ cc[ic+t6-1] = ch[i+t8-1]-ch[i+t9-1];
+ cc[i+t7] = ch[i+t8]+ch[i+t9];
+ cc[ic+t6] = ch[i+t9]-ch[i+t8];
+ }
+ t6 += t10;
+ t7 += t10;
+ t8 += ido;
+ t9 += ido;
+ }
+ }
+ return;
+
+L141:
+
+ t1 = -ido;
+ t3 = 0;
+ t4 = 0;
+ t5 = ipp2*t0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t2;
+ t3 += t2;
+ t4 += t0;
+ t5 -= t0;
+ for ( i=2; i<ido; i+=2 ) {
+ t6 = idp2+t1-i;
+ t7 = i+t3;
+ t8 = i+t4;
+ t9 = i+t5;
+ for ( k=0; k<l1; k++ ) {
+ cc[t7-1] = ch[t8-1]+ch[t9-1];
+ cc[t6-1] = ch[t8-1]-ch[t9-1];
+ cc[t7] = ch[t8]+ch[t9];
+ cc[t6] = ch[t9]-ch[t8];
+ t6 += t10;
+ t7 += t10;
+ t8 += ido;
+ t9 += ido;
+ }
+ }
+ }
+}
+
+STIN void drftf1(int n, double *c, double *ch, double *wa, int *ifac)
+{
+ int i, k1, l1, l2;
+ int na, kh, nf;
+ int ip, iw, ido, idl1, ix2, ix3;
+
+ nf = ifac[1];
+ na = 1;
+ l2 = n;
+ iw = n;
+
+ for ( k1=0; k1<nf; k1++ ) {
+ kh = nf-k1;
+ ip = ifac[kh+1];
+ l1 = l2/ip;
+ ido = n/l2;
+ idl1 = ido*l1;
+ iw -= (ip-1)*ido;
+ na = 1-na;
+
+ if ( ip!=4 )
+ goto L102;
+
+ ix2 = iw+ido;
+ ix3 = ix2+ido;
+ if ( na != 0 )
+ dradf4(ido, l1, ch, c, wa+iw-1, wa+ix2-1, wa+ix3-1);
+ else
+ dradf4(ido, l1, c, ch, wa+iw-1, wa+ix2-1, wa+ix3-1);
+ goto L110;
+
+L102:
+ if ( ip != 2 )
+ goto L104;
+ if ( na != 0 )
+ goto L103;
+
+ dradf2(ido, l1, c, ch, wa+iw-1);
+ goto L110;
+
+L103:
+ dradf2(ido, l1, ch, c, wa+iw-1);
+ goto L110;
+
+L104:
+ if ( ido == 1 )
+ na = 1-na;
+ if ( na != 0 )
+ goto L109;
+
+ dradfg(ido, ip, l1, idl1, c, c, c, ch, ch, wa+iw-1);
+ na = 1;
+ goto L110;
+
+L109:
+ dradfg(ido, ip, l1, idl1, ch, ch, ch, c, c, wa+iw-1);
+ na = 0;
+
+L110:
+ l2=l1;
+ }
+
+ if ( na == 1 )
+ return;
+
+ for ( i=0; i<n; i++ )
+ c[i] = ch[i];
+}
+
+void __ogg_fdrfftf(int n, double *r, double *wsave, int *ifac)
+{
+ if ( n==1 )
+ return;
+ drftf1(n, r, wsave, wsave+n, ifac);
+}
+
+#ifdef LBBBBBBBBBBBBBBBB
+
+STIN void dcsqf1(int n, double *x, double *w, double *xh, int *ifac)
+{
+ int modn, i, k, kc;
+ int np2, ns2;
+ double xim1;
+
+ ns2 = (n+1)>>1;
+ np2 = n;
+
+ kc = np2;
+ for ( k=1; k<ns2; k++ ) {
+ kc--;
+ xh[k] = x[k]+x[kc];
+ xh[kc] = x[k]-x[kc];
+ }
+
+ modn = n%2;
+ if ( modn == 0 )
+ xh[ns2] = x[ns2]+x[ns2];
+
+ for ( k=1; k<ns2; k++ ) {
+ kc = np2-k;
+ x[k] = w[k-1]*xh[kc]+w[kc-1]*xh[k];
+ x[kc] = w[k-1]*xh[k]-w[kc-1]*xh[kc];
+ }
+
+ if ( modn == 0 )
+ x[ns2] = w[ns2-1]*xh[ns2];
+
+ __ogg_fdrfftf(n, x, xh, ifac);
+
+ for ( i=2; i<n; i+=2 ) {
+ xim1 = x[i-1]-x[i];
+ x[i] = x[i-1]+x[i];
+ x[i-1] = xim1;
+ }
+}
+
+void __ogg_fdcosqf(int n, double *x, double *wsave, int *ifac)
+{
+ static double sqrt2=1.4142135623730950488016887242097;
+ double tsqx;
+
+ switch(n) {
+ case 0:
+ case 1:
+ return;
+ case 2:
+ tsqx = sqrt2*x[1];
+ x[1] = x[0]-tsqx;
+ x[0] += tsqx;
+ return;
+ default:
+ dcsqf1(n, x, wsave, wsave+n, ifac);
+ return;
+ }
+}
+
+STIN void dradb2(int ido, int l1, double *cc, double *ch, double *wa1)
+{
+ int i, k, t0, t1, t2, t3, t4, t5, t6;
+ double ti2, tr2;
+
+ t0 = l1*ido;
+
+ t1 = 0;
+ t2 = 0;
+ t3 = (ido<<1)-1;
+ for ( k=0; k<l1; k++ ) {
+ ch[t1] = cc[t2]+cc[t3+t2];
+ ch[t1+t0] = cc[t2]-cc[t3+t2];
+ t2 = (t1 += ido)<<1;
+ }
+
+ if ( ido < 2 )
+ return;
+ if ( ido == 2 )
+ goto L105;
+
+ t1 = 0;
+ t2 = 0;
+ for ( k=0; k<l1; k++ ) {
+ t3 = t1;
+ t5 = (t4 = t2)+(ido<<1);
+ t6 = t0+t1;
+ for ( i=2; i<ido; i+=2 ) {
+ t3 += 2;
+ t4 += 2;
+ t5 -= 2;
+ t6 += 2;
+ ch[t3-1] = cc[t4-1]+cc[t5-1];
+ tr2 = cc[t4-1]-cc[t5-1];
+ ch[t3] = cc[t4]-cc[t5];
+ ti2 = cc[t4]+cc[t5];
+ ch[t6-1] = wa1[i-2]*tr2-wa1[i-1]*ti2;
+ ch[t6] = wa1[i-2]*ti2+wa1[i-1]*tr2;
+ }
+ t2 = (t1 += ido)<<1;
+ }
+
+ if ( ido%2 == 1 )
+ return;
+
+L105:
+ t1 = ido-1;
+ t2 = ido-1;
+ for ( k=0; k<l1; k++ ) {
+ ch[t1] = cc[t2]+cc[t2];
+ ch[t1+t0] = -(cc[t2+1]+cc[t2+1]);
+ t1 += ido;
+ t2 += ido<<1;
+ }
+}
+
+STIN void dradb3(int ido, int l1, double *cc, double *ch, double *wa1, double *wa2)
+{
+ static double taur = -.5;
+ static double taui = .86602540378443864676372317075293618;
+ int i, k, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10;
+ double ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2;
+
+ t0 = l1*ido;
+ t1 = 0;
+ t2 = t0<<1;
+ t3 = ido<<1;
+ t4 = ido+(ido<<1);
+ t5 = 0;
+ for ( k=0; k<l1; k++ ) {
+ tr2 = cc[t3-1]+cc[t3-1];
+ cr2 = cc[t5]+(taur*tr2);
+ ch[t1] = cc[t5]+tr2;
+ ci3 = taui*(cc[t3]+cc[t3]);
+ ch[t1+t0] = cr2-ci3;
+ ch[t1+t2] = cr2+ci3;
+ t1 += ido;
+ t3 += t4;
+ t5 += t4;
+ }
+
+ if ( ido == 1 )
+ return;
+
+ t1 = 0;
+ t3 = ido<<1;
+ for ( k=0; k<l1; k++ ) {
+ t7 = t1+(t1<<1);
+ t6 = (t5 = t7+t3);
+ t8 = t1;
+ t10 = (t9 = t1+t0)+t0;
+
+ for ( i=2; i<ido; i+=2 ) {
+ t5 += 2;
+ t6 -= 2;
+ t7 += 2;
+ t8 += 2;
+ t9 += 2;
+ t10 += 2;
+ tr2 = cc[t5-1]+cc[t6-1];
+ cr2 = cc[t7-1]+(taur*tr2);
+ ch[t8-1] = cc[t7-1]+tr2;
+ ti2 = cc[t5]-cc[t6];
+ ci2 = cc[t7]+(taur*ti2);
+ ch[t8] = cc[t7]+ti2;
+ cr3 = taui*(cc[t5-1]-cc[t6-1]);
+ ci3 = taui*(cc[t5]+cc[t6]);
+ dr2 = cr2-ci3;
+ dr3 = cr2+ci3;
+ di2 = ci2+cr3;
+ di3 = ci2-cr3;
+ ch[t9-1] = wa1[i-2]*dr2-wa1[i-1]*di2;
+ ch[t9] = wa1[i-2]*di2+wa1[i-1]*dr2;
+ ch[t10-1] = wa2[i-2]*dr3-wa2[i-1]*di3;
+ ch[t10] = wa2[i-2]*di3+wa2[i-1]*dr3;
+ }
+ t1+=ido;
+ }
+}
+
+STIN void dradb4(int ido, int l1, double *cc, double *ch, double *wa1,
+ double *wa2, double *wa3)
+{
+ static double sqrt2=1.4142135623730950488016887242097;
+ int i, k, t0, t1, t2, t3, t4, t5, t6, t7, t8;
+ double ci2, ci3, ci4, cr2, cr3, cr4;
+ double ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+
+ t0 = l1*ido;
+ t1 = 0;
+ t2 = ido<<2;
+ t3 = 0;
+ t6 = ido<<1;
+ for ( k=0; k<l1; k++ ) {
+ t4 = t3+t6;
+ t5 = t1;
+ tr3 = cc[t4-1]+cc[t4-1];
+ tr4 = cc[t4]+cc[t4];
+ tr1 = cc[t3]-cc[(t4+=t6)-1];
+ tr2 = cc[t3]+cc[t4-1];
+ ch[t5] = tr2+tr3;
+ ch[t5+=t0] = tr1-tr4;
+ ch[t5+=t0] = tr2-tr3;
+ ch[t5+=t0] = tr1+tr4;
+ t1 += ido;
+ t3 += t2;
+ }
+
+ if ( ido < 2 )
+ return;
+ if ( ido == 2 )
+ goto L105;
+
+ t1 = 0;
+ for ( k=0; k<l1; k++ ) {
+ t5 = (t4 = (t3 = (t2 = t1<<2)+t6))+t6;
+ t7 = t1;
+ for ( i=2; i<ido; i+=2 ) {
+ t2 += 2;
+ t3 += 2;
+ t4 -= 2;
+ t5 -= 2;
+ t7 += 2;
+ ti1 = cc[t2]+cc[t5];
+ ti2 = cc[t2]-cc[t5];
+ ti3 = cc[t3]-cc[t4];
+ tr4 = cc[t3]+cc[t4];
+ tr1 = cc[t2-1]-cc[t5-1];
+ tr2 = cc[t2-1]+cc[t5-1];
+ ti4 = cc[t3-1]-cc[t4-1];
+ tr3 = cc[t3-1]+cc[t4-1];
+ ch[t7-1] = tr2+tr3;
+ cr3 = tr2-tr3;
+ ch[t7] = ti2+ti3;
+ ci3 = ti2-ti3;
+ cr2 = tr1-tr4;
+ cr4 = tr1+tr4;
+ ci2 = ti1+ti4;
+ ci4 = ti1-ti4;
+
+ ch[(t8 = t7+t0)-1] = wa1[i-2]*cr2-wa1[i-1]*ci2;
+ ch[t8] = wa1[i-2]*ci2+wa1[i-1]*cr2;
+ ch[(t8 += t0)-1] = wa2[i-2]*cr3-wa2[i-1]*ci3;
+ ch[t8] = wa2[i-2]*ci3+wa2[i-1]*cr3;
+ ch[(t8 += t0)-1] = wa3[i-2]*cr4-wa3[i-1]*ci4;
+ ch[t8] = wa3[i-2]*ci4+wa3[i-1]*cr4;
+ }
+ t1 += ido;
+ }
+
+ if ( ido%2 == 1)
+ return;
+
+L105:
+
+ t1 = ido;
+ t2 = ido<<2;
+ t3 = ido-1;
+ t4 = ido+(ido<<1);
+ for ( k=0; k<l1; k++ ) {
+ t5 = t3;
+ ti1 = cc[t1]+cc[t4];
+ ti2 = cc[t4]-cc[t1];
+ tr1 = cc[t1-1]-cc[t4-1];
+ tr2 = cc[t1-1]+cc[t4-1];
+ ch[t5] = tr2+tr2;
+ ch[t5+=t0] = sqrt2*(tr1-ti1);
+ ch[t5+=t0] = ti2+ti2;
+ ch[t5+=t0] = -sqrt2*(tr1+ti1);
+
+ t3 += ido;
+ t1 += t2;
+ t4 += t2;
+ }
+}
+
+STIN void dradbg(int ido, int ip, int l1, int idl1, double *cc, double *c1,
+ double *c2, double *ch, double *ch2, double *wa)
+{
+ static double tpi=6.28318530717958647692528676655900577;
+ int idij, ipph, i, j, k, l, ik, is;
+ int t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12;
+ double dc2, ai1, ai2, ar1,ar2, ds2;
+ int nbd;
+ double dcp, arg, dsp, ar1h, ar2h;
+ int ipp2;
+
+ t10 = ip*ido;
+ t0 = l1*ido;
+ arg = tpi/(double)ip;
+ dcp = cos(arg);
+ dsp = sin(arg);
+ nbd = (ido-1)>>1;
+ ipp2 = ip;
+ ipph = (ip+1)>>1;
+ if ( ido < l1 )
+ goto L103;
+
+ t1 = 0;
+ t2 = 0;
+ for ( k=0; k<l1; k++ ) {
+ t3 = t1;
+ t4 = t2;
+ for ( i=0; i<ido; i++ ) {
+ ch[t3] = cc[t4];
+ t3++;
+ t4++;
+ }
+ t1 += ido;
+ t2 += t10;
+ }
+ goto L106;
+
+L103:
+ t1 = 0;
+ for ( i=0; i<ido; i++ ) {
+ t2 = t1;
+ t3 = t1;
+ for ( k=0; k<l1; k++ ) {
+ ch[t2] = cc[t3];
+ t2 += ido;
+ t3 += t10;
+ }
+ t1++;
+ }
+
+L106:
+ t1 = 0;
+ t2 = ipp2*t0;
+ t7 = (t5 = ido<<1);
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1;
+ t4 = t2;
+ t6 = t5;
+ for ( k=0; k<l1; k++ ) {
+ ch[t3] = cc[t6-1]+cc[t6-1];
+ ch[t4] = cc[t6]+cc[t6];
+ t3 += ido;
+ t4 += ido;
+ t6 += t10;
+ }
+ t5 += t7;
+ }
+
+ if (ido == 1)
+ goto L116;
+ if ( nbd < l1 )
+ goto L112;
+
+ t1 = 0;
+ t2 = ipp2*t0;
+ t7 = 0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1;
+ t4 = t2;
+
+ t7 += (ido<<1);
+ t8 = t7;
+ for ( k=0; k<l1; k++ ) {
+ t5 = t3;
+ t6 = t4;
+ t9 = t8;
+ t11 = t8;
+ for ( i=2; i<ido; i+=2 ) {
+ t5 += 2;
+ t6 += 2;
+ t9 += 2;
+ t11 -= 2;
+ ch[t5-1] = cc[t9-1]+cc[t11-1];
+ ch[t6-1] = cc[t9-1]-cc[t11-1];
+ ch[t5] = cc[t9]-cc[t11];
+ ch[t6] = cc[t9]+cc[t11];
+ }
+ t3 += ido;
+ t4 += ido;
+ t8 += t10;
+ }
+ }
+ goto L116;
+
+L112:
+ t1 = 0;
+ t2 = ipp2*t0;
+ t7 = 0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1;
+ t4 = t2;
+ t7 += (ido<<1);
+ t8 = t7;
+ t9 = t7;
+ for ( i=2; i<ido; i+=2 ) {
+ t3 += 2;
+ t4 += 2;
+ t8 += 2;
+ t9 -= 2;
+ t5 = t3;
+ t6 = t4;
+ t11 = t8;
+ t12 = t9;
+ for ( k=0; k<l1; k++ ) {
+ ch[t5-1] = cc[t11-1]+cc[t12-1];
+ ch[t6-1] = cc[t11-1]-cc[t12-1];
+ ch[t5] = cc[t11]-cc[t12];
+ ch[t6] = cc[t11]+cc[t12];
+ t5 += ido;
+ t6 += ido;
+ t11 += t10;
+ t12 += t10;
+ }
+ }
+ }
+
+L116:
+ ar1 = 1.0;
+ ai1 = 0.0;
+ t1 = 0;
+ t9 = (t2 = ipp2*idl1);
+ t3 = (ip-1)*idl1;
+ for ( l=1; l<ipph; l++ ) {
+ t1 += idl1;
+ t2 -= idl1;
+
+ ar1h = dcp*ar1-dsp*ai1;
+ ai1 = dcp*ai1+dsp*ar1;
+ ar1 = ar1h;
+ t4 = t1;
+ t5 = t2;
+ t6 = 0;
+ t7 = idl1;
+ t8 = t3;
+ for ( ik=0; ik<idl1; ik++ ) {
+ c2[t4++] = ch2[t6++]+ar1*ch2[t7++];
+ c2[t5++] = ai1*ch2[t8++];
+ }
+ dc2 = ar1;
+ ds2 = ai1;
+ ar2 = ar1;
+ ai2 = ai1;
+
+ t6 = idl1;
+ t7 = t9-idl1;
+ for ( j=2; j<ipph; j++ ) {
+ t6 += idl1;
+ t7 -= idl1;
+ ar2h = dc2*ar2-ds2*ai2;
+ ai2 = dc2*ai2+ds2*ar2;
+ ar2 = ar2h;
+ t4 = t1;
+ t5 = t2;
+ t11 = t6;
+ t12 = t7;
+ for ( ik=0; ik<idl1; ik++ ) {
+ c2[t4++] += ar2*ch2[t11++];
+ c2[t5++] += ai2*ch2[t12++];
+ }
+ }
+ }
+
+ t1 = 0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += idl1;
+ t2 = t1;
+ for ( ik=0; ik<idl1; ik++ )
+ ch2[ik] += ch2[t2++];
+ }
+
+ t1 = 0;
+ t2 = ipp2*t0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1;
+ t4 = t2;
+ for ( k=0; k<l1; k++ ) {
+ ch[t3] = c1[t3]-c1[t4];
+ ch[t4] = c1[t3]+c1[t4];
+ t3 += ido;
+ t4 += ido;
+ }
+ }
+
+ if ( ido == 1 )
+ goto L132;
+ if ( nbd < l1 )
+ goto L128;
+
+ t1 = 0;
+ t2 = ipp2*t0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1;
+ t4 = t2;
+ for ( k=0; k<l1; k++ ) {
+ t5 = t3;
+ t6 = t4;
+ for ( i=2; i<ido; i+=2 ) {
+ t5 += 2;
+ t6 += 2;
+ ch[t5-1] = c1[t5-1]-c1[t6];
+ ch[t6-1] = c1[t5-1]+c1[t6];
+ ch[t5] = c1[t5]+c1[t6-1];
+ ch[t6] = c1[t5]-c1[t6-1];
+ }
+ t3 += ido;
+ t4 += ido;
+ }
+ }
+ goto L132;
+
+L128:
+ t1 = 0;
+ t2 = ipp2*t0;
+ for ( j=1; j<ipph; j++ ) {
+ t1 += t0;
+ t2 -= t0;
+ t3 = t1;
+ t4 = t2;
+ for ( i=2; i<ido; i+=2 ) {
+ t3 += 2;
+ t4 += 2;
+ t5 = t3;
+ t6 = t4;
+ for ( k=0; k<l1; k++ ) {
+ ch[t5-1] = c1[t5-1]-c1[t6];
+ ch[t6-1] = c1[t5-1]+c1[t6];
+ ch[t5] = c1[t5]+c1[t6-1];
+ ch[t6] = c1[t5]-c1[t6-1];
+ t5 += ido;
+ t6 += ido;
+ }
+ }
+ }
+
+L132:
+ if ( ido == 1 )
+ return;
+
+ for ( ik=0; ik<idl1; ik++ )
+ c2[ik] = ch2[ik];
+
+ t1 = 0;
+ for ( j=1; j<ip; j++ ) {
+ t2 = (t1 += t0);
+ for ( k=0; k<l1; k++ ) {
+ c1[t2] = ch[t2];
+ t2 += ido;
+ }
+ }
+
+ if ( nbd > l1 )
+ goto L139;
+
+ is = -ido-1;
+ t1 =0;
+ for ( j=1; j<ip; j++ ) {
+ is += ido;
+ t1 += t0;
+ idij = is;
+ t2 = t1;
+ for ( i=2; i<ido; i+=2 ) {
+ t2 += 2;
+ idij += 2;
+ t3 = t2;
+ for ( k=0; k<l1; k++ ) {
+ c1[t3-1] = wa[idij-1]*ch[t3-1]-wa[idij]*ch[t3];
+ c1[t3] = wa[idij-1]*ch[t3]+wa[idij]*ch[t3-1];
+ t3 += ido;
+ }
+ }
+ }
+ return;
+
+L139:
+ is = -ido-1;
+ t1 = 0;
+ for ( j=1; j<ip; j++ ) {
+ is += ido;
+ t1 += t0;
+ t2 = t1;
+ for ( k=0; k<l1; k++ ) {
+ idij = is;
+ t3 = t2;
+ for ( i=2; i<ido; i+=2 ) {
+ idij += 2;
+ t3 += 2;
+ c1[t3-1] = wa[idij-1]*ch[t3-1]-wa[idij]*ch[t3];
+ c1[t3] = wa[idij-1]*ch[t3]+wa[idij]*ch[t3-1];
+ }
+ t2 += ido;
+ }
+ }
+}
+
+STIN void drftb1(int n, double *c, double *ch, double *wa, int *ifac)
+{
+ int i, k1, l1, l2;
+ int na;
+ int nf, ip, iw, ix2, ix3, ido, idl1;
+
+ nf = ifac[1];
+ na = 0;
+ l1 = 1;
+ iw = 1;
+
+ for ( k1=0; k1<nf; k1++ ) {
+ ip = ifac[k1 + 2];
+ l2 = ip*l1;
+ ido = n/l2;
+ idl1 = ido*l1;
+ if ( ip != 4 )
+ goto L103;
+ ix2 = iw+ido;
+ ix3 = ix2+ido;
+
+ if ( na != 0 )
+ dradb4(ido, l1, ch, c, wa+iw-1, wa+ix2-1, wa+ix3-1);
+ else
+ dradb4(ido, l1, c, ch, wa+iw-1, wa+ix2-1, wa+ix3-1);
+ na = 1-na;
+ goto L115;
+
+L103:
+ if ( ip != 2 )
+ goto L106;
+
+ if ( na != 0 )
+ dradb2(ido, l1, ch, c, wa+iw-1);
+ else
+ dradb2(ido, l1, c, ch, wa+iw-1);
+ na = 1-na;
+ goto L115;
+
+L106:
+ if ( ip != 3 )
+ goto L109;
+
+ ix2 = iw+ido;
+ if ( na != 0 )
+ dradb3(ido, l1, ch, c, wa+iw-1, wa+ix2-1);
+ else
+ dradb3(ido, l1, c, ch, wa+iw-1, wa+ix2-1);
+ na = 1-na;
+ goto L115;
+
+L109:
+ /* The radix five case can be translated later..... */
+ /* if(ip!=5)goto L112;
+
+ ix2=iw+ido;
+ ix3=ix2+ido;
+ ix4=ix3+ido;
+ if ( na != 0 )
+ dradb5(ido,l1,ch,c,wa+iw-1,wa+ix2-1,wa+ix3-1,wa+ix4-1);
+ else
+ dradb5(ido,l1,c,ch,wa+iw-1,wa+ix2-1,wa+ix3-1,wa+ix4-1);
+ na=1-na;
+ goto L115;
+
+L112:*/
+ if ( na != 0 )
+ dradbg(ido, ip, l1, idl1, ch, ch, ch, c, c, wa+iw-1);
+ else
+ dradbg(ido, ip, l1, idl1, c, c, c, ch, ch, wa+iw-1);
+ if ( ido == 1 )
+ na = 1-na;
+
+L115:
+ l1 = l2;
+ iw += (ip-1)*ido;
+ }
+
+ if ( na == 0 )
+ return;
+
+ for ( i=0; i<n; i++ )
+ c[i] = ch[i];
+}
+
+void __ogg_fdrfftb(int n, double *r, double *wsave, int *ifac)
+{
+ if ( n == 1 )
+ return;
+ drftb1(n, r, wsave, wsave+n, ifac);
+}
+
+STIN void dcsqb1(int n, double *x, double *w, double *xh, int *ifac)
+{
+ int modn, i, k, kc;
+ int np2, ns2;
+ double xim1;
+
+ ns2 = (n+1)>>1;
+ np2 = n;
+
+ for ( i=2; i<n; i+=2 ) {
+ xim1 = x[i-1]+x[i];
+ x[i] -= x[i-1];
+ x[i-1] = xim1;
+ }
+
+ x[0] += x[0];
+ modn = n%2;
+ if ( modn == 0 )
+ x[n-1] += x[n-1];
+
+ __ogg_fdrfftb(n, x, xh, ifac);
+
+ kc = np2;
+ for ( k=1; k<ns2; k++ ) {
+ kc--;
+ xh[k] = w[k-1]*x[kc]+w[kc-1]*x[k];
+ xh[kc] = w[k-1]*x[k]-w[kc-1]*x[kc];
+ }
+
+ if ( modn == 0 )
+ x[ns2] = w[ns2-1]*(x[ns2]+x[ns2]);
+
+ kc = np2;
+ for ( k=1; k<ns2; k++ ) {
+ kc--;
+ x[k] = xh[k]+xh[kc];
+ x[kc] = xh[k]-xh[kc];
+ }
+ x[0] += x[0];
+}
+
+void __ogg_fdcosqb(int n, double *x, double *wsave, int *ifac)
+{
+ static double tsqrt2 = 2.8284271247461900976033774484194;
+ double x1;
+
+ if ( n < 2 ) {
+ x[0] *= 4;
+ return;
+ }
+ if ( n == 2 ) {
+ x1 = (x[0]+x[1])*4;
+ x[1] = tsqrt2*(x[0]-x[1]);
+ x[0] = x1;
+ return;
+ }
+
+ dcsqb1(n, x, wsave, wsave+n, ifac);
+}
+#endif \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/discreteFourierTransform.c b/decoders/session_flags/mesa_sts/src/discreteFourierTransform.c
new file mode 100644
index 0000000..397ceec
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/discreteFourierTransform.c
@@ -0,0 +1,60 @@
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ D I S C R E T E F O U R I E R T R A N S F O R M T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+void __ogg_fdrffti(int n, double *wsave, int *ifac);
+void __ogg_fdrfftf(int n, double *X, double *wsave, int *ifac);
+
+int
+DiscreteFourierTransform(int n, BitSequence *epsilon)
+{
+ double p_value, upperBound, percentile, N_l, N_o, d, *m, *X, *wsave;
+ int i, count, ifac[15];
+
+ if ( ((X = (double*) calloc(n,sizeof(double))) == NULL) ||
+ ((wsave = (double *)calloc(2*n,sizeof(double))) == NULL) ||
+ ((m = (double*)calloc(n/2+1, sizeof(double))) == NULL) ) {
+ if( X != NULL )
+ free(X);
+ if( wsave != NULL )
+ free(wsave);
+ if( m != NULL )
+ free(m);
+ return 0;
+ }
+ for ( i=0; i<n; i++ )
+ X[i] = 2*(int)epsilon[i] - 1;
+
+ __ogg_fdrffti(n, wsave, ifac); /* INITIALIZE WORK ARRAYS */
+ __ogg_fdrfftf(n, X, wsave, ifac); /* APPLY FORWARD FFT */
+
+ m[0] = sqrt(X[0]*X[0]); /* COMPUTE MAGNITUDE */
+
+ for ( i=0; i<n/2; i++ )
+ m[i+1] = sqrt(pow(X[2*i+1],2)+pow(X[2*i+2],2));
+ count = 0; /* CONFIDENCE INTERVAL */
+ upperBound = sqrt(2.995732274*n);
+ for ( i=0; i<n/2; i++ )
+ if ( m[i] < upperBound )
+ count++;
+ percentile = (double)count/(n/2)*100;
+ N_l = (double) count; /* number of peaks less than h = sqrt(3*n) */
+ N_o = (double) 0.95*n/2.0;
+ d = (N_l - N_o)/sqrt(n/4.0*0.95*0.05);
+ p_value = erfc(fabs(d)/sqrt(2.0));
+
+ free(X);
+ free(wsave);
+ free(m);
+
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/decoders/session_flags/mesa_sts/src/frequency.c b/decoders/session_flags/mesa_sts/src/frequency.c
new file mode 100644
index 0000000..8950e3a
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/frequency.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "include/externs.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ F R E Q U E N C Y T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+Frequency(int n, BitSequence *epsilon)
+{
+ int i;
+ double f, s_obs, p_value, sum, sqrt2 = 1.41421356237309504880;
+
+ sum = 0.0;
+ for ( i=0; i<n; i++ )
+ sum += 2*(int)epsilon[i]-1;
+ s_obs = fabs(sum)/sqrt(n);
+ f = s_obs/sqrt2;
+ p_value = erfc(f);
+
+// fprintf(stats[TEST_FREQUENCY], "\t\t\t FREQUENCY TEST\n");
+// fprintf(stats[TEST_FREQUENCY], "\t\t---------------------------------------------\n");
+// fprintf(stats[TEST_FREQUENCY], "\t\tCOMPUTATIONAL INFORMATION:\n");
+// fprintf(stats[TEST_FREQUENCY], "\t\t---------------------------------------------\n");
+// fprintf(stats[TEST_FREQUENCY], "\t\t(a) The nth partial sum = %d\n", (int)sum);
+// fprintf(stats[TEST_FREQUENCY], "\t\t(b) S_n/n = %f\n", sum/n);
+// fprintf(stats[TEST_FREQUENCY], "\t\t---------------------------------------------\n");
+
+// fprintf(stats[TEST_FREQUENCY], "%s\t\tp_value = %f\n\n", p_value < ALPHA ? "FAILURE" : "SUCCESS", p_value); fflush(stats[TEST_FREQUENCY]);
+// fprintf(results[TEST_FREQUENCY], "%f\n", p_value); fflush(results[TEST_FREQUENCY]);
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/decoders/session_flags/mesa_sts/src/genutils.c b/decoders/session_flags/mesa_sts/src/genutils.c
new file mode 100644
index 0000000..df7ddc2
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/genutils.c
@@ -0,0 +1,667 @@
+/*
+ * file: mp.c
+ *
+ * DESCRIPTION
+ *
+ * These functions comprise a multi-precision integer arithmetic
+ * and discrete function package.
+ */
+
+#include "include/genutils.h"
+
+#define MAXPLEN 384
+
+
+/*****************************************
+** greater - Test if x > y *
+** *
+** Returns TRUE (1) if x greater than y, *
+** otherwise FALSE (0). *
+** *
+** Parameters: *
+** *
+** x Address of array x *
+** y Address of array y *
+** l Length both x and y in bytes *
+** *
+******************************************/
+int greater(BYTE *x, BYTE *y, int l)
+{
+ int i;
+
+ for ( i=0; i<l; i++ )
+ if ( x[i] != y[i] )
+ break;
+
+ if ( i == l )
+ return 0;
+
+ if ( x[i] > y[i] )
+ return 1;
+
+ return 0;
+}
+
+
+/*****************************************
+** less - Test if x < y *
+** *
+** Returns TRUE (1) if x less than y, *
+** otherwise FALSE (0). *
+** *
+** Parameters: *
+** *
+** x Address of array x *
+** y Address of array y *
+** l Length both x and y in bytes *
+** *
+******************************************/
+int less(BYTE *x, BYTE *y, int l)
+{
+ int i;
+
+ for ( i=0; i<l; i++ )
+ if ( x[i] != y[i] )
+ break;
+
+ if ( i == l ) {
+ return 0;
+ }
+
+ if ( x[i] < y[i] ) {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*****************************************
+** bshl - shifts array left *
+** by one bit. *
+** *
+** x = x * 2 *
+** *
+** Parameters: *
+** *
+** x Address of array x *
+** l Length array x in bytes *
+** *
+******************************************/
+BYTE bshl(BYTE *x, int l)
+{
+ BYTE *p;
+ int c1, c2;
+
+ p = x + l - 1;
+ c1 = 0;
+ c2 = 0;
+ while ( p != x ) {
+ if ( *p & 0x80 )
+ c2 = 1;
+ *p <<= 1; /* shift the word left once (ls bit = 0) */
+ if ( c1 )
+ *p |= 1;
+ c1 = c2;
+ c2 = 0;
+ p--;
+ }
+
+ if ( *p & 0x80 )
+ c2 = 1;
+ *p <<= 1; /* shift the word left once (ls bit = 0) */
+ if ( c1 )
+ *p |= (DIGIT)1;
+
+ return (BYTE)c2;
+}
+
+
+/*****************************************
+** bshr - shifts array right *
+** by one bit. *
+** *
+** x = x / 2 *
+** *
+** Parameters: *
+** *
+** x Address of array x *
+** l Length array x in bytes *
+** *
+******************************************/
+void bshr(BYTE *x, int l)
+{
+ BYTE *p;
+ int c1,c2;
+
+ p = x;
+ c1 = 0;
+ c2 = 0;
+ while ( p != x+l-1 ) {
+ if ( *p & 0x01 )
+ c2 = 1;
+ *p >>= 1; /* shift the word right once (ms bit = 0) */
+ if ( c1 )
+ *p |= 0x80;
+ c1 = c2;
+ c2 = 0;
+ p++;
+ }
+
+ *p >>= 1; /* shift the word right once (ms bit = 0) */
+ if ( c1 )
+ *p |= 0x80;
+}
+
+
+/*****************************************
+** Mult - Multiply two integers *
+** *
+** A = B * C *
+** *
+** Parameters: *
+** *
+** A Address of the result *
+** B Address of the multiplier *
+** C Address of the multiplicand *
+** LB Length of B in bytes *
+** LC Length of C in bytes *
+** *
+** NOTE: A MUST be LB+LC in length *
+** *
+******************************************/
+int Mult(BYTE *A, BYTE *B, int LB, BYTE *C, int LC)
+{
+ int i, j, k, LA;
+ DIGIT result;
+
+ LA = LB + LC;
+
+ for ( i=LB-1; i>=0; i-- ) {
+ result = 0;
+ for ( j=LC-1; j>=0; j-- ) {
+ k = i+j+1;
+ result = (DIGIT)A[k] + ((DIGIT)(B[i] * C[j])) + (result >> 8);
+ A[k] = (BYTE)result;
+ }
+ A[--k] = (BYTE)(result >> 8);
+ }
+
+ return 0;
+}
+
+
+void ModSqr(BYTE *A, BYTE *B, int LB, BYTE *M, int LM)
+{
+
+ Square(A, B, LB);
+ Mod(A, 2*LB, M, LM);
+}
+
+void ModMult(BYTE *A, BYTE *B, int LB, BYTE *C, int LC, BYTE *M, int LM)
+{
+ Mult(A, B, LB, C, LC);
+ Mod(A, (LB+LC), M, LM);
+}
+
+
+/*****************************************
+** smult - Multiply array by a scalar. *
+** *
+** A = b * C *
+** *
+** Parameters: *
+** *
+** A Address of the result *
+** b Scalar (1 BYTE) *
+** C Address of the multiplicand *
+** L Length of C in bytes *
+** *
+** NOTE: A MUST be L+1 in length *
+** *
+******************************************/
+void smult(BYTE *A, BYTE b, BYTE *C, int L)
+{
+ int i;
+ DIGIT result;
+
+ result = 0;
+ for ( i=L-1; i>0; i-- ) {
+ result = A[i] + ((DIGIT)b * C[i]) + (result >> 8);
+ A[i] = (BYTE)(result & 0xff);
+ A[i-1] = (BYTE)(result >> 8);
+ }
+}
+
+/*****************************************
+** Square() - Square an integer *
+** *
+** A = B^2 *
+** *
+** Parameters: *
+** *
+** A Address of the result *
+** B Address of the operand *
+** L Length of B in bytes *
+** *
+** NOTE: A MUST be 2*L in length *
+** *
+******************************************/
+void Square(BYTE *A, BYTE *B, int L)
+{
+ Mult(A, B, L, B, L);
+}
+
+/*****************************************
+** ModExp - Modular Exponentiation *
+** *
+** A = B ** C (MOD M) *
+** *
+** Parameters: *
+** *
+** A Address of result *
+** B Address of mantissa *
+** C Address of exponent *
+** M Address of modulus *
+** LB Length of B in bytes *
+** LC Length of C in bytes *
+** LM Length of M in bytes *
+** *
+** NOTE: The integer B must be less *
+** than the modulus M. *
+** NOTE: A must be at least 3*LM *
+** bytes long. However, the *
+** result stored in A will be *
+** only LM bytes long. *
+******************************************/
+void ModExp(BYTE *A, BYTE *B, int LB, BYTE *C, int LC, BYTE *M, int LM)
+{
+ BYTE wmask;
+ int bits;
+
+ bits = LC*8;
+ wmask = 0x80;
+
+ A[LM-1] = 1;
+
+ while ( !sniff_bit(C,wmask) ) {
+ wmask >>= 1;
+ bits--;
+ if ( !wmask ) {
+ wmask = 0x80;
+ C++;
+ }
+ }
+
+ while ( bits-- ) {
+ memset(A+LM, 0x00, LM*2);
+
+ /* temp = A*A (MOD M) */
+ ModSqr(A+LM, A,LM, M,LM);
+
+ /* A = lower L bytes of temp */
+ memcpy(A, A+LM*2, LM);
+ memset(A+LM, 0x00, 2*LM);
+
+ if ( sniff_bit(C,wmask) ) {
+ memset(A+LM, 0x00, (LM+LB));
+ ModMult(A+LM, B,LB, A,LM, M,LM); /* temp = B * A (MOD M) */
+ memcpy(A, A+LM+(LM+LB)-LM, LM); /* A = lower LM bytes of temp */
+ memset(A+LM, 0x00, 2*LM);
+ }
+
+ wmask >>= 1;
+ if ( !wmask ) {
+ wmask = 0x80;
+ C++;
+ }
+ }
+}
+
+
+/* DivMod:
+ *
+ * computes:
+ * quot = x / n
+ * rem = x % n
+ * returns:
+ * length of "quot"
+ *
+ * len of rem is lenx+1
+ */
+int DivMod(BYTE *x, int lenx, BYTE *n, int lenn, BYTE *quot, BYTE *rem)
+{
+ BYTE *tx, *tn, *ttx, *ts, bmult[1];
+ int i, shift, lgth_x, lgth_n, t_len, lenq;
+ DIGIT tMSn, mult;
+ unsigned long tMSx;
+ int underflow;
+
+ tx = x;
+ tn = n;
+
+ /* point to the MSD of n */
+ for ( i=0, lgth_n=lenn; i<lenn; i++, lgth_n-- ) {
+ if ( *tn )
+ break;
+ tn++;
+ }
+ if ( !lgth_n )
+ return 0;
+
+ /* point to the MSD of x */
+ for ( i=0, lgth_x=lenx; i<lenx; i++, lgth_x-- ) {
+ if ( *tx )
+ break;
+ tx++;
+ }
+ if ( !lgth_x )
+ return 0;
+
+ if ( lgth_x < lgth_n )
+ lenq = 1;
+ else
+ lenq = lgth_x - lgth_n + 1;
+ memset(quot, 0x00, lenq);
+
+ /* Loop while x > n, WATCH OUT if lgth_x == lgth_n */
+ while ( (lgth_x > lgth_n) || ((lgth_x == lgth_n) && !less(tx, tn, lgth_n)) ) {
+ shift = 1;
+ if ( lgth_n == 1 ) {
+ if ( *tx < *tn ) {
+ tMSx = (DIGIT) (((*tx) << 8) | *(tx+1));
+ tMSn = *tn;
+ shift = 0;
+ }
+ else {
+ tMSx = *tx;
+ tMSn = *tn;
+ }
+ }
+ else if ( lgth_n > 1 ) {
+ tMSx = (DIGIT) (((*tx) << 8) | *(tx+1));
+ tMSn = (DIGIT) (((*tn) << 8) | *(tn+1));
+ if ( (tMSx < tMSn) || ((tMSx == tMSn) && less(tx, tn, lgth_n)) ) {
+ tMSx = (tMSx << 8) | *(tx+2);
+ shift = 0;
+ }
+ }
+ else {
+ tMSx = (DIGIT) (((*tx) << 8) | *(tx+1));
+ tMSn = *tn;
+ shift = 0;
+ }
+
+ mult = (DIGIT) (tMSx / tMSn);
+ if ( mult > 0xff )
+ mult = 0xff;
+ bmult[0] = mult & 0xff;
+
+ ts = rem;
+ do {
+ memset(ts, 0x00, lgth_x+1);
+ Mult(ts, tn, lgth_n, bmult, 1);
+
+ underflow = 0;
+ if ( shift ) {
+ if ( ts[0] != 0 )
+ underflow = 1;
+ else {
+ for ( i=0; i<lgth_x; i++ )
+ ts[i] = ts[i+1];
+ ts[lgth_x] = 0x00;
+ }
+ }
+ if ( greater(ts, tx, lgth_x) || underflow ) {
+ bmult[0]--;
+ underflow = 1;
+ }
+ else
+ underflow = 0;
+ } while ( underflow );
+ sub(tx, lgth_x, ts, lgth_x);
+ if ( shift )
+ quot[lenq - (lgth_x - lgth_n) - 1] = bmult[0];
+ else
+ quot[lenq - (lgth_x - lgth_n)] = bmult[0];
+
+ ttx = tx;
+ t_len = lgth_x;
+ for ( i=0, lgth_x=t_len; i<t_len; i++, lgth_x-- ) {
+ if ( *ttx )
+ break;
+ ttx++;
+ }
+ tx = ttx;
+ }
+ memset(rem, 0x00, lenn);
+ if ( lgth_x )
+ memcpy(rem+lenn-lgth_x, tx, lgth_x);
+
+ return lenq;
+}
+
+
+/*
+ * Mod - Computes an integer modulo another integer
+ *
+ * x = x (mod n)
+ *
+ */
+void Mod(BYTE *x, int lenx, BYTE *n, int lenn)
+{
+ BYTE quot[MAXPLEN+1], rem[2*MAXPLEN+1];
+
+ memset(quot, 0x00, sizeof(quot));
+ memset(rem, 0x00, sizeof(rem));
+ if ( DivMod(x, lenx, n, lenn, quot, rem) ) {
+ memset(x, 0x00, lenx);
+ memcpy(x+lenx-lenn, rem, lenn);
+ }
+}
+
+/*
+ * Div - Computes the integer division of two numbers
+ *
+ * x = x / n
+ *
+ */
+void Div(BYTE *x, int lenx, BYTE *n, int lenn)
+{
+ BYTE quot[MAXPLEN+1], rem[2*MAXPLEN+1];
+ int lenq;
+
+ memset(quot, 0x00, sizeof(quot));
+ memset(rem, 0x00, sizeof(rem));
+ if ( (lenq = DivMod(x, lenx, n, lenn, quot, rem)) != 0 ) {
+ memset(x, 0x00, lenx);
+ memcpy(x+lenx-lenq, quot, lenq);
+ }
+}
+
+
+/*****************************************
+** sub - Subtract two integers *
+** *
+** A = A - B *
+** *
+** *
+** Parameters: *
+** *
+** A Address of subtrahend integer *
+** B Address of subtractor integer *
+** L Length of A and B in bytes *
+** *
+** NOTE: In order to save RAM, B is *
+** two's complemented twice, *
+** rather than using a copy of B *
+** *
+******************************************/
+void sub(BYTE *A, int LA, BYTE *B, int LB)
+{
+ BYTE *tb;
+
+ tb = (BYTE *)calloc(LA, 1);
+ memcpy(tb, B, LB);
+ negate(tb, LB);
+ add(A, LA, tb, LA);
+
+ FREE(tb);
+}
+
+
+/*****************************************
+** negate - Negate an integer *
+** *
+** A = -A *
+** *
+** *
+** Parameters: *
+** *
+** A Address of integer to negate *
+** L Length of A in bytes *
+** *
+******************************************/
+int negate(BYTE *A, int L)
+{
+ int i, tL;
+ DIGIT accum;
+
+ /* Take one's complement of A */
+ for ( i=0; i<L; i++ )
+ A[i] = ~(A[i]);
+
+ /* Add one to get two's complement of A */
+ accum = 1;
+ tL = L-1;
+ while ( accum && (tL >= 0) ) {
+ accum += A[tL];
+ A[tL--] = (BYTE)(accum & 0xff);
+ accum = accum >> 8;
+ }
+
+ return accum;
+}
+
+
+/*
+ * add()
+ *
+ * A = A + B
+ *
+ * LB must be <= LA
+ *
+ */
+BYTE add(BYTE *A, int LA, BYTE *B, int LB)
+{
+ int i, indexA, indexB;
+ DIGIT accum;
+
+ indexA = LA - 1; /* LSD of result */
+ indexB = LB - 1; /* LSD of B */
+
+ accum = 0;
+ for ( i = 0; i < LB; i++ ) {
+ accum += A[indexA];
+ accum += B[indexB--];
+ A[indexA--] = (BYTE)(accum & 0xff);
+ accum = accum >> 8;
+ }
+
+ if ( LA > LB )
+ while ( accum && (indexA >= 0) ) {
+ accum += A[indexA];
+ A[indexA--] = (BYTE)(accum & 0xff);
+ accum = accum >> 8;
+ }
+
+ return (BYTE)accum;
+}
+
+
+void prettyprintBstr(char *S, BYTE *A, int L)
+{
+ int i, extra, ctrb, ctrl;
+
+ if ( L == 0 )
+ printf("%s <empty>", S);
+ else
+ printf("%s\n\t", S);
+ extra = L % 24;
+ if ( extra ) {
+ ctrb = 0;
+ for ( i=0; i<24-extra; i++ ) {
+ printf(" ");
+ if ( ++ctrb == 4) {
+ printf(" ");
+ ctrb = 0;
+ }
+ }
+
+ for ( i=0; i<extra; i++ ) {
+ printf("%02X", A[i]);
+ if ( ++ctrb == 4) {
+ printf(" ");
+ ctrb = 0;
+ }
+ }
+ printf("\n\t");
+ }
+
+ ctrb = ctrl = 0;
+ for ( i=extra; i<L; i++ ) {
+ printf("%02X", A[i]);
+ if ( ++ctrb == 4) {
+ ctrl++;
+ if ( ctrl == 6 ) {
+ printf("\n\t");
+ ctrl = 0;
+ }
+ else
+ printf(" ");
+ ctrb = 0;
+ }
+ }
+ printf("\n\n");
+}
+
+
+/**********************************************************************/
+/* Performs byte reverse for PC based implementation (little endian) */
+/**********************************************************************/
+void byteReverse(unsigned long *buffer, int byteCount)
+{
+ unsigned long value;
+ int count;
+
+ byteCount /= sizeof( unsigned long );
+ for( count = 0; count < byteCount; count++ ) {
+ value = ( buffer[ count ] << 16 ) | ( buffer[ count ] >> 16 );
+ buffer[ count ] = ( ( value & 0xFF00FF00L ) >> 8 ) | ( ( value & 0x00FF00FFL ) << 8 );
+ }
+}
+
+void
+ahtopb (char *ascii_hex, BYTE *p_binary, int bin_len)
+{
+ BYTE nibble;
+ int i;
+
+ for ( i=0; i<bin_len; i++ ) {
+ nibble = ascii_hex[i * 2];
+ if ( nibble > 'F' )
+ nibble -= 0x20;
+ if ( nibble > '9' )
+ nibble -= 7;
+ nibble -= '0';
+ p_binary[i] = nibble << 4;
+
+ nibble = ascii_hex[i * 2 + 1];
+ if ( nibble > 'F' )
+ nibble -= 0x20;
+ if ( nibble > '9' )
+ nibble -= 7;
+ nibble -= '0';
+ p_binary[i] += nibble;
+ }
+} \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/include/cephes.h b/decoders/session_flags/mesa_sts/src/include/cephes.h
new file mode 100644
index 0000000..64c2b1f
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/cephes.h
@@ -0,0 +1,13 @@
+#ifndef _NIST_CEPHES_H_
+#define _NIST_CEPHES_H_
+
+double cephes_igamc(double a, double x);
+double cephes_igam(double a, double x);
+double cephes_lgam(double x);
+double cephes_p1evl(double x, double *coef, int N);
+double cephes_polevl(double x, double *coef, int N);
+double cephes_erf(double x);
+double cephes_erfc(double x);
+double cephes_normal(double x);
+
+#endif /* _CEPHES_H_ */
diff --git a/decoders/session_flags/mesa_sts/src/include/config.h b/decoders/session_flags/mesa_sts/src/include/config.h
new file mode 100644
index 0000000..f31f50f
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/config.h
@@ -0,0 +1,52 @@
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef _NIST_CONFIG_H_
+#define _NIST_CONFIG_H_
+
+#define WINDOWS32
+//#define PROTOTYPES
+//#define LITTLE_ENDIAN
+//#define LOWHI
+
+/*
+ * AUTO DEFINES (DON'T TOUCH!)
+ */
+
+#ifndef CSTRTD
+typedef char *CSTRTD;
+#endif
+#ifndef BSTRTD
+typedef unsigned char *BSTRTD;
+#endif
+
+#ifndef BYTE
+typedef unsigned char BYTE;
+#endif
+#ifndef UINT
+typedef unsigned int UINT;
+#endif
+#ifndef USHORT
+typedef unsigned short USHORT;
+#endif
+//#ifndef ULONG
+//typedef unsigned long ULONG;
+//#endif
+#ifndef DIGIT
+typedef USHORT DIGIT; /* 16-bit word */
+#endif
+#ifndef DBLWORD
+typedef unsigned long DBLWORD; /* 32-bit word */
+#endif
+
+#ifndef WORD64
+typedef unsigned long WORD64[2]; /* 64-bit word */
+#endif
+
+#endif /* _CONFIG_H_ */
+
+#if defined(__cplusplus)
+}
+#endif \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/include/decls.h b/decoders/session_flags/mesa_sts/src/include/decls.h
new file mode 100644
index 0000000..510ae47
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/decls.h
@@ -0,0 +1,24 @@
+#ifndef _NIST_DECLS_H_
+#define _NIST_DECLS_H_
+
+#include <stdio.h>
+#include "defs.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ G L O B A L D A T A S T R U C T U R E S
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+BitSequence *epsilon; // BIT STREAM
+TP tp; // TEST PARAMETER STRUCTURE
+FILE *stats[NUMOFTESTS+1]; // FILE OUTPUT STREAM
+FILE *results[NUMOFTESTS+1]; // FILE OUTPUT STREAM
+FILE *freqfp; // FILE OUTPUT STREAM
+FILE *summary; // FILE OUTPUT STREAM
+int testVector[NUMOFTESTS+1];
+
+char generatorDir[NUMOFGENERATORS][20] = { "AlgorithmTesting", "LCG", "QCG1", "QCG2","CCG", "XOR",
+ "MODEXP", "BBS", "MS", "G-SHA1" };
+
+
+
+#endif \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/include/defs.h b/decoders/session_flags/mesa_sts/src/include/defs.h
new file mode 100644
index 0000000..e0e93f7
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/defs.h
@@ -0,0 +1,73 @@
+#ifndef _NIST_DEFS_H_
+#define _NIST_DEFS_H_
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ D E B U G G I N G A I D E S
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "config.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ M A C R O S
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define MAX(x,y) ((x) < (y) ? (y) : (x))
+#define MIN(x,y) ((x) > (y) ? (y) : (x))
+#define isNonPositive(x) ((x) <= 0.e0 ? 1 : 0)
+#define isPositive(x) ((x) > 0.e0 ? 1 : 0)
+#define isNegative(x) ((x) < 0.e0 ? 1 : 0)
+#define isGreaterThanOne(x) ((x) > 1.e0 ? 1 : 0)
+#define isZero(x) ((x) == 0.e0 ? 1 : 0)
+#define isOne(x) ((x) == 1.e0 ? 1 : 0)
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ G L O B A L C O N S T A N T S
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define ALPHA 0.01 /* SIGNIFICANCE LEVEL */
+#define MAXNUMOFTEMPLATES 148 /* APERIODIC TEMPLATES: 148=>temp_length=9 */
+
+#define NUMOFGENERATORS 10 /* MAX PRNGs */
+#define MAXFILESPERMITTEDFORPARTITION 148
+
+#define TEST_FREQUENCY 1
+#define TEST_BLOCK_FREQUENCY 2
+#define TEST_CUSUM 3
+#define TEST_RUNS 4
+#define TEST_LONGEST_RUN 5
+#define TEST_RANK 6
+#define TEST_FFT 7
+#define TEST_NONPERIODIC 8
+#define TEST_OVERLAPPING 9
+#define TEST_UNIVERSAL 10
+#define TEST_APEN 11
+#define TEST_RND_EXCURSION 12
+#define TEST_RND_EXCURSION_VAR 13
+#define TEST_SERIAL 14
+#define TEST_LINEARCOMPLEXITY 15
+#define TEST_POKER_DETECT 16
+#define TEST_RUNS_DISTRIBUTION 17
+#define TEST_BIN_DERIVATE 18
+#define TEST_SELF_CORR 19
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ G L O B A L D A T A S T R U C T U R E S
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+typedef unsigned char BitSequence;
+
+typedef struct _testParameters {
+ int n;
+ int blockFrequencyBlockLength;
+ int nonOverlappingTemplateBlockLength;
+ int overlappingTemplateBlockLength;
+ int serialBlockLength;
+ int linearComplexitySequenceLength;
+ int approximateEntropyBlockLength;
+ int PokerDetectMLength;
+ int BinaryDerivateKLength;
+ int SelfCorrelationDLength;
+ int numOfBitStreams;
+} TP;
+
+#endif \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/include/externs.h b/decoders/session_flags/mesa_sts/src/include/externs.h
new file mode 100644
index 0000000..9f6ea9b
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/externs.h
@@ -0,0 +1,21 @@
+#ifndef _NIST_EXTERNS_H_
+#define _NIST_EXTERNS_H_
+
+#include "defs.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ G L O B A L D A T A S T R U C T U R E S
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+//extern BitSequence *epsilon; // BIT STREAM
+//extern TP tp; // TEST PARAMETER STRUCTURE
+//extern FILE *stats[NUMOFTESTS+1]; // FILE OUTPUT STREAM
+//extern FILE *results[NUMOFTESTS+1]; // FILE OUTPUT STREAM
+//extern FILE *freqfp; // FILE OUTPUT STREAM
+//extern FILE *summary; // FILE OUTPUT STREAM
+//extern int testVector[NUMOFTESTS+1];
+//
+//extern char generatorDir[NUMOFGENERATORS][20];
+//extern char testNames[NUMOFTESTS+1][32];
+
+#endif \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/include/generators.h b/decoders/session_flags/mesa_sts/src/include/generators.h
new file mode 100644
index 0000000..183d05e
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/generators.h
@@ -0,0 +1,78 @@
+#ifndef _NIST_GENERATORS_H_
+#define _NIST_GENERATORS_H_
+
+
+/* The circular shifts. */
+#define CS1(x) ((((ULONG)x)<<1)|(((ULONG)x)>>31))
+#define CS5(x) ((((ULONG)x)<<5)|(((ULONG)x)>>27))
+#define CS30(x) ((((ULONG)x)<<30)|(((ULONG)x)>>2))
+
+/* K constants */
+
+#define K0 0x5a827999L
+#define K1 0x6ed9eba1L
+#define K2 0x8f1bbcdcL
+#define K3 0xca62c1d6L
+
+#define f1(x,y,z) ( (x & (y ^ z)) ^ z )
+
+#define f3(x,y,z) ( (x & ( y ^ z )) ^ (z & y) )
+
+#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */
+
+#define expand(x) Wbuff[x%16] = CS1(Wbuff[(x - 3)%16 ] ^ Wbuff[(x - 8)%16 ] ^ Wbuff[(x - 14)%16] ^ Wbuff[x%16])
+
+#define sub1Round1(count) { \
+ temp = CS5(A) + f1(B, C, D) + E + Wbuff[count] + K0; \
+ E = D; \
+ D = C; \
+ C = CS30( B ); \
+ B = A; \
+ A = temp; \
+ } \
+
+#define sub2Round1(count) \
+ { \
+ expand(count); \
+ temp = CS5(A) + f1(B, C, D) + E + Wbuff[count%16] + K0; \
+ E = D; \
+ D = C; \
+ C = CS30( B ); \
+ B = A; \
+ A = temp; \
+ } \
+
+#define Round2(count) \
+ { \
+ expand(count); \
+ temp = CS5( A ) + f2( B, C, D ) + E + Wbuff[count%16] + K1; \
+ E = D; \
+ D = C; \
+ C = CS30( B ); \
+ B = A; \
+ A = temp; \
+ } \
+
+#define Round3(count) \
+ { \
+ expand(count); \
+ temp = CS5( A ) + f3( B, C, D ) + E + Wbuff[count%16] + K2; \
+ E = D; \
+ D = C; \
+ C = CS30( B ); \
+ B = A; \
+ A = temp; \
+ }
+
+#define Round4(count) \
+ { \
+ expand(count); \
+ temp = CS5( A ) + f2( B, C, D ) + E + Wbuff[count%16] + K3; \
+ E = D; \
+ D = C; \
+ C = CS30( B ); \
+ B = A; \
+ A = temp; \
+ }
+
+#endif \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/include/genutils.h b/decoders/session_flags/mesa_sts/src/include/genutils.h
new file mode 100644
index 0000000..3d61ed4
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/genutils.h
@@ -0,0 +1,47 @@
+#ifndef _NIST_GENUTILS_H_
+#define _NIST_GENUTILS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+
+typedef struct _MP_struct {
+ int size; /* in bytes */
+ int bitlen; /* in bits, duh */
+ BYTE *val;
+ } MP;
+
+#define FREE(A) if ( (A) ) { free((A)); (A) = NULL; }
+#define ASCII2BIN(ch) ( (((ch) >= '0') && ((ch) <= '9')) ? ((ch) - '0') : (((ch) >= 'A') && ((ch) <= 'F')) ? ((ch) - 'A' + 10) : ((ch) - 'a' + 10) )
+
+#ifndef EXPWD
+#define EXPWD ((DBLWORD)1<<NUMLEN)
+#endif
+
+#define sniff_bit(ptr,mask) (*(ptr) & mask)
+
+/*
+ * Function Declarations
+ */
+int greater(BYTE *x, BYTE *y, int l);
+int less(BYTE *x, BYTE *y, int l);
+BYTE bshl(BYTE *x, int l);
+void bshr(BYTE *x, int l);
+int Mult(BYTE *A, BYTE *B, int LB, BYTE *C, int LC);
+void ModSqr(BYTE *A, BYTE *B, int LB, BYTE *M, int LM);
+void ModMult(BYTE *A, BYTE *B, int LB, BYTE *C, int LC, BYTE *M, int LM);
+void smult(BYTE *A, BYTE b, BYTE *C, int L);
+void Square(BYTE *A, BYTE *B, int L);
+void ModExp(BYTE *A, BYTE *B, int LB, BYTE *C, int LC, BYTE *M, int LM);
+int DivMod(BYTE *x, int lenx, BYTE *n, int lenn, BYTE *quot, BYTE *rem);
+void Mod(BYTE *x, int lenx, BYTE *n, int lenn);
+void Div(BYTE *x, int lenx, BYTE *n, int lenn);
+void sub(BYTE *A, int LA, BYTE *B, int LB);
+int negate(BYTE *A, int L);
+BYTE add(BYTE *A, int LA, BYTE *B, int LB);
+void prettyprintBstr(char *S, BYTE *A, int L);
+void byteReverse(unsigned long *buffer, int byteCount);
+void ahtopb (char *ascii_hex, BYTE *p_binary, int bin_len);
+
+#endif /* _GENUTILS_H_ */
diff --git a/decoders/session_flags/mesa_sts/src/include/matrix.h b/decoders/session_flags/mesa_sts/src/include/matrix.h
new file mode 100644
index 0000000..1352c6f
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/matrix.h
@@ -0,0 +1,18 @@
+#ifndef _NIST_MATRIX_H_
+#define _NIST_MATRIX_H_
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ R A N K A L G O R I T H M F U N C T I O N P R O T O T Y P E S
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int computeRank(int M, int Q, BitSequence **matrix);
+void perform_elementary_row_operations(int flag, int i, int M, int Q, BitSequence **A);
+int find_unit_element_and_swap(int flag, int i, int M, int Q, BitSequence **A);
+int swap_rows(int i, int index, int Q, BitSequence **A);
+int determine_rank(int m, int M, int Q, BitSequence **A);
+BitSequence** create_matrix(int M, int Q);
+void display_matrix(int M, int Q, BitSequence **m);
+void def_matrix(int M, int Q, BitSequence **m, int k, BitSequence *epsilon);
+void delete_matrix(int M, BitSequence **matrix);
+
+#endif \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/include/stat_fncs.h b/decoders/session_flags/mesa_sts/src/include/stat_fncs.h
new file mode 100644
index 0000000..97d02f0
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/stat_fncs.h
@@ -0,0 +1,166 @@
+#ifndef _STAT_FNCS_H_
+#define _STAT_FNCS_H_
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ S T A T I S T I C A L T E S T F U N C T I O N P R O T O T Y P E S
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "defs.h"
+
+/**
+ *
+ * @param in
+ * @param inl
+ * @param out 外部预先分配缓冲区,缓冲区大小因最少为字节数的8倍
+ * @param outbufsize
+ * @return
+ */
+int BytesToBitSequence(unsigned char *in, int inl, BitSequence *outbuf, int bufsize);
+
+/**
+ * 单比特频数检测
+ * @param n
+ * @return
+ */
+int Frequency(int n, BitSequence *epsilon);
+
+/**
+ * 块内频数检测
+ * @param M
+ * @param n
+ * @return
+ */
+int BlockFrequency(int M, int n, BitSequence *epsilon);
+
+/**
+ * 累加和检测
+ * @param n
+ * @return
+ */
+int CumulativeSums(int n, BitSequence *epsilon);
+
+/**
+ * 游程总数检测
+ * @param n
+ * @return
+ */
+int Runs(int n, BitSequence *epsilon);
+
+/**
+ * 块内最大“1”游程检测
+ * @param n
+ * @return
+ */
+int LongestRunOfOnes(int n, BitSequence *epsilon);
+
+/**
+ * 矩阵秩检测
+ * @param n
+ * @return
+ */
+int Rank(int n, BitSequence *epsilon);
+
+/**
+ * 离散傅立叶检测
+ * @param n
+ * @return
+ */
+int DiscreteFourierTransform(int n, BitSequence *epsilon);
+
+/**
+ * 非重叠模版匹配测试
+ * @param m
+ * @param n
+ * @return
+ */
+int NonOverlappingTemplateMatchings(int m, int n, BitSequence *epsilon);
+
+/**
+ * 重叠模版匹配测试
+ * @param m
+ * @param n
+ * @return
+ */
+int OverlappingTemplateMatchings(int m, int n, BitSequence *epsilon);
+
+/**
+ * 通用统计检测
+ * @param n
+ * @return
+ */
+int Universal(int n, BitSequence *epsilon);
+
+/**
+ * 近似熵检测
+ * @param m
+ * @param n
+ * @return
+ */
+int ApproximateEntropy(int m, int n, BitSequence *epsilon);
+
+/**
+ * 自由游程测试
+ * @param n
+ * @return
+ */
+int RandomExcursions(int n, BitSequence *epsilon);
+
+/**
+ * 自由变量测试
+ * @param n
+ * @return
+ */
+int RandomExcursionsVariant(int n, BitSequence *epsilon);
+
+/**
+ * 线性复杂度检测
+ * @param M
+ * @param n
+ * @return
+ */
+int LinearComplexity(int M, int n, BitSequence *epsilon);
+
+/**
+ * 重叠子序列检测
+ * @param m
+ * @param n
+ * @return
+ */
+int Serial(int m, int n, BitSequence *epsilon);
+
+/**
+ * 二元推导检测
+ * @param k
+ * @param n
+ * @param epsilon
+ * @return
+ */
+int BinaryDerivate(int k, int n, BitSequence *epsilon, int epsilon_l);
+
+/**
+ * 自相关测试
+ * @param d
+ * @param n
+ * @param epsilon
+ * @return
+ */
+int SelfCorrelation(int d, int n, BitSequence *epsilon);
+
+/**
+ * 扑克检测
+ * @param M
+ * @param n
+ * @param epsilon
+ * @return
+ */
+int PokerDetect(int M, int n, BitSequence *epsilon);
+
+/**
+ * 游程分布检测
+ * @param n
+ * @param epsilon
+ * @return
+ */
+int RunsDistribution(int n, BitSequence *epsilon);
+
+#endif \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/include/utilities.h b/decoders/session_flags/mesa_sts/src/include/utilities.h
new file mode 100644
index 0000000..8e4fd7d
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/include/utilities.h
@@ -0,0 +1,4 @@
+#include "include/config.h"
+#include "include/defs.h"
+
+int convertToBits(BYTE *x, int xBitLength, int bitsNeeded, int *num_0s, int *num_1s, int *bitsRead, BitSequence* epsilon); \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/linearComplexity.c b/decoders/session_flags/mesa_sts/src/linearComplexity.c
new file mode 100644
index 0000000..ae831de
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/linearComplexity.c
@@ -0,0 +1,135 @@
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+int
+LinearComplexity(int M, int n, BitSequence *epsilon)
+{
+ int i, ii, j, d, N, L, m, N_, parity, sign, K = 6;
+ double p_value, T_, mean, nu[7], chi2;
+ const double pi[7] = { 0.01047, 0.03125, 0.12500, 0.50000, 0.25000, 0.06250, 0.020833 };
+ BitSequence *T, *P, *B_, *C;
+
+ N = (int)floor(n/M);
+ if ( ((B_ = (BitSequence *) calloc(M, sizeof(BitSequence))) == NULL) ||
+ ((C = (BitSequence *) calloc(M, sizeof(BitSequence))) == NULL) ||
+ ((P = (BitSequence *) calloc(M, sizeof(BitSequence))) == NULL) ||
+ ((T = (BitSequence *) calloc(M, sizeof(BitSequence))) == NULL) ) {
+ printf("Insufficient Memory for Work Space:: Linear Complexity Test\n");
+ if ( B_!= NULL )
+ free(B_);
+ if ( C != NULL )
+ free(C);
+ if ( P != NULL )
+ free(P);
+ if ( T != NULL )
+ free(T);
+ return 0;
+ }
+
+
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "-----------------------------------------------------\n");
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "\tL I N E A R C O M P L E X I T Y\n");
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "-----------------------------------------------------\n");
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "\tM (substring length) = %d\n", M);
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "\tN (number of substrings) = %d\n", N);
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "-----------------------------------------------------\n");
+// fprintf(stats[TEST_LINEARCOMPLEXITY], " F R E Q U E N C Y \n");
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "-----------------------------------------------------\n");
+// fprintf(stats[TEST_LINEARCOMPLEXITY], " C0 C1 C2 C3 C4 C5 C6 CHI2 P-value\n");
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "-----------------------------------------------------\n");
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "\tNote: %d bits were discarded!\n", n%M);
+
+ for ( i=0; i<K+1; i++ )
+ nu[i] = 0.00;
+ for ( ii=0; ii<N; ii++ ) {
+ for ( i=0; i<M; i++ ) {
+ B_[i] = 0;
+ C[i] = 0;
+ T[i] = 0;
+ P[i] = 0;
+ }
+ L = 0;
+ m = -1;
+ d = 0;
+ C[0] = 1;
+ B_[0] = 1;
+
+ /* DETERMINE LINEAR COMPLEXITY */
+ N_ = 0;
+ while ( N_ < M ) {
+ d = (int)epsilon[ii*M+N_];
+ for ( i=1; i<=L; i++ )
+ d += C[i] * epsilon[ii*M+N_-i];
+ d = d%2;
+ if ( d == 1 ) {
+ for ( i=0; i<M; i++ ) {
+ T[i] = C[i];
+ P[i] = 0;
+ }
+ for ( j=0; j<M; j++ )
+ if ( B_[j] == 1 )
+ P[j+N_-m] = 1;
+ for ( i=0; i<M; i++ )
+ C[i] = (C[i] + P[i])%2;
+ if ( L <= N_/2 ) {
+ L = N_ + 1 - L;
+ m = N_;
+ for ( i=0; i<M; i++ )
+ B_[i] = T[i];
+ }
+ }
+ N_++;
+ }
+ if ( (parity = (M+1)%2) == 0 )
+ sign = -1;
+ else
+ sign = 1;
+ mean = M/2.0 + (9.0+sign)/36.0 - 1.0/pow(2, M) * (M/3.0 + 2.0/9.0);
+ if ( (parity = M%2) == 0 )
+ sign = 1;
+ else
+ sign = -1;
+ T_ = sign * (L - mean) + 2.0/9.0;
+
+ if ( T_ <= -2.5 )
+ nu[0]++;
+ else if ( T_ <= -1.5 )
+ nu[1]++;
+ else if (T_ <= -0.5 )
+ nu[2]++;
+ else if ( T_ <= 0.5 )
+ nu[3]++;
+ else if ( T_ <= 1.5 )
+ nu[4]++;
+ else if (T_ <= 2.5 )
+ nu[5]++;
+ else
+ nu[6]++;
+ }
+ chi2 = 0.00;
+// for ( i=0; i<K+1; i++ ) {
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "%4d ", (int) nu[i]);
+// }
+ for ( i=0; i<K+1; i++ ) {
+ chi2 += pow(nu[i] - N * pi[i], 2) / (N * pi[i]);
+ }
+ p_value = cephes_igamc(K/2.0, chi2/2.0);
+
+// fprintf(stats[TEST_LINEARCOMPLEXITY], "%9.6f%9.6f\n", chi2, p_value); fflush(stats[TEST_LINEARCOMPLEXITY]);
+// fprintf(results[TEST_LINEARCOMPLEXITY], "%f\n", p_value); fflush(results[TEST_LINEARCOMPLEXITY]);
+
+ free(B_);
+ free(P);
+ free(C);
+ free(T);
+
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/decoders/session_flags/mesa_sts/src/longestRunOfOnes.c b/decoders/session_flags/mesa_sts/src/longestRunOfOnes.c
new file mode 100644
index 0000000..0462778
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/longestRunOfOnes.c
@@ -0,0 +1,125 @@
+/* got rid of unused 'k' */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ L O N G E S T R U N S T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+LongestRunOfOnes(int n, BitSequence *epsilon)
+{
+ double pval, chi2, pi[7];
+ int run, v_n_obs, N, i, j, K, M, V[7];
+ unsigned int nu[7] = { 0, 0, 0, 0, 0, 0, 0 };
+
+ if ( n < 128 ) {
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t\t LONGEST RUNS OF ONES TEST\n");
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t---------------------------------------------\n");
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t n=%d is too short\n", n);
+ return 0;
+ }
+ if ( n < 6272 ) {
+ K = 3;
+ M = 8;
+ V[0] = 1; V[1] = 2; V[2] = 3; V[3] = 4;
+ pi[0] = 0.21484375;
+ pi[1] = 0.3671875;
+ pi[2] = 0.23046875;
+ pi[3] = 0.1875;
+ }
+ else if ( n < 750000 ) {
+ K = 5;
+ M = 128;
+ V[0] = 4; V[1] = 5; V[2] = 6; V[3] = 7; V[4] = 8; V[5] = 9;
+ pi[0] = 0.1174035788;
+ pi[1] = 0.242955959;
+ pi[2] = 0.249363483;
+ pi[3] = 0.17517706;
+ pi[4] = 0.102701071;
+ pi[5] = 0.112398847;
+ }
+ else {
+ K = 6;
+ M = 10000;
+ V[0] = 10; V[1] = 11; V[2] = 12; V[3] = 13; V[4] = 14; V[5] = 15; V[6] = 16;
+ pi[0] = 0.0882;
+ pi[1] = 0.2092;
+ pi[2] = 0.2483;
+ pi[3] = 0.1933;
+ pi[4] = 0.1208;
+ pi[5] = 0.0675;
+ pi[6] = 0.0727;
+ }
+
+ N = n/M;
+ for ( i=0; i<N; i++ ) {
+ v_n_obs = 0;
+ run = 0;
+ for ( j=0; j<M; j++ ) {
+ if ( epsilon[i*M+j] == 1 ) {
+ run++;
+ if ( run > v_n_obs )
+ v_n_obs = run;
+ }
+ else
+ run = 0;
+ }
+ if ( v_n_obs < V[0] )
+ nu[0]++;
+ for ( j=0; j<=K; j++ ) {
+ if ( v_n_obs == V[j] )
+ nu[j]++;
+ }
+ if ( v_n_obs > V[K] )
+ nu[K]++;
+ }
+
+ chi2 = 0.0;
+ for ( i=0; i<=K; i++ )
+ chi2 += ((nu[i] - N * pi[i]) * (nu[i] - N * pi[i])) / (N * pi[i]);
+
+ pval = cephes_igamc((double)(K/2.0), chi2 / 2.0);
+
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t\t LONGEST RUNS OF ONES TEST\n");
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t---------------------------------------------\n");
+// fprintf(stats[TEST_LONGEST_RUN], "\t\tCOMPUTATIONAL INFORMATION:\n");
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t---------------------------------------------\n");
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t(a) N (# of substrings) = %d\n", N);
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t(b) M (Substring Length) = %d\n", M);
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t(c) Chi^2 = %f\n", chi2);
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t---------------------------------------------\n");
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t F R E Q U E N C Y\n");
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t---------------------------------------------\n");
+
+// if ( K == 3 ) {
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t <=1 2 3 >=4 P-value Assignment");
+// fprintf(stats[TEST_LONGEST_RUN], "\n\t\t %3d %3d %3d %3d ", nu[0], nu[1], nu[2], nu[3]);
+// }
+// else if ( K == 5 ) {
+// fprintf(stats[TEST_LONGEST_RUN], "\t\t<=4 5 6 7 8 >=9 P-value Assignment");
+// fprintf(stats[TEST_LONGEST_RUN], "\n\t\t %3d %3d %3d %3d %3d %3d ", nu[0], nu[1], nu[2],
+// nu[3], nu[4], nu[5]);
+// }
+// else {
+// fprintf(stats[TEST_LONGEST_RUN],"\t\t<=10 11 12 13 14 15 >=16 P-value Assignment");
+// fprintf(stats[TEST_LONGEST_RUN],"\n\t\t %3d %3d %3d %3d %3d %3d %3d ", nu[0], nu[1], nu[2],
+// nu[3], nu[4], nu[5], nu[6]);
+// }
+ if ( isNegative(pval) || isGreaterThanOne(pval) ) {
+// fprintf(stats[TEST_LONGEST_RUN], "WARNING: P_VALUE IS OUT OF RANGE.\n");
+ return 0;
+ }
+
+// fprintf(stats[TEST_LONGEST_RUN], "%s\t\tp_value = %f\n\n", pval < ALPHA ? "FAILURE" : "SUCCESS", pval); fflush(stats[TEST_LONGEST_RUN]);
+// fprintf(results[TEST_LONGEST_RUN], "%f\n", pval); fflush(results[TEST_LONGEST_RUN]);
+ if (pval < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/decoders/session_flags/mesa_sts/src/matrix.c b/decoders/session_flags/mesa_sts/src/matrix.c
new file mode 100644
index 0000000..c456187
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/matrix.c
@@ -0,0 +1,170 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "include/externs.h"
+#include "include/matrix.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+R A N K A L G O R I T H M R O U T I N E S
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define MATRIX_FORWARD_ELIMINATION 0
+#define MATRIX_BACKWARD_ELIMINATION 1
+
+int
+computeRank(int M, int Q, BitSequence **matrix)
+{
+ int i, rank, m=MIN(M,Q);
+
+ /* FORWARD APPLICATION OF ELEMENTARY ROW OPERATIONS */
+ for ( i=0; i<m-1; i++ ) {
+ if ( matrix[i][i] == 1 )
+ perform_elementary_row_operations(MATRIX_FORWARD_ELIMINATION, i, M, Q, matrix);
+ else { /* matrix[i][i] = 0 */
+ if ( find_unit_element_and_swap(MATRIX_FORWARD_ELIMINATION, i, M, Q, matrix) == 1 )
+ perform_elementary_row_operations(MATRIX_FORWARD_ELIMINATION, i, M, Q, matrix);
+ }
+ }
+
+ /* BACKWARD APPLICATION OF ELEMENTARY ROW OPERATIONS */
+ for ( i=m-1; i>0; i-- ) {
+ if ( matrix[i][i] == 1 )
+ perform_elementary_row_operations(MATRIX_BACKWARD_ELIMINATION, i, M, Q, matrix);
+ else { /* matrix[i][i] = 0 */
+ if ( find_unit_element_and_swap(MATRIX_BACKWARD_ELIMINATION, i, M, Q, matrix) == 1 )
+ perform_elementary_row_operations(MATRIX_BACKWARD_ELIMINATION, i, M, Q, matrix);
+ }
+ }
+
+ rank = determine_rank(m, M, Q, matrix);
+
+ return rank;
+}
+
+void
+perform_elementary_row_operations(int flag, int i, int M, int Q, BitSequence **A)
+{
+ int j, k;
+
+ if ( flag == MATRIX_FORWARD_ELIMINATION ) {
+ for ( j=i+1; j<M; j++ )
+ if ( A[j][i] == 1 )
+ for ( k=i; k<Q; k++ )
+ A[j][k] = (A[j][k] + A[i][k]) % 2;
+ }
+ else {
+ for ( j=i-1; j>=0; j-- )
+ if ( A[j][i] == 1 )
+ for ( k=0; k<Q; k++ )
+ A[j][k] = (A[j][k] + A[i][k]) % 2;
+ }
+}
+
+int
+find_unit_element_and_swap(int flag, int i, int M, int Q, BitSequence **A)
+{
+ int index, row_op=0;
+
+ if ( flag == MATRIX_FORWARD_ELIMINATION ) {
+ index = i+1;
+ while ( (index < M) && (A[index][i] == 0) )
+ index++;
+ if ( index < M )
+ row_op = swap_rows(i, index, Q, A);
+ }
+ else {
+ index = i-1;
+ while ( (index >= 0) && (A[index][i] == 0) )
+ index--;
+ if ( index >= 0 )
+ row_op = swap_rows(i, index, Q, A);
+ }
+
+ return row_op;
+}
+
+int
+swap_rows(int i, int index, int Q, BitSequence **A)
+{
+ int p;
+ BitSequence temp;
+
+ for ( p=0; p<Q; p++ ) {
+ temp = A[i][p];
+ A[i][p] = A[index][p];
+ A[index][p] = temp;
+ }
+
+ return 1;
+}
+
+int
+determine_rank(int m, int M, int Q, BitSequence **A)
+{
+ int i, j, rank, allZeroes;
+
+ /* DETERMINE RANK, THAT IS, COUNT THE NUMBER OF NONZERO ROWS */
+
+ rank = m;
+ for ( i=0; i<M; i++ ) {
+ allZeroes = 1;
+ for ( j=0; j<Q; j++) {
+ if ( A[i][j] == 1 ) {
+ allZeroes = 0;
+ break;
+ }
+ }
+ if ( allZeroes == 1 )
+ rank--;
+ }
+
+ return rank;
+}
+
+BitSequence**
+create_matrix(int M, int Q)
+{
+ int i;
+ BitSequence **matrix;
+
+ if ( (matrix = (BitSequence **) calloc(M, sizeof(BitSequence *))) == NULL ) {
+ printf("ERROR IN FUNCTION create_matrix: Insufficient memory available.\n");
+
+ return NULL;
+ }
+ else {
+ for ( i=0; i<M; i++ ) {
+ if ( (matrix[i] = calloc(Q, sizeof(BitSequence))) == NULL ) {
+ printf("ERROR IN FUNCTION create_matrix: Insufficient memory for %dx%d matrix.\n", M, M);
+
+ return NULL;
+ }
+ }
+ return matrix;
+ }
+}
+
+void
+def_matrix(int M, int Q, BitSequence **m, int k, BitSequence *epsilon)
+{
+ int i,j;
+
+ for ( i=0; i<M; i++ )
+ for ( j=0; j<Q; j++ )
+ m[i][j] = epsilon[k*(M*Q)+j+i*M];
+}
+
+void
+delete_matrix(int M, BitSequence **matrix)
+{
+ int i;
+
+ if (matrix != NULL) {
+ for (i = 0; i < M; i++) {
+ if (matrix[i] != NULL) {
+ free(matrix[i]);
+ matrix[i] = NULL;
+ }
+ }
+ free(matrix);
+ }
+}
diff --git a/decoders/session_flags/mesa_sts/src/mesa_sts.c b/decoders/session_flags/mesa_sts/src/mesa_sts.c
new file mode 100644
index 0000000..f95ac89
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/mesa_sts.c
@@ -0,0 +1,114 @@
+#include <stdlib.h>
+#include "include/utilities.h"
+#include "include/stat_fncs.h"
+#include "mesa_sts.h"
+
+int mesa_statistical_test_suite(void* data,unsigned int datalen, struct sts_result* result, unsigned int random_judge_switch_flag)
+{
+ TP tp;
+ tp.n = datalen;
+ tp.blockFrequencyBlockLength = 128;
+ tp.nonOverlappingTemplateBlockLength = 9;
+ tp.overlappingTemplateBlockLength = 9;
+ tp.approximateEntropyBlockLength = 10;
+ tp.serialBlockLength = 16;
+ tp.linearComplexitySequenceLength = 500;
+ tp.numOfBitStreams = 1;
+ tp.PokerDetectMLength = 8;
+ tp.BinaryDerivateKLength = 3;
+ tp.SelfCorrelationDLength = 8;
+
+ BitSequence* epsilon = (BitSequence *)calloc(tp.n,sizeof(BitSequence));
+ int done, num_0s, num_1s, bitsRead;
+ num_0s = 0;
+ num_1s = 0;
+ bitsRead = 0;
+ done = 0;
+ done = convertToBits((BYTE*)data,datalen,tp.n,&num_0s,&num_1s,&bitsRead,epsilon);
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_FREQUENCY))
+ {
+ result->frequency = Frequency(tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_BLOCK_FREQUENCY))
+ {
+ result->block_frequency = BlockFrequency(tp.blockFrequencyBlockLength, tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_CUMULATIVE_SUMS))
+ {
+ result->cumulative_sums = CumulativeSums(tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_RUNS))
+ {
+ result->runs = Runs(tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_LONGEST_RUN))
+ {
+ result->longest_run = LongestRunOfOnes(tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_RANK))
+ {
+ result->rank = Rank(tp.n,epsilon);
+ }
+
+ //result->discrete_fourier_transform = DiscreteFourierTransform(tp.n,epsilon);//cost too much time
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_NON_OVERLAPPING_TEMPLATE_MATCHING))
+ {
+ result->non_overlapping_template_matching = NonOverlappingTemplateMatchings(tp.nonOverlappingTemplateBlockLength, tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_OVERLAPPING_TEMPLATE_MATCHING))
+ {
+ result->overlapping_template_matching = OverlappingTemplateMatchings(tp.overlappingTemplateBlockLength, tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_UNIVERSAL))
+ {
+ result->universal = Universal(tp.n,epsilon);
+ }
+
+ //result->approximate_entropy = ApproximateEntropy(tp.approximateEntropyBlockLength, tp.n,epsilon);//cost too much time
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_RANDOM_EXCURSIONS))
+ {
+ result->random_excursions = RandomExcursions(tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_RANDOM_EXCURSIONS_VARIANT))
+ {
+ result->random_excursions_variant = RandomExcursionsVariant(tp.n,epsilon);
+ }
+
+ //result->serial = Serial(tp.serialBlockLength,tp.n,epsilon);//cost too much time
+ //sresult->linear_complexity = LinearComplexity(tp.linearComplexitySequenceLength, tp.n,epsilon);//cost too much time
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_POKER_DETECT))
+ {
+ result->poker_detect = PokerDetect(tp.PokerDetectMLength,tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_RUNS_DISTRIBUTION))
+ {
+ result->runs_distribution = RunsDistribution(tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_SELF_CORRELATION))
+ {
+ result->self_correlation = SelfCorrelation(tp.SelfCorrelationDLength,tp.n,epsilon);
+ }
+
+ if (STS_TEST_FLAG(random_judge_switch_flag, STS_BINARY_DERIVATE))
+ {
+ result->binary_derivative = BinaryDerivate(tp.BinaryDerivateKLength,tp.n,epsilon,tp.n);//this function will change the value of epsilon, must be the last one
+ }
+
+ free(epsilon);
+ epsilon = NULL;
+
+ return 0;
+} \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/nonOverlappingTemplateMatchings.c b/decoders/session_flags/mesa_sts/src/nonOverlappingTemplateMatchings.c
new file mode 100644
index 0000000..1bc47db
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/nonOverlappingTemplateMatchings.c
@@ -0,0 +1,281 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ N O N O V E R L A P P I N G T E M P L A T E T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+static unsigned int template9[] = {
+0,0,0,0,0,0,0,0,1,
+0,0,0,0,0,0,0,1,1,
+0,0,0,0,0,0,1,0,1,
+0,0,0,0,0,0,1,1,1,
+0,0,0,0,0,1,0,0,1,
+0,0,0,0,0,1,0,1,1,
+0,0,0,0,0,1,1,0,1,
+0,0,0,0,0,1,1,1,1,
+0,0,0,0,1,0,0,0,1,
+0,0,0,0,1,0,0,1,1,
+0,0,0,0,1,0,1,0,1,
+0,0,0,0,1,0,1,1,1,
+0,0,0,0,1,1,0,0,1,
+0,0,0,0,1,1,0,1,1,
+0,0,0,0,1,1,1,0,1,
+0,0,0,0,1,1,1,1,1,
+0,0,0,1,0,0,0,1,1,
+0,0,0,1,0,0,1,0,1,
+0,0,0,1,0,0,1,1,1,
+0,0,0,1,0,1,0,0,1,
+0,0,0,1,0,1,0,1,1,
+0,0,0,1,0,1,1,0,1,
+0,0,0,1,0,1,1,1,1,
+0,0,0,1,1,0,0,1,1,
+0,0,0,1,1,0,1,0,1,
+0,0,0,1,1,0,1,1,1,
+0,0,0,1,1,1,0,0,1,
+0,0,0,1,1,1,0,1,1,
+0,0,0,1,1,1,1,0,1,
+0,0,0,1,1,1,1,1,1,
+0,0,1,0,0,0,0,1,1,
+0,0,1,0,0,0,1,0,1,
+0,0,1,0,0,0,1,1,1,
+0,0,1,0,0,1,0,1,1,
+0,0,1,0,0,1,1,0,1,
+0,0,1,0,0,1,1,1,1,
+0,0,1,0,1,0,0,1,1,
+0,0,1,0,1,0,1,0,1,
+0,0,1,0,1,0,1,1,1,
+0,0,1,0,1,1,0,1,1,
+0,0,1,0,1,1,1,0,1,
+0,0,1,0,1,1,1,1,1,
+0,0,1,1,0,0,1,0,1,
+0,0,1,1,0,0,1,1,1,
+0,0,1,1,0,1,0,1,1,
+0,0,1,1,0,1,1,0,1,
+0,0,1,1,0,1,1,1,1,
+0,0,1,1,1,0,1,0,1,
+0,0,1,1,1,0,1,1,1,
+0,0,1,1,1,1,0,1,1,
+0,0,1,1,1,1,1,0,1,
+0,0,1,1,1,1,1,1,1,
+0,1,0,0,0,0,0,1,1,
+0,1,0,0,0,0,1,1,1,
+0,1,0,0,0,1,0,1,1,
+0,1,0,0,0,1,1,1,1,
+0,1,0,0,1,0,0,1,1,
+0,1,0,0,1,0,1,1,1,
+0,1,0,0,1,1,0,1,1,
+0,1,0,0,1,1,1,1,1,
+0,1,0,1,0,0,0,1,1,
+0,1,0,1,0,0,1,1,1,
+0,1,0,1,0,1,0,1,1,
+0,1,0,1,0,1,1,1,1,
+0,1,0,1,1,0,0,1,1,
+0,1,0,1,1,0,1,1,1,
+0,1,0,1,1,1,0,1,1,
+0,1,0,1,1,1,1,1,1,
+0,1,1,0,0,0,1,1,1,
+0,1,1,0,0,1,1,1,1,
+0,1,1,0,1,0,1,1,1,
+0,1,1,0,1,1,1,1,1,
+0,1,1,1,0,1,1,1,1,
+0,1,1,1,1,1,1,1,1,
+1,0,0,0,0,0,0,0,0,
+1,0,0,0,1,0,0,0,0,
+1,0,0,1,0,0,0,0,0,
+1,0,0,1,0,1,0,0,0,
+1,0,0,1,1,0,0,0,0,
+1,0,0,1,1,1,0,0,0,
+1,0,1,0,0,0,0,0,0,
+1,0,1,0,0,0,1,0,0,
+1,0,1,0,0,1,0,0,0,
+1,0,1,0,0,1,1,0,0,
+1,0,1,0,1,0,0,0,0,
+1,0,1,0,1,0,1,0,0,
+1,0,1,0,1,1,0,0,0,
+1,0,1,0,1,1,1,0,0,
+1,0,1,1,0,0,0,0,0,
+1,0,1,1,0,0,1,0,0,
+1,0,1,1,0,1,0,0,0,
+1,0,1,1,0,1,1,0,0,
+1,0,1,1,1,0,0,0,0,
+1,0,1,1,1,0,1,0,0,
+1,0,1,1,1,1,0,0,0,
+1,0,1,1,1,1,1,0,0,
+1,1,0,0,0,0,0,0,0,
+1,1,0,0,0,0,0,1,0,
+1,1,0,0,0,0,1,0,0,
+1,1,0,0,0,1,0,0,0,
+1,1,0,0,0,1,0,1,0,
+1,1,0,0,1,0,0,0,0,
+1,1,0,0,1,0,0,1,0,
+1,1,0,0,1,0,1,0,0,
+1,1,0,0,1,1,0,0,0,
+1,1,0,0,1,1,0,1,0,
+1,1,0,1,0,0,0,0,0,
+1,1,0,1,0,0,0,1,0,
+1,1,0,1,0,0,1,0,0,
+1,1,0,1,0,1,0,0,0,
+1,1,0,1,0,1,0,1,0,
+1,1,0,1,0,1,1,0,0,
+1,1,0,1,1,0,0,0,0,
+1,1,0,1,1,0,0,1,0,
+1,1,0,1,1,0,1,0,0,
+1,1,0,1,1,1,0,0,0,
+1,1,0,1,1,1,0,1,0,
+1,1,0,1,1,1,1,0,0,
+1,1,1,0,0,0,0,0,0,
+1,1,1,0,0,0,0,1,0,
+1,1,1,0,0,0,1,0,0,
+1,1,1,0,0,0,1,1,0,
+1,1,1,0,0,1,0,0,0,
+1,1,1,0,0,1,0,1,0,
+1,1,1,0,0,1,1,0,0,
+1,1,1,0,1,0,0,0,0,
+1,1,1,0,1,0,0,1,0,
+1,1,1,0,1,0,1,0,0,
+1,1,1,0,1,0,1,1,0,
+1,1,1,0,1,1,0,0,0,
+1,1,1,0,1,1,0,1,0,
+1,1,1,0,1,1,1,0,0,
+1,1,1,1,0,0,0,0,0,
+1,1,1,1,0,0,0,1,0,
+1,1,1,1,0,0,1,0,0,
+1,1,1,1,0,0,1,1,0,
+1,1,1,1,0,1,0,0,0,
+1,1,1,1,0,1,0,1,0,
+1,1,1,1,0,1,1,0,0,
+1,1,1,1,0,1,1,1,0,
+1,1,1,1,1,0,0,0,0,
+1,1,1,1,1,0,0,1,0,
+1,1,1,1,1,0,1,0,0,
+1,1,1,1,1,0,1,1,0,
+1,1,1,1,1,1,0,0,0,
+1,1,1,1,1,1,0,1,0,
+1,1,1,1,1,1,1,0,0,
+1,1,1,1,1,1,1,1,0,
+};
+static size_t template_size = sizeof(template9)/sizeof(template9[0]);
+
+int
+NonOverlappingTemplateMatchings(int m, int n, BitSequence *epsilon)
+{
+ int ret = 0;
+ int numOfTemplates[100] = {0, 0, 2, 4, 6, 12, 20, 40, 74, 148, 284, 568, 1116,
+ 2232, 4424, 8848, 17622, 35244, 70340, 140680, 281076, 562152};
+ /*----------------------------------------------------------------------------
+ NOTE: Should additional templates lengths beyond 21 be desired, they must
+ first be constructed, saved into files and then the corresponding
+ number of nonperiodic templates for that file be stored in the m-th
+ position in the numOfTemplates variable.
+ ----------------------------------------------------------------------------*/
+ unsigned int W_obs, nu[6], *Wj = NULL;
+ size_t template_idx = 0;
+ double sum, chi2, p_value, lambda, pi[6], varWj;
+ int i, j, jj, k, match, SKIP, M, N, K = 5;
+ BitSequence *sequence = NULL;
+
+ N = 8;
+ M = n/N;
+
+ if ( (Wj = (unsigned int*)calloc(N, sizeof(unsigned int))) == NULL ) {
+ return 0;
+ }
+ lambda = (M-m+1)/pow(2, m);
+ varWj = M*(1.0/pow(2.0, m) - (2.0*m-1.0)/pow(2.0, 2.0*m));
+
+ if ( ((isNegative(lambda)) || (isZero(lambda))) ||
+ ((sequence = (BitSequence *) calloc(m, sizeof(BitSequence))) == NULL) ) {
+ goto end;
+ }
+ else {
+ if ( numOfTemplates[m] < MAXNUMOFTEMPLATES )
+ SKIP = 1;
+ else
+ SKIP = (int)(numOfTemplates[m]/MAXNUMOFTEMPLATES);
+ numOfTemplates[m] = (int)numOfTemplates[m]/SKIP;
+
+ sum = 0.0;
+ for ( i=0; i<2; i++ ) { /* Compute Probabilities */
+ pi[i] = exp(-lambda+i*log(lambda)-cephes_lgam(i+1));
+ sum += pi[i];
+ }
+ pi[0] = sum;
+ for ( i=2; i<=K; i++ ) { /* Compute Probabilities */
+ pi[i-1] = exp(-lambda+i*log(lambda)-cephes_lgam(i+1));
+ sum += pi[i-1];
+ }
+ pi[K] = 1 - sum;
+
+ for( jj=0; jj<MIN(MAXNUMOFTEMPLATES, numOfTemplates[m]); jj++ ) {
+ sum = 0;
+
+ for ( k=0; k<m; k++ ) {
+ if (template_idx < template_size) {
+ sequence[k] = template9[template_idx];
+ template_idx++;
+ }
+ else {
+ sequence[k] = 0;
+ }
+// fprintf(stats[TEST_NONPERIODIC], "%d", sequence[k]);
+ }
+// fprintf(stats[TEST_NONPERIODIC], " ");
+ for ( k=0; k<=K; k++ )
+ nu[k] = 0;
+ for ( i=0; i<N; i++ ) {
+ W_obs = 0;
+ for ( j=0; j<M-m+1; j++ ) {
+ match = 1;
+ for ( k=0; k<m; k++ ) {
+ if ( (int)sequence[k] != (int)epsilon[i*M+j+k] ) {
+ match = 0;
+ break;
+ }
+ }
+ if ( match == 1 )
+ W_obs++;
+ }
+ Wj[i] = W_obs;
+ }
+ sum = 0;
+ chi2 = 0.0; /* Compute Chi Square */
+ for ( i=0; i<N; i++ ) {
+// if ( m == 10 )
+// fprintf(stats[TEST_NONPERIODIC], "%3d ", Wj[i]);
+// else
+// fprintf(stats[TEST_NONPERIODIC], "%4d ", Wj[i]);
+ chi2 += pow(((double)Wj[i] - lambda)/pow(varWj, 0.5), 2);
+ }
+ p_value = cephes_igamc(N/2.0, chi2/2.0);
+
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+// fprintf(stats[TEST_NONPERIODIC], "\t\tWARNING: P_VALUE IS OUT OF RANGE.\n");
+ goto end;
+ }
+
+// fprintf(stats[TEST_NONPERIODIC], "%9.6f %f %s %3d\n", chi2, p_value, p_value < ALPHA ? "FAILURE" : "SUCCESS", jj);
+ if ( SKIP > 1 )
+ template_idx += (SKIP-1)*2*m;
+// fprintf(results[TEST_NONPERIODIC], "%f\n", p_value); fflush(results[TEST_NONPERIODIC]);
+ if (p_value < ALPHA) {
+ goto end;
+ }
+ }
+ }
+
+ ret = 1;
+// fprintf(stats[TEST_NONPERIODIC], "\n"); fflush(stats[TEST_NONPERIODIC]);
+
+ end:
+ if (Wj != NULL) {
+ free(Wj);
+ }
+ if (sequence != NULL) {
+ free(sequence);
+ }
+ return ret;
+}
diff --git a/decoders/session_flags/mesa_sts/src/overlappingTemplateMatchings.c b/decoders/session_flags/mesa_sts/src/overlappingTemplateMatchings.c
new file mode 100644
index 0000000..70eabdf
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/overlappingTemplateMatchings.c
@@ -0,0 +1,97 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ O V E R L A P P I N G T E M P L A T E T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+double Pr(int u, double eta);
+
+int
+OverlappingTemplateMatchings(int m, int n, BitSequence *epsilon)
+{
+ int ret = 0;
+ int i, k, match;
+ double W_obs, eta, sum, chi2, p_value, lambda;
+ int M, N, j, K = 5;
+ unsigned int nu[6] = { 0, 0, 0, 0, 0, 0 };
+ double pi[6] = { 0.143783, 0.139430, 0.137319, 0.124314, 0.106209, 0.348945 };
+ BitSequence *sequence;
+
+ M = 1032;
+ N = n/M;
+
+ if ( (sequence = (BitSequence *) calloc(m, sizeof(BitSequence))) == NULL ) {
+ return 0;
+ }
+ else {
+ for (i = 0; i < m; i++)
+ sequence[i] = 1;
+ }
+
+ lambda = (double)(M-m+1)/pow(2,m);
+ eta = lambda/2.0;
+ sum = 0.0;
+ for ( i=0; i<K; i++ ) { /* Compute Probabilities */
+ pi[i] = Pr(i, eta);
+ sum += pi[i];
+ }
+ pi[K] = 1 - sum;
+
+ for ( i=0; i<N; i++ ) {
+ W_obs = 0;
+ for ( j=0; j<M-m+1; j++ ) {
+ match = 1;
+ for ( k=0; k<m; k++ ) {
+ if ( sequence[k] != epsilon[i*M+j+k] )
+ match = 0;
+ }
+ if ( match == 1 )
+ W_obs++;
+ }
+ if ( W_obs <= 4 )
+ nu[(int)W_obs]++;
+ else
+ nu[K]++;
+ }
+ sum = 0;
+ chi2 = 0.0; /* Compute Chi Square */
+ for ( i=0; i<K+1; i++ ) {
+ chi2 += pow((double)nu[i] - (double)N*pi[i], 2)/((double)N*pi[i]);
+ sum += nu[i];
+ }
+ p_value = cephes_igamc(K/2.0, chi2/2.0);
+
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+ goto end;
+ }
+ if (p_value < ALPHA) {
+ goto end;
+ }
+
+ ret = 1;
+
+ end:
+ free(sequence);
+ return ret;
+}
+
+double
+Pr(int u, double eta)
+{
+ int l;
+ double sum, p;
+
+ if ( u == 0 )
+ p = exp(-eta);
+ else {
+ sum = 0.0;
+ for ( l=1; l<=u; l++ )
+ sum += exp(-eta-u*log(2)+l*log(eta)-cephes_lgam(l+1)+cephes_lgam(u)-cephes_lgam(l)-cephes_lgam(u-l+1));
+ p = sum;
+ }
+ return p;
+}
diff --git a/decoders/session_flags/mesa_sts/src/pokerDetect.c b/decoders/session_flags/mesa_sts/src/pokerDetect.c
new file mode 100644
index 0000000..44d9a3c
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/pokerDetect.c
@@ -0,0 +1,79 @@
+#include <stdlib.h>
+#include <math.h>
+#include "include/stat_fncs.h"
+#include "include/cephes.h"
+
+typedef struct _PokerNi {
+ unsigned int flag;
+ unsigned int count;
+} PokerNi;
+
+unsigned char toByte(BitSequence *subEpsilon, int M)
+{
+ int i = 0;
+ unsigned char result = 0;
+ for (i = 0; i < M; ++i) {
+ result |= (subEpsilon[i] << (M - i - 1));
+ }
+ return result;
+}
+
+int findIndex(PokerNi *tab, int tabSize, unsigned int flag)
+{
+ int i = 0;
+ for (i = 0; i < tabSize; ++i) {
+ if (tab[i].flag == flag) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int PokerDetect(int M, int n, BitSequence *epsilon)
+{
+ int ret = 0;
+ int i = 0, j = 0, N = n / M, index = 0, c = 0;
+ int maxElements = (int) pow(2, M);
+ double p_value = 0.0, sum_ni = 0.0, mp = 0.0, V = 0.0;
+ unsigned int flag = 0;
+ PokerNi *tab = NULL;
+
+ if (M > 8) {
+ return 0;
+ }
+
+ tab = (PokerNi *)calloc(maxElements, sizeof(PokerNi));
+ if (NULL == tab) {
+ return 0;
+ }
+ for (i = 0; i < maxElements; ++i) {
+ tab[i].flag = (unsigned int) i;
+ tab[i].count = 0;
+ }
+
+ for (i = 0, j = 0; j < N; ++j, i += M) {
+ flag = toByte(epsilon + i, M);
+ index = findIndex(tab, maxElements, flag);
+ if (-1 == index) {
+ goto end;
+ }
+ tab[index].count += 1;
+ }
+
+ for (i = 0; i < maxElements; ++i) {
+ sum_ni += pow(tab[i].count, 2);
+ }
+ mp = (double)maxElements / N;
+ V = mp * sum_ni - N;
+
+ p_value = cephes_igamc((double)(maxElements - 1) / 2, V / 2);
+ if (p_value < ALPHA) {
+ goto end;
+ }
+
+ ret = 1;
+
+ end:
+ free(tab);
+ return ret;
+} \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/randomExcursions.c b/decoders/session_flags/mesa_sts/src/randomExcursions.c
new file mode 100644
index 0000000..135396d
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/randomExcursions.c
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ R A N D O M E X C U R S I O N S T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+RandomExcursions(int n, BitSequence *epsilon)
+{
+ int ret = 0;
+ int b, i, j, k, J, x;
+ int cycleStart, cycleStop, *cycle = NULL, *S_k = NULL;
+ const int stateX[8] = { -4, -3, -2, -1, 1, 2, 3, 4 };
+ int counter[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ double p_value, sum, constraint, nu[6][8];
+ double pi[5][6] = { {0.0000000000, 0.00000000000, 0.00000000000, 0.00000000000, 0.00000000000, 0.0000000000},
+ {0.5000000000, 0.25000000000, 0.12500000000, 0.06250000000, 0.03125000000, 0.0312500000},
+ {0.7500000000, 0.06250000000, 0.04687500000, 0.03515625000, 0.02636718750, 0.0791015625},
+ {0.8333333333, 0.02777777778, 0.02314814815, 0.01929012346, 0.01607510288, 0.0803755143},
+ {0.8750000000, 0.01562500000, 0.01367187500, 0.01196289063, 0.01046752930, 0.0732727051} };
+
+ if ( ((S_k = (int *)calloc(n, sizeof(int))) == NULL) ||
+ ((cycle = (int *)calloc(MAX(1000, n/100), sizeof(int))) == NULL) ) {
+ printf("Random Excursions Test: Insufficent Work Space Allocated.\n");
+ if ( S_k != NULL )
+ free(S_k);
+ if ( cycle != NULL )
+ free(cycle);
+ return 0;
+ }
+
+ J = 0; /* DETERMINE CYCLES */
+ S_k[0] = 2*(int)epsilon[0] - 1;
+ for( i=1; i<n; i++ ) {
+ S_k[i] = S_k[i-1] + 2*epsilon[i] - 1;
+ if ( S_k[i] == 0 ) {
+ J++;
+ if ( J > MAX(1000, n/100) ) {
+ printf("ERROR IN FUNCTION randomExcursions: EXCEEDING THE MAX NUMBER OF CYCLES EXPECTED\n.");
+ goto end;
+ }
+ cycle[J] = i;
+ }
+ }
+ if ( S_k[n-1] != 0 )
+ J++;
+ cycle[J] = n;
+
+ constraint = MAX(0.005*pow(n, 0.5), 500);
+ if (J < constraint) {
+ ret = 1; //TODO
+ goto end;
+ }
+ else {
+ cycleStart = 0;
+ cycleStop = cycle[1];
+ for ( k=0; k<6; k++ )
+ for ( i=0; i<8; i++ )
+ nu[k][i] = 0.;
+ for ( j=1; j<=J; j++ ) { /* FOR EACH CYCLE */
+ for ( i=0; i<8; i++ )
+ counter[i] = 0;
+ for ( i=cycleStart; i<cycleStop; i++ ) {
+ if ( (S_k[i] >= 1 && S_k[i] <= 4) || (S_k[i] >= -4 && S_k[i] <= -1) ) {
+ if ( S_k[i] < 0 )
+ b = 4;
+ else
+ b = 3;
+ counter[S_k[i]+b]++;
+ }
+ }
+ cycleStart = cycle[j]+1;
+ if ( j < J )
+ cycleStop = cycle[j+1];
+
+ for ( i=0; i<8; i++ ) {
+ if ( (counter[i] >= 0) && (counter[i] <= 4) )
+ nu[counter[i]][i]++;
+ else if ( counter[i] >= 5 )
+ nu[5][i]++;
+ }
+ }
+
+ for ( i=0; i<8; i++ ) {
+ x = stateX[i];
+ sum = 0.;
+ for ( k=0; k<6; k++ )
+ sum += pow(nu[k][i] - J*pi[(int)fabs(x)][k], 2) / (J*pi[(int)fabs(x)][k]);
+ p_value = cephes_igamc(2.5, sum/2.0);
+
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+// fprintf(stats[TEST_RND_EXCURSION], "WARNING: P_VALUE IS OUT OF RANGE.\n");
+ goto end;
+ }
+
+// fprintf(stats[TEST_RND_EXCURSION], "%s\t\tx = %2d chi^2 = %9.6f p_value = %f\n",
+// p_value < ALPHA ? "FAILURE" : "SUCCESS", x, sum, p_value);
+// fprintf(results[TEST_RND_EXCURSION], "%f\n", p_value); fflush(results[TEST_RND_EXCURSION]);
+ if (p_value < ALPHA) {
+ goto end;
+ }
+ }
+ }
+// fprintf(stats[TEST_RND_EXCURSION], "\n"); fflush(stats[TEST_RND_EXCURSION]);
+ ret = 1;
+
+ end:
+ free(S_k);
+ free(cycle);
+ return ret;
+}
diff --git a/decoders/session_flags/mesa_sts/src/randomExcursionsVariant.c b/decoders/session_flags/mesa_sts/src/randomExcursionsVariant.c
new file mode 100644
index 0000000..447ddf6
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/randomExcursionsVariant.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ R A N D O M E X C U R S I O N S V A R I A N T T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+RandomExcursionsVariant(int n, BitSequence *epsilon)
+{
+ int ret = 0;
+ int i, p, J, x, constraint, count, *S_k = NULL;
+ const int stateX[18] = { -9, -8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ double p_value;
+
+ if ( (S_k = (int *)calloc(n, sizeof(int))) == NULL ) {
+ return 0;
+ }
+ J = 0;
+ S_k[0] = 2*(int)epsilon[0] - 1;
+ for ( i=1; i<n; i++ ) {
+ S_k[i] = S_k[i-1] + 2*epsilon[i] - 1;
+ if ( S_k[i] == 0 )
+ J++;
+ }
+ if ( S_k[n-1] != 0 )
+ J++;
+
+ constraint = (int)MAX(0.005*pow(n, 0.5), 500);
+ if (J < constraint) {
+ ret = 1; //TODO
+ goto end;
+ }
+ else {
+ for ( p=0; p<=17; p++ ) {
+ x = stateX[p];
+ count = 0;
+ for ( i=0; i<n; i++ )
+ if ( S_k[i] == x )
+ count++;
+ p_value = erfc(fabs(count-J)/(sqrt(2.0*J*(4.0*fabs(x)-2))));
+
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+ goto end;
+ }
+ if (p_value < ALPHA) {
+ goto end;
+ }
+ }
+ }
+
+ ret = 1;
+
+ end:
+ free(S_k);
+ return ret;
+}
diff --git a/decoders/session_flags/mesa_sts/src/rank.c b/decoders/session_flags/mesa_sts/src/rank.c
new file mode 100644
index 0000000..e9898ca
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/rank.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+#include "include/matrix.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ R A N K T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+Rank(int n, BitSequence *epsilon)
+{
+ int ret = 0;
+ int N, i, k, r;
+ double p_value, product, chi_squared, arg1, p_32, p_31, p_30, R, F_32, F_31, F_30;
+ BitSequence **matrix = create_matrix(32, 32);
+
+ N = n/(32*32);
+ if ( isZero(N) ) {
+ p_value = 0.00;
+ }
+ else {
+ r = 32; /* COMPUTE PROBABILITIES */
+ product = 1;
+ for ( i=0; i<=r-1; i++ )
+ product *= ((1.e0-pow(2, i-32))*(1.e0-pow(2, i-32)))/(1.e0-pow(2, i-r));
+ p_32 = pow(2, r*(32+32-r)-32*32) * product;
+
+ r = 31;
+ product = 1;
+ for ( i=0; i<=r-1; i++ )
+ product *= ((1.e0-pow(2, i-32))*(1.e0-pow(2, i-32)))/(1.e0-pow(2, i-r));
+ p_31 = pow(2, r*(32+32-r)-32*32) * product;
+
+ p_30 = 1 - (p_32+p_31);
+
+ F_32 = 0;
+ F_31 = 0;
+ for ( k=0; k<N; k++ ) { /* FOR EACH 32x32 MATRIX */
+ def_matrix(32, 32, matrix, k, epsilon);
+#if (DISPLAY_MATRICES == 1)
+ display_matrix(32, 32, matrix);
+#endif
+ R = computeRank(32, 32, matrix);
+ if ( R == 32 )
+ F_32++; /* DETERMINE FREQUENCIES */
+ if ( R == 31 )
+ F_31++;
+ }
+ F_30 = (double)N - (F_32+F_31);
+
+ chi_squared =(pow(F_32 - N*p_32, 2)/(double)(N*p_32) +
+ pow(F_31 - N*p_31, 2)/(double)(N*p_31) +
+ pow(F_30 - N*p_30, 2)/(double)(N*p_30));
+
+ arg1 = -chi_squared/2.e0;
+
+ p_value = exp(arg1);
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+ goto end;
+ }
+ }
+
+ if (p_value < ALPHA) {
+ goto end;
+ }
+
+ ret = 1;
+
+ end:
+ delete_matrix(32, matrix);
+ return ret;
+}
diff --git a/decoders/session_flags/mesa_sts/src/runs.c b/decoders/session_flags/mesa_sts/src/runs.c
new file mode 100644
index 0000000..ddea5a0
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/runs.c
@@ -0,0 +1,46 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ R U N S T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+Runs(int n, BitSequence *epsilon)
+{
+ int S, k;
+ double pi, V, erfc_arg, p_value;
+
+ S = 0;
+ for ( k=0; k<n; k++ )
+ if ( epsilon[k] )
+ S++;
+ pi = (double)S / (double)n;
+
+ if ( fabs(pi - 0.5) > (2.0 / sqrt(n)) ) {
+ p_value = 0.0;
+ }
+ else {
+
+ V = 1;
+ for ( k=1; k<n; k++ )
+ if ( epsilon[k] != epsilon[k-1] )
+ V++;
+
+ erfc_arg = fabs(V - 2.0 * n * pi * (1-pi)) / (2.0 * pi * (1-pi) * sqrt(2*n));
+ p_value = erfc(erfc_arg);
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+ return 0;
+ }
+ }
+
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
diff --git a/decoders/session_flags/mesa_sts/src/runsDistribution.c b/decoders/session_flags/mesa_sts/src/runsDistribution.c
new file mode 100644
index 0000000..4fad006
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/runsDistribution.c
@@ -0,0 +1,82 @@
+#include <stdlib.h>
+#include <math.h>
+#include "include/stat_fncs.h"
+#include "include/cephes.h"
+
+int RunsDistribution(int n, BitSequence *epsilon)
+{
+ int ret = 0;
+ int i = 0, j = 0, k = 0;
+ unsigned char runFlag = 0x00;
+ double p_value = 0.0, sum_bi = 0.0, sum_gi = 0.0, V = 0.0;
+ double *bi = NULL, *gi = NULL, *e = NULL;
+ double bit = 0.0, git = 0.0, et = 0.0;
+
+ bi = (double *)calloc(n, sizeof(double));
+ if (NULL == bi) {
+ goto end;
+ }
+ gi = (double *)calloc(n, sizeof(double));
+ if (NULL == gi) {
+ goto end;
+ }
+ e = (double *)calloc(n, sizeof(double));
+ if (NULL == e) {
+ goto end;
+ }
+
+ for (i = 1; i <= n; ++i) {
+ e[i - 1] = (double)(n - i + 3) / pow(2, i + 2);
+ if (e[i - 1] >= 5) {
+ k = i;
+ }
+ }
+
+ runFlag = epsilon[0];
+ j = 1;
+ for (i = 1; i < n; ++i) {
+ if (epsilon[i] != runFlag) {
+ if (runFlag == 0x00) {
+ gi[j - 1] += 1;
+ } else if (runFlag == 0x01) {
+ bi[j - 1] += 1;
+ }
+ runFlag = epsilon[i];
+ j = 1;
+ } else {
+ ++j;
+ }
+ }
+
+ for (i = 0; i < k; ++i) {
+ bit = bi[i];
+ et = e[i];
+ sum_bi += pow(bit - et, 2) / et;
+ }
+ for (i = 0; i < k; ++i) {
+ git = gi[i];
+ et = e[i];
+ sum_gi += pow(git - et, 2) / et;
+ }
+ V = sum_bi + sum_gi;
+
+ p_value = cephes_igamc(k - 1, V / 2);
+ if (p_value < ALPHA) {
+ goto end;
+ }
+
+ ret = 1;
+
+ end:
+ if (NULL != bi) {
+ free(bi);
+ }
+ if (NULL != gi) {
+ free(gi);
+ }
+ if (NULL != e) {
+ free(e);
+ }
+ return ret;
+}
+
diff --git a/decoders/session_flags/mesa_sts/src/selfCorrelation.c b/decoders/session_flags/mesa_sts/src/selfCorrelation.c
new file mode 100644
index 0000000..d47a67b
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/selfCorrelation.c
@@ -0,0 +1,25 @@
+#include <math.h>
+#include "include/stat_fncs.h"
+
+int SelfCorrelation(int d, int n, BitSequence *epsilon)
+{
+ int i = 0;
+ int n_d = n - d;
+ int Ad = 0;
+ double V = 0.0, p_value = 0.0, sqrt2 = 1.41421356237309504880;
+
+ for (i = 0; i < n_d - 1; ++i) {
+ Ad += (epsilon[i] ^ epsilon[i + d]);
+ }
+
+ V = 2 * ((double)Ad - ((double)n_d / 2)) / sqrt(n_d);
+
+ p_value = erfc(fabs(V) / sqrt2);
+
+ if (p_value < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
diff --git a/decoders/session_flags/mesa_sts/src/serial.c b/decoders/session_flags/mesa_sts/src/serial.c
new file mode 100644
index 0000000..3d37272
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/serial.c
@@ -0,0 +1,65 @@
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+double psi2(int m, int n, BitSequence *epsilon);
+
+int
+Serial(int m, int n, BitSequence *epsilon)
+{
+ double p_value1, p_value2, psim0, psim1, psim2, del1, del2;
+
+ psim0 = psi2(m, n, epsilon);
+ psim1 = psi2(m - 1, n, epsilon);
+ psim2 = psi2(m - 2, n, epsilon);
+ del1 = psim0 - psim1;
+ del2 = psim0 - 2.0*psim1 + psim2;
+ p_value1 = cephes_igamc(pow(2, m-1)/2, del1/2.0);
+ p_value2 = cephes_igamc(pow(2, m-2)/2, del2/2.0);
+
+ if (p_value1 < ALPHA || p_value2 < ALPHA) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+double
+psi2(int m, int n, BitSequence *epsilon)
+{
+ int i, j, k, powLen;
+ double sum, numOfBlocks;
+ unsigned int *P;
+
+ if ( (m == 0) || (m == -1) )
+ return 0.0;
+ numOfBlocks = n;
+ powLen = (int)pow(2, m+1)-1;
+ if ( (P = (unsigned int*)calloc(powLen,sizeof(unsigned int)))== NULL ) {
+// fprintf(stats[TEST_SERIAL], "Serial Test: Insufficient memory available.\n");
+// fflush(stats[TEST_SERIAL]);
+ return 0.0;
+ }
+ for ( i=1; i<powLen-1; i++ )
+ P[i] = 0; /* INITIALIZE NODES */
+ for ( i=0; i<numOfBlocks; i++ ) { /* COMPUTE FREQUENCY */
+ k = 1;
+ for ( j=0; j<m; j++ ) {
+ if ( epsilon[(i+j)%n] == 0 )
+ k *= 2;
+ else if ( epsilon[(i+j)%n] == 1 )
+ k = 2*k+1;
+ }
+ P[k-1]++;
+ }
+ sum = 0.0;
+ for ( i=(int)pow(2, m)-1; i<(int)pow(2, m+1)-1; i++ )
+ sum += pow(P[i], 2);
+ sum = (sum * pow(2, m)/(double)n) - (double)n;
+ free(P);
+
+ return sum;
+}
diff --git a/decoders/session_flags/mesa_sts/src/universal.c b/decoders/session_flags/mesa_sts/src/universal.c
new file mode 100644
index 0000000..393ea1b
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/universal.c
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/externs.h"
+#include "include/cephes.h"
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ U N I V E R S A L T E S T
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+int
+Universal(int n, BitSequence *epsilon)
+{
+ int ret = 0;
+ int i, j, p, L, Q, K;
+ double arg, sqrt2, sigma, phi, sum, p_value, c;
+ long *T, decRep;
+ const double expected_value[17] = { 0, 0, 0, 0, 0, 0, 5.2177052, 6.1962507, 7.1836656,
+ 8.1764248, 9.1723243, 10.170032, 11.168765,
+ 12.168070, 13.167693, 14.167488, 15.167379 };
+ const double variance[17] = { 0, 0, 0, 0, 0, 0, 2.954, 3.125, 3.238, 3.311, 3.356, 3.384,
+ 3.401, 3.410, 3.416, 3.419, 3.421 };
+
+ /* * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * THE FOLLOWING REDEFINES L, SHOULD THE CONDITION: n >= 1010*2^L*L *
+ * NOT BE MET, FOR THE BLOCK LENGTH L. *
+ * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+ L = 5;
+ if ( n >= 387840 ) L = 6;
+ if ( n >= 904960 ) L = 7;
+ if ( n >= 2068480 ) L = 8;
+ if ( n >= 4654080 ) L = 9;
+ if ( n >= 10342400 ) L = 10;
+ if ( n >= 22753280 ) L = 11;
+ if ( n >= 49643520 ) L = 12;
+ if ( n >= 107560960 ) L = 13;
+ if ( n >= 231669760 ) L = 14;
+ if ( n >= 496435200 ) L = 15;
+ if ( n >= 1059061760 ) L = 16;
+
+ Q = 10*(int)pow(2, L);
+ K = (int) (floor(n/L) - (double)Q); /* BLOCKS TO TEST */
+
+ p = (int)pow(2, L);
+ if ( (L < 6) || (L > 16) || ((double)Q < 10*pow(2, L)) ||
+ ((T = (long *)calloc(p, sizeof(long))) == NULL) ) {
+ return 0;
+ }
+
+ /* COMPUTE THE EXPECTED: Formula 16, in Marsaglia's Paper */
+ c = 0.7 - 0.8/(double)L + (4 + 32/(double)L)*pow(K, -3/(double)L)/15;
+ sigma = c * sqrt(variance[L]/(double)K);
+ sqrt2 = sqrt(2);
+ sum = 0.0;
+ for ( i=0; i<p; i++ )
+ T[i] = 0;
+ for ( i=1; i<=Q; i++ ) { /* INITIALIZE TABLE */
+ decRep = 0;
+ for ( j=0; j<L; j++ )
+ decRep += epsilon[(i-1)*L+j] * (long)pow(2, L-1-j);
+ T[decRep] = i;
+ }
+ for ( i=Q+1; i<=Q+K; i++ ) { /* PROCESS BLOCKS */
+ decRep = 0;
+ for ( j=0; j<L; j++ )
+ decRep += epsilon[(i-1)*L+j] * (long)pow(2, L-1-j);
+ sum += log(i - T[decRep])/log(2);
+ T[decRep] = i;
+ }
+ phi = (double)(sum/(double)K);
+
+ arg = fabs(phi-expected_value[L])/(sqrt2 * sigma);
+ p_value = erfc(arg);
+ if ( isNegative(p_value) || isGreaterThanOne(p_value) ) {
+ goto end;
+ }
+
+ if (p_value < ALPHA) {
+ goto end;
+ }
+
+ ret = 1;
+
+ end:
+ free(T);
+ return ret;
+} \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/src/utilities.c b/decoders/session_flags/mesa_sts/src/utilities.c
new file mode 100644
index 0000000..d3c4973
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/utilities.c
@@ -0,0 +1,45 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+U T I L I T I E S
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include "include/utilities.h"
+
+int
+convertToBits(BYTE *x, int xBitLength, int bitsNeeded, int *num_0s, int *num_1s, int *bitsRead, BitSequence* epsilon)
+{
+ int i, j, count, bit;
+ BYTE mask;
+ int zeros, ones;
+
+ count = 0;
+ zeros = ones = 0;
+ for ( i=0; i<(xBitLength+7)/8; i++ ) {
+ mask = 0x80;
+ for ( j=0; j<8; j++ ) {
+ if ( *(x+i) & mask ) {
+ bit = 1;
+ (*num_1s)++;
+ ones++;
+ }
+ else {
+ bit = 0;
+ (*num_0s)++;
+ zeros++;
+ }
+ mask >>= 1;
+ epsilon[*bitsRead] = bit;
+ (*bitsRead)++;
+ if ( *bitsRead == bitsNeeded )
+ return 1;
+ if ( ++count == xBitLength )
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/decoders/session_flags/mesa_sts/src/version.map b/decoders/session_flags/mesa_sts/src/version.map
new file mode 100644
index 0000000..ea17166
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/src/version.map
@@ -0,0 +1,6 @@
+{
+ global:
+ statistical_test_suite;
+ GIT_VERSION*;
+ local: *;
+}; \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/test/CMakeLists.txt b/decoders/session_flags/mesa_sts/test/CMakeLists.txt
new file mode 100644
index 0000000..f4e7678
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/CMakeLists.txt
@@ -0,0 +1,6 @@
+cmake_minimum_required (VERSION 3.5)
+
+add_executable(gtest_mesa gtest_mesa_sts.cpp)
+target_link_libraries(gtest_mesa gtest pcap pthread libmesa_sts)
+
+file(COPY pcap DESTINATION ./)
diff --git a/decoders/session_flags/mesa_sts/test/gtest_mesa_sts.cpp b/decoders/session_flags/mesa_sts/test/gtest_mesa_sts.cpp
new file mode 100644
index 0000000..2d9658f
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/gtest_mesa_sts.cpp
@@ -0,0 +1,256 @@
+#include <netinet/ip6.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <pcap/pcap.h>
+
+#include "gtest/gtest.h"
+
+#include "mesa_sts.h"
+
+#define MAX_PKT_CNT 1
+
+static int read_pcap_and_judge_randomness(const char* pcap_file, struct sts_result* result)
+{
+ pcap_t *handle;
+ struct pcap_pkthdr *header; // pcap报文头部结构
+ const u_char *packet; // 报文数据指针
+ char errbuf[PCAP_ERRBUF_SIZE];
+ char content[2048] = {0};
+ int content_len = 0;
+ int payload_len;
+ char *payload;
+ int pkt_cnt = 0;
+
+ handle = pcap_open_offline(pcap_file, errbuf);
+ while (pcap_next_ex(handle, &header, &packet) > 0) {
+ unsigned short eth_type = ntohs(*(unsigned short *)(packet + 12));
+ if (eth_type == ETH_P_IP) {
+ int l4_proto = *(unsigned char *)(packet + sizeof(struct ethhdr) + 9);
+ if (l4_proto == IPPROTO_TCP) {
+ int tcp_header_len = (*(unsigned char *)(packet + sizeof(struct ethhdr) + sizeof(struct iphdr) + 12) & 0xf0) >> 2;
+ payload_len = header->caplen - sizeof(struct ethhdr) - sizeof(struct iphdr) - tcp_header_len;
+ payload = (char *)packet + sizeof(struct ethhdr) + sizeof(struct iphdr) + tcp_header_len;
+ } else if (l4_proto == IPPROTO_UDP) {
+ payload_len = header->caplen - sizeof(struct ethhdr) - sizeof(struct iphdr) - sizeof(struct udphdr);
+ payload = (char *)packet + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
+ } else {
+ continue;
+ }
+
+ } else if (eth_type == ETH_P_IPV6) {
+ int l4_proto = *(unsigned char *)(packet + sizeof(struct ethhdr) + 6);
+ if (l4_proto == IPPROTO_TCP) {
+ int tcp_header_len = (*(unsigned char *)(packet + sizeof(struct ethhdr) + sizeof(struct ip6_hdr) + 12) & 0xf0) >> 2;
+ payload_len = header->caplen - sizeof(struct ethhdr) - sizeof(struct ip6_hdr) - tcp_header_len;
+ payload = (char *)packet + sizeof(struct ethhdr) + sizeof(struct ip6_hdr) + tcp_header_len;
+ } else if (l4_proto == IPPROTO_UDP) {
+ payload_len = header->caplen - sizeof(struct ethhdr) - sizeof(struct ip6_hdr) - sizeof(struct udphdr);
+ payload = (char *)packet + sizeof(struct ethhdr) + sizeof(struct ip6_hdr) + sizeof(struct udphdr);
+ } else {
+ continue;
+ }
+ }
+
+ if (payload_len < 100) {
+ continue;
+ }
+
+ memcpy(content + content_len, payload, payload_len);
+ content_len += payload_len;
+ pkt_cnt++;
+ if (pkt_cnt == MAX_PKT_CNT) {
+ break;
+ }
+ }
+
+ mesa_statistical_test_suite(content, content_len, result, 0xffffffff);
+
+ pcap_close(handle);
+
+ return 0;
+}
+
+TEST(random_looking, telegram_mtproto_ipv4_key1)
+{
+ struct sts_result result;
+ read_pcap_and_judge_randomness("pcap/telegram_mtproto_ipv4_key_1.pcap", &result);
+
+ EXPECT_EQ(result.frequency, 1);
+ EXPECT_EQ(result.block_frequency, 1);
+ EXPECT_EQ(result.cumulative_sums, 1);
+ EXPECT_EQ(result.runs, 1);
+ EXPECT_EQ(result.longest_run, 1);
+ EXPECT_EQ(result.rank, 0);
+ EXPECT_EQ(result.non_overlapping_template_matching, 0);
+ EXPECT_EQ(result.overlapping_template_matching, 1);
+ EXPECT_EQ(result.universal, 0);
+ EXPECT_EQ(result.random_excursions, 1);
+ EXPECT_EQ(result.random_excursions_variant, 1);
+ EXPECT_EQ(result.poker_detect, 1);
+ EXPECT_EQ(result.runs_distribution, 1);
+ EXPECT_EQ(result.self_correlation, 1);
+ EXPECT_EQ(result.binary_derivative, 1);
+}
+
+TEST(random_looking, telegram_mtproto_ipv4_key2)
+{
+ struct sts_result result;
+ read_pcap_and_judge_randomness("pcap/telegram_mtproto_ipv4_key_2_dd.pcap", &result);
+
+ EXPECT_EQ(result.frequency, 1);
+ EXPECT_EQ(result.block_frequency, 1);
+ EXPECT_EQ(result.cumulative_sums, 1);
+ EXPECT_EQ(result.runs, 1);
+ EXPECT_EQ(result.longest_run, 1);
+ EXPECT_EQ(result.rank, 0);
+ EXPECT_EQ(result.non_overlapping_template_matching, 0);
+ EXPECT_EQ(result.overlapping_template_matching, 1);
+ EXPECT_EQ(result.universal, 0);
+ EXPECT_EQ(result.random_excursions, 1);
+ EXPECT_EQ(result.random_excursions_variant, 1);
+ EXPECT_EQ(result.poker_detect, 1);
+ EXPECT_EQ(result.runs_distribution, 1);
+ EXPECT_EQ(result.self_correlation, 1);
+ EXPECT_EQ(result.binary_derivative, 1);
+}
+
+TEST(random_looking, telegram_mtproto_ipv4_key3)
+{
+ struct sts_result result;
+ read_pcap_and_judge_randomness("pcap/telegram_mtproto_ipv4_key_3_ee.pcap", &result);
+
+ EXPECT_EQ(result.frequency, 1);
+ EXPECT_EQ(result.block_frequency, 0);
+ EXPECT_EQ(result.cumulative_sums, 1);
+ EXPECT_EQ(result.runs, 0);
+ EXPECT_EQ(result.longest_run, 1);
+ EXPECT_EQ(result.rank, 0);
+ EXPECT_EQ(result.non_overlapping_template_matching, 0);
+ EXPECT_EQ(result.overlapping_template_matching, 1);
+ EXPECT_EQ(result.universal, 0);
+ EXPECT_EQ(result.random_excursions, 1);
+ EXPECT_EQ(result.random_excursions_variant, 1);
+ EXPECT_EQ(result.poker_detect, 0);
+ EXPECT_EQ(result.runs_distribution, 1);
+ EXPECT_EQ(result.self_correlation, 1);
+ EXPECT_EQ(result.binary_derivative, 1);
+}
+
+TEST(random_looking, telegram_mtproto_ipv6_key1)
+{
+ struct sts_result result;
+ read_pcap_and_judge_randomness("pcap/telegram_mtproto_ipv6_key_1.pcap", &result);
+
+ EXPECT_EQ(result.frequency, 1);
+ EXPECT_EQ(result.block_frequency, 1);
+ EXPECT_EQ(result.cumulative_sums, 1);
+ EXPECT_EQ(result.runs, 1);
+ EXPECT_EQ(result.longest_run, 1);
+ EXPECT_EQ(result.rank, 0);
+ EXPECT_EQ(result.non_overlapping_template_matching, 0);
+ EXPECT_EQ(result.overlapping_template_matching, 1);
+ EXPECT_EQ(result.universal, 0);
+ EXPECT_EQ(result.random_excursions, 1);
+ EXPECT_EQ(result.random_excursions_variant, 1);
+ EXPECT_EQ(result.poker_detect, 1);
+ EXPECT_EQ(result.runs_distribution, 1);
+ EXPECT_EQ(result.self_correlation, 1);
+ EXPECT_EQ(result.binary_derivative, 1);
+}
+
+TEST(random_looking, telegram_mtproto_ipv6_key2)
+{
+ struct sts_result result;
+ read_pcap_and_judge_randomness("pcap/telegram_mtproto_ipv6_key_2_dd.pcap", &result);
+
+ EXPECT_EQ(result.frequency, 1);
+ EXPECT_EQ(result.block_frequency, 1);
+ EXPECT_EQ(result.cumulative_sums, 1);
+ EXPECT_EQ(result.runs, 1);
+ EXPECT_EQ(result.longest_run, 1);
+ EXPECT_EQ(result.rank, 0);
+ EXPECT_EQ(result.non_overlapping_template_matching, 0);
+ EXPECT_EQ(result.overlapping_template_matching, 1);
+ EXPECT_EQ(result.universal, 0);
+ EXPECT_EQ(result.random_excursions, 1);
+ EXPECT_EQ(result.random_excursions_variant, 1);
+ EXPECT_EQ(result.poker_detect, 1);
+ EXPECT_EQ(result.runs_distribution, 1);
+ EXPECT_EQ(result.self_correlation, 1);
+ EXPECT_EQ(result.binary_derivative, 1);
+}
+
+TEST(random_looking, telegram_mtproto_ipv6_key3)
+{
+ struct sts_result result;
+ read_pcap_and_judge_randomness("pcap/telegram_mtproto_ipv6_key_3_ee.pcap", &result);
+
+ EXPECT_EQ(result.frequency, 1);
+ EXPECT_EQ(result.block_frequency, 0);
+ EXPECT_EQ(result.cumulative_sums, 1);
+ EXPECT_EQ(result.runs, 1);
+ EXPECT_EQ(result.longest_run, 1);
+ EXPECT_EQ(result.rank, 0);
+ EXPECT_EQ(result.non_overlapping_template_matching, 0);
+ EXPECT_EQ(result.overlapping_template_matching, 1);
+ EXPECT_EQ(result.universal, 0);
+ EXPECT_EQ(result.random_excursions, 1);
+ EXPECT_EQ(result.random_excursions_variant, 1);
+ EXPECT_EQ(result.poker_detect, 0);
+ EXPECT_EQ(result.runs_distribution, 1);
+ EXPECT_EQ(result.self_correlation, 1);
+ EXPECT_EQ(result.binary_derivative, 1);
+}
+
+TEST(non_random_looking, wechat_voice_call)
+{
+ struct sts_result result;
+ read_pcap_and_judge_randomness("pcap/202202161604_win_wifi_30M_pure_wechat_wechat3.5.0.46_voice-call_120s_2_multinat.pcap", &result);
+
+ EXPECT_EQ(result.frequency, 0);
+ EXPECT_EQ(result.block_frequency, 1);
+ EXPECT_EQ(result.cumulative_sums, 0);
+ EXPECT_EQ(result.runs, 0);
+ EXPECT_EQ(result.longest_run, 0);
+ EXPECT_EQ(result.rank, 0);
+ EXPECT_EQ(result.non_overlapping_template_matching, 0);
+ EXPECT_EQ(result.overlapping_template_matching, 1);
+ EXPECT_EQ(result.universal, 0);
+ EXPECT_EQ(result.random_excursions, 1);
+ EXPECT_EQ(result.random_excursions_variant, 1);
+ EXPECT_EQ(result.poker_detect, 1);
+ EXPECT_EQ(result.runs_distribution, 0);
+ EXPECT_EQ(result.self_correlation, 0);
+ EXPECT_EQ(result.binary_derivative, 1);
+}
+
+TEST(non_random_looking, http)
+{
+ struct sts_result result;
+ read_pcap_and_judge_randomness("pcap/xingongsuo_kouling_http_C2S.pcap", &result);
+
+ EXPECT_EQ(result.frequency, 0);
+ EXPECT_EQ(result.block_frequency, 0);
+ EXPECT_EQ(result.cumulative_sums, 0);
+ EXPECT_EQ(result.runs, 1);
+ EXPECT_EQ(result.longest_run, 0);
+ EXPECT_EQ(result.rank, 1);
+ EXPECT_EQ(result.non_overlapping_template_matching, 0);
+ EXPECT_EQ(result.overlapping_template_matching, 1);
+ EXPECT_EQ(result.universal, 0);
+ EXPECT_EQ(result.random_excursions, 1);
+ EXPECT_EQ(result.random_excursions_variant, 1);
+ EXPECT_EQ(result.poker_detect, 0);
+ EXPECT_EQ(result.runs_distribution, 0);
+ EXPECT_EQ(result.self_correlation, 0);
+ EXPECT_EQ(result.binary_derivative, 1);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ //testing::GTEST_FLAG(filter) = "random_looking.telegram_mtproto_ipv6_key1";
+ return RUN_ALL_TESTS();
+} \ No newline at end of file
diff --git a/decoders/session_flags/mesa_sts/test/pcap/202202161604_win_wifi_30M_pure_wechat_wechat3.5.0.46_voice-call_120s_2_multinat.pcap b/decoders/session_flags/mesa_sts/test/pcap/202202161604_win_wifi_30M_pure_wechat_wechat3.5.0.46_voice-call_120s_2_multinat.pcap
new file mode 100644
index 0000000..63787b7
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/pcap/202202161604_win_wifi_30M_pure_wechat_wechat3.5.0.46_voice-call_120s_2_multinat.pcap
Binary files differ
diff --git a/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_1.pcap b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_1.pcap
new file mode 100644
index 0000000..7b966dd
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_1.pcap
Binary files differ
diff --git a/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_2_dd.pcap b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_2_dd.pcap
new file mode 100644
index 0000000..59eb480
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_2_dd.pcap
Binary files differ
diff --git a/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_3_ee.pcap b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_3_ee.pcap
new file mode 100644
index 0000000..8c70a1b
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv4_key_3_ee.pcap
Binary files differ
diff --git a/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_1.pcap b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_1.pcap
new file mode 100644
index 0000000..e0b3144
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_1.pcap
Binary files differ
diff --git a/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_2_dd.pcap b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_2_dd.pcap
new file mode 100644
index 0000000..0659281
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_2_dd.pcap
Binary files differ
diff --git a/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_3_ee.pcap b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_3_ee.pcap
new file mode 100644
index 0000000..03a3678
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/pcap/telegram_mtproto_ipv6_key_3_ee.pcap
Binary files differ
diff --git a/decoders/session_flags/mesa_sts/test/pcap/xingongsuo_kouling_http_C2S.pcap b/decoders/session_flags/mesa_sts/test/pcap/xingongsuo_kouling_http_C2S.pcap
new file mode 100644
index 0000000..cd05da7
--- /dev/null
+++ b/decoders/session_flags/mesa_sts/test/pcap/xingongsuo_kouling_http_C2S.pcap
Binary files differ
diff --git a/decoders/session_flags/onlinemean.c b/decoders/session_flags/onlinemean.c
new file mode 100644
index 0000000..aeb7427
--- /dev/null
+++ b/decoders/session_flags/onlinemean.c
@@ -0,0 +1,37 @@
+#include <math.h>
+#include "onlinemean.h"
+
+void OnlineMean_Init(OnlineMean_t *oMean) {
+ OnlineMean_Reset(oMean);
+}
+
+void OnlineMean_Update(OnlineMean_t *oMean, float newValue) {
+ oMean->count++;
+ if (oMean->count > 1) {
+ float delta = newValue - oMean->mean;
+ oMean->mean += delta / oMean->count;
+ oMean->varsum += delta * (newValue - oMean->mean);
+ } else {
+ oMean->mean = newValue;
+ }
+}
+
+float OnlineMean_GetMean(OnlineMean_t *oMean) {
+ return oMean->mean;
+}
+
+float OnlineMean_GetStd(OnlineMean_t *oMean) {
+ if (oMean->count == 0)
+ return 0;
+#if UNBIASED_ESTIMATOR
+ return sqrt(oMean->varsum / (oMean->count - 1));
+#else
+ return sqrt(oMean->varsum / oMean->count);
+#endif /* UNBIASED_ESTIMATOR */
+}
+
+void OnlineMean_Reset(OnlineMean_t *oMean) {
+ oMean->count = 0;
+ oMean->mean = 0.f;
+ oMean->varsum = 0.f;
+}
diff --git a/decoders/session_flags/onlinemean.h b/decoders/session_flags/onlinemean.h
new file mode 100644
index 0000000..6cb5c95
--- /dev/null
+++ b/decoders/session_flags/onlinemean.h
@@ -0,0 +1,30 @@
+#ifndef ONLINEMEAN_H_
+#define ONLINEMEAN_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Set to 1 to return unbiased (sample) variance
+ * rather than a population variance */
+#define UNBIASED_ESTIMATOR 0
+
+#include <stdint.h>
+
+typedef struct OnlineMean {
+ float mean;
+ float varsum; // variance sum
+ uint32_t count;
+} OnlineMean_t;
+
+void OnlineMean_Init(OnlineMean_t *oMean);
+void OnlineMean_Update(OnlineMean_t *oMean, float newValue);
+float OnlineMean_GetMean(OnlineMean_t *oMean);
+float OnlineMean_GetStd(OnlineMean_t *oMean);
+void OnlineMean_Reset(OnlineMean_t *oMean);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ONLINEMEAN_H_ */
diff --git a/decoders/session_flags/session_flags.cpp b/decoders/session_flags/session_flags.cpp
new file mode 100644
index 0000000..6f7443f
--- /dev/null
+++ b/decoders/session_flags/session_flags.cpp
@@ -0,0 +1,824 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <cJSON.h>
+#include <pthread.h>
+
+#include "onlinemean.h"
+#include "session_flags_internal.h"
+#include "fet.h"
+#include "stellar/stellar.h"
+#include "mesa_sts.h"
+#include "stellar/session.h"
+#include "stellar/log.h"
+
+#define SESSION_BUKLY_THRESHORD 0.95
+#define SESSION_DOWNLOAD_THRESHORD 0.95
+#define SESSION_CBR_STREAMING_THRESHORD 0.15
+#define SESSION_STREAMING_THRESHORD 0.86
+#define SESSION_PSEUDO_UNIDIRECTIONA_THRESHORD 0.95
+#define SESSION_INTERACTIVE_THRESHORD 0.7
+
+#define SESSION_BIDIRECTIONAL_THRESHORD_MIN 0.7
+#define SESSION_BIDIRECTIONAL_THRESHORD_MAX 1.43
+#define SESSION_EWMA_FACTOR 0.9
+
+
+#define SESSION_RANDOM_LOOKING_PAYLOAD_LEN_MIN 100
+
+#define SESSION_ITER_INTERVAL_MS 1000
+#define SESSION_EWMA_ITER_CNT_MIN 5
+
+#define LARGE_PKT_SIZE_MIN 600
+#define LARGE_PKT_SIZE_MAX 1400
+
+#define SESSION_FLAGS_IDENTIFY_STR_LEN 256
+
+extern struct session_flags_init_conf g_sf_conf;
+
+extern uint32_t sts_random_switch;
+
+thread_local OnlineMean_t g_large_pkt_omean = {
+ .mean = 0,
+ .varsum = 0,
+ .count = 0
+};//record the mean and std of packet size, to calculate large packet size
+thread_local uint64_t g_large_pkt_size_update_ms = 0;
+thread_local uint32_t g_large_pkt_size = 0;
+#define LARGE_PKT_SIZE_UPDATE_COUNT_MIN 10000
+#define LARGE_PKT_SIZE_UPDATE_INTERVAL_MS 10000
+
+static void session_flags_calculate_local(struct session_flags_stat *stat, enum session_direction session_dir)
+{
+ if (session_dir == SESSION_DIRECTION_INBOUND)
+ {
+ stat->result.flags |= SESSION_FLAGS_LOCAL_SERVER;
+ stat->result.identify[session_flags_local_server_mask] = 1;
+ }
+ else
+ {
+ stat->result.flags |= SESSION_FLAGS_LOCAL_CLIENT;
+ stat->result.identify[session_flags_local_client_mask] = 1;
+ }
+
+ return;
+}
+
+void session_flags_stat_init(struct session_flags_stat *stat, enum session_direction session_dir)
+{
+ session_flags_calculate_local(stat, session_dir);
+
+ OnlineMean_Init(&stat->iter.c2s.omean);
+ OnlineMean_Init(&stat->iter.s2c.omean);
+
+ stat->main_dir = MAIN_DIR_UNKONWN;
+
+ return;
+};
+
+static void session_flags_EWMA_iter(float *ori_value, float iter_value)
+{
+ float temp = *ori_value;
+
+ if (temp == 0)
+ {
+ temp = iter_value;
+ }
+ else
+ {
+ temp = SESSION_EWMA_FACTOR * temp + (1 - SESSION_EWMA_FACTOR) * iter_value;
+ }
+
+ *ori_value = temp;
+}
+
+static void session_flags_calculate_pseudo_unidirection(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL)
+ return ;
+
+ float c2s_ratio, s2c_ratio;
+ float pseudo_unidirectional;
+ uint64_t delta_payload_pkts = stat->c2s.delta_payload_pkts + stat->s2c.delta_payload_pkts;
+
+ if (delta_payload_pkts == 0)
+ {
+ return;
+ }
+
+ c2s_ratio = stat->c2s.delta_payload_pkts * 1.f / delta_payload_pkts;
+ s2c_ratio = stat->s2c.delta_payload_pkts * 1.f / delta_payload_pkts;
+
+ session_flags_EWMA_iter(&stat->iter.c2s.pseudo_unidirectional, c2s_ratio);
+ session_flags_EWMA_iter(&stat->iter.s2c.pseudo_unidirectional, s2c_ratio);
+
+ if (stat->iter.iter_cnt < SESSION_EWMA_ITER_CNT_MIN || stat->main_dir == MAIN_DIR_UNKONWN)
+ {
+ return;
+ }
+
+ if (stat->main_dir == FLOW_TYPE_C2S)
+ {
+ pseudo_unidirectional = stat->iter.c2s.pseudo_unidirectional;
+ }
+ else
+ {
+ pseudo_unidirectional = stat->iter.s2c.pseudo_unidirectional;
+ }
+
+ if (pseudo_unidirectional > SESSION_PSEUDO_UNIDIRECTIONA_THRESHORD)
+ {
+ stat->result.flags |= SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL;
+ stat->result.identify[session_flags_pseudo_unidirectional_mask] = all_pkts;
+ }
+}
+
+static void session_flags_calculate_unidirection(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_UNIDIRECTIONAL || stat->s2c.pkts == 0 || stat->c2s.pkts == 0)
+ return ;
+
+ if (stat->c2s.payload_pkts == 0 || stat->s2c.payload_pkts == 0)
+ {
+ stat->result.flags |= SESSION_FLAGS_UNIDIRECTIONAL;
+ stat->result.identify[session_flags_unidirectional_mask] = all_pkts;
+ }
+}
+
+static void session_flags_calculate_streaming(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_STREAMING)
+ return;
+
+ float c2s_ratio, s2c_ratio;
+ float streaming;
+ uint64_t delta_bytes = stat->c2s.delta_bytes + stat->s2c.delta_bytes;
+
+ if (delta_bytes == 0)
+ {
+ return;
+ }
+
+ c2s_ratio = stat->c2s.delta_bytes * 1.f / delta_bytes;
+ s2c_ratio = stat->s2c.delta_bytes * 1.f / delta_bytes;
+
+ session_flags_EWMA_iter(&stat->iter.c2s.streaming, c2s_ratio);
+ session_flags_EWMA_iter(&stat->iter.s2c.streaming, s2c_ratio);
+
+ if (stat->iter.iter_cnt < SESSION_EWMA_ITER_CNT_MIN || stat->main_dir == MAIN_DIR_UNKONWN)
+ {
+ return;
+ }
+
+ if (stat->main_dir == FLOW_TYPE_C2S)
+ {
+ streaming = stat->iter.c2s.streaming;
+ }
+ else
+ {
+ streaming = stat->iter.s2c.streaming;
+ }
+
+ if (streaming > SESSION_STREAMING_THRESHORD)
+ {
+ stat->result.flags |= SESSION_FLAGS_STREAMING;
+ stat->result.identify[session_flags_streaming_mask] = all_pkts;
+ }
+}
+
+static void session_flags_calculate_download(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_DOWNLOAD)
+ return ;
+
+ float c2s_ratio, s2c_ratio;
+ float download;
+ uint64_t delta_payload_pkts = stat->c2s.delta_payload_pkts + stat->s2c.delta_payload_pkts;
+
+ if (delta_payload_pkts == 0)
+ {
+ return;
+ }
+
+ c2s_ratio = stat->c2s.delta_large_pkts * 1.f / delta_payload_pkts;
+ s2c_ratio = stat->s2c.delta_large_pkts * 1.f / delta_payload_pkts;
+
+ session_flags_EWMA_iter(&stat->iter.c2s.download, c2s_ratio);
+ session_flags_EWMA_iter(&stat->iter.s2c.download, s2c_ratio);
+
+ if (stat->iter.iter_cnt < SESSION_EWMA_ITER_CNT_MIN || stat->main_dir == MAIN_DIR_UNKONWN)
+ {
+ return;
+ }
+
+ if (stat->main_dir == FLOW_TYPE_C2S)
+ {
+ download = stat->iter.c2s.download;
+ }
+ else
+ {
+ download = stat->iter.s2c.download;
+ }
+
+ if (download > SESSION_DOWNLOAD_THRESHORD)
+ {
+ stat->result.flags |= SESSION_FLAGS_DOWNLOAD;
+ stat->result.identify[session_flags_download_mask] = all_pkts;
+ }
+}
+
+static void session_flags_calculate_bulky(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_BULKY)
+ return ;
+
+ float bulky;
+
+ if (stat->c2s.delta_pkts != 0)
+ {
+ float c2s_ratio;
+ c2s_ratio = stat->c2s.delta_large_pkts * 1.f / stat->c2s.delta_pkts;
+ session_flags_EWMA_iter(&stat->iter.c2s.bulky, c2s_ratio);
+ }
+
+ if (stat->s2c.delta_pkts != 0)
+ {
+ float s2c_ratio;
+ s2c_ratio = stat->s2c.delta_large_pkts * 1.f / stat->s2c.delta_pkts;
+ session_flags_EWMA_iter(&stat->iter.s2c.bulky, s2c_ratio);
+ }
+
+ if (stat->iter.iter_cnt < SESSION_EWMA_ITER_CNT_MIN || stat->main_dir == MAIN_DIR_UNKONWN)
+ {
+ return;
+ }
+
+ if (stat->main_dir == FLOW_TYPE_C2S)
+ {
+ bulky = stat->iter.c2s.bulky;
+ }
+ else
+ {
+ bulky = stat->iter.s2c.bulky;
+ }
+
+ if (bulky > SESSION_BUKLY_THRESHORD)
+ {
+ stat->result.flags |= SESSION_FLAGS_BULKY;
+ stat->result.identify[session_flags_bulky_mask] = all_pkts;
+ }
+}
+
+static void flow_stat_update(struct session *session, struct flow_stat *flow, uint64_t bytes)
+{
+ flow->bytes += bytes;
+ flow->pkts++;
+
+ flow->delta_pkts++;
+ flow->delta_bytes += bytes;
+
+ const struct packet *pkt = session_get0_current_packet(session);
+ size_t payload_len = packet_get_payload_len(pkt);
+
+ if (payload_len > 0)
+ {
+ flow->payload_pkts++;
+ flow->delta_payload_pkts++;
+ }
+
+ if (bytes > g_large_pkt_size)
+ {
+ flow->large_pkts++;
+ flow->delta_large_pkts++;
+ }
+
+ return;
+}
+
+static void session_flags_calculate_main_dir(struct session_flags_stat *stat)
+{
+ if (stat->main_dir != MAIN_DIR_UNKONWN)
+ {
+ return;
+ }
+
+ if (stat->c2s.bytes > stat->s2c.bytes)
+ {
+ stat->main_dir = FLOW_TYPE_C2S;
+ }
+ else
+ {
+ stat->main_dir = FLOW_TYPE_S2C;
+ }
+}
+
+float session_flags_calculate_CV(OnlineMean_t * omean)
+{
+ float CV = -1.f;
+ float mean = OnlineMean_GetMean(omean);
+ if (mean!= 0)
+ {
+ CV = OnlineMean_GetStd(omean) / mean;
+ }
+ // printf("CV:%lf\n", CV);
+ return CV;
+}
+
+static void session_flags_calculate_CBR(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_CBR)
+ return;
+
+ float CBR;
+
+ OnlineMean_Update(&stat->iter.c2s.omean, stat->c2s.rate);
+ OnlineMean_Update(&stat->iter.s2c.omean, stat->s2c.rate);
+
+ stat->iter.c2s.CBR = session_flags_calculate_CV(&stat->iter.c2s.omean);
+ stat->iter.s2c.CBR = session_flags_calculate_CV(&stat->iter.s2c.omean);
+
+ if (stat->main_dir == MAIN_DIR_UNKONWN)
+ {
+ return;
+ }
+
+ if (stat->main_dir == FLOW_TYPE_C2S)
+ {
+ CBR = stat->iter.c2s.CBR;
+ }
+ else
+ {
+ CBR = stat->iter.s2c.CBR;
+ }
+
+ if (CBR < SESSION_CBR_STREAMING_THRESHORD && CBR > 0.0)
+ {
+ stat->result.flags |= SESSION_FLAGS_CBR;
+ stat->result.identify[session_flags_cbr_mask] = all_pkts;
+ }
+}
+
+static void session_flags_calculate_interactive(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_INTERACTIVE || stat->c2s.pkts == 0 || stat->s2c.pkts == 0)
+ return;
+
+ if (stat->stream_live_time_ms > g_sf_conf.interactive_starttime_ms && stat->interactive_pulse_num > g_sf_conf.interactive_pulse_num)
+ {
+ stat->result.flags |= SESSION_FLAGS_INTERACTIVE;
+ stat->result.identify[session_flags_interactive_mask] = all_pkts;
+ }
+}
+
+static void session_flags_calculate_interactive_pulse(struct session_flags_stat *stat, uint64_t cur_ms, uint64_t all_pkts)
+{
+ uint64_t delta_ms = 0;
+ if (cur_ms > stat->last_pkt_ts_ms)
+ {
+ delta_ms = cur_ms - stat->last_pkt_ts_ms;
+ }
+
+ if (delta_ms > g_sf_conf.interactive_latency_ms)
+ {
+ stat->interactive_pulse_num++;
+ session_flags_calculate_interactive(stat, all_pkts);
+ }
+}
+
+static void session_flags_calculate_bound(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat == NULL || stat->c2s.pkts == 0 || stat->s2c.pkts == 0)
+ {
+ return;
+ }
+
+ if (!(stat->result.flags & SESSION_FLAGS_INBOUND) && !(stat->result.flags & SESSION_FLAGS_OUTBOUND))
+ {
+ if (stat->result.flags & SESSION_FLAGS_LOCAL_CLIENT)
+ {
+ if (stat->main_dir == FLOW_TYPE_C2S)
+ {
+ stat->result.flags |= SESSION_FLAGS_OUTBOUND;
+ stat->result.identify[session_flags_outbound_mask] = all_pkts;
+ }
+ else
+ {
+ stat->result.flags |= SESSION_FLAGS_INBOUND;
+ stat->result.identify[session_flags_inbound_mask] = all_pkts;
+ }
+ }
+ else
+ {
+ if (stat->main_dir == FLOW_TYPE_C2S)
+ {
+ stat->result.flags |= SESSION_FLAGS_INBOUND;
+ stat->result.identify[session_flags_inbound_mask] = all_pkts;
+ }
+ else
+ {
+ stat->result.flags |= SESSION_FLAGS_OUTBOUND;
+ stat->result.identify[session_flags_outbound_mask] = all_pkts;
+ }
+ }
+ }
+}
+
+static void session_flags_calculate_dir(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->c2s.pkts != 0 && !(stat->result.flags & SESSION_FLAGS_C2S))
+ {
+ stat->result.flags |= SESSION_FLAGS_C2S;
+ stat->result.identify[session_flags_c2s_mask] = all_pkts;
+ }
+ if (stat->s2c.pkts != 0 && !(stat->result.flags & SESSION_FLAGS_S2C))
+ {
+ stat->result.flags |= SESSION_FLAGS_S2C;
+ stat->result.identify[session_flags_s2c_mask] = all_pkts;
+ }
+}
+
+static void session_flags_calculate_bidirectional(struct session_flags_stat *stat, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_BIDIRECTIONAL)
+ {
+ return;
+ }
+
+ float rate_ratio = stat->c2s.rate / stat->s2c.rate;
+ if (stat->iter.bidirectional == 0)
+ {
+ stat->iter.bidirectional = rate_ratio;
+ }
+ else
+ {//EWMA
+ stat->iter.bidirectional = SESSION_EWMA_FACTOR * stat->iter.bidirectional + (1 - SESSION_EWMA_FACTOR) * rate_ratio;
+ }
+
+ if (stat->iter.iter_cnt >= SESSION_EWMA_ITER_CNT_MIN)
+ {
+ if (stat->iter.bidirectional > SESSION_BIDIRECTIONAL_THRESHORD_MIN && stat->iter.bidirectional < SESSION_BIDIRECTIONAL_THRESHORD_MAX)
+ {
+ stat->result.flags |= SESSION_FLAGS_BIDIRECTIONAL;
+ stat->result.identify[session_flags_bidirectional_mask] = all_pkts;
+ }
+ }
+
+ return;
+}
+
+static void session_flags_calculate_randomness_by_fet(struct session_flags_stat *stat, size_t payload_len, const char *payload, uint64_t all_pkts)
+{
+ if (stat->random_looking_stat.has_judged_fet)
+ {
+ return;
+ }
+
+ stat->random_looking_stat.has_judged_fet = 1;
+
+ if (g_sf_conf.fet_enabled)
+ {
+ struct fet_detail detail;
+ int is_fet = is_data_fet((unsigned char *)payload, payload_len, &detail);
+
+ stat->result.is_tls = detail.is_tls;
+
+ if (is_fet)
+ {//if payload is fet data, then it is definitely random looking
+ stat->result.flags |= SESSION_FLAGS_RANDOM_LOOKING;
+ stat->result.identify[session_flags_random_looking_mask] = all_pkts;
+ }
+ }
+
+ return;
+}
+
+static void session_flags_calculate_randomness_by_sts(struct session_flags_plugin_info *sf_plugin_info, struct session *session, struct session_flags_stat *stat, size_t payload_len, const char *payload, uint64_t all_pkts)
+{
+ if (stat->random_looking_stat.has_judged_sts || sts_random_switch == 0)
+ {
+ return;
+ }
+
+ if (payload_len < SESSION_RANDOM_LOOKING_PAYLOAD_LEN_MIN)
+ {
+ return;
+ }
+
+ stat->random_looking_stat.has_judged_sts = 1;
+
+ struct sts_result result;
+ uint32_t random_flags_count = 0;
+ memset(&result, 0, sizeof(result));
+
+ mesa_statistical_test_suite((void*)payload, payload_len, &result, sts_random_switch);
+
+ if (result.frequency)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_FREQUENCY;
+ random_flags_count++;
+ }
+ if (result.block_frequency)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_BLOCK_FREQUENCY;
+ random_flags_count++;
+ }
+ if (result.cumulative_sums)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_CUMULATIVE_SUMS;
+ random_flags_count++;
+ }
+ if (result.runs)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_RUNS;
+ random_flags_count++;
+ }
+ if (result.longest_run)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_LONGEST_RUN;
+ random_flags_count++;
+ }
+ if (result.rank)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_RANK;
+ random_flags_count++;
+ }
+ if (result.non_overlapping_template_matching)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_NON_OVERLAPPING_TEMPLATE_MATCHING;
+ random_flags_count++;
+ }
+ if (result.overlapping_template_matching)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_OVERLAPPING_TEMPLATE_MATCHING;
+ random_flags_count++;
+ }
+ if (result.universal)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_UNIVERSAL;
+ random_flags_count++;
+ }
+ if (result.random_excursions)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_RANDOM_EXCURSIONS;
+ random_flags_count++;
+ }
+ if (result.random_excursions_variant)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_RANDOM_EXCURSIONS_VARIANT;
+ random_flags_count++;
+ }
+ if (result.poker_detect)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_POKER_DETECT;
+ random_flags_count++;
+ }
+ if (result.runs_distribution)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_RUNS_DISTRIBUTION;
+ random_flags_count++;
+ }
+ if (result.self_correlation)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_SELF_CORRELATION;
+ random_flags_count++;
+ }
+ if (result.binary_derivative)
+ {
+ stat->result.random_looking_flags |= SESSION_FLAGS_BINARY_DERIVATIVE;
+ random_flags_count++;
+ }
+
+ if (random_flags_count > (g_sf_conf.random_judge_flags_cnt / 2))
+ {
+ stat->result.flags |= SESSION_FLAGS_RANDOM_LOOKING;
+ stat->result.identify[session_flags_random_looking_mask] = all_pkts;
+ }
+
+ STELLAR_LOG_DEBUG(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "[%s] calculate random looking flags, flags:0x%x, pkts_num:%d", session_get0_readable_addr(session), stat->result.random_looking_flags, all_pkts);
+}
+
+static void session_flags_calculate_random_looking(struct session_flags_plugin_info *sf_plugin_info, struct session_flags_stat *stat, struct session *session, int topic_id, uint64_t all_pkts)
+{
+ if (stat->result.flags & SESSION_FLAGS_RANDOM_LOOKING)
+ {
+ return;
+ }
+
+ int udp_topic_id = sf_plugin_info->udp_topic_id;
+
+ const struct packet *pkt = session_get0_current_packet(session);
+ size_t payload_len = packet_get_payload_len(pkt);
+ const char *payload = packet_get_payload(pkt);
+
+ if ((topic_id == udp_topic_id) && g_sf_conf.random_looking_udp_ignore_pkts < 0)// disable random looking udp when random_looking_udp_ignore_pkts<0
+ {
+ return;
+ }
+
+ if (payload_len == 0 || payload == NULL)
+ {
+ return;
+ }
+
+ stat->random_looking_stat.payload_pkt_num++;
+ if ((topic_id == udp_topic_id) && stat->random_looking_stat.payload_pkt_num <= g_sf_conf.random_looking_udp_ignore_pkts)
+ {
+ return;
+ }
+
+ session_flags_calculate_randomness_by_fet(stat, payload_len, payload, all_pkts);
+ if (stat->result.flags & SESSION_FLAGS_RANDOM_LOOKING)
+ {
+ return;
+ }
+
+ session_flags_calculate_randomness_by_sts(sf_plugin_info, session, stat, payload_len, payload, all_pkts);
+
+ return;
+}
+
+static void session_flags_reset_delta(struct session_flags_stat *stat)
+{
+ stat->c2s.delta_bytes = 0;
+ stat->c2s.delta_pkts = 0;
+ stat->c2s.delta_large_pkts = 0;
+ stat->c2s.delta_payload_pkts = 0;
+
+ stat->s2c.delta_bytes = 0;
+ stat->s2c.delta_pkts = 0;
+ stat->s2c.delta_large_pkts = 0;
+ stat->s2c.delta_payload_pkts = 0;
+
+ return;
+}
+
+static void session_flags_calculate_rate(struct session_flags_stat *stat, uint64_t delta_ms)
+{
+ stat->c2s.rate = stat->c2s.delta_bytes * 1.f / delta_ms;
+ stat->s2c.rate = stat->s2c.delta_bytes * 1.f / delta_ms;
+
+ return;
+}
+
+static void session_flags_update_large_pkt_size(struct session_flags_plugin_info *sf_plugin_info, uint32_t pkt_len, uint64_t ms)
+{
+ if (pkt_len > 1500)
+ {
+ return;
+ }
+
+ OnlineMean_Update(&g_large_pkt_omean, pkt_len);
+
+ if (g_large_pkt_size_update_ms == 0)
+ {
+ g_large_pkt_size_update_ms = ms;
+ g_large_pkt_size = g_sf_conf.large_ptks_init_size;
+ return;
+ }
+
+ if (ms - g_large_pkt_size_update_ms >= LARGE_PKT_SIZE_UPDATE_INTERVAL_MS && g_large_pkt_omean.count >= LARGE_PKT_SIZE_UPDATE_COUNT_MIN)
+ {
+ g_large_pkt_size_update_ms = ms;
+ g_large_pkt_size = (uint32_t)(0.84f * OnlineMean_GetStd(&g_large_pkt_omean) + OnlineMean_GetMean(&g_large_pkt_omean));//计算公式 0.84 = (g_large_pkt_size - mean) / std, 0.84为正态分布表中80%的概率对应的z值
+
+ if (g_large_pkt_size < LARGE_PKT_SIZE_MIN)
+ {
+ g_large_pkt_size = LARGE_PKT_SIZE_MIN;
+ }
+ else if (g_large_pkt_size > LARGE_PKT_SIZE_MAX)
+ {
+ g_large_pkt_size = LARGE_PKT_SIZE_MAX;
+ }
+
+ STELLAR_LOG_DEBUG(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "session_flags thread %ld update large_pkt_size %d", pthread_self(), g_large_pkt_size);
+ }
+
+ return;
+}
+
+struct session_flags_result *session_flags(struct session_flags_plugin_info *sf_plugin_info, struct session_flags_ctx *ctx, struct session *session, int topic_id, uint32_t bytes, enum flow_type flow_type, uint64_t ms)
+{
+ struct session_flags_stat *stat = &ctx->stat;
+
+ if (stat == NULL || bytes == 0)
+ {
+ return NULL;
+ }
+
+ session_flags_update_large_pkt_size(sf_plugin_info, bytes, ms);
+
+ uint64_t delta_ms = 0;
+
+ if(stat->session_start_time_ms == 0)
+ {
+ stat->session_start_time_ms = ms;
+ }
+ stat->stream_live_time_ms = ms - stat->session_start_time_ms;
+
+ if (stat->last_iter_ts_ms == 0)
+ {
+ stat->last_iter_ts_ms = ms;
+ }
+
+ if (flow_type == FLOW_TYPE_C2S)
+ {
+ flow_stat_update(session, &stat->c2s, bytes);
+ }
+ else
+ {
+ flow_stat_update(session, &stat->s2c, bytes);
+ }
+
+ uint64_t all_pkts = stat->c2s.pkts + stat->s2c.pkts;
+ session_flags_calculate_dir(stat, all_pkts);
+ session_flags_calculate_random_looking(sf_plugin_info, stat, session, topic_id, all_pkts);
+
+ const struct packet *pkt = session_get0_current_packet(session);
+ size_t payload_len = packet_get_payload_len(pkt);
+ if (g_sf_conf.tunneling_enabled && payload_len > 0 && (topic_id == sf_plugin_info->tcp_topic_id))// detect tunneling on tcp only
+ {
+ tunneling_scan_sequence(sf_plugin_info, session, ctx, payload_len, flow_type, all_pkts);
+ }
+
+ if (stat->stream_live_time_ms >= START_JUDGE_TIME_MS)
+ {
+ if (all_pkts > g_sf_conf.main_dir_front_n_pkts)
+ {
+ session_flags_calculate_main_dir(stat);
+ }
+ }
+
+ if (stat->c2s.pkts == 0 || stat->s2c.pkts == 0)
+ {
+ goto END;
+ }
+
+ if (stat->main_dir != MAIN_DIR_UNKONWN)
+ {
+ session_flags_calculate_bound(stat, all_pkts);
+ session_flags_calculate_unidirection(stat, all_pkts);
+ }
+
+ delta_ms = ms - stat->last_iter_ts_ms;
+ if (delta_ms >= SESSION_ITER_INTERVAL_MS)
+ {
+ session_flags_calculate_rate(stat, delta_ms);
+
+ session_flags_calculate_bidirectional(stat, all_pkts);
+ session_flags_calculate_CBR(stat, all_pkts);
+ session_flags_calculate_download(stat, all_pkts);
+ session_flags_calculate_bulky(stat, all_pkts);
+ session_flags_calculate_pseudo_unidirection(stat, all_pkts);
+ session_flags_calculate_streaming(stat, all_pkts);
+ stat->iter.iter_cnt++;
+ stat->last_iter_ts_ms = ms;
+
+ session_flags_reset_delta(stat);
+ }
+
+ session_flags_calculate_interactive_pulse(stat, ms, all_pkts);
+
+END:
+ stat->last_pkt_ts_ms = ms;
+ return &stat->result;
+}
+
+struct session_flags_result *session_flags_get_flags(struct session_flags_stat *stat)
+{
+ if (stat == NULL)
+ return NULL;
+
+ return &stat->result;
+}
+
+struct session_flags_message *session_flags_generate_firewall_message(uint64_t flags, const uint32_t identify[session_flags_all_mask])
+{
+ int flag_num = 0;
+ uint32_t temp_identify[session_flags_all_mask] = {0};
+ struct session_flags_message *flags_msg = (struct session_flags_message *)calloc(1, sizeof(struct session_flags_message));
+
+ flags_msg->magic= MESSAGE_MAGIC;
+ flags_msg->flags = flags;
+
+ for (int i = 0; i < session_flags_all_mask; i++)
+ {
+ if (flags & (SESSION_FLAGS_START << i))
+ {
+ temp_identify[flag_num] = identify[i];
+ flag_num++;
+ }
+ }
+
+ flags_msg->packet_sequence_array = (uint32_t *)calloc(flag_num, sizeof(uint32_t));
+ for (int i = 0; i < flag_num; i++)
+ {
+ flags_msg->packet_sequence_array[i] = temp_identify[i];
+ }
+ flags_msg->array_num = flag_num;
+
+ return flags_msg;
+}
diff --git a/decoders/session_flags/session_flags_internal.h b/decoders/session_flags/session_flags_internal.h
new file mode 100644
index 0000000..eea0b20
--- /dev/null
+++ b/decoders/session_flags/session_flags_internal.h
@@ -0,0 +1,153 @@
+#pragma once
+#include <stdint.h>
+#include "onlinemean.h"
+#include "tunneling.h"
+
+#include "hs/hs_runtime.h"
+#include "stellar/session.h"
+#include "stellar/session_flags.h"
+#include "toml/toml.h"
+
+#define SESSION_FLAGS_LOG_MODULE "SESSION_FLAGS"
+
+enum random_looking_flags
+{
+ session_flags_frequency_mask = 0,
+ session_flags_block_frequency_mask,
+ session_flags_cumulative_sums_mask,
+ session_flags_runs_mask,
+ session_flags_longest_run_mask,
+ session_flags_rank_mask,
+ session_flags_non_overlapping_template_matching_mask,
+ session_flags_overlapping_template_matching_mask,
+ session_flags_universal_mask,
+ session_flags_random_excursions_mask,
+ session_flags_random_excursions_variant_mask,
+ session_flags_poker_detect_mask,
+ session_flags_runs_distribution_mask,
+ session_flags_self_correlation_mask,
+ session_flags_binary_derivative_mask,
+};
+#define SESSION_FLAGS_FREQUENCY (0x0000000000000001)
+#define SESSION_FLAGS_BLOCK_FREQUENCY (SESSION_FLAGS_FREQUENCY << session_flags_block_frequency_mask)
+#define SESSION_FLAGS_CUMULATIVE_SUMS (SESSION_FLAGS_FREQUENCY << session_flags_cumulative_sums_mask)
+#define SESSION_FLAGS_RUNS (SESSION_FLAGS_FREQUENCY << session_flags_runs_mask)
+#define SESSION_FLAGS_LONGEST_RUN (SESSION_FLAGS_FREQUENCY << session_flags_longest_run_mask)
+#define SESSION_FLAGS_RANK (SESSION_FLAGS_FREQUENCY << session_flags_rank_mask)
+#define SESSION_FLAGS_NON_OVERLAPPING_TEMPLATE_MATCHING (SESSION_FLAGS_FREQUENCY << session_flags_non_overlapping_template_matching_mask)
+#define SESSION_FLAGS_OVERLAPPING_TEMPLATE_MATCHING (SESSION_FLAGS_FREQUENCY << session_flags_overlapping_template_matching_mask)
+#define SESSION_FLAGS_UNIVERSAL (SESSION_FLAGS_FREQUENCY << session_flags_universal_mask)
+#define SESSION_FLAGS_RANDOM_EXCURSIONS (SESSION_FLAGS_FREQUENCY << session_flags_random_excursions_mask)
+#define SESSION_FLAGS_RANDOM_EXCURSIONS_VARIANT (SESSION_FLAGS_FREQUENCY << session_flags_random_excursions_variant_mask)
+#define SESSION_FLAGS_POKER_DETECT (SESSION_FLAGS_FREQUENCY << session_flags_poker_detect_mask)
+#define SESSION_FLAGS_RUNS_DISTRIBUTION (SESSION_FLAGS_FREQUENCY << session_flags_runs_distribution_mask)
+#define SESSION_FLAGS_SELF_CORRELATION (SESSION_FLAGS_FREQUENCY << session_flags_self_correlation_mask)
+#define SESSION_FLAGS_BINARY_DERIVATIVE (SESSION_FLAGS_FREQUENCY << session_flags_binary_derivative_mask)
+
+#define MAIN_DIR_UNKONWN -1
+
+#define START_JUDGE_TIME_MS 5000
+
+struct session_flags_result {
+ uint64_t flags;
+ uint64_t random_looking_flags;
+ uint32_t identify[session_flags_all_mask];
+ bool is_tls;
+};
+
+struct session_flags_init_conf{
+ uint32_t interactive_starttime_ms;
+ uint32_t interactive_pulse_num;
+ uint32_t main_dir_front_n_pkts;
+ uint64_t interactive_latency_ms;
+ uint32_t large_ptks_init_size;
+ uint32_t random_judge_flags_cnt;
+ uint32_t session_max_process_time_ms;
+ uint32_t fet_enabled;
+ uint32_t tunneling_enabled;
+ int32_t random_looking_udp_ignore_pkts;
+ uint32_t tunneling_tls_ignore_pkts;
+ uint32_t tunneling_max_scan_pkts;
+ char tunneling_pcre_list[2048];
+ char random_looking_judge_list[2048];
+};
+
+struct session_flags_plugin_info{
+ int plugin_id;
+ int sess_ctx_exdata_idx;
+ struct stellar *st;
+ struct logger *log_handle;
+ int session_flags_topic_id;
+ int tcp_topic_id;
+ int udp_topic_id;
+ hs_database_t *tunneling_hs_db;
+};
+
+struct session_flags_iter_values
+{
+ float bulky;
+ float CBR;
+ float download;
+ float interactive;
+ float pseudo_unidirectional;
+ float streaming;
+ OnlineMean_t omean;
+};
+
+struct session_flags_iter
+{
+ uint32_t iter_cnt;
+ float bidirectional;
+ struct session_flags_iter_values c2s;
+ struct session_flags_iter_values s2c;
+};
+
+struct flow_stat
+{
+ uint64_t bytes;
+ uint64_t pkts;
+ uint64_t payload_pkts;
+ uint64_t large_pkts;
+
+ uint32_t delta_pkts;
+ uint32_t delta_large_pkts;
+ uint32_t delta_payload_pkts;
+ uint32_t delta_bytes;
+
+ float rate;
+};
+
+struct random_looking_stat_info
+{
+ uint8_t has_judged_sts;
+ uint8_t has_judged_fet;
+ uint8_t payload_pkt_num;
+};
+
+struct session_flags_stat
+{
+ struct flow_stat c2s, s2c;
+ uint64_t last_pkt_ts_ms;
+ uint64_t interactive_pulse_num;
+ uint64_t session_start_time_ms;
+ uint64_t stream_live_time_ms;
+ uint64_t last_iter_ts_ms;
+ int main_dir;
+ struct random_looking_stat_info random_looking_stat;
+ struct tunneling_stat_info tunneling_stat;
+ struct session_flags_iter iter;
+ struct session_flags_result result;
+};
+
+struct session_flags_ctx
+{
+ struct session_flags_stat stat;
+ hs_stream_t *tunneling_hs_stream;
+ uint64_t history_flags;
+};
+
+void session_flags_stat_init(struct session_flags_stat *stat, enum session_direction session_dir);
+struct session_flags_result *session_flags(struct session_flags_plugin_info *sf_plugin_info, struct session_flags_ctx *ctx, struct session *session, int etopic_id, uint32_t bytes, enum flow_type flow_type, uint64_t ms);
+struct session_flags_result *session_flags_get_flags(struct session_flags_stat *session_flags);
+struct session_flags_message *session_flags_generate_firewall_message(uint64_t flags, const uint32_t identify[session_flags_all_mask]);
+float session_flags_calculate_CV(OnlineMean_t * omean); \ No newline at end of file
diff --git a/decoders/session_flags/session_flags_plugin.cpp b/decoders/session_flags/session_flags_plugin.cpp
new file mode 100644
index 0000000..db9522c
--- /dev/null
+++ b/decoders/session_flags/session_flags_plugin.cpp
@@ -0,0 +1,404 @@
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "mesa_sts.h"
+#include "cJSON.h"
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+#include "stellar/log.h"
+
+#include "session_flags_internal.h"
+
+#define _MAX_STR_LEN 64
+
+#define UNUSED(x) (void)(x)
+
+const char *CFG_FILE_PATH="./etc/session_flags/session_flags.toml";
+
+uint32_t sts_random_switch = 0;
+
+struct session_flags_init_conf g_sf_conf;
+
+static const char* random_looking_judge_table[] = {
+ "frequency",
+ "block_frequency",
+ "cumulative_sums",
+ "runs",
+ "longest_run",
+ "rank",
+ "non_overlapping_template_matching",
+ "overlapping_template_matching",
+ "universal",
+ "random_excursions",
+ "random_excursions_variant",
+ "poker_detect",
+ "runs_distribution",
+ "self_correlation",
+ "binary_derivative"
+};
+
+static void session_flags_topic_free_cb(void *msg, void *msg_free_arg)
+{
+ UNUSED(msg_free_arg);
+ struct session_flags_message *flags = (struct session_flags_message *)msg;
+
+ if (flags)
+ {
+ if (flags->packet_sequence_array)
+ {
+ free(flags->packet_sequence_array);
+ }
+ free(flags);
+ }
+
+ return;
+}
+
+static void session_flags_exdata_free_cb(int idx, void *ex_ptr, void *arg)
+{
+ UNUSED(idx);
+ UNUSED(arg);
+
+ if (ex_ptr == NULL)
+ {
+ return;
+ }
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)ex_ptr;
+ tunneling_hs_stream_free(ctx);
+
+ free(ex_ptr);
+}
+
+void session_flags_entry(struct session *session, int topic_id, const void *msg, void *per_session_ctx, void *plugin_env)
+{
+ UNUSED(per_session_ctx);
+ if (msg == NULL)
+ {
+ return;
+ }
+
+ const struct packet *pkt = (const struct packet *)msg;
+ struct session_flags_plugin_info *sf_plugin_info = (struct session_flags_plugin_info *)plugin_env;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)session_exdata_get(session, sf_plugin_info->sess_ctx_exdata_idx);
+
+ if (ctx == NULL)
+ {
+ ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ session_exdata_set(session, sf_plugin_info->sess_ctx_exdata_idx, ctx);
+
+ session_flags_stat_init(&ctx->stat, session_get_direction(session));
+ if (g_sf_conf.tunneling_enabled)
+ {
+ tunneling_hs_stream_init(sf_plugin_info, ctx);
+ }
+ }
+
+ struct session_flags_stat *stat = &ctx->stat;
+ session_flags_result *flags_result = NULL;
+ size_t pktlen = 0;
+
+ pktlen = packet_get_raw_len(pkt);
+
+ uint64_t curr_time_ms = 0;
+ const struct timeval *tv;
+
+ tv = packet_get_timeval(pkt);
+ if (tv != NULL)
+ {
+ curr_time_ms = tv->tv_sec * 1000 + tv->tv_usec / 1000;
+ }
+
+ flags_result = session_flags(sf_plugin_info, ctx, session, topic_id, pktlen, session_get_flow_type(session), curr_time_ms);
+
+ if ((stat->stream_live_time_ms > g_sf_conf.session_max_process_time_ms))
+ {
+ stellar_session_plugin_dettach_current_session(session);
+ }
+
+ uint32_t pkts_num = stat->c2s.pkts + stat->s2c.pkts;
+
+ if (flags_result != NULL)
+ {
+ if (flags_result->flags != ctx->history_flags)
+ {
+ struct session_flags_message *flags_msg = session_flags_generate_firewall_message(flags_result->flags, flags_result->identify);
+ if (session_mq_publish_message(session, sf_plugin_info->session_flags_topic_id, flags_msg) < 0)
+ {
+ session_flags_topic_free_cb(flags_msg, NULL);
+
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "[%s] publish session flags message failed, flags:0x%x, pkts_num:%d", session_get0_readable_addr(session), flags_result->flags, pkts_num);
+ return;
+ }
+
+ ctx->history_flags = flags_result->flags;
+
+ STELLAR_LOG_DEBUG(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE,
+ "[%s] flags:0x%x, pkts_num:%d\n \
+ c2s_iter: bulky: %f, CBR: %f, download: %f, interactive: %f, pseudo_unidirectional: %f, streaming: %f, CBR_CV: %f\n \
+ s2c_iter: bulky: %f, CBR: %f, download: %f, interactive: %f, pseudo_unidirectional: %f, streaming: %f, CBR_CV: %f\n \
+ iter_cnt: %d, bidirectional: %f\n", session_get0_readable_addr(session), ctx->history_flags, pkts_num,
+ stat->iter.c2s.bulky, stat->iter.c2s.CBR, stat->iter.c2s.download, stat->iter.c2s.interactive, stat->iter.c2s.pseudo_unidirectional, stat->iter.c2s.streaming, session_flags_calculate_CV(&stat->iter.c2s.omean),
+ stat->iter.s2c.bulky, stat->iter.s2c.CBR, stat->iter.s2c.download, stat->iter.s2c.interactive, stat->iter.s2c.pseudo_unidirectional, stat->iter.s2c.streaming, session_flags_calculate_CV(&stat->iter.s2c.omean),
+ stat->iter.iter_cnt, stat->iter.bidirectional);
+ }
+ }
+
+ return;
+}
+
+static void session_flags_load_config(struct session_flags_plugin_info *sf_plugin_info, const char *cfg_file, struct session_flags_init_conf *g_sf_conf)
+{
+ char errbuf[256] = {0};
+
+ FILE *fp = fopen(cfg_file, "r");
+ if (NULL == fp)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "Can't open config file:%s", cfg_file);
+ return;
+ }
+
+ toml_table_t *toml_root = toml_parse_file(fp, errbuf, sizeof(errbuf));
+ fclose(fp);
+
+ toml_table_t *cfg_tbl = toml_table_in(toml_root, "SESSION_FLAGS");
+ if (NULL == cfg_tbl)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "config file:%s has no key: [SESSION_FLAGS]", cfg_file);
+ toml_free(toml_root);
+ return;
+ }
+
+ toml_datum_t toml_val = toml_int_in(cfg_tbl, "INTERACTIVE_STARTTIME_MS");
+ if (toml_val.ok)
+ {
+ g_sf_conf->interactive_starttime_ms = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->interactive_starttime_ms = 15000;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "INTERACTIVE_PULSE_NUM");
+ if (toml_val.ok)
+ {
+ g_sf_conf->interactive_pulse_num = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->interactive_pulse_num = 4;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "INTERACTIVE_LATENCY_MS");
+ if (toml_val.ok)
+ {
+ g_sf_conf->interactive_latency_ms = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->interactive_latency_ms = 1000;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "MAIN_DIR_FRONT_N_PKTS");
+ if (toml_val.ok)
+ {
+ g_sf_conf->main_dir_front_n_pkts = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->main_dir_front_n_pkts = 100;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "LARGE_PKTS_INIT_SIZE");
+ if (toml_val.ok)
+ {
+ g_sf_conf->large_ptks_init_size = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->large_ptks_init_size = 1000;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "SESSION_MAX_PROCESS_TIME_MS");
+ if (toml_val.ok)
+ {
+ g_sf_conf->session_max_process_time_ms = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->session_max_process_time_ms = 30000;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "FET_ENABLED");
+ if (toml_val.ok)
+ {
+ g_sf_conf->fet_enabled = toml_val.u.b;
+ }
+ else
+ {
+ g_sf_conf->fet_enabled = 1;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "RANDOM_LOOKING_UDP_IGNORE_PKTS");
+ if (toml_val.ok)
+ {
+ g_sf_conf->random_looking_udp_ignore_pkts = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->random_looking_udp_ignore_pkts = 3;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "TUNNELING_TLS_IGNORE_PKTS");
+ if (toml_val.ok)
+ {
+ g_sf_conf->tunneling_tls_ignore_pkts = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->tunneling_tls_ignore_pkts = 4;
+ }
+
+ toml_val = toml_int_in(cfg_tbl, "TUNNELING_MAX_SCAN_PKTS");
+ if (toml_val.ok)
+ {
+ g_sf_conf->tunneling_max_scan_pkts = toml_val.u.i;
+ }
+ else
+ {
+ g_sf_conf->tunneling_max_scan_pkts = 15;
+ }
+
+ toml_val = toml_string_in(cfg_tbl, "TUNNELING_PCRE_LIST");
+ if (toml_val.ok)
+ {
+ strncpy(g_sf_conf->tunneling_pcre_list, toml_val.u.s, sizeof(g_sf_conf->tunneling_pcre_list) - 1);
+ }
+ else
+ {
+ strncpy(g_sf_conf->tunneling_pcre_list, "{\"tunneling_pcre_list\":[]}", sizeof(g_sf_conf->tunneling_pcre_list) - 1);
+ }
+
+ toml_val = toml_string_in(cfg_tbl, "RANDOM_LOOKING_JUDGE_LIST");
+ if (toml_val.ok)
+ {
+ strncpy(g_sf_conf->random_looking_judge_list, toml_val.u.s, sizeof(g_sf_conf->random_looking_judge_list) - 1);
+ }
+ else
+ {
+ strncpy(g_sf_conf->random_looking_judge_list, "{\"random_looking_judge_list\":[]}", sizeof(g_sf_conf->random_looking_judge_list) - 1);
+ }
+}
+
+extern "C" void *session_flags_plugin_init(struct stellar *st)
+{
+ char random_looking_list_str[2048] = {0};
+ struct session_flags_plugin_info *sf_plugin_info = (struct session_flags_plugin_info *)calloc(1, sizeof(struct session_flags_plugin_info));
+ cJSON *json = NULL, *item = NULL;
+ int array_num;
+
+ sf_plugin_info->st = st;
+ sf_plugin_info->sess_ctx_exdata_idx = stellar_exdata_new_index(st, "SESSION_FLAGS_SESS_CTX", session_flags_exdata_free_cb, NULL);
+
+ sf_plugin_info->log_handle = stellar_get_logger(st);
+ if(sf_plugin_info->log_handle==NULL)
+ {
+ printf("stellar_get_logger object failed ...\n");
+ goto ERROR;
+ }
+
+ memset(&g_sf_conf, 0, sizeof(g_sf_conf));
+ session_flags_load_config(sf_plugin_info, CFG_FILE_PATH, &g_sf_conf);
+ tunneling_hyperscan_engine_init(sf_plugin_info, &g_sf_conf);
+
+ json = cJSON_Parse(g_sf_conf.random_looking_judge_list);
+ if (json == NULL)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "cJSON_Parse failed, random_looking_list_str:%s", random_looking_list_str);
+ goto ERROR;
+ }
+ item = cJSON_GetObjectItem(json, "random_looking_judge_list");
+ if (item == NULL)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "cJSON_GetObjectItem failed, random_looking_list_str:%s", random_looking_list_str);
+ goto ERROR;
+ }
+ array_num = cJSON_GetArraySize(item);
+ if (array_num > STS_RANDOM_JUDGE_NUM || array_num < 0)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "array size error, array_num:%d", array_num);
+ goto ERROR;
+ }
+ for (int i = 0; i < array_num; i++)
+ {
+ for (int j = 0; j < STS_RANDOM_JUDGE_NUM; j++)
+ {
+ if (strcmp(cJSON_GetArrayItem(item, i)->valuestring, random_looking_judge_table[j]) == 0)
+ {
+ STS_SET_FLAG(sts_random_switch, j);
+ g_sf_conf.random_judge_flags_cnt++;
+ break;
+ }
+ }
+ }
+ cJSON_Delete(json);
+
+ sf_plugin_info->plugin_id = stellar_session_plugin_register(st, NULL, NULL, sf_plugin_info);
+ if (sf_plugin_info->plugin_id < 0)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "stellar_session_plugin_register failed");
+ goto ERROR;
+
+ }
+
+ sf_plugin_info->session_flags_topic_id = stellar_mq_create_topic(st, SESSION_FLAGS_MESSAGE_TOPIC, session_flags_topic_free_cb, NULL);
+ if (sf_plugin_info->session_flags_topic_id < 0)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "stellar_session_mq_create_topic failed");
+ goto ERROR;
+ }
+
+ sf_plugin_info->tcp_topic_id = stellar_mq_get_topic_id(st, TOPIC_TCP_INPUT);
+ sf_plugin_info->udp_topic_id = stellar_mq_get_topic_id(st, TOPIC_UDP_INPUT);
+ if (sf_plugin_info->tcp_topic_id < 0 || sf_plugin_info->udp_topic_id < 0)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "stellar_session_mq_get_topic_id failed");
+ goto ERROR;
+ }
+
+ stellar_session_mq_subscribe(st, sf_plugin_info->tcp_topic_id, session_flags_entry, sf_plugin_info->plugin_id);
+ stellar_session_mq_subscribe(st, sf_plugin_info->udp_topic_id, session_flags_entry, sf_plugin_info->plugin_id);
+
+ return sf_plugin_info;
+
+ERROR:
+ if (sf_plugin_info != NULL)
+ {
+ free(sf_plugin_info);
+ }
+
+ if (json != NULL)
+ {
+ cJSON_Delete(json);
+ }
+
+ perror("session_flags init failed");
+ exit(-1);
+}
+
+extern "C" void session_flags_plugin_exit(void *plugin_ctx)
+{
+ if (plugin_ctx == NULL)
+ {
+ return;
+ }
+
+ struct session_flags_plugin_info *sf_plugin_info = (struct session_flags_plugin_info *)plugin_ctx;
+
+ tunneling_hyperscan_engine_exit(sf_plugin_info->tunneling_hs_db);
+
+ free(plugin_ctx);
+
+ return;
+} \ No newline at end of file
diff --git a/decoders/session_flags/tunneling.cpp b/decoders/session_flags/tunneling.cpp
new file mode 100644
index 0000000..987ffc5
--- /dev/null
+++ b/decoders/session_flags/tunneling.cpp
@@ -0,0 +1,217 @@
+#include <cctype>
+#include <stdlib.h>
+#include <stdio.h>
+#include "cJSON.h"
+#include "session_flags_internal.h"
+#include "tunneling.h"
+#include "stellar/log.h"
+
+#define UNUSED(x) (void)(x)
+
+thread_local hs_scratch_t *hs_scratch = NULL;
+extern struct session_flags_init_conf g_sf_conf;
+
+static char tunneling_length_to_character(enum flow_type flow_type, size_t len)
+{
+ char ret;
+
+ switch(len)
+ {
+ case 1 ... 200:
+ ret = 'A';
+ break;
+ case 201 ... 600:
+ ret = 'B';
+ break;
+ case 601 ... 1000:
+ ret = 'C';
+ break;
+ case 1001 ... 1460:
+ ret = 'D';
+ break;
+ default:
+ ret = 'Z';
+ break;
+ }
+
+ if (flow_type == FLOW_TYPE_C2S)
+ {
+ return ret;
+ }
+ else
+ {
+ return tolower(ret);
+ }
+}
+
+static int tunneling_match_event_handler(unsigned int id, unsigned long long from, unsigned long long to, unsigned int flags, void *context) {
+ UNUSED(id);
+ UNUSED(from);
+ UNUSED(to);
+ UNUSED(flags);
+
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)context;
+ ctx->stat.result.flags |= SESSION_FLAGS_TUNNELING;
+ return 0;
+}
+
+int tunneling_scan_sequence(struct session_flags_plugin_info *sf_plugin_info, struct session *session, struct session_flags_ctx *ctx, size_t payload_len, enum flow_type flow_type, uint64_t pkts_cnt)
+{
+ if (ctx->stat.result.flags & SESSION_FLAGS_TUNNELING)
+ {
+ return 0;
+ }
+
+ ctx->stat.tunneling_stat.payload_pkt_num++;
+ if (ctx->stat.result.is_tls && ctx->stat.tunneling_stat.payload_pkt_num <= g_sf_conf.tunneling_tls_ignore_pkts)
+ {
+ return 0;
+ }
+
+ if((ctx->stat.result.is_tls==0) && (ctx->stat.tunneling_stat.payload_pkt_num > g_sf_conf.tunneling_max_scan_pkts))
+ {
+ return 0;
+ }
+
+ if((ctx->stat.result.is_tls) && (ctx->stat.tunneling_stat.payload_pkt_num > g_sf_conf.tunneling_max_scan_pkts+g_sf_conf.tunneling_tls_ignore_pkts))
+ {
+ return 0;
+ }
+
+ if (hs_scratch == NULL)
+ {
+ hs_error_t err = hs_alloc_scratch(sf_plugin_info->tunneling_hs_db, &hs_scratch);
+ if (err != HS_SUCCESS)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "hs_alloc_scratch failed, err:%d", err);
+ return -1;
+ }
+ }
+
+ char tunneling_seq_char = tunneling_length_to_character(flow_type, payload_len);
+ STELLAR_LOG_DEBUG(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "session: %s, is tls:%s, total_num: %d, payload_pkt_num: %d, tunneling_seq_char:%c, payload_len:%d",
+ session_get0_readable_addr(session), ctx->stat.result.is_tls == true ? "yes":"no", pkts_cnt, ctx->stat.tunneling_stat.payload_pkt_num, tunneling_seq_char, payload_len);
+
+ hs_error_t err = hs_scan_stream(ctx->tunneling_hs_stream, &tunneling_seq_char, 1, 0, hs_scratch, tunneling_match_event_handler, ctx);
+ if (err != HS_SUCCESS)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "hs_scan_stream failed, err:%d", err);
+ return -1;
+ }
+ if (ctx->stat.result.flags & SESSION_FLAGS_TUNNELING)
+ {
+ ctx->stat.result.identify[session_flags_tunneling_mask] = pkts_cnt;
+ }
+
+ return 0;
+}
+
+void tunneling_hs_stream_init(struct session_flags_plugin_info *sf_plugin_info, struct session_flags_ctx *ctx)
+{
+ hs_error_t err = hs_open_stream(sf_plugin_info->tunneling_hs_db, 0, &ctx->tunneling_hs_stream);
+ if (err != HS_SUCCESS)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "hs_open_stream failed, err:%d", err);
+ return;
+ }
+}
+
+void tunneling_hs_stream_free(struct session_flags_ctx *ctx)
+{
+ if (ctx->tunneling_hs_stream == NULL)
+ {
+ return;
+ }
+
+ hs_close_stream(ctx->tunneling_hs_stream, hs_scratch, NULL, NULL);
+}
+
+int tunneling_hyperscan_engine_init(struct session_flags_plugin_info *sf_plugin_info, struct session_flags_init_conf *g_sf_conf)
+{
+ cJSON *json = NULL, *item = NULL;
+ int array_num;
+ char **pcre = NULL;
+ hs_compile_error_t *compile_err;
+ hs_error_t err;
+ unsigned int *flags = NULL;
+ unsigned int *ids = NULL;
+ int ret = 0;
+
+ g_sf_conf->tunneling_enabled = 0;
+
+ json = cJSON_Parse(g_sf_conf->tunneling_pcre_list);
+ if (json == NULL)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "cJSON_Parse failed, tunneling_pcre_list:%s", g_sf_conf->tunneling_pcre_list);
+ goto END;
+ }
+ item = cJSON_GetObjectItem(json, "tunneling_pcre_list");
+ if (item == NULL)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "cJSON_GetObjectItem failed, tunneling_pcre_list:%s", g_sf_conf->tunneling_pcre_list);
+ goto END;
+ }
+ array_num = cJSON_GetArraySize(item);
+ if (array_num < 0)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "array size error, array_num:%d", array_num);
+ goto END;
+ }
+
+ if (array_num == 0)
+ {
+ goto END;
+ }
+
+ g_sf_conf->tunneling_enabled = 1;
+ pcre = (char **)calloc(array_num, sizeof(char *));
+ for (int i = 0; i < array_num; i++)
+ {
+ pcre[i] = cJSON_GetArrayItem(item, i)->valuestring;
+ }
+
+ flags = (unsigned int *)calloc(array_num, sizeof(unsigned int));
+ ids = (unsigned int *)calloc(array_num, sizeof(unsigned int));
+ for (int i = 0; i < array_num; i++)
+ {
+ flags[i] = HS_FLAG_DOTALL;
+ ids[i] = i;
+ }
+
+ err = hs_compile_multi(pcre, flags, ids, array_num, HS_MODE_STREAM, NULL, &sf_plugin_info->tunneling_hs_db, &compile_err);
+ if (err != HS_SUCCESS)
+ {
+ STELLAR_LOG_FATAL(sf_plugin_info->log_handle, SESSION_FLAGS_LOG_MODULE, "hs_compile_multi failed, err:%d, pattern id: %d, err_msg: %s, pattern: %s", err, compile_err->expression, compile_err->message, pcre[compile_err->expression]);
+ cJSON_Delete(json);
+ free(pcre);
+ ret = -1;
+ goto END;
+ }
+
+END:
+ if (json != NULL)
+ {
+ cJSON_Delete(json);
+ }
+ if (pcre != NULL)
+ {
+ free(pcre);
+ }
+ if (flags != NULL)
+ {
+ free(flags);
+ }
+ if (ids != NULL)
+ {
+ free(ids);
+ }
+ return ret;
+}
+
+void tunneling_hyperscan_engine_exit(hs_database_t *tunneling_hs_db)
+{
+ if (tunneling_hs_db != NULL)
+ {
+ hs_free_database(tunneling_hs_db);
+ }
+}
diff --git a/decoders/session_flags/tunneling.h b/decoders/session_flags/tunneling.h
new file mode 100644
index 0000000..50fc3fe
--- /dev/null
+++ b/decoders/session_flags/tunneling.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <cstdint>
+#include <stdlib.h>
+#include <hs/hs_common.h>
+#include <hs/hs.h>
+#include "stellar/session.h"
+
+struct tunneling_stat_info
+{
+ uint8_t payload_pkt_num;
+};
+
+int tunneling_scan_sequence(struct session_flags_plugin_info *sf_plugin_info, struct session *session, struct session_flags_ctx *ctx, size_t payload_len, flow_type flow_type, uint64_t pkts_cnt);
+void tunneling_hs_stream_init(struct session_flags_plugin_info *sf_plugin_info, struct session_flags_ctx *ctx);
+void tunneling_hs_stream_free(struct session_flags_ctx *ctx);
+int tunneling_hyperscan_engine_init(struct session_flags_plugin_info *sf_plugin_info, struct session_flags_init_conf *g_sf_conf);
+void tunneling_hyperscan_engine_exit(hs_database_t *tunneling_hs_db); \ No newline at end of file
diff --git a/decoders/socks/CMakeLists.txt b/decoders/socks/CMakeLists.txt
new file mode 100644
index 0000000..dabede1
--- /dev/null
+++ b/decoders/socks/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_definitions(-fPIC)
+
+set(SOCKS_SRC socks_decoder.cpp)
+
+add_library(socks STATIC ${SOCKS_SRC})
+add_library(socks_dyn SHARED ${SOCKS_SRC})
+set_target_properties(socks PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/version.map")
+target_include_directories(socks PUBLIC ${CMAKE_SOURCE_DIR}/deps/)
+target_link_libraries(socks toml)
+set_target_properties(socks PROPERTIES PREFIX "")
+
+set_target_properties(socks_dyn PROPERTIES PREFIX "")
+target_link_libraries(socks_dyn toml) \ No newline at end of file
diff --git a/decoders/socks/socks_decoder.cpp b/decoders/socks/socks_decoder.cpp
new file mode 100644
index 0000000..233c077
--- /dev/null
+++ b/decoders/socks/socks_decoder.cpp
@@ -0,0 +1,527 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+extern "C" {
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+}
+
+#include "socks_decoder_internal.h"
+#include "stellar/log.h"
+
+#define UNUSED(x) (void)(x)
+
+
+static void socks_decoder_session_exdata_free(int idx, void *ex_ptr, void *arg)
+{
+ UNUSED(idx);
+ UNUSED(arg);
+
+ if (ex_ptr == NULL)
+ {
+ return;
+ }
+
+ free(ex_ptr);
+
+ return;
+}
+
+//packet format refer to https://zh.wikipedia.org/wiki/SOCKS
+static int socks_rough_rec(const char *payload, size_t payload_len)
+{
+ //for socks4
+ if (payload_len < SOCKS5_REQUEST_METHOD_MIN_LEN)
+ {
+ return -1;
+ }
+
+ if (payload[0] == SOCKS_FIELD_VER_4 && payload[payload_len-1] == 0)
+ {
+ if (payload_len >= 9)
+ {
+ return 0;
+ }
+ }
+
+ if (payload[0] == SOCKS_FIELD_VER_5)
+ {
+ if (2 + (unsigned int)payload[1] == payload_len)
+ {
+
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int socks5_method_request(const char *payload, size_t payload_len)
+{
+ if (payload_len < SOCKS5_REQUEST_METHOD_MIN_LEN)
+ {
+ return -1;
+ }
+
+ if (payload[0] != SOCKS_FIELD_VER_5)
+ {
+ return -1;
+ }
+
+ if ((size_t)(2 + payload[1]) != payload_len)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int socks4_request(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
+{
+ if (payload_len < 9)
+ return -1;
+ if (payload[0] != SOCKS_FIELD_VER_4 || payload[payload_len-1] != 0)
+ return -1;
+
+ struct socks_addr *addr = &(stream->info.dst_addr);
+
+ memcpy(&(addr->port), payload + 2, 2);
+ memcpy(&(addr->ipv4), payload + 4, 4);
+
+ if (addr->ipv4 & 0x00FFFFFF)
+ {
+ addr->type = SOCKS_ADDR_IPV4;
+ return 0;
+ }
+ else if (addr->ipv4 & 0xFF000000)//socks4a hostname
+ {
+ size_t pos = 8;
+ while (pos < payload_len - 2 && payload[pos] != 0)
+ {
+ pos++;
+ }
+
+ if (payload[pos] == 0)
+ {
+ if (payload_len - 1 - pos < 2)
+ return -1;
+
+ addr->fqdn.iov_len = payload_len - 1 - pos - 1;
+ addr->fqdn.iov_base = (unsigned char *)calloc(1, addr->fqdn.iov_len + 1);
+
+ memcpy(addr->fqdn.iov_base, payload+pos+1, addr->fqdn.iov_len);
+
+ addr->type = SOCKS_ADDR_FQDN;
+
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ return -1;
+}
+
+static int socks5_request(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
+{
+ unsigned int port_offset = 0;
+ struct socks_addr *addr = &(stream->info.dst_addr);
+
+ if (payload_len < SOCKS5_REQUEST_MIN_LEN)
+ {
+ return -1;
+ }
+
+ if (payload[0] != SOCKS_FIELD_VER_5)
+ {
+ return -1;
+ }
+
+ if (payload[1] != SOCKS_CMD_CONNECT && payload[1] != SOCKS_CMD_BIND)
+ {
+ return -1;
+ }
+
+ if (payload[2] != SOCKS5_RSV_DEFAULT)
+ {
+ return -1;
+ }
+
+ switch (payload[3])
+ {
+ case SOCKS_ATYPE_IP4:
+ {
+ if (payload_len != 10)
+ {
+ return -1;
+ }
+
+ addr->type = SOCKS_ADDR_IPV4;
+ memcpy(&(addr->ipv4), payload+4, 4);
+
+ port_offset = 8;
+
+ break;
+ }
+ //should be processed
+ case SOCKS_ATYPE_IP6:
+ {
+ if(payload_len != 22)
+ {
+ return -1;
+ }
+ addr->type = SOCKS_ADDR_IPV6;
+ memcpy(addr->ipv6, payload+4, 16);
+ port_offset = 20;
+
+ break;
+ }
+ case SOCKS_ATYPE_FQDN:
+ {
+ if (payload[4] < 0 || payload_len != (size_t)(7 + payload[4]))
+ {
+ return -1;
+ }
+ addr->type = SOCKS_ADDR_FQDN;
+ addr->fqdn.iov_len = payload[4];
+ addr->fqdn.iov_base = (char *)calloc(1, addr->fqdn.iov_len + 1);
+ memcpy(addr->fqdn.iov_base, payload+5, payload[4]);
+
+ port_offset = 5 + payload[4];
+ }
+ break;
+ default:
+ break;
+ }
+
+ memcpy(&(addr->port), payload+port_offset, 2);
+
+ return 0;
+}
+
+static int socks5_pass_request(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
+{
+ unsigned char *ubuf = (unsigned char*)payload;
+ unsigned char *cur;
+ struct socks_info *info = &stream->info;
+
+ if (ubuf[0]!=1)
+ {
+ return -1;
+ }
+ if (payload_len < 2 || payload_len < (size_t)(3+ubuf[1]) || payload_len != (size_t)(3+ubuf[1]+ubuf[2+ubuf[1]]))
+ {
+ return -1;
+ }
+
+ info->user_name.iov_len = ubuf[1];
+ info->user_name.iov_base = (char *)calloc(1, info->user_name.iov_len + 1);
+ memcpy(info->user_name.iov_base, ubuf+2, info->user_name.iov_len);
+
+ info->password.iov_len = ubuf[2+ubuf[1]];
+ info->password.iov_base = (char *)calloc(1, info->password.iov_len + 1);
+ cur = ubuf + 2 + info->user_name.iov_len + 1;
+ memcpy(info->password.iov_base, cur, info->password.iov_len);
+
+ return 0;
+}
+
+static void c2s_handler(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
+{
+ switch (stream->client_state)
+ {
+ case SS_BEGIN:
+ if (socks5_method_request(payload, payload_len) == 0)
+ {
+ stream->client_state = SS_SUB;
+ stream->info.version = SOCKS_VERSION_5;
+ }
+ else if (socks4_request(stream, payload, payload_len) == 0)
+ {
+ stream->client_state = SS_END;
+ stream->info.version = SOCKS_VERSION_4;
+ }
+ else
+ {
+ stream->client_state = SS_FAILED;
+ }
+ break;
+ case SS_SUB:
+ if (socks5_request(stream, payload, payload_len) == 0)
+ {
+ stream->client_state = SS_END;
+ }
+ else if (socks5_pass_request(stream, payload, payload_len) != 0)
+ {
+ stream->client_state = SS_FAILED;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int socks5_method_replay(const char *payload, size_t payload_len)
+{
+ if (payload_len != 2)
+ {
+ return -1;
+ }
+ if (payload[0] != SOCKS_FIELD_VER_5)
+ {
+ return -1;
+ }
+ if (payload[1] != 0 && payload[1]!=2)
+ {
+ return -1;
+ }
+
+ ////printf("socks5 method replay!\n");
+ return 0;
+}
+
+static int socks4_replay(const char *payload, size_t payload_len)
+{
+ if (payload_len != 8)
+ return -1;
+ if (payload[0] != 0 || payload[1] < 90 || payload[1] > 93)
+ return -1;
+
+ return 0;
+}
+
+static int socks5_replay(const char *payload, size_t payload_len)
+{
+ if (payload_len < 6)
+ {
+ return -1;
+ }
+ if (payload[0] != SOCKS_FIELD_VER_5)
+ {
+ return -1;
+ }
+ if (payload[1] != 0)
+ {
+ return -1;
+ }
+ if (payload[2] != 0)
+ {
+ return -1;
+ }
+
+ switch(payload[3])
+ {
+ case SOCKS_ATYPE_IP4:
+ if (payload_len < 10)
+ {
+ return -1;
+ }
+ break;
+ case SOCKS_ATYPE_IP6:
+ if (payload_len != 22)
+ {
+ return -1;
+ }
+ break;
+ case SOCKS_ATYPE_FQDN:
+ if (payload[4] < 0 || payload_len < (size_t)(7 + payload[4]))
+ {
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int socks5_pass_reply(const char *payload, size_t payload_len)
+{
+ if (payload_len != 2)
+ return -1;
+ if (payload[0] != 1 || payload[1] != 0)
+ return -1;
+ return 0;
+}
+
+static void s2c_handler(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
+{
+ switch (stream->server_state)
+ {
+ case SS_BEGIN:
+ if (stream->info.version == SOCKS_VERSION_5 && socks5_method_replay(payload, payload_len) == 0)
+ {
+ stream->server_state = SS_SUB;
+ }
+ else if (stream->info.version == SOCKS_VERSION_4 && socks4_replay(payload, payload_len) == 0)
+ {
+ stream->server_state = SS_END;
+ }
+ else
+ {
+ stream->server_state = SS_FAILED;
+ //printf("socks error, s2c beign\n");
+ }
+ break;
+ case SS_SUB:
+ if (socks5_replay(payload, payload_len) == 0)
+ {
+ stream->server_state = SS_END;
+ }
+ else if (socks5_pass_reply(payload, payload_len) != 0)
+ {
+ stream->server_state = SS_FAILED;
+
+ //printf("socks error, s2c sub\n");
+ }
+ break;
+ default:
+ //error!
+ break;
+ }
+}
+
+int socks_process(struct socks_decoder_info *socks_decoder_info, struct session *sess, struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
+{
+ if (payload_len == 0)
+ {
+ return 0;
+ }
+
+ switch(stream->state)
+ {
+ case STATE_INIT:
+ if (socks_rough_rec(payload, payload_len) < 0)
+ {
+ stream->state = STATE_FAILED;
+ return -1;
+ }
+ stream->state = STATE_OPENING;
+ [[fallthrough]];//continue execute STATE_OPENING
+ case STATE_OPENING:
+ switch (session_get_flow_type(sess))
+ {
+ case FLOW_TYPE_C2S:
+ c2s_handler(stream, payload, payload_len);
+ break;
+ case FLOW_TYPE_S2C:
+ s2c_handler(stream, payload, payload_len);
+ break;
+ default:
+ break;
+ }
+
+ if (stream->client_state == SS_END && stream->server_state == SS_END)
+ {
+ if (session_mq_publish_message(sess, socks_decoder_info->socks_decoder_topic_id, &stream->info) < 0)
+ {
+ STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "session_mq_publish_message OPENING failed");
+ }
+ stellar_session_plugin_dettach_current_session(sess);
+ }
+ else if (stream->client_state == SS_FAILED || stream->server_state == SS_FAILED)
+ {
+ //not a socks proxy
+ stream->state = STATE_FAILED;
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+void socks_decoder_on_message(struct session *sess, int topic_id, const void *msg, void *per_session_ctx, void *plugin_env)
+{
+ UNUSED(per_session_ctx);
+ UNUSED(topic_id);
+ UNUSED(msg);
+
+ struct socks_decoder_info *socks_decoder_info = (struct socks_decoder_info *)plugin_env;
+ struct socks_tunnel_stream *socks_tunel_stream = (struct socks_tunnel_stream *)session_exdata_get(sess, socks_decoder_info->sess_exdata_idx);
+ const char *payload = NULL;
+ size_t payload_len = 0;
+ const struct packet *pkt = NULL;
+
+ if (socks_tunel_stream == NULL)
+ {
+ socks_tunel_stream = (struct socks_tunnel_stream *)calloc(1, sizeof(struct socks_tunnel_stream));
+ session_exdata_set(sess, socks_decoder_info->sess_exdata_idx, socks_tunel_stream);
+ }
+
+ pkt = session_get0_current_packet(sess);
+ payload = packet_get_payload(pkt);
+ payload_len = packet_get_payload_len(pkt);
+
+ if (socks_process(socks_decoder_info, sess, socks_tunel_stream, payload, payload_len) < 0)
+ {
+ stellar_session_plugin_dettach_current_session(sess);
+ }
+}
+
+extern "C" void *socks_decoder_init(struct stellar *st)
+{
+ struct socks_decoder_info *socks_decoder_info = (struct socks_decoder_info *)calloc(1, sizeof(struct socks_decoder_info));
+
+ socks_decoder_info->sess_exdata_idx= stellar_exdata_new_index(st, "SOCKS_DECODER_SESS_CTX", socks_decoder_session_exdata_free, NULL);
+
+ socks_decoder_info->st = st;
+ socks_decoder_info->log_handle = stellar_get_logger(st);
+
+ socks_decoder_info->plugin_id = stellar_session_plugin_register(st, NULL, NULL, socks_decoder_info);
+ if (socks_decoder_info->plugin_id < 0)
+ {
+ STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "stellar_session_plugin_register failed");
+ goto ERROR;
+
+ }
+
+ socks_decoder_info->socks_decoder_topic_id = stellar_mq_create_topic(st, SOCKS_MESSAGE_TOPIC, NULL, NULL);
+ if (socks_decoder_info->socks_decoder_topic_id < 0)
+ {
+ STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "stellar_session_mq_create_topic failed");
+ goto ERROR;
+ }
+
+ socks_decoder_info->tcp_topic_id = stellar_mq_get_topic_id(st, TOPIC_TCP_STREAM);
+ if (socks_decoder_info->tcp_topic_id < 0)
+ {
+ STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "stellar_session_mq_get_topic_id failed");
+ goto ERROR;
+ }
+
+ if (stellar_session_mq_subscribe(st, socks_decoder_info->tcp_topic_id, socks_decoder_on_message, socks_decoder_info->plugin_id) < 0)
+ {
+ STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "stellar_session_mq_subscribe tcp_topic_id failed");
+ goto ERROR;
+ }
+
+ return socks_decoder_info;
+
+ERROR:
+ if (socks_decoder_info != NULL)
+ {
+ free(socks_decoder_info);
+ }
+ perror("socks_decoder init failed");
+ exit(-1);
+}
+
+extern "C" void socks_decoder_exit(void *plugin_env)
+{
+ struct socks_decoder_info *socks_decoder_info = (struct socks_decoder_info *)plugin_env;
+
+ if (socks_decoder_info != NULL)
+ {
+ free(socks_decoder_info);
+ }
+
+ return;
+} \ No newline at end of file
diff --git a/decoders/socks/socks_decoder_internal.h b/decoders/socks/socks_decoder_internal.h
new file mode 100644
index 0000000..8749c62
--- /dev/null
+++ b/decoders/socks/socks_decoder_internal.h
@@ -0,0 +1,56 @@
+#include "stellar/socks_decoder.h"
+
+#define SOCKS_FIELD_VER_4 4
+#define SOCKS_FIELD_VER_5 5
+
+#define SOCKS5_REQUEST_METHOD_MIN_LEN 3
+#define SOCKS5_REQUEST_MIN_LEN 6
+#define SOCKS_VERSION_TYPE_SOCKS4 4
+#define SOCKS_VERSION_TYPE_SOCKS5 5
+
+#define SOCKS_CMD_CONNECT 0x01
+#define SOCKS_CMD_BIND 0x02
+#define SOCKS_CMD_UDP 0x03
+
+#define SOCKS5_RSV_DEFAULT 0
+#define SOCKS_ATYPE_IP4 1
+#define SOCKS_ATYPE_IP6 4
+#define SOCKS_ATYPE_FQDN 3
+
+#define SOCKS5_AUTH_NONE 0x00
+#define SOCKS5_AUTH_PASS 0x02
+
+#define SOCKS_LOG_MOUDLE "SOCKS_DECODER"
+
+struct socks_decoder_info
+{
+ int plugin_id;
+ int sess_exdata_idx;
+ struct stellar *st;
+ struct logger *log_handle;
+ int socks_decoder_topic_id;
+ int tcp_topic_id;
+};
+
+enum socks_establish_state
+{
+ SS_BEGIN = 0,
+ SS_SUB,
+ SS_END,
+ SS_FAILED
+};
+
+enum socks_internal_state
+{
+ STATE_INIT,
+ STATE_OPENING,
+ STATE_FAILED
+};
+
+struct socks_tunnel_stream
+{
+ enum socks_internal_state state;
+ enum socks_establish_state client_state;
+ enum socks_establish_state server_state;
+ struct socks_info info;
+}; \ No newline at end of file
diff --git a/decoders/stratum/CMakeLists.txt b/decoders/stratum/CMakeLists.txt
new file mode 100644
index 0000000..757555e
--- /dev/null
+++ b/decoders/stratum/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_definitions(-fPIC)
+include_directories(${CMAKE_SOURCE_DIR}/deps)
+include_directories(${CMAKE_BINARY_DIR}/vendors/cjson/src/cjson/include)
+
+set(STRATUM_SRC stratum_decoder.cpp)
+
+add_library(stratum STATIC ${STRATUM_SRC})
+add_library(stratum_dyn SHARED ${STRATUM_SRC})
+set_target_properties(stratum PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/version.map")
+
+set_target_properties(stratum PROPERTIES PREFIX "")
+
+target_link_libraries(stratum_dyn)
+set_target_properties(stratum_dyn PROPERTIES PREFIX "") \ No newline at end of file
diff --git a/decoders/stratum/stratum_decoder.cpp b/decoders/stratum/stratum_decoder.cpp
new file mode 100644
index 0000000..3267817
--- /dev/null
+++ b/decoders/stratum/stratum_decoder.cpp
@@ -0,0 +1,311 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "cJSON.h"
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+#include "stellar/stratum_decoder.h"
+#include "stellar/log.h"
+
+#define unused(x) ((void)(x))
+
+#define STRATUM_LOG_MOUDLE "STRATUM_DECODER"
+
+//字段内容宏
+#define METHOD_STR "method"
+#define WORKER_STR "worker"
+#define PARAMS_STR "params"
+#define AGENT_STR "agent"
+
+enum stratum_field_type
+{
+ CRYPTOCURRENCY_FLAG = 1,
+ MINING_POOL_FLAG,
+ MINING_PROGRAM_FLAG,
+ MINING_SUBSCRIBE_FLAG,
+};
+
+struct stratum_decoder_info
+{
+ int plugin_id;
+ struct stellar *st;
+ struct logger *log_handle;
+ int tcp_topic_id;
+ int stratum_decoder_topic_id;
+};
+
+void free_stratum_filed(struct stratum_field *stratum_field)
+{
+ if(stratum_field!=NULL){
+ if(stratum_field->mining_program.iov_base != NULL){
+ free(stratum_field->mining_program.iov_base);
+ stratum_field->mining_program.iov_len = 0;
+ }
+ if(stratum_field->mining_pools.iov_base != NULL){
+ free(stratum_field->mining_pools.iov_base);
+ stratum_field->mining_pools.iov_len = 0;
+ }
+ if(stratum_field->mining_subscribe.iov_base != NULL){
+ free(stratum_field->mining_subscribe.iov_base);
+ stratum_field->mining_subscribe.iov_len = 0;
+ }
+ free(stratum_field);
+ }
+ return;
+}
+
+void stratum_decoder_session_msg_free_cb(void *msg, void *msg_free_arg)
+{
+ unused(msg_free_arg);
+
+ struct stratum_field *stratum_field = (struct stratum_field *)msg;
+
+ free_stratum_filed(stratum_field);
+
+ return;
+}
+
+int stratum_field_assign(char *content, struct stratum_field* stratum_field,int field_range)
+{
+ switch (field_range)
+ {
+ case MINING_POOL_FLAG:
+ stratum_field->mining_pools.iov_len=strlen(content);
+ stratum_field->mining_pools.iov_base=(char*)malloc(stratum_field->mining_pools.iov_len+1);
+ memcpy(stratum_field->mining_pools.iov_base, content, stratum_field->mining_pools.iov_len);
+ ((char*)stratum_field->mining_pools.iov_base)[stratum_field->mining_pools.iov_len]='\0';
+ break;
+ case MINING_PROGRAM_FLAG:
+ stratum_field->mining_program.iov_len=strlen(content);
+ stratum_field->mining_program.iov_base=(char*)malloc(stratum_field->mining_program.iov_len+1);
+ memcpy(stratum_field->mining_program.iov_base, content, stratum_field->mining_program.iov_len);
+ ((char*)stratum_field->mining_program.iov_base)[stratum_field->mining_program.iov_len]='\0';
+ break;
+ case MINING_SUBSCRIBE_FLAG:
+ stratum_field->mining_subscribe.iov_len=strlen(content);
+ stratum_field->mining_subscribe.iov_base=(char*)malloc(stratum_field->mining_subscribe.iov_len+1);
+ memcpy(stratum_field->mining_subscribe.iov_base, content,stratum_field->mining_subscribe.iov_len);
+ ((char*)stratum_field->mining_subscribe.iov_base)[stratum_field->mining_subscribe.iov_len]='\0';
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+struct stratum_field *stratum_json_decode(struct stratum_decoder_info *stratum_decoder_info, char *raw_json_str)
+{
+
+ if(raw_json_str==NULL){
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode error!:raw_json_str is null! ");
+ return NULL;
+ }
+
+ cJSON *root = cJSON_Parse(raw_json_str);
+ if (root == NULL){
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode error!:root_json is null,error: %s ",cJSON_GetErrorPtr());
+ return NULL;
+ }
+
+ cJSON *method_item = cJSON_GetObjectItem(root, METHOD_STR);
+ if(method_item==NULL){
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode error!:not_found '%s' in raw_json_str ", METHOD_STR);
+ cJSON_Delete(root);
+ return NULL;
+ }
+
+ struct stratum_field *stratum_field = (struct stratum_field *)calloc(1, sizeof(struct stratum_field));
+ int field_range = 0;
+ if(cJSON_IsString(method_item)){
+ int assign_ret = 0;
+ STELLAR_LOG_DEBUG(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode,find '%s':%s\n", METHOD_STR, method_item->valuestring);
+
+ if((strncasecmp("mining.subscribe", method_item->valuestring,strlen("mining.subscribe")) == 0)
+ ||(strncasecmp("login", method_item->valuestring,strlen("login")) == 0)){
+ field_range = MINING_SUBSCRIBE_FLAG;
+ assign_ret = stratum_field_assign(raw_json_str, stratum_field, field_range);
+
+ cJSON *paras_item = cJSON_GetObjectItem(root, PARAMS_STR);
+ if(paras_item == NULL){
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode:error!not_found '%s' in raw_json_str ", PARAMS_STR);
+ goto err_ret;
+ }
+
+ if(cJSON_IsArray(paras_item)){
+ int asize = cJSON_GetArraySize(paras_item);
+ stratum_field->type = OTHER;
+ cJSON *array_item = cJSON_GetArrayItem(paras_item, 0);
+ if(cJSON_IsString(array_item)){
+ field_range = MINING_PROGRAM_FLAG;
+ assign_ret = stratum_field_assign(array_item->valuestring, stratum_field, field_range);
+ }
+ if(asize>2){
+ array_item = cJSON_GetArrayItem(paras_item, 2);
+ if(cJSON_IsString(array_item)){
+ field_range = MINING_POOL_FLAG;
+ assign_ret = stratum_field_assign(array_item->valuestring, stratum_field, field_range);
+ }
+ }
+ }else if(cJSON_IsObject(paras_item) || cJSON_IsRaw(paras_item)){
+ cJSON *agent_item = cJSON_GetObjectItem(paras_item, AGENT_STR);
+ if(agent_item == NULL){
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode:error!not_found '%s' in raw_json_str ", AGENT_STR);
+ goto err_ret;
+ }
+ if(cJSON_IsString(agent_item)){
+ field_range = MINING_PROGRAM_FLAG;
+ stratum_field->type = OTHER;
+ assign_ret = stratum_field_assign(agent_item->valuestring, stratum_field, field_range);
+ }
+ }else{
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode:error!found '%s',json_value_type:%d\n ", PARAMS_STR, paras_item->type);
+ goto err_ret;
+ }
+ }else if(strncasecmp("eth_submitLogin", method_item->valuestring, strlen("eth_submitLogin")) == 0){
+ field_range = MINING_SUBSCRIBE_FLAG;
+ assign_ret = stratum_field_assign(raw_json_str, stratum_field, field_range);
+
+ cJSON *worker_item = cJSON_GetObjectItem(root, WORKER_STR);
+ if(worker_item == NULL){
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode:error!not_found '%s' in raw_json_str ", WORKER_STR);
+ goto err_ret;
+ }
+ if(cJSON_IsString(worker_item)){
+ field_range = MINING_PROGRAM_FLAG;
+ stratum_field->type = ETH;
+ assign_ret = stratum_field_assign(worker_item->valuestring, stratum_field, field_range);
+ }else{
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode:error!found '%s',json_value_type:%d\n ", WORKER_STR, worker_item->type);
+ goto err_ret;
+ }
+ }else{
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode:error!found '%s',json_value_type:%d\n ", METHOD_STR, method_item->type);
+ goto err_ret;
+ }
+
+ if(assign_ret<0){
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "field_assign_error:raw_json_str:%s\n ", raw_json_str);
+ goto err_ret;
+ }
+ cJSON_Delete(root);
+ return stratum_field;
+ }else{
+ STELLAR_LOG_INFO(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "json_decode:error!found '%s',json_value_type:%d\n ", METHOD_STR, method_item->type);
+ goto err_ret;
+ }
+
+err_ret:
+ cJSON_Delete(root);
+ free_stratum_filed(stratum_field);
+ return NULL;
+}
+
+struct stratum_field *stratum_data_process(struct stratum_decoder_info *stratum_decoder_info, const char *tcpdata, size_t datalen)
+{
+ if(tcpdata == NULL || datalen == 0)
+ {
+ return NULL;
+ }
+
+ char *tcp_json=(char*)malloc(datalen+1);
+ memcpy(tcp_json, tcpdata, datalen);
+ tcp_json[datalen]='\0';
+
+ struct stratum_field *stratum_field=stratum_json_decode(stratum_decoder_info, tcp_json);
+ if(stratum_field==NULL)
+ {
+ STELLAR_LOG_DEBUG(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "payload_decode failed!stratum_field is null");
+ }
+
+ free(tcp_json);
+ tcp_json=NULL;
+
+ return stratum_field;
+
+}
+
+void stratum_decoder_tcp_on_msg_cb(struct session *sess, int topic_id, const void *msg, void *per_session_ctx, void *plugin_env)
+{
+ unused(topic_id);
+ unused(per_session_ctx);
+ unused(msg);
+
+ struct stratum_decoder_info *stratum_decoder_info = (struct stratum_decoder_info *)plugin_env;
+
+ const struct packet *pkt = session_get0_current_packet(sess);
+ const char *payload = packet_get_payload(pkt);
+ size_t payload_len = packet_get_payload_len(pkt);
+
+ struct stratum_field *stratum_field = stratum_data_process(stratum_decoder_info, payload, payload_len);
+ if (stratum_field != NULL)
+ {
+ if (session_mq_publish_message(sess, stratum_decoder_info->stratum_decoder_topic_id, stratum_field) < 0)
+ {
+ STELLAR_LOG_FATAL(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "stratum_decoder_tcp_on_msg_cb:session_mq_publish_message failed");
+ free_stratum_filed(stratum_field);
+ }
+ stellar_session_plugin_dettach_current_session(sess);
+ }
+
+ return;
+}
+
+extern "C" void *stratum_decoder_init(struct stellar *st)
+{
+ struct stratum_decoder_info *stratum_decoder_info = (struct stratum_decoder_info *)malloc(sizeof(struct stratum_decoder_info));
+
+ stratum_decoder_info->st = st;
+ stratum_decoder_info->log_handle = stellar_get_logger(st);
+
+ stratum_decoder_info->plugin_id = stellar_session_plugin_register(st, NULL, NULL, stratum_decoder_info);
+ if (stratum_decoder_info->plugin_id < 0)
+ {
+ STELLAR_LOG_FATAL(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "stellar_session_plugin_register failed");
+ goto ERROR;
+ }
+
+ stratum_decoder_info->stratum_decoder_topic_id = stellar_mq_create_topic(st, STRATUM_MESSAGE_TOPIC, stratum_decoder_session_msg_free_cb, NULL);
+ if (stratum_decoder_info->stratum_decoder_topic_id < 0)
+ {
+ STELLAR_LOG_FATAL(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "stellar_session_mq_create_topic failed");
+ goto ERROR;
+ }
+
+ stratum_decoder_info->tcp_topic_id = stellar_mq_get_topic_id(st, TOPIC_TCP_STREAM);
+ if (stratum_decoder_info->tcp_topic_id < 0)
+ {
+ STELLAR_LOG_FATAL(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "stellar_session_mq_get_topic_id failed");
+ goto ERROR;
+ }
+
+ if (stellar_session_mq_subscribe(st, stratum_decoder_info->tcp_topic_id, stratum_decoder_tcp_on_msg_cb, stratum_decoder_info->plugin_id) < 0)
+ {
+ STELLAR_LOG_FATAL(stratum_decoder_info->log_handle, STRATUM_LOG_MOUDLE, "stellar_session_mq_subscribe failed");
+ goto ERROR;
+ }
+
+ return stratum_decoder_info;
+
+ERROR:
+ if (stratum_decoder_info)
+ {
+ free(stratum_decoder_info);
+ }
+ return NULL;
+}
+
+extern "C" void stratum_decoder_exit(void *plugin_env)
+{
+ struct stratum_decoder_info *stratum_decoder_info = (struct stratum_decoder_info *)plugin_env;
+
+ if (stratum_decoder_info != NULL)
+ {
+ free(stratum_decoder_info);
+ }
+
+ return;
+} \ No newline at end of file
diff --git a/include/stellar/session_flags.h b/include/stellar/session_flags.h
new file mode 100644
index 0000000..2aeace8
--- /dev/null
+++ b/include/stellar/session_flags.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define MESSAGE_MAGIC 0x12345678
+
+#define SESSION_FLAGS_MESSAGE_TOPIC "TOPIC_SESSION_FLAGS"
+
+enum
+{
+ session_flags_bulky_mask = 1,
+ session_flags_cbr_mask,
+ session_flags_local_client_mask,
+ session_flags_local_server_mask,
+ session_flags_download_mask,
+ session_flags_interactive_mask,
+ session_flags_inbound_mask,
+ session_flags_outbound_mask,
+ session_flags_pseudo_unidirectional_mask,
+ session_flags_streaming_mask,
+ session_flags_unidirectional_mask,
+ session_flags_random_looking_mask,
+ session_flags_c2s_mask,
+ session_flags_s2c_mask,
+ session_flags_bidirectional_mask,
+ session_flags_tunneling_mask,
+ session_flags_all_mask
+};
+
+#define SESSION_FLAGS_START (0x0000000000000001)
+#define SESSION_FLAGS_BULKY (SESSION_FLAGS_START << session_flags_bulky_mask)
+#define SESSION_FLAGS_CBR (SESSION_FLAGS_START << session_flags_cbr_mask)
+#define SESSION_FLAGS_LOCAL_CLIENT (SESSION_FLAGS_START << session_flags_local_client_mask)
+#define SESSION_FLAGS_LOCAL_SERVER (SESSION_FLAGS_START << session_flags_local_server_mask)
+#define SESSION_FLAGS_DOWNLOAD (SESSION_FLAGS_START << session_flags_download_mask)
+#define SESSION_FLAGS_INTERACTIVE (SESSION_FLAGS_START << session_flags_interactive_mask)
+#define SESSION_FLAGS_INBOUND (SESSION_FLAGS_START << session_flags_inbound_mask)
+#define SESSION_FLAGS_OUTBOUND (SESSION_FLAGS_START << session_flags_outbound_mask)
+#define SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL (SESSION_FLAGS_START << session_flags_pseudo_unidirectional_mask)
+#define SESSION_FLAGS_STREAMING (SESSION_FLAGS_START << session_flags_streaming_mask)
+#define SESSION_FLAGS_UNIDIRECTIONAL (SESSION_FLAGS_START << session_flags_unidirectional_mask)
+#define SESSION_FLAGS_RANDOM_LOOKING (SESSION_FLAGS_START << session_flags_random_looking_mask)
+#define SESSION_FLAGS_C2S (SESSION_FLAGS_START << session_flags_c2s_mask)
+#define SESSION_FLAGS_S2C (SESSION_FLAGS_START << session_flags_s2c_mask)
+#define SESSION_FLAGS_BIDIRECTIONAL (SESSION_FLAGS_START << session_flags_bidirectional_mask)
+#define SESSION_FLAGS_TUNNELING (SESSION_FLAGS_START << session_flags_tunneling_mask)
+
+struct session_flags_message
+{
+ int magic;
+ uint64_t flags;
+ uint32_t array_num;
+ uint32_t *packet_sequence_array;
+}; \ No newline at end of file
diff --git a/include/stellar/socks_decoder.h b/include/stellar/socks_decoder.h
new file mode 100644
index 0000000..c3034b8
--- /dev/null
+++ b/include/stellar/socks_decoder.h
@@ -0,0 +1,44 @@
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#ifndef IPV6_ADDR_LEN
+#define IPV6_ADDR_LEN (sizeof(struct in6_addr))
+#endif
+
+
+#define SOCKS_MESSAGE_TOPIC "TOPIC_SOCKS"
+
+
+enum socks_addr_type
+{
+ SOCKS_ADDR_IPV4,
+ SOCKS_ADDR_IPV6,
+ SOCKS_ADDR_FQDN
+};
+
+struct socks_addr
+{
+ enum socks_addr_type type;
+ union
+ {
+ uint32_t ipv4; /* network order */
+ uint8_t ipv6[IPV6_ADDR_LEN] ;
+ struct iovec fqdn;
+ };
+ uint16_t port; /* network order */
+};
+
+enum socks_version
+{
+ SOCKS_VERSION_4,
+ SOCKS_VERSION_5
+};
+
+struct socks_info
+{
+ enum socks_version version;
+ struct socks_addr dst_addr;
+ struct iovec user_name;
+ struct iovec password;
+};//message data
+ \ No newline at end of file
diff --git a/include/stellar/stratum_decoder.h b/include/stellar/stratum_decoder.h
new file mode 100644
index 0000000..a6eed42
--- /dev/null
+++ b/include/stellar/stratum_decoder.h
@@ -0,0 +1,18 @@
+#include <sys/uio.h>
+
+#define STRATUM_MESSAGE_TOPIC "TOPIC_STRATUM"
+
+enum cryptocurrency_type
+{
+ ETH=1,
+ OTHER=2
+};
+
+struct stratum_field
+{
+ enum cryptocurrency_type type;
+ struct iovec mining_pools;
+ struct iovec mining_program;
+ struct iovec mining_subscribe;
+
+};//message data \ No newline at end of file
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 7f8f9f0..89f667b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -3,3 +3,6 @@ add_subdirectory(packet_tool)
add_subdirectory(debug_plugin)
add_subdirectory(lpi_plugin)
add_subdirectory(decoders/http)
+add_subdirectory(decoders/socks)
+add_subdirectory(decoders/stratum)
+add_subdirectory(decoders/session_flags) \ No newline at end of file
diff --git a/test/decoders/session_flags/CMakeLists.txt b/test/decoders/session_flags/CMakeLists.txt
new file mode 100644
index 0000000..4bf55ba
--- /dev/null
+++ b/test/decoders/session_flags/CMakeLists.txt
@@ -0,0 +1,56 @@
+set(DECODER_NAME session_flags)
+
+add_library(${DECODER_NAME}_test SHARED session_flags_test_plugin.cpp)
+add_dependencies(${DECODER_NAME}_test ${DECODER_NAME})
+set_target_properties(${DECODER_NAME}_test PROPERTIES PREFIX "")
+
+set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/testing)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_SOURCE_DIR}/src)
+include_directories(${CMAKE_BINARY_DIR}/vendors/cjson/src/cjson/include)
+include_directories(${PROJECT_SOURCE_DIR}/decoders/session_flags)
+include_directories(${PROJECT_SOURCE_DIR}/include/stellar)
+
+add_executable(gtest_session_flags_pcap session_flags_pcap_test.cpp
+ dummy.c
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/fet.cpp
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/onlinemean.c
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/session_flags_plugin.cpp
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/session_flags.cpp
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/tunneling.cpp)
+target_link_libraries(gtest_session_flags_pcap gtest pcap hyperscan_static hyperscan_runtime_static logger cjson-static libmesa_sts)
+
+add_executable(gtest_session_flags_static session_flags_static_test.cpp
+ dummy.c
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/fet.cpp
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/onlinemean.c
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/session_flags_plugin.cpp
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/session_flags.cpp
+ ${PROJECT_SOURCE_DIR}/decoders/session_flags/tunneling.cpp)
+target_link_libraries(gtest_session_flags_static gtest pcap hyperscan_static hyperscan_runtime_static logger cjson-static libmesa_sts)
+
+add_executable(session_flags_test_main plugin_test_main.cpp)
+set_target_properties(session_flags_test_main
+ PROPERTIES
+ LINK_OPTIONS
+ "-rdynamic"
+ )
+set_target_properties(session_flags_test_main
+ PROPERTIES
+ LINK_FLAGS
+ "-rdynamic"
+ )
+set(LINK_FLAGS "-rdynamic")
+
+target_link_libraries(session_flags_test_main PUBLIC gtest cjson-static stellar_lib -Wl,--whole-archive packet_parser -Wl,--no-whole-archive)
+
+add_subdirectory(test_based_on_stellar)
+
+#copy pcap file folder to build directory
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/pcap DESTINATION ${CMAKE_BINARY_DIR}/test/decoders/session_flags)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/test_based_on_stellar/env/session_flags.toml DESTINATION ${CMAKE_BINARY_DIR}/test/decoders/session_flags/etc/session_flags/)
+
+include(GoogleTest)
+gtest_discover_tests(gtest_session_flags_pcap)
+gtest_discover_tests(gtest_session_flags_static)
diff --git a/test/decoders/session_flags/dummy.c b/test/decoders/session_flags/dummy.c
new file mode 100644
index 0000000..9227261
--- /dev/null
+++ b/test/decoders/session_flags/dummy.c
@@ -0,0 +1,167 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "dummy.h"
+
+#define UNUSED(x) (void)(x)
+
+struct session g_sess;
+
+void session_set_current_payload(struct session *sess, char *payload, int payload_len)
+{
+ UNUSED(sess);
+ g_sess.payload_len = payload_len;
+ g_sess.payload = payload;
+}
+
+int session_exdata_set(struct session *sess, int idx, void *ex_ptr)
+{
+ UNUSED(sess);
+ UNUSED(idx);
+ UNUSED(ex_ptr);
+
+ return 0;
+}
+
+struct logger *stellar_get_logger(struct stellar *st)
+{
+ UNUSED(st);
+
+ return (struct logger *)1;
+}
+
+void *session_exdata_get(struct session *sess, int idx)
+{
+ UNUSED(sess);
+ UNUSED(idx);
+
+ return NULL;
+}
+
+const struct packet *session_get0_current_packet(const struct session *sess)
+{
+ UNUSED(sess);
+
+ return NULL;
+}
+
+uint16_t packet_get_payload_len(const struct packet *pkt)
+{
+ UNUSED(pkt);
+
+ return g_sess.payload_len;
+}
+
+const char *packet_get_payload(const struct packet *pkt)
+{
+ UNUSED(pkt);
+
+ return g_sess.payload;
+}
+
+enum session_direction session_get_direction(const struct session *sess)
+{
+ UNUSED(sess);
+
+ return SESSION_DIRECTION_OUTBOUND;
+}
+
+enum flow_type session_get_flow_type(const struct session *sess)
+{
+ UNUSED(sess);
+
+ return FLOW_TYPE_C2S;
+}
+
+const char *session_get0_readable_addr(const struct session *sess)
+{
+ UNUSED(sess);
+
+ return NULL;
+}
+
+uint16_t packet_get_raw_len(const struct packet *pkt)
+{
+ UNUSED(pkt);
+
+ return 0;
+}
+
+const struct timeval *packet_get_timeval(const struct packet *pkt)
+{
+ UNUSED(pkt);
+
+ return NULL;
+}
+
+void stellar_session_plugin_dettach_current_session(struct session *sess)
+{
+ UNUSED(sess);
+}
+
+int session_mq_publish_message(struct session *sess, int topic_id, void *msg)
+{
+ UNUSED(sess);
+ UNUSED(topic_id);
+ UNUSED(msg);
+
+ return 0;
+}
+
+int stellar_exdata_new_index(struct stellar *st, const char *name, stellar_exdata_free *free_func,void *arg)
+{
+ UNUSED(st);
+ UNUSED(name);
+ UNUSED(free_func);
+ UNUSED(arg);
+
+ return 0;
+}
+
+int stellar_mq_create_topic(struct stellar *st, const char *topic_name, stellar_msg_free_cb_func *msg_free_cb, void *msg_free_arg)
+{
+ UNUSED(st);
+ UNUSED(topic_name);
+ UNUSED(msg_free_cb);
+ UNUSED(msg_free_arg);
+
+ return 0;
+}
+
+int stellar_mq_get_topic_id(struct stellar *st, const char *topic_name)
+{
+ UNUSED(st);
+
+ if (strcmp(topic_name, TOPIC_TCP_INPUT) == 0)
+ {
+ return DUMMY_TCP_TOPIC_ID;
+ }
+ else if (strcmp(topic_name, TOPIC_UDP_INPUT) == 0)
+ {
+ return DUMMY_UDP_TOPIC_ID;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+int stellar_session_mq_subscribe(struct stellar *st, int topic_id, on_session_msg_cb_func *plugin_on_msg_cb, int plugin_id)
+{
+ UNUSED(st);
+ UNUSED(topic_id);
+ UNUSED(plugin_on_msg_cb);
+ UNUSED(plugin_id);
+
+ return 0;
+}
+
+int stellar_session_plugin_register(struct stellar *st, session_ctx_new_func session_ctx_new, session_ctx_free_func session_ctx_free, void *plugin_env)
+{
+ UNUSED(st);
+ UNUSED(session_ctx_new);
+ UNUSED(session_ctx_free);
+ UNUSED(plugin_env);
+
+ return 0;
+} \ No newline at end of file
diff --git a/test/decoders/session_flags/dummy.h b/test/decoders/session_flags/dummy.h
new file mode 100644
index 0000000..aec8383
--- /dev/null
+++ b/test/decoders/session_flags/dummy.h
@@ -0,0 +1,18 @@
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+
+#define DUMMY_TCP_TOPIC_ID 1
+#define DUMMY_UDP_TOPIC_ID 2
+
+struct session //stub just for test
+{
+ int payload_len;
+ char *payload;
+};
+
+void session_set_current_payload(struct session *sess, char *payload, int payload_len);
+
+void *session_flags_plugin_init(struct stellar *st);
+void session_flags_plugin_exit(void *arg); \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/1-dtls.192.168.44.32.pcap b/test/decoders/session_flags/pcap/1-dtls.192.168.44.32.pcap
new file mode 100644
index 0000000..4fa6ce4
--- /dev/null
+++ b/test/decoders/session_flags/pcap/1-dtls.192.168.44.32.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/192.168.56.19.56554.json b/test/decoders/session_flags/pcap/192.168.56.19.56554.json
new file mode 100644
index 0000000..78d1fd1
--- /dev/null
+++ b/test/decoders/session_flags/pcap/192.168.56.19.56554.json
@@ -0,0 +1,20 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 24584,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":4}",
+ "common_flags_2": 2123980808,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":4,\"frequency\":6,\"runs\":6,\"longest_run\":6,\"overlapping_template_matching\":6,\"random_excursions\":6,\"random_excursions_variant\":6,\"poker_detect\":6,\"runs_distribution\":6,\"self_correlation\":6,\"binary_derivative\":6}",
+ "common_flags_3": 2123980936,
+ "common_flags_str_3": "[Client is Local,Inbound]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Inbound\":21,\"C2S\":1,\"S2C\":4,\"frequency\":6,\"runs\":6,\"longest_run\":6,\"overlapping_template_matching\":6,\"random_excursions\":6,\"random_excursions_variant\":6,\"poker_detect\":6,\"runs_distribution\":6,\"self_correlation\":6,\"binary_derivative\":6}",
+ "common_flags_4": 2123981960,
+ "common_flags_str_4": "[Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_4": "{\"Client is Local\":1,\"Inbound\":21,\"Streaming\":100,\"C2S\":1,\"S2C\":4,\"frequency\":6,\"runs\":6,\"longest_run\":6,\"overlapping_template_matching\":6,\"random_excursions\":6,\"random_excursions_variant\":6,\"poker_detect\":6,\"runs_distribution\":6,\"self_correlation\":6,\"binary_derivative\":6}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/192.168.56.19.56555-47.93.59.84.443_869284834001082715.pcapng b/test/decoders/session_flags/pcap/192.168.56.19.56555-47.93.59.84.443_869284834001082715.pcapng
new file mode 100644
index 0000000..b675a02
--- /dev/null
+++ b/test/decoders/session_flags/pcap/192.168.56.19.56555-47.93.59.84.443_869284834001082715.pcapng
Binary files differ
diff --git a/test/decoders/session_flags/pcap/192.168.56.31.58530.json b/test/decoders/session_flags/pcap/192.168.56.31.58530.json
new file mode 100644
index 0000000..67acb45
--- /dev/null
+++ b/test/decoders/session_flags/pcap/192.168.56.31.58530.json
@@ -0,0 +1,17 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 24584,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2}",
+ "common_flags_2": 1185898504,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2,\"frequency\":4,\"block_frequency\":4,\"cumulative_sums\":4,\"runs\":4,\"rank\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"binary_derivative\":4}",
+ "common_flags_3": 1185898632,
+ "common_flags_str_3": "[Client is Local,Inbound]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Inbound\":34,\"C2S\":1,\"S2C\":2,\"frequency\":4,\"block_frequency\":4,\"cumulative_sums\":4,\"runs\":4,\"rank\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"binary_derivative\":4}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/192.168.56.31.58530.pcapng b/test/decoders/session_flags/pcap/192.168.56.31.58530.pcapng
new file mode 100644
index 0000000..6abe0ed
--- /dev/null
+++ b/test/decoders/session_flags/pcap/192.168.56.31.58530.pcapng
Binary files differ
diff --git a/test/decoders/session_flags/pcap/192.168.64.61.62275.asymmetric.pcapng b/test/decoders/session_flags/pcap/192.168.64.61.62275.asymmetric.pcapng
new file mode 100644
index 0000000..afff17d
--- /dev/null
+++ b/test/decoders/session_flags/pcap/192.168.64.61.62275.asymmetric.pcapng
Binary files differ
diff --git a/test/decoders/session_flags/pcap/192.168.64.61.62275.json b/test/decoders/session_flags/pcap/192.168.64.61.62275.json
new file mode 100644
index 0000000..ea68bf3
--- /dev/null
+++ b/test/decoders/session_flags/pcap/192.168.64.61.62275.json
@@ -0,0 +1,14 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 1453269000,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"frequency\":3,\"block_frequency\":3,\"cumulative_sums\":3,\"runs\":3,\"longest_run\":3,\"overlapping_template_matching\":3,\"random_excursions\":3,\"random_excursions_variant\":3,\"runs_distribution\":3,\"binary_derivative\":3}",
+ "common_flags_2": 1453269001,
+ "common_flags_str_2": "[Asymmetric,Client is Local]",
+ "common_flags_identify_info_2": "{\"Asymmetric\":10,\"Client is Local\":1,\"C2S\":1,\"frequency\":3,\"block_frequency\":3,\"cumulative_sums\":3,\"runs\":3,\"longest_run\":3,\"overlapping_template_matching\":3,\"random_excursions\":3,\"random_excursions_variant\":3,\"runs_distribution\":3,\"binary_derivative\":3}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/192.168.64.67.53813.json b/test/decoders/session_flags/pcap/192.168.64.67.53813.json
new file mode 100644
index 0000000..6893c7f
--- /dev/null
+++ b/test/decoders/session_flags/pcap/192.168.64.67.53813.json
@@ -0,0 +1,14 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 2126340104,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2,\"frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_2": 2126340232,
+ "common_flags_str_2": "[Client is Local,Inbound]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"Inbound\":18,\"C2S\":1,\"S2C\":2,\"frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/192.168.64.67.53813.pcapng b/test/decoders/session_flags/pcap/192.168.64.67.53813.pcapng
new file mode 100644
index 0000000..ffb6287
--- /dev/null
+++ b/test/decoders/session_flags/pcap/192.168.64.67.53813.pcapng
Binary files differ
diff --git a/test/decoders/session_flags/pcap/1_result.json b/test/decoders/session_flags/pcap/1_result.json
new file mode 100644
index 0000000..57df72d
--- /dev/null
+++ b/test/decoders/session_flags/pcap/1_result.json
@@ -0,0 +1,11 @@
+[
+ {
+ "common_flags_0": 8208,
+ "common_flags_str_0": "[Server is Local,C2S]",
+ "common_flags_identify_info_0": "1,1,",
+ "common_flags_1": 24592,
+ "common_flags_str_1": "[Server is Local,C2S,S2C]",
+ "common_flags_identify_info_1": "1,1,2,",
+ "name": "session_flags_test"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/bilibili-dtls.pcap b/test/decoders/session_flags/pcap/bilibili-dtls.pcap
new file mode 100644
index 0000000..9316c01
--- /dev/null
+++ b/test/decoders/session_flags/pcap/bilibili-dtls.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/bilibili.json b/test/decoders/session_flags/pcap/bilibili.json
new file mode 100644
index 0000000..640957c
--- /dev/null
+++ b/test/decoders/session_flags/pcap/bilibili.json
@@ -0,0 +1,23 @@
+[
+ {
+ "common_flags_0": 16392,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"S2C\":1}",
+ "common_flags_1": 24584,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":2,\"S2C\":1}",
+ "common_flags_2": 1585602568,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":2,\"S2C\":1,\"block_frequency\":5,\"overlapping_template_matching\":5,\"random_excursions\":5,\"random_excursions_variant\":5,\"poker_detect\":5,\"runs_distribution\":5,\"binary_derivative\":5}",
+ "common_flags_3": 1585602696,
+ "common_flags_str_3": "[Client is Local,Inbound]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Inbound\":25,\"C2S\":2,\"S2C\":1,\"block_frequency\":5,\"overlapping_template_matching\":5,\"random_excursions\":5,\"random_excursions_variant\":5,\"poker_detect\":5,\"runs_distribution\":5,\"binary_derivative\":5}",
+ "common_flags_4": 1585603720,
+ "common_flags_str_4": "[Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_4": "{\"Client is Local\":1,\"Inbound\":25,\"Streaming\":186,\"C2S\":2,\"S2C\":1,\"block_frequency\":5,\"overlapping_template_matching\":5,\"random_excursions\":5,\"random_excursions_variant\":5,\"poker_detect\":5,\"runs_distribution\":5,\"binary_derivative\":5}",
+ "common_flags_5": 1585636488,
+ "common_flags_str_5": "[Client is Local,Inbound,Streaming,Bidirectional]",
+ "common_flags_identify_info_5": "{\"Client is Local\":1,\"Inbound\":25,\"Streaming\":186,\"C2S\":2,\"S2C\":1,\"Bidirectional\":8551,\"block_frequency\":5,\"overlapping_template_matching\":5,\"random_excursions\":5,\"random_excursions_variant\":5,\"poker_detect\":5,\"runs_distribution\":5,\"binary_derivative\":5}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/dns_asymmetric.json b/test/decoders/session_flags/pcap/dns_asymmetric.json
new file mode 100644
index 0000000..3b3510b
--- /dev/null
+++ b/test/decoders/session_flags/pcap/dns_asymmetric.json
@@ -0,0 +1,14 @@
+[
+ {
+ "common_flags_0": 8208,
+ "common_flags_str_0": "[Server is Local]",
+ "common_flags_identify_info_0": "{\"Server is Local\":1,\"C2S\":1}",
+ "common_flags_1": 8209,
+ "common_flags_str_1": "[Asymmetric,Server is Local]",
+ "common_flags_identify_info_1": "{\"Asymmetric\":10,\"Server is Local\":1,\"C2S\":1}",
+ "common_flags_2": 24593,
+ "common_flags_str_2": "[Asymmetric,Server is Local]",
+ "common_flags_identify_info_2": "{\"Asymmetric\":10,\"Server is Local\":1,\"C2S\":1,\"S2C\":12}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/dns_asymmetric.pcap b/test/decoders/session_flags/pcap/dns_asymmetric.pcap
new file mode 100644
index 0000000..116934f
--- /dev/null
+++ b/test/decoders/session_flags/pcap/dns_asymmetric.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/douyin.json b/test/decoders/session_flags/pcap/douyin.json
new file mode 100644
index 0000000..739d1db
--- /dev/null
+++ b/test/decoders/session_flags/pcap/douyin.json
@@ -0,0 +1,20 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 24584,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2}",
+ "common_flags_2": 2124242952,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2,\"frequency\":4,\"cumulative_sums\":4,\"runs\":4,\"longest_run\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"poker_detect\":4,\"runs_distribution\":4,\"self_correlation\":4,\"binary_derivative\":4}",
+ "common_flags_3": 2124244104,
+ "common_flags_str_3": "[Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Inbound\":6036,\"Streaming\":6036,\"C2S\":1,\"S2C\":2,\"frequency\":4,\"cumulative_sums\":4,\"runs\":4,\"longest_run\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"poker_detect\":4,\"runs_distribution\":4,\"self_correlation\":4,\"binary_derivative\":4}",
+ "common_flags_4": 2124244650,
+ "common_flags_str_4": "[Bulky,Client is Local,Download,Inbound,Pseudo Unidirectional,Streaming]",
+ "common_flags_identify_info_4": "{\"Bulky\":6042,\"Client is Local\":1,\"Download\":6042,\"Inbound\":6036,\"Pseudo Unidirectional\":6042,\"Streaming\":6036,\"C2S\":1,\"S2C\":2,\"frequency\":4,\"cumulative_sums\":4,\"runs\":4,\"longest_run\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"poker_detect\":4,\"runs_distribution\":4,\"self_correlation\":4,\"binary_derivative\":4}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/douyin.pcap b/test/decoders/session_flags/pcap/douyin.pcap
new file mode 100644
index 0000000..89e08d3
--- /dev/null
+++ b/test/decoders/session_flags/pcap/douyin.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/ftp-data.pcap b/test/decoders/session_flags/pcap/ftp-data.pcap
new file mode 100644
index 0000000..9517dde
--- /dev/null
+++ b/test/decoders/session_flags/pcap/ftp-data.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/https_download.json b/test/decoders/session_flags/pcap/https_download.json
new file mode 100644
index 0000000..b0f735d
--- /dev/null
+++ b/test/decoders/session_flags/pcap/https_download.json
@@ -0,0 +1,26 @@
+[{
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local,C2S]",
+ "common_flags_identify_info_0": "1,1,",
+ "common_flags_1": 24584,
+ "common_flags_str_1": "[Client is Local,C2S,S2C]",
+ "common_flags_identify_info_1": "1,1,2,",
+ "common_flags_2": 28680,
+ "common_flags_str_2": "[Client is Local,Random Looking,C2S,S2C]",
+ "common_flags_identify_info_2": "1,4,1,2,",
+ "common_flags_3": 28808,
+ "common_flags_str_3": "[Client is Local,Inbound,Random Looking,C2S,S2C]",
+ "common_flags_identify_info_3": "1,1188,4,1,2,",
+ "common_flags_4": 29832,
+ "common_flags_str_4": "[Client is Local,Inbound,Streaming,Random Looking,C2S,S2C]",
+ "common_flags_identify_info_4": "1,1188,1662,4,1,2,",
+ "common_flags_5": 30344,
+ "common_flags_str_5": "[Client is Local,Inbound,Pseudo Unidirectional,Streaming,Random Looking,C2S,S2C]",
+ "common_flags_identify_info_5": "1,1188,3546,1662,4,1,2,",
+ "common_flags_6": 30346,
+ "common_flags_str_6": "[Bulky,Client is Local,Inbound,Pseudo Unidirectional,Streaming,Random Looking,C2S,S2C]",
+ "common_flags_identify_info_6": "4095,1,1188,3546,1662,4,1,2,",
+ "common_flags_7": 30378,
+ "common_flags_str_7": "[Bulky,Client is Local,Download,Inbound,Pseudo Unidirectional,Streaming,Random Looking,C2S,S2C]",
+ "common_flags_identify_info_7": "4095,1,4201,1188,3546,1662,4,1,2,"
+}] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/https_download.pcap b/test/decoders/session_flags/pcap/https_download.pcap
new file mode 100644
index 0000000..f2f5835
--- /dev/null
+++ b/test/decoders/session_flags/pcap/https_download.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/mix-quic-ssl-quic-mail.pcap b/test/decoders/session_flags/pcap/mix-quic-ssl-quic-mail.pcap
new file mode 100644
index 0000000..05f2d03
--- /dev/null
+++ b/test/decoders/session_flags/pcap/mix-quic-ssl-quic-mail.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/mix.json b/test/decoders/session_flags/pcap/mix.json
new file mode 100644
index 0000000..e3144b6
--- /dev/null
+++ b/test/decoders/session_flags/pcap/mix.json
@@ -0,0 +1,65 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 24584,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2}",
+ "common_flags_2": 109076488,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4}",
+ "common_flags_3": 109078186,
+ "common_flags_str_3": "[Bulky,Client is Local,Download,Inbound,Pseudo Unidirectional,Streaming]",
+ "common_flags_identify_info_3": "{\"Bulky\":2250,\"Client is Local\":1,\"Download\":2250,\"Inbound\":2250,\"Pseudo Unidirectional\":2250,\"Streaming\":2250,\"C2S\":1,\"S2C\":2,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4}",
+ "name": "base_0"
+ },
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 1451237384,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"overlapping_template_matching\":10,\"random_excursions\":10,\"random_excursions_variant\":10,\"runs_distribution\":10,\"binary_derivative\":10}",
+ "common_flags_2": 1451237385,
+ "common_flags_str_2": "[Asymmetric,Client is Local]",
+ "common_flags_identify_info_2": "{\"Asymmetric\":4168,\"Client is Local\":1,\"C2S\":1,\"overlapping_template_matching\":10,\"random_excursions\":10,\"random_excursions_variant\":10,\"runs_distribution\":10,\"binary_derivative\":10}",
+ "name": "base_1"
+ },
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 2126323720,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_2": 2126340104,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":6,\"frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_3": 2126340232,
+ "common_flags_str_3": "[Client is Local,Inbound]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Inbound\":29,\"C2S\":1,\"S2C\":6,\"frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "name": "base_2"
+ },
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 1186291720,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2,\"frequency\":2,\"cumulative_sums\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"binary_derivative\":2}",
+ "common_flags_2": 1186291848,
+ "common_flags_str_2": "[Client is Local,Inbound]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"Inbound\":21,\"C2S\":1,\"S2C\":2,\"frequency\":2,\"cumulative_sums\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"binary_derivative\":2}",
+ "common_flags_3": 1186292872,
+ "common_flags_str_3": "[Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Inbound\":21,\"Streaming\":100,\"C2S\":1,\"S2C\":2,\"frequency\":2,\"cumulative_sums\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"binary_derivative\":2}",
+ "common_flags_4": 1186292874,
+ "common_flags_str_4": "[Bulky,Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_4": "{\"Bulky\":430,\"Client is Local\":1,\"Inbound\":21,\"Streaming\":100,\"C2S\":1,\"S2C\":2,\"frequency\":2,\"cumulative_sums\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"binary_derivative\":2}",
+ "common_flags_5": 1186292878,
+ "common_flags_str_5": "[Bulky,CBR Streaming,Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_5": "{\"Bulky\":430,\"CBR Streaming\":6984,\"Client is Local\":1,\"Inbound\":21,\"Streaming\":100,\"C2S\":1,\"S2C\":2,\"frequency\":2,\"cumulative_sums\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"binary_derivative\":2}",
+ "name": "base_3"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/quic_asymmetric.json b/test/decoders/session_flags/pcap/quic_asymmetric.json
new file mode 100644
index 0000000..68e3e37
--- /dev/null
+++ b/test/decoders/session_flags/pcap/quic_asymmetric.json
@@ -0,0 +1,17 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 2126323720,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_2": 2126340104,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":14,\"frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_3": 2126340232,
+ "common_flags_str_3": "[Client is Local,Inbound]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Inbound\":84,\"C2S\":1,\"S2C\":14,\"frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/quic_asymmetric.pcapng b/test/decoders/session_flags/pcap/quic_asymmetric.pcapng
new file mode 100644
index 0000000..5596a03
--- /dev/null
+++ b/test/decoders/session_flags/pcap/quic_asymmetric.pcapng
Binary files differ
diff --git a/test/decoders/session_flags/pcap/quic_asymmetric_1.json b/test/decoders/session_flags/pcap/quic_asymmetric_1.json
new file mode 100644
index 0000000..dab7c23
--- /dev/null
+++ b/test/decoders/session_flags/pcap/quic_asymmetric_1.json
@@ -0,0 +1,14 @@
+[
+ {
+ "common_flags_0": 16392,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"S2C\":1}",
+ "common_flags_1": 1452294152,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"S2C\":1,\"longest_run\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"runs_distribution\":2,\"binary_derivative\":2}",
+ "common_flags_2": 1452294153,
+ "common_flags_str_2": "[Asymmetric,Client is Local]",
+ "common_flags_identify_info_2": "{\"Asymmetric\":37,\"Client is Local\":1,\"S2C\":1,\"longest_run\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"runs_distribution\":2,\"binary_derivative\":2}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/quic_asymmetric_1.pcap b/test/decoders/session_flags/pcap/quic_asymmetric_1.pcap
new file mode 100644
index 0000000..bb55d52
--- /dev/null
+++ b/test/decoders/session_flags/pcap/quic_asymmetric_1.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/quic_online_streaming.json b/test/decoders/session_flags/pcap/quic_online_streaming.json
new file mode 100644
index 0000000..443d331
--- /dev/null
+++ b/test/decoders/session_flags/pcap/quic_online_streaming.json
@@ -0,0 +1,26 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 2124226568,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"frequency\":3,\"cumulative_sums\":3,\"runs\":3,\"longest_run\":3,\"overlapping_template_matching\":3,\"random_excursions\":3,\"random_excursions_variant\":3,\"poker_detect\":3,\"runs_distribution\":3,\"self_correlation\":3,\"binary_derivative\":3}",
+ "common_flags_2": 2124242952,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":4,\"frequency\":3,\"cumulative_sums\":3,\"runs\":3,\"longest_run\":3,\"overlapping_template_matching\":3,\"random_excursions\":3,\"random_excursions_variant\":3,\"poker_detect\":3,\"runs_distribution\":3,\"self_correlation\":3,\"binary_derivative\":3}",
+ "common_flags_3": 2124244104,
+ "common_flags_str_3": "[Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Inbound\":291,\"Streaming\":291,\"C2S\":1,\"S2C\":4,\"frequency\":3,\"cumulative_sums\":3,\"runs\":3,\"longest_run\":3,\"overlapping_template_matching\":3,\"random_excursions\":3,\"random_excursions_variant\":3,\"poker_detect\":3,\"runs_distribution\":3,\"self_correlation\":3,\"binary_derivative\":3}",
+ "common_flags_4": 2124244106,
+ "common_flags_str_4": "[Bulky,Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_4": "{\"Bulky\":630,\"Client is Local\":1,\"Inbound\":291,\"Streaming\":291,\"C2S\":1,\"S2C\":4,\"frequency\":3,\"cumulative_sums\":3,\"runs\":3,\"longest_run\":3,\"overlapping_template_matching\":3,\"random_excursions\":3,\"random_excursions_variant\":3,\"poker_detect\":3,\"runs_distribution\":3,\"self_correlation\":3,\"binary_derivative\":3}",
+ "common_flags_5": 2124276874,
+ "common_flags_str_5": "[Bulky,Client is Local,Inbound,Streaming,Bidirectional]",
+ "common_flags_identify_info_5": "{\"Bulky\":630,\"Client is Local\":1,\"Inbound\":291,\"Streaming\":291,\"C2S\":1,\"S2C\":4,\"Bidirectional\":7315,\"frequency\":3,\"cumulative_sums\":3,\"runs\":3,\"longest_run\":3,\"overlapping_template_matching\":3,\"random_excursions\":3,\"random_excursions_variant\":3,\"poker_detect\":3,\"runs_distribution\":3,\"self_correlation\":3,\"binary_derivative\":3}",
+ "common_flags_6": 2124276938,
+ "common_flags_str_6": "[Bulky,Client is Local,Interactive,Inbound,Streaming,Bidirectional]",
+ "common_flags_identify_info_6": "{\"Bulky\":630,\"Client is Local\":1,\"Interactive\":25786,\"Inbound\":291,\"Streaming\":291,\"C2S\":1,\"S2C\":4,\"Bidirectional\":7315,\"frequency\":3,\"cumulative_sums\":3,\"runs\":3,\"longest_run\":3,\"overlapping_template_matching\":3,\"random_excursions\":3,\"random_excursions_variant\":3,\"poker_detect\":3,\"runs_distribution\":3,\"self_correlation\":3,\"binary_derivative\":3}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/quic_online_streaming.pcap b/test/decoders/session_flags/pcap/quic_online_streaming.pcap
new file mode 100644
index 0000000..9db5c49
--- /dev/null
+++ b/test/decoders/session_flags/pcap/quic_online_streaming.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/ssh-interactive.json b/test/decoders/session_flags/pcap/ssh-interactive.json
new file mode 100644
index 0000000..e6a3e40
--- /dev/null
+++ b/test/decoders/session_flags/pcap/ssh-interactive.json
@@ -0,0 +1,29 @@
+[
+ {
+ "common_flags_0": 8208,
+ "common_flags_str_0": "[Server is Local]",
+ "common_flags_identify_info_0": "{\"Server is Local\":1,\"C2S\":1}",
+ "common_flags_1": 24592,
+ "common_flags_str_1": "[Server is Local]",
+ "common_flags_identify_info_1": "{\"Server is Local\":1,\"C2S\":1,\"S2C\":2}",
+ "common_flags_2": 111173648,
+ "common_flags_str_2": "[Server is Local]",
+ "common_flags_identify_info_2": "{\"Server is Local\":1,\"C2S\":1,\"S2C\":2,\"rank\":7,\"overlapping_template_matching\":7,\"random_excursions\":7,\"random_excursions_variant\":7}",
+ "common_flags_3": 111173904,
+ "common_flags_str_3": "[Server is Local,Outbound]",
+ "common_flags_identify_info_3": "{\"Server is Local\":1,\"Outbound\":40,\"C2S\":1,\"S2C\":2,\"rank\":7,\"overlapping_template_matching\":7,\"random_excursions\":7,\"random_excursions_variant\":7}",
+ "common_flags_4": 111173968,
+ "common_flags_str_4": "[Server is Local,Interactive,Outbound]",
+ "common_flags_identify_info_4": "{\"Server is Local\":1,\"Interactive\":52,\"Outbound\":40,\"C2S\":1,\"S2C\":2,\"rank\":7,\"overlapping_template_matching\":7,\"random_excursions\":7,\"random_excursions_variant\":7}",
+ "name": "base_0"
+ },
+ {
+ "common_flags_0": 16400,
+ "common_flags_str_0": "[Server is Local]",
+ "common_flags_identify_info_0": "{\"Server is Local\":1,\"S2C\":1}",
+ "common_flags_1": 16401,
+ "common_flags_str_1": "[Asymmetric,Server is Local]",
+ "common_flags_identify_info_1": "{\"Asymmetric\":2,\"Server is Local\":1,\"S2C\":1}",
+ "name": "base_1"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/ssh-interactive.pcap b/test/decoders/session_flags/pcap/ssh-interactive.pcap
new file mode 100644
index 0000000..6efe2ab
--- /dev/null
+++ b/test/decoders/session_flags/pcap/ssh-interactive.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/telegram_mtproto_ipv4_key_1.pcap b/test/decoders/session_flags/pcap/telegram_mtproto_ipv4_key_1.pcap
new file mode 100644
index 0000000..7b966dd
--- /dev/null
+++ b/test/decoders/session_flags/pcap/telegram_mtproto_ipv4_key_1.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/telegram_mtproto_ipv6_key_1.pcap b/test/decoders/session_flags/pcap/telegram_mtproto_ipv6_key_1.pcap
new file mode 100644
index 0000000..e0b3144
--- /dev/null
+++ b/test/decoders/session_flags/pcap/telegram_mtproto_ipv6_key_1.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/tencent_meeting_video.json b/test/decoders/session_flags/pcap/tencent_meeting_video.json
new file mode 100644
index 0000000..bb29da4
--- /dev/null
+++ b/test/decoders/session_flags/pcap/tencent_meeting_video.json
@@ -0,0 +1,23 @@
+[
+ {
+ "common_flags_0": 16392,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"S2C\":1}",
+ "common_flags_1": 2126462984,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"S2C\":1,\"frequency\":2,\"block_frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_2": 2126471176,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":5,\"S2C\":1,\"frequency\":2,\"block_frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_3": 2126471432,
+ "common_flags_str_3": "[Client is Local,Outbound]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Outbound\":414,\"C2S\":5,\"S2C\":1,\"frequency\":2,\"block_frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_4": 2126504200,
+ "common_flags_str_4": "[Client is Local,Outbound,Bidirectional]",
+ "common_flags_identify_info_4": "{\"Client is Local\":1,\"Outbound\":414,\"C2S\":5,\"S2C\":1,\"Bidirectional\":2448,\"frequency\":2,\"block_frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_5": 2126504204,
+ "common_flags_str_5": "[CBR Streaming,Client is Local,Outbound,Bidirectional]",
+ "common_flags_identify_info_5": "{\"CBR Streaming\":4095,\"Client is Local\":1,\"Outbound\":414,\"C2S\":5,\"S2C\":1,\"Bidirectional\":2448,\"frequency\":2,\"block_frequency\":2,\"cumulative_sums\":2,\"runs\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/tencent_meeting_video.pcapng b/test/decoders/session_flags/pcap/tencent_meeting_video.pcapng
new file mode 100644
index 0000000..6013cfb
--- /dev/null
+++ b/test/decoders/session_flags/pcap/tencent_meeting_video.pcapng
Binary files differ
diff --git a/test/decoders/session_flags/pcap/tencent_meeting_voice.json b/test/decoders/session_flags/pcap/tencent_meeting_voice.json
new file mode 100644
index 0000000..a81e3c5
--- /dev/null
+++ b/test/decoders/session_flags/pcap/tencent_meeting_voice.json
@@ -0,0 +1,20 @@
+[
+ {
+ "common_flags_0": 16392,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"S2C\":1}",
+ "common_flags_1": 24584,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":2,\"S2C\":1}",
+ "common_flags_2": 2123980808,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":2,\"S2C\":1,\"frequency\":4,\"runs\":4,\"longest_run\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"poker_detect\":4,\"runs_distribution\":4,\"self_correlation\":4,\"binary_derivative\":4}",
+ "common_flags_3": 2123981064,
+ "common_flags_str_3": "[Client is Local,Outbound]",
+ "common_flags_identify_info_3": "{\"Client is Local\":1,\"Outbound\":21,\"C2S\":2,\"S2C\":1,\"frequency\":4,\"runs\":4,\"longest_run\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"poker_detect\":4,\"runs_distribution\":4,\"self_correlation\":4,\"binary_derivative\":4}",
+ "common_flags_4": 2124013832,
+ "common_flags_str_4": "[Client is Local,Outbound,Bidirectional]",
+ "common_flags_identify_info_4": "{\"Client is Local\":1,\"Outbound\":21,\"C2S\":2,\"S2C\":1,\"Bidirectional\":606,\"frequency\":4,\"runs\":4,\"longest_run\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"poker_detect\":4,\"runs_distribution\":4,\"self_correlation\":4,\"binary_derivative\":4}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/tencent_meeting_voice.pcapng b/test/decoders/session_flags/pcap/tencent_meeting_voice.pcapng
new file mode 100644
index 0000000..2935fe5
--- /dev/null
+++ b/test/decoders/session_flags/pcap/tencent_meeting_voice.pcapng
Binary files differ
diff --git a/test/decoders/session_flags/pcap/tls1.2.pcapng b/test/decoders/session_flags/pcap/tls1.2.pcapng
new file mode 100644
index 0000000..ad1a1e0
--- /dev/null
+++ b/test/decoders/session_flags/pcap/tls1.2.pcapng
Binary files differ
diff --git a/test/decoders/session_flags/pcap/tls1.2_no_reuse.pcap b/test/decoders/session_flags/pcap/tls1.2_no_reuse.pcap
new file mode 100644
index 0000000..e911f2b
--- /dev/null
+++ b/test/decoders/session_flags/pcap/tls1.2_no_reuse.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/tls_over_tls_1.2_no_reuse.pcap b/test/decoders/session_flags/pcap/tls_over_tls_1.2_no_reuse.pcap
new file mode 100644
index 0000000..9deb86f
--- /dev/null
+++ b/test/decoders/session_flags/pcap/tls_over_tls_1.2_no_reuse.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/unidirectional.json b/test/decoders/session_flags/pcap/unidirectional.json
new file mode 100644
index 0000000..a681f54
--- /dev/null
+++ b/test/decoders/session_flags/pcap/unidirectional.json
@@ -0,0 +1,14 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 24584,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2}",
+ "common_flags_2": 1185898504,
+ "common_flags_str_2": "[Client is Local]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2,\"frequency\":4,\"block_frequency\":4,\"cumulative_sums\":4,\"runs\":4,\"rank\":4,\"overlapping_template_matching\":4,\"random_excursions\":4,\"random_excursions_variant\":4,\"binary_derivative\":4}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/unidirectional.pcap b/test/decoders/session_flags/pcap/unidirectional.pcap
new file mode 100644
index 0000000..9e7b65a
--- /dev/null
+++ b/test/decoders/session_flags/pcap/unidirectional.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/wechat_voice_call.json b/test/decoders/session_flags/pcap/wechat_voice_call.json
new file mode 100644
index 0000000..a2077d6
--- /dev/null
+++ b/test/decoders/session_flags/pcap/wechat_voice_call.json
@@ -0,0 +1,20 @@
+[
+ {
+ "common_flags_0": 8208,
+ "common_flags_str_0": "[Server is Local]",
+ "common_flags_identify_info_0": "{\"Server is Local\":1,\"C2S\":1}",
+ "common_flags_1": 2123390992,
+ "common_flags_str_1": "[Server is Local]",
+ "common_flags_identify_info_1": "{\"Server is Local\":1,\"C2S\":1,\"S2C\":2,\"longest_run\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_2": 2123391120,
+ "common_flags_str_2": "[Server is Local,Inbound]",
+ "common_flags_identify_info_2": "{\"Server is Local\":1,\"Inbound\":83,\"C2S\":1,\"S2C\":2,\"longest_run\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_3": 2123391124,
+ "common_flags_str_3": "[CBR Streaming,Server is Local,Inbound]",
+ "common_flags_identify_info_3": "{\"CBR Streaming\":3634,\"Server is Local\":1,\"Inbound\":83,\"C2S\":1,\"S2C\":2,\"longest_run\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_4": 2123423892,
+ "common_flags_str_4": "[CBR Streaming,Server is Local,Inbound,Bidirectional]",
+ "common_flags_identify_info_4": "{\"CBR Streaming\":3634,\"Server is Local\":1,\"Inbound\":83,\"C2S\":1,\"S2C\":2,\"Bidirectional\":3808,\"longest_run\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"poker_detect\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/pcap/wechat_voice_call.pcap b/test/decoders/session_flags/pcap/wechat_voice_call.pcap
new file mode 100644
index 0000000..4690892
--- /dev/null
+++ b/test/decoders/session_flags/pcap/wechat_voice_call.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/youtube-quic.pcap b/test/decoders/session_flags/pcap/youtube-quic.pcap
new file mode 100644
index 0000000..b6ae0cd
--- /dev/null
+++ b/test/decoders/session_flags/pcap/youtube-quic.pcap
Binary files differ
diff --git a/test/decoders/session_flags/pcap/youtube.json b/test/decoders/session_flags/pcap/youtube.json
new file mode 100644
index 0000000..6c055db
--- /dev/null
+++ b/test/decoders/session_flags/pcap/youtube.json
@@ -0,0 +1,20 @@
+[
+ {
+ "common_flags_0": 8200,
+ "common_flags_str_0": "[Client is Local]",
+ "common_flags_identify_info_0": "{\"Client is Local\":1,\"C2S\":1}",
+ "common_flags_1": 1991270408,
+ "common_flags_str_1": "[Client is Local]",
+ "common_flags_identify_info_1": "{\"Client is Local\":1,\"C2S\":1,\"S2C\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_2": 1991271560,
+ "common_flags_str_2": "[Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_2": "{\"Client is Local\":1,\"Inbound\":633,\"Streaming\":633,\"C2S\":1,\"S2C\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_3": 1991271562,
+ "common_flags_str_3": "[Bulky,Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_3": "{\"Bulky\":635,\"Client is Local\":1,\"Inbound\":633,\"Streaming\":633,\"C2S\":1,\"S2C\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "common_flags_4": 1991271566,
+ "common_flags_str_4": "[Bulky,CBR Streaming,Client is Local,Inbound,Streaming]",
+ "common_flags_identify_info_4": "{\"Bulky\":635,\"CBR Streaming\":7070,\"Client is Local\":1,\"Inbound\":633,\"Streaming\":633,\"C2S\":1,\"S2C\":2,\"longest_run\":2,\"rank\":2,\"overlapping_template_matching\":2,\"random_excursions\":2,\"random_excursions_variant\":2,\"runs_distribution\":2,\"self_correlation\":2,\"binary_derivative\":2}",
+ "name": "base_0"
+ }
+] \ No newline at end of file
diff --git a/test/decoders/session_flags/plugin_test_main.cpp b/test/decoders/session_flags/plugin_test_main.cpp
new file mode 100644
index 0000000..8f3d996
--- /dev/null
+++ b/test/decoders/session_flags/plugin_test_main.cpp
@@ -0,0 +1,135 @@
+#include "cJSON.h"
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "stellar/stellar.h"
+#ifdef __cplusplus
+}
+#endif
+
+// #define IGNORE_PRINTF
+#ifdef IGNORE_PRINTF
+#define printf(fmt, ...) (0)
+#endif
+static cJSON *g_test_result_root = NULL;
+static cJSON *g_load_result_root = NULL;
+static const char *result_json_path = NULL;
+
+extern "C" int commit_test_result_json(cJSON *node, const char *name)
+{
+ (void)name;
+ if (g_test_result_root)
+ {
+ // cJSON_AddItemToObject(g_test_result_root, name, node);
+ // cJSON_AddStringToObject(node, "name", name);
+ cJSON_AddItemToArray(g_test_result_root, node);
+ return 0;
+ }
+ return -1;
+}
+
+static cJSON *load_result_from_jsonfile(const char *json_path)
+{
+ if (json_path == NULL)
+ return NULL;
+
+ long file_len = 0;
+ char *file_content = NULL;
+ FILE *fp = NULL;
+
+ fp = fopen(json_path, "r+");
+ if (NULL == fp)
+ {
+ return NULL;
+ }
+ fseek(fp, 0, SEEK_END);
+ file_len = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ if (file_len == 0)
+ {
+ fclose(fp);
+ return NULL;
+ }
+ file_content = (char *)malloc(file_len + 1);
+ fread(file_content, file_len, 1, fp);
+ file_content[file_len] = '\0';
+ cJSON *load = cJSON_Parse(file_content);
+ free(file_content);
+ fclose(fp);
+
+ return load;
+}
+
+TEST(PROTOCOL, compare_result_json)
+{
+ EXPECT_EQ(cJSON_GetArraySize(g_test_result_root), cJSON_GetArraySize(g_load_result_root));
+ int ret = cJSON_Compare(g_test_result_root, g_load_result_root, 0);
+ EXPECT_EQ(1, ret);
+
+ if (ret != 1)
+ {
+ char *load_json_str = cJSON_Print(g_load_result_root);
+ printf("LOAD Raw:\n%s\n", load_json_str);
+ free(load_json_str);
+ char *result_json_str = cJSON_Print(g_test_result_root);
+ printf("TEST Raw:\n%s\n", result_json_str);
+ free(result_json_str);
+
+ cJSON *t_load = g_load_result_root->child, *t_test = g_test_result_root->child;
+ while (t_load != NULL)
+ {
+ ret = cJSON_Compare(t_load, t_test, 0);
+ if (ret != 1)
+ {
+ load_json_str = cJSON_Print(t_load);
+ printf("LOAD Diff:\n%s\n", load_json_str);
+ free(load_json_str);
+ result_json_str = cJSON_Print(t_test);
+ printf("TEST Diff:\n%s\n", result_json_str);
+ free(result_json_str);
+ goto fail;
+ }
+ t_load = t_load->next;
+ t_test = t_test->next;
+ }
+ }
+ cJSON_Delete(g_load_result_root);
+ cJSON_Delete(g_test_result_root);
+ return;
+fail:
+ cJSON_Delete(g_load_result_root);
+ cJSON_Delete(g_test_result_root);
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ if (argc < 2)
+ {
+ printf("Usage: %s <result_json_path>\n", argv[0]);
+ result_json_path = NULL;
+ }
+ else
+ {
+ result_json_path = argv[1];
+ g_test_result_root = cJSON_CreateArray();
+ g_load_result_root = load_result_from_jsonfile(result_json_path);
+ assert(g_load_result_root != NULL && g_test_result_root != NULL);
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ struct stellar *st = stellar_new("./conf/stellar.toml", "./plugin/spec.toml", "./conf/log.toml");
+ stellar_run(st);
+ if (result_json_path != NULL)
+ {
+ ret = RUN_ALL_TESTS();
+ }
+ stellar_free(st);
+ return ret;
+}
diff --git a/test/decoders/session_flags/session_flags_pcap_test.cpp b/test/decoders/session_flags/session_flags_pcap_test.cpp
new file mode 100644
index 0000000..14af115
--- /dev/null
+++ b/test/decoders/session_flags/session_flags_pcap_test.cpp
@@ -0,0 +1,630 @@
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stellar/session.h>
+#include <time.h>
+#include <pcap/pcap.h>
+#include <pcap/sll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "session_flags_internal.h"
+#include "gtest/gtest.h"
+extern "C" {
+#include "dummy.h"
+}
+
+#define MAC_FRAME_HEADER_LEN 14
+#define IP_HEADER_LEN 20
+#define TCP_HEADER_LEN 20
+#define UDP_HEADER_LEN 8
+#define LINUX_COOKED_CAPTURE 20
+
+struct pcap_loop_arg
+{
+ pcap_t *pcap_handle;
+ struct session_flags_ctx *ctx;
+};
+
+struct session_flags_plugin_info *sf_plugin_info = NULL;
+
+extern int MESA_dir_link_to_human(int link_route_dir);
+extern char *session_flags_generate_firewall_message(uint64_t flags, uint32_t identify[session_flags_all_mask]);
+
+static inline size_t ts2ms(const timeval *ts)
+{
+ return ts->tv_sec * 1000 + ts->tv_usec / 1000;
+}
+
+static void pcap_handle_cb(pcap_loop_arg *userarg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
+{
+ struct pcap_loop_arg *arg = (struct pcap_loop_arg *)userarg;
+ struct session sess;
+ int payload_len = 0;
+ char *payload = NULL;
+ unsigned short sport, dport;
+ unsigned short eth_proto_type;
+ unsigned char *ip_header;
+ int topic_id = 0;
+
+ memset(&sess, 0, sizeof(sess));
+
+ int data_link_type = pcap_datalink(arg->pcap_handle);
+ switch (data_link_type) {
+ case DLT_EN10MB:
+ eth_proto_type = ntohs(*(unsigned short *)(packet + 12));
+ ip_header = (unsigned char *)(packet + sizeof(struct ethhdr));
+ break;
+ case 276://DLT_LINUX_SLL2
+ eth_proto_type = ntohs(*(unsigned short *)packet);
+ ip_header = (unsigned char *)(packet + LINUX_COOKED_CAPTURE);
+ break;
+ default:
+ return;
+ }
+
+ if (eth_proto_type == ETH_P_IP) {
+ int l4_proto = *(unsigned char *)(ip_header + 9);
+ if (l4_proto == IPPROTO_TCP) {
+ topic_id = DUMMY_TCP_TOPIC_ID;
+ int ip_total_len = ntohs(*(unsigned short *)(ip_header + 2));
+ int ip_header_len = (*(unsigned char *)ip_header & 0x0f) * 4;
+ int tcp_header_len = 4 * ((*(unsigned char *)(ip_header + sizeof(iphdr) + 12) & 0xf0) >> 4);
+ payload_len = ip_total_len - ip_header_len - tcp_header_len;
+ payload = (char *)ip_header + ip_header_len + tcp_header_len;
+ } else if (l4_proto == IPPROTO_UDP) {
+ topic_id = DUMMY_UDP_TOPIC_ID;
+ payload_len = pkthdr->caplen - (ip_header - packet) - sizeof(iphdr) - sizeof(struct udphdr);
+ payload = (char *)ip_header + sizeof(iphdr) + sizeof(struct udphdr);
+ } else {
+ return;
+ }
+
+ sport = ntohs(*(unsigned short *)(ip_header + sizeof(iphdr) + 0));
+ dport = ntohs(*(unsigned short *)(ip_header + sizeof(iphdr) + 2));
+
+ } else if (eth_proto_type == ETH_P_IPV6) {
+ int l4_proto = *(unsigned char *)(ip_header + 6);
+ if (l4_proto == IPPROTO_TCP) {
+ topic_id = DUMMY_TCP_TOPIC_ID;
+ int tcp_header_len = 4 * ((*(unsigned char *)(ip_header + sizeof(struct ip6_hdr) + 12) & 0xf0) >> 4);
+ payload_len = pkthdr->caplen - (ip_header - packet) - sizeof(struct ip6_hdr) - tcp_header_len;
+ payload = (char *)ip_header + sizeof(struct ip6_hdr) + tcp_header_len;
+ } else if (l4_proto == IPPROTO_UDP) {
+ topic_id = DUMMY_UDP_TOPIC_ID;
+ payload_len = pkthdr->caplen - (ip_header - packet) - sizeof(struct ip6_hdr) - sizeof(struct udphdr);
+ payload = (char *)ip_header + sizeof(struct ip6_hdr) + sizeof(struct udphdr);
+ } else {
+ return;
+ }
+
+ sport = ntohs(*(unsigned short *)(ip_header + sizeof(struct ip6_hdr) + 0));
+ dport = ntohs(*(unsigned short *)(ip_header + sizeof(struct ip6_hdr) + 2));
+ } else {
+ return;
+ }
+
+ session_set_current_payload(&sess, payload, payload_len);
+
+ if (sport > dport)
+ {
+ session_flags(sf_plugin_info, arg->ctx, &sess, topic_id, pkthdr->caplen, FLOW_TYPE_C2S, ts2ms(&pkthdr->ts));
+ }
+ else
+ {
+ session_flags(sf_plugin_info, arg->ctx, &sess, topic_id, pkthdr->caplen, FLOW_TYPE_S2C, ts2ms(&pkthdr->ts));
+ }
+
+}
+
+TEST(session_flags, bulky_and_download)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/ftp-data.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_BULKY, SESSION_FLAGS_BULKY);
+ EXPECT_EQ(flags_info->identify[session_flags_bulky_mask], 2333);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_DOWNLOAD, SESSION_FLAGS_DOWNLOAD);
+ EXPECT_EQ(flags_info->identify[session_flags_download_mask], 2333);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, CBR)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/youtube-quic.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_CBR, SESSION_FLAGS_CBR);
+ EXPECT_EQ(flags_info->identify[session_flags_cbr_mask], 3351);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, not_CBR_ftp)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/ftp-data.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_CBR, SESSION_FLAGS_CBR);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, not_CBR_dtls)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/bilibili-dtls.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_CBR, SESSION_FLAGS_CBR);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, server_is_local)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ // session_flags_ip(sf, inet_addr("192.168.1.1"), inet_addr("127.0.0.1"), inet_addr("192.168.1.1"));
+ pcap_t * handle = pcap_open_offline("pcap/ftp-data.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_INBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_LOCAL_SERVER, SESSION_FLAGS_LOCAL_SERVER);
+ EXPECT_EQ(flags_info->identify[session_flags_local_server_mask], 1);
+
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_LOCAL_CLIENT, SESSION_FLAGS_LOCAL_CLIENT);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, client_is_local)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/ftp-data.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_LOCAL_SERVER, SESSION_FLAGS_LOCAL_SERVER);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_LOCAL_CLIENT, SESSION_FLAGS_LOCAL_CLIENT);
+ EXPECT_EQ(flags_info->identify[session_flags_local_client_mask], 1);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, inbound)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/ftp-data.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_LOCAL_CLIENT, SESSION_FLAGS_LOCAL_CLIENT);
+ EXPECT_EQ(flags_info->identify[session_flags_local_client_mask], 1);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_INBOUND, SESSION_FLAGS_INBOUND);
+ EXPECT_EQ(flags_info->identify[session_flags_inbound_mask], 1834);
+
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_OUTBOUND, SESSION_FLAGS_OUTBOUND);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_LOCAL_SERVER, SESSION_FLAGS_LOCAL_SERVER);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, outbound)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/ftp-data.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_INBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_LOCAL_SERVER, SESSION_FLAGS_LOCAL_SERVER);
+ EXPECT_EQ(flags_info->identify[session_flags_local_server_mask], 1);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_OUTBOUND, SESSION_FLAGS_OUTBOUND);
+ EXPECT_EQ(flags_info->identify[session_flags_outbound_mask], 1834);
+
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_LOCAL_CLIENT, SESSION_FLAGS_LOCAL_CLIENT);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_INBOUND, SESSION_FLAGS_INBOUND);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, not_streaming)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/1-dtls.192.168.44.32.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_STREAMING, SESSION_FLAGS_STREAMING);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL, SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_UNIDIRECTIONAL, SESSION_FLAGS_UNIDIRECTIONAL);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, streaming_pseudo_unidirectional_unidirectional)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/ftp-data.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_STREAMING, SESSION_FLAGS_STREAMING);
+ EXPECT_EQ(flags_info->identify[session_flags_streaming_mask], 2333);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL, SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL);
+ EXPECT_EQ(flags_info->identify[session_flags_pseudo_unidirectional_mask], 2333);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_UNIDIRECTIONAL, SESSION_FLAGS_UNIDIRECTIONAL);
+ EXPECT_EQ(flags_info->identify[session_flags_unidirectional_mask], 1834);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, interactive)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/ssh-interactive.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_INTERACTIVE, SESSION_FLAGS_INTERACTIVE);
+ EXPECT_EQ(flags_info->identify[session_flags_interactive_mask], 52);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, not_interactive)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/youtube-quic.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_INTERACTIVE, SESSION_FLAGS_INTERACTIVE);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, random_looking_flags_telegram_mtproto_ipv4_key_1)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/telegram_mtproto_ipv4_key_1.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_RANDOM_LOOKING, SESSION_FLAGS_RANDOM_LOOKING);
+#if 0
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_FREQUENCY, SESSION_FLAGS_FREQUENCY);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_BLOCK_FREQUENCY, SESSION_FLAGS_BLOCK_FREQUENCY);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_CUMULATIVE_SUMS, SESSION_FLAGS_CUMULATIVE_SUMS);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RUNS, SESSION_FLAGS_RUNS);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_LONGEST_RUN, SESSION_FLAGS_LONGEST_RUN);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RANK, 0);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_NON_OVERLAPPING_TEMPLATE_MATCHING, 0);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_OVERLAPPING_TEMPLATE_MATCHING, SESSION_FLAGS_OVERLAPPING_TEMPLATE_MATCHING);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_UNIVERSAL, 0);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RANDOM_EXCURSIONS, SESSION_FLAGS_RANDOM_EXCURSIONS);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RANDOM_EXCURSIONS_VARIANT, SESSION_FLAGS_RANDOM_EXCURSIONS_VARIANT);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_POKER_DETECT, SESSION_FLAGS_POKER_DETECT);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RUNS_DISTRIBUTION, SESSION_FLAGS_RUNS_DISTRIBUTION);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_SELF_CORRELATION, SESSION_FLAGS_SELF_CORRELATION);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_BINARY_DERIVATIVE, SESSION_FLAGS_BINARY_DERIVATIVE);
+#endif
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, random_looking_flags_telegram_mtproto_ipv6_key_1)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t * handle = pcap_open_offline("pcap/telegram_mtproto_ipv6_key_1.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_RANDOM_LOOKING, SESSION_FLAGS_RANDOM_LOOKING);
+
+#if 0
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_FREQUENCY, SESSION_FLAGS_FREQUENCY);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_BLOCK_FREQUENCY, SESSION_FLAGS_BLOCK_FREQUENCY);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_CUMULATIVE_SUMS, SESSION_FLAGS_CUMULATIVE_SUMS);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RUNS, SESSION_FLAGS_RUNS);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_LONGEST_RUN, SESSION_FLAGS_LONGEST_RUN);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RANK, 0);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_NON_OVERLAPPING_TEMPLATE_MATCHING, 0);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_OVERLAPPING_TEMPLATE_MATCHING, SESSION_FLAGS_OVERLAPPING_TEMPLATE_MATCHING);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_UNIVERSAL, 0);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RANDOM_EXCURSIONS, SESSION_FLAGS_RANDOM_EXCURSIONS);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RANDOM_EXCURSIONS_VARIANT, SESSION_FLAGS_RANDOM_EXCURSIONS_VARIANT);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_POKER_DETECT, SESSION_FLAGS_POKER_DETECT);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_RUNS_DISTRIBUTION, SESSION_FLAGS_RUNS_DISTRIBUTION);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_SELF_CORRELATION, SESSION_FLAGS_SELF_CORRELATION);
+ EXPECT_EQ(flags_info->random_looking_flags & SESSION_FLAGS_BINARY_DERIVATIVE, SESSION_FLAGS_BINARY_DERIVATIVE);
+#endif
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, bidirectional)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t *handle = pcap_open_offline("pcap/wechat_voice_call.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_INBOUND);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_BIDIRECTIONAL, SESSION_FLAGS_BIDIRECTIONAL);
+
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, tunneling_tls_fet)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t *handle = pcap_open_offline("pcap/tls1.2_no_reuse.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+ tunneling_hs_stream_init(sf_plugin_info, ctx);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_TUNNELING, SESSION_FLAGS_TUNNELING);
+
+ tunneling_hs_stream_free(ctx);
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, tunneling_tls_over_tls)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t *handle = pcap_open_offline("pcap/tls_over_tls_1.2_no_reuse.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+ tunneling_hs_stream_init(sf_plugin_info, ctx);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_TUNNELING, SESSION_FLAGS_TUNNELING);
+
+ tunneling_hs_stream_free(ctx);
+ pcap_close(handle);
+ free(ctx);
+}
+
+TEST(session_flags, tunneling_tls)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct session_flags_ctx *ctx = (struct session_flags_ctx *)calloc(1, sizeof(struct session_flags_ctx));
+ pcap_t *handle = pcap_open_offline("pcap/tls1.2.pcapng", error);
+ ASSERT_NE(handle, nullptr);
+
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+ tunneling_hs_stream_init(sf_plugin_info, ctx);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_TUNNELING, SESSION_FLAGS_TUNNELING);
+
+ tunneling_hs_stream_free(ctx);
+ pcap_close(handle);
+ free(ctx);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+
+ sf_plugin_info = (struct session_flags_plugin_info *)session_flags_plugin_init(NULL);
+ sf_plugin_info->log_handle = NULL;
+
+ int result = RUN_ALL_TESTS();
+
+ session_flags_plugin_exit(sf_plugin_info);
+
+ return result;
+} \ No newline at end of file
diff --git a/test/decoders/session_flags/session_flags_static_test.cpp b/test/decoders/session_flags/session_flags_static_test.cpp
new file mode 100644
index 0000000..d415807
--- /dev/null
+++ b/test/decoders/session_flags/session_flags_static_test.cpp
@@ -0,0 +1,494 @@
+#include <cstring>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stellar/session.h>
+#include "session_flags_internal.h"
+#include "gtest/gtest.h"
+
+extern "C" {
+#include "dummy.h"
+}
+
+struct session_flags_plugin_info *sf_plugin_info = NULL;
+
+TEST(session_flags_static, bulky)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ int cur_time_ms = 1;
+ for (int i = 0; i < 100; i++)
+ {
+ for (int j = 0; j < 96; j++)
+ {
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 1001, FLOW_TYPE_C2S, cur_time_ms);
+ cur_time_ms++;
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 66, FLOW_TYPE_S2C, cur_time_ms);
+ cur_time_ms++;
+ }
+ for (int j = 0; j < 4; j++)
+ {
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 100, FLOW_TYPE_S2C, cur_time_ms);
+ cur_time_ms++;
+ }
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_BULKY, SESSION_FLAGS_BULKY);
+ EXPECT_EQ(flags_info->identify[session_flags_bulky_mask], 6001);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ("{\"Bulky\":196,\"Client is Local\":1,\"Outbound\":196,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, download_gt_95)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ int cur_time_ms = 1;
+ for (int i = 0; i < 100; i++)
+ {
+ for (int j = 0; j < 96; j++)
+ {
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 1001, FLOW_TYPE_C2S, cur_time_ms);
+ cur_time_ms++;
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 66, FLOW_TYPE_S2C, cur_time_ms);
+ cur_time_ms++;
+ }
+ for (int j = 0; j < 4; j++)
+ {
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 100, FLOW_TYPE_S2C, cur_time_ms);
+ cur_time_ms++;
+ }
+ }
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_DOWNLOAD, SESSION_FLAGS_DOWNLOAD);
+ EXPECT_EQ(flags_info->identify[session_flags_download_mask], 6001);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ("{\"Bulky\":195,\"Client is Local\":1,\"Outbound\":195,\"Streaming\":195,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, download_lt_95)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ int cur_time_ms = 1;
+ for (int i = 0; i < 100; i++)
+ {
+ for (int j = 0; j < 94; j++)
+ {
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 1001, FLOW_TYPE_C2S, cur_time_ms);
+ cur_time_ms++;
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 66, FLOW_TYPE_S2C, cur_time_ms);
+ cur_time_ms++;
+ }
+ for (int j = 0; j < 6; j++)
+ {
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 100, FLOW_TYPE_S2C, cur_time_ms);
+ cur_time_ms++;
+ }
+ }
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_DOWNLOAD, SESSION_FLAGS_DOWNLOAD);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ("{\"Bulky\":195,\"Client is Local\":1,\"Outbound\":195,\"Streaming\":195,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, CBR_lt_1s)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ int std_bytes = 1000;
+ int cur_time_ms = 1;
+ for (int i = 0; i < 100; i++)
+ {
+ long delta = random() % 200;
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, std_bytes + delta, FLOW_TYPE_C2S, cur_time_ms++);
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 66, FLOW_TYPE_S2C, cur_time_ms++);
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_CBR, SESSION_FLAGS_CBR);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ("{\"Bulky\":200,\"Client is Local\":1,\"Download\":200,\"Outbound\":200,\"Pseudo Unidirectional\":200,\"Streaming\":200,\"Unidirectional\":200,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, CBR)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ for (uint64_t cur_time_ms = 1; cur_time_ms < 11000; cur_time_ms += 10)
+ {
+ long delta = random() % 200;
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 1000 + delta, FLOW_TYPE_C2S, cur_time_ms);
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 66, FLOW_TYPE_S2C, cur_time_ms);
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_CBR, SESSION_FLAGS_CBR);
+ EXPECT_EQ(flags_info->identify[session_flags_cbr_mask], 1001);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ("{\"Bulky\":201,\"CBR Streaming\":2021,\"Client is Local\":1,\"Download\":201,\"Outbound\":201,\"Pseudo Unidirectional\":201,\"Streaming\":201,\"Unidirectional\":201,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, not_CBR)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ for (uint64_t cur_time_ms = 1, i = 0; cur_time_ms < 10000; cur_time_ms += 500)
+ {
+ int delta = (i++ % 10) * 100;
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 100 + delta, FLOW_TYPE_C2S, cur_time_ms);
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 66, FLOW_TYPE_S2C, cur_time_ms);
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_CBR, SESSION_FLAGS_CBR);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ( "{\"Client is Local\":1,\"Outbound\":21,\"Pseudo Unidirectional\":21,\"Unidirectional\":21,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, interactive)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ uint64_t cur_time_ms = 1;
+ while(cur_time_ms < 60000)
+ {
+ long delta = random() % 100;
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 1000 + delta, FLOW_TYPE_C2S, cur_time_ms++);
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 66, FLOW_TYPE_S2C, cur_time_ms++);
+ }
+ while(cur_time_ms < 200000)
+ {
+ cur_time_ms += 10000;
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 80, FLOW_TYPE_C2S, cur_time_ms);
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 66, FLOW_TYPE_S2C, cur_time_ms);
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_INTERACTIVE, SESSION_FLAGS_INTERACTIVE);
+ EXPECT_EQ(flags_info->identify[session_flags_interactive_mask], 60009);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ( "{\"Bulky\":1001,\"CBR Streaming\":10021,\"Client is Local\":1,\"Download\":1001,\"Interactive\":60009,\"Outbound\":1001,\"Pseudo Unidirectional\":1001,\"Streaming\":1001,\"Unidirectional\":1001,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, streaming_gt_90)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ session_set_current_payload(&sess, NULL, 100);
+ for (uint64_t cur_time_ms = 1; cur_time_ms < 10000; cur_time_ms++)
+ {
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 861, FLOW_TYPE_C2S, cur_time_ms);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 139, FLOW_TYPE_S2C, cur_time_ms);
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_STREAMING, SESSION_FLAGS_STREAMING);
+ EXPECT_EQ(flags_info->identify[session_flags_streaming_mask], 12001);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ("{\"Client is Local\":1,\"Outbound\":668,\"Streaming\":669,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, streaming_lt_90)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ session_set_current_payload(&sess, NULL, 100);
+ for (uint64_t cur_time_ms = 1; cur_time_ms < 10000; cur_time_ms++)
+ {
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 859, FLOW_TYPE_C2S, cur_time_ms);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 141, FLOW_TYPE_S2C, cur_time_ms);
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_STREAMING, 0);
+ EXPECT_EQ(flags_info->identify[session_flags_streaming_mask], 0);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ("{\"Client is Local\":1,\"Outbound\":668,\"Streaming\":669,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, presu_unidirectional_gt_95)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ uint64_t cur_time_ms = 1;
+ session_set_current_payload(&sess, NULL, 100);
+ for (int i = 0; i < 100; i++)
+ {
+ for (int j = 0; j < 96; j++)
+ {
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 100 + random() % 1000, FLOW_TYPE_C2S, cur_time_ms);
+ cur_time_ms++;
+ }
+ for (int j = 0; j < 4; j++)
+ {
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 100 + random() % 1000, FLOW_TYPE_S2C, cur_time_ms);
+ cur_time_ms++;
+ }
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL, SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL);
+ EXPECT_EQ(flags_info->identify[session_flags_pseudo_unidirectional_mask], 6001);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ( "{\"Client is Local\":1,\"Outbound\":1002,\"Pseudo Unidirectional\":1002,\"Streaming\":1002,\"C2S\":1,\"S2C\":99}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, presu_unidirectional_lt_95)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ uint64_t cur_time_ms = 1;
+ session_set_current_payload(&sess, NULL, 100);
+ for (int i = 0; i < 100; i++)
+ {
+ for (int j = 0; j < 94; j++)
+ {
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 100 + random() % 1000, FLOW_TYPE_C2S, cur_time_ms);
+ cur_time_ms++;
+ }
+ for (int j = 0; j < 6; j++)
+ {
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 100 + random() % 1000, FLOW_TYPE_S2C, cur_time_ms);
+ cur_time_ms++;
+ }
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL, 0);
+ EXPECT_EQ(flags_info->identify[session_flags_pseudo_unidirectional_mask], 0);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ( "{\"Client is Local\":1,\"Outbound\":1002,\"Pseudo Unidirectional\":1002,\"Streaming\":1002,\"C2S\":1,\"S2C\":99}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, Unidirectional)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ for (uint64_t cur_time_ms = 1; cur_time_ms < 10000; cur_time_ms++)
+ {
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 100 + random() % 1000, FLOW_TYPE_C2S, cur_time_ms);
+
+ session_set_current_payload(&sess, NULL, 0);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 80, FLOW_TYPE_S2C, cur_time_ms);
+ }
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_UNIDIRECTIONAL, SESSION_FLAGS_UNIDIRECTIONAL);
+ EXPECT_EQ(flags_info->identify[session_flags_unidirectional_mask], 10001);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL, SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL);
+ EXPECT_EQ(flags_info->identify[session_flags_pseudo_unidirectional_mask], 12001);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ("{\"Client is Local\":1,\"Outbound\":668,\"Pseudo Unidirectional\":669,\"Unidirectional\":669,\"C2S\":1,\"S2C\":2}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, not_unidirectional)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ srandom(time(NULL));
+ uint64_t cur_time_ms = 1;
+ session_set_current_payload(&sess, NULL, 100);
+ for (int i = 1; i < 10000; i++)
+ {
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 100 + random() % 1000, FLOW_TYPE_C2S, cur_time_ms++);
+ }
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 80, FLOW_TYPE_S2C, cur_time_ms++);
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_UNIDIRECTIONAL, SESSION_FLAGS_UNIDIRECTIONAL);
+
+ struct session_flags_message *msg = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ( "{\"Client is Local\":1, \"Outbound\":10000, \"C2S\":1,\"S2C\":10000}", identify_str);
+ EXPECT_EQ(msg->packet_sequence_array[0], 1);
+ EXPECT_EQ(msg->packet_sequence_array[1], 10000);
+ EXPECT_EQ(msg->packet_sequence_array[2], 1);
+ EXPECT_EQ(msg->packet_sequence_array[3], 10000);
+ free(msg->packet_sequence_array);
+ free(msg);
+ free(ctx);
+}
+
+TEST(session_flags_static, dns)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ uint64_t cur_time_ms = 1000;
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 72, FLOW_TYPE_C2S, cur_time_ms);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 72, FLOW_TYPE_C2S, cur_time_ms);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 72, FLOW_TYPE_C2S, cur_time_ms+10000);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 72, FLOW_TYPE_C2S, cur_time_ms+10000);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 146, FLOW_TYPE_S2C, cur_time_ms+15000);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_UDP_TOPIC_ID, 72, FLOW_TYPE_C2S, cur_time_ms+15000);
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_NE(flags_info->flags & SESSION_FLAGS_CBR, SESSION_FLAGS_CBR);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_LOCAL_CLIENT, SESSION_FLAGS_LOCAL_CLIENT);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_C2S, SESSION_FLAGS_C2S);
+ EXPECT_EQ(flags_info->identify[session_flags_c2s_mask], 1);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_S2C, SESSION_FLAGS_S2C);
+ EXPECT_EQ(flags_info->identify[session_flags_s2c_mask], 5);
+
+ //char *identify_str = session_flags_generate_firewall_message(flags_info->flags, flags_info->identify);
+ //EXPECT_STREQ( "{\"Client is Local\":1,\"Outbound\":6,\"C2S\":1,\"S2C\":5}", identify_str);
+ //free(identify_str);
+ free(ctx);
+}
+
+TEST(session_flags_static, bidirectional)
+{
+ struct session_flags_ctx *ctx = (struct session_flags_ctx*)calloc(1, sizeof(struct session_flags_ctx));
+ struct session sess;
+
+ memset(&sess, 0, sizeof(sess));
+ session_flags_stat_init(&ctx->stat, SESSION_DIRECTION_OUTBOUND);
+
+ int cur_time_ms = 1;
+ session_set_current_payload(&sess, NULL, 100);
+ for (int i = 0; i < 5000; i++)
+ {
+ session_set_current_payload(&sess, NULL, 100);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 100, FLOW_TYPE_C2S, cur_time_ms++);
+
+ session_set_current_payload(&sess, NULL, 99);
+ session_flags(sf_plugin_info, ctx, &sess, DUMMY_TCP_TOPIC_ID, 99, FLOW_TYPE_S2C, cur_time_ms++);
+ }
+
+ struct session_flags_result *flags_info = session_flags_get_flags(&ctx->stat);
+ EXPECT_EQ(flags_info->flags & SESSION_FLAGS_BIDIRECTIONAL, SESSION_FLAGS_BIDIRECTIONAL);
+ EXPECT_EQ(flags_info->identify[session_flags_bidirectional_mask], 6001);
+ free(ctx);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+ //testing::GTEST_FLAG(filter) = "";
+ sf_plugin_info = (struct session_flags_plugin_info *)session_flags_plugin_init(NULL);
+ sf_plugin_info->log_handle = NULL;
+ int result = RUN_ALL_TESTS();
+ session_flags_plugin_exit(sf_plugin_info);
+
+ return result;
+} \ No newline at end of file
diff --git a/test/decoders/session_flags/session_flags_test_plugin.cpp b/test/decoders/session_flags/session_flags_test_plugin.cpp
new file mode 100644
index 0000000..27d2377
--- /dev/null
+++ b/test/decoders/session_flags/session_flags_test_plugin.cpp
@@ -0,0 +1,210 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "cJSON.h"
+
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+
+#include "stellar/session_flags.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+int commit_test_result_json(cJSON *node, const char *name);
+}
+#endif
+
+#define unused(x) ((void)(x))
+
+int g_test_session_flags_plugin_id;
+int g_session_flags_topic_id;
+
+int g_receive_msg_count = 0;
+
+static void session_flags_2_str(uint64_t flags, char *str, int str_len)
+{
+ if (str == NULL || str_len == 0)
+ return;
+
+ int offset = 0;
+ str[0] = '[';
+ offset += 1;
+
+ if (flags & SESSION_FLAGS_BULKY && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Bulky,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_CBR && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "CBR Streaming,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_LOCAL_CLIENT && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Client is Local,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_LOCAL_SERVER && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Server is Local,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_DOWNLOAD && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Download,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_INTERACTIVE && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Interactive,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_INBOUND && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Inbound,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_OUTBOUND && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Outbound,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_PSEUDO_UNIDIRECTIONAL && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Pseudo Unidirectional,");
+ offset = strlen(str);
+ }
+
+
+ if (flags & SESSION_FLAGS_STREAMING && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Streaming,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_UNIDIRECTIONAL && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Unidirectional,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_RANDOM_LOOKING && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Random Looking,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_C2S && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "C2S,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_S2C && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "S2C,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_BIDIRECTIONAL && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Bidirectional,");
+ offset = strlen(str);
+ }
+
+ if (flags & SESSION_FLAGS_TUNNELING && str_len > offset)
+ {
+ snprintf(&str[offset], str_len-offset, "Tunneling,");
+ offset = strlen(str);
+ }
+
+
+ str[offset-1] = ']';
+}
+
+static void append_json(cJSON *root, struct session_flags_message *sf_message)
+{
+ char key[128] = {0};
+ snprintf(key, 128, "common_flags_%d", g_receive_msg_count);
+ cJSON_AddNumberToObject(root, (const char *)key, sf_message->flags);
+
+ char str[1024] = {0};
+ session_flags_2_str(sf_message->flags, str, 1024);
+ snprintf(key, 128, "common_flags_str_%d", g_receive_msg_count);
+ cJSON_AddStringToObject(root, (const char *)key, str);
+
+ char identify_str[1024] = {0};
+ int offset = 0;
+ for (unsigned int i = 0; i < sf_message->array_num; i++)
+ {
+ offset += snprintf(identify_str + offset, 1024-offset, "%u,", sf_message->packet_sequence_array[i]);
+ }
+ snprintf(key, 128, "common_flags_identify_info_%d", g_receive_msg_count);
+ cJSON_AddStringToObject(root, (const char *)key, identify_str);
+
+ g_receive_msg_count++;
+}
+
+void test_session_flags_entry(struct session *session, int topic_id, const void *msg, void *per_session_ctx, void *plugin_env)
+{
+ unused(plugin_env);
+ unused(session);
+ unused(topic_id);
+
+ if (msg == NULL)
+ {
+ return;
+ }
+
+ struct session_flags_message *sf_message = (struct session_flags_message *)msg;
+ cJSON *json_root = (cJSON *)per_session_ctx;
+
+ append_json(json_root, sf_message);
+
+}
+
+void *ctx_new(struct session *session, void *plugin_env)
+{
+ unused(plugin_env);
+ unused(session);
+
+ cJSON *root = cJSON_CreateObject();
+ return (void *)root;
+}
+
+void ctx_free(struct session *sess, void *session_ctx, void *plugin_env)
+{
+ unused(sess);
+ unused(plugin_env);
+
+ commit_test_result_json((cJSON *)session_ctx, "session_flags_test");
+}
+
+extern "C" void *SESSION_FLAGS_TEST_PLUG_INIT(struct stellar *st)
+{
+ g_test_session_flags_plugin_id = stellar_session_plugin_register(st, ctx_new, ctx_free, NULL);
+ g_session_flags_topic_id = stellar_mq_get_topic_id(st, SESSION_FLAGS_MESSAGE_TOPIC);
+
+ stellar_session_mq_subscribe(st, g_session_flags_topic_id, test_session_flags_entry, g_test_session_flags_plugin_id);
+
+ return NULL;
+}
+
+extern "C" void SESSION_FLAGS_TEST_PLUG_DESTROY(void *ctx)
+{
+ unused(ctx);
+ return;
+} \ No newline at end of file
diff --git a/test/decoders/session_flags/test_based_on_stellar/CMakeLists.txt b/test/decoders/session_flags/test_based_on_stellar/CMakeLists.txt
new file mode 100644
index 0000000..9f4f5ba
--- /dev/null
+++ b/test/decoders/session_flags/test_based_on_stellar/CMakeLists.txt
@@ -0,0 +1,37 @@
+set(DECODER_NAME session_flags)
+
+set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/test/decoders/session_flags)
+set(SAPP_DEVEL_DIR ${TEST_RUN_DIR}/lib)
+set(TEST_MAIN session_flags_test_main)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_SOURCE_DIR}/test)
+include_directories(/usr/local/include/cjson)
+include_directories(/opt/tsg/framework/include/stellar)
+include_directories(/opt/MESA/include/MESA)
+include_directories(/opt/tsg/stellar/include/)
+
+#various ways to add -rdynamic for centos7, centos8, and different cmake version
+add_definitions(-rdynamic)
+link_directories(${SAPP_DEVEL_DIR})
+
+# assemble test env
+add_test(NAME SESSION_FLAGS_MKDIR_METRIC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/metrics; mkdir -p ${TEST_RUN_DIR}/plugin; mkdir -p ${TEST_RUN_DIR}/log; mkdir -p ${TEST_RUN_DIR}/pcap")
+add_test(NAME SESSION_FLAGS_COPY_SPEC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/plugin/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/spec.toml ${TEST_RUN_DIR}/plugin/spec.toml")
+add_test(NAME SESSION_FLAGS_COPY_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/conf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/stellar.toml ${TEST_RUN_DIR}/conf/stellar.toml")
+add_test(NAME SESSION_FLAGS_COPY_LOG_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/conf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/log.toml ${TEST_RUN_DIR}/conf/log.toml")
+
+# update plugin to be tested
+add_test(NAME SESSION_FLAGS_CP_DECODER_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/decoders/session_flags/session_flags_dyn.so ${TEST_RUN_DIR}/plugin/${DECODER_NAME}.so")
+add_test(NAME SESSION_FLAGS_CP_DECODER_GTEST_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/test/decoders/session_flags/${DECODER_NAME}_test.so ${TEST_RUN_DIR}/plugin/${DECODER_NAME}_test.so")
+
+set_tests_properties(SESSION_FLAGS_MKDIR_METRIC SESSION_FLAGS_COPY_SPEC
+ SESSION_FLAGS_COPY_CONF SESSION_FLAGS_COPY_LOG_CONF
+ SESSION_FLAGS_CP_DECODER_SO SESSION_FLAGS_CP_DECODER_GTEST_SO
+ PROPERTIES FIXTURES_SETUP TestFixture)
+
+set(TEST_PCAP_DIR ${PROJECT_SOURCE_DIR}/test/decoders/session_flags/pcap)
+
+# run tests
+add_test(NAME SESSION_FLAGS_PLUGIN_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/https_download.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_PCAP_DIR}/https_download.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+
diff --git a/test/decoders/session_flags/test_based_on_stellar/env/log.toml b/test/decoders/session_flags/test_based_on_stellar/env/log.toml
new file mode 100644
index 0000000..a51abd8
--- /dev/null
+++ b/test/decoders/session_flags/test_based_on_stellar/env/log.toml
@@ -0,0 +1,4 @@
+[log]
+output = "stderr" # stderr, file
+file = "log/stellar.log"
+level = "ERROR" # TRACE, DEBUG, INFO, WARN, ERROR, FATAL
diff --git a/test/decoders/session_flags/test_based_on_stellar/env/session_flags.toml b/test/decoders/session_flags/test_based_on_stellar/env/session_flags.toml
new file mode 100644
index 0000000..af62a2b
--- /dev/null
+++ b/test/decoders/session_flags/test_based_on_stellar/env/session_flags.toml
@@ -0,0 +1,9 @@
+[SESSION_FLAGS]
+FET_ENABLED=1
+INTERACTIVE_STARTTIME_MS = 10000
+INTERACTIVE_PULSE_NUM = 4
+INTERACTIVE_LATENCY_MS = 5000
+MAIN_DIR_FRONT_N_PKTS = 100
+LARGE_PKTS_INIT_SIZE = 1000
+RANDOM_LOOKING_JUDGE_LIST="{\"random_looking_judge_list\":[ \"frequency\", \"block_frequency\", \"cumulative_sums\", \"runs\", \"longest_run\", \"rank\", \"non_overlapping_template_matching\", \"overlapping_template_matching\", \"universal\", \"random_excursions\", \"random_excursions_variant\", \"poker_detect\", \"runs_distribution\", \"self_correlation\", \"binary_derivative\" ]}"
+TUNNELING_PCRE_LIST="{\"tunneling_pcre_list\":[\"(B|C)(d){3,5}(a|b|c|d)(A|B)b(A|B|C|D)\", \"(B|C)(d){3,5}(a|b|c|d)Aa(A|B|C|D)\", \"(B|C)(d){2}(b|c)(A|B)b(A|B|C|D)\", \"(B|C)(d){2}(b|c)Aa(A|B|C|D)\"]}"
diff --git a/test/decoders/session_flags/test_based_on_stellar/env/spec.toml b/test/decoders/session_flags/test_based_on_stellar/env/spec.toml
new file mode 100644
index 0000000..ead4371
--- /dev/null
+++ b/test/decoders/session_flags/test_based_on_stellar/env/spec.toml
@@ -0,0 +1,9 @@
+[[plugin]]
+path = "./plugin/session_flags.so"
+init = "session_flags_plugin_init"
+exit = "session_flags_plugin_exit"
+
+[[plugin]]
+path = "./plugin/session_flags_test.so"
+init = "SESSION_FLAGS_TEST_PLUG_INIT"
+exit = "SESSION_FLAGS_TEST_PLUG_DESTROY"
diff --git a/test/decoders/session_flags/test_based_on_stellar/env/stellar.toml b/test/decoders/session_flags/test_based_on_stellar/env/stellar.toml
new file mode 100644
index 0000000..308c884
--- /dev/null
+++ b/test/decoders/session_flags/test_based_on_stellar/env/stellar.toml
@@ -0,0 +1,64 @@
+[instance]
+ id = 1 # range: [0, 4095] (20 bit)
+
+[packet_io]
+ mode = "pcapfile" # pcapfile, pcaplist, marsio
+ app_symbol = "stellar"
+ dev_symbol = "nf_0_fw"
+ pcap_path = "./pcap/test.pcap"
+ nr_worker_thread = 1 # range: [1, 256]
+ cpu_mask = [5, 6, 7, 8, 9, 10, 11, 12]
+ idle_yield_interval_ms = 90 # range: [0, 60000] (ms)
+
+[ip_reassembly]
+ enable = 1
+ bucket_entries = 32 # range: [1, 4294967295] (must be power of 2)
+ bucket_num = 1024 # range: [1, 4294967295]
+
+ ip_frag_timeout_ms = 1000 # range: [1, 60000] (ms)
+ ip_frag_expire_polling_interval_ms = 0 # range: [0, 60000] (ms)
+ ip_frag_expire_polling_limit = 1024 # range: [1, 1024]
+
+[session_manager]
+ tcp_session_max = 500
+ udp_session_max = 500
+
+ evict_old_on_tcp_table_limit = 1 # range: [0, 1]
+ evict_old_on_udp_table_limit = 1 # range: [0, 1]
+
+ expire_period_ms = 0 # range: [0, 60000] (ms)
+ expire_batch_max = 1024 # range: [1, 1024]
+
+ [session_manager.tcp_timeout_ms]
+ init = 500 # range: [1, 60000] (ms)
+ handshake = 500 # range: [1, 60000] (ms)
+ data = 500 # range: [1, 15999999000] (ms)
+ half_closed = 500 # range: [1, 604800000] (ms)
+ time_wait = 500 # range: [1, 600000] (ms)
+ discard_default = 1000 # range: [1, 15999999000] (ms)
+ unverified_rst = 500 # range: [1, 600000] (ms)
+
+ [session_manager.udp_timeout_ms]
+ data = 500 # range: [1, 15999999000] (ms)
+ discard_default = 500 # range: [1, 15999999000] (ms)
+
+ [session_manager.duplicated_packet_bloom_filter]
+ enable = 0
+ capacity = 1000000 # range: [1, 4294967295]
+ time_window_ms = 10000 # range: [1, 60000] (ms)
+ error_rate = 0.00001 # range: [0.0, 1.0]
+
+ [session_manager.evicted_session_bloom_filter]
+ enable = 0 # range: [0, 1]
+ capacity = 1000000 # range: [1, 4294967295]
+ time_window_ms = 10000 # range: [1, 60000] (ms)
+ error_rate = 0.00001 # range: [0.0, 1.0]
+
+ [session_manager.tcp_reassembly]
+ enable = 1 # range: [0, 1]
+ timeout_ms = 100 # range: [1, 60000] (ms)
+ buffered_segments_max = 256 # range: [2, 4096] per flow
+
+[stat]
+ merge_interval_ms = 500 # range: [0, 60000] (ms)
+ output_interval_ms = 1000 # range: [0, 60000] (ms)
diff --git a/test/decoders/socks/CMakeLists.txt b/test/decoders/socks/CMakeLists.txt
new file mode 100644
index 0000000..f40930a
--- /dev/null
+++ b/test/decoders/socks/CMakeLists.txt
@@ -0,0 +1,38 @@
+set(DECODER_NAME socks)
+
+add_library(${DECODER_NAME}_test SHARED socks_decoder_test_plugin.cpp)
+add_dependencies(${DECODER_NAME}_test ${DECODER_NAME})
+set_target_properties(${DECODER_NAME}_test PROPERTIES PREFIX "")
+
+set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/testing)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_SOURCE_DIR}/src)
+include_directories(${CMAKE_BINARY_DIR}/vendors/cjson/src/cjson/include)
+include_directories(${PROJECT_SOURCE_DIR}/decoders/socks)
+include_directories(${PROJECT_SOURCE_DIR}/include/stellar)
+
+add_executable(gtest_pcap_socks socks_decoder_pcap_gtest.cpp dummy.c ${PROJECT_SOURCE_DIR}/decoders/socks/socks_decoder.cpp)
+target_link_libraries(gtest_pcap_socks gtest logger pcap)
+
+add_executable(socks_test_main plugin_test_main.cpp)
+set_target_properties(socks_test_main
+ PROPERTIES
+ LINK_OPTIONS
+ "-rdynamic"
+ )
+set_target_properties(socks_test_main
+ PROPERTIES
+ LINK_FLAGS
+ "-rdynamic"
+ )
+set(LINK_FLAGS "-rdynamic")
+target_link_libraries(socks_test_main gtest cjson-static stellar_lib)
+
+add_subdirectory(test_based_on_stellar)
+
+#copy pcap file folder to build directory
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/pcap DESTINATION ${CMAKE_BINARY_DIR}/test/decoders/socks)
+
+include(GoogleTest)
+gtest_discover_tests(gtest_pcap_socks)
diff --git a/test/decoders/socks/dummy.c b/test/decoders/socks/dummy.c
new file mode 100644
index 0000000..bd15d44
--- /dev/null
+++ b/test/decoders/socks/dummy.c
@@ -0,0 +1,151 @@
+#include "dummy.h"
+
+#define UNUSED(x) (void)(x)
+
+int direction;
+
+void session_set_current_state(struct session *sess, enum session_state sess_state)
+{
+ sess->sess_state = sess_state;
+
+ return;
+}
+enum session_state session_get_current_state(const struct session *sess)
+{
+ return sess->sess_state;
+}
+
+void dummy_set_direction(int dir)
+{
+ direction = dir;
+}
+
+struct logger *stellar_get_logger(struct stellar *st)
+{
+ UNUSED(st);
+
+ return NULL;
+}
+
+enum flow_type session_get_flow_type(const struct session *sess)
+{
+ UNUSED(sess);
+
+ return direction;
+}
+
+const struct packet *session_get0_current_packet(const struct session *sess)
+{
+ UNUSED(sess);
+
+ return NULL;
+}
+
+const char *packet_get_payload(const struct packet *pkt)
+{
+ UNUSED(pkt);
+
+ return NULL;
+}
+
+uint16_t packet_get_payload_len(const struct packet *pkt)
+{
+ UNUSED(pkt);
+
+ return 0;
+}
+
+int session_mq_ignore_message(struct session *sess, int topic_id, int plugin_id)
+{
+ UNUSED(sess);
+ UNUSED(topic_id);
+ UNUSED(plugin_id);
+
+ return 0;
+}
+
+int session_exdata_set(struct session *sess, int idx, void *ex_ptr)
+{
+ UNUSED(sess);
+ UNUSED(idx);
+ UNUSED(ex_ptr);
+
+ return 0;
+}
+
+void *session_exdata_get(struct session *sess, int idx)
+{
+ UNUSED(sess);
+ UNUSED(idx);
+
+ return NULL;
+}
+
+void stellar_session_plugin_dettach_current_session(struct session *sess)
+{
+ UNUSED(sess);
+}
+
+int session_mq_publish_message(struct session *sess, int topic_id, void *msg)
+{
+ UNUSED(sess);
+ UNUSED(topic_id);
+ UNUSED(msg);
+
+ return 0;
+}
+
+const char *session_get0_readable_addr(const struct session *sess)
+{
+ UNUSED(sess);
+
+ return NULL;
+}
+
+int stellar_exdata_new_index(struct stellar *st, const char *name, stellar_exdata_free *free_func,void *arg)
+{
+ UNUSED(st);
+ UNUSED(name);
+ UNUSED(free_func);
+ UNUSED(arg);
+
+ return 0;
+}
+
+int stellar_mq_create_topic(struct stellar *st, const char *topic_name, stellar_msg_free_cb_func *msg_free_cb, void *msg_free_arg)
+{
+ UNUSED(st);
+ UNUSED(topic_name);
+ UNUSED(msg_free_cb);
+ UNUSED(msg_free_arg);
+
+ return 0;
+}
+
+int stellar_mq_get_topic_id(struct stellar *st, const char *topic_name)
+{
+ UNUSED(st);
+ UNUSED(topic_name);
+
+ return 0;
+}
+
+int stellar_session_mq_subscribe(struct stellar *st, int topic_id, on_session_msg_cb_func *plugin_on_msg_cb, int plugin_id)
+{
+ UNUSED(st);
+ UNUSED(topic_id);
+ UNUSED(plugin_on_msg_cb);
+ UNUSED(plugin_id);
+
+ return 0;
+}
+
+int stellar_session_plugin_register(struct stellar *st, session_ctx_new_func session_ctx_new, session_ctx_free_func session_ctx_free, void *plugin_env)
+{
+ UNUSED(st);
+ UNUSED(session_ctx_new);
+ UNUSED(session_ctx_free);
+ UNUSED(plugin_env);
+
+ return 0;
+} \ No newline at end of file
diff --git a/test/decoders/socks/dummy.h b/test/decoders/socks/dummy.h
new file mode 100644
index 0000000..48f801e
--- /dev/null
+++ b/test/decoders/socks/dummy.h
@@ -0,0 +1,14 @@
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+
+struct session //stub just for test
+{
+ enum session_state sess_state;
+ int payload_len;
+ char *payload;
+};
+
+void session_set_current_state(struct session *sess, enum session_state sess_state);
+void dummy_set_direction(int dir); \ No newline at end of file
diff --git a/test/decoders/socks/pcap/result.json b/test/decoders/socks/pcap/result.json
new file mode 100644
index 0000000..084d449
--- /dev/null
+++ b/test/decoders/socks/pcap/result.json
@@ -0,0 +1,7 @@
+[{
+ "socks_info": {
+ "version": "SOCKS4",
+ "dst_addr": "127.0.0.1",
+ "dst_port": 8888
+ }
+}] \ No newline at end of file
diff --git a/test/decoders/socks/pcap/socks4.pcap b/test/decoders/socks/pcap/socks4.pcap
new file mode 100644
index 0000000..9091236
--- /dev/null
+++ b/test/decoders/socks/pcap/socks4.pcap
Binary files differ
diff --git a/test/decoders/socks/pcap/socks4_nest_socks4a.pcap b/test/decoders/socks/pcap/socks4_nest_socks4a.pcap
new file mode 100644
index 0000000..b5bcc9f
--- /dev/null
+++ b/test/decoders/socks/pcap/socks4_nest_socks4a.pcap
Binary files differ
diff --git a/test/decoders/socks/pcap/socks4a_domain.pcap b/test/decoders/socks/pcap/socks4a_domain.pcap
new file mode 100644
index 0000000..14fbf10
--- /dev/null
+++ b/test/decoders/socks/pcap/socks4a_domain.pcap
Binary files differ
diff --git a/test/decoders/socks/pcap/socks5_auth_failed.pcap b/test/decoders/socks/pcap/socks5_auth_failed.pcap
new file mode 100644
index 0000000..1c75c66
--- /dev/null
+++ b/test/decoders/socks/pcap/socks5_auth_failed.pcap
Binary files differ
diff --git a/test/decoders/socks/pcap/socks5_auth_success.pcap b/test/decoders/socks/pcap/socks5_auth_success.pcap
new file mode 100644
index 0000000..4c4b9fd
--- /dev/null
+++ b/test/decoders/socks/pcap/socks5_auth_success.pcap
Binary files differ
diff --git a/test/decoders/socks/pcap/socks5_no_auth.pcap b/test/decoders/socks/pcap/socks5_no_auth.pcap
new file mode 100644
index 0000000..a00100f
--- /dev/null
+++ b/test/decoders/socks/pcap/socks5_no_auth.pcap
Binary files differ
diff --git a/test/decoders/socks/plugin_test_main.cpp b/test/decoders/socks/plugin_test_main.cpp
new file mode 100644
index 0000000..8f3d996
--- /dev/null
+++ b/test/decoders/socks/plugin_test_main.cpp
@@ -0,0 +1,135 @@
+#include "cJSON.h"
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "stellar/stellar.h"
+#ifdef __cplusplus
+}
+#endif
+
+// #define IGNORE_PRINTF
+#ifdef IGNORE_PRINTF
+#define printf(fmt, ...) (0)
+#endif
+static cJSON *g_test_result_root = NULL;
+static cJSON *g_load_result_root = NULL;
+static const char *result_json_path = NULL;
+
+extern "C" int commit_test_result_json(cJSON *node, const char *name)
+{
+ (void)name;
+ if (g_test_result_root)
+ {
+ // cJSON_AddItemToObject(g_test_result_root, name, node);
+ // cJSON_AddStringToObject(node, "name", name);
+ cJSON_AddItemToArray(g_test_result_root, node);
+ return 0;
+ }
+ return -1;
+}
+
+static cJSON *load_result_from_jsonfile(const char *json_path)
+{
+ if (json_path == NULL)
+ return NULL;
+
+ long file_len = 0;
+ char *file_content = NULL;
+ FILE *fp = NULL;
+
+ fp = fopen(json_path, "r+");
+ if (NULL == fp)
+ {
+ return NULL;
+ }
+ fseek(fp, 0, SEEK_END);
+ file_len = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ if (file_len == 0)
+ {
+ fclose(fp);
+ return NULL;
+ }
+ file_content = (char *)malloc(file_len + 1);
+ fread(file_content, file_len, 1, fp);
+ file_content[file_len] = '\0';
+ cJSON *load = cJSON_Parse(file_content);
+ free(file_content);
+ fclose(fp);
+
+ return load;
+}
+
+TEST(PROTOCOL, compare_result_json)
+{
+ EXPECT_EQ(cJSON_GetArraySize(g_test_result_root), cJSON_GetArraySize(g_load_result_root));
+ int ret = cJSON_Compare(g_test_result_root, g_load_result_root, 0);
+ EXPECT_EQ(1, ret);
+
+ if (ret != 1)
+ {
+ char *load_json_str = cJSON_Print(g_load_result_root);
+ printf("LOAD Raw:\n%s\n", load_json_str);
+ free(load_json_str);
+ char *result_json_str = cJSON_Print(g_test_result_root);
+ printf("TEST Raw:\n%s\n", result_json_str);
+ free(result_json_str);
+
+ cJSON *t_load = g_load_result_root->child, *t_test = g_test_result_root->child;
+ while (t_load != NULL)
+ {
+ ret = cJSON_Compare(t_load, t_test, 0);
+ if (ret != 1)
+ {
+ load_json_str = cJSON_Print(t_load);
+ printf("LOAD Diff:\n%s\n", load_json_str);
+ free(load_json_str);
+ result_json_str = cJSON_Print(t_test);
+ printf("TEST Diff:\n%s\n", result_json_str);
+ free(result_json_str);
+ goto fail;
+ }
+ t_load = t_load->next;
+ t_test = t_test->next;
+ }
+ }
+ cJSON_Delete(g_load_result_root);
+ cJSON_Delete(g_test_result_root);
+ return;
+fail:
+ cJSON_Delete(g_load_result_root);
+ cJSON_Delete(g_test_result_root);
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ if (argc < 2)
+ {
+ printf("Usage: %s <result_json_path>\n", argv[0]);
+ result_json_path = NULL;
+ }
+ else
+ {
+ result_json_path = argv[1];
+ g_test_result_root = cJSON_CreateArray();
+ g_load_result_root = load_result_from_jsonfile(result_json_path);
+ assert(g_load_result_root != NULL && g_test_result_root != NULL);
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ struct stellar *st = stellar_new("./conf/stellar.toml", "./plugin/spec.toml", "./conf/log.toml");
+ stellar_run(st);
+ if (result_json_path != NULL)
+ {
+ ret = RUN_ALL_TESTS();
+ }
+ stellar_free(st);
+ return ret;
+}
diff --git a/test/decoders/socks/socks_decoder_pcap_gtest.cpp b/test/decoders/socks/socks_decoder_pcap_gtest.cpp
new file mode 100644
index 0000000..b1cde22
--- /dev/null
+++ b/test/decoders/socks/socks_decoder_pcap_gtest.cpp
@@ -0,0 +1,246 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/ether.h>
+#include <pcap/sll.h>
+#include <pcap/pcap.h>
+#include "gtest/gtest.h"
+
+#include "socks_decoder_internal.h"
+extern "C" {
+#include "stellar/session.h"
+#include "dummy.h"
+}
+
+#define LINUX_COOKED_CAPTURE 20
+
+extern int socks_process(struct socks_decoder_info *socks_decoder_info, struct session *sess, struct socks_tunnel_stream *stream, const char *payload, size_t payload_len);
+struct pcap_loop_arg
+{
+ pcap_t *pcap_handle;
+ struct socks_tunnel_stream *stream;
+};
+
+struct socks_decoder_info *socks_decoder_info = NULL;
+
+static void pcap_handle_cb(pcap_loop_arg *userarg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
+{
+ struct pcap_loop_arg *arg = (struct pcap_loop_arg *)userarg;
+ struct session sess;
+ int payload_len = 0;
+ char *payload = NULL;
+ unsigned short sport, dport;
+ unsigned short eth_proto_type;
+ unsigned char *ip_header;
+
+ memset(&sess, 0, sizeof(sess));
+
+ int data_link_type = pcap_datalink(arg->pcap_handle);
+ switch (data_link_type) {
+ case DLT_EN10MB:
+ eth_proto_type = ntohs(*(unsigned short *)(packet + 12));
+ ip_header = (unsigned char *)(packet + sizeof(struct ethhdr));
+ break;
+ case 276://DLT_LINUX_SLL2
+ eth_proto_type = ntohs(*(unsigned short *)packet);
+ ip_header = (unsigned char *)(packet + LINUX_COOKED_CAPTURE);
+ break;
+ default:
+ return;
+ }
+
+ if (eth_proto_type == ETH_P_IP) {
+ int l4_proto = *(unsigned char *)(ip_header + 9);
+ if (l4_proto == IPPROTO_TCP) {
+ int ip_total_len = ntohs(*(unsigned short *)(ip_header + 2));
+ int ip_header_len = (*(unsigned char *)ip_header & 0x0f) * 4;
+ int tcp_header_len = 4 * ((*(unsigned char *)(ip_header + sizeof(iphdr) + 12) & 0xf0) >> 4);
+ payload_len = ip_total_len - ip_header_len - tcp_header_len;
+ payload = (char *)ip_header + ip_header_len + tcp_header_len;
+ } else {
+ return;
+ }
+
+ sport = ntohs(*(unsigned short *)(ip_header + sizeof(iphdr) + 0));
+ dport = ntohs(*(unsigned short *)(ip_header + sizeof(iphdr) + 2));
+
+ } else if (eth_proto_type == ETH_P_IPV6) {
+ int l4_proto = *(unsigned char *)(ip_header + 6);
+ if (l4_proto == IPPROTO_TCP) {
+ int tcp_header_len = 4 * ((*(unsigned char *)(ip_header + sizeof(struct ip6_hdr) + 12) & 0xf0) >> 4);
+ payload_len = pkthdr->caplen - (ip_header - packet) - sizeof(struct ip6_hdr) - tcp_header_len;
+ payload = (char *)ip_header + sizeof(struct ip6_hdr) + tcp_header_len;
+ } else {
+ return;
+ }
+
+ sport = ntohs(*(unsigned short *)(ip_header + sizeof(struct ip6_hdr) + 0));
+ dport = ntohs(*(unsigned short *)(ip_header + sizeof(struct ip6_hdr) + 2));
+ } else {
+ return;
+ }
+
+ session_set_current_state(&sess, SESSION_STATE_ACTIVE);
+
+ if (sport > dport)
+ {
+ dummy_set_direction(FLOW_TYPE_C2S);
+ } else {
+ dummy_set_direction(FLOW_TYPE_S2C);
+ }
+ socks_process(socks_decoder_info, &sess, arg->stream, payload, payload_len);
+
+}
+
+TEST(socks_decoder, socks4)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct socks_tunnel_stream *stream = (struct socks_tunnel_stream *)calloc(1, sizeof(struct socks_tunnel_stream));
+ pcap_t * handle = pcap_open_offline("pcap/socks4.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.stream = stream;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ EXPECT_EQ(stream->info.version, SOCKS_VERSION_4);
+ EXPECT_EQ(stream->info.dst_addr.type, SOCKS_ADDR_IPV4);
+ EXPECT_EQ(stream->info.dst_addr.ipv4, inet_addr("93.184.216.119"));
+ EXPECT_EQ(stream->info.dst_addr.port, htons(80));
+ EXPECT_EQ(stream->info.user_name.iov_len, 0);
+ EXPECT_EQ(stream->info.password.iov_len, 0);
+ EXPECT_EQ(stream->client_state, SS_END);
+ EXPECT_EQ(stream->server_state, SS_END);
+
+ pcap_close(handle);
+ free(stream);
+}
+
+TEST(socks_decoder, socks4a_domain)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct socks_tunnel_stream *stream = (struct socks_tunnel_stream *)calloc(1, sizeof(struct socks_tunnel_stream));
+ pcap_t * handle = pcap_open_offline("pcap/socks4a_domain.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.stream = stream;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ EXPECT_EQ(stream->info.version, SOCKS_VERSION_4);
+ EXPECT_EQ(stream->info.dst_addr.type, SOCKS_ADDR_FQDN);
+ EXPECT_EQ(stream->info.dst_addr.fqdn.iov_len, strlen("www.example.com"));
+ EXPECT_STREQ("www.example.com", (char *)stream->info.dst_addr.fqdn.iov_base);
+ EXPECT_EQ(stream->info.dst_addr.port, htons(80));
+ EXPECT_EQ(stream->info.user_name.iov_len, 0);
+ EXPECT_EQ(stream->info.password.iov_len, 0);
+ EXPECT_EQ(stream->client_state, SS_END);
+ EXPECT_EQ(stream->server_state, SS_END);
+
+ pcap_close(handle);
+ free(stream->info.dst_addr.fqdn.iov_base);
+ free(stream);
+}
+
+TEST(socks_decoder, socks5_no_auth)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct socks_tunnel_stream *stream = (struct socks_tunnel_stream *)calloc(1, sizeof(struct socks_tunnel_stream));
+ pcap_t * handle = pcap_open_offline("pcap/socks5_no_auth.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.stream = stream;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ EXPECT_EQ(stream->info.version, SOCKS_VERSION_5);
+ EXPECT_EQ(stream->info.dst_addr.type, SOCKS_ADDR_IPV4);
+ EXPECT_EQ(stream->info.dst_addr.ipv4, inet_addr("93.184.216.119"));
+ EXPECT_EQ(stream->info.dst_addr.port, htons(80));
+ EXPECT_EQ(stream->info.user_name.iov_len, 0);
+ EXPECT_EQ(stream->info.password.iov_len, 0);
+ EXPECT_EQ(stream->client_state, SS_END);
+ EXPECT_EQ(stream->server_state, SS_END);
+
+ pcap_close(handle);
+ free(stream);
+}
+
+TEST(socks_decoder, socks5_auth_success)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct socks_tunnel_stream *stream = (struct socks_tunnel_stream *)calloc(1, sizeof(struct socks_tunnel_stream));
+ pcap_t * handle = pcap_open_offline("pcap/socks5_auth_success.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.stream = stream;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ EXPECT_EQ(stream->info.version, SOCKS_VERSION_5);
+ EXPECT_EQ(stream->info.dst_addr.type, SOCKS_ADDR_IPV4);
+ EXPECT_EQ(stream->info.dst_addr.ipv4, inet_addr("93.184.216.119"));
+ EXPECT_EQ(stream->info.dst_addr.port, htons(80));
+ EXPECT_EQ(stream->info.user_name.iov_len, strlen("testuser"));
+ EXPECT_STREQ("testuser", (char *)stream->info.user_name.iov_base);
+ EXPECT_EQ(stream->info.password.iov_len, strlen("testuser"));
+ EXPECT_STREQ("testuser", (char *)stream->info.password.iov_base);
+ EXPECT_EQ(stream->client_state, SS_END);
+ EXPECT_EQ(stream->server_state, SS_END);
+
+ pcap_close(handle);
+ free(stream->info.user_name.iov_base);
+ free(stream->info.password.iov_base);
+ free(stream);
+}
+
+TEST(socks_decoder, socks5_auth_fail)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ struct socks_tunnel_stream *stream = (struct socks_tunnel_stream *)calloc(1, sizeof(struct socks_tunnel_stream));
+ pcap_t * handle = pcap_open_offline("pcap/socks5_auth_failed.pcap", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.stream = stream;
+ arg.pcap_handle = handle;
+
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ EXPECT_EQ(stream->client_state, SS_SUB);
+ EXPECT_EQ(stream->server_state, SS_FAILED);
+
+ pcap_close(handle);
+ free(stream->info.user_name.iov_base);
+ free(stream->info.password.iov_base);
+ free(stream);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+
+ socks_decoder_info = (struct socks_decoder_info *)calloc(1, sizeof(struct socks_decoder_info));
+
+ int result = RUN_ALL_TESTS();
+
+ free(socks_decoder_info);
+
+ return result;
+} \ No newline at end of file
diff --git a/test/decoders/socks/socks_decoder_test_plugin.cpp b/test/decoders/socks/socks_decoder_test_plugin.cpp
new file mode 100644
index 0000000..7edcf8d
--- /dev/null
+++ b/test/decoders/socks/socks_decoder_test_plugin.cpp
@@ -0,0 +1,88 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+extern "C" {
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+}
+
+#include "cJSON.h"
+#include "socks_decoder.h"
+
+extern "C" int commit_test_result_json(cJSON *node, const char *name);
+#define unused(x) ((void)(x))
+
+
+int g_test_socks_decoder_plugin_id = 0;
+int g_socks_message_topic_id = 0;
+
+void *ctx_new(struct session *session, void *plugin_env)
+{
+ unused(plugin_env);
+ unused(session);
+
+ cJSON *root = cJSON_CreateObject();
+ return (void *)root;
+}
+
+void ctx_free(struct session *sess, void *session_ctx, void *plugin_env)
+{
+ unused(sess);
+ unused(plugin_env);
+
+ commit_test_result_json((cJSON *)session_ctx, "socks_decoder_test");
+}
+
+static void append_json(cJSON *root, struct socks_info *info)
+{
+ cJSON *json_stream = cJSON_CreateObject();
+
+ cJSON_AddStringToObject(json_stream, "version", info->version == SOCKS_VERSION_4 ? "SOCKS4" : "SOCKS5");
+
+ char ip_str[INET6_ADDRSTRLEN] = {0};
+ if (info->dst_addr.type == SOCKS_ADDR_IPV4) {
+ inet_ntop(AF_INET, &info->dst_addr.ipv4, ip_str, INET_ADDRSTRLEN);
+ } else if (info->dst_addr.type == SOCKS_ADDR_IPV6) {
+ inet_ntop(AF_INET6, info->dst_addr.ipv6, ip_str, INET6_ADDRSTRLEN);
+ } else {
+ memcpy(ip_str, info->dst_addr.fqdn.iov_base, info->dst_addr.fqdn.iov_len);
+ }
+ cJSON_AddStringToObject(json_stream, "dst_addr", ip_str);
+ cJSON_AddNumberToObject(json_stream, "dst_port", ntohs(info->dst_addr.port));
+ cJSON_AddStringToObject(json_stream, "user_name", (char *)info->user_name.iov_base);
+ cJSON_AddStringToObject(json_stream, "password", (char *)info->password.iov_base);
+
+ cJSON_AddItemToObject(root, "socks_info", json_stream);
+}
+
+void test_socks_decoder_on_message(struct session *session, int topic_id, const void *msg, void *per_session_ctx, void *plugin_env)
+{
+ unused(plugin_env);
+ unused(session);
+ unused(topic_id);
+
+ struct socks_info *info = (struct socks_info *)msg;
+ cJSON *json_root = (cJSON *)per_session_ctx;
+
+ append_json(json_root, info);
+}
+
+extern "C" void *SOCKS_DECODER_TEST_PLUG_INIT(struct stellar *st)
+{
+ g_test_socks_decoder_plugin_id = stellar_session_plugin_register(st, ctx_new, ctx_free, NULL);
+ g_socks_message_topic_id = stellar_mq_get_topic_id(st, SOCKS_MESSAGE_TOPIC);
+
+ stellar_session_mq_subscribe(st, g_socks_message_topic_id, test_socks_decoder_on_message, g_test_socks_decoder_plugin_id);
+
+
+ return NULL;
+}
+
+extern "C" void SOCKS_DECODER_TEST_PLUG_DESTROY(void *plugin_env)
+{
+ unused(plugin_env);
+} \ No newline at end of file
diff --git a/test/decoders/socks/test_based_on_stellar/CMakeLists.txt b/test/decoders/socks/test_based_on_stellar/CMakeLists.txt
new file mode 100644
index 0000000..2a3bc25
--- /dev/null
+++ b/test/decoders/socks/test_based_on_stellar/CMakeLists.txt
@@ -0,0 +1,37 @@
+set(DECODER_NAME socks)
+
+set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/test/decoders/socks)
+set(SAPP_DEVEL_DIR ${TEST_RUN_DIR}/lib)
+set(TEST_MAIN socks_test_main)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_SOURCE_DIR}/test)
+include_directories(/usr/local/include/cjson)
+include_directories(/opt/tsg/framework/include/stellar)
+include_directories(/opt/MESA/include/MESA)
+include_directories(/opt/tsg/stellar/include/)
+
+#various ways to add -rdynamic for centos7, centos8, and different cmake version
+add_definitions(-rdynamic)
+link_directories(${SAPP_DEVEL_DIR})
+
+# assemble test env
+add_test(NAME SOCKS_MKDIR_METRIC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/metrics; mkdir -p ${TEST_RUN_DIR}/plugin; mkdir -p ${TEST_RUN_DIR}/log; mkdir -p ${TEST_RUN_DIR}/pcap")
+add_test(NAME SOCKS_COPY_SPEC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/plugin/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/spec.toml ${TEST_RUN_DIR}/plugin/spec.toml")
+add_test(NAME SOCKS_COPY_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/conf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/stellar.toml ${TEST_RUN_DIR}/conf/stellar.toml")
+add_test(NAME SOCKS_COPY_LOG_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/conf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/log.toml ${TEST_RUN_DIR}/conf/log.toml")
+
+# update plugin to be tested
+add_test(NAME SOCKS_CP_DECODER_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/decoders/socks/socks_dyn.so ${TEST_RUN_DIR}/plugin/${DECODER_NAME}.so")
+add_test(NAME SOCKS_CP_DECODER_GTEST_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/test/decoders/socks/${DECODER_NAME}_test.so ${TEST_RUN_DIR}/plugin/${DECODER_NAME}_test.so")
+
+set_tests_properties(SOCKS_MKDIR_METRIC SOCKS_COPY_SPEC
+ SOCKS_COPY_CONF SOCKS_COPY_LOG_CONF
+ SOCKS_CP_DECODER_SO SOCKS_CP_DECODER_GTEST_SO
+ PROPERTIES FIXTURES_SETUP TestFixture)
+
+set(TEST_PCAP_DIR ${PROJECT_SOURCE_DIR}/test/decoders/socks/pcap)
+
+# run tests
+add_test(NAME SOCKS_DECODER_PLUGIN_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/socks4_nest_socks4a.pcap ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_PCAP_DIR}/result.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+
diff --git a/test/decoders/socks/test_based_on_stellar/env/log.toml b/test/decoders/socks/test_based_on_stellar/env/log.toml
new file mode 100644
index 0000000..a51abd8
--- /dev/null
+++ b/test/decoders/socks/test_based_on_stellar/env/log.toml
@@ -0,0 +1,4 @@
+[log]
+output = "stderr" # stderr, file
+file = "log/stellar.log"
+level = "ERROR" # TRACE, DEBUG, INFO, WARN, ERROR, FATAL
diff --git a/test/decoders/socks/test_based_on_stellar/env/spec.toml b/test/decoders/socks/test_based_on_stellar/env/spec.toml
new file mode 100644
index 0000000..71e58b4
--- /dev/null
+++ b/test/decoders/socks/test_based_on_stellar/env/spec.toml
@@ -0,0 +1,9 @@
+[[plugin]]
+path = "./plugin/socks.so"
+init = "socks_decoder_init"
+exit = "socks_decoder_exit"
+
+[[plugin]]
+path = "./plugin/socks_test.so"
+init = "SOCKS_DECODER_TEST_PLUG_INIT"
+exit = "SOCKS_DECODER_TEST_PLUG_DESTROY"
diff --git a/test/decoders/socks/test_based_on_stellar/env/stellar.toml b/test/decoders/socks/test_based_on_stellar/env/stellar.toml
new file mode 100644
index 0000000..308c884
--- /dev/null
+++ b/test/decoders/socks/test_based_on_stellar/env/stellar.toml
@@ -0,0 +1,64 @@
+[instance]
+ id = 1 # range: [0, 4095] (20 bit)
+
+[packet_io]
+ mode = "pcapfile" # pcapfile, pcaplist, marsio
+ app_symbol = "stellar"
+ dev_symbol = "nf_0_fw"
+ pcap_path = "./pcap/test.pcap"
+ nr_worker_thread = 1 # range: [1, 256]
+ cpu_mask = [5, 6, 7, 8, 9, 10, 11, 12]
+ idle_yield_interval_ms = 90 # range: [0, 60000] (ms)
+
+[ip_reassembly]
+ enable = 1
+ bucket_entries = 32 # range: [1, 4294967295] (must be power of 2)
+ bucket_num = 1024 # range: [1, 4294967295]
+
+ ip_frag_timeout_ms = 1000 # range: [1, 60000] (ms)
+ ip_frag_expire_polling_interval_ms = 0 # range: [0, 60000] (ms)
+ ip_frag_expire_polling_limit = 1024 # range: [1, 1024]
+
+[session_manager]
+ tcp_session_max = 500
+ udp_session_max = 500
+
+ evict_old_on_tcp_table_limit = 1 # range: [0, 1]
+ evict_old_on_udp_table_limit = 1 # range: [0, 1]
+
+ expire_period_ms = 0 # range: [0, 60000] (ms)
+ expire_batch_max = 1024 # range: [1, 1024]
+
+ [session_manager.tcp_timeout_ms]
+ init = 500 # range: [1, 60000] (ms)
+ handshake = 500 # range: [1, 60000] (ms)
+ data = 500 # range: [1, 15999999000] (ms)
+ half_closed = 500 # range: [1, 604800000] (ms)
+ time_wait = 500 # range: [1, 600000] (ms)
+ discard_default = 1000 # range: [1, 15999999000] (ms)
+ unverified_rst = 500 # range: [1, 600000] (ms)
+
+ [session_manager.udp_timeout_ms]
+ data = 500 # range: [1, 15999999000] (ms)
+ discard_default = 500 # range: [1, 15999999000] (ms)
+
+ [session_manager.duplicated_packet_bloom_filter]
+ enable = 0
+ capacity = 1000000 # range: [1, 4294967295]
+ time_window_ms = 10000 # range: [1, 60000] (ms)
+ error_rate = 0.00001 # range: [0.0, 1.0]
+
+ [session_manager.evicted_session_bloom_filter]
+ enable = 0 # range: [0, 1]
+ capacity = 1000000 # range: [1, 4294967295]
+ time_window_ms = 10000 # range: [1, 60000] (ms)
+ error_rate = 0.00001 # range: [0.0, 1.0]
+
+ [session_manager.tcp_reassembly]
+ enable = 1 # range: [0, 1]
+ timeout_ms = 100 # range: [1, 60000] (ms)
+ buffered_segments_max = 256 # range: [2, 4096] per flow
+
+[stat]
+ merge_interval_ms = 500 # range: [0, 60000] (ms)
+ output_interval_ms = 1000 # range: [0, 60000] (ms)
diff --git a/test/decoders/stratum/CMakeLists.txt b/test/decoders/stratum/CMakeLists.txt
new file mode 100644
index 0000000..683efee
--- /dev/null
+++ b/test/decoders/stratum/CMakeLists.txt
@@ -0,0 +1,41 @@
+set(DECODER_NAME stratum)
+
+add_library(${DECODER_NAME}_test SHARED stratum_test_plugin.cpp)
+add_dependencies(${DECODER_NAME}_test ${DECODER_NAME})
+set_target_properties(${DECODER_NAME}_test PROPERTIES PREFIX "")
+
+set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/testing)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_SOURCE_DIR}/src)
+include_directories(${CMAKE_SOURCE_DIR}/deps/toml)
+include_directories(${CMAKE_BINARY_DIR}/vendors/cjson/src/cjson/include)
+include_directories(${PROJECT_SOURCE_DIR}/decoders/stratum)
+include_directories(${PROJECT_SOURCE_DIR}/include/stellar)
+
+add_executable(gtest_pcap_stratum stratum_decoder_pcap_gtest.cpp dummy.c ${PROJECT_SOURCE_DIR}/decoders/stratum/stratum_decoder.cpp)
+target_link_libraries(gtest_pcap_stratum gtest pcap cjson-static logger)
+
+add_executable(stratum_test_main plugin_test_main.cpp)
+set_target_properties(stratum_test_main
+ PROPERTIES
+ LINK_OPTIONS
+ "-rdynamic"
+ )
+set_target_properties(stratum_test_main
+ PROPERTIES
+ LINK_FLAGS
+ "-rdynamic"
+ )
+set(LINK_FLAGS "-rdynamic")
+#target_link_options(stratum_test_main PRIVATE -Wl,--whole-archive logger -Wl,--no-whole-archive)
+set(CMAKE_VERBOSE_MAKEFILE ON)
+target_link_libraries(stratum_test_main gtest cjson-static logger stellar_lib)
+
+add_subdirectory(test_based_on_stellar)
+
+#copy pcap file folder to build directory
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/pcap DESTINATION ${CMAKE_BINARY_DIR}/test/decoders/stratum)
+
+include(GoogleTest)
+gtest_discover_tests(gtest_pcap_stratum)
diff --git a/test/decoders/stratum/dummy.c b/test/decoders/stratum/dummy.c
new file mode 100644
index 0000000..0290c59
--- /dev/null
+++ b/test/decoders/stratum/dummy.c
@@ -0,0 +1,86 @@
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+
+#define UNUSED(x) (void)(x)
+
+void stellar_session_plugin_dettach_current_session(struct session *sess)
+{
+ UNUSED(sess);
+}
+
+const struct packet *session_get0_current_packet(const struct session *sess)
+{
+ UNUSED(sess);
+
+ return NULL;
+}
+
+const char *packet_get_payload(const struct packet *pkt)
+{
+ UNUSED(pkt);
+
+ return NULL;
+}
+
+uint16_t packet_get_payload_len(const struct packet *pkt)
+{
+ UNUSED(pkt);
+
+ return 0;
+}
+
+struct logger *stellar_get_logger(struct stellar *st)
+{
+ UNUSED(st);
+
+ return NULL;
+}
+
+int session_mq_publish_message(struct session *sess, int topic_id, void *msg)
+{
+ UNUSED(sess);
+ UNUSED(topic_id);
+ UNUSED(msg);
+
+ return 0;
+}
+
+int stellar_mq_create_topic(struct stellar *st, const char *topic_name, stellar_msg_free_cb_func *msg_free_cb, void *msg_free_arg)
+{
+ UNUSED(st);
+ UNUSED(topic_name);
+ UNUSED(msg_free_cb);
+ UNUSED(msg_free_arg);
+
+ return 0;
+}
+
+int stellar_mq_get_topic_id(struct stellar *st, const char *topic_name)
+{
+ UNUSED(st);
+ UNUSED(topic_name);
+
+ return 0;
+}
+
+int stellar_session_mq_subscribe(struct stellar *st, int topic_id, on_session_msg_cb_func *plugin_on_msg_cb, int plugin_id)
+{
+ UNUSED(st);
+ UNUSED(topic_id);
+ UNUSED(plugin_on_msg_cb);
+ UNUSED(plugin_id);
+
+ return 0;
+}
+
+int stellar_session_plugin_register(struct stellar *st, session_ctx_new_func session_ctx_new, session_ctx_free_func session_ctx_free, void *plugin_env)
+{
+ UNUSED(st);
+ UNUSED(session_ctx_new);
+ UNUSED(session_ctx_free);
+ UNUSED(plugin_env);
+
+ return 0;
+} \ No newline at end of file
diff --git a/test/decoders/stratum/pcap/01-bch_f2pool_cpuminer_1.pcapng b/test/decoders/stratum/pcap/01-bch_f2pool_cpuminer_1.pcapng
new file mode 100644
index 0000000..725aed7
--- /dev/null
+++ b/test/decoders/stratum/pcap/01-bch_f2pool_cpuminer_1.pcapng
Binary files differ
diff --git a/test/decoders/stratum/pcap/02-eth-antpool.pcapng b/test/decoders/stratum/pcap/02-eth-antpool.pcapng
new file mode 100644
index 0000000..5902981
--- /dev/null
+++ b/test/decoders/stratum/pcap/02-eth-antpool.pcapng
Binary files differ
diff --git a/test/decoders/stratum/pcap/03-xmr_f2pool_nanominer_1.pcapng b/test/decoders/stratum/pcap/03-xmr_f2pool_nanominer_1.pcapng
new file mode 100644
index 0000000..dfb0516
--- /dev/null
+++ b/test/decoders/stratum/pcap/03-xmr_f2pool_nanominer_1.pcapng
Binary files differ
diff --git a/test/decoders/stratum/pcap/04-zec-antpool.pcapng b/test/decoders/stratum/pcap/04-zec-antpool.pcapng
new file mode 100644
index 0000000..d46ab7f
--- /dev/null
+++ b/test/decoders/stratum/pcap/04-zec-antpool.pcapng
Binary files differ
diff --git a/test/decoders/stratum/pcap/stratum_result.json b/test/decoders/stratum/pcap/stratum_result.json
new file mode 100644
index 0000000..37747ed
--- /dev/null
+++ b/test/decoders/stratum/pcap/stratum_result.json
@@ -0,0 +1,11 @@
+[
+ {
+ "stratum":
+ {
+ "Tuple4": "192.168.50.243:58748-120.26.148.222:1228-6-0",
+ "type": "OTHER",
+ "mining_program": "cpuminer/2.5.1",
+ "mining_subscribe": "{\"id\": 1, \"method\": \"mining.subscribe\", \"params\": [\"cpuminer/2.5.1\"]}\n"
+ }
+ }
+] \ No newline at end of file
diff --git a/test/decoders/stratum/plugin_test_main.cpp b/test/decoders/stratum/plugin_test_main.cpp
new file mode 100644
index 0000000..8f3d996
--- /dev/null
+++ b/test/decoders/stratum/plugin_test_main.cpp
@@ -0,0 +1,135 @@
+#include "cJSON.h"
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include "stellar/stellar.h"
+#ifdef __cplusplus
+}
+#endif
+
+// #define IGNORE_PRINTF
+#ifdef IGNORE_PRINTF
+#define printf(fmt, ...) (0)
+#endif
+static cJSON *g_test_result_root = NULL;
+static cJSON *g_load_result_root = NULL;
+static const char *result_json_path = NULL;
+
+extern "C" int commit_test_result_json(cJSON *node, const char *name)
+{
+ (void)name;
+ if (g_test_result_root)
+ {
+ // cJSON_AddItemToObject(g_test_result_root, name, node);
+ // cJSON_AddStringToObject(node, "name", name);
+ cJSON_AddItemToArray(g_test_result_root, node);
+ return 0;
+ }
+ return -1;
+}
+
+static cJSON *load_result_from_jsonfile(const char *json_path)
+{
+ if (json_path == NULL)
+ return NULL;
+
+ long file_len = 0;
+ char *file_content = NULL;
+ FILE *fp = NULL;
+
+ fp = fopen(json_path, "r+");
+ if (NULL == fp)
+ {
+ return NULL;
+ }
+ fseek(fp, 0, SEEK_END);
+ file_len = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ if (file_len == 0)
+ {
+ fclose(fp);
+ return NULL;
+ }
+ file_content = (char *)malloc(file_len + 1);
+ fread(file_content, file_len, 1, fp);
+ file_content[file_len] = '\0';
+ cJSON *load = cJSON_Parse(file_content);
+ free(file_content);
+ fclose(fp);
+
+ return load;
+}
+
+TEST(PROTOCOL, compare_result_json)
+{
+ EXPECT_EQ(cJSON_GetArraySize(g_test_result_root), cJSON_GetArraySize(g_load_result_root));
+ int ret = cJSON_Compare(g_test_result_root, g_load_result_root, 0);
+ EXPECT_EQ(1, ret);
+
+ if (ret != 1)
+ {
+ char *load_json_str = cJSON_Print(g_load_result_root);
+ printf("LOAD Raw:\n%s\n", load_json_str);
+ free(load_json_str);
+ char *result_json_str = cJSON_Print(g_test_result_root);
+ printf("TEST Raw:\n%s\n", result_json_str);
+ free(result_json_str);
+
+ cJSON *t_load = g_load_result_root->child, *t_test = g_test_result_root->child;
+ while (t_load != NULL)
+ {
+ ret = cJSON_Compare(t_load, t_test, 0);
+ if (ret != 1)
+ {
+ load_json_str = cJSON_Print(t_load);
+ printf("LOAD Diff:\n%s\n", load_json_str);
+ free(load_json_str);
+ result_json_str = cJSON_Print(t_test);
+ printf("TEST Diff:\n%s\n", result_json_str);
+ free(result_json_str);
+ goto fail;
+ }
+ t_load = t_load->next;
+ t_test = t_test->next;
+ }
+ }
+ cJSON_Delete(g_load_result_root);
+ cJSON_Delete(g_test_result_root);
+ return;
+fail:
+ cJSON_Delete(g_load_result_root);
+ cJSON_Delete(g_test_result_root);
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ if (argc < 2)
+ {
+ printf("Usage: %s <result_json_path>\n", argv[0]);
+ result_json_path = NULL;
+ }
+ else
+ {
+ result_json_path = argv[1];
+ g_test_result_root = cJSON_CreateArray();
+ g_load_result_root = load_result_from_jsonfile(result_json_path);
+ assert(g_load_result_root != NULL && g_test_result_root != NULL);
+ }
+ ::testing::InitGoogleTest(&argc, argv);
+ struct stellar *st = stellar_new("./conf/stellar.toml", "./plugin/spec.toml", "./conf/log.toml");
+ stellar_run(st);
+ if (result_json_path != NULL)
+ {
+ ret = RUN_ALL_TESTS();
+ }
+ stellar_free(st);
+ return ret;
+}
diff --git a/test/decoders/stratum/stratum_decoder_pcap_gtest.cpp b/test/decoders/stratum/stratum_decoder_pcap_gtest.cpp
new file mode 100644
index 0000000..a5c5b72
--- /dev/null
+++ b/test/decoders/stratum/stratum_decoder_pcap_gtest.cpp
@@ -0,0 +1,179 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/ether.h>
+#include <pcap/sll.h>
+#include <pcap/pcap.h>
+#include "gtest/gtest.h"
+
+#include "stellar/stratum_decoder.h"
+
+#include <stellar/session.h>
+
+
+#define LINUX_COOKED_CAPTURE 20
+
+struct pcap_loop_arg
+{
+ pcap_t *pcap_handle;
+ void *ctx;
+};
+
+extern struct stratum_field *stratum_data_process(struct stratum_decoder_info *stratum_decoder_info, const char *tcpdata, size_t datalen);
+extern void free_stratum_filed(struct stratum_field *stratum_field);
+extern "C" void *stratum_decoder_init(struct stellar *st);
+
+struct stratum_decoder_info *stratum_decoder_info = NULL;
+
+static void pcap_handle_cb(pcap_loop_arg *userarg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
+{
+ struct pcap_loop_arg *arg = (struct pcap_loop_arg *)userarg;
+ int payload_len = 0;
+ char *payload = NULL;
+ unsigned short eth_proto_type;
+ unsigned char *ip_header;
+
+ int data_link_type = pcap_datalink(arg->pcap_handle);
+ switch (data_link_type) {
+ case DLT_EN10MB:
+ eth_proto_type = ntohs(*(unsigned short *)(packet + 12));
+ ip_header = (unsigned char *)(packet + sizeof(struct ethhdr));
+ break;
+ case 276://DLT_LINUX_SLL2
+ eth_proto_type = ntohs(*(unsigned short *)packet);
+ ip_header = (unsigned char *)(packet + LINUX_COOKED_CAPTURE);
+ break;
+ default:
+ return;
+ }
+
+ if (eth_proto_type == ETH_P_IP) {
+ int l4_proto = *(unsigned char *)(ip_header + 9);
+ if (l4_proto == IPPROTO_TCP) {
+ int ip_total_len = ntohs(*(unsigned short *)(ip_header + 2));
+ int ip_header_len = (*(unsigned char *)ip_header & 0x0f) * 4;
+ int tcp_header_len = 4 * ((*(unsigned char *)(ip_header + sizeof(iphdr) + 12) & 0xf0) >> 4);
+ payload_len = ip_total_len - ip_header_len - tcp_header_len;
+ payload = (char *)ip_header + ip_header_len + tcp_header_len;
+ } else {
+ return;
+ }
+
+ } else if (eth_proto_type == ETH_P_IPV6) {
+ int l4_proto = *(unsigned char *)(ip_header + 6);
+ if (l4_proto == IPPROTO_TCP) {
+ int tcp_header_len = 4 * ((*(unsigned char *)(ip_header + sizeof(struct ip6_hdr) + 12) & 0xf0) >> 4);
+ payload_len = pkthdr->caplen - (ip_header - packet) - sizeof(struct ip6_hdr) - tcp_header_len;
+ payload = (char *)ip_header + sizeof(struct ip6_hdr) + tcp_header_len;
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ struct stratum_field *stratum = stratum_data_process(stratum_decoder_info, payload, payload_len);
+ if (stratum != NULL)
+ {
+ arg->ctx = stratum;
+ pcap_breakloop(arg->pcap_handle);
+ }
+}
+
+TEST(stratum_decoder, parse01)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ pcap_t *handle = pcap_open_offline("pcap/01-bch_f2pool_cpuminer_1.pcapng", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.pcap_handle = handle;
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct stratum_field *stratum = (struct stratum_field *)arg.ctx;
+ EXPECT_EQ(stratum->type, OTHER);
+ EXPECT_STREQ((const char *)stratum->mining_program.iov_base, "cpuminer/2.5.1");
+ EXPECT_EQ(stratum->mining_pools.iov_len, 0);
+ EXPECT_STREQ((const char *)stratum->mining_subscribe.iov_base, "{\"id\": 1, \"method\": \"mining.subscribe\", \"params\": [\"cpuminer/2.5.1\"]}\n");
+
+ pcap_close(handle);
+ free_stratum_filed(stratum);
+}
+
+TEST(stratum_decoder, parse02)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ pcap_t *handle = pcap_open_offline("pcap/02-eth-antpool.pcapng", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.pcap_handle = handle;
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct stratum_field *stratum = (struct stratum_field *)arg.ctx;
+ EXPECT_EQ(stratum->type, ETH);
+ EXPECT_STREQ((const char *)stratum->mining_program.iov_base, "lolMinerWorker");
+ EXPECT_EQ(stratum->mining_pools.iov_len, 0);
+ EXPECT_STREQ((const char *)stratum->mining_subscribe.iov_base, "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"eth_submitLogin\",\"worker\":\"lolMinerWorker\",\"params\":[\"0xa0279Ad2aA6BDb440f041Aa0947178431d9Bd253.lolMinerWorker\", \"x\"]} \n");
+
+ pcap_close(handle);
+ free_stratum_filed(stratum);
+}
+
+TEST(stratum_decoder, parse03)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ pcap_t *handle = pcap_open_offline("pcap/03-xmr_f2pool_nanominer_1.pcapng", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.pcap_handle = handle;
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct stratum_field *stratum = (struct stratum_field *)arg.ctx;
+ EXPECT_EQ(stratum->type, OTHER);
+ EXPECT_STREQ((const char *)stratum->mining_program.iov_base, "nanominer/3.3.5-cuda11");
+ EXPECT_EQ(stratum->mining_pools.iov_len, 0);
+ EXPECT_STREQ((const char *)stratum->mining_subscribe.iov_base, "{\"id\": 1, \"method\": \"login\", \"params\": { \"login\":\"yhzzhk.001.001\",\"pass\" : \"x\", \"agent\" : \"nanominer/3.3.5-cuda11\"}}\n");
+
+ pcap_close(handle);
+ free_stratum_filed(stratum);
+}
+
+TEST(stratum_decoder, parse04)
+{
+ char error[100];
+ struct pcap_loop_arg arg;
+ pcap_t *handle = pcap_open_offline("pcap/04-zec-antpool.pcapng", error);
+ ASSERT_NE(handle, nullptr);
+
+ memset(&arg, 0, sizeof(arg));
+ arg.pcap_handle = handle;
+ pcap_loop(handle, -1, (pcap_handler)pcap_handle_cb, (u_char *)&arg);
+
+ struct stratum_field *stratum = (struct stratum_field *)arg.ctx;
+ EXPECT_EQ(stratum->type, OTHER);
+ EXPECT_STREQ((const char *)stratum->mining_program.iov_base, "nheqminer/0.5c");
+ EXPECT_STREQ((const char*)stratum->mining_pools.iov_base, "stratum-zec.antpool.com");
+ EXPECT_STREQ((const char *)stratum->mining_subscribe.iov_base, "{\"id\":1,\"method\":\"mining.subscribe\",\"params\":[\"nheqminer/0.5c\", null,\"stratum-zec.antpool.com\",\"8899\"]}\n");
+
+ pcap_close(handle);
+ free_stratum_filed(stratum);
+}
+
+int main(int argc, char **argv)
+{
+ testing::InitGoogleTest(&argc, argv);
+
+ stratum_decoder_info = (struct stratum_decoder_info*)stratum_decoder_init(NULL);
+
+ int result = RUN_ALL_TESTS();
+
+ return result;
+} \ No newline at end of file
diff --git a/test/decoders/stratum/stratum_test_plugin.cpp b/test/decoders/stratum/stratum_test_plugin.cpp
new file mode 100644
index 0000000..3ec563b
--- /dev/null
+++ b/test/decoders/stratum/stratum_test_plugin.cpp
@@ -0,0 +1,72 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "cJSON.h"
+#include "stellar/stellar.h"
+#include "stellar/session.h"
+#include "stellar/stellar_mq.h"
+#include "stellar/stellar_exdata.h"
+
+#include "stratum_decoder.h"
+
+#define unused(x) ((void)(x))
+extern "C" int commit_test_result_json(cJSON *node, const char *name);
+
+int g_test_stratum_decoder_plugin_id = 0;
+int g_stratum_message_topic_id = 0;
+
+void *ctx_new(struct session *session, void *plugin_env)
+{
+ unused(plugin_env);
+ unused(session);
+
+ cJSON *root = cJSON_CreateObject();
+ return (void *)root;
+}
+
+void ctx_free(struct session *sess, void *session_ctx, void *plugin_env)
+{
+ unused(sess);
+ unused(plugin_env);
+
+ commit_test_result_json((cJSON *)session_ctx, "socks_decoder_test");
+}
+
+void test_stratum_decoder_on_message(struct session *session, int topic_id, const void *msg, void *per_session_ctx, void *plugin_env)
+{
+ unused(plugin_env);
+ unused(topic_id);
+
+ struct stratum_field *stratum = (struct stratum_field *)msg;
+ cJSON *root = (cJSON *)per_session_ctx;
+
+ cJSON *stratum_json = cJSON_CreateObject();
+ cJSON_AddItemToObject(root, "stratum", stratum_json);
+
+ cJSON_AddStringToObject(stratum_json, "Tuple4", session_get0_readable_addr(session));
+ cJSON_AddStringToObject(stratum_json, "type", stratum->type == ETH ? "ETH" : "OTHER");
+ cJSON_AddStringToObject(stratum_json, "mining_pools", (char *)stratum->mining_pools.iov_base);
+ cJSON_AddStringToObject(stratum_json, "mining_program", (char *)stratum->mining_program.iov_base);
+ cJSON_AddStringToObject(stratum_json, "mining_subscribe", (char *)stratum->mining_subscribe.iov_base);
+
+}
+
+extern "C" void *STRATUM_DECODER_TEST_PLUG_INIT(struct stellar *st)
+{
+ g_test_stratum_decoder_plugin_id = stellar_session_plugin_register(st, ctx_new, ctx_free, NULL);
+ g_stratum_message_topic_id = stellar_mq_get_topic_id(st, STRATUM_MESSAGE_TOPIC);
+
+ if (stellar_session_mq_subscribe(st, g_stratum_message_topic_id, test_stratum_decoder_on_message, g_test_stratum_decoder_plugin_id) < 0)
+ {
+ printf("subscribe topic %s failed\n", STRATUM_MESSAGE_TOPIC);
+ return NULL;
+ }
+
+ return NULL;
+}
+
+extern "C" void STRATUM_DECODER_TEST_PLUG_DESTROY(void * plugin_env)
+{
+ unused(plugin_env);
+} \ No newline at end of file
diff --git a/test/decoders/stratum/test_based_on_stellar/CMakeLists.txt b/test/decoders/stratum/test_based_on_stellar/CMakeLists.txt
new file mode 100644
index 0000000..1de14e5
--- /dev/null
+++ b/test/decoders/stratum/test_based_on_stellar/CMakeLists.txt
@@ -0,0 +1,37 @@
+set(DECODER_NAME stratum)
+
+set(TEST_RUN_DIR ${CMAKE_BINARY_DIR}/test/decoders/stratum)
+set(SAPP_DEVEL_DIR ${TEST_RUN_DIR}/lib)
+set(TEST_MAIN stratum_test_main)
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_SOURCE_DIR}/test)
+include_directories(/usr/local/include/cjson)
+include_directories(/opt/tsg/framework/include/stellar)
+include_directories(/opt/MESA/include/MESA)
+include_directories(/opt/tsg/stellar/include/)
+
+#various ways to add -rdynamic for centos7, centos8, and different cmake version
+add_definitions(-rdynamic)
+link_directories(${SAPP_DEVEL_DIR})
+
+# assemble test env
+add_test(NAME STRATUM_MKDIR_METRIC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/metrics; mkdir -p ${TEST_RUN_DIR}/plugin; mkdir -p ${TEST_RUN_DIR}/log; mkdir -p ${TEST_RUN_DIR}/pcap")
+add_test(NAME STRATUM_COPY_SPEC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/plugin/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/spec.toml ${TEST_RUN_DIR}/plugin/spec.toml")
+add_test(NAME STRATUM_COPY_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/conf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/stellar.toml ${TEST_RUN_DIR}/conf/stellar.toml")
+add_test(NAME STRATUM_COPY_LOG_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/conf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/env/log.toml ${TEST_RUN_DIR}/conf/log.toml")
+
+# update plugin to be tested
+add_test(NAME STRATUM_CP_DECODER_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/decoders/stratum/stratum_dyn.so ${TEST_RUN_DIR}/plugin/${DECODER_NAME}.so")
+add_test(NAME STRATUM_CP_DECODER_GTEST_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/test/decoders/stratum/${DECODER_NAME}_test.so ${TEST_RUN_DIR}/plugin/${DECODER_NAME}_test.so")
+
+set_tests_properties(STRATUM_MKDIR_METRIC STRATUM_COPY_SPEC
+ STRATUM_COPY_CONF STRATUM_COPY_LOG_CONF
+ STRATUM_CP_DECODER_SO STRATUM_CP_DECODER_GTEST_SO
+ PROPERTIES FIXTURES_SETUP TestFixture)
+
+set(TEST_PCAP_DIR ${PROJECT_SOURCE_DIR}/test/decoders/stratum/pcap)
+
+# run tests
+add_test(NAME STRATUM_DECODER_PLUGIN_TEST COMMAND sh -c "ln -sf ${TEST_PCAP_DIR}/01-bch_f2pool_cpuminer_1.pcapng ${TEST_RUN_DIR}/pcap/test.pcap; ./${TEST_MAIN} ${TEST_PCAP_DIR}/stratum_result.json" WORKING_DIRECTORY ${TEST_RUN_DIR})
+
diff --git a/test/decoders/stratum/test_based_on_stellar/env/log.toml b/test/decoders/stratum/test_based_on_stellar/env/log.toml
new file mode 100644
index 0000000..b5213d9
--- /dev/null
+++ b/test/decoders/stratum/test_based_on_stellar/env/log.toml
@@ -0,0 +1,4 @@
+[log]
+output = "stderr" # stderr, file
+file = "log/stellar.log"
+level = "INFO" # TRACE, DEBUG, INFO, WARN, ERROR, FATAL
diff --git a/test/decoders/stratum/test_based_on_stellar/env/spec.toml b/test/decoders/stratum/test_based_on_stellar/env/spec.toml
new file mode 100644
index 0000000..6eafab8
--- /dev/null
+++ b/test/decoders/stratum/test_based_on_stellar/env/spec.toml
@@ -0,0 +1,9 @@
+[[plugin]]
+path = "./plugin/stratum.so"
+init = "stratum_decoder_init"
+exit = "stratum_decoder_exit"
+
+[[plugin]]
+path = "./plugin/stratum_test.so"
+init = "STRATUM_DECODER_TEST_PLUG_INIT"
+exit = "STRATUM_DECODER_TEST_PLUG_DESTROY"
diff --git a/test/decoders/stratum/test_based_on_stellar/env/stellar.toml b/test/decoders/stratum/test_based_on_stellar/env/stellar.toml
new file mode 100644
index 0000000..308c884
--- /dev/null
+++ b/test/decoders/stratum/test_based_on_stellar/env/stellar.toml
@@ -0,0 +1,64 @@
+[instance]
+ id = 1 # range: [0, 4095] (20 bit)
+
+[packet_io]
+ mode = "pcapfile" # pcapfile, pcaplist, marsio
+ app_symbol = "stellar"
+ dev_symbol = "nf_0_fw"
+ pcap_path = "./pcap/test.pcap"
+ nr_worker_thread = 1 # range: [1, 256]
+ cpu_mask = [5, 6, 7, 8, 9, 10, 11, 12]
+ idle_yield_interval_ms = 90 # range: [0, 60000] (ms)
+
+[ip_reassembly]
+ enable = 1
+ bucket_entries = 32 # range: [1, 4294967295] (must be power of 2)
+ bucket_num = 1024 # range: [1, 4294967295]
+
+ ip_frag_timeout_ms = 1000 # range: [1, 60000] (ms)
+ ip_frag_expire_polling_interval_ms = 0 # range: [0, 60000] (ms)
+ ip_frag_expire_polling_limit = 1024 # range: [1, 1024]
+
+[session_manager]
+ tcp_session_max = 500
+ udp_session_max = 500
+
+ evict_old_on_tcp_table_limit = 1 # range: [0, 1]
+ evict_old_on_udp_table_limit = 1 # range: [0, 1]
+
+ expire_period_ms = 0 # range: [0, 60000] (ms)
+ expire_batch_max = 1024 # range: [1, 1024]
+
+ [session_manager.tcp_timeout_ms]
+ init = 500 # range: [1, 60000] (ms)
+ handshake = 500 # range: [1, 60000] (ms)
+ data = 500 # range: [1, 15999999000] (ms)
+ half_closed = 500 # range: [1, 604800000] (ms)
+ time_wait = 500 # range: [1, 600000] (ms)
+ discard_default = 1000 # range: [1, 15999999000] (ms)
+ unverified_rst = 500 # range: [1, 600000] (ms)
+
+ [session_manager.udp_timeout_ms]
+ data = 500 # range: [1, 15999999000] (ms)
+ discard_default = 500 # range: [1, 15999999000] (ms)
+
+ [session_manager.duplicated_packet_bloom_filter]
+ enable = 0
+ capacity = 1000000 # range: [1, 4294967295]
+ time_window_ms = 10000 # range: [1, 60000] (ms)
+ error_rate = 0.00001 # range: [0.0, 1.0]
+
+ [session_manager.evicted_session_bloom_filter]
+ enable = 0 # range: [0, 1]
+ capacity = 1000000 # range: [1, 4294967295]
+ time_window_ms = 10000 # range: [1, 60000] (ms)
+ error_rate = 0.00001 # range: [0.0, 1.0]
+
+ [session_manager.tcp_reassembly]
+ enable = 1 # range: [0, 1]
+ timeout_ms = 100 # range: [1, 60000] (ms)
+ buffered_segments_max = 256 # range: [2, 4096] per flow
+
+[stat]
+ merge_interval_ms = 500 # range: [0, 60000] (ms)
+ output_interval_ms = 1000 # range: [0, 60000] (ms)
diff --git a/vendors/CMakeLists.txt b/vendors/CMakeLists.txt
index 5ed2c1b..07d5711 100644
--- a/vendors/CMakeLists.txt
+++ b/vendors/CMakeLists.txt
@@ -97,4 +97,41 @@ add_library(libevent-static STATIC IMPORTED GLOBAL)
add_dependencies(libevent-static libevent)
set_property(TARGET libevent-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libevent.a)
set_property(TARGET libevent-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include)
-set_property(TARGET libevent-static PROPERTY INTERFACE_LINK_LIBRARIES pthread) \ No newline at end of file
+set_property(TARGET libevent-static PROPERTY INTERFACE_LINK_LIBRARIES pthread)
+
+set(VENDOR_BUILD ${CMAKE_BINARY_DIR}/vendor/vbuild)
+# colm-0.14.7
+ExternalProject_Add(colm PREFIX colm
+ URL ${CMAKE_CURRENT_SOURCE_DIR}/colm-0.14.7.tar.gz
+ CONFIGURE_COMMAND ./autogen.sh && ./configure --prefix=${VENDOR_BUILD}
+ BUILD_COMMAND make
+ INSTALL_COMMAND make install
+ BUILD_IN_SOURCE 1)
+
+# ragel-7.0.4
+ExternalProject_Add(ragel PREFIX ragel
+ URL ${CMAKE_CURRENT_SOURCE_DIR}/ragel-7.0.4.tar.gz
+ CONFIGURE_COMMAND ./autogen.sh && ./configure --prefix=${VENDOR_BUILD} --disable-manual --with-colm=${VENDOR_BUILD}
+ DEPENDS colm
+ BUILD_COMMAND make
+ INSTALL_COMMAND make install
+ BUILD_IN_SOURCE 1)
+
+# HyperScan 5.4.2
+ExternalProject_Add(hyperscan PREFIX hyperscan
+ URL ${CMAKE_CURRENT_SOURCE_DIR}/hyperscan-5.4.2.tar.gz
+ DEPENDS ragel
+ CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${VENDOR_BUILD} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_CXX_FLAGS="-fPIC")
+
+ExternalProject_Get_Property(hyperscan INSTALL_DIR)
+file(MAKE_DIRECTORY ${VENDOR_BUILD}/include)
+
+add_library(hyperscan_static STATIC IMPORTED GLOBAL)
+add_dependencies(hyperscan_static hyperscan)
+set_property(TARGET hyperscan_static PROPERTY IMPORTED_LOCATION ${VENDOR_BUILD}/lib64/libhs.a)
+set_property(TARGET hyperscan_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${VENDOR_BUILD}/include)
+
+add_library(hyperscan_runtime_static STATIC IMPORTED GLOBAL)
+add_dependencies(hyperscan_runtime_static hyperscan)
+set_property(TARGET hyperscan_runtime_static PROPERTY IMPORTED_LOCATION ${VENDOR_BUILD}/lib64/libhs_runtime.a)
+set_property(TARGET hyperscan_runtime_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${VENDOR_BUILD}/include) \ No newline at end of file
diff --git a/vendors/colm-0.14.7.tar.gz b/vendors/colm-0.14.7.tar.gz
new file mode 100644
index 0000000..a73e692
--- /dev/null
+++ b/vendors/colm-0.14.7.tar.gz
Binary files differ
diff --git a/vendors/hyperscan-5.4.2.tar.gz b/vendors/hyperscan-5.4.2.tar.gz
new file mode 100644
index 0000000..fa49221
--- /dev/null
+++ b/vendors/hyperscan-5.4.2.tar.gz
Binary files differ
diff --git a/vendors/ragel-7.0.4.tar.gz b/vendors/ragel-7.0.4.tar.gz
new file mode 100644
index 0000000..d37daf6
--- /dev/null
+++ b/vendors/ragel-7.0.4.tar.gz
Binary files differ