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.
This commit is contained in:
Felix Lange
2015-04-18 01:50:31 +02:00
parent 3fef601903
commit fc747ef4a6
11 changed files with 160 additions and 129 deletions

View File

@ -6,7 +6,6 @@ import (
"encoding/hex"
"errors"
"fmt"
"io"
"math/big"
"math/rand"
"net"
@ -16,49 +15,45 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/rlp"
)
const nodeIDBits = 512
// Node represents a host on the network.
type Node struct {
ID NodeID
IP net.IP
DiscPort int // UDP listening port for discovery protocol
TCPPort int // TCP listening port for RLPx
IP net.IP // len 4 for IPv4 or 16 for IPv6
UDP, TCP uint16 // port numbers
ID NodeID
}
func newNode(id NodeID, addr *net.UDPAddr) *Node {
ip := addr.IP.To4()
if ip == nil {
ip = addr.IP.To16()
}
return &Node{
ID: id,
IP: addr.IP,
DiscPort: addr.Port,
TCPPort: addr.Port,
IP: ip,
UDP: uint16(addr.Port),
TCP: uint16(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 {
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.
// Please see ParseNode for a description of the format.
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{
Scheme: "enode",
User: url.User(fmt.Sprintf("%x", n.ID[:])),
Host: addr.String(),
}
if n.DiscPort != n.TCPPort {
u.RawQuery = "discport=" + strconv.Itoa(n.DiscPort)
if n.UDP != n.TCP {
u.RawQuery = "discport=" + strconv.Itoa(int(n.UDP))
}
return u.String()
}
@ -98,16 +93,20 @@ func ParseNode(rawurl string) (*Node, error) {
if n.IP = net.ParseIP(ip); n.IP == nil {
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")
}
n.TCP = uint16(tcp)
qv := u.Query()
if qv.Get("discport") == "" {
n.DiscPort = n.TCPPort
n.UDP = n.TCP
} 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")
}
n.UDP = uint16(udp)
}
return &n, nil
}
@ -121,22 +120,6 @@ func MustParseNode(rawurl string) *Node {
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.
// The node identifier is a marshaled elliptic curve public key.
type NodeID [nodeIDBits / 8]byte