Fix DNS issue in 2.4.0 (#937)

* Fix DNS issue in v2.4.0 as well as code refactoring
This commit is contained in:
Zhou Zhiyao 2020-02-11 05:04:16 +08:00 committed by GitHub
parent a5d329391d
commit 29d53fb649
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 244 additions and 132 deletions

View File

@ -191,7 +191,7 @@ func (pool *serverPool) discoverNodes() {
if err != nil {
continue
}
pool.discNodes <- enode.NewV4(pubkey, n.IP, int(n.TCP), int(n.UDP), 0)
pool.discNodes <- enode.NewV4(pubkey, n.IP, int(n.TCP), int(n.UDP))
}
}
@ -667,7 +667,7 @@ func (e *poolEntry) DecodeRLP(s *rlp.Stream) error {
return err
}
addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()}
e.node = enode.NewV4(pubkey, entry.IP, int(entry.Port), int(entry.Port), 0)
e.node = enode.NewV4(pubkey, entry.IP, int(entry.Port), int(entry.Port))
e.addr = make(map[string]*poolEntryAddress)
e.addr[addr.strKey()] = addr
e.addrSelect = *newWeightedRandomSelect()

View File

@ -57,7 +57,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
// Fill up the sender's bucket.
pingKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
pingSender := wrapNode(enode.NewV4(&pingKey.PublicKey, net.IP{}, 99, 99, 0))
pingSender := wrapNode(enode.NewV4(&pingKey.PublicKey, net.IP{}, 99, 99))
last := fillBucket(tab, pingSender)
// Add the sender as if it just pinged us. Revalidate should replace the last node in
@ -289,7 +289,7 @@ func TestTable_Lookup(t *testing.T) {
}
// seed table with initial node (otherwise lookup will terminate immediately)
seedKey, _ := decodePubkey(lookupTestnet.dists[256][0])
seed := wrapNode(enode.NewV4(seedKey, net.IP{}, 0, 256, 0))
seed := wrapNode(enode.NewV4(seedKey, net.IP{}, 0, 256))
tab.stuff([]*node{seed})
results := tab.lookup(lookupTestnet.target, true)
@ -524,7 +524,7 @@ func (tn *preminedTestnet) findnode(toid enode.ID, toaddr *net.UDPAddr, target e
var result []*node
for i, ekey := range tn.dists[toaddr.Port] {
key, _ := decodePubkey(ekey)
node := wrapNode(enode.NewV4(key, net.ParseIP("127.0.0.1"), i, next, 0))
node := wrapNode(enode.NewV4(key, net.ParseIP("127.0.0.1"), i, next))
result = append(result, node)
}
return result, nil

View File

@ -141,7 +141,7 @@ func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*node, error) {
if err != nil {
return nil, err
}
n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP), 0))
n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP)))
err = n.ValidateComplete()
return n, err
}
@ -628,7 +628,7 @@ func (req *ping) handle(t *udp, from *net.UDPAddr, fromKey encPubkey, mac []byte
ReplyTok: mac,
Expiration: uint64(time.Now().Add(expiration).Unix()),
})
n := wrapNode(enode.NewV4(key, from.IP, int(req.From.TCP), from.Port, 0))
n := wrapNode(enode.NewV4(key, from.IP, int(req.From.TCP), from.Port))
t.handleReply(n.ID(), pingPacket, req)
if time.Since(t.db.LastPongReceived(n.ID())) > bondExpiration {
t.sendPing(n.ID(), from, func() { t.tab.addThroughPing(n) })

View File

@ -244,7 +244,7 @@ func TestUDP_findnode(t *testing.T) {
nodes := &nodesByDistance{target: testTarget.id()}
for i := 0; i < bucketSize; i++ {
key := newkey()
n := wrapNode(enode.NewV4(&key.PublicKey, net.IP{10, 13, 0, 1}, 0, i, 0))
n := wrapNode(enode.NewV4(&key.PublicKey, net.IP{10, 13, 0, 1}, 0, i))
nodes.push(n, bucketSize)
}
test.table.stuff(nodes.entries)
@ -311,7 +311,7 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
rpclist := make([]rpcNode, len(list))
for i := range list {
rpclist[i] = nodeToRPC(list[i])
list[i] = wrapNode(enode.NewV4(list[i].Pubkey(), list[i].IP(), list[i].TCP(), list[i].UDP(), 0))
list[i] = wrapNode(enode.NewV4(list[i].Pubkey(), list[i].IP(), list[i].TCP(), list[i].UDP()))
}
test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[:2]})
test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[2:]})

