-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
transport.go
182 lines (162 loc) · 5.36 KB
/
transport.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package libp2ptls
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"os"
"runtime/debug"
"github.com/libp2p/go-libp2p/core/canonicallog"
ci "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/sec"
tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader"
manet "github.com/multiformats/go-multiaddr/net"
)
// ID is the protocol ID (used when negotiating with multistream)
const ID = "/tls/1.0.0"
// Transport constructs secure communication sessions for a peer.
type Transport struct {
identity *Identity
localPeer peer.ID
privKey ci.PrivKey
muxers []protocol.ID
protocolID protocol.ID
}
var _ sec.SecureTransport = &Transport{}
// New creates a TLS encrypted transport
func New(id protocol.ID, key ci.PrivKey, muxers []tptu.StreamMuxer) (*Transport, error) {
localPeer, err := peer.IDFromPrivateKey(key)
if err != nil {
return nil, err
}
muxerIDs := make([]protocol.ID, 0, len(muxers))
for _, m := range muxers {
muxerIDs = append(muxerIDs, m.ID)
}
t := &Transport{
protocolID: id,
localPeer: localPeer,
privKey: key,
muxers: muxerIDs,
}
identity, err := NewIdentity(key)
if err != nil {
return nil, err
}
t.identity = identity
return t, nil
}
// SecureInbound runs the TLS handshake as a server.
// If p is empty, connections from any peer are accepted.
func (t *Transport) SecureInbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
config, keyCh := t.identity.ConfigForPeer(p)
muxers := make([]string, 0, len(t.muxers))
for _, muxer := range t.muxers {
muxers = append(muxers, string(muxer))
}
// TLS' ALPN selection lets the server select the protocol, preferring the server's preferences.
// We want to prefer the client's preference though.
getConfigForClient := config.GetConfigForClient
config.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
alpnLoop:
for _, proto := range info.SupportedProtos {
for _, m := range muxers {
if m == proto {
// Match found. Select this muxer, as it's the client's preference.
// There's no need to add the "libp2p" entry here.
config.NextProtos = []string{proto}
break alpnLoop
}
}
}
if getConfigForClient != nil {
return getConfigForClient(info)
}
return config, nil
}
config.NextProtos = append(muxers, config.NextProtos...)
cs, err := t.handshake(ctx, tls.Server(insecure, config), keyCh)
if err != nil {
addr, maErr := manet.FromNetAddr(insecure.RemoteAddr())
if maErr == nil {
canonicallog.LogPeerStatus(100, p, addr, "handshake_failure", "tls", "err", err.Error())
}
insecure.Close()
}
return cs, err
}
// SecureOutbound runs the TLS handshake as a client.
// Note that SecureOutbound will not return an error if the server doesn't
// accept the certificate. This is due to the fact that in TLS 1.3, the client
// sends its certificate and the ClientFinished in the same flight, and can send
// application data immediately afterwards.
// If the handshake fails, the server will close the connection. The client will
// notice this after 1 RTT when calling Read.
func (t *Transport) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
config, keyCh := t.identity.ConfigForPeer(p)
muxers := make([]string, 0, len(t.muxers))
for _, muxer := range t.muxers {
muxers = append(muxers, (string)(muxer))
}
// Prepend the prefered muxers list to TLS config.
config.NextProtos = append(muxers, config.NextProtos...)
cs, err := t.handshake(ctx, tls.Client(insecure, config), keyCh)
if err != nil {
insecure.Close()
}
return cs, err
}
func (t *Transport) handshake(ctx context.Context, tlsConn *tls.Conn, keyCh <-chan ci.PubKey) (_sconn sec.SecureConn, err error) {
defer func() {
if rerr := recover(); rerr != nil {
fmt.Fprintf(os.Stderr, "panic in TLS handshake: %s\n%s\n", rerr, debug.Stack())
err = fmt.Errorf("panic in TLS handshake: %s", rerr)
}
}()
// handshaking...
if err := tlsConn.HandshakeContext(ctx); err != nil {
return nil, err
}
// Should be ready by this point, don't block.
var remotePubKey ci.PubKey
select {
case remotePubKey = <-keyCh:
default:
}
if remotePubKey == nil {
return nil, errors.New("go-libp2p tls BUG: expected remote pub key to be set")
}
return t.setupConn(tlsConn, remotePubKey)
}
func (t *Transport) setupConn(tlsConn *tls.Conn, remotePubKey ci.PubKey) (sec.SecureConn, error) {
remotePeerID, err := peer.IDFromPublicKey(remotePubKey)
if err != nil {
return nil, err
}
nextProto := tlsConn.ConnectionState().NegotiatedProtocol
// The special ALPN extension value "libp2p" is used by libp2p versions
// that don't support early muxer negotiation. If we see this sepcial
// value selected, that means we are handshaking with a version that does
// not support early muxer negotiation. In this case return empty nextProto
// to indicate no muxer is selected.
if nextProto == "libp2p" {
nextProto = ""
}
return &conn{
Conn: tlsConn,
localPeer: t.localPeer,
remotePeer: remotePeerID,
remotePubKey: remotePubKey,
connectionState: network.ConnectionState{
StreamMultiplexer: protocol.ID(nextProto),
UsedEarlyMuxerNegotiation: nextProto != "",
},
}, nil
}
func (t *Transport) ID() protocol.ID {
return t.protocolID
}