transport.go 2.93 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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
	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
78
	}
79
	sess, err := quicDialAddr(host, tlsConf, quicConfig)
80
81
	if err != nil {
		return nil, err
Marten Seemann's avatar
Marten Seemann committed
82
	}
83
	localMultiaddr, err := quicMultiaddr(sess.LocalAddr())
Marten Seemann's avatar
Marten Seemann committed
84
85
86
	if err != nil {
		return nil, err
	}
87
	return &conn{
88
		sess:            sess,
89
90
91
92
93
94
95
96
97
98
99
100
101
		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
102

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

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

113
114
115
116
117
118
119
120
// 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"
}