Commit fc747ef4 authored by Felix Lange's avatar Felix Lange

p2p/discover: new endpoint format

This commit changes the discovery protocol to use the new "v4" endpoint
format, which allows for separate UDP and TCP ports and makes it
possible to discover the UDP address after NAT.
parent 3fef6019
...@@ -277,8 +277,8 @@ func (s *Ethereum) NodeInfo() *NodeInfo { ...@@ -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(),
} }
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"testing" "testing"
"time" "time"
) )
...@@ -86,9 +87,10 @@ func TestNodeDBInt64(t *testing.T) { ...@@ -86,9 +87,10 @@ func TestNodeDBInt64(t *testing.T) {
func TestNodeDBFetchStore(t *testing.T) { 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) { ...@@ -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)
} }
} }
......
...@@ -6,7 +6,6 @@ import ( ...@@ -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 ( ...@@ -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 {
ID NodeID IP net.IP // len 4 for IPv4 or 16 for IPv6
IP net.IP UDP, TCP uint16 // port numbers
ID NodeID
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{
ID: id, IP: ip,
IP: addr.IP, UDP: uint16(addr.Port),
DiscPort: addr.Port, TCP: uint16(addr.Port),
TCPPort: addr.Port, ID: id,
} }
} }
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) { ...@@ -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 { ...@@ -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
......
...@@ -49,28 +49,28 @@ var parseNodeTests = []struct { ...@@ -49,28 +49,28 @@ var parseNodeTests = []struct {
{ {
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150", rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
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,
}, },
}, },
{ {
rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150",
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,
}, },
}, },
} }
......
...@@ -220,7 +220,7 @@ func (tab *Table) bondall(nodes []*Node) (result []*Node) { ...@@ -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 ...@@ -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)
} }
......
...@@ -261,9 +261,9 @@ func (t findnodeOracle) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID ...@@ -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
......
...@@ -49,16 +49,20 @@ const ( ...@@ -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 ( ...@@ -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 {
...@@ -94,8 +111,9 @@ type conn interface { ...@@ -94,8 +111,9 @@ type conn interface {
// udp implements the RPC protocol. // udp implements the RPC protocol.
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 ...@@ -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 { ...@@ -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 ...@@ -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() { ...@@ -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])
if err != nil { }
glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err) }
continue
} func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error {
status := "ok" packet, fromID, hash, err := decodePacket(buf)
if err := packet.handle(t, from, fromID, hash); err != nil { if err != nil {
status = err.Error() glog.V(logger.Debug).Infof("Bad packet from %v: %v\n", from, err)
} return err
glog.V(logger.Detail).Infof("<<< %v %T: %s\n", from, packet, status)
} }
status := "ok"
if err = packet.handle(t, from, fromID, hash); err != nil {
status = err.Error()
}
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 ...@@ -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
} }
......
...@@ -23,6 +23,15 @@ func init() { ...@@ -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 { ...@@ -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 { ...@@ -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})
...@@ -147,10 +149,10 @@ func TestUDP_findnode(t *testing.T) { ...@@ -147,10 +149,10 @@ func TestUDP_findnode(t *testing.T) {
nodes := &nodesByDistance{target: target} nodes := &nodesByDistance{target: target}
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)
} }
test.table.add(nodes.entries) test.table.add(nodes.entries)
...@@ -158,10 +160,10 @@ func TestUDP_findnode(t *testing.T) { ...@@ -158,10 +160,10 @@ func TestUDP_findnode(t *testing.T) {
// ensure there's a bond with the test node, // ensure there's a bond with the test node,
// findnode won't be accepted otherwise. // findnode won't be accepted otherwise.
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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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 { ...@@ -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 {
......
...@@ -119,14 +119,14 @@ func TestSetupConn(t *testing.T) { ...@@ -119,14 +119,14 @@ func TestSetupConn(t *testing.T) {
prv0, _ := crypto.GenerateKey() prv0, _ := crypto.GenerateKey()
prv1, _ := crypto.GenerateKey() prv1, _ := crypto.GenerateKey()
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,
......
...@@ -394,7 +394,7 @@ func (srv *Server) dialLoop() { ...@@ -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 {
......
...@@ -102,7 +102,7 @@ func TestServerDial(t *testing.T) { ...@@ -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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment