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 }