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