github.com/xfers/quorum@v21.1.0+incompatible/p2p/enode/urlv4.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package enode
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"encoding/hex"
    22  	"errors"
    23  	"fmt"
    24  	"net"
    25  	"net/url"
    26  	"regexp"
    27  	"strconv"
    28  
    29  	"github.com/ethereum/go-ethereum/common/math"
    30  	"github.com/ethereum/go-ethereum/crypto"
    31  	"github.com/ethereum/go-ethereum/p2p/enr"
    32  )
    33  
    34  var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$")
    35  
    36  // MustParseV4 parses a node URL. It panics if the URL is not valid.
    37  func MustParseV4(rawurl string) *Node {
    38  	n, err := ParseV4(rawurl)
    39  	if err != nil {
    40  		panic("invalid node URL: " + err.Error())
    41  	}
    42  	return n
    43  }
    44  
    45  // ParseV4 parses a node URL.
    46  //
    47  // There are two basic forms of node URLs:
    48  //
    49  //   - incomplete nodes, which only have the public key (node ID)
    50  //   - complete nodes, which contain the public key and IP/Port information
    51  //
    52  // For incomplete nodes, the designator must look like one of these
    53  //
    54  //    enode://<hex node id>
    55  //    <hex node id>
    56  //
    57  // For complete nodes, the node ID is encoded in the username portion
    58  // of the URL, separated from the host by an @ sign. The hostname can
    59  // be given as an IP address or a DNS domain name.
    60  // The port in the host name section is the TCP listening port. If the
    61  // TCP and UDP (discovery) ports differ, the UDP port is specified as
    62  // query parameter "discport".
    63  //
    64  // In the following example, the node URL describes
    65  // a node with IP address 10.3.58.6, TCP listening port 30303
    66  // and UDP discovery port 30301.
    67  //
    68  //    enode://<hex node id>@10.3.58.6:30303?discport=30301
    69  func ParseV4(rawurl string) (*Node, error) {
    70  	if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
    71  		id, err := parsePubkey(m[1])
    72  		if err != nil {
    73  			return nil, fmt.Errorf("invalid public key (%v)", err)
    74  		}
    75  		return NewV4(id, nil, 0, 0), nil
    76  	}
    77  	return parseComplete(rawurl)
    78  }
    79  
    80  // NewV4 creates a node from discovery v4 node information. The record
    81  // contained in the node has a zero-length signature.
    82  func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node {
    83  	var r enr.Record
    84  	if len(ip) > 0 {
    85  		r.Set(enr.IP(ip))
    86  	}
    87  	return newV4(pubkey, r, tcp, udp)
    88  }
    89  
    90  // broken out from `func NewV4` (above) same in upstream go-ethereum, but taken out
    91  // to avoid code duplication b/t NewV4 and NewV4Hostname
    92  func newV4(pubkey *ecdsa.PublicKey, r enr.Record, tcp, udp int) *Node {
    93  	if udp != 0 {
    94  		r.Set(enr.UDP(udp))
    95  	}
    96  	if tcp != 0 {
    97  		r.Set(enr.TCP(tcp))
    98  	}
    99  	signV4Compat(&r, pubkey)
   100  	n, err := New(v4CompatID{}, &r)
   101  	if err != nil {
   102  		panic(err)
   103  	}
   104  	return n
   105  }
   106  
   107  // isNewV4 returns true for nodes created by NewV4.
   108  func isNewV4(n *Node) bool {
   109  	var k s256raw
   110  	return n.r.IdentityScheme() == "" && n.r.Load(&k) == nil && len(n.r.Signature()) == 0
   111  }
   112  
   113  // Quorum
   114  
   115  // NewV4Hostname creates a node from discovery v4 node information. The record
   116  // contained in the node has a zero-length signature. It sets the hostname or ip
   117  // of the node depends on hostname context
   118  func NewV4Hostname(pubkey *ecdsa.PublicKey, hostname string, tcp, udp, raftPort int) *Node {
   119  	var r enr.Record
   120  
   121  	if ip := net.ParseIP(hostname); ip == nil {
   122  		r.Set(enr.Hostname(hostname))
   123  	} else {
   124  		r.Set(enr.IP(ip))
   125  	}
   126  
   127  	if raftPort != 0 {
   128  		r.Set(enr.RaftPort(raftPort))
   129  	}
   130  
   131  	return newV4(pubkey, r, tcp, udp)
   132  }
   133  
   134  // End-Quorum
   135  
   136  func parseComplete(rawurl string) (*Node, error) {
   137  	var (
   138  		id               *ecdsa.PublicKey
   139  		ip               net.IP
   140  		tcpPort, udpPort uint64
   141  	)
   142  	u, err := url.Parse(rawurl)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	if u.Scheme != "enode" {
   147  		return nil, errors.New("invalid URL scheme, want \"enode\"")
   148  	}
   149  	// Parse the Node ID from the user portion.
   150  	if u.User == nil {
   151  		return nil, errors.New("does not contain node ID")
   152  	}
   153  	if id, err = parsePubkey(u.User.String()); err != nil {
   154  		return nil, fmt.Errorf("invalid public key (%v)", err)
   155  	}
   156  	// move qv up to here
   157  	qv := u.Query()
   158  	// Parse the IP address.
   159  	ips, err := net.LookupIP(u.Hostname())
   160  	if err != nil {
   161  		// Quorum: if IP look up fail don't return error for raft url
   162  		if qv.Get("raftport") == "" {
   163  			return nil, err
   164  		}
   165  	} else {
   166  		ip = ips[0]
   167  		// Ensure the IP is 4 bytes long for IPv4 addresses.
   168  		if ipv4 := ip.To4(); ipv4 != nil {
   169  			ip = ipv4
   170  		}
   171  	}
   172  	// Parse the port numbers.
   173  	if tcpPort, err = strconv.ParseUint(u.Port(), 10, 16); err != nil {
   174  		return nil, errors.New("invalid port")
   175  	}
   176  	udpPort = tcpPort
   177  	if qv.Get("discport") != "" {
   178  		udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
   179  		if err != nil {
   180  			return nil, errors.New("invalid discport in query")
   181  		}
   182  	}
   183  
   184  	// Quorum
   185  	if qv.Get("raftport") != "" {
   186  		raftPort, err := strconv.ParseUint(qv.Get("raftport"), 10, 16)
   187  		if err != nil {
   188  			return nil, errors.New("invalid raftport in query")
   189  		}
   190  		if u.Hostname() == "" {
   191  			return nil, errors.New("empty hostname in raft url")
   192  		}
   193  		return NewV4Hostname(id, u.Hostname(), int(tcpPort), int(udpPort), int(raftPort)), nil
   194  	}
   195  	// End-Quorum
   196  
   197  	return NewV4(id, ip, int(tcpPort), int(udpPort)), nil
   198  }
   199  
   200  func HexPubkey(h string) (*ecdsa.PublicKey, error) {
   201  	k, err := parsePubkey(h)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	return k, err
   206  }
   207  
   208  // parsePubkey parses a hex-encoded secp256k1 public key.
   209  func parsePubkey(in string) (*ecdsa.PublicKey, error) {
   210  	b, err := hex.DecodeString(in)
   211  	if err != nil {
   212  		return nil, err
   213  	} else if len(b) != 64 {
   214  		return nil, fmt.Errorf("wrong length, want %d hex chars", 128)
   215  	}
   216  	b = append([]byte{0x4}, b...)
   217  	return crypto.UnmarshalPubkey(b)
   218  }
   219  
   220  // used by Quorum RAFT - to derive enodeID
   221  func (n *Node) EnodeID() string {
   222  	var (
   223  		scheme enr.ID
   224  		nodeid string
   225  		key    ecdsa.PublicKey
   226  	)
   227  	n.Load(&scheme)
   228  	n.Load((*Secp256k1)(&key))
   229  	switch {
   230  	case scheme == "v4" || key != ecdsa.PublicKey{}:
   231  		nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:])
   232  	default:
   233  		nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:])
   234  	}
   235  	return nodeid
   236  }
   237  
   238  func (n *Node) URLv4() string {
   239  	var (
   240  		scheme enr.ID
   241  		nodeid string
   242  		key    ecdsa.PublicKey
   243  	)
   244  	n.Load(&scheme)
   245  	n.Load((*Secp256k1)(&key))
   246  	switch {
   247  	case scheme == "v4" || key != ecdsa.PublicKey{}:
   248  		nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:])
   249  	default:
   250  		nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:])
   251  	}
   252  	u := url.URL{Scheme: "enode"}
   253  	if n.Incomplete() {
   254  		u.Host = nodeid
   255  	} else {
   256  		u.User = url.User(nodeid)
   257  		if n.Host() != "" && net.ParseIP(n.Host()) == nil {
   258  			// Quorum
   259  			u.Host = net.JoinHostPort(n.Host(), strconv.Itoa(n.TCP()))
   260  		} else {
   261  			addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()}
   262  			u.Host = addr.String()
   263  		}
   264  		if n.UDP() != n.TCP() {
   265  			u.RawQuery = "discport=" + strconv.Itoa(n.UDP())
   266  		}
   267  		// Quorum
   268  		if n.HasRaftPort() {
   269  			raftQuery := "raftport=" + strconv.Itoa(n.RaftPort())
   270  			if len(u.RawQuery) > 0 {
   271  				u.RawQuery = u.RawQuery + "&" + raftQuery
   272  			} else {
   273  				u.RawQuery = raftQuery
   274  			}
   275  		}
   276  	}
   277  	return u.String()
   278  }
   279  
   280  // PubkeyToIDV4 derives the v4 node address from the given public key.
   281  func PubkeyToIDV4(key *ecdsa.PublicKey) ID {
   282  	e := make([]byte, 64)
   283  	math.ReadBits(key.X, e[:len(e)/2])
   284  	math.ReadBits(key.Y, e[len(e)/2:])
   285  	return ID(crypto.Keccak256Hash(e))
   286  }