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  }