github.com/aergoio/aergo@v1.3.1/polaris/server/mapservice.go (about)

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package server
     7  
     8  import (
     9  	"bufio"
    10  	"fmt"
    11  	"github.com/aergoio/aergo/p2p/v030"
    12  	"math"
    13  	"net"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/aergoio/aergo-actor/actor"
    18  	"github.com/aergoio/aergo-lib/log"
    19  	"github.com/aergoio/aergo/config"
    20  	"github.com/aergoio/aergo/internal/network"
    21  	"github.com/aergoio/aergo/p2p/p2pcommon"
    22  	"github.com/aergoio/aergo/p2p/p2putil"
    23  	"github.com/aergoio/aergo/pkg/component"
    24  	"github.com/aergoio/aergo/polaris/common"
    25  	"github.com/aergoio/aergo/types"
    26  	"github.com/gofrs/uuid"
    27  )
    28  
    29  // internal
    30  const (
    31  	PolarisPingTTL       = common.PolarisConnectionTTL >> 1
    32  
    33  	// polaris will return peers list at most this number
    34  	ResponseMaxPeerLimit = 500
    35  	// libp2p internal library is not always send message instantly, so closing socket soon after sent a message will cause packet loss and read error, us walkaround here till finding the real reason and fix it.
    36  	MsgSendDelay = time.Second * 1
    37  
    38  	PeerHealthcheckInterval = time.Minute
    39  	//PeerHealthcheckInterval = time.Minute * 5
    40  	ConcurrentHealthCheckCount = 20
    41  )
    42  
    43  var (
    44  	EmptyMsgID = p2pcommon.MsgID(uuid.Nil)
    45  )
    46  
    47  type mapService interface {
    48  	getPeerCheckers() []peerChecker
    49  	registerPeer(receivedMeta p2pcommon.PeerMeta) error
    50  	unregisterPeer(peerID types.PeerID)
    51  }
    52  
    53  type peerChecker interface {
    54  	lastCheck() time.Time
    55  	// check checks peer. it will stop check at best effort when timeout is exceeded. and wg done.
    56  	check(wg *sync.WaitGroup, timeout time.Duration)
    57  }
    58  
    59  // PeerMapService is
    60  type PeerMapService struct {
    61  	*component.BaseComponent
    62  
    63  	PrivateNet   bool
    64  	allowPrivate bool
    65  
    66  	ntc p2pcommon.NTContainer
    67  	nt  p2pcommon.NetworkTransport
    68  	hc  HealthCheckManager
    69  	lm  *polarisListManager
    70  
    71  	rwmutex      *sync.RWMutex
    72  	peerRegistry map[types.PeerID]*peerState
    73  }
    74  
    75  func NewPolarisService(cfg *config.Config, ntc p2pcommon.NTContainer) *PeerMapService {
    76  	pms := &PeerMapService{
    77  		rwmutex:      &sync.RWMutex{},
    78  		peerRegistry: make(map[types.PeerID]*peerState),
    79  		allowPrivate: cfg.Polaris.AllowPrivate,
    80  	}
    81  
    82  	pms.BaseComponent = component.NewBaseComponent(PolarisSvc, pms, log.NewLogger("polaris"))
    83  
    84  	pms.ntc = ntc
    85  	pms.hc = NewHCM(pms, pms.nt)
    86  
    87  	pms.PrivateNet = !ntc.ChainID().MainNet
    88  
    89  	pms.lm = NewPolarisListManager(cfg.Polaris, cfg.BaseConfig.AuthDir, pms.Logger)
    90  	// initialize map Servers
    91  	return pms
    92  }
    93  
    94  func (pms *PeerMapService) SetHub(hub *component.ComponentHub) {
    95  	pms.BaseComponent.SetHub(hub)
    96  }
    97  
    98  func (pms *PeerMapService) BeforeStart() {}
    99  
   100  func (pms *PeerMapService) AfterStart() {
   101  	pms.nt = pms.ntc.GetNetworkTransport()
   102  	pms.lm.Start()
   103  	pms.Logger.Info().Str("minAergoVer", p2pcommon.MinimumAergoVersion).Str("maxAergoVer", p2pcommon.MaximumAergoVersion).Str("version", string(common.PolarisMapSub)).Msg("Starting polaris listening")
   104  	pms.nt.AddStreamHandler(common.PolarisMapSub, pms.onConnect)
   105  	pms.hc.Start()
   106  }
   107  
   108  func (pms *PeerMapService) BeforeStop() {
   109  	if pms.nt != nil {
   110  		pms.hc.Stop()
   111  		pms.nt.RemoveStreamHandler(common.PolarisMapSub)
   112  	}
   113  	pms.lm.Stop()
   114  }
   115  
   116  func (pms *PeerMapService) Statistics() *map[string]interface{} {
   117  	return nil
   118  	//dummy := make(map[string]interface{})
   119  	//return &dummy
   120  }
   121  
   122  func (pms *PeerMapService) onConnect(s types.Stream) {
   123  	defer s.Close()
   124  	peerID := s.Conn().RemotePeer()
   125  	remoteIP := p2putil.ExtractIPAddress(s.Conn().RemoteMultiaddr())
   126  	if remoteIP == nil {
   127  		pms.Logger.Info().Str("addr", s.Conn().RemoteMultiaddr().String()).Str(p2putil.LogPeerID, peerID.String()).Msg("Invalid address information")
   128  		return
   129  	}
   130  	remotePeerMeta :=  p2pcommon.PeerMeta{ID: peerID}
   131  	pms.Logger.Debug().Str("addr", remoteIP.String()).Str(p2putil.LogPeerID, peerID.String()).Msg("Received map query")
   132  
   133  	rw := v030.NewV030ReadWriter(bufio.NewReader(s), bufio.NewWriter(s), nil)
   134  
   135  	// receive input
   136  	container, query, err := pms.readRequest(remotePeerMeta, rw)
   137  	if err != nil {
   138  		pms.Logger.Debug().Err(err).Str(p2putil.LogPeerID, peerID.String()).Msg("failed to read query")
   139  		return
   140  	}
   141  
   142  	// check blacklist
   143  	if banned,_ := pms.lm.IsBanned(remoteIP.String(), peerID); banned {
   144  		pms.Logger.Debug().Str("address", remoteIP.String()).Str(p2putil.LogPeerID, peerID.String()).Msg("close soon banned peer")
   145  		return
   146  	}
   147  	resp, err := pms.handleQuery(container, query)
   148  	if err != nil {
   149  		pms.Logger.Debug().Err(err).Str(p2putil.LogPeerID, peerID.String()).Msg("failed to handle query")
   150  		return
   151  	}
   152  
   153  	// response to peer
   154  	if err = pms.writeResponse(container, remotePeerMeta, resp, rw); err != nil {
   155  		pms.Logger.Debug().Err(err).Str(p2putil.LogPeerID, peerID.String()).Msg("failed to write query")
   156  		return
   157  	}
   158  	pms.Logger.Debug().Str("status", resp.Status.String()).Str(p2putil.LogPeerID, peerID.String()).Int("peer_cnt", len(resp.Addresses)).Msg("Sent map response")
   159  
   160  	// TODO send goodbye message.
   161  	time.Sleep(time.Second * 3)
   162  
   163  	// disconnect!
   164  }
   165  
   166  // tryAddPeer will do check connecting peer and add. it will return peer meta information received from
   167  // remote peer setup some
   168  func (pms *PeerMapService) readRequest(meta p2pcommon.PeerMeta, rd p2pcommon.MsgReadWriter) (p2pcommon.Message, *types.MapQuery, error) {
   169  	data, err := rd.ReadMsg()
   170  	if err != nil {
   171  		return nil, nil, err
   172  	}
   173  	queryReq := &types.MapQuery{}
   174  	err = p2putil.UnmarshalMessageBody(data.Payload(), queryReq)
   175  	if err != nil {
   176  		return data, nil, err
   177  	}
   178  
   179  	return data, queryReq, nil
   180  }
   181  
   182  // handleQuery check query parameters, return registered peers, and register this peer if requested.
   183  func (pms *PeerMapService) handleQuery(container p2pcommon.Message, query *types.MapQuery) (*types.MapResponse, error) {
   184  	if query.Status == nil {
   185  		return nil, fmt.Errorf("malformed query %v", query)
   186  	}
   187  	receivedMeta := p2pcommon.FromPeerAddress(query.Status.Sender)
   188  	receivedMeta.Version = query.Status.Version
   189  	maxPeers := int(query.Size)
   190  	if maxPeers <= 0 {
   191  		return nil, fmt.Errorf("invalid argument count %d", maxPeers)
   192  	} else if maxPeers > ResponseMaxPeerLimit {
   193  		pms.Logger.Debug().Str(p2putil.LogPeerID, receivedMeta.ID.String()).Int("req_size", maxPeers).Int("clipped", ResponseMaxPeerLimit).Msg("Clipping too high count of query ")
   194  		maxPeers = ResponseMaxPeerLimit
   195  	}
   196  
   197  	pms.Logger.Debug().Str(p2putil.LogPeerID, receivedMeta.ID.String()).Msg("Handling query.")
   198  
   199  	// make response
   200  	resp := &types.MapResponse{}
   201  
   202  	// check peer version
   203  	if !p2pcommon.CheckVersion(receivedMeta.Version) {
   204  		pms.Logger.Debug().Str(p2putil.LogPeerID, receivedMeta.ID.String()).Str("version", receivedMeta.Version).Msg("peer version is too old, or too new")
   205  		resp.Status = types.ResultStatus_FAILED_PRECONDITION
   206  		resp.Message = common.TooOldVersionMsg
   207  		return resp, nil
   208  
   209  	}
   210  
   211  	// compare chainID
   212  	sameChain, err := pms.checkChain(query.Status.ChainID)
   213  	if err != nil {
   214  		pms.Logger.Debug().Err(err).Str(p2putil.LogPeerID, receivedMeta.ID.String()).Bytes("chainID", query.Status.ChainID).Msg("err parsing chain id")
   215  		resp.Status = types.ResultStatus_INVALID_ARGUMENT
   216  		resp.Message = "invalid chain id"
   217  		return resp, nil
   218  	} else if !sameChain {
   219  		pms.Logger.Debug().Str(p2putil.LogPeerID, receivedMeta.ID.String()).Msg("err different chain")
   220  		resp.Status = types.ResultStatus_UNAUTHENTICATED
   221  		resp.Message = "different chain"
   222  		return resp, nil
   223  	}
   224  
   225  	resp.Addresses = pms.retrieveList(maxPeers, receivedMeta.ID)
   226  
   227  	// old syntax (AddMe) and newer syntax (status.NoExpose) for expose peer
   228  	if query.AddMe && !query.Status.NoExpose {
   229  		// check Sender
   230  		// check peer is really capable to aergosvr
   231  		if !pms.checkConnectness(receivedMeta) {
   232  			pms.Logger.Debug().Str(p2putil.LogPeerID, receivedMeta.ID.String()).Msg("AddMe is set, but cant connect back to peer")
   233  			resp.Status = types.ResultStatus_OK
   234  			resp.Message = "can't connect back, so not registered"
   235  			return resp, nil
   236  		}
   237  		pms.Logger.Debug().Str(p2putil.LogPeerID, receivedMeta.ID.String()).Msg("AddMe is set, and register peer to peer registry")
   238  		pms.registerPeer(receivedMeta)
   239  	}
   240  
   241  	resp.Status = types.ResultStatus_OK
   242  	return resp, nil
   243  }
   244  
   245  func (pms *PeerMapService) retrieveList(maxPeers int, exclude types.PeerID) []*types.PeerAddress {
   246  	list := make([]*types.PeerAddress, 0, maxPeers)
   247  	pms.rwmutex.RLock()
   248  	defer pms.rwmutex.RUnlock()
   249  	for id, ps := range pms.peerRegistry {
   250  		if id == exclude {
   251  			continue
   252  		}
   253  		list = append(list, &ps.addr)
   254  		if len(list) >= maxPeers {
   255  			return list
   256  		}
   257  	}
   258  	return list
   259  }
   260  
   261  func (pms *PeerMapService) registerPeer(receivedMeta p2pcommon.PeerMeta) error {
   262  	peerID := receivedMeta.ID
   263  	pms.rwmutex.Lock()
   264  	defer pms.rwmutex.Unlock()
   265  	now := time.Now()
   266  	prev, ok := pms.peerRegistry[peerID]
   267  	if !ok {
   268  		newState := &peerState{connected: now, PeerMapService: pms, meta: receivedMeta, addr: receivedMeta.ToPeerAddress(), lCheckTime: now}
   269  		pms.Logger.Info().Str("meta", p2putil.ShortMetaForm(receivedMeta)).Str("version",receivedMeta.GetVersion()).Msg("Registering new peer info")
   270  		pms.peerRegistry[peerID] = newState
   271  	} else {
   272  		if prev.meta != receivedMeta {
   273  			pms.Logger.Info().Str("meta", p2putil.ShortMetaForm(prev.meta)).Msg("Replacing previous peer info")
   274  			prev.meta = receivedMeta
   275  			prev.addr = receivedMeta.ToPeerAddress()
   276  		}
   277  		prev.lCheckTime = now
   278  	}
   279  	return nil
   280  }
   281  
   282  func (pms *PeerMapService) unregisterPeer(peerID types.PeerID) {
   283  	pms.rwmutex.Lock()
   284  	defer pms.rwmutex.Unlock()
   285  	pms.Logger.Info().Str(p2putil.LogPeerID, p2putil.ShortForm(peerID)).Msg("Unregistering bad peer")
   286  	delete(pms.peerRegistry, peerID)
   287  }
   288  
   289  func (pms *PeerMapService) applyNewBLEntry(entry types.WhiteListEntry) {
   290  	pms.rwmutex.Lock()
   291  	defer pms.rwmutex.Unlock()
   292  	pms.Logger.Debug().Msg("Applying added blacklist entry; checking peers and remove from registry if banned")
   293  	if entry.PeerID != types.NotSpecifiedID {
   294  		// target is simply single peer
   295  		if ps, found := pms.peerRegistry[entry.PeerID]; found {
   296  			ip := net.ParseIP(ps.meta.IPAddress)
   297  			if ip == nil || entry.Contains(ip, ps.meta.ID) {
   298  				delete(pms.peerRegistry, ps.meta.ID)
   299  			}
   300  		}
   301  	} else {
   302  		for id, ps := range pms.peerRegistry {
   303  			ip := net.ParseIP(ps.meta.IPAddress)
   304  			if ip == nil || entry.Contains(ip, id) {
   305  				delete(pms.peerRegistry,id)
   306  			}
   307  		}
   308  	}
   309  }
   310  
   311  func (pms *PeerMapService) writeResponse(reqContainer p2pcommon.Message, meta p2pcommon.PeerMeta, resp *types.MapResponse, wt p2pcommon.MsgReadWriter) error {
   312  	msgID := p2pcommon.NewMsgID()
   313  	respMsg, err := createV030Message(msgID, reqContainer.ID(), common.MapResponse, resp)
   314  	if err != nil {
   315  		return err
   316  	}
   317  
   318  	return wt.WriteMsg(respMsg)
   319  }
   320  
   321  // TODO code duplication. it can result in a bug.
   322  func createV030Message(msgID, orgID p2pcommon.MsgID, subProtocol p2pcommon.SubProtocol, innerMsg p2pcommon.MessageBody) (p2pcommon.Message, error) {
   323  	bytes, err := p2putil.MarshalMessageBody(innerMsg)
   324  	if err != nil {
   325  		return nil, err
   326  	}
   327  
   328  	msg := common.NewPolarisRespMessage(msgID, orgID, subProtocol, bytes)
   329  	return msg, nil
   330  }
   331  
   332  func (pms *PeerMapService) Receive(context actor.Context) {
   333  	rawMsg := context.Message()
   334  	switch msg := rawMsg.(type) {
   335  	case *CurrentListMsg:
   336  		pms.Logger.Debug().Msg("Got current message")
   337  		context.Respond(pms.getCurrentPeers(msg))
   338  	case *WhiteListMsg:
   339  		pms.Logger.Debug().Msg("Got whitelist message")
   340  		context.Respond(pms.getWhiteList(msg))
   341  	case *BlackListMsg:
   342  		pms.Logger.Debug().Msg("Got blacklist message")
   343  		context.Respond(pms.getBlackList(msg))
   344  	case ListEntriesMsg:
   345  		ret := &types.BLConfEntries{Enabled:pms.lm.enabled}
   346  		entries := pms.lm.ListEntries()
   347  		ret.Entries = make([]string,len(entries))
   348  		for i, e := range entries {
   349  			ret.Entries[i] = e.String()
   350  		}
   351  		context.Respond(ret)
   352  	case *types.AddEntryParams:
   353  		rawEntry := types.RawEntry{PeerId: msg.PeerID, Address:msg.Address, Cidr:msg.Cidr}
   354  		entry, err := types.NewListEntry(rawEntry)
   355  		if err != nil {
   356  			context.Respond(types.RPCErrInvalidArgument)
   357  		}
   358  		pms.lm.AddEntry(entry)
   359  		context.Respond(nil)
   360  		go pms.applyNewBLEntry(entry)
   361  	case *types.RmEntryParams:
   362  		context.Respond(pms.lm.RemoveEntry(int(msg.Index)))
   363  	default:
   364  		pms.Logger.Debug().Interface("msg", msg) // TODO: temporal code for resolve compile error
   365  	}
   366  }
   367  
   368  func (pms *PeerMapService) onPing(s types.Stream) {
   369  	peerID := s.Conn().RemotePeer()
   370  	pms.Logger.Debug().Str(p2putil.LogPeerID, peerID.String()).Msg("Received ping from polaris (maybe)")
   371  
   372  	rw := v030.NewV030ReadWriter(bufio.NewReader(s), bufio.NewWriter(s), nil)
   373  	defer s.Close()
   374  
   375  	req, err := rw.ReadMsg()
   376  	if err != nil {
   377  		return
   378  	}
   379  	pingReq := &types.Ping{}
   380  	err = p2putil.UnmarshalMessageBody(req.Payload(), pingReq)
   381  	if err != nil {
   382  		return
   383  	}
   384  	// TODO: check if sender is known polaris or peer and it not, ban or write to blacklist .
   385  	pingResp := &types.Ping{}
   386  	msgID := p2pcommon.NewMsgID()
   387  	respMsg, err := createV030Message(msgID, req.ID(), p2pcommon.PingResponse, pingResp)
   388  	if err != nil {
   389  		return
   390  	}
   391  
   392  	err = rw.WriteMsg(respMsg)
   393  	if err != nil {
   394  		return
   395  	}
   396  
   397  }
   398  
   399  func (pms *PeerMapService) getCurrentPeers(param *CurrentListMsg) *types.PolarisPeerList {
   400  	retSize := int(param.Size)
   401  	totalSize := len(pms.peerRegistry)
   402  	listSize := calcMinimum(retSize, totalSize, ResponseMaxPeerLimit)
   403  	pList := make([]*types.PolarisPeer, listSize)
   404  	addSize := 0
   405  	pms.rwmutex.Lock()
   406  	pms.rwmutex.Unlock()
   407  	for _, rPeer := range pms.peerRegistry {
   408  		pList[addSize] = &types.PolarisPeer{Address: &rPeer.addr, Connected: rPeer.connected.UnixNano(), LastCheck: rPeer.lastCheck().UnixNano(), Verion:rPeer.meta.Version}
   409  		addSize++
   410  		if addSize >= listSize {
   411  			break
   412  		}
   413  	}
   414  	if addSize < listSize {
   415  		pList = pList[:addSize]
   416  	}
   417  	result := &types.PolarisPeerList{Peers: pList, HasNext: false, Total: uint32(totalSize)}
   418  	return result
   419  }
   420  
   421  func (pms *PeerMapService) getWhiteList(param *WhiteListMsg) *types.PolarisPeerList {
   422  	// TODO implement!
   423  	return &types.PolarisPeerList{}
   424  }
   425  
   426  func (pms *PeerMapService) getBlackList(param *BlackListMsg) *types.PolarisPeerList {
   427  	// TODO implement!
   428  	return &types.PolarisPeerList{}
   429  }
   430  
   431  func calcMinimum(values ...int) int {
   432  	min := math.MaxUint32
   433  	for _, val := range values {
   434  		if min > val {
   435  			min = val
   436  		}
   437  	}
   438  	return min
   439  }
   440  func (pms *PeerMapService) getPeerCheckers() []peerChecker {
   441  	pms.rwmutex.Lock()
   442  	pms.rwmutex.Unlock()
   443  	newSlice := make([]peerChecker, 0, len(pms.peerRegistry))
   444  	for _, rPeer := range pms.peerRegistry {
   445  		newSlice = append(newSlice, rPeer)
   446  	}
   447  	return newSlice
   448  }
   449  
   450  func makeGoAwayMsg(message string) (p2pcommon.Message, error) {
   451  	awayMsg := &types.GoAwayNotice{Message: message}
   452  	msgID := p2pcommon.NewMsgID()
   453  	return createV030Message(msgID, EmptyMsgID, p2pcommon.GoAway, awayMsg)
   454  }
   455  
   456  // send notice message and then disconnect. this routine should only run in RunPeer go routine
   457  func (pms *PeerMapService) SendGoAwayMsg(message string, wt p2pcommon.MsgReadWriter) error {
   458  	msg, err := makeGoAwayMsg(message)
   459  	if err != nil {
   460  		return err
   461  	}
   462  	wt.WriteMsg(msg)
   463  	time.Sleep(MsgSendDelay)
   464  	return nil
   465  }
   466  
   467  //
   468  func (pms *PeerMapService) checkChain(chainIDBytes []byte) (bool, error) {
   469  	chainID := types.NewChainID()
   470  	if err := chainID.Read(chainIDBytes); err != nil {
   471  		return false, err
   472  	}
   473  	sameChain := pms.ntc.ChainID().Equals(chainID)
   474  	if !sameChain && pms.Logger.IsDebugEnabled() {
   475  		pms.Logger.Debug().Str("chain_id", chainID.ToJSON()).Msg("chainid differ")
   476  
   477  	}
   478  	return sameChain, nil
   479  }
   480  
   481  func (pms *PeerMapService) checkConnectness(meta p2pcommon.PeerMeta) bool {
   482  	if !pms.allowPrivate && !network.IsExternalAddr(meta.IPAddress) {
   483  		pms.Logger.Debug().Str("peer_meta", p2putil.ShortMetaForm(meta)).Msg("peer is private address")
   484  		return false
   485  	}
   486  	tempState := &peerState{PeerMapService: pms, meta: meta, addr: meta.ToPeerAddress(), lCheckTime: time.Now(), temporary: true}
   487  	_, err := tempState.checkConnect(PolarisPingTTL)
   488  	if err != nil {
   489  		pms.Logger.Debug().Err(err).Str(p2putil.LogPeerID, p2putil.ShortForm(meta.ID)).Msg("Ping check was failed.")
   490  		return false
   491  	} else {
   492  		pms.Logger.Debug().Str(p2putil.LogPeerID, p2putil.ShortForm(meta.ID)).Msg("Ping check is succeeded.")
   493  		return true
   494  	}
   495  }