/* * This file Copyright (C) 2013-2014 Mnemosyne LLC * * It may be used under the GNU GPL versions 2 or 3 * or any future license endorsed by Mnemosyne LLC. * */ #include /* isspace() */ #include /* EILSEQ */ #include /* strlen(), strncmp() */ #include #define __LIBTRANSMISSION_VARIANT_MODULE__ #include "transmission.h" #include "utils.h" /* tr_free */ #include "variant.h" #include "variant-common.h" #include "libtransmission-test.h" #ifndef _WIN32 #define STACK_SMASH_DEPTH (1 * 1000 * 1000) #else #define STACK_SMASH_DEPTH (100 * 1000) #endif static int testInt(void) { uint8_t buf[128]; int64_t val; int err; uint8_t const* end; /* good int string */ tr_snprintf((char*)buf, sizeof(buf), "i64e"); err = tr_bencParseInt(buf, buf + 4, &end, &val); check_int(err, ==, 0); check_int(val, ==, 64); check_ptr(end, ==, buf + 4); /* missing 'e' */ end = NULL; val = 888; err = tr_bencParseInt(buf, buf + 3, &end, &val); check_int(err, ==, EILSEQ); check_int(val, ==, 888); check_ptr(end, ==, NULL); /* empty buffer */ err = tr_bencParseInt(buf, buf + 0, &end, &val); check_int(err, ==, EILSEQ); check_int(val, ==, 888); check_ptr(end, ==, NULL); /* bad number */ tr_snprintf((char*)buf, sizeof(buf), "i6z4e"); err = tr_bencParseInt(buf, buf + 5, &end, &val); check_int(err, ==, EILSEQ); check_int(val, ==, 888); check_ptr(end, ==, NULL); /* negative number */ tr_snprintf((char*)buf, sizeof(buf), "i-3e"); err = tr_bencParseInt(buf, buf + 4, &end, &val); check_int(err, ==, 0); check_int(val, ==, -3); check_ptr(end, ==, buf + 4); /* zero */ tr_snprintf((char*)buf, sizeof(buf), "i0e"); err = tr_bencParseInt(buf, buf + 4, &end, &val); check_int(err, ==, 0); check_int(val, ==, 0); check_ptr(end, ==, buf + 3); /* no leading zeroes allowed */ val = 0; end = NULL; tr_snprintf((char*)buf, sizeof(buf), "i04e"); err = tr_bencParseInt(buf, buf + 4, &end, &val); check_int(err, ==, EILSEQ); check_int(val, ==, 0); check_ptr(end, ==, NULL); return 0; } static int testStr(void) { uint8_t buf[128]; int err; int n; uint8_t const* end; uint8_t const* str; size_t len; /* string len is designed to overflow */ n = tr_snprintf((char*)buf, sizeof(buf), "%zu:boat", (size_t)(SIZE_MAX - 2)); err = tr_bencParseStr(buf, buf + n, &end, &str, &len); check_int(err, ==, EILSEQ); check_uint(len, ==, 0); check_ptr(str, ==, NULL); check_ptr(end, ==, NULL); /* good string */ n = tr_snprintf((char*)buf, sizeof(buf), "4:boat"); err = tr_bencParseStr(buf, buf + n, &end, &str, &len); check_int(err, ==, 0); check_uint(len, ==, 4); check_mem(str, ==, "boat", len); check_ptr(end, ==, buf + 6); str = NULL; end = NULL; len = 0; /* string goes past end of buffer */ err = tr_bencParseStr(buf, buf + (n - 1), &end, &str, &len); check_int(err, ==, EILSEQ); check_uint(len, ==, 0); check_ptr(str, ==, NULL); check_ptr(end, ==, NULL); /* empty string */ n = tr_snprintf((char*)buf, sizeof(buf), "0:"); err = tr_bencParseStr(buf, buf + n, &end, &str, &len); check_int(err, ==, 0); check_uint(len, ==, 0); check_uint(*str, ==, '\0'); check_ptr(end, ==, buf + 2); str = NULL; end = NULL; len = 0; /* short string */ n = tr_snprintf((char*)buf, sizeof(buf), "3:boat"); err = tr_bencParseStr(buf, buf + n, &end, &str, &len); check_int(err, ==, 0); check_uint(len, ==, 3); check_mem(str, ==, "boa", len); check_ptr(end, ==, buf + 5); str = NULL; end = NULL; len = 0; return 0; } static int testString(char const* str, bool isGood) { tr_variant val; char const* end = NULL; char* saved; size_t const len = strlen(str); size_t savedLen; int err; err = tr_variantFromBencFull(&val, str, len, NULL, &end); if (!isGood) { check_int(err, !=, 0); } else { check_int(err, ==, 0); #if 0 fprintf(stderr, "in: [%s]\n", str); fprintf(stderr, "out:\n%s", tr_variantToStr(&val, TR_VARIANT_FMT_JSON, NULL)); #endif check_ptr(end, ==, str + len); saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &savedLen); check_str(saved, ==, str); check_uint(savedLen, ==, len); tr_free(saved); tr_variantFree(&val); } return 0; } static int testParse(void) { tr_variant val; tr_variant* child; tr_variant* child2; char buf[512]; char const* end; int err; size_t len; int64_t i; char* saved; tr_snprintf((char*)buf, sizeof(buf), "i64e"); err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end); check_int(err, ==, 0); check(tr_variantGetInt(&val, &i)); check_int(i, ==, 64); check_ptr(end, ==, buf + 4); tr_variantFree(&val); tr_snprintf((char*)buf, sizeof(buf), "li64ei32ei16ee"); err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end); check_int(err, ==, 0); check_ptr(end, ==, buf + strlen((char*)buf)); check_uint(val.val.l.count, ==, 3); check(tr_variantGetInt(&val.val.l.vals[0], &i)); check_int(i, ==, 64); check(tr_variantGetInt(&val.val.l.vals[1], &i)); check_int(i, ==, 32); check(tr_variantGetInt(&val.val.l.vals[2], &i)); check_int(i, ==, 16); saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len); check_str(saved, ==, buf); tr_free(saved); tr_variantFree(&val); end = NULL; tr_snprintf((char*)buf, sizeof(buf), "lllee"); err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end); check_int(err, !=, 0); check_ptr(end, ==, NULL); end = NULL; tr_snprintf((char*)buf, sizeof(buf), "le"); err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end); check_int(err, ==, 0); check_ptr(end, ==, buf + 2); saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len); check_str(saved, ==, "le"); tr_free(saved); tr_variantFree(&val); if ((err = testString("llleee", true)) != 0) { return err; } if ((err = testString("d3:cow3:moo4:spam4:eggse", true)) != 0) { return err; } if ((err = testString("d4:spaml1:a1:bee", true)) != 0) { return err; } if ((err = testString("d5:greenli1ei2ei3ee4:spamd1:ai123e3:keyi214eee", true)) != 0) { return err; } if ((err = testString("d9:publisher3:bob17:publisher-webpage15:www.example.com18:publisher.location4:homee", true)) != 0) { return err; } if ((err = testString("d8:completei1e8:intervali1800e12:min intervali1800e5:peers0:e", true)) != 0) { return err; } if ((err = testString("d1:ai0e1:be", false)) != 0) /* odd number of children */ { return err; } if ((err = testString("", false)) != 0) { return err; } if ((err = testString(" ", false)) != 0) { return err; } /* nested containers * parse an unsorted dict * save as a sorted dict */ end = NULL; tr_snprintf((char*)buf, sizeof(buf), "lld1:bi32e1:ai64eeee"); err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end); check_int(err, ==, 0); check_ptr(end, ==, buf + strlen((char const*)buf)); check_ptr((child = tr_variantListChild(&val, 0)), !=, NULL); check_ptr((child2 = tr_variantListChild(child, 0)), !=, NULL); saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len); check_str(saved, ==, "lld1:ai64e1:bi32eeee"); tr_free(saved); tr_variantFree(&val); /* too many endings */ end = NULL; tr_snprintf((char*)buf, sizeof(buf), "leee"); err = tr_variantFromBencFull(&val, buf, sizeof(buf), NULL, &end); check_int(err, ==, 0); check_ptr(end, ==, buf + 2); saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len); check_str(saved, ==, "le"); tr_free(saved); tr_variantFree(&val); /* no ending */ end = NULL; tr_snprintf((char*)buf, sizeof(buf), "l1:a1:b1:c"); err = tr_variantFromBencFull(&val, buf, strlen(buf), NULL, &end); check_int(err, !=, 0); /* incomplete string */ end = NULL; tr_snprintf((char*)buf, sizeof(buf), "1:"); err = tr_variantFromBencFull(&val, buf, strlen(buf), NULL, &end); check_int(err, !=, 0); return 0; } static void stripWhitespace(char* in) { char* out = in; for (; in != NULL && *in != '\0'; ++in) { if (!isspace(*in)) { *out++ = *in; } } *out = '\0'; } static int testJSONSnippet(char const* benc_str, char const* expected) { tr_variant top; char* serialized; struct evbuffer* buf; tr_variantFromBenc(&top, benc_str, strlen(benc_str)); buf = tr_variantToBuf(&top, TR_VARIANT_FMT_JSON); serialized = (char*)evbuffer_pullup(buf, -1); stripWhitespace(serialized); #if 0 fprintf(stderr, "benc: %s\n", benc_str); fprintf(stderr, "json: %s\n", serialized); fprintf(stderr, "want: %s\n", expected); #endif check_str(serialized, ==, expected); tr_variantFree(&top); evbuffer_free(buf); return 0; } static int testJSON(void) { int val; char const* benc_str; char const* expected; benc_str = "i6e"; expected = "6"; if ((val = testJSONSnippet(benc_str, expected)) != 0) { return val; } benc_str = "d5:helloi1e5:worldi2ee"; expected = "{\"hello\":1,\"world\":2}"; if ((val = testJSONSnippet(benc_str, expected)) != 0) { return val; } benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3eee"; expected = "{\"foo\":[1,2,3],\"hello\":1,\"world\":2}"; if ((val = testJSONSnippet(benc_str, expected)) != 0) { return val; } benc_str = "d5:helloi1e5:worldi2e3:fooli1ei2ei3ed1:ai0eeee"; expected = "{\"foo\":[1,2,3,{\"a\":0}],\"hello\":1,\"world\":2}"; if ((val = testJSONSnippet(benc_str, expected)) != 0) { return val; } benc_str = "d4:argsd6:statusle7:status2lee6:result7:successe"; expected = "{\"args\":{\"status\":[],\"status2\":[]},\"result\":\"success\"}"; if ((val = testJSONSnippet(benc_str, expected)) != 0) { return val; } return 0; } static int testMerge(void) { size_t len; tr_variant dest; tr_variant src; int64_t i; char const* s; tr_quark const i1 = tr_quark_new("i1", 2); tr_quark const i2 = tr_quark_new("i2", 2); tr_quark const i3 = tr_quark_new("i3", 2); tr_quark const i4 = tr_quark_new("i4", 2); tr_quark const s5 = tr_quark_new("s5", 2); tr_quark const s6 = tr_quark_new("s6", 2); tr_quark const s7 = tr_quark_new("s7", 2); tr_quark const s8 = tr_quark_new("s8", 2); /* initial dictionary (default values) */ tr_variantInitDict(&dest, 10); tr_variantDictAddInt(&dest, i1, 1); tr_variantDictAddInt(&dest, i2, 2); tr_variantDictAddInt(&dest, i4, -35); /* remains untouched */ tr_variantDictAddStr(&dest, s5, "abc"); tr_variantDictAddStr(&dest, s6, "def"); tr_variantDictAddStr(&dest, s7, "127.0.0.1"); /* remains untouched */ /* new dictionary, will overwrite items in dest */ tr_variantInitDict(&src, 10); tr_variantDictAddInt(&src, i1, 1); /* same value */ tr_variantDictAddInt(&src, i2, 4); /* new value */ tr_variantDictAddInt(&src, i3, 3); /* new key:value */ tr_variantDictAddStr(&src, s5, "abc"); /* same value */ tr_variantDictAddStr(&src, s6, "xyz"); /* new value */ tr_variantDictAddStr(&src, s8, "ghi"); /* new key:value */ tr_variantMergeDicts(&dest, /*const*/ &src); check(tr_variantDictFindInt(&dest, i1, &i)); check_int(i, ==, 1); check(tr_variantDictFindInt(&dest, i2, &i)); check_int(i, ==, 4); check(tr_variantDictFindInt(&dest, i3, &i)); check_int(i, ==, 3); check(tr_variantDictFindInt(&dest, i4, &i)); check_int(i, ==, -35); check(tr_variantDictFindStr(&dest, s5, &s, &len)); check_uint(len, ==, 3); check_str(s, ==, "abc"); check(tr_variantDictFindStr(&dest, s6, &s, &len)); check_uint(len, ==, 3); check_str(s, ==, "xyz"); check(tr_variantDictFindStr(&dest, s7, &s, &len)); check_uint(len, ==, 9); check_str(s, ==, "127.0.0.1"); check(tr_variantDictFindStr(&dest, s8, &s, &len)); check_uint(len, ==, 3); check_str(s, ==, "ghi"); tr_variantFree(&dest); tr_variantFree(&src); return 0; } static int testStackSmash(void) { size_t len; int err; char* in; char const* end; tr_variant val; char* saved; int const depth = STACK_SMASH_DEPTH; in = tr_new(char, depth * 2 + 1); for (int i = 0; i < depth; ++i) { in[i] = 'l'; in[depth + i] = 'e'; } in[depth * 2] = '\0'; err = tr_variantFromBencFull(&val, in, depth * 2, NULL, &end); check_int(err, ==, 0); check_ptr(end, ==, in + depth * 2); saved = tr_variantToStr(&val, TR_VARIANT_FMT_BENC, &len); check_str(saved, ==, in); tr_free(in); tr_free(saved); tr_variantFree(&val); return 0; } static int testBool(void) { tr_variant top; int64_t intVal; bool boolVal; tr_quark const key1 = tr_quark_new("key1", 4); tr_quark const key2 = tr_quark_new("key2", 4); tr_quark const key3 = tr_quark_new("key3", 4); tr_quark const key4 = tr_quark_new("key4", 4); tr_variantInitDict(&top, 0); tr_variantDictAddBool(&top, key1, false); tr_variantDictAddBool(&top, key2, 0); tr_variantDictAddInt(&top, key3, true); tr_variantDictAddInt(&top, key4, 1); check(tr_variantDictFindBool(&top, key1, &boolVal)); check(!boolVal); check(tr_variantDictFindBool(&top, key2, &boolVal)); check(!boolVal); check(tr_variantDictFindBool(&top, key3, &boolVal)); check(boolVal); check(tr_variantDictFindBool(&top, key4, &boolVal)); check(boolVal); check(tr_variantDictFindInt(&top, key1, &intVal)); check_int(intVal, ==, 0); check(tr_variantDictFindInt(&top, key2, &intVal)); check_int(intVal, ==, 0); check(tr_variantDictFindInt(&top, key3, &intVal)); check_int(intVal, !=, 0); check(tr_variantDictFindInt(&top, key4, &intVal)); check_int(intVal, !=, 0); tr_variantFree(&top); return 0; } static int testParse2(void) { tr_variant top; tr_variant top2; int64_t intVal; char const* strVal; double realVal; bool boolVal; size_t len; char* benc; char const* end; size_t strLen; tr_quark const key_bool = tr_quark_new("this-is-a-bool", TR_BAD_SIZE); tr_quark const key_real = tr_quark_new("this-is-a-real", TR_BAD_SIZE); tr_quark const key_int = tr_quark_new("this-is-an-int", TR_BAD_SIZE); tr_quark const key_str = tr_quark_new("this-is-a-string", TR_BAD_SIZE); tr_variantInitDict(&top, 0); tr_variantDictAddBool(&top, key_bool, true); tr_variantDictAddInt(&top, key_int, 1234); tr_variantDictAddReal(&top, key_real, 0.5); tr_variantDictAddStr(&top, key_str, "this-is-a-string"); benc = tr_variantToStr(&top, TR_VARIANT_FMT_BENC, &len); check_str(benc, ==, "d14:this-is-a-booli1e14:this-is-a-real8:0.50000016:this-is-a-string16:this-is-a-string14:this-is-an-" "inti1234ee"); check_int(tr_variantFromBencFull(&top2, benc, len, NULL, &end), ==, 0); check_ptr(end, ==, benc + len); check(tr_variantIsDict(&top2)); check(tr_variantDictFindInt(&top, key_int, &intVal)); check_int(intVal, ==, 1234); check(tr_variantDictFindBool(&top, key_bool, &boolVal)); check(boolVal); check(tr_variantDictFindStr(&top, key_str, &strVal, &strLen)); check_uint(strLen, ==, 16); check_str(strVal, ==, "this-is-a-string"); check(tr_variantDictFindReal(&top, key_real, &realVal)); check_int((int)(realVal * 100), ==, 50); tr_variantFree(&top2); tr_free(benc); tr_variantFree(&top); return 0; } int main(void) { static testFunc const tests[] = { testInt, testStr, testParse, testJSON, testMerge, testBool, testParse2, testStackSmash }; return runTests(tests, NUM_TESTS(tests)); }