This commit is contained in:
Felix Lange 2015-05-06 20:21:41 +00:00
commit f99c960439
12 changed files with 162 additions and 131 deletions

View file

@ -277,8 +277,8 @@ func (s *Ethereum) NodeInfo() *NodeInfo {
NodeUrl: node.String(), NodeUrl: node.String(),
NodeID: node.ID.String(), NodeID: node.ID.String(),
IP: node.IP.String(), IP: node.IP.String(),
DiscPort: node.DiscPort, DiscPort: int(node.UDP),
TCPPort: node.TCPPort, TCPPort: int(node.TCP),
ListenAddr: s.net.ListenAddr, ListenAddr: s.net.ListenAddr,
Td: s.ChainManager().Td().String(), Td: s.ChainManager().Td().String(),
} }

View file

@ -6,6 +6,7 @@ import (
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"testing" "testing"
"time" "time"
) )
@ -88,7 +89,8 @@ func TestNodeDBFetchStore(t *testing.T) {
node := &Node{ node := &Node{
ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
IP: net.IP([]byte{192, 168, 0, 1}), IP: net.IP([]byte{192, 168, 0, 1}),
TCPPort: 30303, UDP: 30303,
TCP: 30303,
} }
inst := time.Now() inst := time.Now()
@ -124,7 +126,7 @@ func TestNodeDBFetchStore(t *testing.T) {
} }
if stored := db.node(node.ID); stored == nil { if stored := db.node(node.ID); stored == nil {
t.Errorf("node: not found") t.Errorf("node: not found")
} else if !bytes.Equal(stored.ID[:], node.ID[:]) || !stored.IP.Equal(node.IP) || stored.TCPPort != node.TCPPort { } else if !reflect.DeepEqual(stored, node) {
t.Errorf("node: data mismatch: have %v, want %v", stored, node) t.Errorf("node: data mismatch: have %v, want %v", stored, node)
} }
} }

View file

