// managedConnectNewPeer connects to peers >= v1.0.0. The peer is added as a // node and a peer. The peer is only added if a nil error is returned. func (g *Gateway) managedConnectNewPeer(conn net.Conn, remoteVersion string, remoteAddr modules.NetAddress) error { g.mu.RLock() port := g.port g.mu.RUnlock() // Send our dialable address to the peer so they can dial us back should we // disconnect. err := connectPortHandshake(conn, port) if err != nil { return err } // Attempt to add the peer to the node list. If the add is successful and // the address is a local address, mark the peer as a local peer. local := false err = g.managedAddUntrustedNode(remoteAddr) if err != nil && remoteAddr.IsLocal() { local = true } g.mu.Lock() defer g.mu.Unlock() g.addPeer(&peer{ Peer: modules.Peer{ Inbound: false, Local: local, NetAddress: remoteAddr, Version: remoteVersion, }, sess: muxado.Client(conn), }) return nil }
// addNode adds an address to the set of nodes on the network. func (g *Gateway) addNode(addr modules.NetAddress) error { if _, exists := g.nodes[addr]; exists { return errors.New("node already added") } else if net.ParseIP(addr.Host()) == nil { return errors.New("address is not routable: " + string(addr)) } else if net.ParseIP(addr.Host()).IsLoopback() { return errors.New("cannot add loopback address") } g.nodes[addr] = struct{}{} return nil }
// addNode adds an address to the set of nodes on the network. func (g *Gateway) addNode(addr modules.NetAddress) error { if addr == g.myAddr { return errOurAddress } else if _, exists := g.nodes[addr]; exists { return errNodeExists } else if addr.IsValid() != nil { return errors.New("address is not valid: " + string(addr)) } else if net.ParseIP(addr.Host()) == nil { return errors.New("address must be an IP address: " + string(addr)) } g.nodes[addr] = struct{}{} return nil }
// Connect establishes a persistent connection to a peer, and adds it to the // Gateway's peer list. func (g *Gateway) Connect(addr modules.NetAddress) error { if addr == g.Address() { return errors.New("can't connect to our own address") } if build.Release != "testing" && addr.IsLoopback() { return errors.New("can't connect to loopback address") } id := g.mu.RLock() _, exists := g.peers[addr] g.mu.RUnlock(id) if exists { return errors.New("peer already added") } conn, err := net.DialTimeout("tcp", string(addr), dialTimeout) if err != nil { return err } // send our version if err := encoding.WriteObject(conn, build.Version); err != nil { return err } // read version ack var remoteVersion string if err := encoding.ReadObject(conn, &remoteVersion, maxAddrLength); err != nil { return err } else if remoteVersion == "reject" { return errors.New("peer rejected connection") } // decide whether to accept this version if build.VersionCmp(remoteVersion, "0.3.3") < 0 { conn.Close() return errors.New("unacceptable version: " + remoteVersion) } g.log.Println("INFO: connected to new peer", addr) id = g.mu.Lock() g.addPeer(&peer{addr: addr, sess: muxado.Client(conn), inbound: false}) g.mu.Unlock(id) // call initRPCs id = g.mu.RLock() for name, fn := range g.initRPCs { go g.RPC(addr, name, fn) } g.mu.RUnlock(id) return nil }
// managedConnectOldPeer connects to peers < v1.0.0. The peer is added as a // node and a peer. The peer is only added if a nil error is returned. func (g *Gateway) managedConnectOldPeer(conn net.Conn, remoteVersion string, remoteAddr modules.NetAddress) error { // Attempt to add the peer to the node list. If the add is successful and // the address is a local address, mark the peer as a local peer. local := false err := g.managedAddUntrustedNode(remoteAddr) if err != nil && remoteAddr.IsLocal() { local = true } g.mu.Lock() defer g.mu.Unlock() g.addPeer(&peer{ Peer: modules.Peer{ Inbound: false, Local: local, NetAddress: remoteAddr, Version: remoteVersion, }, sess: muxado.Client(conn), }) return nil }
// Connect establishes a persistent connection to a peer, and adds it to the // Gateway's peer list. func (g *Gateway) Connect(addr modules.NetAddress) error { if err := g.threads.Add(); err != nil { return err } defer g.threads.Done() if addr == g.Address() { return errors.New("can't connect to our own address") } if err := addr.IsValid(); err != nil { return errors.New("can't connect to invalid address") } if net.ParseIP(addr.Host()) == nil { return errors.New("address must be an IP address") } g.mu.RLock() _, exists := g.peers[addr] g.mu.RUnlock() if exists { return errors.New("peer already added") } conn, err := net.DialTimeout("tcp", string(addr), dialTimeout) if err != nil { return err } remoteVersion, err := connectVersionHandshake(conn, build.Version) if err != nil { conn.Close() return err } if build.VersionCmp(remoteVersion, "1.0.0") < 0 { err = g.managedConnectOldPeer(conn, remoteVersion, addr) } else { err = g.managedConnectNewPeer(conn, remoteVersion, addr) } if err != nil { conn.Close() return err } g.log.Debugln("INFO: connected to new peer", addr) // call initRPCs g.mu.RLock() for name, fn := range g.initRPCs { go func(name string, fn modules.RPCFunc) { if g.threads.Add() != nil { return } defer g.threads.Done() err := g.RPC(addr, name, fn) if err != nil { g.log.Debugf("INFO: RPC %q on peer %q failed: %v", name, addr, err) } }(name, fn) } g.mu.RUnlock() return nil }
// managedConnect establishes a persistent connection to a peer, and adds it to // the Gateway's peer list. func (g *Gateway) managedConnect(addr modules.NetAddress) error { // Perform verification on the input address. g.mu.RLock() gaddr := g.myAddr g.mu.RUnlock() if addr == gaddr { return errors.New("can't connect to our own address") } if err := addr.IsStdValid(); err != nil { return errors.New("can't connect to invalid address") } if net.ParseIP(addr.Host()) == nil { return errors.New("address must be an IP address") } g.mu.RLock() _, exists := g.peers[addr] g.mu.RUnlock() if exists { return errPeerExists } // Dial the peer and perform peer initialization. conn, err := g.dial(addr) if err != nil { return err } // Perform peer initialization. remoteVersion, err := connectVersionHandshake(conn, build.Version) if err != nil { conn.Close() return err } if build.VersionCmp(remoteVersion, handshakeUpgradeVersion) < 0 { err = g.managedConnectOldPeer(conn, remoteVersion, addr) } else { err = g.managedConnectNewPeer(conn, remoteVersion, addr) } if err != nil { conn.Close() return err } g.log.Debugln("INFO: connected to new peer", addr) // Connection successful, clear the timeout as to maintain a persistent // connection to this peer. conn.SetDeadline(time.Time{}) // call initRPCs g.mu.RLock() for name, fn := range g.initRPCs { go func(name string, fn modules.RPCFunc) { if g.threads.Add() != nil { return } defer g.threads.Done() err := g.managedRPC(addr, name, fn) if err != nil { g.log.Debugf("INFO: RPC %q on peer %q failed: %v", name, addr, err) } }(name, fn) } g.mu.RUnlock() return nil }