Skip to content

Commit cb36d26

Browse files
multi: add dns_hostname_address field
1 parent fcdd9bc commit cb36d26

File tree

13 files changed

+314
-43
lines changed

13 files changed

+314
-43
lines changed

config.go

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -325,26 +325,28 @@ type Config struct {
325325
// loadConfig function. We need to expose the 'raw' strings so the
326326
// command line library can access them.
327327
// Only the parsed net.Addrs should be used!
328-
RawRPCListeners []string `long:"rpclisten" description:"Add an interface/port/socket to listen for RPC connections"`
329-
RawRESTListeners []string `long:"restlisten" description:"Add an interface/port/socket to listen for REST connections"`
330-
RawListeners []string `long:"listen" description:"Add an interface/port to listen for peer connections"`
331-
RawExternalIPs []string `long:"externalip" description:"Add an ip:port to the list of local addresses we claim to listen on to peers. If a port is not specified, the default (9735) will be used regardless of other parameters"`
332-
ExternalHosts []string `long:"externalhosts" description:"Add a hostname:port that should be periodically resolved to announce IPs for. If a port is not specified, the default (9735) will be used."`
333-
RPCListeners []net.Addr
334-
RESTListeners []net.Addr
335-
RestCORS []string `long:"restcors" description:"Add an ip:port/hostname to allow cross origin access from. To allow all origins, set as \"*\"."`
336-
Listeners []net.Addr
337-
ExternalIPs []net.Addr
338-
DisableListen bool `long:"nolisten" description:"Disable listening for incoming peer connections"`
339-
DisableRest bool `long:"norest" description:"Disable REST API"`
340-
DisableRestTLS bool `long:"no-rest-tls" description:"Disable TLS for REST connections"`
341-
WSPingInterval time.Duration `long:"ws-ping-interval" description:"The ping interval for REST based WebSocket connections, set to 0 to disable sending ping messages from the server side"`
342-
WSPongWait time.Duration `long:"ws-pong-wait" description:"The time we wait for a pong response message on REST based WebSocket connections before the connection is closed as inactive"`
343-
NAT bool `long:"nat" description:"Toggle NAT traversal support (using either UPnP or NAT-PMP) to automatically advertise your external IP address to the network -- NOTE this does not support devices behind multiple NATs"`
344-
AddPeers []string `long:"addpeer" description:"Specify peers to connect to first"`
345-
MinBackoff time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
346-
MaxBackoff time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
347-
ConnectionTimeout time.Duration `long:"connectiontimeout" description:"The timeout value for network connections. Valid time units are {ms, s, m, h}."`
328+
RawRPCListeners []string `long:"rpclisten" description:"Add an interface/port/socket to listen for RPC connections"`
329+
RawRESTListeners []string `long:"restlisten" description:"Add an interface/port/socket to listen for REST connections"`
330+
RawListeners []string `long:"listen" description:"Add an interface/port to listen for peer connections"`
331+
RawExternalIPs []string `long:"externalip" description:"Add an ip:port to the list of local addresses we claim to listen on to peers. If a port is not specified, the default (9735) will be used regardless of other parameters"`
332+
RawExternalDNSHostnameAddress string `long:"external-dns-hostname" description:"Specify a DNS hostname for the node's external address. If no port is provided, the default (9735) is used."`
333+
ExternalHosts []string `long:"externalhosts" description:"Add a hostname:port that should be periodically resolved to announce IPs for. If a port is not specified, the default (9735) will be used."`
334+
RPCListeners []net.Addr
335+
RESTListeners []net.Addr
336+
RestCORS []string `long:"restcors" description:"Add an ip:port/hostname to allow cross origin access from. To allow all origins, set as \"*\"."`
337+
Listeners []net.Addr
338+
ExternalIPs []net.Addr
339+
ExternalDNSHostnameAddress *lnwire.DNSHostnameAddress
340+
DisableListen bool `long:"nolisten" description:"Disable listening for incoming peer connections"`
341+
DisableRest bool `long:"norest" description:"Disable REST API"`
342+
DisableRestTLS bool `long:"no-rest-tls" description:"Disable TLS for REST connections"`
343+
WSPingInterval time.Duration `long:"ws-ping-interval" description:"The ping interval for REST based WebSocket connections, set to 0 to disable sending ping messages from the server side"`
344+
WSPongWait time.Duration `long:"ws-pong-wait" description:"The time we wait for a pong response message on REST based WebSocket connections before the connection is closed as inactive"`
345+
NAT bool `long:"nat" description:"Toggle NAT traversal support (using either UPnP or NAT-PMP) to automatically advertise your external IP address to the network -- NOTE this does not support devices behind multiple NATs"`
346+
AddPeers []string `long:"addpeer" description:"Specify peers to connect to first"`
347+
MinBackoff time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
348+
MaxBackoff time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
349+
ConnectionTimeout time.Duration `long:"connectiontimeout" description:"The timeout value for network connections. Valid time units are {ms, s, m, h}."`
348350

349351
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <global-level>,<subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"`
350352

@@ -1555,6 +1557,7 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
15551557
ltndLog.Infof("Listening on the p2p interface is disabled!")
15561558
cfg.Listeners = nil
15571559
cfg.ExternalIPs = nil
1560+
cfg.ExternalDNSHostnameAddress = nil
15581561
} else {
15591562

15601563
// Add default port to all listener addresses if needed and remove
@@ -1578,6 +1581,23 @@ func ValidateConfig(cfg Config, interceptor signal.Interceptor, fileParser,
15781581
return nil, err
15791582
}
15801583

1584+
// Add default port to external dnsh hostname address if needed.
1585+
if cfg.RawExternalDNSHostnameAddress != "" {
1586+
addr, err := parseAddr(
1587+
cfg.RawExternalDNSHostnameAddress, cfg.net,
1588+
)
1589+
if err != nil {
1590+
return nil, err
1591+
}
1592+
dnsAddr, ok := addr.(*lnwire.DNSHostnameAddress)
1593+
if !ok {
1594+
return nil, fmt.Errorf("failed to cast "+
1595+
"address to lnwire.DNSHostnameAddr: "+
1596+
"got %T", addr)
1597+
}
1598+
cfg.ExternalDNSHostnameAddress = dnsAddr
1599+
}
1600+
15811601
// For the p2p port it makes no sense to listen to an Unix socket.
15821602
// Also, we would need to refactor the brontide listener to support
15831603
// that.

discovery/gossiper.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1988,6 +1988,7 @@ func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement,
19881988
HaveNodeAnnouncement: true,
19891989
LastUpdate: timestamp,
19901990
Addresses: msg.Addresses,
1991+
DNSHostnameAddress: msg.DNSHostnameAddress,
19911992
PubKeyBytes: msg.NodeID,
19921993
Alias: msg.Alias.String(),
19931994
AuthSigBytes: msg.Signature.ToSignatureBytes(),

graph/db/addr.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io"
88
"net"
99

10+
"github.com/lightningnetwork/lnd/lnwire"
1011
"github.com/lightningnetwork/lnd/tor"
1112
)
1213

