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