/* tvbtest.c * Standalone program to test functionality of tvbuffs. * * tvbtest : tvbtest.o tvbuff.o except.o * * Copyright (c) 2000 by Gilbert Ramirez * * SPDX-License-Identifier: GPL-2.0-or-later * */ #include "config.h" #include #include #include #include "tvbuff.h" #include "proto.h" #include "exceptions.h" #include "wsutil/array.h" #include "wsutil/pint.h" #include bool failed; typedef struct { struct { uint8_t needle; int offset; } g8; struct { bool test; uint16_t needle; int offset; } g16; struct { bool test; ws_mempbrk_pattern pattern; int offset; unsigned char found_needle; } mempbrk; } search_test_params; static bool test_searches(tvbuff_t *tvb, int offset, search_test_params *sp) { volatile bool ex_thrown = false; TRY { sp->g8.offset = tvb_find_uint8(tvb, offset, -1, sp->g8.needle); if (sp->g16.test) { sp->g16.offset = tvb_find_uint16(tvb, offset, -1, sp->g16.needle); } if (sp->mempbrk.test) { sp->mempbrk.offset = tvb_ws_mempbrk_pattern_uint8(tvb, offset, -1, &sp->mempbrk.pattern, &sp->mempbrk.found_needle); } } CATCH_ALL { ex_thrown = true; } ENDTRY; return ex_thrown; } /* Tests a tvbuff against the expected pattern/length. * Returns true if all tests succeeed, false if any test fails */ static bool test(tvbuff_t *tvb, const char* name, uint8_t* expected_data, unsigned expected_length, unsigned expected_reported_length) { unsigned length; unsigned reported_length; uint8_t *ptr; volatile bool ex_thrown; volatile uint32_t val32; uint32_t expected32; unsigned incr, i; length = tvb_captured_length(tvb); if (length != expected_length) { printf("01: Failed TVB=%s Length of tvb=%u while expected length=%u\n", name, length, expected_length); failed = true; return false; } reported_length = tvb_reported_length(tvb); if (reported_length != expected_reported_length) { printf("01: Failed TVB=%s Reported length of tvb=%u while expected reported length=%u\n", name, reported_length, expected_reported_length); failed = true; return false; } /* Test boundary case. A BoundsError exception should be thrown. */ ex_thrown = false; TRY { tvb_get_ptr(tvb, 0, length + 1); } CATCH(BoundsError) { ex_thrown = true; } CATCH(FragmentBoundsError) { printf("02: Caught wrong exception: FragmentBoundsError\n"); } CATCH(ReportedBoundsError) { printf("02: Caught wrong exception: ReportedBoundsError\n"); } CATCH_ALL { printf("02: Caught wrong exception: %lu\n", exc->except_id.except_code); } ENDTRY; if (!ex_thrown) { printf("02: Failed TVB=%s No BoundsError when retrieving %u bytes\n", name, length + 1); failed = true; return false; } /* Test boundary case with reported_length+1. A ReportedBoundsError exception should be thrown. */ ex_thrown = false; TRY { tvb_get_ptr(tvb, 0, reported_length + 1); } CATCH(BoundsError) { printf("03: Caught wrong exception: BoundsError\n"); } CATCH(FragmentBoundsError) { printf("03: Caught wrong exception: FragmentBoundsError\n"); } CATCH(ReportedBoundsError) { ex_thrown = true; } CATCH_ALL { printf("03: Caught wrong exception: %lu\n", exc->except_id.except_code); } ENDTRY; if (!ex_thrown) { printf("03: Failed TVB=%s No ReportedBoundsError when retrieving %u bytes\n", name, reported_length + 1); failed = true; return false; } /* Test boundary case. A BoundsError exception should be thrown. */ ex_thrown = false; TRY { tvb_get_ptr(tvb, -1, 2); } CATCH(BoundsError) { ex_thrown = true; } CATCH(FragmentBoundsError) { printf("04: Caught wrong exception: FragmentBoundsError\n"); } CATCH(ReportedBoundsError) { printf("04: Caught wrong exception: ReportedBoundsError\n"); } CATCH_ALL { printf("04: Caught wrong exception: %lu\n", exc->except_id.except_code); } ENDTRY; if (!ex_thrown) { printf("04: Failed TVB=%s No BoundsError when retrieving 2 bytes from" " offset -1\n", name); failed = true; return false; } /* Test boundary case. A BoundsError exception should not be thrown. */ ex_thrown = false; TRY { tvb_get_ptr(tvb, 0, length ? 1 : 0); } CATCH(BoundsError) { ex_thrown = true; } CATCH(FragmentBoundsError) { printf("05: Caught wrong exception: FragmentBoundsError\n"); } CATCH(ReportedBoundsError) { printf("05: Caught wrong exception: ReportedBoundsError\n"); } CATCH_ALL { printf("05: Caught wrong exception: %lu\n", exc->except_id.except_code); } ENDTRY; if (ex_thrown) { printf("05: Failed TVB=%s BoundsError when retrieving 1 bytes from" " offset 0\n", name); failed = true; return false; } /* Test boundary case. A BoundsError exception should not be thrown. */ ex_thrown = false; TRY { tvb_get_ptr(tvb, -1, length ? 1 : 0); } CATCH(BoundsError) { ex_thrown = true; } CATCH(FragmentBoundsError) { printf("06: Caught wrong exception: FragmentBoundsError\n"); } CATCH(ReportedBoundsError) { printf("06: Caught wrong exception: ReportedBoundsError\n"); } CATCH_ALL { printf("06: Caught wrong exception: %lu\n", exc->except_id.except_code); } ENDTRY; if (ex_thrown) { printf("06: Failed TVB=%s BoundsError when retrieving 1 bytes from" " offset -1\n", name); failed = true; return false; } /* Check data at boundary. An exception should not be thrown. */ if (length >= 4) { ex_thrown = false; TRY { val32 = tvb_get_ntohl(tvb, 0); } CATCH_ALL { ex_thrown = true; } ENDTRY; if (ex_thrown) { printf("07: Failed TVB=%s Exception when retrieving " "uint32_t from offset 0\n", name); failed = true; return false; } expected32 = pntoh32(expected_data); if (val32 != expected32) { printf("08: Failed TVB=%s uint32_t @ 0 %u != expected %u\n", name, val32, expected32); failed = true; return false; } } /* Check data at boundary. An exception should not be thrown. */ if (length >= 4) { ex_thrown = false; TRY { val32 = tvb_get_ntohl(tvb, -4); } CATCH_ALL { ex_thrown = true; } ENDTRY; if (ex_thrown) { printf("09: Failed TVB=%s Exception when retrieving " "uint32_t from offset 0\n", name); failed = true; return false; } expected32 = pntoh32(&expected_data[length-4]); if (val32 != expected32) { printf("10: Failed TVB=%s uint32_t @ -4 %u != expected %u\n", name, val32, expected32); failed = true; return false; } } /* Sweep across data in various sized increments checking * tvb_memdup() */ for (incr = 1; incr < length; incr++) { for (i = 0; i < length - incr; i += incr) { ptr = (uint8_t*)tvb_memdup(NULL, tvb, i, incr); if (memcmp(ptr, &expected_data[i], incr) != 0) { printf("11: Failed TVB=%s Offset=%u Length=%u " "Bad memdup\n", name, i, incr); failed = true; wmem_free(NULL, ptr); return false; } wmem_free(NULL, ptr); } } /* One big memdup */ ptr = (uint8_t*)tvb_memdup(NULL, tvb, 0, -1); if ((length != 0 && memcmp(ptr, expected_data, length) != 0) || (length == 0 && ptr != NULL)) { printf("12: Failed TVB=%s Offset=0 Length=-1 " "Bad memdup\n", name); failed = true; wmem_free(NULL, ptr); return false; } wmem_free(NULL, ptr); /* Test some searches. * For now, just do a few trivial searches with easily verifiable * results... each of the searches is expected to find their target at * the offset from which the search commences. Walk through the tvb * and run these tests at each byte position. */ for (i = 0; i < length; i++) { search_test_params sp; memset(&sp, 0, sizeof sp); /* Search for the uint8_t at this offset. */ sp.g8.needle = expected_data[i]; /* If at least two bytes left, search for the uint16_t at this offset. */ sp.g16.test = length - i > 1; if (sp.g16.test) { sp.g16.needle = (expected_data[i] << 8) | expected_data[i + 1]; } /* If the uint8_t at this offset is nonzero, try * tvb_ws_mempbrk_pattern_uint8 as well. * ws_mempbrk_compile("\0") is not effective... */ sp.mempbrk.test = expected_data[i] != 0; if (sp.mempbrk.test) { char pattern_string[2] = {expected_data[i], '\0'}; ws_mempbrk_compile(&sp.mempbrk.pattern, pattern_string); } ex_thrown = test_searches(tvb, i, &sp); if (ex_thrown) { printf("13: Failed TVB=%s Exception when searching, offset %d\n", name, i); failed = true; return false; } if ((unsigned)sp.g8.offset != i) { printf("13: Failed TVB=%s Wrong offset for uint8_t:%02x," " got %d, expected %d\n", name, sp.g8.needle, sp.g8.offset, i); failed = true; return false; } if (sp.g16.test && (unsigned)sp.g16.offset != i) { printf("13: Failed TVB=%s Wrong offset for uint16_t:%04x," " got %d, expected %d\n", name, sp.g16.needle, sp.g16.offset, i); failed = true; return false; } if (sp.mempbrk.test && (unsigned)sp.mempbrk.offset != i) { printf("13: Failed TVB=%s Wrong offset for mempbrk:%02x," " got %d, expected %d\n", name, expected_data[i], sp.mempbrk.offset, i); failed = true; return false; } if (sp.mempbrk.test && sp.mempbrk.found_needle != expected_data[i]) { printf("13: Failed TVB=%s Wrong needle found for mempbrk:%02x," " got %02x, expected %02x\n", name, expected_data[i], sp.mempbrk.found_needle, expected_data[i]); failed = true; return false; } } printf("Passed TVB=%s\n", name); return true; } static void run_tests(void) { int i, j; tvbuff_t *tvb_parent; tvbuff_t *tvb_empty; tvbuff_t *tvb_small[3]; tvbuff_t *tvb_large[3]; tvbuff_t *tvb_subset[6]; tvbuff_t *tvb_empty_subset; uint8_t *small[3]; unsigned small_length[3]; unsigned small_reported_length[3]; uint8_t *large[3]; unsigned large_length[3]; unsigned large_reported_length[3]; uint8_t *subset[6]; unsigned subset_length[6]; unsigned subset_reported_length[6]; uint8_t temp; uint8_t *comp[6]; tvbuff_t *tvb_comp[6]; unsigned comp_length[6]; unsigned comp_reported_length[6]; tvbuff_t *tvb_comp_subset; unsigned comp_subset_length; unsigned comp_subset_reported_length; uint8_t *comp_subset; int len; tvb_parent = tvb_new_real_data((const uint8_t*)"", 0, 0); for (i = 0; i < 3; i++) { small[i] = g_new(uint8_t, 16); temp = 16 * i; for (j = 0; j < 16; j++) { small[i][j] = temp + j; } small_length[i] = 16; small_reported_length[i] = 17; tvb_small[i] = tvb_new_child_real_data(tvb_parent, small[i], 16, 17); tvb_set_free_cb(tvb_small[i], g_free); } for (i = 0; i < 3; i++) { large[i] = g_new(uint8_t, 19); temp = 19 * i; for (j = 0; j < 19; j++) { large[i][j] = temp + j; } large_length[i] = 19; large_reported_length[i] = 20; tvb_large[i] = tvb_new_child_real_data(tvb_parent, large[i], 19, 20); tvb_set_free_cb(tvb_large[i], g_free); } /* Test empty tvb */ tvb_empty = tvb_new_child_real_data(tvb_parent, NULL, 0, 1); test(tvb_empty, "Empty", NULL, 0, 1); /* Test the "real" tvbuff objects. */ test(tvb_small[0], "Small 0", small[0], small_length[0], small_reported_length[0]); test(tvb_small[1], "Small 1", small[1], small_length[1], small_reported_length[1]); test(tvb_small[2], "Small 2", small[2], small_length[2], small_reported_length[2]); test(tvb_large[0], "Large 0", large[0], large_length[0], large_reported_length[0]); test(tvb_large[1], "Large 1", large[1], large_length[1], large_reported_length[1]); test(tvb_large[2], "Large 2", large[2], large_length[2], large_reported_length[2]); subset_length[0] = 8; subset_reported_length[0] = 9; tvb_subset[0] = tvb_new_subset_length_caplen(tvb_small[0], 0, 8, 9); subset[0] = &small[0][0]; subset_length[1] = 10; subset_reported_length[1] = 11; tvb_subset[1] = tvb_new_subset_length_caplen(tvb_large[0], -10, 10, 11); subset[1] = &large[0][9]; subset_length[2] = 16; subset_reported_length[2] = 17; tvb_subset[2] = tvb_new_subset_length_caplen(tvb_small[1], -16, -1, 17); subset[2] = &small[1][0]; subset_length[3] = 3; subset_reported_length[3] = 4; tvb_subset[3] = tvb_new_subset_length_caplen(tvb_subset[0], 0, 3, 4); subset[3] = &small[0][0]; subset_length[4] = 5; subset_reported_length[4] = 6; tvb_subset[4] = tvb_new_subset_length_caplen(tvb_subset[1], -5, 5, 6); subset[4] = &large[0][14]; subset_length[5] = 8; subset_reported_length[5] = 9; tvb_subset[5] = tvb_new_subset_length_caplen(tvb_subset[2], 4, 8, 9); subset[5] = &small[1][4]; /* Test the "subset" tvbuff objects. */ test(tvb_subset[0], "Subset 0", subset[0], subset_length[0], subset_reported_length[0]); test(tvb_subset[1], "Subset 1", subset[1], subset_length[1], subset_reported_length[1]); test(tvb_subset[2], "Subset 2", subset[2], subset_length[2], subset_reported_length[2]); test(tvb_subset[3], "Subset 3", subset[3], subset_length[3], subset_reported_length[3]); test(tvb_subset[4], "Subset 4", subset[4], subset_length[4], subset_reported_length[4]); test(tvb_subset[5], "Subset 5", subset[5], subset_length[5], subset_reported_length[5]); /* Subset of an empty tvb. */ tvb_empty_subset = tvb_new_subset_length_caplen(tvb_empty, 0, 0, 1); test(tvb_empty_subset, "Empty Subset", NULL, 0, 1); /* One Real */ printf("Making Composite 0\n"); tvb_comp[0] = tvb_new_composite(); comp_length[0] = small_length[0]; comp_reported_length[0] = small_reported_length[0]; comp[0] = small[0]; tvb_composite_append(tvb_comp[0], tvb_small[0]); tvb_composite_finalize(tvb_comp[0]); /* Two Reals */ printf("Making Composite 1\n"); tvb_comp[1] = tvb_new_composite(); comp_length[1] = small_length[0] + small_length[1]; comp_reported_length[1] = small_reported_length[0] + small_reported_length[1]; comp[1] = (uint8_t*)g_malloc(comp_length[1]); memcpy(comp[1], small[0], small_length[0]); memcpy(&comp[1][small_length[0]], small[1], small_length[1]); tvb_composite_append(tvb_comp[1], tvb_small[0]); tvb_composite_append(tvb_comp[1], tvb_small[1]); tvb_composite_finalize(tvb_comp[1]); /* One subset */ printf("Making Composite 2\n"); tvb_comp[2] = tvb_new_composite(); comp_length[2] = subset_length[1]; comp_reported_length[2] = subset_reported_length[1]; comp[2] = subset[1]; tvb_composite_append(tvb_comp[2], tvb_subset[1]); tvb_composite_finalize(tvb_comp[2]); /* Two subsets */ printf("Making Composite 3\n"); tvb_comp[3] = tvb_new_composite(); comp_length[3] = subset_length[4] + subset_length[5]; comp_reported_length[3] = subset_reported_length[4] + subset_reported_length[5]; comp[3] = (uint8_t*)g_malloc(comp_length[3]); memcpy(comp[3], subset[4], subset_length[4]); memcpy(&comp[3][subset_length[4]], subset[5], subset_length[5]); tvb_composite_append(tvb_comp[3], tvb_subset[4]); tvb_composite_append(tvb_comp[3], tvb_subset[5]); tvb_composite_finalize(tvb_comp[3]); /* One real, one subset */ printf("Making Composite 4\n"); tvb_comp[4] = tvb_new_composite(); comp_length[4] = small_length[0] + subset_length[1]; comp_reported_length[4] = small_reported_length[0] + subset_reported_length[1]; comp[4] = (uint8_t*)g_malloc(comp_length[4]); memcpy(&comp[4][0], small[0], small_length[0]); memcpy(&comp[4][small_length[0]], subset[1], subset_length[1]); tvb_composite_append(tvb_comp[4], tvb_small[0]); tvb_composite_append(tvb_comp[4], tvb_subset[1]); tvb_composite_finalize(tvb_comp[4]); /* 4 composites */ printf("Making Composite 5\n"); tvb_comp[5] = tvb_new_composite(); comp_length[5] = comp_length[0] + comp_length[1] + comp_length[2] + comp_length[3]; comp_reported_length[5] = comp_reported_length[0] + comp_reported_length[1] + comp_reported_length[2] + comp_reported_length[3]; comp[5] = (uint8_t*)g_malloc(comp_length[5]); len = 0; memcpy(&comp[5][len], comp[0], comp_length[0]); len += comp_length[0]; memcpy(&comp[5][len], comp[1], comp_length[1]); len += comp_length[1]; memcpy(&comp[5][len], comp[2], comp_length[2]); len += comp_length[2]; memcpy(&comp[5][len], comp[3], comp_length[3]); tvb_composite_append(tvb_comp[5], tvb_comp[0]); tvb_composite_append(tvb_comp[5], tvb_comp[1]); tvb_composite_append(tvb_comp[5], tvb_comp[2]); tvb_composite_append(tvb_comp[5], tvb_comp[3]); tvb_composite_finalize(tvb_comp[5]); /* A subset of one of the composites. */ tvb_comp_subset = tvb_new_subset_remaining(tvb_comp[1], 1); comp_subset = &comp[1][1]; comp_subset_length = comp_length[1] - 1; comp_subset_reported_length = comp_reported_length[1] - 1; /* Test the "composite" tvbuff objects. */ test(tvb_comp[0], "Composite 0", comp[0], comp_length[0], comp_reported_length[0]); test(tvb_comp[1], "Composite 1", comp[1], comp_length[1], comp_reported_length[1]); test(tvb_comp[2], "Composite 2", comp[2], comp_length[2], comp_reported_length[2]); test(tvb_comp[3], "Composite 3", comp[3], comp_length[3], comp_reported_length[3]); test(tvb_comp[4], "Composite 4", comp[4], comp_length[4], comp_reported_length[4]); test(tvb_comp[5], "Composite 5", comp[5], comp_length[5], comp_reported_length[5]); /* Test the subset of the composite. */ test(tvb_comp_subset, "Subset of Composite", comp_subset, comp_subset_length, comp_subset_reported_length); /* free memory. */ /* Don't free: comp[0] */ g_free(comp[1]); /* Don't free: comp[2] */ g_free(comp[3]); g_free(comp[4]); g_free(comp[5]); tvb_free_chain(tvb_parent); /* should free all tvb's and associated data */ } typedef struct { // Raw bytes int enc_len; const uint8_t *enc; // Varint parameters int encoding; int maxlen; // Results unsigned long expect_except; uint64_t expect_val; unsigned expect_len; } varint_test_s; DIAG_OFF_PEDANTIC varint_test_s varint[] = { {0, (const uint8_t *)"", 0, FT_VARINT_MAX_LEN, DissectorError, 0, 0}, // no encoding specified // ENC_VARINT_PROTOBUF {0, (const uint8_t *)"", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, ReportedBoundsError, 0, 0}, {1, (const uint8_t *)"\x00", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, 0, 0, 1}, {1, (const uint8_t *)"\x01", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, 0, 1, 1}, {1, (const uint8_t *)"\x7f", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, 0, 0x7f, 1}, {2, (const uint8_t *)"\x80\x01", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, 0, UINT64_C(1)<<7, 2}, {1, (const uint8_t *)"\x80", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, ReportedBoundsError, 0, 0}, // truncated data {2, (const uint8_t *)"\x80\x01", ENC_VARINT_PROTOBUF, 1, 0, 0, 0}, // truncated read {5, (const uint8_t *)"\x80\x80\x80\x80\x01", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, 0, UINT64_C(1)<<28, 5}, {10, (const uint8_t *)"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, 0, UINT64_C(1)<<63, 10}, {10, (const uint8_t *)"\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, 0, 0xffffffffffffffff, 10}, {10, (const uint8_t *)"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x02", ENC_VARINT_PROTOBUF, FT_VARINT_MAX_LEN, 0, 0, 10}, // overflow // ENC_VARINT_SDNV {0, (const uint8_t *)"", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, ReportedBoundsError, 0, 0}, {1, (const uint8_t *)"\x00", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, 0, 0, 1}, {1, (const uint8_t *)"\x01", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, 0, 1, 1}, {1, (const uint8_t *)"\x7f", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, 0, 0x7f, 1}, {2, (const uint8_t *)"\x81\x00", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, 0, UINT64_C(1)<<7, 2}, {1, (const uint8_t *)"\x81", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, ReportedBoundsError, 1, 0}, // truncated data {2, (const uint8_t *)"\x81\x00", ENC_VARINT_SDNV, 1, 0, 1, 0}, // truncated read {5, (const uint8_t *)"\x81\x80\x80\x80\x00", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, 0, UINT64_C(1)<<28, 5}, {10, (const uint8_t *)"\x81\x80\x80\x80\x80\x80\x80\x80\x80\x00", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, 0, UINT64_C(1)<<63, 10}, {10, (const uint8_t *)"\x81\xff\xff\xff\xff\xff\xff\xff\xff\x7f", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, 0, 0xffffffffffffffff, 10}, {10, (const uint8_t *)"\x82\x80\x80\x80\x80\x80\x80\x80\x80\x00", ENC_VARINT_SDNV, FT_VARINT_MAX_LEN, 0, UINT64_C(1)<<57, 0}, // overflow }; DIAG_ON_PEDANTIC static void varint_tests(void) { tvbuff_t *tvb_parent, *tvb; volatile unsigned long got_ex; uint64_t got_val; volatile unsigned got_len; tvb_parent = tvb_new_real_data((const uint8_t*)"", 0, 0); for (size_t ix = 0; ix < array_length(varint); ++ix) { const varint_test_s *vit = &varint[ix]; tvb = tvb_new_child_real_data(tvb_parent, vit->enc, vit->enc_len, vit->enc_len); got_ex = 0; got_val = 0; got_len = 0; TRY { got_len = tvb_get_varint(tvb, 0, vit->maxlen, &got_val, vit->encoding); } CATCH_ALL { got_ex = exc->except_id.except_code; } ENDTRY; if (got_ex != vit->expect_except) { printf("Failed varint #%zu with exception=%lu while expected exception=%lu\n", ix, got_ex, vit->expect_except); failed = true; continue; } if (got_val != vit->expect_val) { printf("Failed varint #%zu value=%" PRIu64 " while expected value=%" PRIu64 "\n", ix, got_val, vit->expect_val); failed = true; continue; } if (got_len != vit->expect_len) { printf("Failed varint #%zu length=%u while expected length=%u\n", ix, got_len, vit->expect_len); failed = true; continue; } printf("Passed varint #%zu\n", ix); } tvb_free_chain(tvb_parent); /* should free all tvb's and associated data */ } #define DATA_AND_LEN(X) .data = X, .len = sizeof(X) - 1 static void zstd_tests (void) { #ifdef HAVE_ZSTD typedef struct { const char* desc; const uint8_t* data; size_t len; const char* expect; } zstd_testcase; zstd_testcase tests[] = { { .desc = "Uncompressing 'foobar'", DATA_AND_LEN ("\x28\xb5\x2f\xfd\x20\x07\x39\x00\x00\x66\x6f\x6f\x62\x61\x72\x00"), .expect = "foobar" }, { .desc = "Uncompressing invalid data", DATA_AND_LEN ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"), .expect = NULL }, { .desc = "Uncompressing too short length", .data = "\x28\xb5\x2f\xfd\x20\x07\x39\x00\x00\x66\x6f\x6f\x62\x61\x72\x00", .len = 1, .expect = NULL }, { .desc = "Uncompressing two frames of data", // data is two frames of compressed data with compression level 1. // the first frame is the string "foo" with no null terminator. // the second frame is the string "bar" with a null terminator. DATA_AND_LEN ("\x28\xb5\x2f\xfd\x20\x03\x19\x00\x00\x66\x6f\x6f" "\x28\xb5\x2f\xfd\x20\x04\x21\x00\x00\x62\x61\x72\x00"), .expect = "foobar" }, { .desc = "Uncompressing two frames of data. 2nd frame has too short length.", // data is two frames of compressed data with compression level 1. // the first frame is the string "foo" with no null terminator. // the second frame is the string "bar" with a null terminator. .data ="\x28\xb5\x2f\xfd\x20\x03\x19\x00\x00\x66\x6f\x6f" "\x28\xb5\x2f\xfd\x20\x04\x21\x00\x00\x62\x61\x72\x00", .len = 13, .expect = NULL }, { .desc = "Uncompressing two frames of data. 2nd frame is malformed.", // data is two frames of compressed data with compression level 1. // the first frame is the string "foo" with no null terminator. // the second frame is malformed. DATA_AND_LEN ("\x28\xb5\x2f\xfd\x20\x03\x19\x00\x00\x66\x6f\x6f" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"), .expect = NULL }, { .desc = "Uncompressing no data", .data = "\0", .len = 0, .expect = "" }, }; for (size_t i = 0; i < array_length(tests); i++) { zstd_testcase *t = tests + i; printf ("ZSTD test: %s ... begin\n", t->desc); tvbuff_t *tvb = tvb_new_real_data (t->data, (const unsigned) t->len, (const unsigned) t->len); tvbuff_t *got = tvb_uncompress_zstd (tvb, 0, (int) t->len); if (!t->expect) { if (got) { fprintf (stderr, "ZSTD test: %s ... FAIL: Expected error, but got non-NULL from uncompress\n", t->desc); failed = true; return; } } else { if (!got) { printf ("ZSTD test: %s ... FAIL: Expected success, but got NULL from uncompress.\n", t->desc); failed = true; return; } char * got_str = tvb_get_string_enc (NULL, got, 0, tvb_reported_length (got), ENC_ASCII); if (0 != strcmp (got_str, t->expect)) { printf ("ZSTD test: %s ... FAIL: Expected \"%s\", got \"%s\".\n", t->desc, t->expect, got_str); failed = true; return; } wmem_free (NULL, got_str); tvb_free (got); } tvb_free (tvb); printf ("ZSTD test: %s ... OK\n", t->desc); } #else printf ("Skipping ZSTD test. ZSTD is not available.\n"); #endif } /* Note: valgrind can be used to check for tvbuff memory leaks */ int main(void) { /* For valgrind: See GLib documentation: "Running GLib Applications" */ g_setenv("G_DEBUG", "gc-friendly", 1); g_setenv("G_SLICE", "always-malloc", 1); except_init(); run_tests(); varint_tests(); zstd_tests (); except_deinit(); exit(failed?1:0); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */