commit 11db688588b8740c3c61479ba61f927aa7f4b234
parent 7d546dd306c2f101d6cc41d40db3dc1128c22df2
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Fri, 18 Dec 2015 23:47:49 +0100
add support for announce-list (multi-tracker) and other improvements
- add support for announce-list: list of list of strings, see BEP 12.
they are a list of trackers with a list of urls of possible fallbacks.
- when announce-list is set announce is not used as recommended by the spec.
- unify announce-list and announce.
other improvements:
- clean torrent struct (calloc).
- consistent error messages.
- free some buffers (bstr2str).
Diffstat:
M | sbtd.h | | | 8 | +++++++- |
M | torrent.c | | | 117 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- |
2 files changed, 95 insertions(+), 30 deletions(-)
diff --git a/sbtd.h b/sbtd.h
@@ -30,13 +30,19 @@ struct file {
char *path;
};
+struct announce {
+ char **urls;
+ size_t len;
+};
+
struct torrent {
char *buf;
size_t buflen;
struct ben *ben;
struct ben *info;
struct ben *pieces;
- char *announce;
+ struct announce *announcers;
+ size_t nannouncers;
char *filename;
struct file *files;
size_t nfiles;
diff --git a/torrent.c b/torrent.c
@@ -36,9 +36,15 @@ dumptorrent(struct torrent *t)
uint8_t md[20];
char hex[41];
long long i;
- size_t n;
+ size_t n, m;
- printf("announce: %s\n", t->announce);
+ printf("announcers:\n");
+ for (n = 0; n < t->nannouncers; n++) {
+ printf("%zu: announcer\n", n);
+ for (m = 0; m < t->announcers[n].len; m++)
+ printf("\t%zu: %s\n", m,
+ t->announcers[n].urls[m]);
+ }
if (t->filename[0])
printf("directory: %s\n", t->filename);
@@ -66,10 +72,10 @@ loadtorrent(char *f)
{
struct torrent *t;
struct ben *files, *file;
- struct ben *length, *path, *tmp;
- size_t n, pathlen, totallen = 0;
+ struct ben *length, *path, *tmp, *a;
+ size_t i, j, pathlen, totallen = 0;
- t = emalloc(sizeof(*t));
+ t = ecalloc(1, sizeof(*t));
if (readfile(f, &t->buf, &t->buflen) < 0) {
warnx("failed to read %s", f);
goto err0;
@@ -95,11 +101,49 @@ loadtorrent(char *f)
goto err2;
}
- if (!dlookstr(t->ben, "announce")) {
+ if ((tmp = dlookstr(t->ben, "announce-list")) && tmp->len) {
+ if (tmp->type != 'l') {
+ warnx("announce-list must be of type list");
+ goto err3;
+ }
+ t->announcers = ecalloc(tmp->len, sizeof(struct announce));
+
+ for (i = 0; tmp; tmp = tmp->next, i++, t->nannouncers++) {
+ if (tmp->v->type != 'l') {
+ warnx("announce-list item must be of type list");
+ goto err3;
+ }
+ if (!tmp->v->len)
+ continue;
+ t->announcers[i].urls = ecalloc(tmp->v->len, sizeof(char *));
+ for (a = tmp->v, j = 0; a; a = a->next, j++, t->announcers[i].len++) {
+ if (a->v->type != 's') {
+ warnx("announce item must be of type string");
+ goto err3;
+ }
+ t->announcers[i].urls[j] = bstr2str(a->v);
+ }
+ }
+ } else if ((tmp = dlookstr(t->ben, "announce"))) {
+ t->announcers = ecalloc(tmp->len, sizeof(struct announce));
+ t->nannouncers = 1;
+ t->announcers[0].len = 1;
+ t->announcers[0].urls = ecalloc(1, sizeof(char *));
+ t->announcers[0].urls[0] = bstr2str(tmp);
+ } else {
warnx("no announce field in %s", f);
goto err2;
}
- t->announce = bstr2str(dlookstr(t->ben, "announce"));
+
+ if (!dlookstr(t->info, "piece length")) {
+ warnx("no piece length field in %s", f);
+ goto err2;
+ }
+ t->piecelen = dlookstr(t->info, "piece length")->i;
+ if (t->piecelen <= 0) {
+ warnx("piecelen is <= 0 in %s", f);
+ goto err2;
+ }
if (!dlookstr(t->info, "name")) {
warnx("no filename field in %s", f);
@@ -110,13 +154,12 @@ loadtorrent(char *f)
/* multi-file mode or single file ? */
if ((files = dlookstr(t->info, "files"))) {
if (files->type != 'l') {
- warnx("invalid: \"files\" not a list");
+ warnx("files must be of type list");
goto err2;
}
- t->nfiles = files->len;
t->files = ecalloc(files->len, sizeof(struct file));
- for (file = files, n = 0; file; file = file->next, n++) {
+ for (file = files; file; file = file->next, t->nfiles++) {
if (!(length = dlookstr(file->v, "length"))) {
warnx("no length field in %s", f);
goto err3;
@@ -130,7 +173,7 @@ loadtorrent(char *f)
goto err3;
}
totallen += (size_t)length->i;
- t->files[n].len = (size_t)length->i;
+ t->files[t->nfiles].len = (size_t)length->i;
if (!(path = dlookstr(file->v, "path")))
break;
@@ -145,11 +188,11 @@ loadtorrent(char *f)
}
pathlen += tmp->v->len + 1;
}
- t->files[n].path = ecalloc(1, pathlen);
+ t->files[t->nfiles].path = ecalloc(1, pathlen);
for (tmp = path; tmp; tmp = tmp->next) {
- strlcat(t->files[n].path, tmp->v->s, pathlen);
+ strlcat(t->files[t->nfiles].path, tmp->v->s, pathlen);
if (tmp != path && tmp->next)
- strlcat(t->files[n].path, "/", pathlen);
+ strlcat(t->files[t->nfiles].path, "/", pathlen);
}
}
} else {
@@ -165,35 +208,35 @@ loadtorrent(char *f)
warnx("length is < 0 in %s", f);
goto err2;
}
- t->nfiles = n = 1;
+ t->nfiles = 1;
t->files = ecalloc(1, sizeof(struct file));
t->files[0].len = length->i;
- t->files[0].path = strdup(t->filename);
- t->filename = "";
+ t->files[0].path = t->filename;
+ t->filename = strdup("");
totallen = (size_t)length->i;
}
- if (!dlookstr(t->info, "piece length")) {
- warnx("no piece length field in %s", f);
- goto err2;
- }
- t->piecelen = dlookstr(t->info, "piece length")->i;
- if (t->piecelen <= 0) {
- warnx("piecelen is <= 0 in %s", f);
- goto err2;
- }
-
t->npieces = (totallen + t->piecelen - 1) / t->piecelen;
t->piecebm = newbit(t->npieces);
calcinfohash(t);
return t;
+
err3:
- while (n--)
- free(t->files[n].path);
+ i = t->nannouncers;
+ while(i--) {
+ j = t->announcers[i].len;
+ while (j--)
+ free(t->announcers[i].urls[j]);
+ free(&(t->announcers[i]));
+ }
+ free(t->announcers);
+ while (t->nfiles--)
+ free(t->files[t->nfiles].path);
free(t->files);
err2:
+ free(t->filename);
bfree(t->ben);
err1:
free(t->buf);
@@ -205,8 +248,24 @@ err0:
void
unloadtorrent(struct torrent *t)
{
+ size_t i, j;
+
if (!t)
return;
+
+ i = t->nannouncers;
+ while(i--) {
+ j = t->announcers[i].len;
+ while (j--)
+ free(t->announcers[i].urls[j]);
+ free(&(t->announcers[i]));
+ }
+ free(t->announcers);
+
+ for (i = 0; i < t->nfiles; i++)
+ free(t->files[i].path);
+ free(t->files);
+
bfree(t->ben);
free(t->buf);
free(t->piecebm);