package router import ( "fmt" "net" "strconv" "sync" "time" ) type Connection interface { Local() *Peer Remote() *Peer RemoteTCPAddr() string Outbound() bool Established() bool BreakTie(Connection) ConnectionTieBreak Shutdown(error) Log(args ...interface{}) } type ConnectionTieBreak int const ( TieBreakWon ConnectionTieBreak = iota TieBreakLost TieBreakTied ) var ErrConnectToSelf = fmt.Errorf("Cannot connect to ourself") type RemoteConnection struct { local *Peer remote *Peer remoteTCPAddr string outbound bool established bool } type LocalConnection struct { sync.RWMutex RemoteConnection TCPConn *net.TCPConn version byte tcpSender TCPSender remoteUDPAddr *net.UDPAddr SessionKey *[32]byte heartbeatTCP *time.Ticker Router *Router uid uint64 actionChan chan<- ConnectionAction finished <-chan struct{} // closed to signal that actorLoop has finished forwarder OverlayForwarder } type ConnectionAction func() error func NewRemoteConnection(from, to *Peer, tcpAddr string, outbound bool, established bool) *RemoteConnection { return &RemoteConnection{ local: from, remote: to, remoteTCPAddr: tcpAddr, outbound: outbound, established: established, } } func (conn *RemoteConnection) Local() *Peer { return conn.local } func (conn *RemoteConnection) Remote() *Peer { return conn.remote } func (conn *RemoteConnection) RemoteTCPAddr() string { return conn.remoteTCPAddr } func (conn *RemoteConnection) Outbound() bool { return conn.outbound } func (conn *RemoteConnection) Established() bool { return conn.established } func (conn *RemoteConnection) BreakTie(Connection) ConnectionTieBreak { return TieBreakTied } func (conn *RemoteConnection) Shutdown(error) {} func (conn *RemoteConnection) Log(args ...interface{}) { log.Println(append(append([]interface{}{}, fmt.Sprintf("->[%s|%s]:", conn.remoteTCPAddr, conn.remote)), args...)...) } func (conn *RemoteConnection) ErrorLog(args ...interface{}) { log.Errorln(append(append([]interface{}{}, fmt.Sprintf("->[%s|%s]:", conn.remoteTCPAddr, conn.remote)), args...)...) } // Does not return anything. If the connection is successful, it will // end up in the local peer's connections map. func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, udpAddr *net.UDPAddr, router *Router, acceptNewPeer bool) { if connRemote.local != router.Ourself.Peer { log.Fatal("Attempt to create local connection from a peer which is not ourself") } // NB, we're taking a copy of connRemote here. actionChan := make(chan ConnectionAction, ChannelSize) finished := make(chan struct{}) conn := &LocalConnection{ RemoteConnection: *connRemote, Router: router, TCPConn: tcpConn, remoteUDPAddr: udpAddr, uid: randUint64(), actionChan: actionChan, finished: finished} go conn.run(actionChan, finished, acceptNewPeer) } func (conn *LocalConnection) BreakTie(dupConn Connection) ConnectionTieBreak { dupConnLocal := dupConn.(*LocalConnection) // conn.uid is used as the tie breaker here, in the knowledge that // both sides will make the same decision. if conn.uid < dupConnLocal.uid { return TieBreakWon } else if dupConnLocal.uid < conn.uid { return TieBreakLost } else { return TieBreakTied } } func (conn *LocalConnection) Established() bool { conn.RLock() defer conn.RUnlock() return conn.established } // Send directly, not via the Actor. If it goes via the Actor we can // get a deadlock where LocalConnection is blocked talking to // LocalPeer and LocalPeer is blocked trying send a ProtocolMsg via // LocalConnection, and the channels are full in both directions so // nothing can proceed. func (conn *LocalConnection) SendProtocolMsg(m ProtocolMsg) { if err := conn.sendProtocolMsg(m); err != nil { conn.Shutdown(err) } } // ACTOR methods // NB: The conn.* fields are only written by the connection actor // process, which is the caller of the ConnectionAction funs. Hence we // do not need locks for reading, and only need write locks for fields // read by other processes. // Async func (conn *LocalConnection) Shutdown(err error) { // err should always be a real error, even if only io.EOF if err == nil { panic("nil error") } // Run on its own goroutine in case the channel is backed up go func() { conn.sendAction(func() error { return err }) }() } // Send an actor request to the actorLoop, but don't block if // actorLoop has exited - see http://blog.golang.org/pipelines for // pattern func (conn *LocalConnection) sendAction(action ConnectionAction) { select { case conn.actionChan <- action: case <-conn.finished: } } // ACTOR server func (conn *LocalConnection) run(actionChan <-chan ConnectionAction, finished chan<- struct{}, acceptNewPeer bool) { var err error // important to use this var and not create another one with 'err :=' defer func() { conn.shutdown(err) }() defer close(finished) conn.TCPConn.SetLinger(0) intro, err := ProtocolIntroParams{ MinVersion: conn.Router.ProtocolMinVersion, MaxVersion: ProtocolMaxVersion, Features: conn.makeFeatures(), Conn: conn.TCPConn, Password: conn.Router.Password, Outbound: conn.outbound, }.DoIntro() if err != nil { return } conn.SessionKey = intro.SessionKey conn.tcpSender = intro.Sender conn.version = intro.Version remote, err := conn.parseFeatures(intro.Features) if err != nil { return } if err = conn.registerRemote(remote, acceptNewPeer); err != nil { return } conn.Log("connection ready; using protocol version", conn.version) params := ForwarderParams{ RemotePeer: conn.remote, LocalIP: conn.TCPConn.LocalAddr().(*net.TCPAddr).IP, RemoteAddr: conn.remoteUDPAddr, ConnUID: conn.uid, Crypto: conn.forwarderCrypto(), SendControlMessage: conn.sendOverlayControlMessage, } if conn.forwarder, err = conn.Router.Overlay.MakeForwarder(params); err != nil { return } // As soon as we do AddConnection, the new connection becomes // visible to the packet routing logic. So AddConnection must // come after MakeForwarder if err = conn.Router.Ourself.AddConnection(conn); err != nil { return } // SetListener has the side-effect of telling the forwarder // that the connection is confirmed. This comes after // AddConnection, because only after that completes do we know // the connection is valid: in particular that it is not a // duplicate connection to the same peer. Sending heartbeats // on a duplicate connection can trip up crypto at the other // end, since the associated UDP packets may get decoded by // the other connection. It is also generally wasteful to // engage in any interaction with the remote on a connection // that turns out to be invalid. conn.forwarder.SetListener(ConnectionAsForwarderListener{conn}) // receiveTCP must follow also AddConnection. In the absence // of any indirect connectivity to the remote peer, the first // we hear about it (and any peers reachable from it) is // through topology gossip it sends us on the connection. We // must ensure that the connection has been added to Ourself // prior to processing any such gossip, otherwise we risk // immediately gc'ing part of that newly received portion of // the topology (though not the remote peer itself, since that // will have a positive ref count), leaving behind dangling // references to peers. Hence we must invoke AddConnection, // which is *synchronous*, first. conn.heartbeatTCP = time.NewTicker(TCPHeartbeat) go conn.receiveTCP(intro.Receiver) // AddConnection must precede actorLoop. More precisely, it // must precede shutdown, since that invokes DeleteConnection // and is invoked on termination of this entire // function. Essentially this boils down to a prohibition on // running AddConnection in a separate goroutine, at least not // without some synchronisation. Which in turn requires the // launching of the receiveTCP goroutine to precede actorLoop. err = conn.actorLoop(actionChan) } func (conn *LocalConnection) makeFeatures() map[string]string { return map[string]string{ "PeerNameFlavour": PeerNameFlavour, "Name": conn.local.Name.String(), "NickName": conn.local.NickName, "UID": fmt.Sprint(conn.local.UID), "ConnID": fmt.Sprint(conn.uid), } } type features map[string]string func (features features) MustHave(keys []string) error { for _, key := range keys { if _, ok := features[key]; !ok { return fmt.Errorf("Field %s is missing", key) } } return nil } func (features features) Get(key string) string { return features[key] } func (conn *LocalConnection) parseFeatures(features features) (*Peer, error) { if err := features.MustHave([]string{"PeerNameFlavour", "Name", "NickName", "UID", "ConnID"}); err != nil { return nil, err } remotePeerNameFlavour := features.Get("PeerNameFlavour") if remotePeerNameFlavour != PeerNameFlavour { return nil, fmt.Errorf("Peer name flavour mismatch (ours: '%s', theirs: '%s')", PeerNameFlavour, remotePeerNameFlavour) } name, err := PeerNameFromString(features.Get("Name")) if err != nil { return nil, err } nickName := features.Get("NickName") uid, err := ParsePeerUID(features.Get("UID")) if err != nil { return nil, err } remoteConnID, err := strconv.ParseUint(features.Get("ConnID"), 10, 64) if err != nil { return nil, err } conn.uid ^= remoteConnID return NewPeer(name, nickName, uid, 0), nil } func (conn *LocalConnection) registerRemote(remote *Peer, acceptNewPeer bool) error { if acceptNewPeer { conn.remote = conn.Router.Peers.FetchWithDefault(remote) } else { conn.remote = conn.Router.Peers.FetchAndAddRef(remote.Name) if conn.remote == nil { return fmt.Errorf("Found unknown remote name: %s at %s", remote.Name, conn.remoteTCPAddr) } } if conn.remote.UID != remote.UID { return fmt.Errorf("Connection appears to be with different version of a peer we already know of") } if conn.remote == conn.local { return ErrConnectToSelf } return nil } func (conn *LocalConnection) actorLoop(actionChan <-chan ConnectionAction) (err error) { for err == nil { select { case action := <-actionChan: err = action() case <-conn.heartbeatTCP.C: err = conn.sendSimpleProtocolMsg(ProtocolHeartbeat) } } return } func (conn *LocalConnection) shutdown(err error) { if conn.remote == nil { log.Errorf("->[%s] connection shutting down due to error during handshake: %v", conn.remoteTCPAddr, err) } else { conn.ErrorLog("connection shutting down due to error:", err) } if conn.TCPConn != nil { checkWarn(conn.TCPConn.Close()) } if conn.remote != nil { conn.Router.Peers.Dereference(conn.remote) conn.Router.Ourself.DeleteConnection(conn) } stopTicker(conn.heartbeatTCP) if conn.forwarder != nil { conn.forwarder.Stop() } conn.Router.ConnectionMaker.ConnectionTerminated(conn.remoteTCPAddr, err) } func (conn *LocalConnection) forwarderCrypto() *OverlayCrypto { if !conn.Router.UsingPassword() { return nil } name := conn.local.NameByte return &OverlayCrypto{ Dec: NewNaClDecryptor(conn.SessionKey, conn.outbound), Enc: NewNaClEncryptor(name, conn.SessionKey, conn.outbound, false), EncDF: NewNaClEncryptor(name, conn.SessionKey, conn.outbound, true), } } func (conn *LocalConnection) sendOverlayControlMessage(msg []byte) error { return conn.sendProtocolMsg(ProtocolMsg{ProtocolOverlayControlMsg, msg}) } type ConnectionAsForwarderListener struct{ conn *LocalConnection } func (l ConnectionAsForwarderListener) Established() { l.conn.sendAction(func() error { old := l.conn.established l.conn.Lock() l.conn.established = true l.conn.Unlock() if !old { l.conn.Router.Ourself.ConnectionEstablished(l.conn) } return nil }) } func (l ConnectionAsForwarderListener) Error(err error) { l.conn.sendAction(func() error { return err }) } // Helpers func (conn *LocalConnection) sendSimpleProtocolMsg(tag ProtocolTag) error { return conn.sendProtocolMsg(ProtocolMsg{tag: tag}) } func (conn *LocalConnection) sendProtocolMsg(m ProtocolMsg) error { return conn.tcpSender.Send(Concat([]byte{byte(m.tag)}, m.msg)) } func (conn *LocalConnection) receiveTCP(receiver TCPReceiver) { var err error for { conn.extendReadDeadline() var msg []byte if msg, err = receiver.Receive(); err != nil { break } if len(msg) < 1 { conn.Log("ignoring blank msg") continue } if err = conn.handleProtocolMsg(ProtocolTag(msg[0]), msg[1:]); err != nil { break } } conn.Shutdown(err) } func (conn *LocalConnection) handleProtocolMsg(tag ProtocolTag, payload []byte) error { switch tag { case ProtocolHeartbeat: case ProtocolOverlayControlMsg: conn.forwarder.ControlMessage(payload) case ProtocolGossipUnicast, ProtocolGossipBroadcast, ProtocolGossip: return conn.Router.handleGossip(tag, payload) default: conn.Log("ignoring unknown protocol tag:", tag) } return nil } func (conn *LocalConnection) extendReadDeadline() { conn.TCPConn.SetReadDeadline(time.Now().Add(TCPHeartbeat * 2)) } func (conn *LocalConnection) Forward(key ForwardPacketKey) FlowOp { return conn.forwarder.Forward(key) } func tickerChan(ticker *time.Ticker) <-chan time.Time { if ticker != nil { return ticker.C } return nil } func stopTicker(ticker *time.Ticker) { if ticker != nil { ticker.Stop() } }