7

I'm building a Bittorrent client using Node.js and am failing at getting answer from peers over the PWP metadata extension (BEP 0009)

I get peers from the DHT (BEP 0005) (where i announce), then send Handshake and Extended Handshake over PWP using a net Socket.

buildHandshake = (torrent, ext) => { // torrent contains mainly infoHash
    const buf = Buffer.alloc(68)
    buf.writeUInt8(19, 0)
    buf.write('BitTorrent protocol', 1)
    if (ext) {
        const big = new Uint64BE(1048576)
        big.toBuffer().copy(buf, 20)
    } else {
        buf.writeUInt32BE(0, 20)
        buf.writeUInt32BE(0, 24)
    }
    torrent.infoHashBuffer.copy(buf, 28)
    anon.nodeId().copy(buf, 48) // tool that generates a nodeId once.
    return buf
}

buildExtRequest = (id, msg) => {
    const size = msg.length + 1
    const buf = Buffer.alloc(size + 5)
    buf.writeUInt32BE(size, 0)
    buf.writeUInt8(20, 4)
    buf.writeUInt8(id, 5)
    msg.copy(buf, 6)
    return buf
}

const client = new net.Socket()
     client.connect(peer.port, peer.ip, () => {
        client.write(buildHandshake(torrent, true))
        const extHandshake = bencode.encode({
            m: {
                ut_metadata: 2,
            },
            metadata_size: self.metaDataSize, // 0 by default
            p: client.localPort,
            v: Buffer.from('Hypertube 0.1')
        })
        client.write(buildExtRequest(0, extHandshake))
})

From here, i get Handshakes and extended Hanshakes back (and sometimes Bitfields), then i require metadata pieces:

const req = bencode.encode({ msg_type: 0, piece: 0 })

// utMetadata is from extended Handshake dictionary m.ut_metadata 
client.write(message.buildExtRequest(utMetadata, req))

After what, i don't hear from the peer anymore. After 2mins without keeping alive, connection timeouts.

Has anybody got an idea why i don't get answered back ?

Olivier Pichou
  • 157
  • 1
  • 6
  • Have you tried to use WireShark and compare to a working client? – Encombe Dec 28 '16 at 22:03
  • a hexdump of your and their messages would be useful – the8472 Dec 29 '16 at 15:29
  • 2
    I worked on Wireshark and found out where the problem was: bencode.encode will encode key without value if value is undefined, and wont throw an error. This and weirdly, the length prefix was missing one byte, turning following messages into not-extended but rather _interested_ or _unchoke_ wrongly formated, causing peer to close. – Olivier Pichou Jan 01 '17 at 04:23
  • Btw `const big = new Uint64BE(1048576); big.toBuffer().copy(buf, 20)` == `buf.writeIntBE(1048576,20,8)`. or even `buf.fill` –  Jun 07 '17 at 00:34

1 Answers1

3

BitTorrent protocol message formating can be unclear if you're a first timer, like me.

message structure is always as follows (except for handshake):

<len><message>

where len is a UInt32 big endian of value message.length, message is whatever you're sending except handshake.

For example:

Extended protocol piece request: ut_metadata piece message

<len><id><extId><ut_metadata dict>

where:

  • len is a UInt32 big endian of value: size of ()
  • Id is a Uint8 of value 20 (it's the protocol extension indicator)
  • extId is a UInt8. Its value depends on the extended handshake received from the peer (in which the extId of ut_metadata exchange is given)
  • ut_metadata dict is a bencoded dictionary:

    { 'msg_type': 0, 'piece': 0 }

    d8:msg_typei0e5:piecei0ee

(here is on the first line the object - the dictionary - and on the second line is the same object once bencoded)

  • msg_type is 0 (it's the request message indicator for BEP 0009 piece request.

  • piece is the index of the piece you request (0 would be the first piece)

In general:

Not giving the right value to <len> will result in messages badly interpreted by peer, and therefore not getting the right answers, not getting any answer and eventually connection being closed (by the peer or through your own messages)

Gurwinder Singh
  • 38,557
  • 6
  • 51
  • 76
Olivier Pichou
  • 157
  • 1
  • 6
  • Hello, could I please check. Meaning to say, `len` is the length of the message? So, in this case, it will be the length of `d8:msg_typei0e5:piecei0ee`? – jake wong May 31 '20 at 14:00