github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/enode/urlv4.go (about)

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