diff -ur libbt-1.02/include/btmessage.h libbt/include/btmessage.h
--- libbt-1.02/include/btmessage.h	Fri Jun 25 17:42:46 2004
+++ libbt/include/btmessage.h	Fri Jun 25 17:43:34 2004
@@ -8,5 +8,15 @@
   BT_MSG_BITFIELD,
   BT_MSG_REQUEST,
   BT_MSG_PIECE,
-  BT_MSG_CANCEL
+  BT_MSG_CANCEL,
+  BT_MSG_KEYAMB=9,
 };
+
+#define BT_BY_HASH_BYTE 7
+#ifndef DISABLE_HASH_MODE
+#define BT_BY_HASH_BITMASK 0x01
+#else
+#define BT_BY_HASH_BITMASK 0x00
+#endif
+#define BT_MAX_HASH_LEN 1792
+
diff -ur libbt-1.02/include/peer.h libbt/include/peer.h
--- libbt-1.02/include/peer.h	Fri Jun 25 17:42:53 2004
+++ libbt/include/peer.h	Fri Jun 25 17:43:41 2004
@@ -59,6 +59,7 @@
     btRequestQueue inqueue;	/* request queue for incoming data */
     btRequestQueue queue;	/* outbound requests */
     time_t lastreceived;	/* time of last receipt of a piece */
+    char flags[8];		/* protocol flags */ /*FIXME! magic cookie*/
 } btPeer;
 
 typedef struct btPeerset {
diff -ur libbt-1.02/include/segmenter.h libbt/include/segmenter.h
--- libbt-1.02/include/segmenter.h	Fri Jun 25 17:42:57 2004
+++ libbt/include/segmenter.h	Fri Jun 25 17:43:46 2004
@@ -32,7 +32,8 @@
     int blocksize;
     btFile** file;
     int npieces;
-    char *hashes;		/* SHA data */
+    char (*hashes)[SHA_DIGEST_LENGTH];	/* SHA data */
+    int *hash_order;
     btPartialPiece *partial;
 } btFileSet;
 
@@ -47,6 +48,8 @@
 
 /* finds or allocates the given piece */
 btPartialPiece *seg_getPiece( btFileSet *fs, int piece);
+/* same, but by hash; note a return doesn't guarentee an unambiguous match */
+btPartialPiece *seg_getPiece_by_hash( btFileSet *fs, char *hash, int hlen);
 
 int seg_writebuf( btFileSet *fs, int piece, int offset, char *buf, int len) ;
 
diff -ur libbt-1.02/src/peer.c libbt/src/peer.c
--- libbt-1.02/src/peer.c	Fri Jun 25 18:09:48 2004
+++ libbt/src/peer.c	Fri Jun 25 18:09:25 2004
@@ -54,9 +54,9 @@
 
 /* prototypes */
 int
-send_have( btPeer *peer, int piece);
+send_have( btFileSet *, btPeer *peer, int piece);
 int 
-send_cancel( btPeer *peer, int piece, int offs, int len);
+send_cancel( btFileSet *, btPeer *peer, int piece, int offs, int len);
 int
 send_choke( btPeer *peer, int choke) ;
 
@@ -216,7 +216,11 @@
     *    \x13BitTorrent protocol\0\0\0\0\0\0\0\0<sha1 info hash><20byte peerid>
     */
     char shake[0x14] = "\x13" "BitTorrent protocol";
-    char flags[8] = { 0 };
+#ifndef DISABLE_HASH_MODE
+    char flags[8] = "\0\0\0\0\0\0\0\x01";
+#else
+    char flags[8] = "\0\0\0\0\0\0\0\x00";
+#endif
     int err;
 #if 0
     printf("peer_send_handshake\n");
@@ -249,6 +253,11 @@
  */
 #define PROTO_LENGTH 0x14
 #define FLAGS_LENGTH 0x08
+#ifndef DISABLE_HASH_MODE
+#define KNOWN_FLAGS "\0\0\0\0\0\0\0\x01"
+#else
+#define KNOWN_FLAGS "\0\0\0\0\0\0\0\x00"
+#endif
 static int check_handshake( btContext *ctx, btPeer *peer, char *shake, int len) 
 {
     char *flags = shake + PROTO_LENGTH;
@@ -265,16 +274,28 @@
 	peer->ios.error = BTERR_PROTOCOL_ID;
 	return -1;
     }
