transport.go 3.16 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"
Marten Seemann's avatar
Marten Seemann committed
8

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

18
19
20
21
22
23
var quicConfig = &quic.Config{
	MaxReceiveStreamFlowControlWindow:     3 * (1 << 20),   // 3 MB
	MaxReceiveConnectionFlowControlWindow: 4.5 * (1 << 20), // 4.5 MB
	Versions: []quic.VersionNumber{101},
}

24
var quicDialAddr = quic.DialAddr
Marten Seemann's avatar
Marten Seemann committed
25

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

33
var _ tpt.Transport = &transport{}
34

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

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

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

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

116
117
118
119
120
121
122
123
// 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"
}