Official cwtch.im peer and server implementations. https://cwtch.im
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

126 lines
4.4KB

  1. package plugins
  2. import (
  3. "cwtch.im/cwtch/event"
  4. "cwtch.im/cwtch/protocol/connections"
  5. "git.openprivacy.ca/openprivacy/connectivity"
  6. "git.openprivacy.ca/openprivacy/libricochet-go/policies"
  7. "git.openprivacy.ca/openprivacy/log"
  8. "sync"
  9. "time"
  10. )
  11. // networkCheck is a convenience plugin for testing high level availability of onion services
  12. type networkCheck struct {
  13. bus event.Manager
  14. queue event.Queue
  15. acn connectivity.ACN
  16. onionsToCheck []string
  17. breakChan chan bool
  18. running bool
  19. offline bool
  20. offlineLock sync.Mutex
  21. }
  22. // NewNetworkCheck returns a Plugin that when started will attempt various network tests
  23. func NewNetworkCheck(bus event.Manager, acn connectivity.ACN) Plugin {
  24. nc := &networkCheck{bus: bus, acn: acn, queue: event.NewQueue(), breakChan: make(chan bool, 1)}
  25. return nc
  26. }
  27. func (nc *networkCheck) Start() {
  28. go nc.run()
  29. }
  30. func (nc *networkCheck) run() {
  31. nc.running = true
  32. nc.bus.Subscribe(event.ProtocolEngineStartListen, nc.queue)
  33. nc.bus.Subscribe(event.NewMessageFromPeer, nc.queue)
  34. nc.bus.Subscribe(event.PeerAcknowledgement, nc.queue)
  35. nc.bus.Subscribe(event.EncryptedGroupMessage, nc.queue)
  36. nc.bus.Subscribe(event.PeerStateChange, nc.queue)
  37. nc.bus.Subscribe(event.ServerStateChange, nc.queue)
  38. var lastMessageReceived time.Time
  39. for {
  40. select {
  41. case <-nc.breakChan:
  42. nc.running = false
  43. return
  44. case e := <-nc.queue.OutChan():
  45. switch e.EventType {
  46. // On receipt of a Listen request for an onion service we will add the onion to our list
  47. // and then we will wait a minute and check the connection for the first time (the onion should be up)
  48. // under normal operating circumstances
  49. case event.ProtocolEngineStartListen:
  50. log.Debugf("initiating connection check for %v", e.Data[event.Onion])
  51. nc.onionsToCheck = append(nc.onionsToCheck, e.Data[event.Onion])
  52. case event.PeerStateChange:
  53. fallthrough
  54. case event.ServerStateChange:
  55. // if we successfully connect / authenticated to a remote server / peer then we obviously have internet
  56. connectionState := e.Data[event.ConnectionState]
  57. nc.offlineLock.Lock()
  58. if nc.offline && (connectionState == connections.ConnectionStateName[connections.AUTHENTICATED] || connectionState == connections.ConnectionStateName[connections.CONNECTED]) {
  59. lastMessageReceived = time.Now()
  60. nc.bus.Publish(event.NewEvent(event.NetworkStatus, map[event.Field]string{event.Error: "", event.Status: "Success"}))
  61. nc.offline = false
  62. }
  63. nc.offlineLock.Unlock()
  64. default:
  65. // if we receive either an encrypted group message or a peer acknowledgement we can assume the network
  66. // is up and running (our onion service might still not be available, but we would aim to detect that
  67. // through other actions
  68. // we reset out timer
  69. lastMessageReceived = time.Now()
  70. nc.offlineLock.Lock()
  71. if nc.offline {
  72. nc.bus.Publish(event.NewEvent(event.NetworkStatus, map[event.Field]string{event.Error: "", event.Status: "Success"}))
  73. nc.offline = false
  74. }
  75. nc.offlineLock.Unlock()
  76. }
  77. case <-time.After(tickTime):
  78. // if we haven't received an action in the last minute...kick off a set of testing
  79. if time.Now().Sub(lastMessageReceived) > time.Minute {
  80. for _, onion := range nc.onionsToCheck {
  81. go nc.checkConnection(onion)
  82. }
  83. }
  84. }
  85. }
  86. }
  87. func (nc *networkCheck) Shutdown() {
  88. if nc.running {
  89. nc.queue.Shutdown()
  90. log.Debugf("shutting down network status plugin")
  91. nc.breakChan <- true
  92. }
  93. }
  94. //
  95. func (nc *networkCheck) checkConnection(onion string) {
  96. // we want to definitively time these actions out faster than tor will, because these onions should definitely be
  97. // online
  98. ClientTimeout := policies.TimeoutPolicy(time.Second * 60)
  99. err := ClientTimeout.ExecuteAction(func() error {
  100. conn, _, err := nc.acn.Open(onion)
  101. if err == nil {
  102. conn.Close()
  103. }
  104. return err
  105. })
  106. nc.offlineLock.Lock()
  107. defer nc.offlineLock.Unlock()
  108. // regardless of the outcome we want to report a status to let anyone who might care know that we did do a check
  109. if err != nil {
  110. log.Debugf("publishing network error for %v", onion)
  111. nc.bus.Publish(event.NewEvent(event.NetworkStatus, map[event.Field]string{event.Onion: onion, event.Error: err.Error(), event.Status: "Error"}))
  112. nc.offline = false
  113. } else {
  114. log.Debugf("publishing network success for %v", onion)
  115. nc.bus.Publish(event.NewEvent(event.NetworkStatus, map[event.Field]string{event.Onion: onion, event.Error: "", event.Status: "Success"}))
  116. nc.offline = true
  117. }
  118. }