• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.78
/lncfg/address.go
1
package lncfg
2

3
import (
4
        "context"
5
        "crypto/tls"
6
        "encoding/hex"
7
        "fmt"
8
        "net"
9
        "strconv"
10
        "strings"
11

12
        "github.com/btcsuite/btcd/btcec/v2"
13
        "github.com/lightningnetwork/lnd/lnwire"
14
        "github.com/lightningnetwork/lnd/tor"
15
)
16

17
// TCPResolver is a function signature that resolves an address on a given
18
// network.
19
type TCPResolver = func(network, addr string) (*net.TCPAddr, error)
20

21
// NormalizeAddresses returns a new slice with all the passed addresses
22
// normalized with the given default port and all duplicates removed.
23
func NormalizeAddresses(addrs []string, defaultPort string,
24
        tcpResolver TCPResolver) ([]net.Addr, error) {
3✔
25

3✔
26
        result := make([]net.Addr, 0, len(addrs))
3✔
27
        seen := map[string]struct{}{}
3✔
28

3✔
29
        for _, addr := range addrs {
6✔
30
                parsedAddr, err := ParseAddressString(
3✔
31
                        addr, defaultPort, tcpResolver,
3✔
32
                )
3✔
33
                if err != nil {
3✔
UNCOV
34
                        return nil, fmt.Errorf("parse address %s failed: %w",
×
UNCOV
35
                                addr, err)
×
UNCOV
36
                }
×
37

38
                if _, ok := seen[parsedAddr.String()]; !ok {
6✔
39
                        result = append(result, parsedAddr)
3✔
40
                        seen[parsedAddr.String()] = struct{}{}
3✔
41
                }
3✔
42
        }
43

44
        return result, nil
3✔
45
}
46

47
// EnforceSafeAuthentication enforces "safe" authentication taking into account
48
// the interfaces that the RPC servers are listening on, and if macaroons and
49
// TLS is activated or not. To protect users from using dangerous config
50
// combinations, we'll prevent disabling authentication if the server is
51
// listening on a public interface.
52
func EnforceSafeAuthentication(addrs []net.Addr, macaroonsActive,
53
        tlsActive bool) error {
3✔
54

3✔
55
        // We'll now examine all addresses that this RPC server is listening
3✔
56
        // on. If it's a localhost address or a private address, we'll skip it,
3✔
57
        // otherwise, we'll return an error if macaroons are inactive.
3✔
58
        for _, addr := range addrs {
6✔
59
                if IsLoopback(addr.String()) || IsUnix(addr) || IsPrivate(addr) {
6✔
60
                        continue
3✔
61
                }
62

63
                if !macaroonsActive {
×
64
                        return fmt.Errorf("detected RPC server listening on "+
×
65
                                "publicly reachable interface %v with "+
×
66
                                "authentication disabled! Refusing to start "+
×
67
                                "with --no-macaroons specified", addr)
×
68
                }
×
69

70
                if !tlsActive {
×
71
                        return fmt.Errorf("detected RPC server listening on "+
×
72
                                "publicly reachable interface %v with "+
×
73
                                "encryption disabled! Refusing to start "+
×
74
                                "with --no-rest-tls specified", addr)
×
75
                }
×
76
        }
77

78
        return nil
3✔
79
}
80

81
// parseNetwork parses the network type of the given address.
82
func parseNetwork(addr net.Addr) string {
3✔
83
        switch addr := addr.(type) {
3✔
84
        // TCP addresses resolved through net.ResolveTCPAddr give a default
85
        // network of "tcp", so we'll map back the correct network for the given
86
        // address. This ensures that we can listen on the correct interface
87
        // (IPv4 vs IPv6).
88
        case *net.TCPAddr:
3✔
89
                if addr.IP.To4() != nil {
6✔
90
                        return "tcp4"
3✔
91
                }
3✔
92
                return "tcp6"
×
93

94
        default:
×
95
                return addr.Network()
×
96
        }
97
}
98

99
// ListenOnAddress creates a listener that listens on the given address.
100
func ListenOnAddress(addr net.Addr) (net.Listener, error) {
3✔
101
        return net.Listen(parseNetwork(addr), addr.String())
3✔
102
}
3✔
103

104
// TLSListenOnAddress creates a TLS listener that listens on the given address.
105
func TLSListenOnAddress(addr net.Addr,
106
        config *tls.Config) (net.Listener, error) {
3✔
107
        return tls.Listen(parseNetwork(addr), addr.String(), config)
3✔
108
}
3✔
109

