mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2024-11-27 12:05:23 +00:00
commit
6432eaa9f5
@ -292,11 +292,10 @@ exit:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) shutdown() {
|
func (n *node) shutdown() {
|
||||||
n.core.Stop()
|
|
||||||
n.admin.Stop()
|
n.admin.Stop()
|
||||||
n.multicast.Stop()
|
n.multicast.Stop()
|
||||||
n.tuntap.Stop()
|
n.tuntap.Stop()
|
||||||
os.Exit(0)
|
n.core.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool {
|
func (n *node) sessionFirewall(pubkey *crypto.BoxPubKey, initiator bool) bool {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/Arceliar/phony"
|
||||||
"github.com/gologme/log"
|
"github.com/gologme/log"
|
||||||
|
|
||||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
@ -19,14 +20,17 @@ import (
|
|||||||
// configured multicast interface, Yggdrasil will attempt to peer with that node
|
// configured multicast interface, Yggdrasil will attempt to peer with that node
|
||||||
// automatically.
|
// automatically.
|
||||||
type Multicast struct {
|
type Multicast struct {
|
||||||
core *yggdrasil.Core
|
phony.Inbox
|
||||||
config *config.NodeState
|
core *yggdrasil.Core
|
||||||
log *log.Logger
|
config *config.NodeState
|
||||||
sock *ipv6.PacketConn
|
log *log.Logger
|
||||||
groupAddr string
|
sock *ipv6.PacketConn
|
||||||
listeners map[string]*yggdrasil.TcpListener
|
groupAddr string
|
||||||
listenPort uint16
|
listeners map[string]*yggdrasil.TcpListener
|
||||||
isOpen bool
|
listenPort uint16
|
||||||
|
isOpen bool
|
||||||
|
announcer *time.Timer
|
||||||
|
platformhandler *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init prepares the multicast interface for use.
|
// Init prepares the multicast interface for use.
|
||||||
@ -63,9 +67,9 @@ func (m *Multicast) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.isOpen = true
|
m.isOpen = true
|
||||||
go m.multicastStarted()
|
|
||||||
go m.listen()
|
go m.listen()
|
||||||
go m.announce()
|
m.Act(m, m.multicastStarted)
|
||||||
|
m.Act(m, m.announce)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -73,6 +77,12 @@ func (m *Multicast) Start() error {
|
|||||||
// Stop is not implemented for multicast yet.
|
// Stop is not implemented for multicast yet.
|
||||||
func (m *Multicast) Stop() error {
|
func (m *Multicast) Stop() error {
|
||||||
m.isOpen = false
|
m.isOpen = false
|
||||||
|
if m.announcer != nil {
|
||||||
|
m.announcer.Stop()
|
||||||
|
}
|
||||||
|
if m.platformhandler != nil {
|
||||||
|
m.platformhandler.Stop()
|
||||||
|
}
|
||||||
m.sock.Close()
|
m.sock.Close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -136,108 +146,108 @@ func (m *Multicast) announce() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
for {
|
interfaces := m.Interfaces()
|
||||||
interfaces := m.Interfaces()
|
// There might be interfaces that we configured listeners for but are no
|
||||||
// There might be interfaces that we configured listeners for but are no
|
// longer up - if that's the case then we should stop the listeners
|
||||||
// longer up - if that's the case then we should stop the listeners
|
for name, listener := range m.listeners {
|
||||||
for name, listener := range m.listeners {
|
// Prepare our stop function!
|
||||||
// Prepare our stop function!
|
stop := func() {
|
||||||
stop := func() {
|
listener.Stop()
|
||||||
listener.Stop <- true
|
delete(m.listeners, name)
|
||||||
delete(m.listeners, name)
|
m.log.Debugln("No longer multicasting on", name)
|
||||||
m.log.Debugln("No longer multicasting on", name)
|
}
|
||||||
}
|
// If the interface is no longer visible on the system then stop the
|
||||||
// If the interface is no longer visible on the system then stop the
|
// listener, as another one will be started further down
|
||||||
// listener, as another one will be started further down
|
if _, ok := interfaces[name]; !ok {
|
||||||
if _, ok := interfaces[name]; !ok {
|
stop()
|
||||||
stop()
|
continue
|
||||||
continue
|
}
|
||||||
}
|
// It's possible that the link-local listener address has changed so if
|
||||||
// It's possible that the link-local listener address has changed so if
|
// that is the case then we should clean up the interface listener
|
||||||
// that is the case then we should clean up the interface listener
|
found := false
|
||||||
found := false
|
listenaddr, err := net.ResolveTCPAddr("tcp6", listener.Listener.Addr().String())
|
||||||
listenaddr, err := net.ResolveTCPAddr("tcp6", listener.Listener.Addr().String())
|
if err != nil {
|
||||||
if err != nil {
|
stop()
|
||||||
stop()
|
continue
|
||||||
continue
|
}
|
||||||
}
|
// Find the interface that matches the listener
|
||||||
// Find the interface that matches the listener
|
if intf, err := net.InterfaceByName(name); err == nil {
|
||||||
if intf, err := net.InterfaceByName(name); err == nil {
|
if addrs, err := intf.Addrs(); err == nil {
|
||||||
if addrs, err := intf.Addrs(); err == nil {
|
// Loop through the addresses attached to that listener and see if any
|
||||||
// Loop through the addresses attached to that listener and see if any
|
// of them match the current address of the listener
|
||||||
// of them match the current address of the listener
|
for _, addr := range addrs {
|
||||||
for _, addr := range addrs {
|
if ip, _, err := net.ParseCIDR(addr.String()); err == nil {
|
||||||
if ip, _, err := net.ParseCIDR(addr.String()); err == nil {
|
// Does the interface address match our listener address?
|
||||||
// Does the interface address match our listener address?
|
if ip.Equal(listenaddr.IP) {
|
||||||
if ip.Equal(listenaddr.IP) {
|
found = true
|
||||||
found = true
|
break
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the address has not been found on the adapter then we should stop
|
|
||||||
// and clean up the TCP listener. A new one will be created below if a
|
|
||||||
// suitable link-local address is found
|
|
||||||
if !found {
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Now that we have a list of valid interfaces from the operating system,
|
// If the address has not been found on the adapter then we should stop
|
||||||
// we can start checking if we can send multicasts on them
|
// and clean up the TCP listener. A new one will be created below if a
|
||||||
for _, iface := range interfaces {
|
// suitable link-local address is found
|
||||||
// Find interface addresses
|
if !found {
|
||||||
addrs, err := iface.Addrs()
|
stop()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
for _, addr := range addrs {
|
|
||||||
addrIP, _, _ := net.ParseCIDR(addr.String())
|
|
||||||
// Ignore IPv4 addresses
|
|
||||||
if addrIP.To4() != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Ignore non-link-local addresses
|
|
||||||
if !addrIP.IsLinkLocalUnicast() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Join the multicast group
|
|
||||||
m.sock.JoinGroup(&iface, groupAddr)
|
|
||||||
// Try and see if we already have a TCP listener for this interface
|
|
||||||
var listener *yggdrasil.TcpListener
|
|
||||||
if l, ok := m.listeners[iface.Name]; !ok || l.Listener == nil {
|
|
||||||
// No listener was found - let's create one
|
|
||||||
listenaddr := fmt.Sprintf("[%s%%%s]:%d", addrIP, iface.Name, m.listenPort)
|
|
||||||
if li, err := m.core.ListenTCP(listenaddr); err == nil {
|
|
||||||
m.log.Debugln("Started multicasting on", iface.Name)
|
|
||||||
// Store the listener so that we can stop it later if needed
|
|
||||||
m.listeners[iface.Name] = li
|
|
||||||
listener = li
|
|
||||||
} else {
|
|
||||||
m.log.Warnln("Not multicasting on", iface.Name, "due to error:", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// An existing listener was found
|
|
||||||
listener = m.listeners[iface.Name]
|
|
||||||
}
|
|
||||||
// Make sure nothing above failed for some reason
|
|
||||||
if listener == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Get the listener details and construct the multicast beacon
|
|
||||||
lladdr := listener.Listener.Addr().String()
|
|
||||||
if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil {
|
|
||||||
a.Zone = ""
|
|
||||||
destAddr.Zone = iface.Name
|
|
||||||
msg := []byte(a.String())
|
|
||||||
m.sock.WriteTo(msg, nil, destAddr)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
time.Sleep(time.Second * 15)
|
|
||||||
}
|
}
|
||||||
|
// Now that we have a list of valid interfaces from the operating system,
|
||||||
|
// we can start checking if we can send multicasts on them
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
// Find interface addresses
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
addrIP, _, _ := net.ParseCIDR(addr.String())
|
||||||
|
// Ignore IPv4 addresses
|
||||||
|
if addrIP.To4() != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Ignore non-link-local addresses
|
||||||
|
if !addrIP.IsLinkLocalUnicast() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Join the multicast group
|
||||||
|
m.sock.JoinGroup(&iface, groupAddr)
|
||||||
|
// Try and see if we already have a TCP listener for this interface
|
||||||
|
var listener *yggdrasil.TcpListener
|
||||||
|
if l, ok := m.listeners[iface.Name]; !ok || l.Listener == nil {
|
||||||
|
// No listener was found - let's create one
|
||||||
|
listenaddr := fmt.Sprintf("[%s%%%s]:%d", addrIP, iface.Name, m.listenPort)
|
||||||
|
if li, err := m.core.ListenTCP(listenaddr); err == nil {
|
||||||
|
m.log.Debugln("Started multicasting on", iface.Name)
|
||||||
|
// Store the listener so that we can stop it later if needed
|
||||||
|
m.listeners[iface.Name] = li
|
||||||
|
listener = li
|
||||||
|
} else {
|
||||||
|
m.log.Warnln("Not multicasting on", iface.Name, "due to error:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// An existing listener was found
|
||||||
|
listener = m.listeners[iface.Name]
|
||||||
|
}
|
||||||
|
// Make sure nothing above failed for some reason
|
||||||
|
if listener == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Get the listener details and construct the multicast beacon
|
||||||
|
lladdr := listener.Listener.Addr().String()
|
||||||
|
if a, err := net.ResolveTCPAddr("tcp6", lladdr); err == nil {
|
||||||
|
a.Zone = ""
|
||||||
|
destAddr.Zone = iface.Name
|
||||||
|
msg := []byte(a.String())
|
||||||
|
m.sock.WriteTo(msg, nil, destAddr)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.announcer = time.AfterFunc(time.Second*15, func() {
|
||||||
|
m.Act(m, m.announce)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) listen() {
|
func (m *Multicast) listen() {
|
||||||
|
@ -32,21 +32,16 @@ import (
|
|||||||
var awdlGoroutineStarted bool
|
var awdlGoroutineStarted bool
|
||||||
|
|
||||||
func (m *Multicast) multicastStarted() {
|
func (m *Multicast) multicastStarted() {
|
||||||
if awdlGoroutineStarted {
|
C.StopAWDLBrowsing()
|
||||||
return
|
for intf := range m.Interfaces() {
|
||||||
}
|
if intf == "awdl0" {
|
||||||
awdlGoroutineStarted = true
|
C.StartAWDLBrowsing()
|
||||||
for {
|
break
|
||||||
C.StopAWDLBrowsing()
|
|
||||||
for intf := range m.Interfaces() {
|
|
||||||
if intf == "awdl0" {
|
|
||||||
m.log.Infoln("Multicast discovery is using AWDL discovery")
|
|
||||||
C.StartAWDLBrowsing()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
time.Sleep(time.Minute)
|
|
||||||
}
|
}
|
||||||
|
m.platformhandler = time.AfterFunc(time.Minute, func() {
|
||||||
|
m.Act(m, m.multicastStarted)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error {
|
func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error {
|
||||||
|
@ -102,11 +102,12 @@ func (c *Conn) search() error {
|
|||||||
if sinfo != nil {
|
if sinfo != nil {
|
||||||
// Need to clean up to avoid a session leak
|
// Need to clean up to avoid a session leak
|
||||||
sinfo.cancel.Cancel(nil)
|
sinfo.cancel.Cancel(nil)
|
||||||
|
sinfo.sessions.removeSession(sinfo)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if sinfo != nil {
|
if sinfo != nil {
|
||||||
// Finish initializing the session
|
// Finish initializing the session
|
||||||
sinfo.conn = c
|
sinfo.setConn(nil, c)
|
||||||
}
|
}
|
||||||
c.session = sinfo
|
c.session = sinfo
|
||||||
err = e
|
err = e
|
||||||
|
@ -96,7 +96,7 @@ func (c *Core) _addPeerLoop() {
|
|||||||
if err := c.CallPeer(peer, intf); err != nil {
|
if err := c.CallPeer(peer, intf); err != nil {
|
||||||
c.log.Errorln("Failed to add peer:", err)
|
c.log.Errorln("Failed to add peer:", err)
|
||||||
}
|
}
|
||||||
}(peer, "")
|
}(peer, "") // TODO: this should be acted and not in a goroutine?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add peers from the InterfacePeers section
|
// Add peers from the InterfacePeers section
|
||||||
@ -106,13 +106,12 @@ func (c *Core) _addPeerLoop() {
|
|||||||
if err := c.CallPeer(peer, intf); err != nil {
|
if err := c.CallPeer(peer, intf); err != nil {
|
||||||
c.log.Errorln("Failed to add peer:", err)
|
c.log.Errorln("Failed to add peer:", err)
|
||||||
}
|
}
|
||||||
}(peer, intf)
|
}(peer, intf) // TODO: this should be acted and not in a goroutine?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sit for a while
|
|
||||||
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
|
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
|
||||||
c.Act(c, c._addPeerLoop)
|
c.Act(nil, c._addPeerLoop)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,5 +191,12 @@ func (c *Core) Stop() {
|
|||||||
// This function is unsafe and should only be ran by the core actor.
|
// This function is unsafe and should only be ran by the core actor.
|
||||||
func (c *Core) _stop() {
|
func (c *Core) _stop() {
|
||||||
c.log.Infoln("Stopping...")
|
c.log.Infoln("Stopping...")
|
||||||
c.addPeerTimer.Stop()
|
if c.addPeerTimer != nil {
|
||||||
|
c.addPeerTimer.Stop()
|
||||||
|
}
|
||||||
|
c.link.stop()
|
||||||
|
for _, peer := range c.GetPeers() {
|
||||||
|
c.DisconnectPeer(peer.Port)
|
||||||
|
}
|
||||||
|
c.log.Infoln("Stopped")
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ type link struct {
|
|||||||
mutex sync.RWMutex // protects interfaces below
|
mutex sync.RWMutex // protects interfaces below
|
||||||
interfaces map[linkInfo]*linkInterface
|
interfaces map[linkInfo]*linkInterface
|
||||||
tcp tcp // TCP interface support
|
tcp tcp // TCP interface support
|
||||||
|
stopped chan struct{}
|
||||||
// TODO timeout (to remove from switch), read from config.ReadTimeout
|
// TODO timeout (to remove from switch), read from config.ReadTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +71,7 @@ func (l *link) init(c *Core) error {
|
|||||||
l.mutex.Lock()
|
l.mutex.Lock()
|
||||||
l.interfaces = make(map[linkInfo]*linkInterface)
|
l.interfaces = make(map[linkInfo]*linkInterface)
|
||||||
l.mutex.Unlock()
|
l.mutex.Unlock()
|
||||||
|
l.stopped = make(chan struct{})
|
||||||
|
|
||||||
if err := l.tcp.init(l); err != nil {
|
if err := l.tcp.init(l); err != nil {
|
||||||
c.log.Errorln("Failed to start TCP interface")
|
c.log.Errorln("Failed to start TCP interface")
|
||||||
@ -86,7 +88,7 @@ func (l *link) reconfigure() {
|
|||||||
func (l *link) call(uri string, sintf string) error {
|
func (l *link) call(uri string, sintf string) error {
|
||||||
u, err := url.Parse(uri)
|
u, err := url.Parse(uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("peer %s is not correctly formatted", uri)
|
return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err)
|
||||||
}
|
}
|
||||||
pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/")
|
pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/")
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
@ -134,6 +136,14 @@ func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote st
|
|||||||
return &intf, nil
|
return &intf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *link) stop() error {
|
||||||
|
close(l.stopped)
|
||||||
|
if err := l.tcp.stop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (intf *linkInterface) handler() error {
|
func (intf *linkInterface) handler() error {
|
||||||
// TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later
|
// TODO split some of this into shorter functions, so it's easier to read, and for the FIXME duplicate peer issue mentioned later
|
||||||
myLinkPub, myLinkPriv := crypto.NewBoxKeys()
|
myLinkPub, myLinkPriv := crypto.NewBoxKeys()
|
||||||
@ -224,7 +234,18 @@ func (intf *linkInterface) handler() error {
|
|||||||
go intf.peer.start()
|
go intf.peer.start()
|
||||||
intf.reader.Act(nil, intf.reader._read)
|
intf.reader.Act(nil, intf.reader._read)
|
||||||
// Wait for the reader to finish
|
// Wait for the reader to finish
|
||||||
|
// TODO find a way to do this without keeping live goroutines around
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-intf.link.stopped:
|
||||||
|
intf.msgIO.close()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
}()
|
||||||
err = <-intf.reader.err
|
err = <-intf.reader.err
|
||||||
|
// TODO don't report an error if it's just a 'use of closed network connection'
|
||||||
if err != nil {
|
if err != nil {
|
||||||
intf.link.core.log.Infof("Disconnected %s: %s, source %s; error: %s",
|
intf.link.core.log.Infof("Disconnected %s: %s, source %s; error: %s",
|
||||||
strings.ToUpper(intf.info.linkType), themString, intf.info.local, err)
|
strings.ToUpper(intf.info.linkType), themString, intf.info.local, err)
|
||||||
|
@ -414,8 +414,10 @@ func (sinfo *sessionInfo) _updateNonce(theirNonce *crypto.BoxNonce) {
|
|||||||
|
|
||||||
// Resets all sessions to an uninitialized state.
|
// Resets all sessions to an uninitialized state.
|
||||||
// Called after coord changes, so attemtps to use a session will trigger a new ping and notify the remote end of the coord change.
|
// Called after coord changes, so attemtps to use a session will trigger a new ping and notify the remote end of the coord change.
|
||||||
|
// Only call this from the router actor.
|
||||||
func (ss *sessions) reset() {
|
func (ss *sessions) reset() {
|
||||||
for _, sinfo := range ss.sinfos {
|
for _, _sinfo := range ss.sinfos {
|
||||||
|
sinfo := _sinfo // So we can safely put it in a closure
|
||||||
sinfo.Act(ss.router, func() {
|
sinfo.Act(ss.router, func() {
|
||||||
sinfo.reset = true
|
sinfo.reset = true
|
||||||
})
|
})
|
||||||
@ -470,7 +472,8 @@ func (sinfo *sessionInfo) _recvPacket(p *wire_trafficPacket) {
|
|||||||
callback := func() {
|
callback := func() {
|
||||||
util.PutBytes(p.Payload)
|
util.PutBytes(p.Payload)
|
||||||
if !isOK || k != sinfo.sharedSesKey || !sinfo._nonceIsOK(&p.Nonce) {
|
if !isOK || k != sinfo.sharedSesKey || !sinfo._nonceIsOK(&p.Nonce) {
|
||||||
// Either we failed to decrypt, or the session was updated, or we received this packet in the mean time
|
// Either we failed to decrypt, or the session was updated, or we
|
||||||
|
// received this packet in the mean time
|
||||||
util.PutBytes(bs)
|
util.PutBytes(bs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ const tcp_ping_interval = (default_timeout * 2 / 3)
|
|||||||
// The TCP listener and information about active TCP connections, to avoid duplication.
|
// The TCP listener and information about active TCP connections, to avoid duplication.
|
||||||
type tcp struct {
|
type tcp struct {
|
||||||
link *link
|
link *link
|
||||||
|
waitgroup sync.WaitGroup
|
||||||
mutex sync.Mutex // Protecting the below
|
mutex sync.Mutex // Protecting the below
|
||||||
listeners map[string]*TcpListener
|
listeners map[string]*TcpListener
|
||||||
calls map[string]struct{}
|
calls map[string]struct{}
|
||||||
@ -46,7 +47,12 @@ type tcp struct {
|
|||||||
// multicast interfaces.
|
// multicast interfaces.
|
||||||
type TcpListener struct {
|
type TcpListener struct {
|
||||||
Listener net.Listener
|
Listener net.Listener
|
||||||
Stop chan bool
|
stop chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TcpListener) Stop() {
|
||||||
|
defer func() { recover() }()
|
||||||
|
close(l.stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function to set additional options for specific connection types.
|
// Wrapper function to set additional options for specific connection types.
|
||||||
@ -96,6 +102,16 @@ func (t *tcp) init(l *link) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tcp) stop() error {
|
||||||
|
t.mutex.Lock()
|
||||||
|
for _, listener := range t.listeners {
|
||||||
|
listener.Stop()
|
||||||
|
}
|
||||||
|
t.mutex.Unlock()
|
||||||
|
t.waitgroup.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tcp) reconfigure() {
|
func (t *tcp) reconfigure() {
|
||||||
t.link.core.config.Mutex.RLock()
|
t.link.core.config.Mutex.RLock()
|
||||||
added := util.Difference(t.link.core.config.Current.Listen, t.link.core.config.Previous.Listen)
|
added := util.Difference(t.link.core.config.Current.Listen, t.link.core.config.Previous.Listen)
|
||||||
@ -121,7 +137,7 @@ func (t *tcp) reconfigure() {
|
|||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
if listener, ok := t.listeners[d[6:]]; ok {
|
if listener, ok := t.listeners[d[6:]]; ok {
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
listener.Stop <- true
|
listener.Stop()
|
||||||
t.link.core.log.Infoln("Stopped TCP listener:", d[6:])
|
t.link.core.log.Infoln("Stopped TCP listener:", d[6:])
|
||||||
} else {
|
} else {
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
@ -141,8 +157,9 @@ func (t *tcp) listen(listenaddr string) (*TcpListener, error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
l := TcpListener{
|
l := TcpListener{
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
Stop: make(chan bool),
|
stop: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
t.waitgroup.Add(1)
|
||||||
go t.listener(&l, listenaddr)
|
go t.listener(&l, listenaddr)
|
||||||
return &l, nil
|
return &l, nil
|
||||||
}
|
}
|
||||||
@ -152,6 +169,7 @@ func (t *tcp) listen(listenaddr string) (*TcpListener, error) {
|
|||||||
|
|
||||||
// Runs the listener, which spawns off goroutines for incoming connections.
|
// Runs the listener, which spawns off goroutines for incoming connections.
|
||||||
func (t *tcp) listener(l *TcpListener, listenaddr string) {
|
func (t *tcp) listener(l *TcpListener, listenaddr string) {
|
||||||
|
defer t.waitgroup.Done()
|
||||||
if l == nil {
|
if l == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -166,7 +184,6 @@ func (t *tcp) listener(l *TcpListener, listenaddr string) {
|
|||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
}
|
}
|
||||||
// And here we go!
|
// And here we go!
|
||||||
accepted := make(chan bool)
|
|
||||||
defer func() {
|
defer func() {
|
||||||
t.link.core.log.Infoln("Stopping TCP listener on:", l.Listener.Addr().String())
|
t.link.core.log.Infoln("Stopping TCP listener on:", l.Listener.Addr().String())
|
||||||
l.Listener.Close()
|
l.Listener.Close()
|
||||||
@ -175,36 +192,29 @@ func (t *tcp) listener(l *TcpListener, listenaddr string) {
|
|||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
}()
|
}()
|
||||||
t.link.core.log.Infoln("Listening for TCP on:", l.Listener.Addr().String())
|
t.link.core.log.Infoln("Listening for TCP on:", l.Listener.Addr().String())
|
||||||
|
go func() {
|
||||||
|
<-l.stop
|
||||||
|
l.Listener.Close()
|
||||||
|
}()
|
||||||
|
defer l.Stop()
|
||||||
for {
|
for {
|
||||||
var sock net.Conn
|
sock, err := l.Listener.Accept()
|
||||||
var err error
|
if err != nil {
|
||||||
// Listen in a separate goroutine, as that way it does not block us from
|
t.link.core.log.Errorln("Failed to accept connection:", err)
|
||||||
// receiving "stop" events
|
|
||||||
go func() {
|
|
||||||
sock, err = l.Listener.Accept()
|
|
||||||
accepted <- true
|
|
||||||
}()
|
|
||||||
// Wait for either an accepted connection, or a message telling us to stop
|
|
||||||
// the TCP listener
|
|
||||||
select {
|
|
||||||
case <-accepted:
|
|
||||||
if err != nil {
|
|
||||||
t.link.core.log.Errorln("Failed to accept connection:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go t.handler(sock, true, nil)
|
|
||||||
case <-l.Stop:
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.waitgroup.Add(1)
|
||||||
|
go t.handler(sock, true, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if we already are calling this address
|
// Checks if we already are calling this address
|
||||||
func (t *tcp) isAlreadyCalling(saddr string) bool {
|
func (t *tcp) startCalling(saddr string) bool {
|
||||||
t.mutex.Lock()
|
t.mutex.Lock()
|
||||||
defer t.mutex.Unlock()
|
defer t.mutex.Unlock()
|
||||||
_, isIn := t.calls[saddr]
|
_, isIn := t.calls[saddr]
|
||||||
return isIn
|
t.calls[saddr] = struct{}{}
|
||||||
|
return !isIn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a connection already exists.
|
// Checks if a connection already exists.
|
||||||
@ -218,12 +228,9 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) {
|
|||||||
if sintf != "" {
|
if sintf != "" {
|
||||||
callname = fmt.Sprintf("%s/%s", saddr, sintf)
|
callname = fmt.Sprintf("%s/%s", saddr, sintf)
|
||||||
}
|
}
|
||||||
if t.isAlreadyCalling(callname) {
|
if !t.startCalling(callname) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.mutex.Lock()
|
|
||||||
t.calls[callname] = struct{}{}
|
|
||||||
t.mutex.Unlock()
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// Block new calls for a little while, to mitigate livelock scenarios
|
// Block new calls for a little while, to mitigate livelock scenarios
|
||||||
time.Sleep(default_timeout)
|
time.Sleep(default_timeout)
|
||||||
@ -252,6 +259,7 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.waitgroup.Add(1)
|
||||||
t.handler(conn, false, saddr)
|
t.handler(conn, false, saddr)
|
||||||
} else {
|
} else {
|
||||||
dst, err := net.ResolveTCPAddr("tcp", saddr)
|
dst, err := net.ResolveTCPAddr("tcp", saddr)
|
||||||
@ -316,12 +324,14 @@ func (t *tcp) call(saddr string, options interface{}, sintf string) {
|
|||||||
t.link.core.log.Debugln("Failed to dial TCP:", err)
|
t.link.core.log.Debugln("Failed to dial TCP:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.waitgroup.Add(1)
|
||||||
t.handler(conn, false, nil)
|
t.handler(conn, false, nil)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}) {
|
func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}) {
|
||||||
|
defer t.waitgroup.Done() // Happens after sock.close
|
||||||
defer sock.Close()
|
defer sock.Close()
|
||||||
t.setExtraOptions(sock)
|
t.setExtraOptions(sock)
|
||||||
stream := stream{}
|
stream := stream{}
|
||||||
|
Loading…
Reference in New Issue
Block a user