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