110
// IsLoopback returns true if an address describes a loopback interface.
111
func IsLoopback(host string) bool {
3✔
112
        if strings.Contains(host, "localhost") {
6✔
113
                return true
3✔
114
        }
3✔
115

116
        rawHost, _, _ := net.SplitHostPort(host)
3✔
117
        addr := net.ParseIP(rawHost)
3✔
118
        if addr == nil {
6✔
119
                return false
3✔
120
        }
3✔
121

122
        return addr.IsLoopback()
3✔
123
}
124

125
// isIPv6Host returns true if the host is IPV6 and false otherwise.
126
func isIPv6Host(host string) bool {
3✔
127
        v6Addr := net.ParseIP(host)
3✔
128
        if v6Addr == nil {
3✔
UNCOV
129
                return false
×
UNCOV
130
        }
×
131

132
        // The documentation states that if the IP address is an IPv6 address,
133
        // then To4() will return nil.
134
        return v6Addr.To4() == nil
3✔
135
}
136

137
// isUnspecifiedHost returns true if the host IP is considered unspecified.
138
func isUnspecifiedHost(host string) bool {
3✔
139
        addr := net.ParseIP(host)
3✔
140
        if addr == nil {
3✔
UNCOV
141
                return false
×
UNCOV
142
        }
×
143

144
        return addr.IsUnspecified()
3✔
145
}
146

147
// IsUnix returns true if an address describes an Unix socket address.
148
func IsUnix(addr net.Addr) bool {
3✔
149
        return strings.HasPrefix(addr.Network(), "unix")
3✔
150
}
3✔
151

