|
|
|
@ -5,6 +5,7 @@ |
|
|
|
#include "etcp.h" |
|
|
|
#include "etcp.h" |
|
|
|
#include "etcp_api.h" |
|
|
|
#include "etcp_api.h" |
|
|
|
#include "etcp_connections.h" |
|
|
|
#include "etcp_connections.h" |
|
|
|
|
|
|
|
#include "etcp_debug.h" |
|
|
|
#include "etcp_session.h" |
|
|
|
#include "etcp_session.h" |
|
|
|
#include "config_parser.h" |
|
|
|
#include "config_parser.h" |
|
|
|
#include "../lib/debug_config.h" |
|
|
|
#include "../lib/debug_config.h" |
|
|
|
@ -18,6 +19,76 @@ static struct UASYNC* ua; |
|
|
|
static struct frame frames[4096], saved[4]; |
|
|
|
static struct frame frames[4096], saved[4]; |
|
|
|
static unsigned head, tail, drops, reinit[2], down[2]; |
|
|
|
static unsigned head, tail, drops, reinit[2], down[2]; |
|
|
|
static int drop[2][3], blackhole, dead_primary; |
|
|
|
static int drop[2][3], blackhole, dead_primary; |
|
|
|
|
|
|
|
static char dump_message[512]; |
|
|
|
|
|
|
|
static unsigned dump_count, dump_warnings; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Перехватывает диагностический вывод без временных файлов и изменения библиотек.
|
|
|
|
|
|
|
|
static void capture_dump(int level, const char* category, const char* message) { |
|
|
|
|
|
|
|
if (strcmp(category, "dump")) return; |
|
|
|
|
|
|
|
if (level == DEBUG_LEVEL_WARN) { dump_warnings++; return; } |
|
|
|
|
|
|
|
if (level != DEBUG_LEVEL_DEBUG) return; |
|
|
|
|
|
|
|
snprintf(dump_message, sizeof(dump_message), "%s", message); |
|
|
|
|
|
|
|
dump_count++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ACK/payload должны корректно декодировать все 32 бита, включая границу знакового int.
|
|
|
|
|
|
|
|
static void debug_sequence_boundaries(void) { |
|
|
|
|
|
|
|
const uint32_t values[] = {0, 1, 0x7f000000, 0x7fffffff, 0x80000000, 0xff000000, UINT32_MAX}; |
|
|
|
|
|
|
|
struct ETCP_DGRAM* pkt = u_calloc(1, sizeof(*pkt) + ETCP_ACK_BASE_SIZE); CHECK(pkt); |
|
|
|
|
|
|
|
debug_level_t saved = g_debug_config.category_levels[DEBUG_CATEGORY_DUMP]; |
|
|
|
|
|
|
|
int console = g_debug_config.console_enabled; |
|
|
|
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_DUMP, DEBUG_LEVEL_DEBUG); |
|
|
|
|
|
|
|
debug_enable_console(0); |
|
|
|
|
|
|
|
debug_set_log_hook(capture_dump); |
|
|
|
|
|
|
|
for (unsigned i = 0; i < sizeof(values) / sizeof(values[0]); i++) { |
|
|
|
|
|
|
|
for (int ack = 0; ack < 2; ack++) { |
|
|
|
|
|
|
|
memset(pkt->data, 0, ETCP_ACK_BASE_SIZE); |
|
|
|
|
|
|
|
pkt->data[0] = ack ? ETCP_SECTION_ACK : ETCP_SECTION_PAYLOAD; |
|
|
|
|
|
|
|
pkt->data_len = ack ? ETCP_ACK_BASE_SIZE : 5; |
|
|
|
|
|
|
|
for (int j = 0; j < 4; j++) pkt->data[1 + ack + j] = (uint8_t)(values[i] >> (8 * j)); |
|
|
|
|
|
|
|
if (ack) pkt->data[6] = pkt->data[7] = 255; |
|
|
|
|
|
|
|
dump_count = 0; dump_message[0] = 0; |
|
|
|
|
|
|
|
etcp_dump_pkt_sections(pkt, NULL, 0); |
|
|
|
|
|
|
|
char expected[80]; |
|
|
|
|
|
|
|
if (ack) snprintf(expected, sizeof(expected), " ack=%u/0 dup=65535", values[i]); |
|
|
|
|
|
|
|
else snprintf(expected, sizeof(expected), " payload=0(seq=%u)", values[i]); |
|
|
|
|
|
|
|
CHECK(dump_count == 1 && strstr(dump_message, expected)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Укороченная секция не должна читать отсутствующие байты или печатать выдуманный seq.
|
|
|
|
|
|
|
|
for (int ack = 0; ack < 2; ack++) { |
|
|
|
|
|
|
|
pkt->data[0] = ack ? ETCP_SECTION_ACK : ETCP_SECTION_PAYLOAD; |
|
|
|
|
|
|
|
for (unsigned len = 0; len < (ack ? ETCP_ACK_BASE_SIZE : 5); len++) { |
|
|
|
|
|
|
|
pkt->data_len = len; dump_count = 0; dump_message[0] = 0; |
|
|
|
|
|
|
|
etcp_dump_pkt_sections(pkt, NULL, 1); |
|
|
|
|
|
|
|
CHECK(dump_count == 1 && !strstr(dump_message, ack ? " ack=" : " payload=")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Длинный пакет обязан безопасно усекать дамп, в том числе при выключенной категории.
|
|
|
|
|
|
|
|
u_free(pkt); |
|
|
|
|
|
|
|
pkt = u_calloc(1, sizeof(*pkt) + 32 * ETCP_ACK_BASE_SIZE); CHECK(pkt); |
|
|
|
|
|
|
|
for (int ack = 0; ack < 2; ack++) { |
|
|
|
|
|
|
|
unsigned width = ack ? ETCP_ACK_BASE_SIZE : 5; |
|
|
|
|
|
|
|
pkt->data_len = 32 * width; |
|
|
|
|
|
|
|
memset(pkt->data, 0, 32 * ETCP_ACK_BASE_SIZE); |
|
|
|
|
|
|
|
for (unsigned offset = 0; offset < pkt->data_len; offset += width) { |
|
|
|
|
|
|
|
pkt->data[offset] = ack ? ETCP_SECTION_ACK : ETCP_SECTION_TIMESTAMP; |
|
|
|
|
|
|
|
memset(pkt->data + offset + (ack ? 2 : 1), 255, width - (ack ? 2 : 1)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (int enabled = 0; enabled < 2; enabled++) { |
|
|
|
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_DUMP, enabled ? DEBUG_LEVEL_DEBUG : DEBUG_LEVEL_DISABLED); |
|
|
|
|
|
|
|
dump_count = dump_warnings = 0; dump_message[0] = 0; |
|
|
|
|
|
|
|
etcp_dump_pkt_sections(pkt, NULL, enabled); |
|
|
|
|
|
|
|
if (enabled) CHECK(dump_count == 1 && dump_warnings == 1 && strstr(dump_message, " [truncated]")); |
|
|
|
|
|
|
|
else CHECK(dump_count == 0 && dump_warnings == 0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
debug_set_log_hook(NULL); |
|
|
|
|
|
|
|
debug_enable_console(console); |
|
|
|
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_DUMP, saved); |
|
|
|
|
|
|
|
u_free(pkt); |
|
|
|
|
|
|
|
puts("PASS diagnostic uint32 boundaries, truncated sections and long ACK/timestamp dumps"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static ssize_t capture(socket_t fd, const void* data, size_t len, const struct sockaddr* addr, socklen_t alen, |
|
|
|
static ssize_t capture(socket_t fd, const void* data, size_t len, const struct sockaddr* addr, socklen_t alen, |
|
|
|
struct ETCP_LINK* link, void* arg) { |
|
|
|
struct ETCP_LINK* link, void* arg) { |
|
|
|
@ -179,9 +250,10 @@ static void receive_window_case(int wrap) { |
|
|
|
} |
|
|
|
} |
|
|
|
int main(void) { |
|
|
|
int main(void) { |
|
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); |
|
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); |
|
|
|
|
|
|
|
size_t baseline=u_get_allocated_count(); |
|
|
|
|
|
|
|
debug_sequence_boundaries(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
const char* log=getenv("SESSION_TEST_LOG"); |
|
|
|
const char* log=getenv("SESSION_TEST_LOG"); |
|
|
|
if (log) { debug_enable_file_output(log,1); debug_set_category_level(DEBUG_CATEGORY_ETCP,DEBUG_LEVEL_DEBUG); } |
|
|
|
if (log) { debug_enable_file_output(log,1); debug_set_category_level(DEBUG_CATEGORY_ETCP,DEBUG_LEVEL_DEBUG); } |
|
|
|
size_t baseline=u_get_allocated_count(); |
|
|
|
|
|
|
|
data_header_validation(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
data_header_validation(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
timestamp_boundaries(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
timestamp_boundaries(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
receive_window_case(0); CHECK(u_get_allocated_count() == baseline); |
|
|
|
receive_window_case(0); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|