From 51b913eb2ff91adfe8cd5e417759a6057c5a8a21 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Thu, 13 Nov 2008 16:17:55 +0000 Subject: XBitTorrent --- peer.c | 487 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 peer.c (limited to 'peer.c') diff --git a/peer.c b/peer.c new file mode 100644 index 0000000..d8078b1 --- /dev/null +++ b/peer.c @@ -0,0 +1,487 @@ +/* $Id$ */ +/* + * Copyright (c) 2005 Dimitri Sokolyuk + * + * Permission to use, copy, modify, and 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "meta.h" +#include "peer.h" /* not used */ +#include "tools.h" /* peerid() */ +#include "files.h" + +#ifndef lint +static const char rcsid[] = "@(#)$Id$"; +#endif /* not lint */ + +#define HSLEN 68 +#define PROTO "BitTorrent protocol" +#define PROTOLEN 19 +#define FLAGSLEN 8 + +// LIST_HEAD(btplist, btpeer) phead; + +char * +btgethandshake(const u_char *info, const u_char *id) +{ + /* Handshake ::= */ + static char buf[HSLEN]; + int len, off; + + off = 0; len = 1; + memset(buf, PROTOLEN, len); + + off += len; len = PROTOLEN; + memcpy(buf + off, PROTO, len); + + off += len; len = FLAGSLEN; + memset(buf + off, 0, len); + + off += len; len = SHA1LEN; + memcpy(buf + off, info, len); + + off += len; len = SHA1LEN; + if (id == NULL) + memset(buf + off, 0, len); + else + memcpy(buf + off, id, len); + + return buf; +} + +int +btchkhandshake(const char *src, const u_char *info, const u_char *id) +{ + char buf[HSLEN], *etalon; + + if (src == NULL) + return 0; + + /* ignore flags */ + memcpy(buf, src, HSLEN); + memset(buf + PROTOLEN + 1, 0, FLAGSLEN); + etalon = btgethandshake(info, id); + + return (memcmp(buf, etalon, id == NULL ? HSLEN - SHA1LEN : HSLEN) == 0); +} + +/**********************************************************************/ + +int +btaddpeer(struct btplist *plist, struct btpeer *peer) +{ + struct btpeer *p; + + LIST_FOREACH(p, plist, link) { + if (memcmp(&peer->sin, &p->sin, sizeof(struct sockaddr_in)) == 0) { + /* found same peer, reset its ttl value */ + p->ttl = MAX_TTL; + return 1; + } + } + + /* initial values */ + setbit(&peer->flags, LOCAL_CHOKED); + clrbit(&peer->flags, LOCAL_INTERESTED); + setbit(&peer->flags, REMOTE_CHOKED); + clrbit(&peer->flags, REMOTE_INTERESTED); + peer->ttl = MAX_TTL; + peer->state = IDLE; + LIST_INSERT_HEAD(plist, peer, link); + + return 0; +} + +void +btdumpplist(struct btplist *plist) +{ + struct btpeer *p; + int i = 0; + + LIST_FOREACH(p, plist, link) { + printf("%2d: %s:%d (%d) %c%c:%c%c\n", + ++i, + inet_ntoa(p->sin.sin_addr), + ntohs(p->sin.sin_port), + p->ttl, + "cC"[!!isset(&p->flags, LOCAL_CHOKED)], + "iI"[!!isset(&p->flags, LOCAL_INTERESTED)], + "cC"[!!isset(&p->flags, REMOTE_CHOKED)], + "iI"[!!isset(&p->flags, REMOTE_INTERESTED)] + ); + } +} + +int +btclearoldpeers(struct btplist *plist) +{ + struct btpeer *p, *nxt; + int n = 0; + + for (p = LIST_FIRST(plist); p != LIST_END(plist); p = nxt) { + nxt = LIST_NEXT(p, link); + if (--p->ttl <= 0 && p->state == FAILED) { + LIST_REMOVE(p, link); + free(p); + } else + ++n; + } + + return n; +} + +void +btdelplist(struct btplist *plist) +{ + struct btpeer *p, *nxt; + + for (p = LIST_FIRST(plist); p != LIST_END(plist); p = nxt) { + nxt = LIST_NEXT(p, link); + free(p); + } + LIST_INIT(plist); +} + +void +btdbg(struct btpeer *p, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "%s:%d ", + inet_ntoa(p->sin.sin_addr), + ntohs(p->sin.sin_port)); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +/**********************************************************************/ + +// newpeer() +/* alloc space, alloc 68 bytes for msgbuf (handshake length) */ + +#if 0 +int +chkhandshake(struct btpeer *p) +{ + char buf[20]; + + read(p->sockd, buf, 1); + if (*buf != 19) + drop(p); + read(p->sockd, buf, 19); + if (memcmp(buf, "Bittorrent Protocol", 19) != 0) + drop(p); + read(p->sockd, buf, 8); + read(p->sockd, buf, 20); + if (memcmp(buf, mp->infohash, 20) != 0) + drop(p); + read(p->sockd, buf, 20); + memcpy(p->peerid, buf, 20); + +// nonblock(p->sockid); + return 0; +} + + +int +readmsg(struct btpeer *p) +{ + size_t len, ret; + + if (p->msgbuf == NULL) { + read(p->sockd, &len, sizeof(size_t)); + p->buflen = ntohl(len); + p->msgbuf = malloc(p->buflen); + p->bufoff = 0; + } else { + ret = read(p->sockd, p->msgbuf + p->bufoff, p->buflen - p->bufoff); + p->bufoff += ret; + } + + if (p->msgbuf != NULL && p->buflen - p->bufoff == 0) { + dispatch(p); + free(p->msgbuf); + p->msgbuf = NULL; + } +} + +int +dispatch(struct btmeta *mp, struct btpeer *p) +{ + int piece, offset, length; + + switch (p->state) { + case IDLE: + case CONNECTING: + case HANDSHAKE: + case SUCCESS: + switch (*p->msgbuf) { + case MSG_CHOKE: + setbit(&p->flags, REMOTE_CHOKED); + break; + case MSG_UNCHOKE: + clrbit(&p->flags, REMOTE_CHOKED); + break; + case MSG_INTERESTED: + setbit(&p->flags, REMOTE_INTERESTED); + break; + case MSG_UNINTERESTED: + clrbit(&p->flags, REMOTE_INTERESTED); + break; + case MSG_HAVE: + piece = htonl(*(int *)&p->msgbuf[1]); + btsetbit(p->bitfield, piece); + + sendhave(); + + break; + case MSG_BITFIELD: + memcpy(p->bitfield, &p->msgbuf[1], p->buflen); + break; + case MSG_REQUEST: + piece = ntohl(*(int *)&p->msgbuf[1]); + offset = ntohl(*(int *)&p->msgbuf[5]); + length = ntohl(*(int *)&p->msgbuf[9]); + + btallocrequest(mp, p, piece, offset, length); + break; + case MSG_PIECE: + piece = ntohl(*(int *)&p->msgbuf[1]); + offset = ntohl(*(int *)&p->msgbuf[5]); + length = p->length - 9; + + btwriterequest(p, piece, offset, length, &p->msgbuf[9]); + btfreerequest(mp, p, piece, offset, length); + break; + case MSG_CANCEL: + piece = ntohl(*(int *)&p->msgbuf[1]); + offset = ntohl(*(int *)&p->msgbuf[5]); + length = ntohl(*(int *)&p->msgbuf[9]); + btfreerequest(mp, p, piece, offset, length); + break; + } + break; + case FAILED: + } +} + + +#endif + +/**********************************************************************/ + +/* messages: +-1 keep alive | length int | + 0 choke | length int | id char | + 1 unchoke | length int | id char | + 2 interested | length int | id char | + 3 uninterested | length int | id char | + 4 bitfield | length int | id char | bitfield bfsz | + 5 have | length int | id char | piece int | + 6 request | length int | id char | piece int | offset int | length int | + 7 piece | length int | id char | piece int | offset int | data blksz | + 8 cancel | length int | id char | piece int | offset int | lenght int | + */ + +char * +btmessage(struct btmeta *m, struct btpeer *p, size_t *mlen) +{ + size_t off; + int tmp; + char *buf; + + *mlen = sizeof(int); + if (p->msg >= MSG_CHOKE) + *mlen += sizeof(char); + if (p->msg == MSG_BITFIELD) + *mlen += m->bitlen; + if (p->msg >= MSG_HAVE) + *mlen += sizeof(int); + if (p->msg >= MSG_REQUEST) { + *mlen += sizeof(int); + if (p->msg == MSG_PIECE) + *mlen += BLOCKSZ; + else + *mlen += sizeof(int); + } + + buf = calloc(*mlen, sizeof(char)); + if (buf == NULL) + return NULL; + + tmp = htonl(*mlen - sizeof(int)); + memcpy(buf, &tmp, sizeof(int)); + off = sizeof(int); + + if (p->msg >= MSG_CHOKE) { + memcpy(buf + off, &p->msg, sizeof(char)); + off += sizeof(char); + } + if (p->msg == MSG_BITFIELD) + memcpy(buf + off, m->bitfield, m->bitlen); + if (p->msg >= MSG_HAVE) { + tmp = htonl(p->piece); + memcpy(buf + off, &tmp, sizeof(int)); + off += sizeof(int); + } + if (p->msg >= MSG_REQUEST) { + tmp = htonl(p->offset); + memcpy(buf + off, &tmp, sizeof(int)); + off += sizeof(int); + if (p->msg == MSG_PIECE) + memcpy(buf + off, p->data, BLOCKSZ); + else { + tmp = htonl(p->length); + memcpy(buf + off, &tmp, sizeof(int)); + } + } + + return buf; +} + +void +handlemsg(struct btmeta *mp, struct btpeer *p, char *buf, size_t buflen) +{ + size_t length; + enum msg msg; + int piece, offset, blklen; + + length = ntohl(*(int *)buf); + buf += sizeof(int); + + if (length == 0) + return; + + msg = *(char *)buf; + buf += sizeof(char); + length -= sizeof(char); + + switch (msg) { + case MSG_CHOKE: + setbit(&p->flags, REMOTE_CHOKED); + break; + case MSG_UNCHOKE: + clrbit(&p->flags, REMOTE_CHOKED); + break; + case MSG_INTERESTED: + setbit(&p->flags, REMOTE_INTERESTED); + break; + case MSG_UNINTERESTED: + clrbit(&p->flags, REMOTE_INTERESTED); + break; + case MSG_HAVE: + assert(p->bitfield); + piece = htonl(*(int *)buf); + btsetbit(p->bitfield, piece); + break; + case MSG_BITFIELD: + if (p->bitfield == NULL) { + p->bitfield = calloc(length, sizeof(char)); + assert(p->bitfield); + } + memcpy(p->bitfield, buf, length); + break; + case MSG_REQUEST: + piece = htonl(*(int *)buf); + buf += sizeof(int); + offset = htonl(*(int *)buf); + buf += sizeof(int); + blklen = htonl(*(int *)buf); + + if (p->data == NULL) { + p->data = calloc(mp->plength, sizeof(char)); + assert(p->data); + // btread(mp, p->data, piece); + } + + /* XXX send, put on queue */ + + break; + case MSG_PIECE: + piece = htonl(*(int *)buf); + buf += sizeof(int); + offset = htonl(*(int *)buf); + blklen = buflen - sizeof(int) * 2; + + if (p->data == NULL) { + p->data = calloc(mp->plength, sizeof(char)); + assert(p->data); + } + + /* XXX recieve, put on queue */ + + break; + case MSG_CANCEL: + piece = htonl(*(int *)buf); + buf += sizeof(int); + offset = htonl(*(int *)buf); + buf += sizeof(int); + blklen = htonl(*(int *)buf); + + /* XXX remove from queue */ + + break; + default: + break; + } +} + +#if 0 +payload(enum msg *msg) +{ + switch (msg) { + case MSG_CHOKE: + case MSG_UNCHOKE: + case MSG_INTERESTED: + case MSG_UNINTERESTED: + buf[0] = (int)4; + buf[3] = (char)msg; + } +} + +btsndchoked(struct btpeer *p) +{ + /* | length | id */ + int buf[2]; + + p->msg = MSG_CHOKE; + + buf[0] = sizeof(p->msg); + buf[1] = p->msg; +} + +btsndhave(struct btpeer *p) +{ + int buf[3]; + p->msg = MSG_HAVE; + + buf[0] = sizeof(p->msg) + sizeof(p->piece); + buf[1] = p->msg; + buf[2] = p->piece; +} +#endif -- cgit v1.2.3