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 19 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 <errno.h>
#include <fcntl.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>


#include <openssl/sha.h> #include <openssl/sha.h>


@@ -19,6 +12,7 @@
/* /*
* d * d
* announce = url * announce = url
* announce-list = l l url ... e ... e
* info = d * info = d
* name = advisory file/dir save name * name = advisory file/dir save name
* piece length = power of two length of each block * 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 void
print_metainfo(struct metainfo *tp) mi_free_announce(struct mi_announce *ann)
{ {
unsigned i; if (ann->tiers != NULL) {

for (int ti = 0; ti < ann->ntiers; ti++)
printf("Info hash: "); if (ann->tiers[ti].urls != NULL) {
for (i = 0; i < 20; i++) for (int ui = 0; ui < ann->tiers[ti].nurls; ui++)
printf("%.2x", tp->info_hash[i]); if (ann->tiers[ti].urls[ui] != NULL)
printf("\n"); free(ann->tiers[ti].urls[ui]);
printf("Tracker URL: %s\n", tp->announce); free(ann->tiers[ti].urls);
printf("Piece length: %lld\n", (long long)tp->piece_length); }
printf("Number of pieces: %u\n", tp->npieces); free(ann->tiers);
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);
} }
printf("Total length: %lld\n\n", (long long)tp->total_length); free(ann);
} }


static int static void
check_path(const char *path, size_t len) mi_shuffle_announce(struct mi_announce *ann)
{ {
if (len == 0) for (int i = 0; i < ann->ntiers; i++) {
return 0; for (int j = 0; j < ann->tiers[i].nurls - 1; j++) {
else if (len == 1 && path[0] == '.') char *tmp = ann->tiers[i].urls[j];
return 0; int ri = rand_between(j, ann->tiers[i].nurls - 1);
else if (len == 2 && path[0] == '.' && path[1] == '.') ann->tiers[i].urls[j] = ann->tiers[i].urls[ri];
return 0; ann->tiers[i].urls[ri] = tmp;
else if (memchr(path, '/', len) != NULL) }
return 0; }
return 1;
} }


int struct mi_announce *
fill_fileinfo(const char *fdct, struct fileinfo *tfp) mi_announce(const char *p)
{ {
size_t npath, plen, len; int ti, ui;
const char *plst, *iter, *str; const char *alst, *ulst, *url;
struct mi_announce *res;


if (!benc_dct_chk(fdct, 2, BE_INT, 1, "length", BE_LST, 1, "path")) if ((res = calloc(1, sizeof(*res))) == NULL)
return EINVAL; return NULL;


tfp->length = benc_dget_int(fdct, "length"); if ((alst = benc_dget_lst(p, "announce-list")) != NULL) {
plst = benc_dget_lst(fdct, "path"); 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) { while (iter != NULL) {
if (!benc_isstr(iter)) benc_mem(iter, &len, &iter);
return EINVAL; npaths++;
str = benc_mem(iter, &len, &iter);
if (!check_path(str, len))
return EINVAL;
npath++;
plen += len; plen += len;
} }
if (npath == 0)
return EINVAL;


if ((tfp->path = malloc(plen + (npath - 1) + 1)) == NULL) if ((res = malloc(plen + (npaths - 1) + 1)) == NULL)
return ENOMEM; return NULL;


iter = benc_first(plst); iter = benc_first(plst);
str = benc_mem(iter, &len, &iter); str = benc_mem(iter, &len, &iter);
memcpy(tfp->path, str, len); bcopy(str, res, len);
plen = len; plen = len;
npath--; npaths--;
while (npath > 0) { while (npaths > 0) {
tfp->path[plen++] = '/'; res[plen] = '/';
plen++;
str = benc_mem(iter, &len, &iter); str = benc_mem(iter, &len, &iter);
memcpy(tfp->path + plen, str, len); bcopy(str, res + plen, len);
plen += len; plen += len;
npath--; npaths--;
} }
tfp->path[plen] = '\0'; res[plen] = '\0';
return 0; return res;
} }


void void
clear_metainfo(struct metainfo *mip) mi_free_files(unsigned nfiles, struct mi_file *files)
{ {
int i; for (unsigned i = 0; i < nfiles; i++)
if (mip->piece_hash != NULL) if (files[i].path != NULL)
free(mip->piece_hash); free(files[i].path);
if (mip->announce != NULL) free(files);
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);
} }


int struct mi_file *
fill_metainfo(const char *bep, struct metainfo *tp, int mem_hashes) mi_files(const char *p)
{ {
size_t len; struct mi_file *fi;
int err = 0; const char *info = benc_dget_dct(p, "info");
const char *base_addr = bep; const char *files = benc_dget_lst(info, "files");
const char *hash_addr; if (files != NULL) {

int i = 0;
if (!benc_dct_chk(bep, 5, unsigned nfiles = benc_nelems(files);
BE_STR, 1, "announce", const char *fdct = benc_first(files);
BE_DCT, 1, "info", if ((fi = calloc(nfiles, sizeof(*fi))) == NULL)
BE_INT, 2, "info", "piece length", return NULL;
BE_STR, 2, "info", "pieces", for (fdct = benc_first(files); fdct != NULL; fdct = benc_next(fdct)) {
BE_STR, 2, "info", "name")) fi[i].length = benc_dget_int(fdct, "length");
return EINVAL; fi[i].path = mi_filepath(benc_dget_lst(fdct, "path"));

if (fi[i].path == NULL) {
if ((tp->announce = benc_dget_str(bep, "announce", NULL)) == NULL) { mi_free_files(nfiles, fi);
err = ENOMEM; return NULL;
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;
} }
} else { i++;
err = ENOMEM;
goto out;
} }
} else if (benc_dct_chk(bep, 1, BE_LST, 1, "files")) { } else {
int i; if ((fi = calloc(1, sizeof(*fi))) == NULL)
const char *flst, *fdct; return NULL;

fi[0].length = benc_dget_int(info, "length");
flst = benc_dget_lst(bep, "files"); fi[0].path = benc_dget_str(info, "name", NULL);
tp->nfiles = benc_nelems(flst); if (fi[0].path == NULL) {
if (tp->nfiles < 1) { free(fi);
err = EINVAL; return NULL;
goto out;
} }
tp->files = calloc(tp->nfiles, sizeof(struct fileinfo)); }

return fi;
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;
}


if ((err = fill_fileinfo(fdct, &tp->files[i])) != 0) static int
goto out; 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; static int
i++; 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 return fcount > 0 ? 1 : 0;
goto out; }
out:
if (err != 0)
clear_metainfo(tp);


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 int
load_metainfo(const char *path, off_t size, int mem_hashes, mi_test(const char *p, size_t size)
struct metainfo **res) {
{ const char *info;
char *buf; const char *alst;
int fd, err = 0; const char *pieces;

const char *files;
if ((fd = open(path, O_RDONLY)) == -1) const char *fdct;
return errno; const char *name;

size_t slen, npieces;
if (size <= 0) { off_t length = 0, piece_length;
struct stat sb;
if (fstat(fd, &sb) == -1) {
close(fd);
return errno;
} else
size = sb.st_size;
}


if ((buf = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) if (benc_validate(p, size) != 0 || !benc_isdct(p))
err = errno; return 0;
close(fd);


if (err == 0) if ((alst = benc_dget_any(p, "announce-list")) != NULL) {
err = benc_validate(buf, size); 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) char *
if ((*res = calloc(1, sizeof(**res))) == NULL) mi_load(const char *path, size_t *size)
err = ENOMEM; {
if (err == 0) void *res = NULL;
if ((err = fill_metainfo(buf, *res, mem_hashes)) != 0) size_t mi_size = (1 << 21);
free(*res);


munmap(buf, size); if ((errno = read_whole_file(&res, &mi_size, path)) != 0)
return err; 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 #ifndef BTPD_METAINFO_H
#define BTPD_METAINFO_H #define BTPD_METAINFO_H


struct fileinfo { struct mi_file {
char *path; char *path;
off_t length; off_t length;
}; };


struct metainfo { struct mi_tier {
char *name; int nurls;
char *announce; char **urls;
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;
}; };


int fill_fileinfo(const char *fdct, struct fileinfo *fip); struct mi_announce {
int fill_metainfo(const char *base, struct metainfo *mip, int mem_hashes); int ntiers;
void clear_metainfo(struct metainfo *mip); struct mi_tier *tiers;
void print_metainfo(struct metainfo *mip); };
int load_metainfo(const char *path, off_t size, int mem_hashes, struct metainfo **res); 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 #endif

||||||
x
 
000:0
Loading…
Cancel
Save