View File

@ -21,7 +21,6 @@ import (
"encoding/hex"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/log"
"math/bits"
"math/rand"
"net"
@ -59,9 +58,10 @@ func (n *Node) Seq() uint64 {
return n.r.Seq()
}
// Incomplete returns true for nodes with no IP address.
// Quorum
// Incomplete returns true for nodes with no IP address and no hostname if with raftport.
func (n *Node) Incomplete() bool {
return n.IP() == nil
return n.IP() == nil && (!n.HasRaftPort() || (n.Host() == "" && n.HasRaftPort()))
}
// Load retrieves an entry from the underlying record.
@ -70,21 +70,27 @@ func (n *Node) Load(k enr.Entry) error {
}
// IP returns the IP address of the node.
//
// Quorum
// To support DNS lookup in node ip. The function performs hostname lookup if hostname is defined in enr.Hostname
// and falls back to enr.IP value in case of failure. It also makes sure the resolved IP is in IPv4 or IPv6 format
func (n *Node) IP() net.IP {
// QUORUM
// no host is set, so use the IP directly
if n.Host() == "" {
// no host is set, so use the IP directly
return n.loadIP()
}
// attempt to look up IP addresses if host is a FQDN
lookupIPs, err := net.LookupIP(n.Host())
if err != nil {
log.Debug("hostname couldn't resolve, using IP instead", "hostname", n.Host(), "err", err.Error())
return n.loadIP()
}
// set to first ip by default
return lookupIPs[0]
// END QUORUM
// set to first ip by default & as Ethereum upstream
ip := lookupIPs[0]
// Ensure the IP is 4 bytes long for IPv4 addresses.
if ipv4 := ip.To4(); ipv4 != nil {
ip = ipv4
}
return ip
}
func (n *Node) loadIP() net.IP {
@ -99,6 +105,7 @@ func (n *Node) Host() string {
n.Load((*enr.Hostname)(&hostname))
return hostname
}
// End-Quorum
// UDP returns the UDP port of the node.

View File

@ -19,6 +19,7 @@ package enode
import (
"encoding/hex"
"fmt"
"net"
"testing"
"github.com/ethereum/go-ethereum/p2p/enr"
@ -60,3 +61,84 @@ func TestPythonInterop(t *testing.T) {
}
}
}
// Quorum
// test Incomplete
func TestIncomplete(t *testing.T) {
var testCases = []struct {
n *Node
isIncomplete bool
}{
{
n: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
net.IP{127, 0, 0, 1},
52150,
52150,
),
isIncomplete: false,
},
{
n: NewV4(hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
net.ParseIP("::"),
52150,
52150,
),
isIncomplete: false,
},
{
n: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
52150,
52150,
),
isIncomplete: false,
},
{
n: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
nil,
52150,
52150,
),
isIncomplete: true,
},
{
n: NewV4Hostname(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
"hostname",
52150,
52150,
50400,
),
isIncomplete: false,
},
{
n: NewV4Hostname(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
"hostname",
52150,
52150,
0,
),
isIncomplete: true,
},
{
n: NewV4Hostname(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
"",
52150,
52150,
50400,
),
isIncomplete: true,
},
}
for i, test := range testCases {
if test.n.Incomplete() != test.isIncomplete {
t.Errorf("test %d: Node.Incomplete() mismatch:\ngot: %v\nwant: %v", i, test.n.Incomplete(), test.isIncomplete)
}
}
}

View File

