tInitial commit - libeech - bittorrent library
 (HTM) git clone git://z3bra.org/libeech.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 25c4a9d5da6502bfe86acf4b83a58097397c3e00
 (HTM) Author: z3bra <contactatz3bradotorg>
       Date:   Tue, 17 Oct 2017 19:27:29 +0200
       
       Initial commit
       
       Diffstat:
         A LICENSE                             |      13 +++++++++++++
         A README                              |      34 +++++++++++++++++++++++++++++++
         A be.c                                |     318 +++++++++++++++++++++++++++++++
         A be.h                                |      29 +++++++++++++++++++++++++++++
         A config.mk                           |      12 ++++++++++++
         A libeech.c                           |      67 +++++++++++++++++++++++++++++++
         A libeech.h                           |      16 ++++++++++++++++
         A makefile                            |      23 +++++++++++++++++++++++
         A sha1.c                              |     272 +++++++++++++++++++++++++++++++
         A sha1.h                              |      19 +++++++++++++++++++
         A torrent.c                           |      22 ++++++++++++++++++++++
         A util.c                              |      18 ++++++++++++++++++
         A util.h                              |       3 +++
       
       13 files changed, 846 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/LICENSE b/LICENSE
       t@@ -0,0 +1,13 @@
       +Copyright (c) 2016 Willy Goiffon <willyatmailoodotorg>
       +
       +Permission to use, copy, modify, and/or distribute this software for any
       +purpose with or without fee is hereby granted, provided that the above
       +copyright notice and this permission notice appear in all copies.
       +
       +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
       +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
       +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
       +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
       +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
       +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
       +PERFORMANCE OF THIS SOFTWARE.
 (DIR) diff --git a/README b/README
       t@@ -0,0 +1,34 @@
       +libeech
       +=======
       +
       +BitTorrent library
       +
       +The libeech BitTorrent library (will) implement all the functionality
       +described in the bittorrent protocol RFC[0], as well as the distributed
       +hash table (DHT) protocol[1].
       +
       +Installation
       +------------
       +Edit the `config.mk` file to match your setup, then run:
       +
       +        $ make
       +        # make install
       +
       +Usage
       +-----
       +Include the header file as follow:
       +
       +        #include <libeech.h>
       +
       +Link your program against it with
       +
       +        cc pgm.c -leech -o pgm
       +
       +Until v1.0 is complete, please check the `leec.h` for the API.
       +
       +License
       +-------
       +ISC License. See LICENSE file for copyright and license details.
       +
       +[0] http://jonas.nitro.dk/bittorrent/bittorrent-rfc.html
       +[1] https://en.wikipedia.org/wiki/Distributed_hash_table
 (DIR) diff --git a/be.c b/be.c
       t@@ -0,0 +1,318 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <ctype.h>
       +#include <limits.h>
       +#include <string.h>
       +
       +#include "be.h"
       +
       +#define MIN(a,b) ((a)<(b)?(a):(b))
       +#define MAX(a,b) ((a)>(b)?(a):(b))
       +
       +int
       +beinit(struct be *b, char *s, size_t l)
       +{
       +        if (!b || !s || !l)
       +                return -1;
       +
       +        memset(b, 0, sizeof(*b));
       +        b->start = s;
       +        b->end = b->start + l - 1;
       +        b->off = b->start;
       +
       +        return 0;
       +}
       +
       +int
       +beatol(char **str, long *l)
       +{
       +        long s = 1;
       +        long v = 0;
       +        char *sp = *str;
       +
       +        if (!sp)
       +                return -1;
       +
       +        /* define the sign of our number */
       +        if (*sp == '-') {
       +                s = -1;
       +                sp++;
       +                /* -0 is invalid, even for "-03" */
       +                if (*sp == '0')
       +                        return -1;
       +        }
       +
       +        /* 0 followed by a number is considered invalid */
       +        if (sp[0] == '0' && isdigit(sp[1]))
       +                return -1;
       +
       +        /* read out number until next non-number, or end of string */
       +        while(isdigit(*sp)) {
       +                v *= 10;
       +                v += *sp++ - '0';
       +        }
       +
       +        if (l)
       +                *l = v * s;
       +
       +        /* move initial pointer to the actual read data */
       +        *str = sp;
       +
       +        return 0;
       +}
       +
       +ssize_t
       +beint(struct be *b, long *n)
       +{
       +        char *sp;
       +        long num;
       +
       +        if (!b)
       +                return -1;
       +
       +        sp = b->off;
       +
       +        if (*(sp++) != 'i')
       +                return -1;
       +
       +        beatol(&sp, &num);
       +
       +        if (*sp != 'e')
       +                return -1;
       +
       +        if (n)
       +                *n = num;
       +
       +        return sp - b->off + 1;
       +}
       +
       +ssize_t
       +bestr(struct be *b, char **s, size_t *l)
       +{
       +        char *sp;
       +        ssize_t len;
       +
       +        if (!b)
       +                return -1;
       +
       +        sp = b->off;
       +
       +        if (beatol(&sp, &len) < 0)
       +                return -1;
       +
       +        if (len < 0 || *sp++ != ':')
       +                return -1;
       +
       +        if (s)
       +                *s = sp;
       +
       +        if (l)
       +                *l = (size_t)len;
       +
       +        return sp - b->off + len;
       +}
       +
       +ssize_t
       +belist(struct be *b, size_t *n)
       +{
       +        size_t c = 0;
       +        struct be i;
       +
       +        if (!b)
       +                return -1;
       +
       +        beinit(&i, b->off, b->end - b->off + 1);
       +
       +        while(belistover(&i)) {
       +                belistnext(&i);
       +                c++;
       +        }
       +
       +        if (*i.off == 'e')
       +                i.off++;
       +
       +        if (n)
       +                *n = c;
       +
       +        return i.off - b->off;
       +}
       +
       +ssize_t
       +bedict(struct be *b, size_t *n)
       +{
       +        size_t c = 0;
       +        struct be i;
       +
       +        if (!b)
       +                return -1;
       +
       +        beinit(&i, b->off, b->end - b->off + 1);
       +
       +        while(!belistover(&i)) {
       +                bedictnext(&i, NULL, NULL, NULL);
       +                c++;
       +        }
       +
       +        if (*i.off == 'e')
       +                i.off++;
       +
       +        if (n)
       +                *n = c;
       +
       +        return i.off - b->off;
       +}
       +
       +int
       +belistover(struct be *b) {
       +        return b->off >= b->end || *b->off == 'e';
       +}
       +
       +int
       +belistnext(struct be *b)
       +{
       +        if (!b || *b->off == 'e')
       +                return -1;
       +
       +        if (b->off == b->start && *b->off == 'l') {
       +                b->off++;
       +                return 0;
       +        }
       +
       +        return benext(b) > 0;
       +}
       +
       +int
       +bedictnext(struct be *b, char **k, size_t *l, struct be *v)
       +{
       +        if (!b || *b->off == 'e')
       +                return -1;
       +
       +        /* move to first element if we're at the start */
       +        if (b->off == b->start && *b->off == 'd')
       +                b->off++;
       +
       +        /* retrieve key name and length */
       +        if (bestr(b, k, l) < 0)
       +                return -1;
       +
       +        if (benext(b) > 0 && v)
       +                beinit(v, b->off, b->end - b->off + 1);
       +
       +        return benext(b) > 0;
       +}
       +
       +ssize_t
       +benext(struct be *b)
       +{
       +        int r = 0;
       +
       +        if (!b)
       +                return -1;
       +
       +        /* check for end of buffer */
       +        if (b->off >= b->end)
       +                return -1;
       +
       +        /* TODO: implement betype() */
       +        switch(betype(b)) {
       +        case 'i':
       +                r = beint(b, NULL);
       +                break;
       +        case 'l':
       +                r = belist(b, NULL);
       +                break;
       +        case 'd':
       +                r = bedict(b, NULL);
       +                break;
       +        case 's':
       +                r = bestr(b, NULL, NULL);
       +                break;
       +        }
       +
       +        b->off += r;
       +
       +        return r;
       +}
       +
       +char
       +betype(struct be *b)
       +{
       +        switch(*b->off) {
       +        case 'i':
       +        case 'l':
       +        case 'd':
       +                return *b->off;
       +                break; /* NOTREACHED */
       +        }
       +        return isdigit(*b->off) ? 's' : 0;
       +}
       +
       +int
       +bekv(struct be *b, char *k, size_t l, struct be *v)
       +{
       +        char *key = NULL;
       +        size_t klen = 0;
       +        struct be i;
       +
       +        if (!b)
       +                return -1;
       +
       +        if (*b->off != 'd')
       +                return -1;
       +
       +        beinit(&i, b->off, b->end - b->off + 1);
       +
       +        /* search the data 'till the end */
       +        while (!belistover(&i) && bedictnext(&i, &key, &klen, v)) {
       +                /* we found our key! */
       +                if (!strncmp(k, key, MIN(l, klen)))
       +                        return 0;
       +
       +                /* recursive call to search inner dictionaries */
       +                if (betype(&i) == 'd' && !bekv(&i, k, l, v))
       +                        return 0;
       +        }
       +
       +        /* couldn't find anything, sorry */
       +        return -1;
       +}
       +
       +int
       +bepath(struct be *b, char **p, size_t l)
       +{
       +        char *s;
       +        size_t r;
       +        struct be i;
       +
       +        if (!b || betype(b) != 'l')
       +                return -1;
       +
       +        beinit(&i, b->off, b->end - b->off + 1);
       +
       +        while(belistnext(&i) && !belistover(&i)) {
       +                if (!bestr(&i, &s, &r))
       +                        continue;
       +                strncat(*p, "/", l);
       +                strncat(*p, s, r);
       +        }
       +        return 0;
       +}
       +
       +char *
       +bekstr(struct be *b, char *k, size_t kl)
       +{
       +        char *sp;
       +        size_t vl;
       +        struct be v;
       +        static char s[LINE_MAX];
       +
       +        memset(s, 0, LINE_MAX);
       +        if (bekv(b, k, kl, &v) < 0)
       +                return NULL;
       +        if (bestr(&v, &sp, &vl) < 0)
       +                return NULL;
       +
       +        memcpy(s, sp, MIN(vl, LINE_MAX));
       +        s[MIN(vl, LINE_MAX - 1)] = 0;
       +
       +        return s;
       +}
 (DIR) diff --git a/be.h b/be.h
       t@@ -0,0 +1,29 @@
       +/* See LICENSE file for copyright and license details. */
       +#ifndef _BE_H
       +#define _BE_H
       +
       +#include <stdint.h>
       +#include <unistd.h>
       +
       +struct be {
       +        char *start;
       +        char *end;
       +        char *off;
       +};
       +
       +int beinit(struct be *, char *, size_t);
       +int beatol(char **, long *);
       +ssize_t beint(struct be *, long *);
       +ssize_t bestr(struct be *, char **, size_t *);
       +ssize_t belist(struct be *, size_t *);
       +ssize_t bedict(struct be *, size_t *);
       +ssize_t benext(struct be *);
       +int belistover(struct be *);
       +int belistnext(struct be *);
       +int bedictnext(struct be *, char **, size_t *, struct be *);
       +char betype(struct be *);
       +int bekv(struct be *, char *, size_t, struct be *);
       +int bepath(struct be *, char **, size_t);
       +char * bekstr(struct be *, char *, size_t);
       +
       +#endif
 (DIR) diff --git a/config.mk b/config.mk
       t@@ -0,0 +1,12 @@
       +VERSION = 0.0
       +
       +CC = cc
       +LD = ${CC}
       +
       +PREFIX = /usr/local
       +MANDIR = ${PREFIX}/man
       +
       +CPPFLAGS = -DVERSION=\"${VERSION}\"
       +CFLAGS = ${CPPFLAGS} -Wall -Wextra -pedantic -g
       +LDFLAGS =
       +LDLIBS =
 (DIR) diff --git a/libeech.c b/libeech.c
       t@@ -0,0 +1,67 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <limits.h>
       +#include <stdint.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <string.h>
       +#include <time.h>
       +
       +#include <sys/types.h>
       +#include <sys/stat.h>
       +#include <unistd.h>
       +
       +#include "be.h"
       +#include "sha1.h"
       +#include "util.h"
       +#include "libeech.h"
       +
       +static char * peerid();
       +
       +static char *
       +peerid()
       +{
       +        int n;
       +        char hexa[40];
       +        unsigned char hash[20];
       +        static char peerid[21] = "-GT0000-XXXXXXXXXXXX";
       +
       +        srand(time(NULL)); /* good-enough seed */
       +        n = rand();
       +        snprintf(hexa, 40, "%08x%08x\n", n, ~n);
       +        sha1((unsigned char *)hexa, 16, hash);
       +        memcpy(peerid + 8, tohex(hash, hexa, 12), 12);
       +
       +        return peerid;
       +}
       +
       +int
       +glch_loadtorrent(struct torrent *t, char *path)
       +{
       +        FILE *f;
       +        char *b, *sp;
       +        struct stat sb;
       +        /* read torrent file into a memory buffer */
       +        if (stat(path, &sb))
       +                return -1;
       +        if (!(f = fopen(path, "r")))
       +                return -1;
       +        b = malloc(sb.st_size);
       +        fread(b, 1, sb.st_size, f);
       +        b[sb.st_size - 1] = '\0';
       +        fclose(f);
       +
       +        /* retrieve "announce" key */
       +        beinit(&t->be, b, sb.st_size);
       +        sp = bekstr(&t->be, "announce", 8);
       +        if (!sp)
       +                return -1;
       +        memcpy(t->tr, sp, strlen(sp));
       +
       +        sp = peerid();
       +        if (!sp)
       +                return -1;
       +        memcpy(t->id, sp, 20);
       +        t->id[20] = 0;
       +
       +        return 0;
       +}
 (DIR) diff --git a/libeech.h b/libeech.h
       t@@ -0,0 +1,16 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <limits.h>
       +
       +#include "be.h"
       +
       +#define PCESIZ 1048576
       +#define BLKSIZ 16384
       +#define MSGSIZ ((BLKSIZ) + 13)
       +
       +struct torrent {
       +        char id[21];
       +        char tr[PATH_MAX];
       +        struct be be;
       +};
       +
       +int glch_loadtorrent(struct torrent *, char *);
 (DIR) diff --git a/makefile b/makefile
       t@@ -0,0 +1,23 @@
       +include config.mk
       +
       +all: libeech.a torrent
       +libeech.a: libeech.a(libeech.o) libeech.a(util.o) libeech.a(be.o) libeech.a(sha1.o)
       +libeech.o: libeech.c libeech.h
       +
       +torrent: torrent.c libeech.a
       +        $(CC) $< -I. -L. -leech -o $@
       +.o.a:
       +        $(AR) rcs $@ $<
       +
       +install: libeech.a libeech.h
       +        mkdir -p $(DESTDIR)$(PREFIX)/lib
       +        mkdir -p $(DESTDIR)$(PREFIX)/include
       +        cp libeech.a $(DESTDIR)$(PREFIX)/lib/
       +        cp libeech.h $(DESTDIR)$(PREFIX)/include/
       +
       +uninstall:
       +        rm $(DESTDIR)$(PREFIX)/lib/libeech.a
       +        rm $(DESTDIR)$(PREFIX)/include/libeech.h
       +
       +clean:
       +        rm -f libeech.a torrent *.o
 (DIR) diff --git a/sha1.c b/sha1.c
       t@@ -0,0 +1,272 @@
       +/* LibTomCrypt, modular cryptographic library -- Tom St Denis
       + *
       + * LibTomCrypt is a library that provides various cryptographic
       + * algorithms in a highly modular and flexible manner.
       + *
       + * The library is free for all purposes without any express
       + * guarantee it works.
       + */
       +
       +#include <stddef.h>
       +#include <stdint.h>
       +
       +#include "sha1.h"
       +
       +#define F0(x,y,z)  (z ^ (x & (y ^ z)))
       +#define F1(x,y,z)  (x ^ y ^ z)
       +#define F2(x,y,z)  ((x & y) | (z & (x | y)))
       +#define F3(x,y,z)  (x ^ y ^ z)
       +
       +#define ROL(x,n) ( (((x)<<(n))&(UINT32_MAX))|(((x)>>(sizeof(x)*8-(n)))&(UINT32_MAX)) )
       +
       +#define MAX(x, y) ( ((x)>(y))?(x):(y) )
       +#define MIN(x, y) ( ((x)<(y))?(x):(y) )
       +
       +#define STORE32L(x, y)                                                                     \
       +  do { (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \
       +       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } while(0)
       +
       +#define LOAD32L(x, y)                            \
       +  do { x = ((uint32_t)((y)[3] & 255)<<24) | \
       +           ((uint32_t)((y)[2] & 255)<<16) | \
       +           ((uint32_t)((y)[1] & 255)<<8)  | \
       +           ((uint32_t)((y)[0] & 255)); } while(0)
       +
       +#define STORE64L(x, y)                                                                     \
       +  do { (y)[7] = (unsigned char)(((x)>>56)&255); (y)[6] = (unsigned char)(((x)>>48)&255);   \
       +       (y)[5] = (unsigned char)(((x)>>40)&255); (y)[4] = (unsigned char)(((x)>>32)&255);   \
       +       (y)[3] = (unsigned char)(((x)>>24)&255); (y)[2] = (unsigned char)(((x)>>16)&255);   \
       +       (y)[1] = (unsigned char)(((x)>>8)&255); (y)[0] = (unsigned char)((x)&255); } while(0)
       +
       +#define LOAD64L(x, y)                                                       \
       +  do { x = (((uint64_t)((y)[7] & 255))<<56)|(((uint64_t)((y)[6] & 255))<<48)| \
       +           (((uint64_t)((y)[5] & 255))<<40)|(((uint64_t)((y)[4] & 255))<<32)| \
       +           (((uint64_t)((y)[3] & 255))<<24)|(((uint64_t)((y)[2] & 255))<<16)| \
       +           (((uint64_t)((y)[1] & 255))<<8)|(((uint64_t)((y)[0] & 255))); } while(0)
       +
       +#define STORE32H(x, y)                                                                     \
       +  do { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255);   \
       +       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } while(0)
       +
       +#define LOAD32H(x, y)                            \
       +  do { x = ((uint32_t)((y)[0] & 255)<<24) | \
       +           ((uint32_t)((y)[1] & 255)<<16) | \
       +           ((uint32_t)((y)[2] & 255)<<8)  | \
       +           ((uint32_t)((y)[3] & 255)); } while(0)
       +
       +#define STORE64H(x, y)                                                                     \
       +do { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255);     \
       +     (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255);     \
       +     (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255);     \
       +     (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } while(0)
       +
       +#define LOAD64H(x, y)                                                      \
       +do { x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \
       +         (((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \
       +         (((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \
       +         (((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); } while(0)
       +
       +static int  sha1_compress(sha1_context *md, unsigned char *buf)
       +{
       +    uint32_t a,b,c,d,e,W[80],i;
       +
       +    /* copy the state into 512-bits into W[0..15] */
       +    for (i = 0; i < 16; i++) {
       +        LOAD32H(W[i], buf + (4*i));
       +    }
       +
       +    /* copy state */
       +    a = md->state[0];
       +    b = md->state[1];
       +    c = md->state[2];
       +    d = md->state[3];
       +    e = md->state[4];
       +
       +    /* expand it */
       +    for (i = 16; i < 80; i++) {
       +        W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
       +    }
       +
       +    /* compress */
       +    /* round one */
       +    #define FF0(a,b,c,d,e,i) e = (ROL(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROL(b, 30);
       +    #define FF1(a,b,c,d,e,i) e = (ROL(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROL(b, 30);
       +    #define FF2(a,b,c,d,e,i) e = (ROL(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROL(b, 30);
       +    #define FF3(a,b,c,d,e,i) e = (ROL(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROL(b, 30);
       +
       +    for (i = 0; i < 20; ) {
       +       FF0(a,b,c,d,e,i++);
       +       FF0(e,a,b,c,d,i++);
       +       FF0(d,e,a,b,c,i++);
       +       FF0(c,d,e,a,b,i++);
       +       FF0(b,c,d,e,a,i++);
       +    }
       +
       +    /* round two */
       +    for (; i < 40; )  {
       +       FF1(a,b,c,d,e,i++);
       +       FF1(e,a,b,c,d,i++);
       +       FF1(d,e,a,b,c,i++);
       +       FF1(c,d,e,a,b,i++);
       +       FF1(b,c,d,e,a,i++);
       +    }
       +
       +    /* round three */
       +    for (; i < 60; )  {
       +       FF2(a,b,c,d,e,i++);
       +       FF2(e,a,b,c,d,i++);
       +       FF2(d,e,a,b,c,i++);
       +       FF2(c,d,e,a,b,i++);
       +       FF2(b,c,d,e,a,i++);
       +    }
       +
       +    /* round four */
       +    for (; i < 80; )  {
       +       FF3(a,b,c,d,e,i++);
       +       FF3(e,a,b,c,d,i++);
       +       FF3(d,e,a,b,c,i++);
       +       FF3(c,d,e,a,b,i++);
       +       FF3(b,c,d,e,a,i++);
       +    }
       +
       +    #undef FF0
       +    #undef FF1
       +    #undef FF2
       +    #undef FF3
       +
       +    /* store */
       +    md->state[0] = md->state[0] + a;
       +    md->state[1] = md->state[1] + b;
       +    md->state[2] = md->state[2] + c;
       +    md->state[3] = md->state[3] + d;
       +    md->state[4] = md->state[4] + e;
       +
       +    return 0;
       +}
       +
       +/**
       +   Initialize the hash state
       +   @param md   The hash state you wish to initialize
       +   @return 0 if successful
       +*/
       +int sha1_init(sha1_context * md)
       +{
       +   if (md == NULL) return 1;
       +   md->state[0] = 0x67452301UL;
       +   md->state[1] = 0xefcdab89UL;
       +   md->state[2] = 0x98badcfeUL;
       +   md->state[3] = 0x10325476UL;
       +   md->state[4] = 0xc3d2e1f0UL;
       +   md->curlen = 0;
       +   md->length = 0;
       +   return 0;
       +}
       +
       +/**
       +   Process a block of memory though the hash
       +   @param md     The hash state
       +   @param in     The data to hash
       +   @param inlen  The length of the data (octets)
       +   @return 0 if successful
       +*/
       +int sha1_process (sha1_context * md, const unsigned char *in, unsigned long inlen)
       +{
       +    size_t n;
       +    size_t i;
       +    int    err;
       +    if (md == NULL) return 1;
       +    if (in == NULL) return 1;
       +    if (md->curlen > sizeof(md->buf)) {
       +       return 1;
       +    }
       +    if ((md->length + inlen) < md->length) {
       +      return 1;
       +    }
       +    while (inlen > 0) {
       +        if (md->curlen == 0 && inlen >= 64) {
       +           if ((err = sha1_compress (md, (unsigned char *)in)) != 0) {
       +              return err;
       +           }
       +           md->length += 64 * 8;
       +           in             += 64;
       +           inlen          -= 64;
       +        } else {
       +           n = MIN(inlen, (64 - md->curlen));
       +           for (i = 0; i < n; i++) {
       +               md->buf[md->curlen + i] = in[i];
       +           }
       +           md->curlen += n;
       +           in             += n;
       +           inlen          -= n;
       +           if (md->curlen == 64) {
       +              if ((err = sha1_compress (md, md->buf)) != 0) {
       +                 return err;
       +              }
       +              md->length += 8*64;
       +              md->curlen = 0;
       +           }
       +       }
       +    }
       +    return 0;
       +}
       +
       +/**
       +   Terminate the hash to get the digest
       +   @param md  The hash state
       +   @param out [out] The destination of the hash (20 bytes)
       +   @return 0 if successful
       +*/
       +int sha1_done(sha1_context * md, unsigned char *out)
       +{
       +    int i;
       +
       +    if (md  == NULL) return 1;
       +    if (out == NULL) return 1;
       +
       +    if (md->curlen >= sizeof(md->buf)) {
       +       return 1;
       +    }
       +
       +    /* increase the length of the message */
       +    md->length += md->curlen * 8;
       +
       +    /* append the '1' bit */
       +    md->buf[md->curlen++] = (unsigned char)0x80;
       +
       +    /* if the length is currently above 56 bytes we append zeros
       +     * then compress.  Then we can fall back to padding zeros and length
       +     * encoding like normal.
       +     */
       +    if (md->curlen > 56) {
       +        while (md->curlen < 64) {
       +            md->buf[md->curlen++] = (unsigned char)0;
       +        }
       +        sha1_compress(md, md->buf);
       +        md->curlen = 0;
       +    }
       +
       +    /* pad upto 56 bytes of zeroes */
       +    while (md->curlen < 56) {
       +        md->buf[md->curlen++] = (unsigned char)0;
       +    }
       +
       +    /* store length */
       +    STORE64H(md->length, md->buf+56);
       +    sha1_compress(md, md->buf);
       +
       +    /* copy output */
       +    for (i = 0; i < 5; i++) {
       +        STORE32H(md->state[i], out+(4*i));
       +    }
       +    return 0;
       +}
       +
       +int sha1(const unsigned char *msg, size_t len, unsigned char *hash)
       +{
       +        sha1_context ctx;
       +        int ret;
       +        if ((ret = sha1_init(&ctx))) return ret;
       +        if ((ret = sha1_process(&ctx, msg, len))) return ret;
       +        if ((ret = sha1_done(&ctx, hash))) return ret;
       +        return 0;
       +}
 (DIR) diff --git a/sha1.h b/sha1.h
       t@@ -0,0 +1,19 @@
       +/* LibTomCrypt, modular cryptographic library -- Tom St Denis
       + *
       + * LibTomCrypt is a library that provides various cryptographic
       + * algorithms in a highly modular and flexible manner.
       + *
       + * The library is free for all purposes without any express
       + * guarantee it works.
       + */
       +
       +typedef struct {
       +    uint64_t length;
       +    uint32_t state[5], curlen;
       +    unsigned char buf[64];
       +} sha1_context;
       +
       +int sha1_init(sha1_context * md);
       +int sha1_process(sha1_context * md, const unsigned char *in, unsigned long inlen);
       +int sha1_done(sha1_context * md, unsigned char *hash);
       +int sha1(const unsigned char *in, size_t len, unsigned char *hash);
 (DIR) diff --git a/torrent.c b/torrent.c
       t@@ -0,0 +1,22 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +
       +#include <libeech.h>
       +
       +int
       +main(int argc, char *argv[])
       +{
       +        struct torrent t;
       +
       +        if (argc < 2) {
       +                fprintf(stderr, "%s TORRENT\n", argv[0]);
       +                return -1;
       +        }
       +
       +        if (!glch_loadtorrent(&t, argv[1])) {
       +                printf("Peer ID: %s\n", t.id);
       +                printf("Tracker: %s\n", t.tr);
       +        }
       +                
       +        return 0;
       +}
 (DIR) diff --git a/util.c b/util.c
       t@@ -0,0 +1,18 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <string.h>
       +
       +char *
       +tohex(unsigned char *in, char *out, size_t len)
       +{
       +        size_t i, j;
       +        char hex[] = "0123456789abcdef";
       +
       +        memset(out, 0, len*2 + 1);
       +        for (i=0, j=0; i<len; i++, j++) {
       +                out[j]   = hex[in[i] >> 4];
       +                out[++j] = hex[in[i] & 15];
       +        }
       +
       +        return out;
       +}
       +
 (DIR) diff --git a/util.h b/util.h
       t@@ -0,0 +1,3 @@
       +/* See LICENSE file for copyright and license details. */
       +#include <stdlib.h>
       +char * tohex(unsigned char *, char *, size_t);