From 7eb62eec0df0f58b192e96f90470a890287da4dd Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Wed, 8 Nov 2017 15:00:18 +1100 Subject: [PATCH] Move some code around --- client.go | 183 --------------------------------------------------- handshake.go | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ misc.go | 18 +++++ 3 files changed, 198 insertions(+), 183 deletions(-) create mode 100644 handshake.go diff --git a/client.go b/client.go index 47d3b54b..1779c289 100644 --- a/client.go +++ b/client.go @@ -2,10 +2,8 @@ package torrent import ( "bufio" - "bytes" "context" "crypto/rand" - "encoding/hex" "errors" "expvar" "fmt" @@ -711,187 +709,6 @@ func (cl *Client) incomingPeerPort() int { return port } -// Convert a net.Addr to its compact IP representation. Either 4 or 16 bytes -// per "yourip" field of http://www.bittorrent.org/beps/bep_0010.html. -func addrCompactIP(addr net.Addr) (string, error) { - host, _, err := net.SplitHostPort(addr.String()) - if err != nil { - return "", err - } - ip := net.ParseIP(host) - if v4 := ip.To4(); v4 != nil { - if len(v4) != 4 { - panic(v4) - } - return string(v4), nil - } - return string(ip.To16()), nil -} - -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 - peerID [20]byte -) - -func (pex *peerExtensionBytes) SupportsExtended() bool { - return pex[5]&0x10 != 0 -} - -func (pex *peerExtensionBytes) SupportsDHT() bool { - return pex[7]&0x01 != 0 -} - -func (pex *peerExtensionBytes) SupportsFast() bool { - return pex[7]&0x04 != 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(hex.EncodeToString(res.peerExtensionBytes[:]), 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 -// connection in the message loop. -type deadlineReader struct { - nc net.Conn - r io.Reader -} - -func (r deadlineReader) Read(b []byte) (int, error) { - // Keep-alives should be received every 2 mins. Give a bit of gracetime. - err := r.nc.SetReadDeadline(time.Now().Add(150 * time.Second)) - if err != nil { - return 0, fmt.Errorf("error setting read deadline: %s", err) - } - return r.r.Read(b) -} - -func handleEncryption( - rw io.ReadWriter, - skeys mse.SecretKeyIter, - policy EncryptionPolicy, -) ( - ret io.ReadWriter, - headerEncrypted bool, - cryptoMethod uint32, - err error, -) { - if !policy.ForceEncryption { - var protocol [len(pp.Protocol)]byte - _, err = io.ReadFull(rw, protocol[:]) - if err != nil { - return - } - rw = struct { - io.Reader - io.Writer - }{ - io.MultiReader(bytes.NewReader(protocol[:]), rw), - rw, - } - if string(protocol[:]) == pp.Protocol { - ret = rw - return - } - } - headerEncrypted = true - ret, err = mse.ReceiveHandshake(rw, skeys, func(provides uint32) uint32 { - cryptoMethod = func() uint32 { - switch { - case policy.ForceEncryption: - return mse.CryptoMethodRC4 - case policy.DisableEncryption: - return mse.CryptoMethodPlaintext - case policy.PreferNoEncryption && provides&mse.CryptoMethodPlaintext != 0: - return mse.CryptoMethodPlaintext - default: - return mse.DefaultCryptoSelector(provides) - } - }() - return cryptoMethod - }) - return -} - func (cl *Client) initiateHandshakes(c *connection, t *Torrent) (ok bool, err error) { if c.headerEncrypted { var rw io.ReadWriter diff --git a/handshake.go b/handshake.go new file mode 100644 index 00000000..b79b5fd6 --- /dev/null +++ b/handshake.go @@ -0,0 +1,180 @@ +package torrent + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "net" + "time" + + pp "github.com/anacrolix/torrent/peer_protocol" + + "github.com/anacrolix/missinggo" + "github.com/anacrolix/torrent/metainfo" + "github.com/anacrolix/torrent/mse" +) + +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 + peerID [20]byte +) + +func (pex *peerExtensionBytes) SupportsExtended() bool { + return pex[5]&0x10 != 0 +} + +func (pex *peerExtensionBytes) SupportsDHT() bool { + return pex[7]&0x01 != 0 +} + +func (pex *peerExtensionBytes) SupportsFast() bool { + return pex[7]&0x04 != 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(hex.EncodeToString(res.peerExtensionBytes[:]), 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 +// connection in the message loop. +type deadlineReader struct { + nc net.Conn + r io.Reader +} + +func (r deadlineReader) Read(b []byte) (int, error) { + // Keep-alives should be received every 2 mins. Give a bit of gracetime. + err := r.nc.SetReadDeadline(time.Now().Add(150 * time.Second)) + if err != nil { + return 0, fmt.Errorf("error setting read deadline: %s", err) + } + return r.r.Read(b) +} + +func handleEncryption( + rw io.ReadWriter, + skeys mse.SecretKeyIter, + policy EncryptionPolicy, +) ( + ret io.ReadWriter, + headerEncrypted bool, + cryptoMethod uint32, + err error, +) { + if !policy.ForceEncryption { + var protocol [len(pp.Protocol)]byte + _, err = io.ReadFull(rw, protocol[:]) + if err != nil { + return + } + rw = struct { + io.Reader + io.Writer + }{ + io.MultiReader(bytes.NewReader(protocol[:]), rw), + rw, + } + if string(protocol[:]) == pp.Protocol { + ret = rw + return + } + } + headerEncrypted = true + ret, err = mse.ReceiveHandshake(rw, skeys, func(provides uint32) uint32 { + cryptoMethod = func() uint32 { + switch { + case policy.ForceEncryption: + return mse.CryptoMethodRC4 + case policy.DisableEncryption: + return mse.CryptoMethodPlaintext + case policy.PreferNoEncryption && provides&mse.CryptoMethodPlaintext != 0: + return mse.CryptoMethodPlaintext + default: + return mse.DefaultCryptoSelector(provides) + } + }() + return cryptoMethod + }) + return +} diff --git a/misc.go b/misc.go index 9d26738c..6ff47775 100644 --- a/misc.go +++ b/misc.go @@ -2,6 +2,7 @@ package torrent import ( "errors" + "net" "github.com/anacrolix/torrent/metainfo" pp "github.com/anacrolix/torrent/peer_protocol" @@ -85,3 +86,20 @@ func chunkIndexSpec(index int, pieceLength, chunkSize pp.Integer) chunkSpec { func connLessTrusted(l, r *connection) bool { return l.netGoodPiecesDirtied() < r.netGoodPiecesDirtied() } + +// Convert a net.Addr to its compact IP representation. Either 4 or 16 bytes +// per "yourip" field of http://www.bittorrent.org/beps/bep_0010.html. +func addrCompactIP(addr net.Addr) (string, error) { + host, _, err := net.SplitHostPort(addr.String()) + if err != nil { + return "", err + } + ip := net.ParseIP(host) + if v4 := ip.To4(); v4 != nil { + if len(v4) != 4 { + panic(v4) + } + return string(v4), nil + } + return string(ip.To16()), nil +} -- 2.48.1