p2p: fix ports handling, add test cases

This commit is contained in:
Lucas Vasconcelos 2024-11-27 20:01:17 -03:00
parent 09f5fd6983
commit 2f8ab8080f
3 changed files with 110 additions and 25 deletions

View file

@ -74,6 +74,7 @@ func TestNodeEndpoints(t *testing.T) {
wantUDP int
wantTCP int
wantQUIC int
wantDNS string
}
tests := []endpointTest{
{
@ -268,6 +269,54 @@ func TestNodeEndpoints(t *testing.T) {
wantIP: netip.MustParseAddr("2001::ff00:0042:8329"),
wantQUIC: 9001,
},
{
name: "dns-only",
node: func() *Node {
var r enr.Record
n := SignNull(&r, id)
n.dnsName = "example.com"
n.tcp = 30303
n.udp = 30303
return n
}(),
wantTCP: 30303,
wantUDP: 30303,
wantDNS: "example.com",
},
{
name: "dns-with-ports",
node: func() *Node {
var r enr.Record
r.Set(enr.TCP(9000))
r.Set(enr.UDP(9001))
n := SignNull(&r, id)
n.dnsName = "node.example.org"
n.tcp = 9000
n.udp = 9001
return n
}(),
wantTCP: 9000,
wantUDP: 9001,
wantDNS: "node.example.org",
},
{
name: "dns-with-ip-fallback",
node: func() *Node {
var r enr.Record
r.Set(enr.IPv4Addr(netip.MustParseAddr("192.168.1.1")))
r.Set(enr.TCP(9000))
r.Set(enr.UDP(9000))
n := SignNull(&r, id)
n.dnsName = "node.example.org"
n.tcp = 9000
n.udp = 9000
return n
}(),
wantIP: netip.MustParseAddr("192.168.1.1"),
wantTCP: 9000,
wantUDP: 9000,
wantDNS: "node.example.org",
},
}
for _, test := range tests {
@ -284,6 +333,9 @@ func TestNodeEndpoints(t *testing.T) {
if quic, _ := test.node.QUICEndpoint(); test.wantQUIC != int(quic.Port()) {
t.Errorf("node has wrong QUIC port %d, want %d", quic.Port(), test.wantQUIC)
}
if test.wantDNS != test.node.DNSName() {
t.Errorf("node has wrong DNS name %s, want %s", test.node.DNSName(), test.wantDNS)
}
})
}
}

View file

@ -101,6 +101,32 @@ func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node {
return n
}
func NewV4WithDNS(pubkey *ecdsa.PublicKey, ip net.IP, dnsName string, tcp, udp int) *Node {
var r enr.Record
if len(ip) > 0 {
r.Set(enr.IP(ip))
}
// Always set TCP/UDP ports regardless of IP
// This is to ensure that the node is always
// considered valid even if the IP is not
// set.
if tcp != 0 {
r.Set(enr.TCP(tcp))
}
if udp != 0 {
r.Set(enr.UDP(udp))
}
signV4Compat(&r, pubkey)
n, err := New(v4CompatID{}, &r)
if err != nil {
panic(err)
}
n.dnsName = dnsName
n.tcp = uint16(tcp)
n.udp = uint16(udp)
return n
}
// isNewV4 returns true for nodes created by NewV4.
func isNewV4(n *Node) bool {
var k s256raw
@ -139,16 +165,18 @@ func parseComplete(rawurl string) (*Node, error) {
}
// Check if hostname is an IP address and create node accordingly
hostname := u.Hostname()
if ip := net.ParseIP(hostname); ip != nil {
// Create node with IP
node := NewV4(id, ip, int(tcpPort), int(udpPort))
return node, nil
ip := net.ParseIP(hostname)
if ip == nil {
ips, err := lookupIPFunc(hostname)
if err != nil {
return NewV4WithDNS(id, nil, hostname, int(tcpPort), int(udpPort)), nil
}
// Create node for DNS name
node := NewV4(id, nil, int(tcpPort), int(udpPort))
node.dnsName = hostname
return node, nil
ip = ips[0]
}
if ipv4 := ip.To4(); ipv4 != nil {
ip = ipv4
}
return NewV4(id, ip, int(tcpPort), int(udpPort)), nil
}
// parsePubkey parses a hex-encoded secp256k1 public key.
@ -178,13 +206,13 @@ func (n *Node) URLv4() string {
nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:])
}
u := url.URL{Scheme: "enode"}
if !n.ip.IsValid() {
if !n.ip.IsValid() && n.dnsName == "" {
u.Host = nodeid
} else {
return u.String()
}
u.User = url.User(nodeid)
// Use DNS name if available, otherwise use IP
if n.DNSName() != "" {
u.Host = fmt.Sprintf("%s:%d", n.DNSName(), n.TCP())
if n.dnsName != "" {
u.Host = fmt.Sprintf("%s:%d", n.dnsName, n.TCP())
} else {
addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()}
u.Host = addr.String()
@ -192,7 +220,6 @@ func (n *Node) URLv4() string {
if n.UDP() != n.TCP() {
u.RawQuery = "discport=" + strconv.Itoa(n.UDP())
}
}
return u.String()
}

View file

@ -70,10 +70,6 @@ var parseNodeTests = []struct {
wantError: enr.ErrInvalidSig.Error(),
},
// Complete node URLs with IP address and ports
{
input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@invalid.:3",
wantError: `no such host`,
},
{
input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo",
wantError: `invalid port`,
@ -82,6 +78,16 @@ var parseNodeTests = []struct {
input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo",
wantError: `invalid discport in query`,
},
{
input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@valid.:3",
wantResult: NewV4WithDNS(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
nil,
"valid.",
3,
3,
),
},
{
input: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
wantResult: NewV4(