152
// IsPrivate returns true if the address is private. The definitions are,
153
//
154
//        https://en.wikipedia.org/wiki/Link-local_address
155
//        https://en.wikipedia.org/wiki/Multicast_address
156
//        Local IPv4 addresses, https://tools.ietf.org/html/rfc1918
157
//        Local IPv6 addresses, https://tools.ietf.org/html/rfc4193
UNCOV
158
func IsPrivate(addr net.Addr) bool {
×
UNCOV
159
        switch addr := addr.(type) {
×
UNCOV
160
        case *net.TCPAddr:
×
UNCOV
161
                // Check 169.254.0.0/16 and fe80::/10.
×
UNCOV
162
                if addr.IP.IsLinkLocalUnicast() {
×
UNCOV
163
                        return true
×
UNCOV
164
                }
×
165

166
                // Check 224.0.0.0/4 and ff00::/8.
UNCOV
167
                if addr.IP.IsLinkLocalMulticast() {
×
UNCOV
168
                        return true
×
UNCOV
169
                }
×
170

171
                // Check 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.
UNCOV
172
                if ip4 := addr.IP.To4(); ip4 != nil {
×
UNCOV
173
                        return ip4[0] == 10 ||
×
UNCOV
174
                                (ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
×
UNCOV
175
                                (ip4[0] == 192 && ip4[1] == 168)
×
UNCOV
176
                }
×
177

178
                // Check fc00::/7.
UNCOV
179
                return len(addr.IP) == net.IPv6len && addr.IP[0]&0xfe == 0xfc
×
180

181
        default:
×
182
                return false
×
183
        }
184
}
185

186
// ParseAddressString converts an address in string format to a net.Addr that is
187
// compatible with lnd. UDP is not supported because lnd needs reliable
188
// connections. We accept a custom function to resolve any TCP addresses so
189
// that caller is able control exactly how resolution is performed.
190
func ParseAddressString(strAddress string, defaultPort string,
191
        tcpResolver TCPResolver) (net.Addr, error) {
3✔
192

3✔
193
        var parsedNetwork, parsedAddr string
3✔
194

3✔
195
        // Addresses can either be in network://address:port format,
3✔
196
        // network:address:port, address:port, or just port. We want to support
3✔
197
        // all possible types.
3✔
198
        if strings.Contains(strAddress, "://") {
3✔
UNCOV
199
                parts := strings.Split(strAddress, "://")
×
UNCOV
200
                parsedNetwork, parsedAddr = parts[0], parts[1]
×
201
        } else if strings.Contains(strAddress, ":") {
6✔
202
                parts := strings.Split(strAddress, ":")
3✔
203
                parsedNetwork = parts[0]
3✔
204
                parsedAddr = strings.Join(parts[1:], ":")
3✔
205
        }
3✔
206

207
        // Only TCP and Unix socket addresses are valid. We can't use IP or
208
        // UDP only connections for anything we do in lnd.
209
        switch parsedNetwork {
3✔
UNCOV
210
        case "unix", "unixpacket":
×
UNCOV
211
                return net.ResolveUnixAddr(parsedNetwork, parsedAddr)
×
212

UNCOV
213
        case "tcp", "tcp4", "tcp6":
×
UNCOV
214
                return tcpResolver(
×
UNCOV
215
                        parsedNetwork, verifyPort(parsedAddr, defaultPort),
×
UNCOV
216
                )
×
217

218
        case "ip", "ip4", "ip6", "udp", "udp4", "udp6", "unixgram":
×
219
                return nil, fmt.Errorf("only TCP or unix socket "+
×
220
                        "addresses are supported: %s", parsedAddr)
×
221

222
        default:
3✔
223
                // We'll now possibly apply the default port, use the local
3✔
224
                // host short circuit, or parse out an all interfaces listen.
3✔
225
                addrWithPort := verifyPort(strAddress, defaultPort)
3✔
226
                rawHost, rawPort, _ := net.SplitHostPort(addrWithPort)
3✔
227

3✔
228
                // If we reach this point, then we'll check to see if we have
3✔
229
                // an onion addresses, if so, we can directly pass the raw
3✔
230
                // address and port to create the proper address.
3✔
231
                if tor.IsOnionHost(rawHost) {
6✔
232
                        portNum, err := strconv.Atoi(rawPort)
3✔
233
                        if err != nil {
3✔
234
                                return nil, err
×
235
                        }
×
236

237
                        return &tor.OnionAddr{
3✔
238
                                OnionService: rawHost,
3✔
239
                                Port:         portNum,
3✔
240
                        }, nil
3✔
241
                }
242

243
                // Otherwise, we'll attempt the resolve the host. The Tor
244
                // resolver is unable to resolve local addresses,
245
                // IPv6 addresses, or the all-interfaces address, so we'll use
246
                // the system resolver instead for those.
247
                if rawHost == "" || IsLoopback(rawHost) ||
3✔
248
                        isIPv6Host(rawHost) || isUnspecifiedHost(rawHost) {
6✔
249

3✔
250
                        return net.ResolveTCPAddr("tcp", addrWithPort)
3✔
251
                }
3✔
252

253
                // If we've reached this point, then it's possible that this
254
                // resolve returns an error if it isn't able to resolve the
255
                // host. For example, local entries in /etc/hosts will fail to
256
                // be resolved by Tor. In order to handle this case, we'll fall
257
                // back to the normal system resolver if we fail with an
258
                // identifiable error.
259
                addr, err := tcpResolver("tcp", addrWithPort)
3✔
260
                if err != nil {
3✔
UNCOV
261
                        torErrStr := "tor host is unreachable"
×
UNCOV
262
                        if strings.Contains(err.Error(), torErrStr) {
×
263
                                return net.ResolveTCPAddr("tcp", addrWithPort)
×
264
                        }
×
265

UNCOV
266
                        return nil, err
×
267
                }
268

269
                return addr, nil
3✔
270
        }
271
}
272

273
// ParseLNAddressString converts a string of the form <pubkey>@<addr> into an
274
// lnwire.NetAddress. The <pubkey> must be presented in hex, and result in a
275
// 33-byte, compressed public key that lies on the secp256k1 curve. The <addr>
276
// may be any address supported by ParseAddressString. If no port is specified,
277
// the defaultPort will be used. Any tcp addresses that need resolving will be
278
// resolved using the custom TCPResolver.
279
func ParseLNAddressString(strAddress string, defaultPort string,
UNCOV
280
        tcpResolver TCPResolver) (*lnwire.NetAddress, error) {
×
UNCOV
281

×
UNCOV
282
        pubKey, parsedAddr, err := ParseLNAddressPubkey(strAddress)
×
UNCOV
283
        if err != nil {
×
UNCOV
284
                return nil, err
×
UNCOV
285
        }
×
286

287
        // Finally, parse the address string using our generic address parser.
UNCOV
288
        addr, err := ParseAddressString(parsedAddr, defaultPort, tcpResolver)
×
UNCOV
289
        if err != nil {
×
UNCOV
290
                return nil, fmt.Errorf("invalid lightning address address: %w",
×
UNCOV
291
                        err)
×
UNCOV
292
        }
×
293

UNCOV
294
        return &lnwire.NetAddress{
×
UNCOV
295
                IdentityKey: pubKey,
×
UNCOV
296
                Address:     addr,
×
UNCOV
297
        }, nil
×
298
}
299

300
// ParseLNAddressPubkey converts a string of the form <pubkey>@<addr> into two
301
// pieces: the pubkey bytes and an addr string. It validates that the pubkey
302
// is of a valid form.
303
func ParseLNAddressPubkey(strAddress string) (*btcec.PublicKey, string, error) {
3✔
304
        // Split the address string around the @ sign.
3✔
305
        parts := strings.Split(strAddress, "@")
3✔
306

3✔
307
        // The string is malformed if there are not exactly two parts.
3✔
308
        if len(parts) != 2 {
3✔
UNCOV
309
                return nil, "", fmt.Errorf("invalid lightning address %s: "+
×
UNCOV
310
                        "must be of the form <pubkey-hex>@<addr>", strAddress)
×
UNCOV
311
        }
×
312

313
        // Now, take the first portion as the hex pubkey, and the latter as the
314
        // address string.
315
        parsedPubKey, parsedAddr := parts[0], parts[1]
3✔
316

3✔
317
        // Decode the hex pubkey to get the raw compressed pubkey bytes.
3✔
318
        pubKeyBytes, err := hex.DecodeString(parsedPubKey)
3✔
319
        if err != nil {
3✔
UNCOV
320
                return nil, "", fmt.Errorf("invalid lightning address "+
×
UNCOV
321
                        "pubkey: %w", err)
×
UNCOV
322
        }
×
323

324
        // The compressed pubkey should have a length of exactly 33 bytes.
325
        if len(pubKeyBytes) != 33 {
3✔
UNCOV
326
                return nil, "", fmt.Errorf("invalid lightning address pubkey: "+
×
UNCOV
327
                        "length must be 33 bytes, found %d", len(pubKeyBytes))
×
UNCOV
328
        }
×
329

330
        // Parse the pubkey bytes to verify that it corresponds to valid public
331
        // key on the secp256k1 curve.
332
        pubKey, err := btcec.ParsePubKey(pubKeyBytes)
3✔
333
        if err != nil {
3✔
UNCOV
334
                return nil, "", fmt.Errorf("invalid lightning address "+
×
UNCOV
335
                        "pubkey: %w", err)
×
UNCOV
336
        }
×
337

338
        return pubKey, parsedAddr, nil
3✔
339
}
340

341
// verifyPort makes sure that an address string has both a host and a port. If
342
// there is no port found, the default port is appended. If the address is just
343
// a port, then we'll assume that the user is using the short cut to specify a
344
// localhost:port address.
345
func verifyPort(address string, defaultPort string) string {
3✔
346
        host, port, err := net.SplitHostPort(address)
3✔
347
        if err != nil {
6✔
348
                // If the address itself is just an integer, then we'll assume
3✔
349
                // that we're mapping this directly to a localhost:port pair.
3✔
350
                // This ensures we maintain the legacy behavior.
3✔
351
                if _, err := strconv.Atoi(address); err == nil {
3✔
UNCOV
352
                        return net.JoinHostPort("localhost", address)
×
UNCOV
353
                }
×
354

355
                // Otherwise, we'll assume that the address just failed to
356
                // attach its own port, so we'll use the default port. In the
357
                // case of IPv6 addresses, if the host is already surrounded by
358
                // brackets, then we'll avoid using the JoinHostPort function,
359
                // since it will always add a pair of brackets.
360
                if strings.HasPrefix(address, "[") {
3✔
UNCOV
361
                        return address + ":" + defaultPort
×
UNCOV
362
                }
×
363
                return net.JoinHostPort(address, defaultPort)
3✔
364
        }
365

366
        // In the case that both the host and port are empty, we'll use the
367
        // default port.
368
        if host == "" && port == "" {
3✔
UNCOV
369
                return ":" + defaultPort
×
UNCOV
370
        }
×
371

372
        return address
3✔
373
}
374

375
// ClientAddressDialer creates a gRPC dialer that can also dial unix socket
376
// addresses instead of just TCP addresses.
377
func ClientAddressDialer(defaultPort string) func(context.Context,
378
        string) (net.Conn, error) {
×
379

×
380
        return func(ctx context.Context, addr string) (net.Conn, error) {
×
381
                parsedAddr, err := ParseAddressString(
×
382
                        addr, defaultPort, net.ResolveTCPAddr,
×
383
                )
×
384
                if err != nil {
×
385
                        return nil, err
×
386
                }
×
387

388
                d := net.Dialer{}
×
389
                return d.DialContext(
×
390
                        ctx, parsedAddr.Network(), parsedAddr.String(),
×
391
                )
×
392
        }
393
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc