/* $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