BitTorrent Handshake Protocol
once you parse .torrent file you get the following components:
- announce (url to which you need to query to get peers, also known as tracker)
- info hash (torrent info hash which contains stuff like what files are present and the structure etc..)
You need to get the peers who have the data you want to download, you request to announce URL with info hash and other info to send you all the peers address(ip and port).
To start talking to these peers you need to first create a TCP handshake with them, this is the protocol that talk about the structure of this protocol
You have the following fields in the protocol which are sent to peer over the TCP connection:
| filed | size (in bytes) | purpose | value |
|---|---|---|---|
| pstrlen | 1 | length of the protocol name | 19 always |
| pstr | 19 | name of the protocol | “BitTorrent protocol” always |
| reserved | 8 | sometimes empty, sometimes contains DHT (Distributed Hash Table) | |
| info_hash | 20 | 20 bytes info_hash which we parsed from .torrent file | [20]byte("info_hash"), info_hash: structure of the files/names of the files itself which we want to download |
| peer_id | 20 | identifying yourself in the network | [20]byte() peer_id generated by you |
you create a connection to the peer ip and port with the following info
// standard go struct for BitTorrent handshake protocol
type PeerHandshake struct {
PstrLen uint8 // always 19
Pstr string // "BitTorrent protocol"
Reserved [8]byte // usually empty, sometimes contains DHT
InfoHash [20]byte // 20 bytes info_hash read from .torrent file
PeerID [20]byte // 20 bytes peerid generated by you
}
type PeerConnection struct {
Conn net.TCPConn
HandShake *PeerHandshake
}
func HandShake(peer_id [20]byte, infoHash [20]byte) net.TCPConn {
ph := NewHandShake(infoHash, peerId)
address := fmt.Sprintf("%s:%d", peer.IP.String(), peer.Port) // peer ip,port extracted from talking to tracker/announce URL
conn, err := net.DialTimeout("tcp", address, timeout) // creating "tcp" conn
if err != nil {
return nil, err
}
_, err = conn.Write(ph.Encode()) // URL encode and send it over to peer
if err != nil {
return nil, err
}
buf := make([]byte, 68) // since the BitTorrent handshake protocol is exactly 68 bytes
_, err = conn.Read(buf)
if err != nil {
conn.Close()
return nil, err
}
decodedPH, err := DecodeHandShake(buf) // recieved output will be of the same structure as defined in PeerHandshakeProtocol struct
if decodedPH.InfoHash != infoHash {
return nil, fmt.Errorf("infoHash mismatch, expected: %v, got: %v", ph.InfoHash, decodedPH.InfoHash)
}
return &PeerConnection{Conn: conn, HandShake: decodedPH}, nil
}Here in the above code you can see struct: PeerConnection which has Conn which is has an active TCP connection with the peer, which you can use it to download the data wanted.