@ -6,7 +6,6 @@ import (
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"io"
"math/big" "math/big"
"math/rand" "math/rand"
"net" "net"
@ -16,49 +15,45 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/rlp"
) )
const nodeIDBits = 512 const nodeIDBits = 512
// Node represents a host on the network. // Node represents a host on the network.
type Node struct { type Node struct {
IP net.IP // len 4 for IPv4 or 16 for IPv6
UDP, TCP uint16 // port numbers
ID NodeID ID NodeID
IP net.IP
DiscPort int // UDP listening port for discovery protocol
TCPPort int // TCP listening port for RLPx
} }
func newNode(id NodeID, addr *net.UDPAddr) *Node { func newNode(id NodeID, addr *net.UDPAddr) *Node {
ip := addr.IP.To4()
if ip == nil {
ip = addr.IP.To16()
}
return &Node{ return &Node{
IP: ip,
UDP: uint16(addr.Port),
TCP: uint16(addr.Port),
ID: id, ID: id,
IP: addr.IP,
DiscPort: addr.Port,
TCPPort: addr.Port,
} }
} }
func (n *Node) isValid() bool {
// TODO: don't accept localhost, LAN addresses from internet hosts
return !n.IP.IsMulticast() && !n.IP.IsUnspecified() && n.TCPPort != 0 && n.DiscPort != 0
}
func (n *Node) addr() *net.UDPAddr { func (n *Node) addr() *net.UDPAddr {
return &net.UDPAddr{IP: n.IP, Port: n.DiscPort} return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)}
} }
// The string representation of a Node is a URL. // The string representation of a Node is a URL.
// Please see ParseNode for a description of the format. // Please see ParseNode for a description of the format.
func (n *Node) String() string { func (n *Node) String() string {
addr := net.TCPAddr{IP: n.IP, Port: n.TCPPort} addr := net.TCPAddr{IP: n.IP, Port: int(n.TCP)}
u := url.URL{ u := url.URL{
Scheme: "enode", Scheme: "enode",
User: url.User(fmt.Sprintf("%x", n.ID[:])), User: url.User(fmt.Sprintf("%x", n.ID[:])),
Host: addr.String(), Host: addr.String(),
} }
if n.DiscPort != n.TCPPort { if n.UDP != n.TCP {
u.RawQuery = "discport=" + strconv.Itoa(n.DiscPort) u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP))
} }
return u.String() return u.String()
} }
@ -98,16 +93,20 @@ func ParseNode(rawurl string) (*Node, error) {
if n.IP = net.ParseIP(ip); n.IP == nil { if n.IP = net.ParseIP(ip); n.IP == nil {
return nil, errors.New("invalid IP address") return nil, errors.New("invalid IP address")
} }
if n.TCPPort, err = strconv.Atoi(port); err != nil { tcp, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return nil, errors.New("invalid port") return nil, errors.New("invalid port")
} }
n.TCP = uint16(tcp)
qv := u.Query() qv := u.Query()
if qv.Get("discport") == "" { if qv.Get("discport") == "" {
n.DiscPort = n.TCPPort n.UDP = n.TCP
} else { } else {
if n.DiscPort, err = strconv.Atoi(qv.Get("discport")); err != nil { udp, err := strconv.ParseUint(qv.Get("discport"), 10, 16)
if err != nil {
return nil, errors.New("invalid discport in query") return nil, errors.New("invalid discport in query")
} }
n.UDP = uint16(udp)
} }
return &n, nil return &n, nil
} }
@ -121,22 +120,6 @@ func MustParseNode(rawurl string) *Node {
return n return n
} }
func (n Node) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, rpcNode{IP: n.IP.String(), Port: uint16(n.TCPPort), ID: n.ID})
}
func (n *Node) DecodeRLP(s *rlp.Stream) (err error) {
var ext rpcNode
if err = s.Decode(&ext); err == nil {
n.TCPPort = int(ext.Port)
n.DiscPort = int(ext.Port)
n.ID = ext.ID
if n.IP = net.ParseIP(ext.IP); n.IP == nil {
return errors.New("invalid IP string")
}
}
return err
}
// NodeID is a unique identifier for each node. // NodeID is a unique identifier for each node.
// The node identifier is a marshaled elliptic curve public key. // The node identifier is a marshaled elliptic curve public key.
type NodeID [nodeIDBits / 8]byte type NodeID [nodeIDBits / 8]byte

View file

@ -51,8 +51,8 @@ var parseNodeTests = []struct {
wantResult: &Node{ wantResult: &Node{
ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
IP: net.ParseIP("127.0.0.1"), IP: net.ParseIP("127.0.0.1"),
DiscPort: 52150, UDP: 52150,
TCPPort: 52150, TCP: 52150,
}, },
}, },
{ {
@ -60,17 +60,17 @@ var parseNodeTests = []struct {
wantResult: &Node{ wantResult: &Node{
ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
IP: net.ParseIP("::"), IP: net.ParseIP("::"),
DiscPort: 52150, UDP: 52150,
TCPPort: 52150, TCP: 52150,
}, },
}, },
{ {
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=223344", rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334",
wantResult: &Node{ wantResult: &Node{
ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), ID: MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
IP: net.ParseIP("127.0.0.1"), IP: net.ParseIP("127.0.0.1"),
DiscPort: 223344, UDP: 22334,
TCPPort: 52150, TCP: 52150,
}, },
}, },
} }

View file

@ -220,7 +220,7 @@ func (tab *Table) bondall(nodes []*Node) (result []*Node) {
rc := make(chan *Node, len(nodes)) rc := make(chan *Node, len(nodes))
for i := range nodes { for i := range nodes {
go func(n *Node) { go func(n *Node) {
nn, _ := tab.bond(false, n.ID, n.addr(), uint16(n.TCPPort)) nn, _ := tab.bond(false, n.ID, n.addr(), uint16(n.TCP))
rc <- nn rc <- nn
}(nodes[i]) }(nodes[i])
} }
@ -299,12 +299,7 @@ func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAdd
tab.net.waitping(id) tab.net.waitping(id)
} }
// Bonding succeeded, update the node database // Bonding succeeded, update the node database
w.n = &Node{ w.n = &Node{ID: id, IP: addr.IP, UDP: uint16(addr.Port), TCP: tcpPort}
ID: id,
IP: addr.IP,
DiscPort: addr.Port,
TCPPort: int(tcpPort),
}
tab.db.updateNode(w.n) tab.db.updateNode(w.n)
close(w.done) close(w.done)
} }