@@ -26,6 +27,9 @@ const (
2627

2728
// v3OnionAddr denotes a version 3 Tor (prop224) onion service address.
2829
v3OnionAddr addressType = 3
30+
31+
// dnsHostnameAddr denotes a DNS hostname address.
32+
dnsHostnameAddr addressType = 4
2933
)
3034

3135
// encodeTCPAddr serializes a TCP address into its compact raw bytes
@@ -121,6 +125,47 @@ func encodeOnionAddr(w io.Writer, addr *tor.OnionAddr) error {
121125
return nil
122126
}
123127

128+
// encodeDNSHostnameAddr serializes a DNS hostname address into its compact raw
129+
// bytes representation. It writes the address type, hostname length, hostname,
130+
// and port (in big-endian order) to the writer. The function validates that the
131+
// hostname is non-empty and does not exceed 255 characters. Returns an error if
132+
// any part of the serialization fails.
133+
func encodeDNSHostnameAddr(w io.Writer, addr *lnwire.DNSHostnameAddress) error {
134+
// Validate the hostname.
135+
if len(addr.Hostname) == 0 {
136+
return errors.New("hostname cannot be empty")
137+
}
138+
if len(addr.Hostname) > 255 {
139+
return errors.New("hostname exceeds maximum length of 255 " +
140+
"characters")
141+
}
142+
143+
// Write the address type.
144+
if _, err := w.Write([]byte{byte(dnsHostnameAddr)}); err != nil {
145+
return err
146+
}
147+
148+
// Write the length of the hostname.
149+
hostnameLen := byte(len(addr.Hostname))
150+
if _, err := w.Write([]byte{hostnameLen}); err != nil {
151+
return err
152+
}
153+
154+
// Write the hostname bytes.
155+
if _, err := w.Write([]byte(addr.Hostname)); err != nil {
156+
return err
157+
}
158+
159+
// Write the port in big-endian order.
160+
var port [2]byte
161+
binary.BigEndian.PutUint16(port[:], uint16(addr.Port))
162+
if _, err := w.Write(port[:]); err != nil {
163+
return err
164+
}
165+
166+
return nil
167+
}
168+
124169
// DeserializeAddr reads the serialized raw representation of an address and
125170
// deserializes it into the actual address. This allows us to avoid address
126171
// resolution within the channeldb package.
@@ -200,6 +245,29 @@ func DeserializeAddr(r io.Reader) (net.Addr, error) {
200245
OnionService: onionService,
201246
Port: port,
202247
}
248+
249+
case dnsHostnameAddr:
250+
var hostnameLen byte
251+
err := binary.Read(r, binary.BigEndian, &hostnameLen)
252+
if err != nil {
253+
return nil, err
254+
}
255+
256+
hostname := make([]byte, hostnameLen)
257+
if _, err := r.Read(hostname); err != nil {
258+
return nil, err
259+
}
260+
261+
var port [2]byte
262+
if _, err := r.Read(port[:]); err != nil {
263+
return nil, err
264+
}
265+
266+
address = &lnwire.DNSHostnameAddress{
267+
Hostname: string(hostname),
268+
Port: int(binary.BigEndian.Uint16(port[:])),
269+
}
270+
203271
default:
204272
return nil, ErrUnknownAddressType
205273
}
@@ -215,6 +283,8 @@ func SerializeAddr(w io.Writer, address net.Addr) error {
215283
return encodeTCPAddr(w, addr)
216284
case *tor.OnionAddr:
217285
return encodeOnionAddr(w, addr)
286+
case *lnwire.DNSHostnameAddress:
287+
return encodeDNSHostnameAddr(w, addr)
218288
default:
219289
return ErrUnknownAddressType
220290
}

