From cd0eb82f20b4b0186207b8f5653b83dde83ca03c Mon Sep 17 00:00:00 2001 From: Richard Nyberg Date: Tue, 12 Sep 2006 15:12:34 +0000 Subject: [PATCH] The metainfo code provided a load -> test -> struct metainfo interface. The metainfo struct has been replaced by functions for qeurying specific items from the torrent. In addition, the tests of the torrent data has been improved. --- misc/metainfo.c | 508 ++++++++++++++++++++++++++++++------------------ misc/metainfo.h | 43 ++-- 2 files changed, 348 insertions(+), 203 deletions(-) diff --git a/misc/metainfo.c b/misc/metainfo.c index 856db32..5b4df6c 100644 --- a/misc/metainfo.c +++ b/misc/metainfo.c @@ -1,14 +1,7 @@ -#include -#include -#include - #include -#include #include -#include #include #include -#include #include @@ -19,6 +12,7 @@ /* * d * announce = url + * announce-list = l l url ... e ... e * info = d * name = advisory file/dir save name * piece length = power of two length of each block @@ -30,225 +24,367 @@ * */ +uint8_t * +mi_hashes(const char *p) +{ + return benc_dget_mema(benc_dget_dct(p, "info"), "pieces", NULL); +} + +size_t +mi_npieces(const char *p) +{ + size_t plen; + benc_dget_mem(benc_dget_dct(p, "info"), "pieces", &plen); + return plen / 20; +} + +int +mi_simple(const char *p) +{ + return benc_dget_lst(benc_dget_dct(p, "info"), "files") == NULL; +} + void -print_metainfo(struct metainfo *tp) -{ - unsigned i; - - printf("Info hash: "); - for (i = 0; i < 20; i++) - printf("%.2x", tp->info_hash[i]); - printf("\n"); - printf("Tracker URL: %s\n", tp->announce); - printf("Piece length: %lld\n", (long long)tp->piece_length); - printf("Number of pieces: %u\n", tp->npieces); - printf("Number of files: %u\n", tp->nfiles); - printf("Advisory name: %s\n", tp->name); - printf("Files:\n"); - for (i = 0; i < tp->nfiles; i++) { - printf("%s (%lld)\n", - tp->files[i].path, (long long)tp->files[i].length); +mi_free_announce(struct mi_announce *ann) +{ + if (ann->tiers != NULL) { + for (int ti = 0; ti < ann->ntiers; ti++) + if (ann->tiers[ti].urls != NULL) { + for (int ui = 0; ui < ann->tiers[ti].nurls; ui++) + if (ann->tiers[ti].urls[ui] != NULL) + free(ann->tiers[ti].urls[ui]); + free(ann->tiers[ti].urls); + } + free(ann->tiers); } - printf("Total length: %lld\n\n", (long long)tp->total_length); + free(ann); } -static int -check_path(const char *path, size_t len) +static void +mi_shuffle_announce(struct mi_announce *ann) { - if (len == 0) - return 0; - else if (len == 1 && path[0] == '.') - return 0; - else if (len == 2 && path[0] == '.' && path[1] == '.') - return 0; - else if (memchr(path, '/', len) != NULL) - return 0; - return 1; + for (int i = 0; i < ann->ntiers; i++) { + for (int j = 0; j < ann->tiers[i].nurls - 1; j++) { + char *tmp = ann->tiers[i].urls[j]; + int ri = rand_between(j, ann->tiers[i].nurls - 1); + ann->tiers[i].urls[j] = ann->tiers[i].urls[ri]; + ann->tiers[i].urls[ri] = tmp; + } + } } -int -fill_fileinfo(const char *fdct, struct fileinfo *tfp) +struct mi_announce * +mi_announce(const char *p) { - size_t npath, plen, len; - const char *plst, *iter, *str; + int ti, ui; + const char *alst, *ulst, *url; + struct mi_announce *res; - if (!benc_dct_chk(fdct, 2, BE_INT, 1, "length", BE_LST, 1, "path")) - return EINVAL; + if ((res = calloc(1, sizeof(*res))) == NULL) + return NULL; - tfp->length = benc_dget_int(fdct, "length"); - plst = benc_dget_lst(fdct, "path"); + if ((alst = benc_dget_lst(p, "announce-list")) != NULL) { + res->ntiers = benc_nelems(alst); + if ((res->tiers = calloc(res->ntiers, sizeof(*res->tiers))) == NULL) + goto error; + ti = 0; ulst = benc_first(alst); + while (ulst != NULL) { + res->tiers[ti].nurls = benc_nelems(ulst); + res->tiers[ti].urls = + calloc(res->tiers[ti].nurls, sizeof(*res->tiers[ti].urls)); + if (res->tiers[ti].urls == NULL) + goto error; + + ui = 0; url = benc_first(ulst); + while (url != NULL) { + if ((res->tiers[ti].urls[ui] = + benc_str(url, NULL, NULL)) == NULL) + goto error; + ui++; url = benc_next(url); + } + + ti++; ulst = benc_next(ulst); + } + } else { + res->ntiers = 1; + if ((res->tiers = calloc(1, sizeof(*res->tiers))) == NULL) + goto error; + res->tiers[0].nurls = 1; + if ((res->tiers[0].urls = + calloc(1, sizeof(*res->tiers[0].urls))) == NULL) + goto error; + if ((res->tiers[0].urls[0] = + benc_dget_str(p, "announce", NULL)) == NULL) + goto error; + } + mi_shuffle_announce(res); + return res; + +error: + if (res != NULL) + mi_free_announce(res); + return NULL; +} + +off_t +mi_piece_length(const char *p) +{ + return benc_dget_int(benc_dget_dct(p, "info"), "piece length"); +} + +off_t +mi_total_length(const char *p) +{ + const char *info = benc_dget_dct(p, "info"); + const char *files = benc_dget_lst(info, "files"); + if (files != NULL) { + off_t length = 0; + const char *fdct = benc_first(files); + while (fdct != NULL) { + length += benc_dget_int(fdct, "length"); + fdct = benc_next(fdct); + } + return length; + } else + return benc_dget_int(info, "length"); +} + +uint8_t * +mi_info_hash(const char *p, uint8_t *hash) +{ + const char *info = benc_dget_dct(p, "info"); + if (hash == NULL) + if ((hash = malloc(20)) == NULL) + return NULL; + return SHA1(info, benc_length(info), hash); +} + +char * +mi_name(const char *p) +{ + return benc_dget_str(benc_dget_dct(p, "info"), "name", NULL); +} + +size_t +mi_nfiles(const char *p) +{ + const char *files = benc_dget_lst(benc_dget_dct(p, "info"), "files"); + if (files != NULL) + return benc_nelems(files); + else + return 1; +} + +static char * +mi_filepath(const char *plst) +{ + char *res = NULL; + const char *str; + size_t npaths = 0, plen = 0, len; + const char *iter = benc_first(plst); - npath = plen = 0; - iter = benc_first(plst); while (iter != NULL) { - if (!benc_isstr(iter)) - return EINVAL; - str = benc_mem(iter, &len, &iter); - if (!check_path(str, len)) - return EINVAL; - npath++; + benc_mem(iter, &len, &iter); + npaths++; plen += len; } - if (npath == 0) - return EINVAL; - if ((tfp->path = malloc(plen + (npath - 1) + 1)) == NULL) - return ENOMEM; + if ((res = malloc(plen + (npaths - 1) + 1)) == NULL) + return NULL; iter = benc_first(plst); str = benc_mem(iter, &len, &iter); - memcpy(tfp->path, str, len); + bcopy(str, res, len); plen = len; - npath--; - while (npath > 0) { - tfp->path[plen++] = '/'; + npaths--; + while (npaths > 0) { + res[plen] = '/'; + plen++; str = benc_mem(iter, &len, &iter); - memcpy(tfp->path + plen, str, len); + bcopy(str, res + plen, len); plen += len; - npath--; + npaths--; } - tfp->path[plen] = '\0'; - return 0; + res[plen] = '\0'; + return res; } void -clear_metainfo(struct metainfo *mip) -{ - int i; - if (mip->piece_hash != NULL) - free(mip->piece_hash); - if (mip->announce != NULL) - free(mip->announce); - if (mip->files != NULL) { - for (i = 0; i < mip->nfiles; i++) { - if (mip->files[i].path != NULL) - free(mip->files[i].path); - } - free(mip->files); - } - if (mip->name != NULL) - free(mip->name); +mi_free_files(unsigned nfiles, struct mi_file *files) +{ + for (unsigned i = 0; i < nfiles; i++) + if (files[i].path != NULL) + free(files[i].path); + free(files); } -int -fill_metainfo(const char *bep, struct metainfo *tp, int mem_hashes) -{ - size_t len; - int err = 0; - const char *base_addr = bep; - const char *hash_addr; - - if (!benc_dct_chk(bep, 5, - BE_STR, 1, "announce", - BE_DCT, 1, "info", - BE_INT, 2, "info", "piece length", - BE_STR, 2, "info", "pieces", - BE_STR, 2, "info", "name")) - return EINVAL; - - if ((tp->announce = benc_dget_str(bep, "announce", NULL)) == NULL) { - err = ENOMEM; - goto out; - } - bep = benc_dget_dct(bep, "info"); - SHA1(bep, benc_length(bep), tp->info_hash); - tp->piece_length = benc_dget_int(bep, "piece length"); - hash_addr = benc_dget_mem(bep, "pieces", &len); - tp->npieces = len / 20; - tp->pieces_off = hash_addr - base_addr; - if (mem_hashes) { - tp->piece_hash = (uint8_t (*)[20])benc_dget_mema(bep, "pieces", NULL); - if (tp->piece_hash == NULL) { - err = ENOMEM; - goto out; - } - } - tp->name = benc_dget_str(bep, "name", NULL); - - if (benc_dct_chk(bep, 1, BE_INT, 1, "length")) { - tp->total_length = benc_dget_int(bep, "length"); - tp->nfiles = 1; - tp->files = calloc(1, sizeof(struct fileinfo)); - if (tp->files != NULL) { - tp->files[0].length = tp->total_length; - tp->files[0].path = strdup(tp->name); - if (tp->files[0].path == NULL) { - err = ENOMEM; - goto out; +struct mi_file * +mi_files(const char *p) +{ + struct mi_file *fi; + const char *info = benc_dget_dct(p, "info"); + const char *files = benc_dget_lst(info, "files"); + if (files != NULL) { + int i = 0; + unsigned nfiles = benc_nelems(files); + const char *fdct = benc_first(files); + if ((fi = calloc(nfiles, sizeof(*fi))) == NULL) + return NULL; + for (fdct = benc_first(files); fdct != NULL; fdct = benc_next(fdct)) { + fi[i].length = benc_dget_int(fdct, "length"); + fi[i].path = mi_filepath(benc_dget_lst(fdct, "path")); + if (fi[i].path == NULL) { + mi_free_files(nfiles, fi); + return NULL; } - } else { - err = ENOMEM; - goto out; + i++; } - } else if (benc_dct_chk(bep, 1, BE_LST, 1, "files")) { - int i; - const char *flst, *fdct; - - flst = benc_dget_lst(bep, "files"); - tp->nfiles = benc_nelems(flst); - if (tp->nfiles < 1) { - err = EINVAL; - goto out; + } else { + if ((fi = calloc(1, sizeof(*fi))) == NULL) + return NULL; + fi[0].length = benc_dget_int(info, "length"); + fi[0].path = benc_dget_str(info, "name", NULL); + if (fi[0].path == NULL) { + free(fi); + return NULL; } - tp->files = calloc(tp->nfiles, sizeof(struct fileinfo)); - - tp->total_length = 0; - i = 0; - for (fdct = benc_first(flst); fdct != NULL; fdct = benc_next(fdct)) { - if (!benc_isdct(fdct)) { - err = EINVAL; - goto out; - } + } + return fi; +} - if ((err = fill_fileinfo(fdct, &tp->files[i])) != 0) - goto out; +static int +mi_test_path(const char *path, size_t len) +{ + if (len == 0) + return 0; + else if (len == 1 && path[0] == '.') + return 0; + else if (len == 2 && path[0] == '.' && path[1] == '.') + return 0; + else if (memchr(path, '/', len) != NULL) + return 0; + return 1; +} - tp->total_length += tp->files[i].length; - i++; +static int +mi_test_files(const char *files) +{ + int fcount = 0; + const char *fdct = benc_first(files); + while (fdct != NULL) { + const char *plst; + const char *path; + int pcount = 0; + if (!benc_isdct(fdct)) + return 0; + if (benc_dget_int(fdct, "length") <= 0) + return 0; + if ((plst = benc_dget_lst(fdct, "path")) == NULL) + return 0; + path = benc_first(plst); + while (path != NULL) { + size_t plen; + const char *pstr = benc_mem(path, &plen, &path); + if (pstr == NULL || !mi_test_path(pstr, plen)) + return 0; + pcount++; } + if (pcount == 0) + return 0; + fcount++; + fdct = benc_next(fdct); } - else - goto out; -out: - if (err != 0) - clear_metainfo(tp); + return fcount > 0 ? 1 : 0; +} - return err; +static int +mi_test_announce_list(const char *alst) +{ + int lstcount = 0; + const char *t = benc_first(alst); + while (t != NULL && benc_islst(t)) { + int strcount = 0; + const char *s = benc_first(t); + while (s != NULL && benc_isstr(s)) { + strcount++; + s = benc_next(s); + } + if (strcount == 0) + return 0; + lstcount++; + t = benc_next(t); + } + return lstcount > 0 ? 1 : 0; } int -load_metainfo(const char *path, off_t size, int mem_hashes, - struct metainfo **res) -{ - char *buf; - int fd, err = 0; - - if ((fd = open(path, O_RDONLY)) == -1) - return errno; - - if (size <= 0) { - struct stat sb; - if (fstat(fd, &sb) == -1) { - close(fd); - return errno; - } else - size = sb.st_size; - } +mi_test(const char *p, size_t size) +{ + const char *info; + const char *alst; + const char *pieces; + const char *files; + const char *fdct; + const char *name; + size_t slen, npieces; + off_t length = 0, piece_length; - if ((buf = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) - err = errno; - close(fd); + if (benc_validate(p, size) != 0 || !benc_isdct(p)) + return 0; - if (err == 0) - err = benc_validate(buf, size); + if ((alst = benc_dget_any(p, "announce-list")) != NULL) { + if (!benc_islst(alst)) + return 0; + if (!mi_test_announce_list(alst)) + return 0; + } else if (benc_dget_mem(p, "announce", NULL) == NULL) + return 0; + + if ((info = benc_dget_dct(p, "info")) == NULL) + return 0; + if ((name = benc_dget_mem(info, "name", &slen)) != NULL) + if (!mi_test_path(name, slen)) + return 0; + if ((piece_length = benc_dget_int(info, "piece length")) <= 0) + return 0; + if ((pieces = benc_dget_mem(info, "pieces", &slen)) == NULL || + slen % 20 != 0) + return 0; + npieces = slen / 20; + if ((length = benc_dget_int(info, "length")) != 0) { + if (length < 0 || benc_dget_any(info, "files") != NULL) + return 0; + } else { + if ((files = benc_dget_lst(info, "files")) == NULL) + return 0; + if (!mi_test_files(files)) + return 0; + fdct = benc_first(files); + while (fdct != NULL) { + length += benc_dget_int(fdct, "length"); + fdct = benc_next(fdct); + } + } + if (length < (npieces - 1) * piece_length || + length > npieces * piece_length) + return 0; + return 1; +} - if (err == 0) - if ((*res = calloc(1, sizeof(**res))) == NULL) - err = ENOMEM; - if (err == 0) - if ((err = fill_metainfo(buf, *res, mem_hashes)) != 0) - free(*res); +char * +mi_load(const char *path, size_t *size) +{ + void *res = NULL; + size_t mi_size = (1 << 21); - munmap(buf, size); - return err; + if ((errno = read_whole_file(&res, &mi_size, path)) != 0) + return NULL; + if (!mi_test(res, mi_size)) { + free(res); + errno = EINVAL; + return NULL; + } + if (size != NULL) + *size = mi_size; + return res; } diff --git a/misc/metainfo.h b/misc/metainfo.h index 242bab3..ab5b0cf 100644 --- a/misc/metainfo.h +++ b/misc/metainfo.h @@ -1,28 +1,37 @@ #ifndef BTPD_METAINFO_H #define BTPD_METAINFO_H -struct fileinfo { +struct mi_file { char *path; off_t length; }; -struct metainfo { - char *name; - char *announce; - uint8_t info_hash[20]; - uint8_t (*piece_hash)[20]; - unsigned pieces_off; - uint32_t npieces; - off_t piece_length; - off_t total_length; - unsigned nfiles; - struct fileinfo *files; +struct mi_tier { + int nurls; + char **urls; }; -int fill_fileinfo(const char *fdct, struct fileinfo *fip); -int fill_metainfo(const char *base, struct metainfo *mip, int mem_hashes); -void clear_metainfo(struct metainfo *mip); -void print_metainfo(struct metainfo *mip); -int load_metainfo(const char *path, off_t size, int mem_hashes, struct metainfo **res); +struct mi_announce { + int ntiers; + struct mi_tier *tiers; +}; + +char *mi_name(const char *p); +uint8_t *mi_info_hash(const char *p, uint8_t *hash); +uint8_t *mi_hashes(const char *p); +int mi_simple(const char *p); +size_t mi_npieces(const char *p); +off_t mi_total_length(const char *p); +off_t mi_piece_length(const char *p); + +struct mi_announce *mi_announce(const char *p); +void mi_free_announce(struct mi_announce *ann); + +size_t mi_nfiles(const char *p); +struct mi_file *mi_files(const char *p); +void mi_free_files(unsigned nfiles, struct mi_file *files); + +int mi_test(const char *p, size_t size); +char *mi_load(const char *path, size_t *size); #endif