View file

@ -261,9 +261,9 @@ func (t findnodeOracle) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID
panic("query to node at distance 0") panic("query to node at distance 0")
default: default:
// TODO: add more randomness to distances // TODO: add more randomness to distances
next := toaddr.Port - 1 next := uint16(toaddr.Port) - 1
for i := 0; i < bucketSize; i++ { for i := 0; i < bucketSize; i++ {
result = append(result, &Node{ID: randomID(t.target, next), DiscPort: next}) result = append(result, &Node{ID: randomID(t.target, int(next)), UDP: next})
} }
} }
return result, nil return result, nil

View file

@ -15,7 +15,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
const Version = 3 const Version = 4
// Errors // Errors
var ( var (
@ -49,16 +49,20 @@ const (
// RPC request structures // RPC request structures
type ( type (
ping struct { ping struct {
Version uint // must match Version Version uint
IP string // our IP From, To rpcEndpoint
Port uint16 // our port
Expiration uint64 Expiration uint64
} }
// reply to Ping // pong is the reply to ping.
pong struct { pong struct {
ReplyTok []byte // This field should mirror the UDP envelope address
Expiration uint64 // of the ping packet, which provides a way to discover the
// the external address (after NAT).
To rpcEndpoint
ReplyTok []byte // This contains the hash of the ping packet.
Expiration uint64 // Absolute timestamp at which the packet becomes invalid.
} }
findnode struct { findnode struct {
@ -73,12 +77,25 @@ type (
Nodes []*Node Nodes []*Node
Expiration uint64 Expiration uint64
} }
rpcEndpoint struct {
IP net.IP // len 4 for IPv4 or 16 for IPv6
UDP uint16 // for discovery protocol
TCP uint16 // for RLPx protocol
}
) )
type rpcNode struct { func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint {
IP string ip := addr.IP.To4()
Port uint16 if ip == nil {
ID NodeID ip = addr.IP.To16()
}
return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
}
func validNode(n *Node) bool {
// TODO: don't accept localhost, LAN addresses from internet hosts
return !n.IP.IsMulticast() && !n.IP.IsUnspecified() && n.UDP != 0
} }
type packet interface { type packet interface {
@ -96,6 +113,7 @@ type conn interface {
type udp struct { type udp struct {
conn conn conn conn
priv *ecdsa.PrivateKey priv *ecdsa.PrivateKey
ourEndpoint rpcEndpoint
addpending chan *pending addpending chan *pending
gotreply chan reply gotreply chan reply
@ -176,6 +194,8 @@ func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath strin
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port} realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
} }
} }
// TODO: separate TCP port
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
udp.Table = newTable(udp, PubkeyID(&priv.PublicKey), realaddr, nodeDBPath) udp.Table = newTable(udp, PubkeyID(&priv.PublicKey), realaddr, nodeDBPath)
go udp.loop() go udp.loop()
go udp.readLoop() go udp.readLoop()
@ -194,8 +214,8 @@ func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error {
errc := t.pending(toid, pongPacket, func(interface{}) bool { return true }) errc := t.pending(toid, pongPacket, func(interface{}) bool { return true })
t.send(toaddr, pingPacket, ping{ t.send(toaddr, pingPacket, ping{
Version: Version, Version: Version,
IP: t.self.IP.String(), From: t.ourEndpoint,
Port: uint16(t.self.TCPPort), To: makeEndpoint(toaddr, 0), // TODO: maybe use known TCP port from DB
Expiration: uint64(time.Now().Add(expiration).Unix()), Expiration: uint64(time.Now().Add(expiration).Unix()),
}) })
return <-errc return <-errc
@ -214,7 +234,7 @@ func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node
reply := r.(*neighbors) reply := r.(*neighbors)
for _, n := range reply.Nodes { for _, n := range reply.Nodes {
nreceived++ nreceived++
if n.isValid() { if validNode(n) {
nodes = append(nodes, n) nodes = append(nodes, n)
} }
} }
@ -374,17 +394,22 @@ func (t *udp) readLoop() {
if err != nil { if err != nil {
return return
} }
packet, fromID, hash, err := decodePacket(buf[:nbytes]) t.handlePacket(from, buf[:nbytes])
}
}
func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error {
packet, fromID, hash, err := decodePacket(buf)
if err != nil { if err != nil {
glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err) glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err)
continue return err
} }
status := "ok" status := "ok"
if err := packet.handle(t, from, fromID, hash); err != nil { if err = packet.handle(t, from, fromID, hash); err != nil {
status = err.Error() status = err.Error()
} }
glog.V(logger.Detail).Infof("<<< %v %T: %s\n", from, packet, status) glog.V(logger.Detail).Infof("<<< %v %T: %s\n", from, packet, status)
} return err
} }
func decodePacket(buf []byte) (packet, NodeID, []byte, error) { func decodePacket(buf []byte) (packet, NodeID, []byte, error) {
@ -425,12 +450,13 @@ func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) er
return errBadVersion return errBadVersion
} }
t.send(from, pongPacket, pong{ t.send(from, pongPacket, pong{
To: makeEndpoint(from, req.From.TCP),
ReplyTok: mac, ReplyTok: mac,
Expiration: uint64(time.Now().Add(expiration).Unix()), Expiration: uint64(time.Now().Add(expiration).Unix()),
}) })
if !t.handleReply(fromID, pingPacket, req) { if !t.handleReply(fromID, pingPacket, req) {
// Note: we're ignoring the provided IP address right now // Note: we're ignoring the provided IP address right now
go t.bond(true, fromID, from, req.Port) go t.bond(true, fromID, from, req.From.TCP)
} }
return nil return nil
} }

