about summary refs log tree commit diff
path: root/misc
diff options
context:
space:
mode:
authorRichard Nyberg <rnyberg@murmeldjur.se>2006-09-12 15:12:34 +0000
committerRichard Nyberg <rnyberg@murmeldjur.se>2006-09-12 15:12:34 +0000
commitcd0eb82f20b4b0186207b8f5653b83dde83ca03c (patch)
tree6bca76b9777c7e2dbd3018d4397e468bce72c0ff /misc
parentfc20273a800fa62dfd801018761c30716ced920c (diff)
downloadbtpd-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')
-rw-r--r--misc/metainfo.c508
-rw-r--r--misc/metainfo.h43
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 <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;
 }
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