github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/ver.go (about) 1 package network 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "os" 9 "strings" 10 "time" 11 12 "github.com/piotrnar/gocoin/client/common" 13 "github.com/piotrnar/gocoin/lib/btc" 14 "github.com/piotrnar/gocoin/lib/others/sys" 15 "github.com/piotrnar/gocoin/lib/secp256k1" 16 ) 17 18 var IgnoreExternalIpFrom = []string{} 19 20 func (c *OneConnection) SendVersion() { 21 b := bytes.NewBuffer([]byte{}) 22 23 binary.Write(b, binary.LittleEndian, uint32(common.Version)) 24 binary.Write(b, binary.LittleEndian, uint64(common.Services)) 25 binary.Write(b, binary.LittleEndian, uint64(time.Now().Unix())) 26 27 b.Write(c.PeerAddr.NetAddr.Bytes()) 28 if ExternalAddrLen() > 0 { 29 b.Write(BestExternalAddr()) 30 } else { 31 b.Write(bytes.Repeat([]byte{0}, 26)) 32 } 33 34 b.Write(nonce[:]) 35 36 common.LockCfg() 37 btc.WriteVlen(b, uint64(len(common.UserAgent))) 38 b.Write([]byte(common.UserAgent)) 39 common.UnlockCfg() 40 41 binary.Write(b, binary.LittleEndian, uint32(common.Last.BlockHeight())) 42 if !common.GetBool(&common.CFG.TXPool.Enabled) { 43 b.WriteByte(0) // don't notify me about txs 44 } 45 46 c.SendRawMsg("version", b.Bytes()) 47 } 48 49 func (c *OneConnection) IsGocoin() bool { 50 return strings.HasPrefix(c.Node.Agent, "/Gocoin:") 51 } 52 53 func (c *OneConnection) HandleVersion(pl []byte) error { 54 if len(pl) < 80 /*Up to, includiong, the nonce */ { 55 return errors.New("MsgTooShort") 56 } 57 58 c.Mutex.Lock() 59 c.Node.Version = binary.LittleEndian.Uint32(pl[0:4]) 60 c.Node.Services = binary.LittleEndian.Uint64(pl[4:12]) 61 c.PeerAddr.Services = c.Node.Services 62 copy(c.Node.Nonce[:], pl[72:80]) 63 c.Node.Timestamp = binary.LittleEndian.Uint64(pl[12:20]) 64 c.Node.ReportedIp4 = binary.BigEndian.Uint32(pl[40:44]) 65 66 use_this_ip := sys.ValidIp4(pl[40:44]) 67 68 if len(pl) >= 82 { 69 le, of := btc.VLen(pl[80:]) 70 if of == 0 || len(pl) < 80+le { 71 c.Mutex.Unlock() 72 return errors.New("MsgCorrupt") 73 } 74 of += 80 75 c.Node.Agent = string(pl[of : of+le]) 76 of += le 77 if len(pl) >= of+4 { 78 c.Node.Height = binary.LittleEndian.Uint32(pl[of : of+4]) 79 c.X.GetBlocksDataNow = true 80 of += 4 81 if len(pl) > of && pl[of] == 0 { 82 c.Node.DoNotRelayTxs = true 83 } 84 } 85 c.X.IsGocoin = c.IsGocoin() 86 } 87 c.PeerAddr.NodeAgent = c.Node.Agent 88 c.X.VersionReceived = true 89 c.Mutex.Unlock() 90 91 if !c.X.IsSpecial { 92 FriendsAccess.Lock() 93 for _, ua := range SpecialAgents { 94 if strings.HasPrefix(c.Node.Agent, ua) { 95 c.X.IsSpecial = true 96 goto special 97 } 98 } 99 for _, ip := range SpecialIPs { 100 if c.PeerAddr.Ip4 == ip { 101 c.X.IsSpecial = true 102 goto special 103 } 104 } 105 special: 106 FriendsAccess.Unlock() 107 } 108 109 if !c.X.IsSpecial { 110 if c.Node.Version < MIN_PROTO_VERSION { 111 return errors.New("TooLow") 112 } 113 if (c.Node.Services & btc.SERVICE_SEGWIT) == 0 { 114 return errors.New("NoSegwit") 115 } 116 if !c.HasNetworkService() { 117 return errors.New("NoService") 118 } 119 if c.Node.Nonce == [8]byte{0, 0, 0, 0, 0, 0, 0, 0} { 120 return errors.New("NullNonce") 121 } 122 if c.Node.Nonce == nonce { 123 return errors.New("OurNonce") 124 } 125 } 126 127 // check if we don't have this nonce yet 128 Mutex_net.Lock() 129 for _, v := range OpenCons { 130 if v != c { 131 v.Mutex.Lock() 132 yes := v.X.VersionReceived && v.Node.Nonce == c.Node.Nonce 133 v.Mutex.Unlock() 134 if yes { 135 Mutex_net.Unlock() 136 return errors.New("SameNonce") 137 } 138 } 139 } 140 Mutex_net.Unlock() 141 142 if use_this_ip { 143 if bytes.Equal(pl[40:44], c.PeerAddr.Ip4[:]) { 144 if common.FLAG.Log { 145 ExternalIpMutex.Lock() 146 f, _ := os.OpenFile("badip_log.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660) 147 if f != nil { 148 fmt.Fprintf(f, "%s: OWN IP from %s @ %s - %d\n", 149 time.Now().Format("2006-01-02 15:04:05"), 150 c.Node.Agent, c.PeerAddr.Ip(), c.ConnID) 151 f.Close() 152 } 153 ExternalIpMutex.Unlock() 154 } 155 common.CountSafe("IgnoreExtIP-O") 156 use_this_ip = false 157 } else if len(pl) >= 86 && binary.BigEndian.Uint32(pl[66:70]) != 0 && 158 !bytes.Equal(pl[66:70], c.PeerAddr.Ip4[:]) { 159 if common.FLAG.Log { 160 ExternalIpMutex.Lock() 161 f, _ := os.OpenFile("badip_log.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660) 162 if f != nil { 163 fmt.Fprintf(f, "%s: BAD IP=%d.%d.%d.%d from %s @ %s - %d\n", 164 time.Now().Format("2006-01-02 15:04:05"), 165 pl[66], pl[67], pl[68], pl[69], c.Node.Agent, c.PeerAddr.Ip(), c.ConnID) 166 f.Close() 167 } 168 ExternalIpMutex.Unlock() 169 } 170 common.CountSafe("IgnoreExtIP-B") 171 use_this_ip = false 172 } 173 } 174 175 if use_this_ip { 176 ExternalIpMutex.Lock() 177 if _, known := ExternalIp4[c.Node.ReportedIp4]; !known { // New IP 178 use_this_ip = true 179 for x, v := range IgnoreExternalIpFrom { 180 if c.Node.Agent == v { 181 use_this_ip = false 182 common.CountSafe(fmt.Sprint("IgnoreExtIP", x)) 183 break 184 } 185 } 186 if use_this_ip && common.IsListenTCP() && common.GetExternalIp() == "" { 187 fmt.Printf("New external IP %d.%d.%d.%d from ConnID=%d (%s)\n> ", 188 pl[40], pl[41], pl[42], pl[43], c.ConnID, c.Node.Agent) 189 } 190 } 191 if use_this_ip { 192 ExternalIp4[c.Node.ReportedIp4] = [2]uint{ExternalIp4[c.Node.ReportedIp4][0] + 1, 193 uint(time.Now().Unix())} 194 } 195 ExternalIpMutex.Unlock() 196 } 197 198 c.SendRawMsg("verack", []byte{}) 199 return nil 200 } 201 202 // SendAuth sends auth messages (only used by other gocoin nodes). 203 func (c *OneConnection) SendAuth() { 204 rnd := make([]byte, 32) 205 copy(rnd, c.Node.Nonce[:]) 206 r, s, er := btc.EcdsaSign(common.SecretKey, rnd) 207 if er != nil { 208 println(er.Error()) 209 return 210 } 211 var sig secp256k1.Signature 212 sig.R.Set(r) 213 sig.S.Set(s) 214 215 msg := bytes.NewBuffer(sig.Bytes()) 216 // add last block hash and last block height 217 common.Last.Mutex.Lock() 218 msg.Write(common.Last.Block.BlockHash.Hash[:]) 219 binary.Write(msg, binary.LittleEndian, uint32(common.Last.Block.Height)) 220 common.Last.Mutex.Unlock() 221 c.SendRawMsg("auth", msg.Bytes()) 222 } 223 224 // AuthRvcd processes auth messages (from other gocoin nodes). 225 func (c *OneConnection) AuthRvcd(pl []byte) { 226 if c.X.AuthMsgGot > 0 { 227 c.DoS("AuthMsgCnt") // Only allow one auth message per connection (DoS prevention) 228 return 229 } 230 c.X.AuthMsgGot++ 231 232 c.X.Authorized = false 233 234 var sig secp256k1.Signature 235 var pkey secp256k1.XY 236 var m secp256k1.Number 237 var b32 [32]byte 238 239 sig_len := sig.ParseBytes(pl) 240 if sig_len < 0 { 241 return 242 } 243 244 copy(b32[:8], nonce[:]) // the remaining bytes shall be zero'ed 245 m.SetBytes(b32[:]) 246 247 FriendsAccess.Lock() 248 for _, pub := range AuthPubkeys { 249 if pkey.ParsePubkey(pub) && sig.Verify(&pkey, &m) { 250 c.X.Authorized = true 251 break 252 } 253 } 254 FriendsAccess.Unlock() 255 if !c.X.Authorized { 256 return 257 } 258 259 // Authorized node - check for last block data fields 260 if len(pl) >= sig_len+32+4 { 261 bl_height := binary.LittleEndian.Uint32(pl[sig_len+32 : sig_len+36]) 262 common.Last.Mutex.Lock() 263 c.X.ChainSynchronized = bl_height >= uint32(common.Last.Block.Height) 264 common.Last.Mutex.Unlock() 265 266 if c.X.ChainSynchronized { 267 copy(b32[:], pl[sig_len:sig_len+32]) 268 common.LockCfg() 269 common.ApplyLTB(btc.NewUint256(b32[:]), bl_height) 270 common.UnlockCfg() 271 } 272 } 273 var repl [1]byte // return whether (we think that) we are synchronized 274 if common.GetBool(&common.BlockChainSynchronized) { 275 repl[0] = 1 276 } 277 c.SendRawMsg("authack", repl[:]) 278 } 279 280 func (c *OneConnection) HasNetworkService() bool { 281 return (c.Node.Services & (btc.SERVICE_NETWORK | btc.SERVICE_NETWORK_LIMITED)) != 0 282 }