transport.go 3.32 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
var quicDialAddr = quic.DialAddr
Marten Seemann's avatar
Marten Seemann committed
30

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

38
var _ tpt.Transport = &transport{}
39

40
41
42
43
44
// 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
45
	}
46
47
48
49
	tlsConf, err := generateConfig(key)
	if err != nil {
		return nil, err
	}
50
51
52
	return &transport{
		privKey:   key,
		localPeer: localPeer,
53
		tlsConf:   tlsConf,
54
	}, nil
Marten Seemann's avatar
Marten Seemann committed
55
56
}

57
58
59
60
61
// 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
62
	}
63
	var remotePubKey ic.PubKey
64
	tlsConf := t.tlsConf.Clone()
65
66
67
	// 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.
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
	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
86
	}
87
	sess, err := quicDialAddr(host, tlsConf, quicConfig)
88
89
	if err != nil {
		return nil, err
Marten Seemann's avatar
Marten Seemann committed
90
	}
91
	localMultiaddr, err := quicMultiaddr(sess.LocalAddr())
Marten Seemann's avatar
Marten Seemann committed
92
93
94
	if err != nil {
		return nil, err
	}
95
	return &conn{
96
		sess:            sess,
97
98
99
100
101
102
103
104
105
106
107
108
109
		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
110

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

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

121
122
123
124
125
126
127
128
// 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"
}