-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
address.go
365 lines (307 loc) · 11.3 KB
/
address.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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
package lncfg
import (
"context"
"crypto/tls"
"encoding/hex"
"fmt"
"net"
"strconv"
"strings"
"github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/tor"
)
var (
loopBackAddrs = []string{"localhost", "127.0.0.1", "[::1]"}
)
// TCPResolver is a function signature that resolves an address on a given
// network.
type TCPResolver = func(network, addr string) (*net.TCPAddr, error)
// NormalizeAddresses returns a new slice with all the passed addresses
// normalized with the given default port and all duplicates removed.
func NormalizeAddresses(addrs []string, defaultPort string,
tcpResolver TCPResolver) ([]net.Addr, error) {
result := make([]net.Addr, 0, len(addrs))
seen := map[string]struct{}{}
for _, addr := range addrs {
parsedAddr, err := ParseAddressString(
addr, defaultPort, tcpResolver,
)
if err != nil {
return nil, err
}
if _, ok := seen[parsedAddr.String()]; !ok {
result = append(result, parsedAddr)
seen[parsedAddr.String()] = struct{}{}
}
}
return result, nil
}
// EnforceSafeAuthentication enforces "safe" authentication taking into account
// the interfaces that the RPC servers are listening on, and if macaroons and
// TLS is activated or not. To protect users from using dangerous config
// combinations, we'll prevent disabling authentication if the server is
// listening on a public interface.
func EnforceSafeAuthentication(addrs []net.Addr, macaroonsActive,
tlsActive bool) error {
// We'll now examine all addresses that this RPC server is listening
// on. If it's a localhost address or a private address, we'll skip it,
// otherwise, we'll return an error if macaroons are inactive.
for _, addr := range addrs {
if IsLoopback(addr.String()) || IsUnix(addr) || IsPrivate(addr) {
continue
}
if !macaroonsActive {
return fmt.Errorf("detected RPC server listening on "+
"publicly reachable interface %v with "+
"authentication disabled! Refusing to start "+
"with --no-macaroons specified", addr)
}
if !tlsActive {
return fmt.Errorf("detected RPC server listening on "+
"publicly reachable interface %v with "+
"encryption disabled! Refusing to start "+
"with --no-rest-tls specified", addr)
}
}
return nil
}
// parseNetwork parses the network type of the given address.
func parseNetwork(addr net.Addr) string {
switch addr := addr.(type) {
// TCP addresses resolved through net.ResolveTCPAddr give a default
// network of "tcp", so we'll map back the correct network for the given
// address. This ensures that we can listen on the correct interface
// (IPv4 vs IPv6).
case *net.TCPAddr:
if addr.IP.To4() != nil {
return "tcp4"
}
return "tcp6"
default:
return addr.Network()
}
}
// ListenOnAddress creates a listener that listens on the given address.
func ListenOnAddress(addr net.Addr) (net.Listener, error) {
return net.Listen(parseNetwork(addr), addr.String())
}
// TLSListenOnAddress creates a TLS listener that listens on the given address.
func TLSListenOnAddress(addr net.Addr,
config *tls.Config) (net.Listener, error) {
return tls.Listen(parseNetwork(addr), addr.String(), config)
}
// IsLoopback returns true if an address describes a loopback interface.
func IsLoopback(addr string) bool {
for _, loopback := range loopBackAddrs {
if strings.Contains(addr, loopback) {
return true
}
}
return false
}
// isIPv6Host returns true if the host is IPV6 and false otherwise.
func isIPv6Host(host string) bool {
v6Addr := net.ParseIP(host)
if v6Addr == nil {
return false
}
// The documentation states that if the IP address is an IPv6 address,
// then To4() will return nil.
return v6Addr.To4() == nil
}
// IsUnix returns true if an address describes an Unix socket address.
func IsUnix(addr net.Addr) bool {
return strings.HasPrefix(addr.Network(), "unix")
}
// IsPrivate returns true if the address is private. The definitions are,
// https://en.wikipedia.org/wiki/Link-local_address
// https://en.wikipedia.org/wiki/Multicast_address
// Local IPv4 addresses, https://tools.ietf.org/html/rfc1918
// Local IPv6 addresses, https://tools.ietf.org/html/rfc4193
func IsPrivate(addr net.Addr) bool {
switch addr := addr.(type) {
case *net.TCPAddr:
// Check 169.254.0.0/16 and fe80::/10.
if addr.IP.IsLinkLocalUnicast() {
return true
}
// Check 224.0.0.0/4 and ff00::/8.
if addr.IP.IsLinkLocalMulticast() {
return true
}
// Check 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.
if ip4 := addr.IP.To4(); ip4 != nil {
return ip4[0] == 10 ||
(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
(ip4[0] == 192 && ip4[1] == 168)
}
// Check fc00::/7.
return len(addr.IP) == net.IPv6len && addr.IP[0]&0xfe == 0xfc
default:
return false
}
}
// ParseAddressString converts an address in string format to a net.Addr that is
// compatible with lnd. UDP is not supported because lnd needs reliable
// connections. We accept a custom function to resolve any TCP addresses so
// that caller is able control exactly how resolution is performed.
func ParseAddressString(strAddress string, defaultPort string,
tcpResolver TCPResolver) (net.Addr, error) {
var parsedNetwork, parsedAddr string
// Addresses can either be in network://address:port format,
// network:address:port, address:port, or just port. We want to support
// all possible types.
if strings.Contains(strAddress, "://") {
parts := strings.Split(strAddress, "://")
parsedNetwork, parsedAddr = parts[0], parts[1]
} else if strings.Contains(strAddress, ":") {
parts := strings.Split(strAddress, ":")
parsedNetwork = parts[0]
parsedAddr = strings.Join(parts[1:], ":")
}
// Only TCP and Unix socket addresses are valid. We can't use IP or
// UDP only connections for anything we do in lnd.
switch parsedNetwork {
case "unix", "unixpacket":
return net.ResolveUnixAddr(parsedNetwork, parsedAddr)
case "tcp", "tcp4", "tcp6":
return tcpResolver(
parsedNetwork, verifyPort(parsedAddr, defaultPort),
)
case "ip", "ip4", "ip6", "udp", "udp4", "udp6", "unixgram":
return nil, fmt.Errorf("only TCP or unix socket "+
"addresses are supported: %s", parsedAddr)
default:
// We'll now possibly apply the default port, use the local
// host short circuit, or parse out an all interfaces listen.
addrWithPort := verifyPort(strAddress, defaultPort)
rawHost, rawPort, _ := net.SplitHostPort(addrWithPort)
// If we reach this point, then we'll check to see if we have
// an onion addresses, if so, we can directly pass the raw
// address and port to create the proper address.
if tor.IsOnionHost(rawHost) {
portNum, err := strconv.Atoi(rawPort)
if err != nil {
return nil, err
}
return &tor.OnionAddr{
OnionService: rawHost,
Port: portNum,
}, nil
}
// Otherwise, we'll attempt the resolve the host. The Tor
// resolver is unable to resolve local or IPv6 addresses, so
// we'll use the system resolver instead.
if rawHost == "" || IsLoopback(rawHost) ||
isIPv6Host(rawHost) {
return net.ResolveTCPAddr("tcp", addrWithPort)
}
// If we've reached this point, then it's possible that this
// resolve returns an error if it isn't able to resolve the
// host. For eaxmple, local entries in /etc/hosts will fail to
// be resolved by Tor. In order to handle this case, we'll fall
// back to the normal system resolver if we fail with an
// identifiable error.
addr, err := tcpResolver("tcp", addrWithPort)
if err != nil {
torErrStr := "tor host is unreachable"
if strings.Contains(err.Error(), torErrStr) {
return net.ResolveTCPAddr("tcp", addrWithPort)
}
return nil, err
}
return addr, nil
}
}
// ParseLNAddressString converts a string of the form <pubkey>@<addr> into an
// lnwire.NetAddress. The <pubkey> must be presented in hex, and result in a
// 33-byte, compressed public key that lies on the secp256k1 curve. The <addr>
// may be any address supported by ParseAddressString. If no port is specified,
// the defaultPort will be used. Any tcp addresses that need resolving will be
// resolved using the custom TCPResolver.
func ParseLNAddressString(strAddress string, defaultPort string,
tcpResolver TCPResolver) (*lnwire.NetAddress, error) {
// Split the address string around the @ sign.
parts := strings.Split(strAddress, "@")
// The string is malformed if there are not exactly two parts.
if len(parts) != 2 {
return nil, fmt.Errorf("invalid lightning address %s: "+
"must be of the form <pubkey-hex>@<addr>", strAddress)
}
// Now, take the first portion as the hex pubkey, and the latter as the
// address string.
parsedPubKey, parsedAddr := parts[0], parts[1]
// Decode the hex pubkey to get the raw compressed pubkey bytes.
pubKeyBytes, err := hex.DecodeString(parsedPubKey)
if err != nil {
return nil, fmt.Errorf("invalid lightning address pubkey: %v", err)
}
// The compressed pubkey should have a length of exactly 33 bytes.
if len(pubKeyBytes) != 33 {
return nil, fmt.Errorf("invalid lightning address pubkey: "+
"length must be 33 bytes, found %d", len(pubKeyBytes))
}
// Parse the pubkey bytes to verify that it corresponds to valid public
// key on the secp256k1 curve.
pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256())
if err != nil {
return nil, fmt.Errorf("invalid lightning address pubkey: %v", err)
}
// Finally, parse the address string using our generic address parser.
addr, err := ParseAddressString(parsedAddr, defaultPort, tcpResolver)
if err != nil {
return nil, fmt.Errorf("invalid lightning address address: %v", err)
}
return &lnwire.NetAddress{
IdentityKey: pubKey,
Address: addr,
}, nil
}
// verifyPort makes sure that an address string has both a host and a port. If
// there is no port found, the default port is appended. If the address is just
// a port, then we'll assume that the user is using the short cut to specify a
// localhost:port address.
func verifyPort(address string, defaultPort string) string {
host, port, err := net.SplitHostPort(address)
if err != nil {
// If the address itself is just an integer, then we'll assume
// that we're mapping this directly to a localhost:port pair.
// This ensures we maintain the legacy behavior.
if _, err := strconv.Atoi(address); err == nil {
return net.JoinHostPort("localhost", address)
}
// Otherwise, we'll assume that the address just failed to
// attach its own port, so we'll use the default port. In the
// case of IPv6 addresses, if the host is already surrounded by
// brackets, then we'll avoid using the JoinHostPort function,
// since it will always add a pair of brackets.
if strings.HasPrefix(address, "[") {
return address + ":" + defaultPort
}
return net.JoinHostPort(address, defaultPort)
}
// In the case that both the host and port are empty, we'll use the
// default port.
if host == "" && port == "" {
return ":" + defaultPort
}
return address
}
// ClientAddressDialer creates a gRPC dialer that can also dial unix socket
// addresses instead of just TCP addresses.
func ClientAddressDialer(defaultPort string) func(context.Context,
string) (net.Conn, error) {
return func(ctx context.Context, addr string) (net.Conn, error) {
parsedAddr, err := ParseAddressString(
addr, defaultPort, net.ResolveTCPAddr,
)
if err != nil {
return nil, err
}
d := net.Dialer{}
return d.DialContext(
ctx, parsedAddr.Network(), parsedAddr.String(),
)
}
}