torrentd

simple torrent daemon
git clone git@git.2f30.org/torrentd.git
Log | Files | Refs | LICENSE

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:
Msbtd.h | 8+++++++-
Mtorrent.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);