-    if (len >= PROTO_LENGTH + FLAGS_LENGTH 
-	    && memcmp( flags, "\0\0\0\0\0\0\0\0", FLAGS_LENGTH)!= 0) 
-    {
-	/* bad flags */
+    if (len >= PROTO_LENGTH+FLAGS_LENGTH) {
 	int i;
-	printf("Unknown flags in handshake: ");
-	for (i=0; i<8; i++) {
-	    printf("%02x", flags[i]);
+	int unk=0;
+	for (i=0; i<FLAGS_LENGTH; ++i) {
+	    if (flags[i] & ~(KNOWN_FLAGS[i])) unk=1;
+	}
+	if (unk) {
+	    printf("Unknown flags in handshake: ");
+	    for (i=0; i<8; i++) {
+		printf("%02x", flags[i] & ~(KNOWN_FLAGS[i]));
+	    }
+	    printf("\n");
+	}
+#if 0
+	if(!(flags[BT_BY_HASH_BYTE]&BT_BY_HASH_BITMASK)&&
+!(ctx->downloads[peer->download]->fileset.haveids)) {
+	    /* If the other end needs by_index mode, and we don't have the
+	    * indicies, not much we can do */
+	    peer->ios.error = BTERR_UNKNOWN_FLAGS;
+	    return -1;
 	}
-	printf("\n");
+#endif
     }
     if (len >= PROTO_LENGTH + FLAGS_LENGTH + SHA_DIGEST_LENGTH)
     {
@@ -321,6 +342,7 @@
     }
     if (len >= PROTO_LENGTH + FLAGS_LENGTH + SHA_DIGEST_LENGTH + IDSIZE) {
 	memcpy( peer->id, id, IDSIZE);
+	memcpy(peer->flags, flags, FLAGS_LENGTH);
     }
     return 0;
 }
@@ -654,7 +676,7 @@
 				p->currentPiece = NULL;
 				/* send cancels, check that requests work */
 				while ((req = dequeue_request( &p->inqueue)) != NULL) {
-				    if (send_cancel(p, req->block, req->offset, 
+				    if (send_cancel(&dl->fileset, p, req->block, req->offset, 
 					    req->length
 					)) {
 					p->state = PEER_ERROR;
@@ -666,7 +688,7 @@
 				peer_send_request( ctx, p);
 			    }
 			    if (p->state == PEER_GOOD) {
-				if (send_have( p, piece)) {
+				if (send_have(&dl->fileset, p, piece)) {
 				    p->state = PEER_ERROR;
 				    err = -1;
 				    goto cleanup;
@@ -729,6 +751,408 @@
     return res;
 }
 
+/*
+ * Return 1 if there are more messages waiting
+ * Return 0 on success,
+ * return -1 on error  (peer->ios.error contains the error code)
+ * return -2 on unknown message.
+ */
+
+#define WORKBUF 80
+int
+recv_peermsg_h( btContext *ctx, btPeer *peer)
+{
+    btDownload *dl = ctx->downloads[peer->download];
+    btPartialPiece *pp;
+    int32_t nbo_len;
+    int len;
+    char msg[WORKBUF];
+    char *nmsg, *param;
+    int res = 0;
+    int err;
+    int32_t nbo;
+
+    DIE_UNLESS(peer->download<ctx->downloadcount);
+
+    err = kStream_fpeek( &peer->ios, (char *)&nbo_len, sizeof(nbo_len));
+    if (err < 0) return -1;
+    assert(err == sizeof(nbo_len));
+    len = ntohl(nbo_len);
+    if (len <= WORKBUF) {
+	nmsg = msg;
+    } else if (len <= MAXMESSAGE) {
+	nmsg = btmalloc(len+sizeof(int32_t));
+    } else {	/* Too big a packet, kill the peer. */
+        peer->ios.error = BTERR_LARGEPACKET;
+	return -1;
+    }
+#if 0
+    printf("A/%d: looking for %d bytes %d available (addr %d)\n", peer->ios.fd, len+4, kStream_iqlen( &peer->ios), kStream_in_addr(&peer->ios));
+#endif
+    if (len == 0) {
+	/* keep alive */
+	err = kStream_fread( &peer->ios, (char *)&nbo_len, sizeof(nbo_len));
+	DIE_UNLESS(err == sizeof(nbo_len));
+	return 0;
+    }
+    DIE_UNLESS(len <= MAXMESSAGE && len >= 0);
+    err = kStream_fread( &peer->ios, nmsg, len + sizeof(int32_t));
+    if (err < 0) goto cleanup;
+    DIE_UNLESS( err == len + (int)sizeof(int32_t));
+
+#if 0
+    printf("A/%d: got message %d (len %d)\n", peer->ios.fd, nmsg[4], len+sizeof(int32_t));
+#endif
+
+    /* got message */
+    param = nmsg+5;
+    switch (nmsg[4]) {
+	case BT_MSG_CHOKE:
+	    if (len!=1) {
+		peer->ios.error=BTERR_LARGEPACKET;
+		res=-1;
+	    }
+	    if (peer->remote.choked == 0) {
+		int rst;
+		peer->remote.choked = 1;
+
+                if (peer->currentPiece) {
+		    btPartialPiece *piece = peer->currentPiece;
+		    /* cancel this peer's piece and reassign */
+		    bs_clr( &dl->requested, piece->piecenumber);
+		    rst = bs_firstClr( &piece->filled);
+		    if (rst >= 0) {
+			piece->nextbyteReq = rst;
+		    }
+		}
+		peer->currentPiece=NULL;
+
+		clear_request_queue( &peer->inqueue);
+		if (peer->local.interested) {
+		    /* stop the rate counter */
+		    stop_rate_timer( &peer->remote, time(NULL));
+		}
+	    }
+	    break;
+	case BT_MSG_UNCHOKE:
+	    if(len!=1) {
+		peer->ios.error=BTERR_LARGEPACKET;
+		res=-1;
+	    }
+	    time(&peer->lastreceived);
+	    if (peer->remote.choked == 1) {
+		peer->remote.choked = 0;
+		/* queue requests */
+		if (!peer->local.interested) {
+		    /* recheck interest */
+		    int interest = update_interested( ctx, peer);
+		    if (!interest) break;
+		}
+		start_rate_timer( &peer->remote, time(NULL));
+		if (peer_send_request( ctx, peer)) { res = -1; goto cleanup; }
+	    }
+	    break;
+	case BT_MSG_INTERESTED:
+	    if(len!=1) {
+		peer->ios.error=BTERR_LARGEPACKET;
+		res=-1;
+	    }
+	    peer->remote.interested = 1;
+	    ctx->downloads[peer->download]->peerset.interestedpeers++;
+	    break;
+	case BT_MSG_NOTINTERESTED:
+	    if(len!=1) {
+		peer->ios.error=BTERR_LARGEPACKET;
+		res=-1;
+	    }
+	    peer->remote.interested = 0;
+	    ctx->downloads[peer->download]->peerset.interestedpeers--;
+	    break;
+	case BT_MSG_HAVE:
+	    {
+		int block;
+		int interest;
+
+		if(len>SHA_DIGEST_LENGTH+1) {
+		    /* the protocol allows for larger hashes, but this is the
+		    * largest hash we support */
+		    peer->ios.error=BTERR_LARGEPACKET;
+		    res=-1;
+		} else {
+		    if((pp=seg_getPiece_by_hash(&dl->fileset, param, len-1))) {
+			if(pp->piecenumber>=0) {
+
+			    UNSHIFT_INT32(param,nbo,block);
+			    bs_set( &peer->blocks, block);
+			    interest = update_interested( ctx, peer);
+			    if (interest && 
+				!peer->remote.snubbed && 
+				!peer->remote.choked)
+			    {
+				start_rate_timer( &peer->remote, time(NULL));
+				if (peer_send_request( ctx, peer)) { res = -1; goto cleanup; }
+			    }
+		        } else {
+			    /* Can't resolve the piece number. Perfectly legal
+			    * protocol, but we can't do anything with it */
+			}
+		    } else {
+			peer->ios.error=BTERR_HASH_MISMATCH;
+			res=-1;
+		    }
+		}
+	    }
+	    break;
+	case BT_MSG_BITFIELD:
+	    if((len-1)<<3 > dl->fileset.npieces) {
+		peer->ios.error=BTERR_LARGEPACKET;
+		res=-1;
+	    } else {
+		char *hostbo;
+		int i;
+		hostbo=btmalloc(len-1);
+		DIE_UNLESS(hostbo); /* FIXME */
+		bzero(hostbo, len-1);
+
+		i=(len-1)<<3;
+		if(i>dl->fileset.completed.nbits) i=dl->fileset.completed.nbits;
+		--i;
+
+		for(; i>=0; --i) {
+			hostbo[dl->fileset.hash_order[i]>>3]|=((nmsg[5+(i>>3)]>>
+(7-(i&0x7)))&0x1)<<(7-(dl->fileset.hash_order[i]&0x7));
+                }
+
+
+		kBitSet_readBytes( &peer->blocks, hostbo, len-1);
+		btfree(hostbo);
+		update_interested( ctx, peer);
+	    }
+	    break;
+	case BT_MSG_REQUEST:
+	    {
+		int piece;
+		int offs;
+		int plen;
+
+		if(len<13) {
+		    /* insufficient payload, we can't work with this */
+		    peer->ios.error=BTERR_NETWORK_ERROR;
+		    res=-1;
+		} else if(len>SHA_DIGEST_LENGTH+9) {
+		    peer->ios.error=BTERR_LARGEPACKET;
+		    res=-1;
+		} else {
+		    UNSHIFT_INT32(param,nbo,offs);
+		    UNSHIFT_INT32(param,nbo,plen);
+		    len-=9; /* type byte and the above params */
+		    DIE_UNLESS(len>=4&&len<=SHA_DIGEST_LENGTH);
+
+		    if(!(pp=seg_getPiece_by_hash(&dl->fileset, param, len))) {
+			peer->ios.error=BTERR_HASH_MISMATCH;
+			res=-1;
+		    } else if((piece=pp->piecenumber)<0) {
+			/* This can't be fixed without a major redesign */
+			DIE_UNLESS(0);
+		    } else {
+
+		        if (peer->local.choked) {
+			    /* ignore requests from choked peers */
+			    break;
+			}
+
+			if (!bs_isSet(&peer->blocks, piece) && 
+			      bs_isSet(&ctx->downloads[peer->download]->fileset.completed, piece)) {
+			    queue_request( &peer->queue, piece, offs, plen);
+			} else {
+			    time_t now;
+			    time(&now);
+			    send_choke( peer, 1);
+			    stop_rate_timer( &peer->local, now);
+			    printf("%d: Choked by invalid request for block %d (%s have it)\n", peer->ios.fd, piece,
+				bs_isSet(&peer->blocks, piece)?"they":"we don't");
+			}
+		    }
+		}
+	    }
+	    break;
+	case BT_MSG_PIECE:
+	    {
+		int piece;
+		int offs;
+		int hlen;
+
+		if(len<14) {
+		    /* minimum length: type(1), offset (4), hashlen(1), piece
+		    * designator (min 4), piece (min 4) */
+		    peer->ios.error=BTERR_NETWORK_ERROR;
+		    res=-1;
+		    break;
+		}
+		len-=6; /* other lengths unknown, strip known */
+
+		UNSHIFT_INT32(param,nbo,offs);
+
+		hlen=*param++;
+		if(hlen>0x1F) {
+		    /* More than 1792 _bytes of _hash_?! */
+		    peer->ios.error=BTERR_LARGEPACKET;
+		    res=-1;
+		    break;
+		}
+		hlen=((hlen&0x3)|0x4)<<(hlen>>2);
+		if(hlen>SHA_DIGEST_LENGTH||hlen+4>len) {
+		    peer->ios.error=BTERR_LARGEPACKET;
+		    res=-1;
+		    break;
+		}
+
+		if(!(pp=seg_getPiece_by_hash(&dl->fileset, param, hlen))) {
+		    peer->ios.error=BTERR_HASH_MISMATCH;
+		    res=-1;
+		    break;
+		}
+		if((piece=pp->piecenumber)<0) {
+		    /* This can't be fixed without a major redesign */
+		    DIE_UNLESS(0);
+		}
+		/* grab our piece */
+		param+=hlen;
+		len-=hlen;
+
+		pp=peer->currentPiece;
+#if 0
+		printf("%d: got piece %d, offs %d, len %d\n", 
+			peer->ios.fd,
+			piece,
+			offs,
+			len);
+#endif
+		/* FIXME: check requests with ones we've sent */
+		time(&peer->lastreceived);
+		if (!pp || !remove_queued_request( &peer->inqueue, piece, offs, len)) {
+		    printf("%d: Unneeded data: piece %d %d+%d\n", peer->ios.fd, piece, offs, len);
+		} else {
+		    int done = seg_writebuf( &dl->fileset, piece, offs, param, len);
+		    if (done < 0) {
+			bts_perror(errno, "error writing buffer");
+			abort();
+		    }
+
+
+		    if (done) {
+			int i;
+			bs_clr( &dl->interested, piece);
+#if 0
+			bs_dump( "completed", &dl->fileset.completed);
+#endif
+			for (i=0; i<dl->peerset.len; i++) {
+			    btPeer *p = dl->peerset.peer[i];
+			    if (p->currentPiece == pp) {
+			        btRequest *req;
+
+				p->currentPiece = NULL;
+				/* send cancels, check that requests work */
+				while ((req = dequeue_request( &p->inqueue)) != NULL) {
+				    if (send_cancel(&dl->fileset, p, req->block, req->offset, 
+					    req->length
+					)) {
+					p->state = PEER_ERROR;
+					err = -1;
+					goto cleanup;
+				    }
+				}
+
+				peer_send_request( ctx, p);
+			    }
+			    if (p->state == PEER_GOOD) {
+				if (send_have(&dl->fileset, p, piece)) {
+				    p->state = PEER_ERROR;
+				    err = -1;
+				    goto cleanup;
+				}
+			    }
+			} /* for i in peerset.len */
+		    } /* if done */
+		    if (peer_send_request( ctx, peer)) { res = -1; goto cleanup; }
+		} /* if !unknown piece */
+	    }
+	    break;
+	case BT_MSG_CANCEL:
+	    {
+	        int piece, offs, len;
+
+		if(len<12) {
+		    peer->ios.error=BTERR_NETWORK_ERROR;
+		    res=-1;
+		} else if(len>SHA_DIGEST_LENGTH+8) {
+		    peer->ios.error=BTERR_LARGEPACKET;
+		    res=-1;
+		} else {
+		    /* this cancels a specific request */
+		    UNSHIFT_INT32(param,nbo,offs);
+		    UNSHIFT_INT32(param,nbo,len);
+
+		    len-=8;
+		    param+=8;
+		    if(!(pp=seg_getPiece_by_hash(&dl->fileset, param, len))) {
+			peer->ios.error=BTERR_HASH_MISMATCH;
+			res=-1;
+		    } else if((piece=pp->piecenumber)<0) {
+			/* This can't be fixed without a major redesign */
+			DIE_UNLESS(0);
+		    } else {
+#if 0
+			printf("%d: got cancel %d, offs %d, len %d\n", 
+				peer->ios.fd,
+				piece,
+				offs,
+				len);
+#endif
+			remove_queued_request( &peer->queue, piece, offs, len);
+		    }
+		}
+	    }
+	    break;
+	case BT_MSG_KEYAMB:
+	    /* we always use the full hash, this should never happen */
+	    peer->ios.error=BTERR_HASH_MISMATCH;
+	    res = -1;
+	    break;
+	default:
+	    /* unknown message */
+	    res = -2;
+	    break;
+    }
+
+cleanup:
+    /* cleanup */
+    if (err < 0) {
+	/* if there has been an error, report it */
+	res = -1;
+    } 
+
+    if (res == 0) {
+	/* check if there is another message waiting */
+	err = kStream_fpeek( &peer->ios, (char *)&nbo_len, sizeof(nbo_len));
+	if (err == sizeof(nbo_len)) {
+	    int tlen;
+	    tlen = ntohl(nbo_len) + sizeof(nbo_len);
+#if 0
+	    printf("B/looking for %d bytes %d available (addr %d)\n", tlen, kStream_iqlen( &peer->ios), kStream_in_addr(&peer->ios));
+#endif
+	    DIE_UNLESS(tlen <= MAXMESSAGE && tlen >= 0);
+	    if (kStream_iqlen( &peer->ios) >= tlen) res = 1;
+	}
+    }
+
+    if (len > WORKBUF) {
+	btfree(nmsg);
+    }
+    return res;
+}
+#undef WORKBUF
+
 
 /* 
  * returns 
@@ -780,34 +1204,66 @@
 
 
 int
-send_have( btPeer *peer, int piece) {
+send_have( btFileSet *fs, btPeer *peer, int piece) {
+	if(peer->flags[BT_BY_HASH_BYTE]&BT_BY_HASH_BITMASK) {
+		char msg[SHA_DIGEST_LENGTH+1];
+		msg[0]=BT_MSG_HAVE;
+		memcpy(msg+1, fs->hashes[piece], SHA_DIGEST_LENGTH);
+		return send_message( peer, msg, SHA_DIGEST_LENGTH+1);
+	} else {
     char buf[5];
     char *p=buf;
     int32_t nbo;
     SHIFT_BYTE( p, BT_MSG_HAVE);
     SHIFT_INT32( p, nbo, piece);
     return send_message( peer, buf, p-buf);
+	}
 }
 
 int 
-send_bitfield( btPeer *peer, kBitSet *set) {
+send_bitfield( btFileSet *fs, btPeer *peer, kBitSet *set) {
     int32_t nslen;
     char type=BT_MSG_BITFIELD;
     int res;
-    int nbytes = (set->nbits + 7)/8;
+    int nbytes = (set->nbits + 7)>>3;
 
     nslen = htonl( 1 + nbytes);
     res = kStream_fwrite( &peer->ios, (void *)&nslen, sizeof(nslen));
     if (res < 0) return -1;
     res = kStream_fwrite( &peer->ios, &type, 1);
     if (res < 0) return -2;
+	if(peer->flags[BT_BY_HASH_BYTE]&BT_BY_HASH_BITMASK) {
+		char *netbo;
+		int i;
+		netbo=btmalloc(nbytes);
+		DIE_UNLESS(netbo); /* FIXME */
+		bzero(netbo, nbytes);
+
+		for(i=0; i<set->nbits; ++i) {
+			netbo[i>>3]|=((set->bits[fs->hash_order[i]>>3]>>
+(7-(fs->hash_order[i]&0x7)))&0x1)<<(7-(i&0x7));
+		}
+		res = kStream_fwrite( &peer->ios, netbo, nbytes);
+		btfree(netbo);
+	} else {
     res = kStream_fwrite( &peer->ios, set->bits, nbytes);
+	}
     if (res < 0) return -3;
     return res;
 }
 
 int 
-send_request( btPeer *peer, int piece, int offs, int len) {
+send_request( btFileSet *fs, btPeer *peer, int piece, int offs, int len) {
+	if(peer->flags[BT_BY_HASH_BYTE]&BT_BY_HASH_BITMASK) {
+		/* only 9 extra bytes are needed, but alignment is a problem */
+		char _msg[SHA_DIGEST_LENGTH+12];
+		char *msg=_msg+3;
+		msg[0]=BT_MSG_REQUEST;
+		*(int32_t *)(msg+1)=htonl(offs);
+		*(int32_t *)(msg+5)=htonl(len);
+		memcpy(msg+9, fs->hashes[piece], SHA_DIGEST_LENGTH);
+		return send_message(peer, msg, SHA_DIGEST_LENGTH+9);
+	} else {
     char buf[13];
     char *p=buf;
     int32_t nbo;
@@ -819,10 +1275,27 @@
     printf("%d: send_request( ..., %d, %d, %d)\n", peer->ios.fd, piece, offs, len);
 #endif
     return send_message( peer, buf, p-buf);
+	}
 }
 
 int 
-send_piece( btPeer *peer, int piece, int offs, char* cbuf, int len) {
+send_piece( btFileSet *fs, btPeer *peer, int piece, int offs, char* cbuf,
+int len) {
+	if(peer->flags[BT_BY_HASH_BYTE]&BT_BY_HASH_BITMASK) {
+		int ret;
+		char msg[SHA_DIGEST_LENGTH+12];
+		*(int32_t *)msg=htonl(len+SHA_DIGEST_LENGTH+6); /* length */
+		msg[4]=BT_MSG_PIECE; /* type */
+		*(int32_t *)(msg+12)=htonl(offs);
+		memcpy(msg+5, msg+12, 4); /* offset */
+		msg[9]=0x09; /* ((0x09&0x3)|0x4)<<(0x09>>2)==20 hlen */
+		memcpy(msg+10, fs->hashes[piece], SHA_DIGEST_LENGTH);
+
+		ret=kStream_fwrite(&peer->ios, msg, SHA_DIGEST_LENGTH+10);
+		if(ret<0) return ret;
+
+		return kStream_fwrite(&peer->ios, cbuf, len);
+	} else {
     int tlen;
     int32_t nslen;
     char buf[9];
@@ -844,6 +1317,7 @@
     res = kStream_fwrite( &peer->ios, cbuf, len);
     if (res < 0) return -3;
     return res;
+	}
 }
 
 int 
@@ -857,7 +1331,7 @@
     err = seg_readbuf( fs, req->block, req->offset, g_filebuffer, req->length);
     if (err < 0) return err;
     
-    err = send_piece( peer, req->block, req->offset, g_filebuffer, req->length);
+    err = send_piece( fs, peer, req->block, req->offset, g_filebuffer, req->length);
     if (err < 0) return err;
 
     fs->ul += req->length;
@@ -866,7 +1340,17 @@
 }
 
 int 
-send_cancel( btPeer *peer, int piece, int offs, int len) {
+send_cancel( btFileSet *fs, btPeer *peer, int piece, int offs, int len) {
+	if(peer->flags[BT_BY_HASH_BYTE]&BT_BY_HASH_BITMASK) {
+		/* only 9 extra bytes are needed, but alignment is a problem */
+		char _msg[SHA_DIGEST_LENGTH+12];
+		char *msg=_msg+3;
+		msg[0]=BT_MSG_CANCEL;
+		*(int32_t *)(msg+1)=htonl(offs);
+		*(int32_t *)(msg+5)=htonl(len);
+		memcpy(msg+9, fs->hashes[piece], SHA_DIGEST_LENGTH);
+		return send_message(peer, msg, SHA_DIGEST_LENGTH+9);
+	} else {
     char buf[13];
     char *p=buf;
     int32_t nbo;
@@ -878,6 +1362,7 @@
     printf("%d: send_cancel (%d, %d + %d)\n", peer->ios.fd, piece, offs, len);
 #endif
     return send_message( peer, buf, p-buf);
+	}
 }
 
 #if 0
@@ -1028,7 +1513,10 @@
 #if 0
     printf("%d: before message state %d\n", p->ios.fd, p->state);
 #endif
-    if (p->state == PEER_INIT) {
+    if (p->state == PEER_GOOD) {
+	if(p->flags[BT_BY_HASH_BYTE]&BT_BY_HASH_BITMASK) res=recv_peermsg_h(ctx, p);
+	else res = recv_peermsg( ctx, p);
+    } else if (p->state == PEER_INIT) {
 	/* socket isn't yet completely initialized */
 	/* int stat = ctx->statmap[p->ios.fd]; */
 	printf("Waiting for %d to complete connection.\n", p->ios.fd);
@@ -1044,8 +1532,6 @@
 	    res = 1;  /* assume there is another message */
 	    p->state = PEER_GOOD;
 	}
-    } else if (p->state == PEER_GOOD) {
-	res = recv_peermsg( ctx, p);
     } else {
 	printf("%d: Peer in unexpected state %d\n", p->ios.fd, p->state);
 	res = -3;
@@ -1206,7 +1692,7 @@
 	    len = REQUEST_SIZE;
 	}
 	piece->nextbyteReq += len;
-	res = send_request(p, piece->piecenumber, start, len);
+	res = send_request(&dl->fileset, p, piece->piecenumber, start, len);
 	queue_request( &p->inqueue, piece->piecenumber, start, len);
     }
     return res;
@@ -1217,7 +1703,8 @@
 #if 0
     printf("peer_send_bitfield\n");
 #endif
-    return send_bitfield( peer, &ctx->downloads[peer->download]->fileset.completed);
+    return send_bitfield( &ctx->downloads[peer->download]->fileset, peer,
+&ctx->downloads[peer->download]->fileset.completed);
 }
 
 static int compare_rate( btContext *ctx, btPeer *a, btPeer *b, time_t now) {
diff -ur libbt-1.02/src/segmenter.c libbt/src/segmenter.c
--- libbt-1.02/src/segmenter.c	Fri Jun 25 17:43:19 2004
+++ libbt/src/segmenter.c	Fri Jun 25 18:06:27 2004
@@ -62,6 +62,17 @@
     return p;
 }
 
+btPartialPiece *seg_getPiece_by_hash(btFileSet *fs, char *hash, int hlen) {
+	int i;
+
+	assert(fs);
+	for(i=0; i<fs->npieces; ++i) {
+		if(!memcmp(fs->hashes[i], hash, hlen)) return seg_getPiece(fs,
+i);
+	}
+	return NULL;
+}
+
 int 
 seg_piecelen( btFileSet *fs, int piece) {
     int blocklen;
@@ -134,25 +145,51 @@
     int len = seg_piecelen( fs, piece);
 
     SHA1( buf, len, digest);
-    r = memcmp( digest, &fs->hashes[piece*SHA_DIGEST_LENGTH], SHA_DIGEST_LENGTH);
+    r = memcmp( digest, fs->hashes[piece], SHA_DIGEST_LENGTH);
     return r==0;
 }
 
 
+static int _hashcmp(const char **a, const char **b)
+{
+	return memcmp(*a, *b, SHA_DIGEST_LENGTH);
+}
+
 btFileSet*
 btFileSet_create( btFileSet *fs, int npieces, int blocksize, const char *hashbuf) {
     if (!fs) {
 	fs = btcalloc( 1, sizeof(btFileSet));
-	DIE_UNLESS(fs);
+	if(!fs) return NULL;
     }
     fs->npieces = npieces;
     fs->blocksize = blocksize;
     fs->dl = 0;
     fs->ul = 0;
-    fs->hashes = btmalloc( npieces * SHA_DIGEST_LENGTH);
+    if(!(fs->hashes = btmalloc( npieces * SHA_DIGEST_LENGTH))) goto abort;
+    if(!(fs->hash_order = btmalloc( npieces * sizeof(int)))) goto abort;
+    {
+	int i;
+	const char **hashtmp;
+	/* alloca() is tempting, but this could be largish */
+	if(!(hashtmp=btmalloc(npieces * sizeof(char *)))) goto abort;
+	for(i=0; i<npieces; ++i) hashtmp[i]=hashbuf+SHA_DIGEST_LENGTH*i;
+	qsort((void *)hashtmp, npieces, sizeof(char *),
+(int (*)(const void *, const void *))_hashcmp);
+	for(i=0; i<npieces; ++i) fs->hash_order[i]=(hashtmp[i]-(char *)hashbuf)
+/ SHA_DIGEST_LENGTH;
+	btfree(hashtmp);
+    }
     memcpy(fs->hashes, hashbuf, npieces * SHA_DIGEST_LENGTH);
     kBitSet_create(&fs->completed, npieces);
     return fs;
+
+abort:
+    if(fs) {
+	if(fs->hash_order) btfree(fs->hash_order);
+	if(fs->hashes) btfree(fs->hashes);
+	btfree(fs);
+    }
+    return NULL;
 }
 
 void btFileSet_destroy( btFileSet *fs) {
@@ -163,6 +200,7 @@
 	btfree( fs->file[i]);
     }
     btfree( fs->file);
+    if(fs->hash_order) btfree( fs->hash_order);
     btfree( fs->hashes);
     kBitSet_finit(&fs->completed);
     p=fs->partial;