graph/db/graph.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3972,13 +3972,20 @@ func putLightningNode(nodeBucket kvdb.RwBucket, aliasBucket kvdb.RwBucket, // no
39723972
return err
39733973
}
39743974

3975-
numAddresses := uint16(len(node.Addresses))
3975+
var allAddresses []net.Addr
3976+
var numAddresses uint16
3977+
if node.DNSHostnameAddress != nil {
3978+
allAddresses = append(allAddresses, node.DNSHostnameAddress)
3979+
numAddresses += 1
3980+
}
3981+
allAddresses = append(allAddresses, node.Addresses...)
3982+
numAddresses += uint16(len(node.Addresses))
39763983
byteOrder.PutUint16(scratch[:2], numAddresses)
39773984
if _, err := b.Write(scratch[:2]); err != nil {
39783985
return err
39793986
}
39803987

3981-
for _, address := range node.Addresses {
3988+
for _, address := range allAddresses {
39823989
if err := SerializeAddr(&b, address); err != nil {
39833990
return err
39843991
}
@@ -4171,14 +4178,20 @@ func deserializeLightningNode(r io.Reader) (models.LightningNode, error) {
41714178
numAddresses := int(byteOrder.Uint16(scratch[:2]))
41724179

41734180
var addresses []net.Addr
4181+
var dnsHostnameAddress *lnwire.DNSHostnameAddress
41744182
for i := 0; i < numAddresses; i++ {
41754183
address, err := DeserializeAddr(r)
41764184
if err != nil {
41774185
return models.LightningNode{}, err
41784186
}
4179-
addresses = append(addresses, address)
4187+
if dnsAddr, ok := address.(*lnwire.DNSHostnameAddress); ok {
4188+
dnsHostnameAddress = dnsAddr
4189+
} else {
4190+
addresses = append(addresses, address)
4191+
}
41804192
}
41814193
node.Addresses = addresses
4194+
node.DNSHostnameAddress = dnsHostnameAddress
41824195

41834196
node.AuthSigBytes, err = wire.ReadVarBytes(r, 0, 80, "sig")
41844197
if err != nil {

graph/db/models/node.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ type LightningNode struct {
3232
// Address is the TCP address this node is reachable over.
3333
Addresses []net.Addr
3434

35+
// DNSHostnameAddress is the DNS hostname address for this node.
36+
DNSHostnameAddress *lnwire.DNSHostnameAddress
37+
3538
// Color is the selected color for the node.
3639
Color color.RGBA
3740

@@ -109,13 +112,14 @@ func (l *LightningNode) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement,
109112
}
110113

111114
nodeAnn := &lnwire.NodeAnnouncement{
112-
Features: l.Features.RawFeatureVector,
113-
NodeID: l.PubKeyBytes,
114-
RGBColor: l.Color,
115-
Alias: alias,
116-
Addresses: l.Addresses,
117-
Timestamp: uint32(l.LastUpdate.Unix()),
118-
ExtraOpaqueData: l.ExtraOpaqueData,
115+
Features: l.Features.RawFeatureVector,
116+
NodeID: l.PubKeyBytes,
117+
RGBColor: l.Color,
118+
Alias: alias,
119+
Addresses: l.Addresses,
120+
DNSHostnameAddress: l.DNSHostnameAddress,
121+
Timestamp: uint32(l.LastUpdate.Unix()),
122+
ExtraOpaqueData: l.ExtraOpaqueData,
119123
}
120124

121125
if !signed {

lnrpc/peersrpc/peers_server.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,32 @@ func (s *Server) UpdateNodeAnnouncement(_ context.Context,
371371
}
372372
}
373373

374+
if req.DnsHostnameAddress != "" {
375+
addr, err := s.cfg.ParseAddr(req.DnsHostnameAddress)
376+
if err != nil {
377+
return nil, fmt.Errorf("invalid dns hostname address "+
378+
"value: %w", err)
379+
}
380+
dnsAddr, ok := addr.(*lnwire.DNSHostnameAddress)
381+
if !ok {
382+
return nil, fmt.Errorf("failed to cast address to "+
383+
"lnwire.DNSHostnameAddr: got %T", addr)
384+
}
385+
386+
if dnsAddr != currentNodeAnn.DNSHostnameAddress {
387+
resp.Ops = append(resp.Ops, &lnrpc.Op{
388+
Entity: "dns_hostname_address",
389+
Actions: []string{
390+
fmt.Sprintf("changed to %v", dnsAddr),
391+
},
392+
})
393+
nodeModifiers = append(
394+
nodeModifiers,
395+
netann.NodeAnnSetDNSHostnameAddress(dnsAddr),
396+
)
397+
}
398+
}
399+
374400
if len(req.AddressUpdates) > 0 {
375401
newAddrs, ops, err := s.updateAddresses(
376402
currentNodeAnn.Addresses,

lnwire/dns_hostname_addr.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package lnwire
2+
3+
import (
4+
"fmt"
5+
"net"
6+
)
7+
8+
// DNSHostnameAddress is a custom implementation of the net.Addr interface.
9+
type DNSHostnameAddress struct {
10+
Hostname string
11+
Port int
12+
}
13+
14+
// A compile-time check to ensure that DNSHostnameAddr implements
15+
// the net.Addr interface.
16+
var _ net.Addr = (*DNSHostnameAddress)(nil)
17+
18+
// Network returns the network type, e.g., "tcp".
19+
func (d *DNSHostnameAddress) Network() string {
20+
return "tcp"
21+
}
22+
23+
// String returns the address in the form "hostname:port".
24+
func (d *DNSHostnameAddress) String() string {
25+
return fmt.Sprintf("%s:%d", d.Hostname, d.Port)
26+
}

0 commit comments

Comments
 (0)