github.com/annchain/OG@v0.0.9/p2p/onode/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 onode
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"encoding/hex"
    22  	"errors"
    23  	"fmt"
    24  	ogcrypto2 "github.com/annchain/OG/deprecated/ogcrypto"
    25  	"github.com/annchain/OG/p2p/dns"
    26  	"net"
    27  	"net/url"
    28  	"regexp"
    29  	"strconv"
    30  
    31  	"github.com/annchain/OG/common/math"
    32  	"github.com/annchain/OG/p2p/enr"
    33  )
    34  
    35  var incompleteNodeURL = regexp.MustCompile("(?i)^(?:onode://)?([0-9a-f]+)$")
    36  
    37  // MustParseV4 parses a node URL. It panics if the URL is not valid.
    38  func MustParseV4(rawurl string) *Node {
    39  	n, err := ParseV4(rawurl)
    40  	if err != nil {
    41  		panic("invalid node URL: " + err.Error())
    42  	}
    43  	return n
    44  }
    45  
    46  // ParseV4 parses a node URL.
    47  //
    48  // There are two basic forms of node URLs:
    49  //
    50  //   - incomplete nodes, which only have the public key (node ID)
    51  //   - complete nodes, which contain the public key and IP/Port information
    52  //
    53  // For incomplete nodes, the designator must look like one of these
    54  //
    55  //    onode://<hex node id>
    56  //    <hex node id>
    57  //
    58  // For complete nodes, the node ID is encoded in the username portion
    59  // of the URL, separated from the host by an @ sign. The hostname can
    60  // only be given as an IP address, DNS domain names are not allowed.
    61  // The port in the host name section is the TCP listening port. If the
    62  // TCP and UDP (discovery) ports differ, the UDP port is specified as
    63  // query parameter "discport".
    64  //
    65  // In the following example, the node URL describes
    66  // a node with IP address 10.3.58.6, TCP listening port 30303
    67  // and UDP discovery port 30301.
    68  //
    69  //    onode://<hex node id>@10.3.58.6:30303?discport=30301
    70  func ParseV4(rawurl string) (*Node, error) {
    71  	if m := incompleteNodeURL.FindStringSubmatch(rawurl); m != nil {
    72  		id, err := parsePubkey(m[1])
    73  		if err != nil {
    74  			return nil, fmt.Errorf("invalid node ID (%v)", err)
    75  		}
    76  		return NewV4(id, nil, 0, 0), nil
    77  	}
    78  	return parseComplete(rawurl)
    79  }
    80  
    81  // NewV4 creates a node from discovery v4 node information. The record
    82  // contained in the node has a zero-length signature.
    83  func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp int) *Node {
    84  	var r enr.Record
    85  	if ip != nil {
    86  		eIp := enr.IP(ip)
    87  		r.Set(&eIp)
    88  	}
    89  	if udp != 0 {
    90  		eudp := enr.UDP(udp)
    91  		r.Set(&eudp)
    92  	}
    93  	if tcp != 0 {
    94  		etcp := enr.TCP(tcp)
    95  		r.Set(&etcp)
    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  func parseComplete(rawurl string) (*Node, error) {
   106  	var (
   107  		id               *ecdsa.PublicKey
   108  		ip               net.IP
   109  		tcpPort, udpPort uint64
   110  	)
   111  	u, err := url.Parse(rawurl)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	if u.Scheme != "onode" {
   116  		return nil, errors.New("invalid URL scheme, want \"onode\"")
   117  	}
   118  	// Parse the Node ID from the user portion.
   119  	if u.User == nil {
   120  		return nil, errors.New("does not contain node ID")
   121  	}
   122  	if id, err = parsePubkey(u.User.String()); err != nil {
   123  		return nil, fmt.Errorf("invalid node ID (%v)", err)
   124  	}
   125  	// Parse the IP address.
   126  	host, port, err := net.SplitHostPort(u.Host)
   127  	if err != nil {
   128  		return nil, fmt.Errorf("invalid host: %v", err)
   129  	}
   130  	if ip = net.ParseIP(host); ip == nil {
   131  		// try resove IP
   132  		if ip, err = dns.Lookup(host); err != nil {
   133  			return nil, errors.New("invalid IP address")
   134  		}
   135  
   136  	}
   137  	// Ensure the IP is 4 bytes long for IPv4 addresses.
   138  	if ipv4 := ip.To4(); ipv4 != nil {
   139  		ip = ipv4
   140  	}
   141  	// Parse the port numbers.
   142  	if tcpPort, err = strconv.ParseUint(port, 10, 16); err != nil {
   143  		return nil, errors.New("invalid port")
   144  	}
   145  	udpPort = tcpPort
   146  	qv := u.Query()
   147  	if qv.Get("discport") != "" {
   148  		udpPort, err = strconv.ParseUint(qv.Get("discport"), 10, 16)
   149  		if err != nil {
   150  			return nil, errors.New("invalid discport in query")
   151  		}
   152  	}
   153  	return NewV4(id, ip, int(tcpPort), int(udpPort)), nil
   154  }
   155  
   156  // parsePubkey parses a hex-encoded secp256k1 public key.
   157  func parsePubkey(in string) (*ecdsa.PublicKey, error) {
   158  	b, err := hex.DecodeString(in)
   159  	if err != nil {
   160  		return nil, err
   161  	} else if len(b) != 64 {
   162  		return nil, fmt.Errorf("wrong length, want %d hex chars", 128)
   163  	}
   164  	b = append([]byte{0x4}, b...)
   165  	return ogcrypto2.UnmarshalPubkey(b)
   166  }
   167  
   168  func (n *Node) v4URL() string {
   169  	var (
   170  		scheme enr.ID
   171  		nodeid string
   172  		key    ecdsa.PublicKey
   173  	)
   174  	n.Load(&scheme)
   175  	n.Load((*Secp256k1)(&key))
   176  	switch {
   177  	case scheme == "v4" || key != ecdsa.PublicKey{}:
   178  		nodeid = fmt.Sprintf("%x", ogcrypto2.FromECDSAPub(&key)[1:])
   179  	default:
   180  		nodeid = fmt.Sprintf("%s.%x", scheme, n.Id[:])
   181  	}
   182  	u := url.URL{Scheme: "onode"}
   183  	if n.Incomplete() {
   184  		u.Host = nodeid
   185  	} else {
   186  		addr := net.TCPAddr{IP: n.IP(), Port: n.TCP()}
   187  		u.User = url.User(nodeid)
   188  		u.Host = addr.String()
   189  		if n.UDP() != n.TCP() {
   190  			u.RawQuery = "discport=" + strconv.Itoa(n.UDP())
   191  		}
   192  	}
   193  	return u.String()
   194  }
   195  
   196  // PubkeyToIDV4 derives the v4 node address from the given public key.
   197  func PubkeyToIDV4(key *ecdsa.PublicKey) ID {
   198  	e := make([]byte, 64)
   199  	math.ReadBits(key.X, e[:len(e)/2])
   200  	math.ReadBits(key.Y, e[len(e)/2:])
   201  	return ID(ogcrypto2.Keccak256Hash(e).Bytes)
   202  }