Expose handshake stuff in peer_protocol
This commit is contained in:
parent
a69cd7bb9b
commit
76a3c0891a
@ -57,7 +57,7 @@ type Client struct {
|
|||||||
dhtServers []*dht.Server
|
dhtServers []*dht.Server
|
||||||
ipBlockList iplist.Ranger
|
ipBlockList iplist.Ranger
|
||||||
// Our BitTorrent protocol extension bytes, sent in our BT handshakes.
|
// Our BitTorrent protocol extension bytes, sent in our BT handshakes.
|
||||||
extensionBytes peerExtensionBytes
|
extensionBytes pp.PeerExtensionBits
|
||||||
|
|
||||||
// Set of addresses that have our client ID. This intentionally will
|
// Set of addresses that have our client ID. This intentionally will
|
||||||
// include ourselves if we end up trying to connect to our own address
|
// include ourselves if we end up trying to connect to our own address
|
||||||
@ -730,12 +730,12 @@ func (cl *Client) receiveHandshakes(c *connection) (t *Torrent, err error) {
|
|||||||
|
|
||||||
// Returns !ok if handshake failed for valid reasons.
|
// Returns !ok if handshake failed for valid reasons.
|
||||||
func (cl *Client) connBTHandshake(c *connection, ih *metainfo.Hash) (ret metainfo.Hash, ok bool, err error) {
|
func (cl *Client) connBTHandshake(c *connection, ih *metainfo.Hash) (ret metainfo.Hash, ok bool, err error) {
|
||||||
res, ok, err := handshake(c.rw(), ih, cl.peerID, cl.extensionBytes)
|
res, ok, err := pp.Handshake(c.rw(), ih, cl.peerID, cl.extensionBytes)
|
||||||
if err != nil || !ok {
|
if err != nil || !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ret = res.Hash
|
ret = res.Hash
|
||||||
c.PeerExtensionBytes = res.peerExtensionBytes
|
c.PeerExtensionBytes = res.PeerExtensionBits
|
||||||
c.PeerID = res.PeerID
|
c.PeerID = res.PeerID
|
||||||
c.completedHandshake = time.Now()
|
c.completedHandshake = time.Now()
|
||||||
return
|
return
|
||||||
|
@ -90,7 +90,7 @@ type connection struct {
|
|||||||
PeerInterested bool
|
PeerInterested bool
|
||||||
PeerChoked bool
|
PeerChoked bool
|
||||||
PeerRequests map[request]struct{}
|
PeerRequests map[request]struct{}
|
||||||
PeerExtensionBytes peerExtensionBytes
|
PeerExtensionBytes pp.PeerExtensionBits
|
||||||
// The pieces the peer has claimed to have.
|
// The pieces the peer has claimed to have.
|
||||||
peerPieces bitmap.Bitmap
|
peerPieces bitmap.Bitmap
|
||||||
// The peer has everything. This can occur due to a special message, when
|
// The peer has everything. This can occur due to a special message, when
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package torrent
|
package torrent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
pp "github.com/anacrolix/torrent/peer_protocol"
|
||||||
"crypto"
|
"crypto"
|
||||||
"expvar"
|
"expvar"
|
||||||
)
|
)
|
||||||
@ -16,8 +17,8 @@ const (
|
|||||||
pexExtendedId
|
pexExtendedId
|
||||||
)
|
)
|
||||||
|
|
||||||
func defaultPeerExtensionBytes() peerExtensionBytes {
|
func defaultPeerExtensionBytes() PeerExtensionBits {
|
||||||
return newPeerExtensionBytes(ExtensionBitDHT, ExtensionBitExtended, ExtensionBitFast)
|
return pp.NewPeerExtensionBytes(pp.ExtensionBitDHT, pp.ExtensionBitExtended, pp.ExtensionBitFast)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I could move a lot of these counters to their own file, but I suspect they
|
// I could move a lot of these counters to their own file, but I suspect they
|
||||||
|
133
handshake.go
133
handshake.go
@ -2,146 +2,15 @@ package torrent
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/anacrolix/missinggo"
|
|
||||||
|
|
||||||
"github.com/anacrolix/torrent/metainfo"
|
|
||||||
"github.com/anacrolix/torrent/mse"
|
"github.com/anacrolix/torrent/mse"
|
||||||
pp "github.com/anacrolix/torrent/peer_protocol"
|
pp "github.com/anacrolix/torrent/peer_protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExtensionBit uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
ExtensionBitDHT = 0 // http://www.bittorrent.org/beps/bep_0005.html
|
|
||||||
ExtensionBitExtended = 20 // http://www.bittorrent.org/beps/bep_0010.html
|
|
||||||
ExtensionBitFast = 2 // http://www.bittorrent.org/beps/bep_0006.html
|
|
||||||
)
|
|
||||||
|
|
||||||
func handshakeWriter(w io.Writer, bb <-chan []byte, done chan<- error) {
|
|
||||||
var err error
|
|
||||||
for b := range bb {
|
|
||||||
_, err = w.Write(b)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
done <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
type (
|
|
||||||
peerExtensionBytes [8]byte
|
|
||||||
)
|
|
||||||
|
|
||||||
func (me peerExtensionBytes) String() string {
|
|
||||||
return hex.EncodeToString(me[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPeerExtensionBytes(bits ...ExtensionBit) (ret peerExtensionBytes) {
|
|
||||||
for _, b := range bits {
|
|
||||||
ret.SetBit(b)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pex peerExtensionBytes) SupportsExtended() bool {
|
|
||||||
return pex.GetBit(ExtensionBitExtended)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pex peerExtensionBytes) SupportsDHT() bool {
|
|
||||||
return pex.GetBit(ExtensionBitDHT)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pex peerExtensionBytes) SupportsFast() bool {
|
|
||||||
return pex.GetBit(ExtensionBitFast)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pex *peerExtensionBytes) SetBit(bit ExtensionBit) {
|
|
||||||
pex[7-bit/8] |= 1 << (bit % 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pex peerExtensionBytes) GetBit(bit ExtensionBit) bool {
|
|
||||||
return pex[7-bit/8]&(1<<(bit%8)) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type handshakeResult struct {
|
|
||||||
peerExtensionBytes
|
|
||||||
PeerID
|
|
||||||
metainfo.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// ih is nil if we expect the peer to declare the InfoHash, such as when the
|
|
||||||
// peer initiated the connection. Returns ok if the handshake was successful,
|
|
||||||
// and err if there was an unexpected condition other than the peer simply
|
|
||||||
// abandoning the handshake.
|
|
||||||
func handshake(sock io.ReadWriter, ih *metainfo.Hash, peerID [20]byte, extensions peerExtensionBytes) (res handshakeResult, ok bool, err error) {
|
|
||||||
// Bytes to be sent to the peer. Should never block the sender.
|
|
||||||
postCh := make(chan []byte, 4)
|
|
||||||
// A single error value sent when the writer completes.
|
|
||||||
writeDone := make(chan error, 1)
|
|
||||||
// Performs writes to the socket and ensures posts don't block.
|
|
||||||
go handshakeWriter(sock, postCh, writeDone)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
close(postCh) // Done writing.
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// Wait until writes complete before returning from handshake.
|
|
||||||
err = <-writeDone
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error writing: %s", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
post := func(bb []byte) {
|
|
||||||
select {
|
|
||||||
case postCh <- bb:
|
|
||||||
default:
|
|
||||||
panic("mustn't block while posting")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
post([]byte(pp.Protocol))
|
|
||||||
post(extensions[:])
|
|
||||||
if ih != nil { // We already know what we want.
|
|
||||||
post(ih[:])
|
|
||||||
post(peerID[:])
|
|
||||||
}
|
|
||||||
var b [68]byte
|
|
||||||
_, err = io.ReadFull(sock, b[:68])
|
|
||||||
if err != nil {
|
|
||||||
err = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if string(b[:20]) != pp.Protocol {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
missinggo.CopyExact(&res.peerExtensionBytes, b[20:28])
|
|
||||||
missinggo.CopyExact(&res.Hash, b[28:48])
|
|
||||||
missinggo.CopyExact(&res.PeerID, b[48:68])
|
|
||||||
peerExtensions.Add(res.peerExtensionBytes.String(), 1)
|
|
||||||
|
|
||||||
// TODO: Maybe we can just drop peers here if we're not interested. This
|
|
||||||
// could prevent them trying to reconnect, falsely believing there was
|
|
||||||
// just a problem.
|
|
||||||
if ih == nil { // We were waiting for the peer to tell us what they wanted.
|
|
||||||
post(res.Hash[:])
|
|
||||||
post(peerID[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wraps a raw connection and provides the interface we want for using the
|
// Wraps a raw connection and provides the interface we want for using the
|
||||||
// connection in the message loop.
|
// connection in the message loop.
|
||||||
type deadlineReader struct {
|
type deadlineReader struct {
|
||||||
@ -201,3 +70,5 @@ func handleEncryption(
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PeerExtensionBits=pp.PeerExtensionBits
|
137
peer_protocol/handshake.go
Normal file
137
peer_protocol/handshake.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package peer_protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/anacrolix/missinggo"
|
||||||
|
"github.com/anacrolix/torrent/metainfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExtensionBit uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExtensionBitDHT = 0 // http://www.bittorrent.org/beps/bep_0005.html
|
||||||
|
ExtensionBitExtended = 20 // http://www.bittorrent.org/beps/bep_0010.html
|
||||||
|
ExtensionBitFast = 2 // http://www.bittorrent.org/beps/bep_0006.html
|
||||||
|
)
|
||||||
|
|
||||||
|
func handshakeWriter(w io.Writer, bb <-chan []byte, done chan<- error) {
|
||||||
|
var err error
|
||||||
|
for b := range bb {
|
||||||
|
_, err = w.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
PeerExtensionBits [8]byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func (me PeerExtensionBits) String() string {
|
||||||
|
return hex.EncodeToString(me[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPeerExtensionBytes(bits ...ExtensionBit) (ret PeerExtensionBits) {
|
||||||
|
for _, b := range bits {
|
||||||
|
ret.SetBit(b)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pex PeerExtensionBits) SupportsExtended() bool {
|
||||||
|
return pex.GetBit(ExtensionBitExtended)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pex PeerExtensionBits) SupportsDHT() bool {
|
||||||
|
return pex.GetBit(ExtensionBitDHT)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pex PeerExtensionBits) SupportsFast() bool {
|
||||||
|
return pex.GetBit(ExtensionBitFast)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pex *PeerExtensionBits) SetBit(bit ExtensionBit) {
|
||||||
|
pex[7-bit/8] |= 1 << (bit % 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pex PeerExtensionBits) GetBit(bit ExtensionBit) bool {
|
||||||
|
return pex[7-bit/8]&(1<<(bit%8)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandshakeResult struct {
|
||||||
|
PeerExtensionBits
|
||||||
|
PeerID [20]byte
|
||||||
|
metainfo.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// ih is nil if we expect the peer to declare the InfoHash, such as when the
|
||||||
|
// peer initiated the connection. Returns ok if the Handshake was successful,
|
||||||
|
// and err if there was an unexpected condition other than the peer simply
|
||||||
|
// abandoning the Handshake.
|
||||||
|
func Handshake(sock io.ReadWriter, ih *metainfo.Hash, peerID [20]byte, extensions PeerExtensionBits) (res HandshakeResult, ok bool, err error) {
|
||||||
|
// Bytes to be sent to the peer. Should never block the sender.
|
||||||
|
postCh := make(chan []byte, 4)
|
||||||
|
// A single error value sent when the writer completes.
|
||||||
|
writeDone := make(chan error, 1)
|
||||||
|
// Performs writes to the socket and ensures posts don't block.
|
||||||
|
go handshakeWriter(sock, postCh, writeDone)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
close(postCh) // Done writing.
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// Wait until writes complete before returning from handshake.
|
||||||
|
err = <-writeDone
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error writing: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
post := func(bb []byte) {
|
||||||
|
select {
|
||||||
|
case postCh <- bb:
|
||||||
|
default:
|
||||||
|
panic("mustn't block while posting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post([]byte(Protocol))
|
||||||
|
post(extensions[:])
|
||||||
|
if ih != nil { // We already know what we want.
|
||||||
|
post(ih[:])
|
||||||
|
post(peerID[:])
|
||||||
|
}
|
||||||
|
var b [68]byte
|
||||||
|
_, err = io.ReadFull(sock, b[:68])
|
||||||
|
if err != nil {
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(b[:20]) != Protocol {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
missinggo.CopyExact(&res.PeerExtensionBits, b[20:28])
|
||||||
|
missinggo.CopyExact(&res.Hash, b[28:48])
|
||||||
|
missinggo.CopyExact(&res.PeerID, b[48:68])
|
||||||
|
// peerExtensions.Add(res.PeerExtensionBits.String(), 1)
|
||||||
|
|
||||||
|
// TODO: Maybe we can just drop peers here if we're not interested. This
|
||||||
|
// could prevent them trying to reconnect, falsely believing there was
|
||||||
|
// just a problem.
|
||||||
|
if ih == nil { // We were waiting for the peer to tell us what they wanted.
|
||||||
|
post(res.Hash[:])
|
||||||
|
post(peerID[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
@ -182,12 +182,12 @@ func TestTorrentMetainfoIncompleteMetadata(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer nc.Close()
|
defer nc.Close()
|
||||||
|
|
||||||
var pex peerExtensionBytes
|
var pex PeerExtensionBits
|
||||||
pex.SetBit(ExtensionBitExtended)
|
pex.SetBit(pp.ExtensionBitExtended)
|
||||||
hr, ok, err := handshake(nc, &ih, [20]byte{}, pex)
|
hr, ok, err := pp.Handshake(nc, &ih, [20]byte{}, pex)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.True(t, hr.peerExtensionBytes.GetBit(ExtensionBitExtended))
|
assert.True(t, hr.PeerExtensionBits.GetBit(pp.ExtensionBitExtended))
|
||||||
assert.EqualValues(t, cl.PeerID(), hr.PeerID)
|
assert.EqualValues(t, cl.PeerID(), hr.PeerID)
|
||||||
assert.Equal(t, ih, hr.Hash)
|
assert.Equal(t, ih, hr.Hash)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user