View file

@ -23,6 +23,15 @@ func init() {
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel)) logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel))
} }
// shared test variables
var (
futureExp = uint64(time.Now().Add(10 * time.Hour).Unix())
testTarget = MustHexID("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101")
testRemote = rpcEndpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2}
testLocalAnnounced = rpcEndpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4}
testLocal = rpcEndpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6}
)
type udpTest struct { type udpTest struct {
t *testing.T t *testing.T
pipe *dgramPipe pipe *dgramPipe
@ -52,8 +61,7 @@ func (test *udpTest) packetIn(wantError error, ptype byte, data packet) error {
return test.errorf("packet (%d) encode error: %v", err) return test.errorf("packet (%d) encode error: %v", err)
} }
test.sent = append(test.sent, enc) test.sent = append(test.sent, enc)
err = data.handle(test.udp, test.remoteaddr, PubkeyID(&test.remotekey.PublicKey), enc[:macSize]) if err = test.udp.handlePacket(test.remoteaddr, enc); err != wantError {
if err != wantError {
return test.errorf("error mismatch: got %q, want %q", err, wantError) return test.errorf("error mismatch: got %q, want %q", err, wantError)
} }
return nil return nil
@ -90,18 +98,12 @@ func (test *udpTest) errorf(format string, args ...interface{}) error {
return err return err
} }
// shared test variables
var (
futureExp = uint64(time.Now().Add(10 * time.Hour).Unix())
testTarget = MustHexID("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101")
)
func TestUDP_packetErrors(t *testing.T) { func TestUDP_packetErrors(t *testing.T) {
test := newUDPTest(t) test := newUDPTest(t)
defer test.table.Close() defer test.table.Close()
test.packetIn(errExpired, pingPacket, &ping{IP: "foo", Port: 99, Version: Version}) test.packetIn(errExpired, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version})
test.packetIn(errBadVersion, pingPacket, &ping{IP: "foo", Port: 99, Version: 99, Expiration: futureExp}) test.packetIn(errBadVersion, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: 99, Expiration: futureExp})
test.packetIn(errUnsolicitedReply, pongPacket, &pong{ReplyTok: []byte{}, Expiration: futureExp}) test.packetIn(errUnsolicitedReply, pongPacket, &pong{ReplyTok: []byte{}, Expiration: futureExp})
test.packetIn(errUnknownNode, findnodePacket, &findnode{Expiration: futureExp}) test.packetIn(errUnknownNode, findnodePacket, &findnode{Expiration: futureExp})
test.packetIn(errUnsolicitedReply, neighborsPacket, &neighbors{Expiration: futureExp}) test.packetIn(errUnsolicitedReply, neighborsPacket, &neighbors{Expiration: futureExp})
@ -148,8 +150,8 @@ func TestUDP_findnode(t *testing.T) {
for i := 0; i < bucketSize; i++ { for i := 0; i < bucketSize; i++ {
nodes.push(&Node{ nodes.push(&Node{
IP: net.IP{1, 2, 3, byte(i)}, IP: net.IP{1, 2, 3, byte(i)},
DiscPort: i + 2, UDP: uint16(i + 2),
TCPPort: i + 2, TCP: uint16(i + 3),
ID: randomID(test.table.self.ID, i+2), ID: randomID(test.table.self.ID, i+2),
}, bucketSize) }, bucketSize)
} }
@ -160,8 +162,8 @@ func TestUDP_findnode(t *testing.T) {
test.table.db.updateNode(&Node{ test.table.db.updateNode(&Node{
ID: PubkeyID(&test.remotekey.PublicKey), ID: PubkeyID(&test.remotekey.PublicKey),
IP: test.remoteaddr.IP, IP: test.remoteaddr.IP,
DiscPort: test.remoteaddr.Port, UDP: uint16(test.remoteaddr.Port),
TCPPort: 99, TCP: 99,
}) })
// check that closest neighbors are returned. // check that closest neighbors are returned.
test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp}) test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp})
@ -204,9 +206,9 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
// send the reply as two packets. // send the reply as two packets.
list := []*Node{ list := []*Node{
MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303"), MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"),
MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"), MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"),
MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301"), MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"),
MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"), MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"),
} }
test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: list[:2]}) test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: list[:2]})
@ -231,7 +233,8 @@ func TestUDP_successfulPing(t *testing.T) {
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
test.packetIn(nil, pingPacket, &ping{IP: "foo", Port: 99, Version: Version, Expiration: futureExp}) // The remote side sends a ping packet to initiate the exchange.
test.packetIn(nil, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version, Expiration: futureExp})
close(done) close(done)
}() }()
@ -239,12 +242,34 @@ func TestUDP_successfulPing(t *testing.T) {
test.waitPacketOut(func(p *pong) { test.waitPacketOut(func(p *pong) {
pinghash := test.sent[0][:macSize] pinghash := test.sent[0][:macSize]
if !bytes.Equal(p.ReplyTok, pinghash) { if !bytes.Equal(p.ReplyTok, pinghash) {
t.Errorf("got ReplyTok %x, want %x", p.ReplyTok, pinghash) t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash)
}
wantTo := rpcEndpoint{
// The mirrored UDP address is the UDP packet sender
IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port),
// The mirrored TCP port is the one from the ping packet
TCP: testRemote.TCP,
}
if !reflect.DeepEqual(p.To, wantTo) {
t.Errorf("got pong.To %v, want %v", p.To, wantTo)
} }
}) })
// remote is unknown, the table pings back. // remote is unknown, the table pings back.
test.waitPacketOut(func(p *ping) error { return nil }) test.waitPacketOut(func(p *ping) error {
if !reflect.DeepEqual(p.From, test.udp.ourEndpoint) {
t.Errorf("got ping.From %v, want %v", p.From, test.udp.ourEndpoint)
}
wantTo := rpcEndpoint{
// The mirrored UDP address is the UDP packet sender.
IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port),
TCP: 0,
}
if !reflect.DeepEqual(p.To, wantTo) {
t.Errorf("got ping.To %v, want %v", p.To, wantTo)
}
return nil
})
test.packetIn(nil, pongPacket, &pong{Expiration: futureExp}) test.packetIn(nil, pongPacket, &pong{Expiration: futureExp})
// ping should return shortly after getting the pong packet. // ping should return shortly after getting the pong packet.
@ -259,11 +284,11 @@ func TestUDP_successfulPing(t *testing.T) {
if !bytes.Equal(rnode.IP, test.remoteaddr.IP) { if !bytes.Equal(rnode.IP, test.remoteaddr.IP) {
t.Errorf("node has wrong IP: got %v, want: %v", rnode.IP, test.remoteaddr.IP) t.Errorf("node has wrong IP: got %v, want: %v", rnode.IP, test.remoteaddr.IP)
} }
if rnode.DiscPort != test.remoteaddr.Port { if int(rnode.UDP) != test.remoteaddr.Port {
t.Errorf("node has wrong Port: got %v, want: %v", rnode.DiscPort, test.remoteaddr.Port) t.Errorf("node has wrong UDP port: got %v, want: %v", rnode.UDP, test.remoteaddr.Port)
} }
if rnode.TCPPort != 99 { if rnode.TCP != testRemote.TCP {
t.Errorf("node has wrong Port: got %v, want: %v", rnode.TCPPort, 99) t.Errorf("node has wrong TCP port: got %v, want: %v", rnode.TCP, testRemote.TCP)
} }
} }
@ -327,7 +352,7 @@ func (c *dgramPipe) Close() error {
} }
func (c *dgramPipe) LocalAddr() net.Addr { func (c *dgramPipe) LocalAddr() net.Addr {
return &net.UDPAddr{} return &net.UDPAddr{IP: testLocal.IP, Port: int(testLocal.UDP)}
} }
func (c *dgramPipe) waitPacketOut() []byte { func (c *dgramPipe) waitPacketOut() []byte {

View file

@ -121,12 +121,12 @@ func TestSetupConn(t *testing.T) {
node0 := &discover.Node{ node0 := &discover.Node{
ID: discover.PubkeyID(&prv0.PublicKey), ID: discover.PubkeyID(&prv0.PublicKey),
IP: net.IP{1, 2, 3, 4}, IP: net.IP{1, 2, 3, 4},
TCPPort: 33, TCP: 33,
} }
node1 := &discover.Node{ node1 := &discover.Node{
ID: discover.PubkeyID(&prv1.PublicKey), ID: discover.PubkeyID(&prv1.PublicKey),
IP: net.IP{5, 6, 7, 8}, IP: net.IP{5, 6, 7, 8},
TCPPort: 44, TCP: 44,
} }
hs0 := &protoHandshake{ hs0 := &protoHandshake{
Version: baseProtocolVersion, Version: baseProtocolVersion,

View file

@ -15,7 +15,7 @@ import (
) )
const ( const (
baseProtocolVersion = 3 baseProtocolVersion = 4
baseProtocolLength = uint64(16) baseProtocolLength = uint64(16)
baseProtocolMaxMsgSize = 10 * 1024 * 1024 baseProtocolMaxMsgSize = 10 * 1024 * 1024

View file

@ -394,7 +394,7 @@ func (srv *Server) dialLoop() {
} }
func (srv *Server) dialNode(dest *discover.Node) { func (srv *Server) dialNode(dest *discover.Node) {
addr := &net.TCPAddr{IP: dest.IP, Port: dest.TCPPort} addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)}
glog.V(logger.Debug).Infof("Dialing %v\n", dest) glog.V(logger.Debug).Infof("Dialing %v\n", dest)
conn, err := srv.Dialer.Dial("tcp", addr.String()) conn, err := srv.Dialer.Dial("tcp", addr.String())
if err != nil { if err != nil {

View file

@ -102,7 +102,7 @@ func TestServerDial(t *testing.T) {
// tell the server to connect // tell the server to connect
tcpAddr := listener.Addr().(*net.TCPAddr) tcpAddr := listener.Addr().(*net.TCPAddr)
srv.SuggestPeer(&discover.Node{IP: tcpAddr.IP, TCPPort: tcpAddr.Port}) srv.SuggestPeer(&discover.Node{IP: tcpAddr.IP, TCP: uint16(tcpAddr.Port)})
select { select {
case conn := <-accepted: case conn := <-accepted: