#include "naut/metainfo.h" #include "naut/bencode.h" #include "test.h" #include #include #include #ifndef NAUT_FIXTURES #define NAUT_FIXTURES "tests/fixtures" #endif static uint8_t *slurp(const char *name, size_t *len) { char path[1024]; snprintf(path, sizeof path, "%s/%s", NAUT_FIXTURES, name); FILE *f = fopen(path, "rb"); if (!f) { fprintf(stderr, " cannot open %s\n", path); return NULL; } fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET); uint8_t *b = malloc(n); if (fread(b, 1, n, f) != (size_t)n) { fclose(f); free(b); return NULL; } fclose(f); *len = (size_t)n; return b; } static int hexeq(const uint8_t *got, const char *hex, size_t n) { for (size_t i = 0; i < n; i++) { unsigned b; sscanf(hex + i*2, "%2x", &b); if (got[i] != (uint8_t)b) return 0; } return 1; } static naut_metainfo load(const char *name) { naut_metainfo mi; memset(&mi, 0, sizeof mi); size_t len; uint8_t *b = slurp(name, &len); if (!b) { return mi; } naut_err e = naut_metainfo_parse(b, len, &mi); free(b); if (e != NAUT_OK) fprintf(stderr, " parse %s failed: %s\n", name, naut_strerror(e)); return mi; } int main(void) { /* --- v1 single-file: info-hash must match libtorrent exactly --- */ { naut_metainfo mi = load("single_v1.torrent"); CHECK(mi.has_v1 && !mi.has_v2); CHECK(hexeq(mi.infohash_v1, "7f2555dfd18ba1c4a024264e939d7a5f8b25311b", 20)); CHECK_EQ(mi.num_pieces, 13); CHECK_EQ(mi.piece_length, 16384); CHECK_EQ(mi.total_length, 200000); CHECK_EQ(mi.num_files, 1); CHECK_EQ(mi.num_trackers, 2); char hex[41]; naut_infohash_v1_hex(&mi, hex); CHECK(strcmp(hex, "7f2555dfd18ba1c4a024264e939d7a5f8b25311b") == 0); naut_metainfo_free(&mi); } /* --- v1 multi-file --- */ { naut_metainfo mi = load("multi_v1.torrent"); CHECK(mi.has_v1); CHECK(hexeq(mi.infohash_v1, "a1ea0ef182fd34532e8c4bddbc1dd25669a874ac", 20)); CHECK_EQ(mi.num_pieces, 9); CHECK_EQ(mi.total_length, 145000); CHECK(mi.num_files == 2); /* multi-file paths are '/'-joined; one lives under sub/ */ int found_sub = 0; for (size_t i = 0; i < mi.num_files; i++) if (strstr(mi.files[i].path, "sub/")) found_sub = 1; CHECK(found_sub); naut_metainfo_free(&mi); } /* --- hybrid: BOTH info-hashes present and correct --- */ { naut_metainfo mi = load("hybrid.torrent"); CHECK(mi.has_v1 && mi.has_v2); CHECK(hexeq(mi.infohash_v1, "7646920c5608655e18c7c3a8425a4f8f767e2fcf", 20)); CHECK(hexeq(mi.infohash_v2, "89e5335bbee10fecdfa5f6856db99c13cde84cdb5e1267aa7463574e87c8e6be", 32)); naut_metainfo_free(&mi); } /* --- v2-only: v2 hash, no v1 --- */ { naut_metainfo mi = load("v2.torrent"); CHECK(!mi.has_v1 && mi.has_v2); CHECK(hexeq(mi.infohash_v2, "c8aab2c350b7f887135bc867ec4d2afccbc019d240584a443fd4bdbf1cca5f55", 32)); naut_metainfo_free(&mi); } /* --- magnet: hex btih + btmh + dn + tr --- */ { naut_magnet m; const char *uri = "magnet:?xt=urn:btih:7646920c5608655e18c7c3a8425a4f8f767e2fcf" "&xt=urn:btmh:122089e5335bbee10fecdfa5f6856db99c13cde84cdb5e1267aa7463574e87c8e6be" "&dn=multi+folder&tr=udp%3A%2F%2Ftracker.example.com%3A8080"; CHECK(naut_magnet_parse(uri, &m) == NAUT_OK); CHECK(m.has_v1 && m.has_v2); CHECK(hexeq(m.infohash_v1, "7646920c5608655e18c7c3a8425a4f8f767e2fcf", 20)); CHECK(hexeq(m.infohash_v2, "89e5335bbee10fecdfa5f6856db99c13cde84cdb5e1267aa7463574e87c8e6be", 32)); CHECK(m.name && strcmp(m.name, "multi folder") == 0); /* + and %xx decoded */ CHECK_EQ(m.num_trackers, 1); CHECK(strcmp(m.trackers[0], "udp://tracker.example.com:8080") == 0); naut_magnet_free(&m); } /* --- magnet: base32 btih --- */ { naut_magnet m; CHECK(naut_magnet_parse( "magnet:?xt=urn:btih:P4SVLX6RROQ4JIBEEZHJHHL2L6FSKMI3", &m) == NAUT_OK); CHECK(m.has_v1); CHECK(hexeq(m.infohash_v1, "7f2555dfd18ba1c4a024264e939d7a5f8b25311b", 20)); naut_magnet_free(&m); CHECK(naut_magnet_parse("magnet:?dn=nohash", &m) == NAUT_ERR_PROTO); CHECK(naut_magnet_parse("http://not-a-magnet", &m) == NAUT_ERR_INVAL); } /* --- BEP-9 raw info dictionary entry point preserves its exact hash --- */ { size_t len; uint8_t *torrent = slurp("single_v1.torrent", &len); naut_bc_doc *doc = NULL; CHECK(torrent && naut_bc_parse(torrent, len, &doc) == NAUT_OK); const naut_bc *info = naut_bc_dict_get(naut_bc_root(doc), "info"); const char *trackers[] = { "udp://tracker.example:80" }; naut_metainfo mi; CHECK(info && naut_metainfo_parse_info( info->raw, info->raw_len, trackers, 1, &mi) == NAUT_OK); CHECK(hexeq(mi.infohash_v1, "7f2555dfd18ba1c4a024264e939d7a5f8b25311b", 20)); CHECK(mi.num_trackers == 1 && strcmp(mi.trackers[0], trackers[0]) == 0); naut_metainfo_free(&mi); naut_bc_free(doc); free(torrent); } TEST_MAIN_END(); }