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

lightningnetwork / lnd / 15280233992

27 May 2025 04:07PM UTC coverage: 58.56% (+0.6%) from 57.977%
15280233992

Pull #9455

github

web-flow
Merge 015c776a9 into 93a6ab875
Pull Request #9455: discovery+lnwire: add support for DNS host name in NodeAnnouncement msg

145 of 291 new or added lines in 7 files covered. (49.83%)

120 existing lines in 10 files now uncovered.

97608 of 166681 relevant lines covered (58.56%)

1.82 hits per line

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

52.86
/lnwire/dns_addr.go
1
package lnwire
2

3
import (
4
        "errors"
5
        "fmt"
6
        "net"
7
        "strconv"
8
        "strings"
9
        "unicode"
10
)
11

12
// DNSAddr is a custom implementation of the net.Addr interface.
13
type DNSAddr struct {
14
        // Hostname represents the DNS hostname (e.g., "example.com").
15
        Hostname string
16

17
        // Port is the network port number (e.g., 9735 for LND peer port).
18
        Port int
19
}
20

21
// A compile-time check to ensure that DNSAddr implements the net.Addr
22
// interface.
23
var _ net.Addr = (*DNSAddr)(nil)
24

25
// Network returns the network type, e.g., "tcp".
26
func (d *DNSAddr) Network() string {
3✔
27
        return "tcp"
3✔
28
}
3✔
29

30
// String returns the address in the form "hostname:port".
31
func (d *DNSAddr) String() string {
3✔
32
        return net.JoinHostPort(d.Hostname, strconv.Itoa(d.Port))
3✔
33
}
3✔
34

35
// NewDNSAddr creates a new DNS address with the given host and port. It returns
36
// an error if the hostname is invalid according to DNS standards/BOLT 07
37
// specifications or if the port is outside the valid TCP
38
// range (1-65535).
39
func NewDNSAddr(host string, port int) (*DNSAddr, error) {
3✔
40
        dnsAddr := &DNSAddr{
3✔
41
                Hostname: host,
3✔
42
                Port:     port,
3✔
43
        }
3✔
44

3✔
45
        if err := dnsAddr.validate(); err != nil {
3✔
NEW
46
                return nil, err
×
NEW
47
        }
×
48

49
        return dnsAddr, nil
3✔
50
}
51

52
// validate checks that the DNSHostnameAddress is well-formed according to
53
// Lightning Network's BOLT 07 specification requirements. It ensures the
54
// hostname is not empty, doesn't exceed 255 characters, contains only ASCII
55
// characters, and that the port number is within the valid TCP range (1-65535).
56
func (d *DNSAddr) validate() error {
3✔
57
        // Ensure the hostname is not empty.
3✔
58
        if len(d.Hostname) == 0 {
3✔
NEW
59
                return errors.New("hostname cannot be empty")
×
NEW
60
        }
×
61

62
        // Ensure the hostname does not exceed the maximum allowed length of 255
63
        // octets. This limit includes the length of all labels, separators
64
        // (dots), and the null terminator.
65
        if len(d.Hostname) > 255 {
3✔
NEW
66
                return fmt.Errorf("hostname length is %d, exceeds maximum "+
×
NEW
67
                        "length of 255 characters", len(d.Hostname))
×
NEW
68
        }
×
69

70
        // Check if the hostname resembles an IPv4 address.
71
        if IsIPv4Like(d.Hostname) {
3✔
NEW
72
                return fmt.Errorf("hostname '%s' resembles an IPv4 address "+
×
NEW
73
                        "and cannot be used as a valid DNS hostname",
×
NEW
74
                        d.Hostname)
×
NEW
75
        }
×
76

77
        // Split hostname into labels and validate each.
78
        labels := strings.Split(d.Hostname, ".")
3✔
79
        for _, label := range labels {
6✔
80
                // RFC 1035, Section 2.3.4: Labels must not be empty.
3✔
81
                if len(label) == 0 {
3✔
NEW
82
                        return errors.New("hostname contains an empty label")
×
NEW
83
                }
×
84

85
                // RFC 1035, Section 2.3.4: The maximum length of a label is 63
86
                // characters.
87
                if len(label) > 63 {
3✔
NEW
88
                        return fmt.Errorf("hostname label '%s' exceeds "+
×
NEW
89
                                "maximum length of 63 characters", label)
×
NEW
90
                }
×
91

92
                // RFC 952 and RFC 1123: Labels must not start or end with a
93
                // hyphen. RFC 1123 relaxed the rules from RFC 952 to allow
94
                // hostnames to start with a digit, but the rule about hyphens
95
                // remains.
96
                if label[0] == '-' || label[len(label)-1] == '-' {
3✔
NEW
97
                        return fmt.Errorf("hostname label '%s' starts or ends "+
×
NEW
98
                                "with a hyphen", label)
×
NEW
99
                }
×
100

101
                // RFC 1035, Section 2.3.1: Labels must consist of only ASCII
102
                // letters (a-z, A-Z), digits (0-9), and hyphens (-). Any other
103
                // character, including non-ASCII characters, underscores, or
104
                // symbols, is invalid. BOLT 07 enforces this rule to ensure
105
                // node addresses are compatible with DNS standards.
106
                for _, ch := range label {
6✔
107
                        if !(unicode.IsLetter(ch) ||
3✔
108
                                unicode.IsDigit(ch) || (ch == '-')) {
3✔
NEW
109

×
NEW
110
                                return fmt.Errorf("hostname label '%s' "+
×
NEW
111
                                        "contains an invalid character '%c'",
×
NEW
112
                                        label, ch)
×
NEW
113
                        }
×
114
                }
115
        }
116

117
        // Validate port number. Valid TCP ports are in the range 1-65535.
118
        if d.Port < 1 || d.Port > 65535 {
3✔
NEW
119
                return fmt.Errorf("invalid port number %d, must be between 1 "+
×
NEW
120
                        "and 65535", d.Port)
×
NEW
121
        }
×
122

123
        return nil
3✔
124
}
125

126
// IsIPv4Like checks if the given address resembles an IPv4 address
127
// structure (e.g., "500.0.0.0"). It does not validate if the numeric
128
// values are within the standard IPv4 range (0-255).
129
func IsIPv4Like(address string) bool {
3✔
130
        // Split the address into parts using ".".
3✔
131
        parts := strings.Split(address, ".")
3✔
132
        if len(parts) != 4 {
6✔
133
                // IPv4-like addresses must have exactly 4 parts.
3✔
134
                return false
3✔
135
        }
3✔
136

137
        // Check each part to ensure it's numeric.
NEW
138
        for _, part := range parts {
×
NEW
139
                if _, err := strconv.Atoi(part); err != nil {
×
NEW
140
                        // Part is not numeric, so it's not IPv4-like.
×
NEW
141
                        return false
×
NEW
142
                }
×
143
        }
144

NEW
145
        return true
×
146
}
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