@ -105,7 +105,6 @@ func TestDBFetchStore(t *testing.T) {
net.IP{192, 168, 0, 1},
30303,
30303,
0,
)
inst := time.Now()
num := 314
@ -169,7 +168,6 @@ var nodeDBSeedQueryNodes = []struct {
net.IP{127, 0, 0, 3},
30303,
30303,
0,
),
pong: time.Now().Add(-3 * time.Hour),
},
@ -181,7 +179,6 @@ var nodeDBSeedQueryNodes = []struct {
net.IP{127, 0, 0, 3},
30303,
30303,
0,
),
pong: time.Now().Add(-4 * time.Second),
},
@ -193,7 +190,6 @@ var nodeDBSeedQueryNodes = []struct {
net.IP{127, 0, 0, 1},
30303,
30303,
0,
),
pong: time.Now().Add(-2 * time.Second),
},
@ -203,7 +199,6 @@ var nodeDBSeedQueryNodes = []struct {
net.IP{127, 0, 0, 2},
30303,
30303,
0,
),
pong: time.Now().Add(-3 * time.Second),
},
@ -213,7 +208,6 @@ var nodeDBSeedQueryNodes = []struct {
net.IP{127, 0, 0, 3},
30303,
30303,
0,
),
pong: time.Now().Add(-1 * time.Second),
},
@ -223,7 +217,6 @@ var nodeDBSeedQueryNodes = []struct {
net.IP{127, 0, 0, 3},
30303,
30303,
0,
),
pong: time.Now().Add(-2 * time.Second),
},
@ -233,7 +226,6 @@ var nodeDBSeedQueryNodes = []struct {
net.IP{127, 0, 0, 3},
30303,
30303,
0,
),
pong: time.Now().Add(-2 * time.Second),
},
@ -339,7 +331,6 @@ var nodeDBExpirationNodes = []struct {
net.IP{127, 0, 0, 1},
30303,
30303,
0,
),
pong: time.Now().Add(-dbNodeExpiration + time.Minute),
exp: false,
@ -349,7 +340,6 @@ var nodeDBExpirationNodes = []struct {
net.IP{127, 0, 0, 2},
30303,
30303,
0,
),
pong: time.Now().Add(-dbNodeExpiration - time.Minute),
exp: true,

View File

