transport.go 3.29 KB
Newer Older
Marten Seemann's avatar
Marten Seemann committed
1
2
3
package libp2pquic

import (
4
	"context"
5
	"crypto/tls"
6
	"crypto/x509"
7
	"errors"
8
	"net"
Marten Seemann's avatar
Marten Seemann committed
9

10
11
	ic "github.com/libp2p/go-libp2p-crypto"
	peer "github.com/libp2p/go-libp2p-peer"
Marten Seemann's avatar
Marten Seemann committed
12
	tpt "github.com/libp2p/go-libp2p-transport"
13
	quic "github.com/lucas-clemente/quic-go"
Marten Seemann's avatar
Marten Seemann committed
14
	ma "github.com/multiformats/go-multiaddr"
15
	manet "github.com/multiformats/go-multiaddr-net"
Marten Seemann's avatar
Marten Seemann committed
16
	"github.com/whyrusleeping/mafmt"
Marten Seemann's avatar
Marten Seemann committed
17
18
)

19
20
21
22
var quicConfig = &quic.Config{
	MaxReceiveStreamFlowControlWindow:     3 * (1 << 20),   // 3 MB
	MaxReceiveConnectionFlowControlWindow: 4.5 * (1 << 20), // 4.5 MB
	Versions: []quic.VersionNumber{101},
23
24
25
26
	AcceptCookie: func(clientAddr net.Addr, cookie *quic.Cookie) bool {
		// TODO(#6): require source address validation when under load
		return true
	},
27
28
}

29
30
31
32
// The Transport implements the tpt.Transport interface for QUIC connections.
type transport struct {
	privKey   ic.PrivKey
	localPeer peer.ID
33
	tlsConf   *tls.Config
Marten Seemann's avatar
Marten Seemann committed
34
}
Marten Seemann's avatar
Marten Seemann committed
35

36
var _ tpt.Transport = &transport{}
37

38
39
40
41
42
// NewTransport creates a new QUIC transport
func NewTransport(key ic.PrivKey) (tpt.Transport, error) {
	localPeer, err := peer.IDFromPrivateKey(key)
	if err != nil {
		return nil, err
Marten Seemann's avatar
Marten Seemann committed
43
	}
44
45
46
47
	tlsConf, err := generateConfig(key)
	if err != nil {
		return nil, err
	}
48
49
50
	return &transport{
		privKey:   key,
		localPeer: localPeer,
51
		tlsConf:   tlsConf,
52
	}, nil
Marten Seemann's avatar
Marten Seemann committed
53
54
}

55
56
57
58
59
// Dial dials a new QUIC connection
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.Conn, error) {
	_, host, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
Marten Seemann's avatar
Marten Seemann committed
60
	}
61
	var remotePubKey ic.PubKey
62
	tlsConf := t.tlsConf.Clone()
63
64
65
	// We need to check the peer ID in the VerifyPeerCertificate callback.
	// The tls.Config it is also used for listening, and we might also have concurrent dials.
	// Clone it so we can check for the specific peer ID we're dialing here.
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
	tlsConf.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
		chain := make([]*x509.Certificate, len(rawCerts))
		for i := 0; i < len(rawCerts); i++ {
			cert, err := x509.ParseCertificate(rawCerts[i])
			if err != nil {
				return err
			}
			chain[i] = cert
		}
		var err error
		remotePubKey, err = getRemotePubKey(chain)
		if err != nil {
			return err
		}
		if !p.MatchesPublicKey(remotePubKey) {
			return errors.New("peer IDs don't match")
		}
		return nil
84
	}
Marten Seemann's avatar
Marten Seemann committed
85
	sess, err := quic.DialAddrContext(ctx, host, tlsConf, quicConfig)
86
87
	if err != nil {
		return nil, err
Marten Seemann's avatar
Marten Seemann committed
88
	}
89
	localMultiaddr, err := quicMultiaddr(sess.LocalAddr())
Marten Seemann's avatar
Marten Seemann committed
90
91
92
	if err != nil {
		return nil, err
	}
93
	return &conn{
94
		sess:            sess,
95
96
97
98
99
100
101
102
103
104
105
106
107
		privKey:         t.privKey,
		localPeer:       t.localPeer,
		localMultiaddr:  localMultiaddr,
		remotePubKey:    remotePubKey,
		remotePeerID:    p,
		remoteMultiaddr: raddr,
	}, nil
}

// CanDial determines if we can dial to an address
func (t *transport) CanDial(addr ma.Multiaddr) bool {
	return mafmt.QUIC.Matches(addr)
}
Marten Seemann's avatar
Marten Seemann committed
108

109
110
// Listen listens for new QUIC connections on the passed multiaddr.
func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
111
	return newListener(addr, t, t.localPeer, t.privKey, t.tlsConf)
Marten Seemann's avatar
Marten Seemann committed
112
113
}

114
115
116
// Proxy returns true if this transport proxies.
func (t *transport) Proxy() bool {
	return false
Marten Seemann's avatar
Marten Seemann committed
117
118
}

119
120
121
122
123
124
125
126
// Protocols returns the set of protocols handled by this transport.
func (t *transport) Protocols() []int {
	return []int{ma.P_QUIC}
}

func (t *transport) String() string {
	return "QUIC"
}