libeech

BitTorrent library
git clone git://z3bra.org/libeech.git
Log | Files | Refs | README | LICENSE

commit fad1cd5caca4176138b2378e02f18c782ecd886f
parent e308b69b6b819eed9e2e29cab389ce4879c61d8b
Author: z3bra <contactatz3bradotorg>
Date:   Thu Jul  5 09:02:35 +0200

Describe all functions

Diffstat:
libeech.c | 250+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 201 insertions(+), 49 deletions(-)
diff --git a/libeech.c b/libeech.c @@ -66,26 +66,31 @@ static struct peer * delpeer(struct peer *, struct peer *); static int peercnt(struct peer *); /* handle peer wire protocol (PWP) messages */ +static ssize_t pwprecv(struct peer *); +static ssize_t pwpsend(struct peer *, char *, size_t); + +/* send, receive and verify handshakes */ static ssize_t pwptxhs(struct torrent *, struct peer *); static ssize_t pwprxhs(struct peer *); static int pwphsck(struct torrent *, char *, long); -static ssize_t pwprecv(struct peer *); -static ssize_t pwpsend(struct peer *, char *, size_t); - +/* send different PWP messages */ static ssize_t pwptxst(struct peer *, int); static ssize_t pwptxhv(struct peer *, long); static ssize_t pwptxbf(struct torrent *, struct peer *); static ssize_t pwptxrq(struct torrent *, struct peer *); +/* receive different PWP messages */ static int pwprxst(struct peer *, int); static int pwprxhv(struct peer *, size_t, char *); static int pwprxbf(struct peer *, size_t, char *); static int pwprxrq(struct torrent *, struct peer *, size_t, char *); static int pwprxpc(struct torrent *, struct peer *, size_t, char *); +/* handle all received PWP messages */ static int pwprxcb(struct torrent *, struct peer *, int, size_t, char *); +/* functions to run when a peer can receive or has sent bytes over the wire */ static int pwptx(struct torrent *, struct peer *); static int pwprx(struct torrent *, struct peer *); @@ -93,12 +98,15 @@ static int pwprx(struct torrent *, struct peer *); static int netconn(char *, int); static int netloop(struct torrent *, int); + +/* check wether a specific bit is set or not in a byte */ static int bit(char *bits, long off) { return !!(bits[off / 8] & (1 << (7 - off%8))); } +/* set a bit in a byte to one */ static char * setbit(char *bits, long off) { @@ -106,6 +114,7 @@ setbit(char *bits, long off) return bits; } +/* set a bit in a byte to zero */ static char * clrbit(char *bits, long off) { @@ -113,6 +122,7 @@ clrbit(char *bits, long off) return bits; } +/* generate a random peer ID */ static char * peerid() { @@ -153,6 +163,15 @@ torrentsize(struct torrent *t) return sz; } +/* + * Read file(s) from torrent file, and store their path/size in a struct + * + * TODO: Remove this function entirely, and create helpers to fetch + * these info directly from the torrent file. + * torrent->files would thus be a `struct be` pointing to either "file" + * or "files" attribute in the torrent, removing one more malloc() + * in the library. + */ static long torrentfiles(struct torrent *t) { @@ -194,6 +213,8 @@ torrentfiles(struct torrent *t) return n; } + +/* Verify that all mandatory keys of a torrent are present */ static int chktorrent(struct be *b) { @@ -242,6 +263,7 @@ chktorrent(struct be *b) return 0; } +/* read a piece from a metafile (named after the SHA1 infohash) */ static long readpiece(struct torrent *t, struct piece *p, long n) { @@ -266,6 +288,7 @@ readpiece(struct torrent *t, struct piece *p, long n) return p->sz; } +/* write a piece to a metafile (named after the SHA1 infohash) */ static long writepiece(struct torrent *t, struct piece *p) { @@ -299,6 +322,7 @@ writepiece(struct torrent *t, struct piece *p) return 0; } +/* Verify that a piece matches its SHA1 hash */ static int chkpiece(struct torrent *t, struct piece *p, long n) { @@ -308,6 +332,13 @@ chkpiece(struct torrent *t, struct piece *p, long n) return memcmp(t->ph + n*20, hash, 20); } +/* + * Create a new peer struct, and add it to the list of peers + * + * TODO: Remove the malloc() and have the caller pass the `struct peer` + * to add as an argument. + * Memory tracking of those peers is on the caller responsibility. + */ static struct peer * addpeer(struct peer *pl, char *host, int port) { @@ -330,6 +361,7 @@ addpeer(struct peer *pl, char *host, int port) return p; } +/* Find a peer by its host + port */ static struct peer * getpeer(struct peer *pl, char *host, int port) { @@ -342,6 +374,13 @@ getpeer(struct peer *pl, char *host, int port) return NULL; } +/* + * Remove a peer from the struct + * + * TODO: Do not free() the peer to delete, and return a pointer to + * it instead. + * Memory tracking of those peers is on the caller responsibility. + */ static struct peer * delpeer(struct peer *pl, struct peer *p) { @@ -358,57 +397,37 @@ delpeer(struct peer *pl, struct peer *p) return pl; } +/* Count the number of peers currently added to this torrent */ static int peercnt(struct peer *pl) { return (pl ? 1 + peercnt(pl->next) : 0); } -static ssize_t -pwptxhs(struct torrent *t, struct peer *p) -{ - char m[68]; - - m[0] = 19; - memcpy(m + 1, "BitTorrent protocol", 19); - memset(m + 20, 0, 8); - memcpy(m + 28, t->ih, 20); - memcpy(m + 48, t->id, 20); - - return pwpsend(p, m, 68); -} - -static ssize_t -pwprxhs(struct peer *p) -{ - ssize_t r; - - while ((r = recv(p->fd, p->rxbuf, 68 - p->rxbufsz, 0)) > 0) - p->rxbufsz += r; - - if (r < 0) - return -1; - return p->rxbufsz; -} - -static int -pwphsck(struct torrent *t, char *hs, long l) -{ - if (l != 68) - return -1; - if (hs[0] != 19) - return -1; - if (memcmp(hs+1, "BitTorrent protocol", 19)) - return -1; - if (memcmp(hs+28, t->ih, 20)) - return -1; - if (!memcmp(hs+48, t->id, 20)) - return -1; +/* + * PWP (Peer Wire Protocol) message management + * + * Standard PWP messages: [LENGTH][TYPE][PAYLOAD] + * Length: 4 bytes bigendian; Size of the type + payload + * Type is a single byte + * Payload is variable and is length - 1 + */ - return 0; -} +/* + * Wrapper to recv() syscall. We first PEEK 4 bytes in the messages, + * to retrieve its length. + * Once we have it, we read this amount of bytes + 4 (the initial size) + * from the socket. Reading bytes is done in a buffer tied to the peer, + * so that if we cannot read the whole message yet, the remaining parts + * will be read on the next iteration. + * + * The function returns the numbers of bytes missing in the message, + * or -1 in case of errors. + * When it returns 0, it means the message is fully read and can be + * parsed. + */ static ssize_t pwprecv(struct peer *p) { @@ -436,6 +455,10 @@ pwprecv(struct peer *p) return l - p->rxbufsz; } +/* + * Wrapper to the send() syscall. It will loop to send the full message, + * until everything is sent, or an error occurs. + */ static ssize_t pwpsend(struct peer *p, char *m, size_t sz) { @@ -454,12 +477,81 @@ pwpsend(struct peer *p, char *m, size_t sz) } /* - * Standard PWP messages: [LENGTH][TYPE][PAYLOAD] - * Length: 4 bytes bigendian; Size of the type + payload - * Type is a single byte - * Payload is variable and is length - 1 + * Create and send a handshake message. From the Bittorrent spec: + * + * > A handshake is a string of bytes with the following structure: + * > + * > ---------------------------------------------------------------- + * > | Name Length | Protocol Name | Reserved | Info Hash | Peer ID | + * > ---------------------------------------------------------------- + * + * Name length: size of the "Protocol Name" attribute + * Protocol name: "Bittorrent protocol", as of BitTorrent Protocol revision 1.33 (BTP/1.0) + * Reserved: Used to list supported extensions to the protocol + * Info hash: SHA1 hash of the full "info" key from the torrent + * Peer ID: Our own ID, used for identification (or not at all) + */ +static ssize_t +pwptxhs(struct torrent *t, struct peer *p) +{ + char m[68]; + + m[0] = 19; + memcpy(m + 1, "BitTorrent protocol", 19); + memset(m + 20, 0, 8); + memcpy(m + 28, t->ih, 20); + memcpy(m + 48, t->id, 20); + + return pwpsend(p, m, 68); +} + +/* + * Wrapper to the recv() syscall to receive handshakes + * Handshake messages size is fixed in BTP/1.0 to 68 bytes. + * We cannot receive it as a standard PWP message. */ +static ssize_t +pwprxhs(struct peer *p) +{ + ssize_t r; + + while ((r = recv(p->fd, p->rxbuf, 68 - p->rxbufsz, 0)) > 0) + p->rxbufsz += r; + + if (r < 0) + return -1; + + return p->rxbufsz; +} + +/* Verify that a handshake is valid according to a torrent */ +static int +pwphsck(struct torrent *t, char *hs, long l) +{ + if (l != 68) + return -1; + if (hs[0] != 19) + return -1; + if (memcmp(hs+1, "BitTorrent protocol", 19)) + return -1; + if (memcmp(hs+28, t->ih, 20)) + return -1; + if (!memcmp(hs+48, t->id, 20)) + return -1; + + return 0; +} +/* + * Send a change of status PWP message. + * These messages have no payload and can be sent the same way. + * + * 0: CHOKE + * 1: UNCHOKE + * 2: INTERESTED + * 3: UNINTERESTED + * + */ static ssize_t pwptxst(struct peer *p, int t) { @@ -476,6 +568,7 @@ pwptxst(struct peer *p, int t) return pwpsend(p, m, 5); } +/* Send a HAVE PWP message */ static ssize_t pwptxhv(struct peer *p, long n) { @@ -495,6 +588,10 @@ pwptxhv(struct peer *p, long n) return pwpsend(p, m, 9); } +/* + * Send a BITFIELD message with our full bitfield (no cheating with HAVE + * messages here) + */ static ssize_t pwptxbf(struct torrent *t, struct peer *p) { @@ -517,6 +614,20 @@ pwptxbf(struct torrent *t, struct peer *p) return pwpsend(p, m, l + 5); } +/* + * Send a REQUEST PWP message for all blocks in a piece. + * + * We request only once piece at a time to a peer. The piece currently + * being requested is saved in the peer struct. A piece number of -1 + * means that the peer is not requesting any piece. + * A piece number of t->npiece means that no piece can be requested to + * the peer (no new piece available). + * + * The function will send a REQUEST messages for all the blocks in the + * piece, then return. + * + * TODO: Implement a function to select a piece from different algorithms + */ static ssize_t pwptxrq(struct torrent *t, struct peer *p) { @@ -564,6 +675,7 @@ pwptxrq(struct torrent *t, struct peer *p) return 0; } +/* Receive and treat a change of status from the peer */ static int pwprxst(struct peer *p, int type) { @@ -584,6 +696,7 @@ pwprxst(struct peer *p, int type) return 0; } +/* Receive and treat a HAVE message from the peer */ static int pwprxhv(struct peer *p, size_t sz, char *pl) { @@ -594,6 +707,7 @@ pwprxhv(struct peer *p, size_t sz, char *pl) return 0; } +/* Receive and save the BITFIELD sent by a peer */ static int pwprxbf(struct peer *p, size_t sz, char *pl) { @@ -601,12 +715,23 @@ pwprxbf(struct peer *p, size_t sz, char *pl) return 0; } +/* Receive a block REQUEST from a peer */ static int pwprxrq(struct torrent *t, struct peer *p, size_t sz, char *pl) { return 0; } +/* + * Receive a PIECE block from a peer + * + * After receiving a block, we'll verify if the piece matches its SHA1 hash. + * If it does, we write the piece to a metafile named after the hexadecimal + * version of the info hash. + * + * TODO: Keep track of received blocks, so we can discard a piece if we + * receive all blocks, but the hash doesn't match what we expect. + */ static int pwprxpc(struct torrent *t, struct peer *p, size_t sz, char *pl) { @@ -630,6 +755,10 @@ pwprxpc(struct torrent *t, struct peer *p, size_t sz, char *pl) return 0; } +/* + * PWP message received callback + * This function will run the appropriate function based on the message type + */ static int pwprxcb(struct torrent *t, struct peer *p, int type, size_t sz, char *pl) { @@ -658,6 +787,7 @@ pwprxcb(struct torrent *t, struct peer *p, int type, size_t sz, char *pl) return 0; } +/* Logic to send messages when peer is ready */ static int pwptx(struct torrent *t, struct peer *p) { @@ -681,6 +811,7 @@ pwptx(struct torrent *t, struct peer *p) return 0; } +/* Logic to receive messages when peer sent data */ static int pwprx(struct torrent *t, struct peer *p) { @@ -702,6 +833,7 @@ pwprx(struct torrent *t, struct peer *p) return 0; } +/* Initiate connection with a peer */ static int netconn(char *host, int port) { @@ -733,6 +865,7 @@ netconn(char *host, int port) return fd; } +/* Poll() all peers for read/write readiness on each iteration */ static int netloop(struct torrent *t, int timeout) { @@ -777,6 +910,16 @@ netloop(struct torrent *t, int timeout) return npeer; } + +/* + * Library function call + */ + +/* + * Load a torrent in a torrent struct + * + * RETURN VALUE: returns 0 if the torrent is loaded, -1 otherwise + */ int glch_loadtorrent(struct torrent *t, char *b, size_t s) { @@ -823,6 +966,11 @@ glch_loadtorrent(struct torrent *t, char *b, size_t s) return 1; } +/* + * Connect to a remote peer + * + * RETURN VALUE: returns 0 if connection is established, -1 otherwise + */ int glch_addpeer(struct torrent *t, char *host, int port) { @@ -844,6 +992,10 @@ glch_addpeer(struct torrent *t, char *host, int port) return 0; } +/* + * Perform one network iteration with all the peers, or timeout if + * noone replies + */ int glch_leech(struct torrent *t, int timeout) {