gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/gateway/peers.go (about) 1 package gateway 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "time" 8 9 "gitlab.com/NebulousLabs/fastrand" 10 "gitlab.com/NebulousLabs/ratelimit" 11 "gitlab.com/SiaPrime/SiaPrime/build" 12 "gitlab.com/SiaPrime/SiaPrime/encoding" 13 "gitlab.com/SiaPrime/SiaPrime/modules" 14 "gitlab.com/SiaPrime/SiaPrime/types" 15 ) 16 17 var ( 18 errPeerExists = errors.New("already connected to this peer") 19 errPeerRejectedConn = errors.New("peer rejected connection") 20 ) 21 22 // insufficientVersionError indicates a peer's version is insufficient. 23 type insufficientVersionError string 24 25 // Error implements the error interface for insufficientVersionError. 26 func (s insufficientVersionError) Error() string { 27 return "unacceptable version: " + string(s) 28 } 29 30 // invalidVersionError indicates a peer's version is not a valid version number. 31 type invalidVersionError string 32 33 // Error implements the error interface for invalidVersionError. 34 func (s invalidVersionError) Error() string { 35 return "invalid version: " + string(s) 36 } 37 38 type peer struct { 39 modules.Peer 40 rl *ratelimit.RateLimit 41 sess streamSession 42 } 43 44 // sessionHeader is sent after the initial version exchange. It prevents peers 45 // on different blockchains from connecting to each other, and prevents the 46 // gateway from connecting to itself. 47 type sessionHeader struct { 48 GenesisID types.BlockID 49 UniqueID gatewayID 50 NetAddress modules.NetAddress 51 } 52 53 func (p *peer) open() (modules.PeerConn, error) { 54 conn, err := p.sess.Open() 55 if err != nil { 56 return nil, err 57 } 58 // Apply the local ratelimit. 59 conn = ratelimit.NewRLConn(conn, p.rl, nil) 60 // Apply the global ratelimit. 61 conn = ratelimit.NewRLConn(conn, modules.GlobalRateLimits, nil) 62 return &peerConn{conn, p.NetAddress}, nil 63 } 64 65 func (p *peer) accept() (modules.PeerConn, error) { 66 conn, err := p.sess.Accept() 67 if err != nil { 68 return nil, err 69 } 70 return &peerConn{conn, p.NetAddress}, nil 71 } 72 73 // addPeer adds a peer to the Gateway's peer list, spawns a listener thread to 74 // handle its requests and increments the remotePeers accordingly 75 func (g *Gateway) addPeer(p *peer) { 76 g.peers[p.NetAddress] = p 77 go g.threadedListenPeer(p) 78 } 79 80 // callInitRPCs calls the rpcs that are registered to be called upon connecting 81 // to a peer. 82 func (g *Gateway) callInitRPCs(addr modules.NetAddress) { 83 for name, fn := range g.initRPCs { 84 go func(name string, fn modules.RPCFunc) { 85 if g.threads.Add() != nil { 86 return 87 } 88 defer g.threads.Done() 89 90 err := g.managedRPC(addr, name, fn) 91 if err != nil { 92 g.log.Debugf("INFO: RPC %q on peer %q failed: %v", name, addr, err) 93 } 94 }(name, fn) 95 } 96 } 97 98 // randomOutboundPeer returns a random outbound peer. 99 func (g *Gateway) randomOutboundPeer() (modules.NetAddress, error) { 100 // Get the list of outbound peers. 101 var addrs []modules.NetAddress 102 for addr, peer := range g.peers { 103 if peer.Inbound { 104 continue 105 } 106 addrs = append(addrs, addr) 107 } 108 if len(addrs) == 0 { 109 return "", errNoPeers 110 } 111 112 // Of the remaining options, select one at random. 113 return addrs[fastrand.Intn(len(addrs))], nil 114 } 115 116 // permanentListen handles incoming connection requests. If the connection is 117 // accepted, the peer will be added to the Gateway's peer list. 118 func (g *Gateway) permanentListen(closeChan chan struct{}) { 119 // Signal that the permanentListen thread has completed upon returning. 120 defer close(closeChan) 121 122 for { 123 conn, err := g.listener.Accept() 124 if err != nil { 125 g.log.Debugln("[PL] Closing permanentListen:", err) 126 return 127 } 128 129 go g.threadedAcceptConn(conn) 130 131 // Sleep after each accept. This limits the rate at which the Gateway 132 // will accept new connections. The intent here is to prevent new 133 // incoming connections from kicking out old ones before they have a 134 // chance to request additional nodes. 135 select { 136 case <-time.After(acceptInterval): 137 case <-g.threads.StopChan(): 138 return 139 } 140 } 141 } 142 143 // threadedAcceptConn adds a connecting node as a peer. 144 func (g *Gateway) threadedAcceptConn(conn net.Conn) { 145 if g.threads.Add() != nil { 146 conn.Close() 147 return 148 } 149 defer g.threads.Done() 150 conn.SetDeadline(time.Now().Add(connStdDeadline)) 151 152 addr := modules.NetAddress(conn.RemoteAddr().String()) 153 g.log.Debugf("INFO: %v wants to connect", addr) 154 155 g.mu.RLock() 156 _, exists := g.blacklist[addr.Host()] 157 g.mu.RUnlock() 158 if exists { 159 g.log.Debugf("INFO: %v was rejected. (blacklisted)", addr) 160 conn.Close() 161 return 162 } 163 remoteVersion, err := acceptVersionHandshake(conn, build.Version) 164 if err != nil { 165 g.log.Debugf("INFO: %v wanted to connect but version handshake failed: %v", addr, err) 166 conn.Close() 167 return 168 } 169 170 if err = acceptableVersion(remoteVersion); err == nil { 171 err = g.managedAcceptConnPeer(conn, remoteVersion) 172 } 173 if err != nil { 174 g.log.Debugf("INFO: %v wanted to connect, but failed: %v", addr, err) 175 conn.Close() 176 return 177 } 178 179 // Handshake successful, remove the deadline. 180 conn.SetDeadline(time.Time{}) 181 182 g.log.Debugf("INFO: accepted connection from new peer %v (v%v)", addr, remoteVersion) 183 } 184 185 // acceptableSessionHeader returns an error if remoteHeader indicates a peer 186 // that should not be connected to. 187 func acceptableSessionHeader(ourHeader, remoteHeader sessionHeader, remoteAddr string) error { 188 if remoteHeader.GenesisID != ourHeader.GenesisID { 189 return errPeerGenesisID 190 } else if remoteHeader.UniqueID == ourHeader.UniqueID { 191 return errOurAddress 192 } else if err := remoteHeader.NetAddress.IsStdValid(); err != nil { 193 return fmt.Errorf("invalid remote address: %v", err) 194 } 195 return nil 196 } 197 198 // managedAcceptConnPeer accepts connection requests from peers >= v1.3.1. 199 // The requesting peer is added as a node and a peer. The peer is only added if 200 // a nil error is returned. 201 func (g *Gateway) managedAcceptConnPeer(conn net.Conn, remoteVersion string) error { 202 g.log.Debugln("Sending sessionHeader with address", g.myAddr, g.myAddr.IsLocal()) 203 // Perform header handshake. 204 g.mu.RLock() 205 ourHeader := sessionHeader{ 206 GenesisID: types.GenesisID, 207 UniqueID: g.staticID, 208 NetAddress: g.myAddr, 209 } 210 rl := g.rl 211 g.mu.RUnlock() 212 213 remoteHeader, err := exchangeRemoteHeader(conn, ourHeader) 214 if err != nil { 215 return err 216 } 217 if err := exchangeOurHeader(conn, ourHeader); err != nil { 218 return err 219 } 220 221 // Get the remote address on which the connecting peer is listening on. 222 // This means we need to combine the incoming connections ip address with 223 // the announced open port of the peer. 224 remoteIP := modules.NetAddress(conn.RemoteAddr().String()).Host() 225 remotePort := remoteHeader.NetAddress.Port() 226 remoteAddr := modules.NetAddress(net.JoinHostPort(remoteIP, remotePort)) 227 228 // Accept the peer. 229 peer := &peer{ 230 Peer: modules.Peer{ 231 Inbound: true, 232 // NOTE: local may be true even if the supplied NetAddress is not 233 // actually reachable. 234 Local: remoteAddr.IsLocal(), 235 // Ignoring claimed IP address (which should be == to the socket address) 236 // by the host but keeping note of the port number so we can call back 237 NetAddress: remoteAddr, 238 Version: remoteVersion, 239 }, 240 rl: rl, 241 sess: newServerStream(conn, remoteVersion), 242 } 243 g.mu.Lock() 244 g.acceptPeer(peer) 245 g.mu.Unlock() 246 247 // Attempt to ping the supplied address. If successful, we will add 248 // remoteHeader.NetAddress to our node list after accepting the peer. We 249 // do this in a goroutine so that we can begin communicating with the peer 250 // immediately. 251 go func() { 252 err := g.staticPingNode(remoteAddr) 253 if err == nil { 254 g.mu.Lock() 255 g.addNode(remoteAddr) 256 g.mu.Unlock() 257 } 258 }() 259 260 return nil 261 } 262 263 // acceptPeer makes room for the peer if necessary by kicking out existing 264 // peers, then adds the peer to the peer list. 265 func (g *Gateway) acceptPeer(p *peer) { 266 // If we are not fully connected, add the peer without kicking any out. 267 if len(g.peers) < fullyConnectedThreshold { 268 g.addPeer(p) 269 return 270 } 271 272 // Select a peer to kick. Outbound peers and local peers are not 273 // available to be kicked. 274 var addrs []modules.NetAddress 275 for addr, peer := range g.peers { 276 // Do not kick outbound peers or local peers. 277 if !peer.Inbound || peer.Local { 278 continue 279 } 280 281 // Prefer kicking a peer with the same hostname. 282 if addr.Host() == p.NetAddress.Host() { 283 addrs = []modules.NetAddress{addr} 284 break 285 } 286 addrs = append(addrs, addr) 287 } 288 if len(addrs) == 0 { 289 // There is nobody suitable to kick, therefore do not kick anyone. 290 g.addPeer(p) 291 return 292 } 293 294 // Of the remaining options, select one at random. 295 kick := addrs[fastrand.Intn(len(addrs))] 296 297 g.peers[kick].sess.Close() 298 delete(g.peers, kick) 299 g.log.Printf("INFO: disconnected from %v to make room for %v\n", kick, p.NetAddress) 300 g.addPeer(p) 301 } 302 303 // acceptableVersion returns an error if the version is unacceptable. 304 func acceptableVersion(version string) error { 305 if !build.IsVersion(version) { 306 return invalidVersionError(version) 307 } 308 if build.VersionCmp(version, minimumAcceptablePeerVersion) < 0 { 309 return insufficientVersionError(version) 310 } 311 return nil 312 } 313 314 // connectVersionHandshake performs the version handshake and should be called 315 // on the side making the connection request. The remote version is only 316 // returned if err == nil. 317 func connectVersionHandshake(conn net.Conn, version string) (remoteVersion string, err error) { 318 // Send our version. 319 if err := encoding.WriteObject(conn, version); err != nil { 320 return "", fmt.Errorf("failed to write version: %v", err) 321 } 322 // Read remote version. 323 if err := encoding.ReadObject(conn, &remoteVersion, build.MaxEncodedVersionLength); err != nil { 324 return "", fmt.Errorf("failed to read remote version: %v", err) 325 } 326 // Check that their version is acceptable. 327 if remoteVersion == "reject" { 328 return "", errPeerRejectedConn 329 } 330 if err := acceptableVersion(remoteVersion); err != nil { 331 return "", err 332 } 333 return remoteVersion, nil 334 } 335 336 // acceptVersionHandshake performs the version handshake and should be 337 // called on the side accepting a connection request. The remote version is 338 // only returned if err == nil. 339 func acceptVersionHandshake(conn net.Conn, version string) (remoteVersion string, err error) { 340 // Read remote version. 341 if err := encoding.ReadObject(conn, &remoteVersion, build.MaxEncodedVersionLength); err != nil { 342 return "", fmt.Errorf("failed to read remote version: %v", err) 343 } 344 // Check that their version is acceptable. 345 if err := acceptableVersion(remoteVersion); err != nil { 346 if err := encoding.WriteObject(conn, "reject"); err != nil { 347 return "", fmt.Errorf("failed to write reject: %v", err) 348 } 349 return "", err 350 } 351 // Send our version. 352 if err := encoding.WriteObject(conn, version); err != nil { 353 return "", fmt.Errorf("failed to write version: %v", err) 354 } 355 return remoteVersion, nil 356 } 357 358 // exchangeOurHeader writes ourHeader and reads the remote's error response. 359 func exchangeOurHeader(conn net.Conn, ourHeader sessionHeader) error { 360 // Send our header. 361 if err := encoding.WriteObject(conn, ourHeader); err != nil { 362 return fmt.Errorf("failed to write header: %v", err) 363 } 364 365 // Read remote response. 366 var response string 367 if err := encoding.ReadObject(conn, &response, 100); err != nil { 368 return fmt.Errorf("failed to read header acceptance: %v", err) 369 } else if response == modules.StopResponse { 370 return errors.New("peer did not want a connection") 371 } else if response != modules.AcceptResponse { 372 return fmt.Errorf("peer rejected our header: %v", response) 373 } 374 return nil 375 } 376 377 // exchangeRemoteHeader reads the remote header and writes an error response. 378 func exchangeRemoteHeader(conn net.Conn, ourHeader sessionHeader) (sessionHeader, error) { 379 // Read remote header. 380 var remoteHeader sessionHeader 381 if err := encoding.ReadObject(conn, &remoteHeader, maxEncodedSessionHeaderSize); err != nil { 382 return sessionHeader{}, fmt.Errorf("failed to read remote header: %v", err) 383 } 384 385 // Validate remote header and write acceptance or rejection. 386 err := acceptableSessionHeader(ourHeader, remoteHeader, conn.RemoteAddr().String()) 387 if err != nil { 388 encoding.WriteObject(conn, err.Error()) // error can be ignored 389 return sessionHeader{}, fmt.Errorf("peer's header was not acceptable: %v", err) 390 } else if err := encoding.WriteObject(conn, modules.AcceptResponse); err != nil { 391 return sessionHeader{}, fmt.Errorf("failed to write header acceptance: %v", err) 392 } 393 394 return remoteHeader, nil 395 } 396 397 // managedConnectPeer connects to peers >= v1.3.1. The peer is added as a 398 // node and a peer. The peer is only added if a nil error is returned. 399 func (g *Gateway) managedConnectPeer(conn net.Conn, remoteVersion string, remoteAddr modules.NetAddress) error { 400 g.log.Debugln("Sending sessionHeader with address", g.myAddr, g.myAddr.IsLocal()) 401 // Perform header handshake. 402 g.mu.RLock() 403 ourHeader := sessionHeader{ 404 GenesisID: types.GenesisID, 405 UniqueID: g.staticID, 406 NetAddress: g.myAddr, 407 } 408 g.mu.RUnlock() 409 410 if err := exchangeOurHeader(conn, ourHeader); err != nil { 411 return err 412 } else if _, err := exchangeRemoteHeader(conn, ourHeader); err != nil { 413 return err 414 } 415 return nil 416 } 417 418 // managedConnect establishes a persistent connection to a peer, and adds it to 419 // the Gateway's peer list. 420 func (g *Gateway) managedConnect(addr modules.NetAddress) error { 421 // Perform verification on the input address. 422 g.mu.RLock() 423 gaddr := g.myAddr 424 g.mu.RUnlock() 425 if addr == gaddr { 426 return errors.New("can't connect to our own address") 427 } 428 if err := addr.IsStdValid(); err != nil { 429 return errors.New("can't connect to invalid address") 430 } 431 if net.ParseIP(addr.Host()) == nil { 432 return errors.New("address must be an IP address") 433 } 434 if _, exists := g.blacklist[addr.Host()]; exists { 435 return errors.New("can't connect to blacklisted address") 436 } 437 g.mu.RLock() 438 _, exists := g.peers[addr] 439 g.mu.RUnlock() 440 if exists { 441 return errPeerExists 442 } 443 444 // Dial the peer and perform peer initialization. 445 conn, err := g.staticDial(addr) 446 if err != nil { 447 return err 448 } 449 450 // Perform peer initialization. 451 remoteVersion, err := connectVersionHandshake(conn, build.Version) 452 if err != nil { 453 conn.Close() 454 return err 455 } 456 457 if err = acceptableVersion(remoteVersion); err == nil { 458 err = g.managedConnectPeer(conn, remoteVersion, addr) 459 } 460 if err != nil { 461 conn.Close() 462 return err 463 } 464 465 // Connection successful, clear the timeout as to maintain a persistent 466 // connection to this peer. 467 conn.SetDeadline(time.Time{}) 468 469 // Add the peer. 470 g.mu.Lock() 471 defer g.mu.Unlock() 472 473 g.addPeer(&peer{ 474 Peer: modules.Peer{ 475 Inbound: false, 476 Local: addr.IsLocal(), 477 NetAddress: addr, 478 Version: remoteVersion, 479 }, 480 rl: g.rl, 481 sess: newClientStream(conn, remoteVersion), 482 }) 483 g.addNode(addr) 484 g.nodes[addr].WasOutboundPeer = true 485 486 if err := g.saveSyncNodes(); err != nil { 487 g.log.Println("ERROR: Unable to save new outbound peer to gateway:", err) 488 } 489 490 g.log.Debugln("INFO: connected to new peer", addr) 491 492 // call initRPCs 493 g.callInitRPCs(addr) 494 495 return nil 496 } 497 498 // Connect establishes a persistent connection to a peer, and adds it to the 499 // Gateway's peer list. 500 func (g *Gateway) Connect(addr modules.NetAddress) error { 501 if err := g.threads.Add(); err != nil { 502 return err 503 } 504 defer g.threads.Done() 505 return g.managedConnect(addr) 506 } 507 508 // Disconnect terminates a connection to a peer and removes it from the 509 // Gateway's peer list. The peer's address remains in the node list. 510 func (g *Gateway) Disconnect(addr modules.NetAddress) error { 511 if err := g.threads.Add(); err != nil { 512 return err 513 } 514 defer g.threads.Done() 515 516 g.mu.RLock() 517 p, exists := g.peers[addr] 518 g.mu.RUnlock() 519 if !exists { 520 return errors.New("not connected to that node") 521 } 522 523 p.sess.Close() 524 g.mu.Lock() 525 // Peer is removed from the peer list as well as the node list, to prevent 526 // the node from being re-connected while looking for a replacement peer. 527 delete(g.peers, addr) 528 delete(g.nodes, addr) 529 g.mu.Unlock() 530 531 g.log.Println("INFO: disconnected from peer", addr) 532 return nil 533 } 534 535 // ConnectManual is a wrapper for the Connect function. It is specifically used 536 // if a user wants to connect to a node manually. This also removes the node 537 // from the blacklist. 538 func (g *Gateway) ConnectManual(addr modules.NetAddress) error { 539 g.mu.Lock() 540 var err error 541 if _, exists := g.blacklist[addr.Host()]; exists { 542 delete(g.blacklist, addr.Host()) 543 err = g.saveSync() 544 } 545 g.mu.Unlock() 546 return build.ComposeErrors(err, g.Connect(addr)) 547 } 548 549 // DisconnectManual is a wrapper for the Disconnect function. It is 550 // specifically used if a user wants to connect to a node manually. This also 551 // adds the node to the blacklist. 552 func (g *Gateway) DisconnectManual(addr modules.NetAddress) error { 553 err := g.Disconnect(addr) 554 if err == nil { 555 g.mu.Lock() 556 g.blacklist[addr.Host()] = struct{}{} 557 err = g.saveSync() 558 g.mu.Unlock() 559 } 560 return err 561 } 562 563 // Online returns true if the node is connected to the internet. During testing 564 // we always assume that the node is online 565 func (g *Gateway) Online() bool { 566 if build.Release == "dev" || build.Release == "testing" { 567 return true 568 } 569 570 g.mu.RLock() 571 defer g.mu.RUnlock() 572 for _, p := range g.peers { 573 if !p.Local { 574 return true 575 } 576 } 577 return false 578 } 579 580 // Peers returns the addresses currently connected to the Gateway. 581 func (g *Gateway) Peers() []modules.Peer { 582 g.mu.RLock() 583 defer g.mu.RUnlock() 584 var peers []modules.Peer 585 for _, p := range g.peers { 586 peers = append(peers, p.Peer) 587 } 588 return peers 589 }