mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-22 10:57:42 +08:00
We had a very nasty problem: handshakes were serial so incoming dials would wait for each other to finish handshaking. this was particularly problematic when handshakes hung-- nodes would not recover quickly. This led to gateways not bootstrapping peers fast enough. The approach taken here is to do what crypto/tls does: defer the handshake until Read/Write[1]. There are a number of reasons why this is _the right thing to do_: - it delays handshaking until it is known to be necessary (doing io) - it "accepts" before the handshake, getting the handshake out of the critical path entirely. - it defers to the user's parallelization of conn handling. users must implement this in some way already so use that, instead of picking constants surely to be wrong (how many handshakes to run in parallel?) [0] http://golang.org/src/crypto/tls/conn.go#L886
127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
// package secio handles establishing secure communication between two peers.
|
|
package secio
|
|
|
|
import (
|
|
"io"
|
|
|
|
ci "github.com/ipfs/go-ipfs/p2p/crypto"
|
|
|
|
msgio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio"
|
|
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
|
peer "github.com/ipfs/go-ipfs/p2p/peer"
|
|
)
|
|
|
|
// SessionGenerator constructs secure communication sessions for a peer.
|
|
type SessionGenerator struct {
|
|
LocalID peer.ID
|
|
PrivateKey ci.PrivKey
|
|
}
|
|
|
|
// NewSession takes an insecure io.ReadWriter, sets up a TLS-like
|
|
// handshake with the other side, and returns a secure session.
|
|
// The handshake isn't run until the connection is read or written to.
|
|
// See the source for the protocol details and security implementation.
|
|
// The provided Context is only needed for the duration of this function.
|
|
func (sg *SessionGenerator) NewSession(ctx context.Context, insecure io.ReadWriteCloser) (Session, error) {
|
|
return newSecureSession(ctx, sg.LocalID, sg.PrivateKey, insecure)
|
|
}
|
|
|
|
type Session interface {
|
|
// ReadWriter returns the encrypted communication channel
|
|
ReadWriter() msgio.ReadWriteCloser
|
|
|
|
// LocalPeer retrieves the local peer.
|
|
LocalPeer() peer.ID
|
|
|
|
// LocalPrivateKey retrieves the local private key
|
|
LocalPrivateKey() ci.PrivKey
|
|
|
|
// RemotePeer retrieves the remote peer.
|
|
RemotePeer() peer.ID
|
|
|
|
// RemotePublicKey retrieves the remote's public key
|
|
// which was received during the handshake.
|
|
RemotePublicKey() ci.PubKey
|
|
|
|
// Close closes the secure session
|
|
Close() error
|
|
}
|
|
|
|
// SecureReadWriter returns the encrypted communication channel
|
|
func (s *secureSession) ReadWriter() msgio.ReadWriteCloser {
|
|
if err := s.Handshake(); err != nil {
|
|
return &closedRW{err}
|
|
}
|
|
return s.secure
|
|
}
|
|
|
|
// LocalPeer retrieves the local peer.
|
|
func (s *secureSession) LocalPeer() peer.ID {
|
|
return s.localPeer
|
|
}
|
|
|
|
// LocalPrivateKey retrieves the local peer's PrivateKey
|
|
func (s *secureSession) LocalPrivateKey() ci.PrivKey {
|
|
return s.localKey
|
|
}
|
|
|
|
// RemotePeer retrieves the remote peer.
|
|
func (s *secureSession) RemotePeer() peer.ID {
|
|
if err := s.Handshake(); err != nil {
|
|
return ""
|
|
}
|
|
return s.remotePeer
|
|
}
|
|
|
|
// RemotePeer retrieves the remote peer.
|
|
func (s *secureSession) RemotePublicKey() ci.PubKey {
|
|
if err := s.Handshake(); err != nil {
|
|
return nil
|
|
}
|
|
return s.remote.permanentPubKey
|
|
}
|
|
|
|
// Close closes the secure session
|
|
func (s *secureSession) Close() error {
|
|
s.cancel()
|
|
s.handshakeMu.Lock()
|
|
defer s.handshakeMu.Unlock()
|
|
if s.secure == nil {
|
|
return s.insecure.Close() // hadn't secured yet.
|
|
}
|
|
return s.secure.Close()
|
|
}
|
|
|
|
// closedRW implements a stub msgio interface that's already
|
|
// closed and errored.
|
|
type closedRW struct {
|
|
err error
|
|
}
|
|
|
|
func (c *closedRW) Read(buf []byte) (int, error) {
|
|
return 0, c.err
|
|
}
|
|
|
|
func (c *closedRW) Write(buf []byte) (int, error) {
|
|
return 0, c.err
|
|
}
|
|
|
|
func (c *closedRW) NextMsgLen() (int, error) {
|
|
return 0, c.err
|
|
}
|
|
|
|
func (c *closedRW) ReadMsg() ([]byte, error) {
|
|
return nil, c.err
|
|
}
|
|
|
|
func (c *closedRW) WriteMsg(buf []byte) error {
|
|
return c.err
|
|
}
|
|
|
|
func (c *closedRW) Close() error {
|
|
return c.err
|
|
}
|
|
|
|
func (c *closedRW) ReleaseMsg(m []byte) {
|
|
}
|