github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/addr.go (about) 1 package network 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "sort" 8 "sync" 9 "time" 10 11 "github.com/piotrnar/gocoin/client/common" 12 "github.com/piotrnar/gocoin/client/network/peersdb" 13 "github.com/piotrnar/gocoin/lib/btc" 14 "github.com/piotrnar/gocoin/lib/others/qdb" 15 "github.com/piotrnar/gocoin/lib/others/sys" 16 ) 17 18 var ( 19 ExternalIp4 map[uint32][2]uint = make(map[uint32][2]uint) // [0]-count, [1]-timestamp 20 ExternalIpMutex sync.Mutex 21 ExternalIpExpireTicker int 22 ) 23 24 func ExternalAddrLen() (res int) { 25 ExternalIpMutex.Lock() 26 res = len(ExternalIp4) 27 ExternalIpMutex.Unlock() 28 return 29 } 30 31 type ExternalIpRec struct { 32 IP uint32 33 Cnt uint 34 Tim uint 35 } 36 37 // GetExternalIPs returns the list sorted by "freshness". 38 func GetExternalIPs() (arr []ExternalIpRec) { 39 ExternalIpMutex.Lock() 40 defer ExternalIpMutex.Unlock() 41 42 arr = make([]ExternalIpRec, 0, len(ExternalIp4)+1) 43 var arx *ExternalIpRec 44 45 if external_ip := common.GetExternalIp(); external_ip != "" { 46 var a, b, c, d int 47 if n, _ := fmt.Sscanf(external_ip, "%d.%d.%d.%d", &a, &b, &c, &d); n == 4 && (uint(a|b|c|d)&0xffffff00) == 0 { 48 arx = new(ExternalIpRec) 49 arx.IP = (uint32(a) << 24) | (uint32(b) << 16) | (uint32(c) << 8) | uint32(d) 50 arx.Cnt = 1e6 51 arx.Tim = uint(time.Now().Unix()) + 60 52 arr = append(arr, *arx) 53 } 54 } 55 56 if len(ExternalIp4) > 0 { 57 for ip, rec := range ExternalIp4 { 58 if arx != nil && arx.IP == ip { 59 continue 60 } 61 arr = append(arr, ExternalIpRec{IP: ip, Cnt: rec[0], Tim: rec[1]}) 62 } 63 64 if len(arr) > 1 { 65 sort.Slice(arr, func(i, j int) bool { 66 if arr[i].Cnt > 3 && arr[j].Cnt > 3 || arr[i].Cnt == arr[j].Cnt { 67 return arr[i].Tim > arr[j].Tim 68 } 69 return arr[i].Cnt > arr[j].Cnt 70 }) 71 } 72 } 73 74 return 75 } 76 77 func BestExternalAddr() []byte { 78 arr := GetExternalIPs() 79 80 // Expire any extra IP if it has been stale for more than an hour 81 if len(arr) > 1 { 82 worst := &arr[len(arr)-1] 83 84 if uint(time.Now().Unix())-worst.Tim > 3600 { 85 common.CountSafe("ExternalIPExpire") 86 ExternalIpMutex.Lock() 87 if ExternalIp4[worst.IP][0] == worst.Cnt { 88 delete(ExternalIp4, worst.IP) 89 } 90 ExternalIpMutex.Unlock() 91 } 92 } 93 94 res := make([]byte, 26) 95 binary.LittleEndian.PutUint64(res[0:8], common.Services) 96 // leave ip6 filled with zeros, except for the last 2 bytes: 97 res[18], res[19] = 0xff, 0xff 98 if len(arr) > 0 { 99 binary.BigEndian.PutUint32(res[20:24], arr[0].IP) 100 } 101 binary.BigEndian.PutUint16(res[24:26], common.DefaultTcpPort()) 102 return res 103 } 104 105 // HandleGetaddr sends the response to "getaddr" message. 106 // Sends addr message with up to 500 randomly selected peers from our database. 107 // Selects only peers that we have been seeing alive and have not been banned. 108 func (c *OneConnection) HandleGetaddr() { 109 pers := peersdb.GetRecentPeers(MaxAddrsPerMessage, false, func(p *peersdb.PeerAddr) bool { 110 return p.Banned != 0 || !p.SeenAlive // we only return addresses that we've seen alive 111 }) 112 if len(pers) > 0 { 113 buf := new(bytes.Buffer) 114 btc.WriteVlen(buf, uint64(len(pers))) 115 for i := range pers { 116 binary.Write(buf, binary.LittleEndian, pers[i].Time) 117 buf.Write(pers[i].NetAddr.Bytes()) 118 } 119 c.SendRawMsg("addr", buf.Bytes()) 120 } 121 } 122 123 func (c *OneConnection) SendOwnAddr() { 124 if ExternalAddrLen() > 0 { 125 buf := new(bytes.Buffer) 126 btc.WriteVlen(buf, uint64(1)) 127 binary.Write(buf, binary.LittleEndian, uint32(time.Now().Unix())) 128 buf.Write(BestExternalAddr()) 129 c.SendRawMsg("addr", buf.Bytes()) 130 } 131 } 132 133 // ParseAddr parses the network's "addr" message. 134 func (c *OneConnection) ParseAddr(pl []byte) { 135 var c_ip_invalid, c_future, c_old, c_new_rejected, c_new_taken, c_stale uint64 136 have_enough := peersdb.PeerDB.Count() > peersdb.MinPeersInDB 137 b := bytes.NewBuffer(pl) 138 cnt, _ := btc.ReadVLen(b) 139 for i := 0; i < int(cnt); i++ { 140 var buf [30]byte 141 n, e := b.Read(buf[:]) 142 if n != len(buf) || e != nil { 143 common.CountSafe("AddrError") 144 c.DoS("AddrError") 145 break 146 } 147 a := peersdb.NewPeer(buf[:]) 148 if !sys.ValidIp4(a.Ip4[:]) { 149 c_ip_invalid++ 150 } else { 151 now := uint32(time.Now().Unix()) 152 if a.Time > now { 153 if a.Time-now >= 3600 { // It more than 1 hour in the future, reject it 154 c_future++ 155 if c.Misbehave("AdrFuture", 50) { 156 break 157 } 158 } 159 a.Time = now 160 } else if have_enough && now-a.Time >= 24*3600 { 161 c_stale++ // addr older than 24 hour - ignore it, if we have enough in the DB 162 continue 163 } 164 k := qdb.KeyType(a.UniqID()) 165 peersdb.Lock() 166 v := peersdb.PeerDB.Get(k) 167 if v != nil { 168 op := peersdb.NewPeer(v[:]) 169 if !op.SeenAlive && a.Time > op.Time { 170 op.Time = a.Time // only update the time if peer not seen alive (yet) 171 } 172 a = op 173 c_old++ 174 } else { 175 if peersdb.PeerDB.Count() >= peersdb.MaxPeersInDB+peersdb.MaxPeersDeviation { 176 c_new_rejected++ 177 goto unlock_db 178 } 179 a.CameFromIP = c.PeerAddr.Ip4[:] 180 c_new_taken++ 181 } 182 peersdb.PeerDB.Put(k, a.Bytes()) 183 unlock_db: 184 peersdb.Unlock() 185 } 186 } 187 common.CounterMutex.Lock() 188 if c_ip_invalid > 0 { 189 common.CountAdd("AddrIPinvalid", c_ip_invalid) 190 } 191 if c_future > 0 { 192 common.CountAdd("AddrFuture", c_future) 193 } 194 if c_old > 0 { 195 common.CountAdd("AddrUpdated", c_old) 196 } 197 if c_new_taken > 0 { 198 common.CountAdd("AddrNewYES", c_new_taken) 199 } 200 if c_new_rejected > 0 { 201 common.CountAdd("AddrNewNO", c_new_rejected) 202 } 203 if c_stale > 0 { 204 common.CountAdd("AddrStale", c_stale) 205 } 206 common.CounterMutex.Unlock() 207 c.Mutex.Lock() 208 c.X.AddrMsgsRcvd++ 209 c.X.NewAddrsRcvd += c_new_taken + c_new_rejected 210 c.Mutex.Unlock() 211 if c.X.NewAddrsRcvd > 100 && c.X.AddrMsgsRcvd >= 10 && time.Now().Sub(c.X.ConnectedAt) < 10*time.Second { 212 // delete all the new records that came from this ip 213 delcnt := peersdb.DeleteFromIP(c.PeerAddr.Ip4[:]) 214 common.CountSafeAdd("AddrBanUndone", uint64(delcnt)) 215 //println("Address flood from", c.PeerAddr.Ip(), c.Node.Agent, c.X.Incomming, c.X.AddrMsgsRcvd, time.Now().Sub(c.X.ConnectedAt).String(), delcnt) 216 c.DoS("AddrFlood") 217 } 218 }