cwtch/app/plugins/networkCheck.go

157 lines
5.4 KiB
Go
Raw Permalink Normal View History

package plugins
import (
"cwtch.im/cwtch/event"
2019-11-04 20:18:59 +00:00
"cwtch.im/cwtch/protocol/connections"
2022-07-05 22:31:44 +00:00
"cwtch.im/cwtch/utils"
"git.openprivacy.ca/openprivacy/connectivity"
"git.openprivacy.ca/openprivacy/log"
2019-11-08 00:39:27 +00:00
"sync"
"time"
)
2020-11-20 23:39:03 +00:00
// NetworkCheckError is a status for when the NetworkCheck Plugin has had an error making an out going connection indicating it may be offline
const NetworkCheckError = "Error"
// NetworkCheckSuccess is a status for when the NetworkCheck Plugin has had a successful message from a peer, indicating it is online right now
const NetworkCheckSuccess = "Success"
const NetworkCheckPeriod = time.Minute
// networkCheck is a convenience plugin for testing high level availability of onion services
type networkCheck struct {
2022-03-22 19:45:29 +00:00
bus event.Manager
queue event.Queue
onion string
acn connectivity.ACN
breakChan chan bool
running bool
offline bool
offlineLock sync.Mutex
}
// NewNetworkCheck returns a Plugin that when started will attempt various network tests
func NewNetworkCheck(onion string, bus event.Manager, acn connectivity.ACN) Plugin {
2022-03-22 19:45:29 +00:00
nc := &networkCheck{onion: onion, bus: bus, acn: acn, queue: event.NewQueue(), breakChan: make(chan bool, 1)}
return nc
}
func (nc *networkCheck) Start() {
go nc.run()
}
2023-04-17 19:05:02 +00:00
func (nc *networkCheck) Id() PluginID {
return NETWORKCHECK
}
func (nc *networkCheck) run() {
nc.running = true
2020-11-20 23:39:03 +00:00
nc.offline = true
nc.bus.Subscribe(event.ProtocolEngineStartListen, nc.queue)
nc.bus.Subscribe(event.NewMessageFromPeer, nc.queue)
nc.bus.Subscribe(event.PeerAcknowledgement, nc.queue)
nc.bus.Subscribe(event.EncryptedGroupMessage, nc.queue)
2019-11-04 20:18:59 +00:00
nc.bus.Subscribe(event.PeerStateChange, nc.queue)
nc.bus.Subscribe(event.ServerStateChange, nc.queue)
2020-11-20 23:39:03 +00:00
nc.bus.Subscribe(event.NewGetValMessageFromPeer, nc.queue)
nc.bus.Subscribe(event.NewRetValMessageFromPeer, nc.queue)
var lastMessageReceived = time.Now()
for {
select {
case <-nc.breakChan:
nc.running = false
return
case e := <-nc.queue.OutChan():
switch e.EventType {
// On receipt of a Listen request for an onion service we will add the onion to our list
// and then we will wait a minute and check the connection for the first time (the onion should be up)
// under normal operating circumstances
case event.ProtocolEngineStartListen:
if nc.onion == (e.Data[event.Onion]) {
log.Debugf("initiating connection check for %v", e.Data[event.Onion])
2021-06-02 18:34:57 +00:00
if time.Since(lastMessageReceived) > time.Minute {
nc.selfTest()
}
} else {
log.Errorf("network check plugin received an event for a different profile than it was started with. Internal wiring is probably wrong.")
2020-11-20 23:39:03 +00:00
}
2019-11-04 20:18:59 +00:00
case event.PeerStateChange:
fallthrough
case event.ServerStateChange:
// if we successfully connect / authenticated to a remote server / peer then we obviously have internet
connectionState := e.Data[event.ConnectionState]
2019-11-08 00:39:27 +00:00
nc.offlineLock.Lock()
2020-11-20 23:39:03 +00:00
if connectionState == connections.ConnectionStateName[connections.AUTHENTICATED] || connectionState == connections.ConnectionStateName[connections.CONNECTED] {
2019-11-04 20:18:59 +00:00
lastMessageReceived = time.Now()
2020-11-20 23:39:03 +00:00
if nc.offline {
nc.bus.Publish(event.NewEvent(event.NetworkStatus, map[event.Field]string{event.Error: "", event.Status: NetworkCheckSuccess}))
nc.offline = false
}
2019-11-04 20:18:59 +00:00
}
2019-11-08 00:39:27 +00:00
nc.offlineLock.Unlock()
default:
// if we receive either an encrypted group message or a peer acknowledgement we can assume the network
// is up and running (our onion service might still not be available, but we would aim to detect that
// through other actions
// we reset out timer
lastMessageReceived = time.Now()
2019-11-08 00:39:27 +00:00
nc.offlineLock.Lock()
2019-11-04 20:18:59 +00:00
if nc.offline {
2020-11-20 23:39:03 +00:00
nc.bus.Publish(event.NewEvent(event.NetworkStatus, map[event.Field]string{event.Error: "", event.Status: NetworkCheckSuccess}))
2019-11-04 20:18:59 +00:00
nc.offline = false
}
2019-11-08 00:39:27 +00:00
nc.offlineLock.Unlock()
}
case <-time.After(NetworkCheckPeriod):
// if we haven't received an action in the last minute...kick off a set of testing
2021-06-02 18:34:57 +00:00
if time.Since(lastMessageReceived) > time.Minute {
2020-11-20 23:39:03 +00:00
nc.selfTest()
lastMessageReceived = time.Now()
}
}
}
}
func (nc *networkCheck) Shutdown() {
if nc.running {
nc.queue.Shutdown()
log.Debugf("shutting down network status plugin")
nc.breakChan <- true
}
}
2020-11-20 23:39:03 +00:00
func (nc *networkCheck) selfTest() {
go nc.checkConnection(nc.onion)
2020-11-20 23:39:03 +00:00
}
func (nc *networkCheck) checkConnection(onion string) {
2023-04-17 19:05:02 +00:00
progress, _ := nc.acn.GetBootstrapStatus()
if progress != 100 {
2020-11-20 23:39:03 +00:00
return
}
// we want to definitively time these actions out faster than tor will, because these onions should definitely be
// online
2022-07-05 22:31:44 +00:00
ClientTimeout := utils.TimeoutPolicy(time.Second * 60)
err := ClientTimeout.ExecuteAction(func() error {
conn, _, err := nc.acn.Open(onion)
if err == nil {
2023-04-17 19:05:02 +00:00
_ = conn.Close()
}
return err
})
2019-11-08 00:39:27 +00:00
nc.offlineLock.Lock()
defer nc.offlineLock.Unlock()
// regardless of the outcome we want to report a status to let anyone who might care know that we did do a check
if err != nil {
2020-11-20 23:39:03 +00:00
log.Debugf("publishing network error for %v -- %v\n", onion, err)
nc.bus.Publish(event.NewEvent(event.NetworkStatus, map[event.Field]string{event.Onion: onion, event.Error: err.Error(), event.Status: NetworkCheckError}))
nc.offline = true
} else {
log.Debugf("publishing network success for %v", onion)
2020-11-20 23:39:03 +00:00
nc.bus.Publish(event.NewEvent(event.NetworkStatus, map[event.Field]string{event.Onion: onion, event.Error: "", event.Status: NetworkCheckSuccess}))
nc.offline = false
}
}