@ -56,7 +56,7 @@ func MustParseV4(rawurl string) *Node {
//
// For complete nodes, the node ID is encoded in the username portion
// of the URL, separated from the host by an @ sign. The hostname can
// only be given as an IP address, DNS domain names are not allowed.
// be given as an IP address or a DNS domain name.
// The port in the host name section is the TCP listening port. If the
// TCP and UDP (discovery) ports differ, the UDP port is specified as
// query parameter "discport".
@ -70,37 +70,32 @@ func ParseV4(rawurl string) (*Node, error) {
if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
id, err := parsePubkey(m[1])
if err != nil {
return nil, fmt.Errorf("invalid node ID (%v)", err)
return nil, fmt.Errorf("invalid public key (%v)", err)
}
return NewV4(id, nil, 0, 0, 0), nil
return NewV4(id, nil, 0, 0), nil
}
return parseComplete(rawurl)
}
// NewV4 creates a node from discovery v4 node information. The record
// contained in the node has a zero-length signature.
func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp, raftPort int) *Node {
func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node {
var r enr.Record
if ip != nil {
if len(ip) > 0 {
r.Set(enr.IP(ip))
}
return newV4(pubkey, r, tcp, udp, raftPort)
return newV4(pubkey, r, tcp, udp)
}
// broken out from `func NewV4` (above) same in upstream go-ethereum, but taken out
// to avoid code duplication b/t NewV4 and NewV4Hostname
func newV4(pubkey *ecdsa.PublicKey, r enr.Record, tcp, udp, raftPort int) *Node {
func newV4(pubkey *ecdsa.PublicKey, r enr.Record, tcp, udp int) *Node {
if udp != 0 {
r.Set(enr.UDP(udp))
}
if tcp != 0 {
r.Set(enr.TCP(tcp))
}
if raftPort != 0 { // Quorum
r.Set(enr.RaftPort(raftPort))
}
signV4Compat(&r, pubkey)
n, err := New(v4CompatID{}, &r)
if err != nil {
@ -112,14 +107,22 @@ func newV4(pubkey *ecdsa.PublicKey, r enr.Record, tcp, udp, raftPort int) *Node
// Quorum
// NewV4Hostname creates a node from discovery v4 node information. The record
// contained in the node has a zero-length signature. It sets the hostname of
// the node instead of the IP address.
// contained in the node has a zero-length signature. It sets the hostname or ip
// of the node depends on hostname context
func NewV4Hostname(pubkey *ecdsa.PublicKey, hostname string, tcp, udp, raftPort int) *Node {
var r enr.Record
if hostname != "" {
if ip := net.ParseIP(hostname); ip == nil {
r.Set(enr.Hostname(hostname))
} else {
r.Set(enr.IP(ip))
}
return newV4(pubkey, r, tcp, udp, raftPort)
if raftPort != 0 {
r.Set(enr.RaftPort(raftPort))
}
return newV4(pubkey, r, tcp, udp)
}
// End-Quorum
@ -127,6 +130,7 @@ func NewV4Hostname(pubkey *ecdsa.PublicKey, hostname string, tcp, udp, raftPort
func parseComplete(rawurl string) (*Node, error) {
var (
id *ecdsa.PublicKey
ip net.IP
tcpPort, udpPort uint64
)
u, err := url.Parse(rawurl)
@ -141,14 +145,29 @@ func parseComplete(rawurl string) (*Node, error) {
return nil, errors.New("does not contain node ID")
}
if id, err = parsePubkey(u.User.String()); err != nil {
return nil, fmt.Errorf("invalid node ID (%v)", err)
return nil, fmt.Errorf("invalid public key (%v)", err)
}
// move qv up to here
qv := u.Query()
// Parse the IP address.
ips, err := net.LookupIP(u.Hostname())
if err != nil {
// Quorum: if IP look up fail don't return error for raft url
if qv.Get("raftport") == "" {
return nil, err
}
} else {
ip = ips[0]
// Ensure the IP is 4 bytes long for IPv4 addresses.
if ipv4 := ip.To4(); ipv4 != nil {
ip = ipv4
}
}
// Parse the port numbers.
if tcpPort, err = strconv.ParseUint(u.Port(), 10, 16); err != nil {
return nil, errors.New("invalid port")
}
udpPort = tcpPort
qv := u.Query()
if qv.Get("discport") != "" {
udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
if err != nil {
@ -156,21 +175,20 @@ func parseComplete(rawurl string) (*Node, error) {
}
}
var node *Node
// Quorum
if qv.Get("raftport") != "" {
raftPort, err := strconv.ParseUint(qv.Get("raftport"), 10, 16)
if err != nil {
return nil, errors.New("invalid raftport in query")
}
node = NewV4Hostname(id, u.Hostname(), int(tcpPort), int(udpPort), int(raftPort))
} else {
node = NewV4Hostname(id, u.Hostname(), int(tcpPort), int(udpPort), 0)
if u.Hostname() == "" {
return nil, errors.New("empty hostname in raft url")
}
return NewV4Hostname(id, u.Hostname(), int(tcpPort), int(udpPort), int(raftPort)), nil
}
// End-Quorum
return node, nil
return NewV4(id, ip, int(tcpPort), int(udpPort)), nil
}
func HexPubkey(h string) (*ecdsa.PublicKey, error) {
@ -229,9 +247,14 @@ func (n *Node) v4URL() string {
if n.Incomplete() {
u.Host = nodeid
} else {
addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()}
u.User = url.User(nodeid)
u.Host = addr.String()
if n.Host() != "" && net.ParseIP(n.Host()) == nil {
// Quorum
u.Host = net.JoinHostPort(n.Host(), strconv.Itoa(n.TCP()))
} else {
addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()}
u.Host = addr.String()
}
if n.UDP() != n.TCP() {
u.RawQuery = "discport=" + strconv.Itoa(n.UDP())
}

View File

@ -20,6 +20,7 @@ import (
"bytes"
"crypto/ecdsa"
"math/big"
"net"
"reflect"
"strings"
"testing"
@ -37,7 +38,7 @@ var parseNodeTests = []struct {
},
{
rawurl: "enode://01010101@123.124.125.126:3",
wantError: `invalid node ID (wrong length, want 128 hex chars)`,
wantError: `invalid public key (wrong length, want 128 hex chars)`,
},
// Complete nodes with IP address.
{
@ -50,42 +51,38 @@ var parseNodeTests = []struct {
},
{
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
wantResult: NewV4Hostname(
wantResult: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
"127.0.0.1",
net.IP{127, 0, 0, 1},
52150,
52150,
0,
),
},
{
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150",
wantResult: NewV4Hostname(
wantResult: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
"::",
net.ParseIP("::"),
52150,
52150,
0,
),
},
{
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150",
wantResult: NewV4Hostname(
wantResult: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
"2001:db8:3c4d:15::abcd:ef12",
net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
52150,
52150,
0,
),
},
{
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334",
wantResult: NewV4Hostname(
wantResult: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
"127.0.0.1",
net.IP{0x7f, 0x0, 0x0, 0x1},
52150,
22334,
0,
),
},
// Incomplete nodes with no address.
@ -93,30 +90,45 @@ var parseNodeTests = []struct {
rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
wantResult: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
nil, 0, 0, 0,
nil, 0, 0,
),
},
{
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
wantResult: NewV4(
hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
nil, 0, 0, 0,
nil, 0, 0,
),
},
// Invalid URLs
{
rawurl: "01010101",
wantError: `invalid node ID (wrong length, want 128 hex chars)`,
wantError: `invalid public key (wrong length, want 128 hex chars)`,
},
{
rawurl: "enode://01010101",
wantError: `invalid node ID (wrong length, want 128 hex chars)`,
wantError: `invalid public key (wrong length, want 128 hex chars)`,
},
{
// This test checks that errors from url.Parse are handled.
rawurl: "://foo",
wantError: `parse ://foo: missing protocol scheme`,
},
{
// Quorum: raft url with invalid hostname (no error, hostname will be saved)
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3?raftport=50401",
wantResult: NewV4Hostname(hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), "hostname", 3, 3, 50401),
},
{
// Quorum: raft url with valid hostname
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@localhost:3?raftport=50401",
wantResult: NewV4Hostname(hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), "localhost", 3, 3, 50401),
},
{
// Quorum: raft url with no hostname
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@:3?raftport=50401",
wantError: `empty hostname in raft url`,
},
}
func hexPubkey(h string) *ecdsa.PublicKey {
@ -128,20 +140,7 @@ func hexPubkey(h string) *ecdsa.PublicKey {
}
func TestParseNode(t *testing.T) {
extraTests := []struct {
rawurl string
wantError string
wantResult *Node
}{
{
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3",
wantResult: NewV4Hostname(hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), "hostname", 3, 3, 0, ),
},
}
testNodes := append(parseNodeTests, extraTests...)
for _, test := range testNodes {
for _, test := range parseNodeTests {
n, err := ParseV4(test.rawurl)
if test.wantError != "" {
if err == nil {

View File

@ -363,7 +363,7 @@ func (srv *Server) Self() *enode.Node {
srv.lock.Unlock()
if ln == nil {
return enode.NewV4(&srv.PrivateKey.PublicKey, net.ParseIP("0.0.0.0"), 0, 0, 0)
return enode.NewV4(&srv.PrivateKey.PublicKey, net.ParseIP("0.0.0.0"), 0, 0)
}
return ln.Node()
}
@ -1007,7 +1007,7 @@ func nodeFromConn(pubkey *ecdsa.PublicKey, conn net.Conn) *enode.Node {
ip = tcp.IP
port = tcp.Port
}
return enode.NewV4(pubkey, ip, port, port, 0)
return enode.NewV4(pubkey, ip, port, port)
}
func truncateName(s string) string {

View File

@ -152,7 +152,7 @@ func TestServerDial(t *testing.T) {
// tell the server to connect
tcpAddr := listener.Addr().(*net.TCPAddr)
node := enode.NewV4(remid, tcpAddr.IP, tcpAddr.Port, 0, 0)
node := enode.NewV4(remid, tcpAddr.IP, tcpAddr.Port, 0)
srv.AddPeer(node)
select {
@ -422,7 +422,7 @@ func TestServerAtCap(t *testing.T) {
func TestServerPeerLimits(t *testing.T) {
srvkey := newkey()
clientkey := newkey()
clientnode := enode.NewV4(&clientkey.PublicKey, nil, 0, 0, 0)
clientnode := enode.NewV4(&clientkey.PublicKey, nil, 0, 0)
var tp = &setupTransport{
pubkey: &clientkey.PublicKey,
@ -512,21 +512,21 @@ func TestServerSetupConn(t *testing.T) {
},
{
tt: &setupTransport{pubkey: clientpub},
dialDest: enode.NewV4(&newkey().PublicKey, nil, 0, 0, 0),
dialDest: enode.NewV4(&newkey().PublicKey, nil, 0, 0),
flags: dynDialedConn,
wantCalls: "doEncHandshake,close,",
wantCloseErr: DiscUnexpectedIdentity,
},
{
tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: randomID().Bytes()}},
dialDest: enode.NewV4(clientpub, nil, 0, 0, 0),
dialDest: enode.NewV4(clientpub, nil, 0, 0),
flags: dynDialedConn,
wantCalls: "doEncHandshake,doProtoHandshake,close,",
wantCloseErr: DiscUnexpectedIdentity,
},
{
tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: errors.New("foo")},
dialDest: enode.NewV4(clientpub, nil, 0, 0, 0),
dialDest: enode.NewV4(clientpub, nil, 0, 0),
flags: dynDialedConn,
wantCalls: "doEncHandshake,doProtoHandshake,close,",
wantCloseErr: errors.New("foo"),
@ -578,7 +578,7 @@ func TestServerSetupConn_whenNotInRaftCluster(t *testing.T) {
clientpub = &clientkey.PublicKey
)
clientNode := enode.NewV4(clientpub, nil, 0, 0, 0)
clientNode := enode.NewV4(clientpub, nil, 0, 0)
srv := &Server{
Config: Config{
PrivateKey: srvkey,
@ -616,7 +616,7 @@ func TestServerSetupConn_whenNotPermissioned(t *testing.T) {
clientkey, srvkey = newkey(), newkey()
clientpub = &clientkey.PublicKey
)
clientNode := enode.NewV4(clientpub, nil, 0, 0, 0)
clientNode := enode.NewV4(clientpub, nil, 0, 0)
srv := &Server{
Config: Config{
PrivateKey: srvkey,

View File

@ -165,7 +165,7 @@ func (n *NodeConfig) UnmarshalJSON(data []byte) error {
// Node returns the node descriptor represented by the config.
func (n *NodeConfig) Node() *enode.Node {
return enode.NewV4(&n.PrivateKey.PublicKey, net.IP{127, 0, 0, 1}, int(n.Port), int(n.Port), 0)
return enode.NewV4(&n.PrivateKey.PublicKey, net.IP{127, 0, 0, 1}, int(n.Port), int(n.Port))
}
// RandomNodeConfig returns node configuration with a randomly generated ID and

View File

@ -325,16 +325,19 @@ func (pm *ProtocolManager) ProposeNewPeer(enodeId string, isLearner bool) (uint1
if pm.isLearnerNode() {
return 0, errors.New("learner node can't add peer or learner")
}
parsedUrl, _ := url.Parse(enodeId)
node, err := enode.ParseV4(enodeId)
if err != nil {
return 0, err
}
//use the hostname instead of the IP, since if DNS is not enabled, the hostname should *be* the IP
ip := net.ParseIP(parsedUrl.Hostname())
if !pm.useDns && (len(ip.To4()) != 4) {
return 0, fmt.Errorf("expected IPv4 address (with length 4), but got IP of length %v", len(node.IP()))
if !pm.useDns {
// hostname is not allowed if DNS is not enabled
if node.Host() != "" {
return 0, fmt.Errorf("raft must enable dns to use hostname")
}
if len(node.IP()) != 4 {
return 0, fmt.Errorf("expected IPv4 address (with length 4), but got IP of length %v", len(node.IP()))
}
}
if !node.HasRaftPort() {
@ -357,7 +360,7 @@ func (pm *ProtocolManager) ProposeNewPeer(enodeId string, isLearner bool) (uint1
pm.confChangeProposalC <- raftpb.ConfChange{
Type: confChangeType,
NodeID: uint64(raftId),
Context: address.toBytes(pm.useDns),
Context: address.toBytes(),
}
return raftId, nil
@ -768,11 +771,6 @@ func (pm *ProtocolManager) entriesToApply(allEntries []raftpb.Entry) (entriesToA
}
func (pm *ProtocolManager) raftUrl(address *Address) string {
if !pm.useDns {
parsedIp := net.ParseIP(address.Hostname)
return fmt.Sprintf("http://%s:%d", parsedIp.To4(), address.RaftPort)
}
if parsedIp := net.ParseIP(address.Hostname); parsedIp != nil {
if ipv4 := parsedIp.To4(); ipv4 != nil {
//this is an IPv4 address
@ -1001,7 +999,7 @@ func (pm *ProtocolManager) makeInitialRaftPeers() (raftPeers []etcdRaft.Peer, pe
address := newAddress(raftId, node.RaftPort(), node, pm.useDns)
raftPeers[i] = etcdRaft.Peer{
ID: uint64(raftId),
Context: address.toBytes(pm.useDns),
Context: address.toBytes(),
}
if raftId == pm.raftId {

View File

@ -45,7 +45,7 @@ func TestProtocolManager_whenAppliedIndexOutOfSync(t *testing.T) {
for i := 0; i < count; i++ {
ports[i] = nextPort(t)
nodeKeys[i] = mustNewNodeKey(t)
peers[i] = enode.NewV4(&(nodeKeys[i].PublicKey), net.IPv4(127, 0, 0, 1), 0, 0, int(ports[i]))
peers[i] = enode.NewV4Hostname(&(nodeKeys[i].PublicKey), net.IPv4(127, 0, 0, 1).String(), 0, 0, int(ports[i]))
}
raftNodes := make([]*RaftService, count)
for i := 0; i < count; i++ {

View File

@ -33,13 +33,13 @@ type ClusterInfo struct {
NodeActive bool `json:"nodeActive"`
}
func newAddress(raftId uint16, raftPort int, node *enode.Node, withHostname bool) *Address {
func newAddress(raftId uint16, raftPort int, node *enode.Node, useDns bool) *Address {
// derive 64 byte nodeID from 128 byte enodeID
id, err := enode.RaftHexID(node.EnodeID())
if err != nil {
panic(err)
}
if withHostname {
if useDns && node.Host() != "" {
return &Address{
RaftId: raftId,
NodeId: id,
@ -66,13 +66,14 @@ type Peer struct {
}
// RLP Address encoding, for transport over raft and storage in LevelDB.
func (addr *Address) toBytes(withHostname bool) []byte {
func (addr *Address) toBytes() []byte {
var toEncode interface{}
if withHostname {
// need to check if addr.Hostname is hostname/ip
if ip := net.ParseIP(addr.Hostname); ip == nil {
toEncode = addr
} else {
toEncode = []interface{}{addr.RaftId, addr.NodeId, net.ParseIP(addr.Hostname), addr.P2pPort, addr.RaftPort}
toEncode = []interface{}{addr.RaftId, addr.NodeId, ip, addr.P2pPort, addr.RaftPort}
}
buffer, err := rlp.EncodeToBytes(toEncode)
@ -83,14 +84,14 @@ func (addr *Address) toBytes(withHostname bool) []byte {
}
func bytesToAddress(input []byte) *Address {
//try the new format first
// try the new format first
addr := new(Address)
streamNew := rlp.NewStream(bytes.NewReader(input), 0)
if err := streamNew.Decode(addr); err == nil {
return addr
}
//else try the old format
// else try the old format
var temp struct {
RaftId uint16
NodeId enode.EnodeID

View File

@ -97,7 +97,7 @@ func (pm *ProtocolManager) triggerSnapshot(index uint64) {
//snapData := pm.blockchain.CurrentBlock().Hash().Bytes()
//snap, err := pm.raftStorage.CreateSnapshot(pm.appliedIndex, &pm.confState, snapData)
snapData := pm.buildSnapshot().toBytes(pm.useDns)
snapData := pm.buildSnapshot().toBytes()
snap, err := pm.raftStorage.CreateSnapshot(index, &pm.confState, snapData)
if err != nil {
panic(err)
@ -206,32 +206,43 @@ func (pm *ProtocolManager) loadSnapshot() *raftpb.Snapshot {
}
}
func (snapshot *SnapshotWithHostnames) toBytes(useDns bool) []byte {
// we have DNS enabled, so only use the new snapshot type
if useDns {
buffer, err := rlp.EncodeToBytes(snapshot)
if err != nil {
panic(fmt.Sprintf("error: failed to RLP-encode Snapshot: %s", err.Error()))
}
return buffer
}
func (snapshot *SnapshotWithHostnames) toBytes() []byte {
var (
useOldSnapshot bool
oldSnapshot SnapshotWithoutHostnames
toEncode interface{}
)
// DNS is not enabled, use the old snapshot type, converting from hostnames to IP addresses
oldSnapshot := new(SnapshotWithoutHostnames)
// use old snapshot if all snapshot.Addresses are ips
// but use the new snapshot if any of it is a hostname
useOldSnapshot = true
oldSnapshot.HeadBlockHash, oldSnapshot.RemovedRaftIds = snapshot.HeadBlockHash, snapshot.RemovedRaftIds
oldSnapshot.Addresses = make([]AddressWithoutHostname, len(snapshot.Addresses))
for index, addrWithHost := range snapshot.Addresses {
// validate addrWithHost.Hostname is a hostname/ip
ip := net.ParseIP(addrWithHost.Hostname)
if ip == nil {
// this is a hostname
useOldSnapshot = false
break
}
// this is an ip
oldSnapshot.Addresses[index] = AddressWithoutHostname{
addrWithHost.RaftId,
addrWithHost.NodeId,
net.ParseIP(addrWithHost.Hostname),
ip,
addrWithHost.P2pPort,
addrWithHost.RaftPort,
}
}
buffer, err := rlp.EncodeToBytes(oldSnapshot)
if useOldSnapshot {
toEncode = oldSnapshot
} else {
toEncode = snapshot
}
buffer, err := rlp.EncodeToBytes(toEncode)
if err != nil {
panic(fmt.Sprintf("error: failed to RLP-encode Snapshot: %s", err.Error()))
}
@ -247,6 +258,7 @@ func bytesToSnapshot(input []byte) *SnapshotWithHostnames {
return snapshot
}
// Build new snapshot with hostname from legacy Address struct
snapshotOld := new(SnapshotWithoutHostnames)
streamOldSnapshot := rlp.NewStream(bytes.NewReader(input), 0)
if errOld = streamOldSnapshot.Decode(snapshotOld); errOld == nil {

View File

@ -378,7 +378,7 @@ func RandomAddr() *BzzAddr {
if err != nil {
panic("unable to generate key")
}
node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303, 0)
node := enode.NewV4(&key.PublicKey, net.IP{127, 0, 0, 1}, 30303, 30303)
return NewAddr(node)
}

View File

@ -146,7 +146,7 @@ func initialize(t *testing.T) {
peerNodeId := nodes[j].id
address, _ := net.ResolveTCPAddr("tcp", nodes[j].server.ListenAddr)
peerPort := uint16(address.Port)
peer := enode.NewV4(&peerNodeId.PublicKey, address.IP, int(peerPort), int(peerPort), 0)
peer := enode.NewV4(&peerNodeId.PublicKey, address.IP, int(peerPort), int(peerPort))
node.server.AddPeer(peer)
}

View File

@ -223,7 +223,7 @@ func initialize(t *testing.T) {
for j := 0; j < i; j++ {
peerNodeId := nodes[j].id
address, _ := net.ResolveTCPAddr("tcp", nodes[j].server.ListenAddr)
peer := enode.NewV4(&peerNodeId.PublicKey, address.IP, address.Port, address.Port, 0)
peer := enode.NewV4(&peerNodeId.PublicKey, address.IP, address.Port, address.Port)
nodes[i].server.AddPeer(peer)
}
}