How to get responses to ut_metadata piece request ? (node.js Bit Torrent BEP 0009)

686 views Asked by At

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 ?

1

There are 1 answers

1
Olivier Pichou On BEST ANSWER

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)