Commits

Robert Lowry  committed 940c8a8

first commit

  • Participants

Comments (0)

Files changed (8)

+MIT/X Consortium License
+
+(C)opyright MMX Robert Lowry <robertwlowry@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+# ptorrent - pico torrent
+
+include config.mk
+
+BIN = ptorrent
+SRC = ptorrent.c sha1.c bencode.c
+OBJ = ${SRC:.c=.o}
+
+all: options ${BIN}
+
+options:
+	@echo ptorrent build options:
+	@echo "CFLAGS	= ${CFLAGS}"
+	@echo "LDFLAGS	= ${LDFLAGS}"
+	@echo "CC	= ${CC}"
+
+.c.o:
+	@echo CC $<
+	@${CC} -c ${CFLAGS} $<
+
+.o:
+	@echo CC -o $@
+	@${CC} -o $@ $< ${LDFLAGS}
+
+${OBJ}: config.mk
+
+${BIN}: ${OBJ}
+	@echo CC -o $@
+	@${CC} -o $@ ${OBJ} ${LDFLAGS}
+
+clean:
+	@echo cleaning
+	@rm -f ${BIN} ${OBJ} ptorrent-${VERSION}.tar.gz 
+
+dist: clean
+	@echo creating ptorrent-${VERSION}.tar.gz
+	@mkdir -p ptorrent-${VERSION}
+	@cp -r LICENSE Makefile README config.mk ptorrent.1 ptorrent-${VERSION}
+	@tar -cf ptorrent-${VERSION}.tar ptorrent-${VERSION}
+	@gzip ptorrent-${VERSION}.tar
+	@rm -rf ptorrent-${VERSION}
+
+install: all
+	@echo installing executable files to ${DESTDIR}${PREFIX}/bin
+	@mkdir -p ${DESTDIR}${PREFIX}/bin
+	@for bin in ${BIN}; do cp -f $$bin ${DESTDIR}${PREFIX}/bin/$$bin; done
+	@for bin in ${BIN}; do chmod 755   ${DESTDIR}${PREFIX}/bin/$$bin; done
+	@echo installing man pages to ${DESTDIR}${MANPREFIX}/man1
+	@mkdir -p ${DESTDIR}${MANPREFIX}/man1
+	@sed "s/VERSION/${VERSION}/g" < ptorrent.1 > ${DESTDIR}${MANPREFIX}/man1/ptorrent.1
+	@chmod 644 ${DESTDIR}${MANPREFIX}/man1/ptorrent.1
+
+uninstall:
+	@echo removing executable friles from ${DESTDIR}${PREFIX}/bin
+	@for bin in ${BIN}; do rm -f ${DESTDIR}${PREFIX}/bin/$$bin; done
+	@echo removing man pages from ${DESTDIR}${MANPREFIX}/man1
+	@rm -rf ${DESTDIR}${MANPREFIX}/man1/ptorrent.1
+
+.PHONY: all options clean dist install uninstall
+/*
+ * C implementation of a bencode decoder.
+ * This is the format defined by BitTorrent:
+ *  http://wiki.theory.org/BitTorrentSpecification#bencoding
+ *
+ * The only external requirements are a few [standard] function calls and
+ * the long long type.  Any sane system should provide all of these things.
+ *
+ * See the bencode.h header file for usage information.
+ *
+ * This is released into the public domain:
+ *  http://en.wikipedia.org/wiki/Public_Domain
+ *
+ * Written by:
+ *   Mike Frysinger <vapier@gmail.com>
+ * And improvements from:
+ *   Gilles Chanteperdrix <gilles.chanteperdrix@xenomai.org>
+ * be_dict_lookup() and be_sha1_node() and small improvements by:
+ *   Robert Lowry <robertwlowry@gmail.com>
+ */
+
+/*
+ * This implementation isn't optimized at all as I wrote it to support a bogus
+ * system.  I have no real interest in this format.  Feel free to send me
+ * patches (so long as you don't copyright them and you release your changes
+ * into the public domain as well).
+ */
+
+#include <stdio.h>  /* fprintf() */
+#include <stdlib.h> /* malloc() realloc() free() strtoll() */
+#include <string.h> /* memset() */
+
+#include "bencode.h"
+#include "sha1.h"
+
+#define uchar unsigned char
+
+static void *emalloc(size_t size) {
+	void *p = malloc(size);
+	if(p == NULL) {
+		fprintf(stderr,"error: cannot allocate memory\n");
+		exit(1);
+	}
+	return p;
+}
+
+static be_node *be_alloc(be_type type)
+{
+	be_node *ret = emalloc(sizeof(*ret));
+	memset(ret, 0x00, sizeof(*ret));
+	ret->type = type;
+	return ret;
+}
+
+static long long _be_decode_int(const char **data, long long *data_len)
+{
+	char *endp;
+	long long ret = strtoll(*data, &endp, 10);
+	*data_len -= (endp - *data);
+	*data = endp;
+	return ret;
+}
+
+long long be_str_len(be_node *node)
+{
+	long long ret = 0;
+	if (node->val.s)
+		memcpy(&ret, node->val.s - sizeof(ret), sizeof(ret));
+	return ret;
+}
+
+static char *_be_decode_str(const char **data, long long *data_len)
+{
+	long long sllen = _be_decode_int(data, data_len);
+	long slen = sllen;
+	unsigned long len;
+	char *ret = NULL;
+
+	/* slen is signed, so negative values get rejected */
+	if (sllen < 0)
+		return ret;
+
+	/* reject attempts to allocate large values that overflow the
+	 * size_t type which is used with malloc()
+	 */
+	if (sizeof(long long) != sizeof(long))
+		if (sllen != slen)
+			return ret;
+
+	/* make sure we have enough data left */
+	if (sllen > *data_len - 1)
+		return ret;
+
+	/* switch from signed to unsigned so we don't overflow below */
+	len = slen;
+
+	if (**data == ':') {
+		char *_ret = emalloc(sizeof(sllen) + len + 1);
+		memcpy(_ret, &sllen, sizeof(sllen));
+		ret = _ret + sizeof(sllen);
+		memcpy(ret, *data + 1, len);
+		ret[len] = '\0';
+		*data += len + 1;
+		*data_len -= len + 1;
+	}
+	return ret;
+}
+
+static be_node *_be_decode(const char **data, long long *data_len)
+{
+	be_node *ret = NULL;
+
+	if (!*data_len)
+		return ret;
+
+	switch (**data) {
+		/* lists */
+		case 'l': {
+			unsigned int i = 0;
+
+			ret = be_alloc(BE_LIST);
+
+			--(*data_len);
+			++(*data);
+			while (**data != 'e') {
+				ret->val.l = realloc(ret->val.l, (i + 2) * sizeof(*ret->val.l));
+				ret->val.l[i] = _be_decode(data, data_len);
+				++i;
+			}
+			--(*data_len);
+			++(*data);
+
+			ret->val.l[i] = NULL;
+
+			return ret;
+		}
+
+		/* dictionaries */
+		case 'd': {
+			unsigned int i = 0;
+
+			ret = be_alloc(BE_DICT);
+
+			--(*data_len);
+			++(*data);
+			while (**data != 'e') {
+				ret->val.d = realloc(ret->val.d, (i + 2) * sizeof(*ret->val.d));
+				ret->val.d[i].key = _be_decode_str(data, data_len);
+				ret->val.d[i].val = _be_decode(data, data_len);
+				++i;
+			}
+			--(*data_len);
+			++(*data);
+
+			ret->val.d[i].val = NULL;
+
+			return ret;
+		}
+
+		/* integers */
+		case 'i': {
+			ret = be_alloc(BE_INT);
+
+			--(*data_len);
+			++(*data);
+			ret->val.i = _be_decode_int(data, data_len);
+			if (**data != 'e')
+				return NULL;
+			--(*data_len);
+			++(*data);
+
+			return ret;
+		}
+
+		default:
+			/* byte strings (switch ranges aren't standard) */
+			if(**data >= '0' && **data <= '9') {
+				ret = be_alloc(BE_STR);
+
+				ret->val.s = _be_decode_str(data, data_len);
+
+				return ret;
+			}
+			/* invalid */
+			break;
+	}
+
+	return ret;
+}
+
+be_node *be_decoden(const char *data, long long len)
+{
+	return _be_decode(&data, &len);
+}
+
+be_node *be_decode(const char *data)
+{
+	return be_decoden(data, strlen(data));
+}
+
+be_node *be_dict_lookup(be_node *dict, const char *key) {
+	be_dict *d;
+
+	if(dict->type != BE_DICT)
+		return NULL;
+
+	for(d = dict->val.d; d->val; d++)
+		if(!strcmp(key,d->key))
+			return d->val;
+
+	return NULL;
+}
+
+static void _be_sha1_node(SHA_CTX *c, be_node *node) {
+	char buf[1024];
+	int i;
+
+	switch(node->type) {
+		case BE_STR:
+			snprintf(buf,sizeof(buf),"%lli:",be_str_len(node));
+			SHA1_Update(c,(uchar*)buf,strlen(buf));
+			SHA1_Update(c,(uchar*)node->val.s,be_str_len(node));
+			break;
+		case BE_INT:
+			snprintf(buf,sizeof(buf),"i%llie",node->val.i);
+			SHA1_Update(c,(uchar*)buf,strlen(buf));
+			break;
+		case BE_LIST:
+			SHA1_Update(c,(uchar*)"l",1);
+			for(i = 0; node->val.l[i]; i++)
+				_be_sha1_node(c,node->val.l[i]);
+			SHA1_Update(c,(uchar*)"e",1);
+			break;
+		case BE_DICT:
+			SHA1_Update(c,(uchar*)"d",1);
+			for(i = 0; node->val.d[i].val; i++) {
+				snprintf(buf,sizeof(buf),"%i:",strlen(node->val.d[i].key));
+				SHA1_Update(c,(uchar*)buf,strlen(buf));
+				SHA1_Update(c,(uchar*)node->val.d[i].key,strlen(node->val.d[i].key));
+				_be_sha1_node(c,node->val.d[i].val);
+			}
+			SHA1_Update(c,(uchar*)"e",1);
+			break;
+	}
+}
+
+unsigned char *be_sha1_node(be_node *node, unsigned char *buf) {
+	SHA_CTX c;
+
+	SHA1_Init(&c);
+	_be_sha1_node(&c,node);
+	SHA1_Final(buf,&c);
+
+	return buf;
+}
+
+static inline void _be_free_str(char *str)
+{
+	if (str)
+		free(str - sizeof(long long));
+}
+void be_free(be_node *node)
+{
+	switch (node->type) {
+		case BE_STR:
+			_be_free_str(node->val.s);
+			break;
+
+		case BE_INT:
+			break;
+
+		case BE_LIST: {
+			unsigned int i;
+			for (i = 0; node->val.l[i]; ++i)
+				be_free(node->val.l[i]);
+			free(node->val.l);
+			break;
+		}
+
+		case BE_DICT: {
+			unsigned int i;
+			for (i = 0; node->val.d[i].val; ++i) {
+				_be_free_str(node->val.d[i].key);
+				be_free(node->val.d[i].val);
+			}
+			free(node->val.d);
+			break;
+		}
+	}
+	free(node);
+}
+
+#ifdef BE_DEBUG
+#include <stdio.h>
+#include <stdint.h>
+
+static void _be_dump_indent(ssize_t indent)
+{
+	while (indent-- > 0)
+		printf("    ");
+}
+static void _be_dump(be_node *node, ssize_t indent)
+{
+	size_t i;
+
+	_be_dump_indent(indent);
+	indent = abs(indent);
+
+	switch (node->type) {
+		case BE_STR:
+			printf("str = %s (len = %lli)\n", node->val.s, be_str_len(node));
+			break;
+
+		case BE_INT:
+			printf("int = %lli\n", node->val.i);
+			break;
+
+		case BE_LIST:
+			puts("list [");
+
+			for (i = 0; node->val.l[i]; ++i)
+				_be_dump(node->val.l[i], indent + 1);
+
+			_be_dump_indent(indent);
+			puts("]");
+			break;
+
+		case BE_DICT:
+			puts("dict {");
+
+			for (i = 0; node->val.d[i].val; ++i) {
+				_be_dump_indent(indent + 1);
+				printf("%s => ", node->val.d[i].key);
+				_be_dump(node->val.d[i].val, -(indent + 1));
+			}
+
+			_be_dump_indent(indent);
+			puts("}");
+			break;
+	}
+}
+void be_dump(be_node *node)
+{
+	_be_dump(node, 0);
+}
+#endif
+/*
+ * C implementation of a bencode decoder.
+ * This is the format defined by BitTorrent:
+ *  http://wiki.theory.org/BitTorrentSpecification#bencoding
+ *
+ * The only external requirements are a few [standard] function calls and
+ * the long long type.  Any sane system should provide all of these things.
+ *
+ * This is released into the public domain.
+ * Written by Mike Frysinger <vapier@gmail.com>.
+ */
+
+/* USAGE:
+ *  - pass the string full of the bencoded data to be_decode()
+ *  - parse the resulting tree however you like
+ *  - call be_free() on the tree to release resources
+ */
+
+#ifndef _BENCODE_H
+#define _BENCODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	BE_STR,
+	BE_INT,
+	BE_LIST,
+	BE_DICT,
+} be_type;
+
+struct be_dict;
+struct be_node;
+
+/*
+ * XXX: the "val" field of be_dict and be_node can be confusing ...
+ */
+
+typedef struct be_dict {
+	char *key;
+	struct be_node *val;
+} be_dict;
+
+typedef struct be_node {
+	be_type type;
+	union {
+		char *s;
+		long long i;
+		struct be_node **l;
+		struct be_dict *d;
+	} val;
+} be_node;
+
+extern long long be_str_len(be_node *node);
+extern be_node *be_decode(const char *bencode);
+extern be_node *be_decoden(const char *bencode, long long bencode_len);
+extern be_node *be_dict_lookup(be_node *dict, const char *key);
+extern unsigned char *be_sha1_node(be_node *node, unsigned char *buf);
+extern void be_free(be_node *node);
+extern void be_dump(be_node *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+# ptorrent version
+VERSION	= 0.1a
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = ${PREFIX}/share/man
+
+# includes and libs
+INCS = -I. -I/usr/include
+LIBS = -L/usr/lib -lc
+
+# flags
+CFLAGS = -g -Wall ${INCS} -DVERSION=\"$(VERSION)\"
+LDFLAGS = ${LIBS}
+
+# compiler
+CC = cc
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "bencode.h"
+#include "sha1.h"
+
+#define debug(...) if(debuglevel) fprintf(stderr,"ptorrent: "__VA_ARGS__)
+#define vprint(...) if(verbose) printf(__VA_ARGS__)
+
+#define PATH_MAX 1024
+#define HTTP_HOST_MAX 256
+#define HTTP_PATH_MAX 1024
+#define HTTP_PORT_MAX 7
+
+typedef struct Torrent Torrent;
+typedef struct Chunk Chunk;
+typedef struct Piece Piece;
+typedef struct File File;
+typedef struct Peer Peer;
+
+struct Torrent {
+	enum { T_NONE=0, T_SINGLE, T_MULTI } type;
+	unsigned char infohash[20];
+
+	/* meta-info */
+	char *announce;
+	char **announcelist;
+	char *comment, *creator;
+	time_t created;
+
+	/* info */
+	size_t piecelen;
+	size_t npieces;
+	char *name;
+	long long length;
+
+	File **files;
+	Piece **pieces;
+	Peer *peers;
+};
+
+struct Chunk {
+	int file;
+	size_t offset;
+	size_t length;
+	size_t poffset;
+	void *ptr;
+};
+
+struct Piece {
+	Torrent *torrent;
+	enum { S_UNKNOWN=0, S_BUNK, S_VALID } status;
+	unsigned char sha1[20];
+	Chunk **chunks;
+};
+
+struct File {
+	char *dir;
+	char *path;
+	long long length;
+	int fileref;
+	int fd;
+};
+
+struct Peer {
+	char *id;
+	char *ip;
+	int port;
+	Peer *next;
+};
+
+static void usage();
+static void eprint(const char *fmt, ...);
+static void *emalloc(size_t size);
+static void *emallocz(size_t size);
+static char *estrdup(const char *s);
+static void create_dirtree(const char *dir);
+static int tcpopen(const char *host, const char *port);
+static Torrent *parse_torrent_file(const char *filename);
+static Torrent *parse_torrent_buf(const char *bencode, size_t length);
+static void map_chunks(Torrent *t);
+static void check_file(File *f);
+static void open_file(File *f);
+static void close_file(File *f);
+static void load_piece(Piece *p);
+static void unload_piece(Piece *p);
+static void verify_piece(Piece *p);
+static void verify_torrent(Torrent *t);
+
+static int debuglevel = 0;
+static int verbose = 0;
+
+static long pagesize;
+
+static void 
+usage() {
+	eprint("ptorrent "VERSION" (c) MMX Robert Lowry\n"
+		"usage: ptorrent [-v|-d] <torrent>\n");
+}
+
+static void
+eprint(const char *fmt, ...) {
+	va_list ap;
+	char buf[512];
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "ptorrent: %s", buf);
+	if(fmt[0] && fmt[strlen(fmt) - 1] == ':')
+		fprintf(stderr, " %s\n", strerror(errno));
+	exit(1);
+}
+
+static void *
+emalloc(size_t size) {
+	void *ret = malloc(size);
+	if(ret == NULL) eprint("cannot allocate memory\n");
+	return ret;
+}
+
+static void *
+emallocz(size_t size) {
+	void *ret = emalloc(size);
+	memset(ret,0x00,size);
+	return ret;
+}
+
+static void *
+erealloc(void *p, size_t s) {
+	void *ret = realloc(p,s);
+	if(ret == NULL) eprint("cannot allocate memory\n");
+	return ret;
+}
+
+static char *
+estrdup(const char* s) {
+	char *ret = strdup(s);
+	if(ret == NULL) eprint("cannot allocate memory\n");
+	return ret;
+}
+
+static void
+create_dirtree(const char *dir) {
+	char tmp[PATH_MAX];
+	char *p = NULL;
+	size_t len;
+
+	snprintf(tmp,sizeof(tmp),"%s",dir);
+	len = strlen(tmp);
+	if(tmp[len - 1] == '/')
+		tmp[len - 1] = '\0';
+	for(p = tmp + 1; *p; p++)
+		if(*p == '/') {
+			*p = '\0';
+			mkdir(tmp, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+			*p = '/';
+		}
+	mkdir(tmp, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+}
+
+static int
+tcpopen(const char *host, const char *port) {
+	static struct addrinfo hints;
+	struct addrinfo *res, *r;
+	int fd;
+
+	memset(&hints, 0x00, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	if(getaddrinfo(host, port, &hints, &res) != 0)
+		return -1;
+	for(r = res; r; r = r->ai_next) {
+		if((fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) == -1)
+			continue;
+		if(connect(fd, r->ai_addr, r->ai_addrlen) == 0)
+			break;
+		close(fd);
+	}
+	freeaddrinfo(res);
+	if(!r) return -1;
+
+	return fd;
+}
+
+static Torrent *
+parse_torrent_file(const char *filename) {
+	Torrent *t;
+	char *buf;
+	FILE *f;
+	size_t size;
+
+	debug("loading torrent file %s\n",filename);
+
+	f = fopen(filename,"r");
+	if(f == NULL)
+		return NULL;
+	if(fgetc(f) != 'd') {
+		fclose(f);
+		return NULL;
+	}
+	fseek(f,0L,SEEK_END);
+	size = ftell(f);
+	fseek(f,0L,SEEK_SET);
+	buf = emalloc(size);
+	fread(buf,size,1,f);
+	fclose(f);
+	t = parse_torrent_buf(buf,size);
+	free(buf);
+	return t;
+}
+
+static Torrent *
+parse_torrent_buf(const char *bencode, size_t len) {
+	Torrent *t;
+	be_node *root, *info;
+	be_dict *d, *dd;
+	char path[PATH_MAX];
+	int i, j, k;
+
+	debug("parsing torrent file\n");
+
+	root = be_decoden(bencode,len);
+	if(!root) return NULL;
+	t = emallocz(sizeof(Torrent));
+	info = be_dict_lookup(root,"info");
+	if(info && root->type == BE_DICT && info->type == BE_DICT) {
+		be_sha1_node(info,t->infohash);
+		/* parse root dict */
+		for(d = root->val.d; d->val; d++) {
+			debug("parsing %s\n",d->key);
+			if(!strcmp("announce",d->key)) {
+				t->announce = estrdup(d->val->val.s);
+			} else if(!strcmp("announce-list",d->key)) {
+				k = 0;
+				for(i=0; d->val->val.l[i]; i++) {
+					for(j=0; d->val->val.l[i]->val.l[j]; j++) {
+						t->announcelist = erealloc(t->announcelist,(k + 2) * sizeof(char*));
+						t->announcelist[k] = estrdup(d->val->val.l[i]->val.l[j]->val.s);
+						k++;
+					}
+				}
+				t->announcelist[k] = NULL;
+			} else if(!strcmp("comment",d->key)) {
+				t->comment = estrdup(d->val->val.s);
+			} else if(!strcmp("created by",d->key)) {
+				t->creator = estrdup(d->val->val.s);
+			} else if(!strcmp("creation date",d->key)) {
+				t->created = d->val->val.i;
+			}
+		}
+		/* parse info dict */
+		for(d = info->val.d; d->val; d++) {
+			debug("parsing info->%s\n",d->key);
+			if(!strcmp("piece length",d->key)) {
+				t->piecelen = d->val->val.i;
+			} else if(!strcmp("pieces",d->key)) {
+				t->npieces = be_str_len(d->val) / 20;
+				t->pieces = emalloc((t->npieces + 1) * sizeof(Piece*));
+				for(i = 0; i < t->npieces; i++) {
+					t->pieces[i] = emallocz(sizeof(Piece));
+					t->pieces[i]->torrent = t;
+					memcpy(t->pieces[i]->sha1,&d->val->val.s[i*20],20);
+				}
+				t->pieces[i] = NULL;
+			} else if(!strcmp("name",d->key)) {
+				t->name = estrdup(d->val->val.s);
+			} else if(!strcmp("length",d->key)) {
+				t->type = T_SINGLE;
+				t->length = d->val->val.i;
+			} else if(!strcmp("files",d->key)) {
+				t->type = T_MULTI;
+				for(i=0; d->val->val.l[i]; i++);
+				t->files = emalloc((i+1)*sizeof(File*));
+				for(i=0; d->val->val.l[i]; i++) {
+					t->files[i] = emallocz(sizeof(File));
+					t->files[i]->fd = -1;
+					for(dd = d->val->val.l[i]->val.d; dd->val; dd++) {
+						if(!strcmp("length",dd->key)) {
+							t->files[i]->length = dd->val->val.i;
+							t->length += t->files[i]->length;
+						} else if(!strcmp("path",dd->key)) {
+							path[0] = '\0';
+							for(j=0; dd->val->val.l[j]; j++) {
+								strcat(path,"/");
+								strcat(path,dd->val->val.l[j]->val.s);
+							}
+							t->files[i]->path = estrdup(path);
+						}
+					}
+				}
+				t->files[i] = NULL;
+			}
+		}
+	}
+
+	if(t->type == T_SINGLE) {
+		t->files = emalloc(2 * sizeof(File*));
+		t->files[0] = emalloc(sizeof(File));
+		t->files[0]->fd = -1;
+		t->files[0]->path = t->name;
+		t->files[0]->dir = ".";
+		t->files[0]->length = t->length;
+		t->files[1] = NULL;
+	}
+
+	if(t->type == T_MULTI) {
+		for(i = 0; t->files[i]; i++) {
+			t->files[i]->dir = t->name;
+		}
+	}
+
+
+	if(!t->announce || !t->piecelen || !t->npieces || !t->pieces || !t->name || !t->length)
+		eprint("invalid torrent\n");
+
+	be_free(root);
+	return t;
+}
+
+static void
+map_chunks(Torrent *t) {
+	size_t pos, length, offset;
+	int f, i, j;
+
+	debug("map_chunks\n");
+
+	f = 0;
+	offset = 0;
+	length = t->piecelen;
+	for(i = 0; i < t->npieces; i++) {
+		pos = 0;
+		if(i == t->npieces - 1)
+			length = t->length % t->piecelen;
+		for(j = 0; pos < length; j++) {
+			t->pieces[i]->chunks = erealloc(t->pieces[i]->chunks,(j+2)*sizeof(Chunk*));
+			t->pieces[i]->chunks[j] = emalloc(sizeof(Chunk));
+			t->pieces[i]->chunks[j]->file = f;
+			t->pieces[i]->chunks[j]->offset = offset;
+			if(length - pos < t->files[f]->length - offset) {
+				t->pieces[i]->chunks[j]->length = length - pos;
+				offset += length - pos;
+				pos += length - pos;
+			} else {
+				t->pieces[i]->chunks[j]->length = t->files[f]->length - offset;
+				pos += t->files[f]->length - offset;
+				f++;
+				offset = 0;
+			}
+			//printf("piece[%i/%i] chunk[%i] file[%i] %s %i\n",i,t->npieces-1,j,f,t->files[t->pieces[i]->chunks[j]->file]->path,t->pieces[i]->chunks[j]->length);
+		}
+		t->pieces[i]->chunks[j] = NULL;
+	}
+	t->pieces[i] = NULL;
+}
+
+static void
+check_file(File *f) {
+	struct stat st;
+	char path[PATH_MAX];
+	char *p;
+	int fd;
+
+	debug("check_file %s\n",f->path);
+
+	snprintf(path,sizeof(path),"%s/%s",f->dir,f->path);
+	for(p = &path[strlen(path) - 1]; p >= path; p--)
+		if(*p == '/') break;
+	*p = '\0';
+	create_dirtree(path);
+	*p = '/';
+	if(stat(path,&st) != 0 || st.st_size < f->length) {
+		fd = open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+		lseek(fd,f->length-1,SEEK_SET);
+		write(fd,"1",1);
+		close(fd);
+	}
+}
+
+static void
+open_file(File *f) {
+	char path[PATH_MAX];
+
+	snprintf(path,sizeof(path),"./%s/%s",f->dir,f->path);
+	f->fileref++;
+	if(f->fd == -1)
+		f->fd = open(path,O_RDWR);
+	if(f->fd == -1)
+		eprint("error opening file %s:",path);
+}
+
+static void
+close_file(File *f) {
+	f->fileref--;
+	if(f->fileref <= 0) {
+		close(f->fd);
+		f->fd = -1;
+		f->fileref = 0;
+	}
+}
+
+static void
+load_piece(Piece *p) {
+	void *pa;
+	File *f;
+	int i;
+
+	for(i = 0; p->chunks[i]; i++) {
+		f = p->torrent->files[p->chunks[i]->file];
+		open_file(f);
+		p->chunks[i]->poffset = (p->chunks[i]->offset % pagesize);
+		pa = mmap(0, p->chunks[i]->length + p->chunks[i]->poffset, PROT_READ, MAP_PRIVATE, f->fd, p->chunks[i]->offset - p->chunks[i]->poffset);
+		if(pa == MAP_FAILED) eprint("mmap failed:");
+		p->chunks[i]->ptr = pa;
+		close_file(f);
+	}
+}
+
+static void
+unload_piece(Piece *p) {
+	int i;
+
+	for(i = 0; p->chunks[i]; i++)
+		munmap(p->chunks[i]->ptr,p->chunks[i]->length + p->chunks[i]->poffset);
+}
+
+static void
+verify_piece(Piece *p) {
+	SHA_CTX c;
+	int i;
+	unsigned char sha1[20];
+
+	SHA1_Init(&c);
+	load_piece(p);
+	for(i = 0; p->chunks[i]; i++)
+		SHA1_Update(&c, (unsigned char*)p->chunks[i]->ptr + p->chunks[i]->poffset, p->chunks[i]->length);
+	unload_piece(p);
+	SHA1_Final(sha1,&c);
+
+	if(!memcmp(sha1,p->sha1,20))
+		p->status = S_VALID;
+	else
+		p->status = S_BUNK;
+}
+
+static void
+verify_torrent(Torrent *t) {
+	int verified = 0;
+	int i;
+
+	vprint("Verifying torrent:\n");
+	for(i=0; t->pieces[i]; i++) {
+		verify_piece(t->pieces[i]);
+		if(t->pieces[i]->status == S_VALID) {
+			verified++;
+			vprint("+");
+		} else {
+			vprint(".");
+		}
+		fflush(stdout);
+	}
+	vprint("\n[%i/%i] Pieces verified\n",verified,t->npieces);
+}
+
+int
+main(int argc, char **argv) {
+	char *torrent = NULL;
+	Torrent *t;
+	int i;
+
+	for(i = 1; i < argc; i++) {
+		if(argv[i][0] == '-') {
+			switch(argv[i][1]) {
+				case 'd': debuglevel = 1; verbose = 1; break;
+				case 'v': verbose = 1; break;
+				default: usage(); break;
+			}
+		} else {
+			if(torrent) usage();
+			else torrent = argv[i];
+		}
+	}
+
+	vprint("ptorrent "VERSION" (c) MMX Robert Lowry\n");
+
+	pagesize = sysconf(_SC_PAGESIZE);
+	if(pagesize == -1) eprint("sysconf(_SC_PAGESIZE) failed:");
+
+	t = parse_torrent_file(torrent);
+	if(!t) eprint("invalid torrent\n");
+
+	for(i = 0; t->files[i]; i++)
+		check_file(t->files[i]);
+	map_chunks(t);
+	verify_torrent(t);
+
+	return 0;
+}
+
+
+
+
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98 
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1_Update changed from
+	void SHA1_Update(SHA_CTX* context, unsigned char* data, unsigned int
+len)
+to
+	void SHA1_Update(SHA_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it.  This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1_Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1_Update to 
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.  
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+#define SHA1HANDSOFF
+
+#ifdef VERBOSE
+#include <stdio.h>
+#endif
+#include <string.h>
+
+#include "sha1.h"
+
+/* Use result of AC_C_BIGENDIAN autoconf test. */
+#ifndef WORDS_BIGENDIAN
+#define SHA1_LITTLE_ENDIAN 1
+#endif
+
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifdef SHA1_LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+#ifdef VERBOSE  /* SAK */
+void SHAPrintContext(SHA_CTX *context, char *msg){
+  printf("%s (%d,%d) %x %x %x %x %x\n",
+	 msg,
+	 context->count[0], context->count[1], 
+	 context->state[0],
+	 context->state[1],
+	 context->state[2],
+	 context->state[3],
+	 context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1_Transform(uint32_t state[5], unsigned char buffer[64])
+{
+uint32_t a, b, c, d, e;
+typedef union {
+    unsigned char c[64];
+    uint32_t l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+static unsigned char workspace[64];
+    block = (CHAR64LONG16*)workspace;
+    memcpy(block, buffer, 64);
+#else
+    block = (CHAR64LONG16*)buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1_Init(SHA_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1_Update(SHA_CTX* context, unsigned char* data, uint32_t len)	/*
+JHB */
+{
+uint32_t i, j;	/* JHB */
+
+#ifdef VERBOSE
+    SHAPrintContext(context, "before");
+#endif
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63) {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1_Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1_Transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+    SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1_Final(unsigned char digest[20], SHA_CTX* context)
+{
+uint32_t i;	/* JHB */
+unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1_Update(context, (unsigned char *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+        SHA1_Update(context, (unsigned char *)"\0", 1);
+    }
+    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1_Transform() */
+    for (i = 0; i < 20; i++) {
+        digest[i] = (unsigned char)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    i = 0;	/* JHB */
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(finalcount, 0, 8);	/* SWR */
+#ifdef SHA1HANDSOFF  /* make SHA1_Transform overwrite it's own static vars */
+    SHA1_Transform(context->state, context->buffer);
+#endif
+}
+
+#ifndef SHA1_H
+#define SHA1_H
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+23 Apr 2001 version from http://sea-to-sky.net/~sreid/
+Modified slightly to take advantage of autoconf.
+See sha1.c for full history comments.
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+
+typedef struct {
+    uint32_t state[5];
+    uint32_t count[2];
+    unsigned char buffer[64];
+} SHA_CTX;
+
+void SHA1_Transform(uint32_t state[5], unsigned char buffer[64]);
+void SHA1_Init(SHA_CTX* context);
+void SHA1_Update(SHA_CTX* context, unsigned char* data, uint32_t len); /* JHB */
+void SHA1_Final(unsigned char digest[20], SHA_CTX* context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+