Browse Source

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.
master
Richard Nyberg 18 years ago
parent
commit
cd0eb82f20
2 changed files with 348 additions and 203 deletions
  1. +322
    -186
      misc/metainfo.c
  2. +26
    -17
      misc/metainfo.h

+ 322
- 186
misc/metainfo.c View File

@@ -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;
}

+ 26
- 17
misc/metainfo.h View File

@@ -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

Loading…
Cancel
Save