- #include <sys/types.h>
-
- #include <errno.h>
- #include <inttypes.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include <openssl/sha.h>
-
- #include "benc.h"
- #include "metainfo.h"
- #include "subr.h"
-
- /*
- * 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
- * pieces = 20b of sha1-hash * num of pieces
- * length = length of file in bytes in single file download
- * files = l d
- * length = length of file in bytes
- * path = l path components
- *
- */
-
- 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
- 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);
- }
- free(ann);
- }
-
- static void
- mi_shuffle_announce(struct mi_announce *ann)
- {
- 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;
- }
- }
- }
-
- struct mi_announce *
- mi_announce(const char *p)
- {
- int ti, ui;
- const char *alst, *ulst, *url;
- struct mi_announce *res;
-
- if ((res = calloc(1, sizeof(*res))) == NULL)
- return NULL;
-
- 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);
-
- while (iter != NULL) {
- benc_mem(iter, &len, &iter);
- npaths++;
- plen += len;
- }
-
- if ((res = malloc(plen + (npaths - 1) + 1)) == NULL)
- return NULL;
-
- iter = benc_first(plst);
- str = benc_mem(iter, &len, &iter);
- bcopy(str, res, len);
- plen = len;
- npaths--;
- while (npaths > 0) {
- res[plen] = '/';
- plen++;
- str = benc_mem(iter, &len, &iter);
- bcopy(str, res + plen, len);
- plen += len;
- npaths--;
- }
- res[plen] = '\0';
- return res;
- }
-
- void
- 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);
- }
-
- 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;
- }
- i++;
- }
- } 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;
- }
- }
- return fi;
- }
-
- 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;
- }
-
- 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);
- }
- return fcount > 0 ? 1 : 0;
- }
-
- 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
- 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 (benc_validate(p, size) != 0 || !benc_isdct(p))
- return 0;
-
- 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;
- }
-
- char *
- mi_load(const char *path, size_t *size)
- {
- char *res;
- size_t mi_size = (1 << 21);
-
- if ((res = read_file(path, NULL, &mi_size)) == NULL)
- return NULL;
- if (!mi_test(res, mi_size)) {
- free(res);
- errno = EINVAL;
- return NULL;
- }
- if (size != NULL)
- *size = mi_size;
- return res;
- }
|