diff options
| author | Richard Nyberg <rnyberg@murmeldjur.se> | 2006-09-12 15:12:34 +0000 |
|---|---|---|
| committer | Richard Nyberg <rnyberg@murmeldjur.se> | 2006-09-12 15:12:34 +0000 |
| commit | cd0eb82f20b4b0186207b8f5653b83dde83ca03c (patch) | |
| tree | 6bca76b9777c7e2dbd3018d4397e468bce72c0ff /misc/metainfo.c | |
| parent | fc20273a800fa62dfd801018761c30716ced920c (diff) | |
| download | btpd-cd0eb82f20b4b0186207b8f5653b83dde83ca03c.tar.gz btpd-cd0eb82f20b4b0186207b8f5653b83dde83ca03c.zip | |
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.
Diffstat (limited to 'misc/metainfo.c')
| -rw-r--r-- | misc/metainfo.c | 508 |
1 files changed, 322 insertions, 186 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 <sys/types.h> -#include <sys/mman.h> -#include <sys/stat.h> - #include <errno.h> -#include <fcntl.h> #include <inttypes.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include <openssl/sha.h> @@ -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; } |