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 }