summaryrefslogtreecommitdiff
path: root/peer.c
diff options
context:
space:
mode:
Diffstat (limited to 'peer.c')
-rw-r--r--peer.c487
1 files changed, 487 insertions, 0 deletions
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 <demon@vhost.dyndns.org>
+ *
+ * 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 <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 ::= <plen(1)> <pstr(19)> <flags(8)> <info_hash(20)> <peer_id(20)> */
+ 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