torProvider now also consults network-liveness #1

Merged
sarah merged 1 commits from networkStatus into master 2020-05-08 21:25:44 +00:00
5 changed files with 83 additions and 36 deletions

4
acn.go
View File

@ -34,10 +34,12 @@ type ListenService interface {
// ACN is Anonymous Communication Network implementation wrapper that supports Open for new connections and Listen to accept connections
type ACN interface {
// GetBootstrapStatus returns an int 0-100 on the percent the bootstrapping of the underlying network is at and an optional string message
// On Network down it returns -1
// On ACN error state it returns -2
GetBootstrapStatus() (int, string)
// WaitTillBootstrapped Blocks until underlying network is bootstrapped
WaitTillBootstrapped()
// Sets the calback function to be called when ACN status changes
// Sets the callback function to be called when ACN status changes
SetStatusCallback(callback func(int, string))
// Restarts the underlying connection

0
quality.sh Normal file → Executable file
View File

0
tests.sh Normal file → Executable file
View File

View File

@ -26,6 +26,7 @@ import (
const (
minStatusIntervalMs = 200
maxStatusIntervalMs = 2000
restartCooldown = time.Second * 30
)
const (
@ -33,6 +34,12 @@ const (
CannotDialRicochetAddressError = connectivity.Error("CannotDialRicochetAddressError")
)
const (
networkUnknown int = iota
networkDown
networkUp
)
type onionListenService struct {
os *tor.OnionService
tp *torProvider
@ -47,6 +54,7 @@ type torProvider struct {
breakChan chan bool
childListeners map[string]*onionListenService
statusCallback func(int, string)
lastRestartTime time.Time
}
func (ols *onionListenService) AddressFull() string {
@ -66,14 +74,22 @@ func (ols *onionListenService) Close() {
ols.os.Close()
}
// GetBootstrapStatus returns an int -1 on error or 0-100 on the percent the bootstrapping of the underlying network is at and an optional string message
// GetBootstrapStatus returns an int 0-100 on the percent the bootstrapping of the underlying network is at and an optional string message
// returns -1 on network disconnected
// returns -2 on error
func (tp *torProvider) GetBootstrapStatus() (int, string) {
if tp.t == nil {
return -1, "error: no tor, trying to restart..."
return -2, "error: no tor, trying to restart..."
}
netStatus := tp.getNetworkStatus()
if netStatus == networkDown {
return -1, "Network Down"
}
kvs, err := tp.t.Control.GetInfo("status/bootstrap-phase")
if err != nil {
return -1, "error"
return -2, "error querrying status/bootstrap-phase"
}
progress := 0
status := ""
@ -104,6 +120,21 @@ func (tp *torProvider) WaitTillBootstrapped() {
}
}
// getNetworkStatus returns tor's beleif in the underlying network's status
func (tp *torProvider) getNetworkStatus() int {
if tp.t == nil {
return networkDown
}
val, err := tp.t.Control.GetInfo("network-liveness")
if err != nil {
return networkDown
}
if val[0].Val == "up" {
return networkUp
}
return networkDown
}
func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (connectivity.ListenService, error) {
var onion = ""
var privkey ed25519.PrivateKey
@ -154,9 +185,7 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne
}
func (tp *torProvider) Restart() {
if tp.statusCallback != nil {
tp.statusCallback(0, "rebooting")
}
tp.callStatusCallback(0, "rebooting")
tp.restart()
}
@ -199,6 +228,14 @@ func (tp *torProvider) SetStatusCallback(callback func(int, string)) {
tp.statusCallback = callback
}
func (tp *torProvider) callStatusCallback(prog int, status string) {
tp.lock.Lock()
if tp.statusCallback != nil {
tp.statusCallback(prog, status)
}
tp.lock.Unlock()
}
// NewTorACN creates/starts a Tor ACN and returns a usable ACN object
func NewTorACN(appDirectory string, bundledTorPath string) (connectivity.ACN, error) {
tp, err := startTor(appDirectory, bundledTorPath)
@ -225,7 +262,7 @@ func newHideCmd(exePath string) process.Creator {
func startTor(appDirectory string, bundledTorPath string) (*torProvider, error) {
dataDir := path.Join(appDirectory, "tor")
os.MkdirAll(dataDir, 0700)
tp := &torProvider{appDirectory: appDirectory, bundeledTorPath: bundledTorPath, childListeners: make(map[string]*onionListenService), breakChan: make(chan bool), statusCallback: nil}
tp := &torProvider{appDirectory: appDirectory, bundeledTorPath: bundledTorPath, childListeners: make(map[string]*onionListenService), breakChan: make(chan bool), statusCallback: nil, lastRestartTime: time.Now()}
// attempt connect to system tor
log.Debugf("dialing system tor control port\n")
@ -277,34 +314,38 @@ func (tp *torProvider) unregisterListener(id string) {
func (tp *torProvider) monitorRestart() {
lastBootstrapProgress := 0
lastNetworkStatus := networkUnknown
interval := minStatusIntervalMs
for {
select {
case <-time.After(time.Millisecond * time.Duration(interval)):
prog, status := tp.GetBootstrapStatus()
if prog == -1 && tp.t != nil {
tp.lock.Lock()
if tp.statusCallback != nil {
tp.statusCallback(prog, status)
}
tp.lock.Unlock()
tp.restart()
interval = minStatusIntervalMs
} else if prog != lastBootstrapProgress {
tp.lock.Lock()
if tp.statusCallback != nil {
tp.statusCallback(prog, status)
}
tp.lock.Unlock()
interval = minStatusIntervalMs
} else {
if interval < maxStatusIntervalMs {
interval *= 2
}
if interval < maxStatusIntervalMs {
interval *= 2
}
lastBootstrapProgress = prog
netStatus := tp.getNetworkStatus()
if netStatus == networkDown {
if lastNetworkStatus != netStatus {
tp.callStatusCallback(-1, "Network Down")
}
lastBootstrapProgress = 0
lastNetworkStatus = netStatus
} else {
lastNetworkStatus = networkUp
prog, status := tp.GetBootstrapStatus()
if prog == -1 && tp.t != nil {
tp.callStatusCallback(-2, status)
log.Infof("monitorRestart calling tp.restart() with prog:%v\n", prog)
tp.restart()
interval = minStatusIntervalMs
} else if prog != lastBootstrapProgress {
tp.callStatusCallback(prog, status)
interval = minStatusIntervalMs
}
lastBootstrapProgress = prog
}
case <-tp.breakChan:
return
}
@ -312,13 +353,17 @@ func (tp *torProvider) monitorRestart() {
}
func (tp *torProvider) restart() {
for _, child := range tp.childListeners {
child.Close()
}
tp.lock.Lock()
Outdated
Review

should probably defer unlock here since we return from multiple points, or otherwise reformat to remove the multiple paths

should probably defer unlock here since we return from multiple points, or otherwise reformat to remove the multiple paths
defer tp.lock.Unlock()
if time.Now().Sub(tp.lastRestartTime) < restartCooldown {
return
}
tp.lastRestartTime = time.Now()
for _, child := range tp.childListeners {
delete(tp.childListeners, child.AddressIdentity())
child.os.Close()
}
tp.t.Close()
tp.t = nil

View File

@ -48,4 +48,4 @@ func IsValidHostname(address string) bool {
}
}
return false
}
}