From d3398bd074b4b89b62ff3d95d3f93b6b3443fec8 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Mon, 10 Jan 2022 12:51:21 -0800 Subject: [PATCH 1/8] Allow Custom Tor Config --- .gitignore | 2 ++ testing/tor/torrc | 4 ---- tor/torProvider.go | 6 +++--- tor/torProvider_test.go | 5 ++++- tor/torUtils_test.go | 8 ++++++++ tor/torrcBuilder.go | 12 ++++++++++++ 6 files changed, 29 insertions(+), 8 deletions(-) delete mode 100644 testing/tor/torrc diff --git a/.gitignore b/.gitignore index 9e85f5a..a0787cc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ tor/tor/ vendor/ *.cover.out tmp/ +testing/tor/torrc +testing/tor/* \ No newline at end of file diff --git a/testing/tor/torrc b/testing/tor/torrc deleted file mode 100644 index 459089c..0000000 --- a/testing/tor/torrc +++ /dev/null @@ -1,4 +0,0 @@ -SOCKSPort 9050 -ControlPort 9051 -# "examplehashedpassword" - used for testing -HashedControlPassword 16:C15305F97789414B601259E3EC5E76B8E55FC56A9F562B713F3D2BA257 diff --git a/tor/torProvider.go b/tor/torProvider.go index d45effc..8510d8d 100644 --- a/tor/torProvider.go +++ b/tor/torProvider.go @@ -55,9 +55,9 @@ func (l *logWriter) Write(p []byte) (int, error) { } type onionListenService struct { - lock sync.Mutex - os *tor.OnionService - tp *torProvider + lock sync.Mutex + os *tor.OnionService + tp *torProvider } type torProvider struct { diff --git a/tor/torProvider_test.go b/tor/torProvider_test.go index 7400517..e7104a1 100644 --- a/tor/torProvider_test.go +++ b/tor/torProvider_test.go @@ -3,7 +3,7 @@ package tor import ( "fmt" "git.openprivacy.ca/openprivacy/log" - "path" + path "path/filepath" "testing" ) @@ -18,6 +18,9 @@ func TestTorProvider(t *testing.T) { progChan := make(chan int) log.SetLevel(log.LevelDebug) torpath := path.Join("..", "tmp/tor") + + NewTorrc().WithControlPort(9051).WithHashedPassword("examplehashedpassword").Build(path.Join("..", "testing", "tor", "torrc")) + log.Debugf("setting tor path %v", torpath) acn, err := NewTorACNWithAuth(path.Join("../testing/"), torpath, 9051, HashedPasswordAuthenticator{"examplehashedpassword"}) if err != nil { diff --git a/tor/torUtils_test.go b/tor/torUtils_test.go index 0416c2a..2e7c60d 100644 --- a/tor/torUtils_test.go +++ b/tor/torUtils_test.go @@ -98,3 +98,11 @@ func TestGenerateTorrc(t *testing.T) { } os.Remove(path) } + +func TestPreviewTorrc(t *testing.T) { + expected := "SocksPort 9050 OnionTrafficOnly\nControlPort 9061" + torrc := NewTorrc().WithCustom([]string{"SocksPort 9050"}).WithControlPort(9061).WithOnionTrafficOnly().Preview() + if torrc != expected { + t.Fatalf("unexpected torrc generated: [%v] [%v]", expected, torrc) + } +} diff --git a/tor/torrcBuilder.go b/tor/torrcBuilder.go index 1b4ea2f..6937d7c 100644 --- a/tor/torrcBuilder.go +++ b/tor/torrcBuilder.go @@ -34,6 +34,13 @@ func (tb *TorrcBuilder) WithControlPort(port int) *TorrcBuilder { return tb } +// WithCustom clobbers the torrc builder and allows the client to set any option they want, while benefiting +// from other configuration options. +func (tb *TorrcBuilder) WithCustom(lines []string) *TorrcBuilder { + tb.lines = lines + return tb +} + // WithOnionTrafficOnly ensures that the tor process only routes tor onion traffic. func (tb *TorrcBuilder) WithOnionTrafficOnly() *TorrcBuilder { for i, line := range tb.lines { @@ -61,6 +68,11 @@ func (tb *TorrcBuilder) Build(path string) error { return ioutil.WriteFile(path, []byte(strings.Join(tb.lines, "\n")), 0600) } +// Preview provides a string representation of the torrc file without writing it to a file location. +func (tb *TorrcBuilder) Preview() string { + return strings.Join(tb.lines, "\n") +} + // GenerateHashedPassword calculates a hash in the same way tha tor --hash-password does // this function takes a salt as input which is not great from an api-misuse perspective, but // we make it private. -- 2.25.1 From dbc3d675ec0237035d964fe65c9c7079787150f1 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Mon, 10 Jan 2022 12:56:59 -0800 Subject: [PATCH 2/8] Update quality to staticcheck --- testing/quality.sh | 14 ++++++++------ tor/torProvider.go | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/testing/quality.sh b/testing/quality.sh index 0599b46..5262cc6 100755 --- a/testing/quality.sh +++ b/testing/quality.sh @@ -1,9 +1,7 @@ #!/bin/sh echo "Checking code quality (you want to see no output here)" - -echo "Formatting:" -gofmt -s -w -l . +echo "" echo "Vetting:" go list ./... | xargs go vet @@ -11,12 +9,16 @@ go list ./... | xargs go vet echo "" echo "Linting:" -go list ./... | xargs golint +staticcheck ./... + + +echo "Time to format" +gofmt -l -s -w . # ineffassign (https://github.com/gordonklaus/ineffassign) echo "Checking for ineffectual assignment of errors (unchecked errors...)" ineffassign . -# misspell (https://github.com/client9/misspell) +# misspell (https://github.com/client9/misspell/cmd/misspell) echo "Checking for misspelled words..." -go list ./... | xargs misspell +misspell . | grep -v "testing/" | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea" diff --git a/tor/torProvider.go b/tor/torProvider.go index 8510d8d..8d4b954 100644 --- a/tor/torProvider.go +++ b/tor/torProvider.go @@ -410,7 +410,7 @@ func startTor(appDirectory string, bundledTorPath string, controlPort int, authe tp.t = t } else { log.Debugln("Could not find a viable tor running or to run") - return nil, fmt.Errorf("Could not connect to or start Tor that met requirements (min Tor version 0.3.5.x)") + return nil, fmt.Errorf("could not connect to or start Tor that met requirements (min Tor version 0.3.5.x)") } err = tp.checkVersion() -- 2.25.1 From 9169018529b8abad422f2265b66ad8e4cd0d72da Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 11 Jan 2022 12:11:45 -0800 Subject: [PATCH 3/8] ProxyACN and ErrorACN for nicer ACN Management --- acn.go | 2 ++ error_acn.go | 49 +++++++++++++++++++++++++++++++++ localProvider.go | 4 +++ proxy_acn.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++ tor/torProvider.go | 6 +++++ 5 files changed, 128 insertions(+) create mode 100644 error_acn.go create mode 100644 proxy_acn.go diff --git a/acn.go b/acn.go index 3da4f52..af5f497 100644 --- a/acn.go +++ b/acn.go @@ -58,5 +58,7 @@ type ACN interface { // GetVersion returns a string of what the ACN returns when asked for a version GetVersion() string + Callback() func(int, string) + Close() } diff --git a/error_acn.go b/error_acn.go new file mode 100644 index 0000000..425356c --- /dev/null +++ b/error_acn.go @@ -0,0 +1,49 @@ +package connectivity + +import ( + "fmt" + "net" +) + +// ErrorACN - a status-callback safe errored ACN. Use this when ACN construction goes wrong +// and you need a safe substitute that can later be replaced with a working ACN without impacting calling clients. +type ErrorACN struct { + statusCallbackCache func(int, string) +} + +func (e ErrorACN) Callback() func(int, string) { + return e.statusCallbackCache +} + +func (e ErrorACN) GetBootstrapStatus() (int, string) { + return -1, "error initializing tor" +} + +func (e ErrorACN) WaitTillBootstrapped() { +} + +func (e *ErrorACN) SetStatusCallback(callback func(int, string)) { + e.statusCallbackCache = callback +} + +func (e ErrorACN) Restart() { +} + +func (e ErrorACN) Open(hostname string) (net.Conn, string, error) { + return nil, "", fmt.Errorf("error initializing tor") +} + +func (e ErrorACN) Listen(identity PrivateKey, port int) (ListenService, error) { + return nil, fmt.Errorf("error initializing tor") +} + +func (e ErrorACN) GetPID() (int, error) { + return -1, fmt.Errorf("error initializing tor") +} + +func (e ErrorACN) GetVersion() string { + return "Error Initializing Tor" +} + +func (e ErrorACN) Close() { +} diff --git a/localProvider.go b/localProvider.go index 5a08baa..6c66499 100644 --- a/localProvider.go +++ b/localProvider.go @@ -18,6 +18,10 @@ func NewLocalACN() ACN { return &localProvider{} } +func (lp *localProvider) Callback() func(int, string) { + return func(int, string) {} +} + func (ls *localListenService) AddressFull() string { return ls.l.Addr().String() } diff --git a/proxy_acn.go b/proxy_acn.go new file mode 100644 index 0000000..0a3958a --- /dev/null +++ b/proxy_acn.go @@ -0,0 +1,67 @@ +package connectivity + +import ( + "net" +) + +// ProxyACN because there is rarely a problem that can't be solved by another layer of indirection. +// ACN is a core resource that many parts of a system may need access too e.g. all clients and servers need an instance +// and a UI may also need status information and a configuration interface. +// We want to allow configuration and replacement of an ACN without impacting the API of all downstream systems - introducing +// ProxyACN - a wrapper around an ACN that allows safe replacement of a running ACN that is transparent to callers. +type ProxyACN struct { + acn ACN +} + +func NewProxyACN(acn ACN) ProxyACN { + return ProxyACN{ + acn: acn, + } +} + +// ReplaceACN closes down the current ACN and replaces it with a new ACN. +func (p *ProxyACN) ReplaceACN(acn ACN) { + p.acn.Close() + acn.SetStatusCallback(p.acn.Callback()) + p.acn = acn +} + +func (p *ProxyACN) GetBootstrapStatus() (int, string) { + return p.acn.GetBootstrapStatus() +} + +func (p *ProxyACN) WaitTillBootstrapped() { + p.acn.WaitTillBootstrapped() +} + +func (p *ProxyACN) SetStatusCallback(callback func(int, string)) { + p.acn.SetStatusCallback(callback) +} + +func (p *ProxyACN) Restart() { + p.acn.Restart() +} + +func (p *ProxyACN) Open(hostname string) (net.Conn, string, error) { + return p.acn.Open(hostname) +} + +func (p *ProxyACN) Listen(identity PrivateKey, port int) (ListenService, error) { + return p.acn.Listen(identity, port) +} + +func (p *ProxyACN) GetPID() (int, error) { + return p.acn.GetPID() +} + +func (p *ProxyACN) GetVersion() string { + return p.acn.GetVersion() +} + +func (p *ProxyACN) Close() { + p.acn.Close() +} + +func (p *ProxyACN) Callback() func(int, string) { + return p.acn.Callback() +} diff --git a/tor/torProvider.go b/tor/torProvider.go index 8d4b954..2a3e182 100644 --- a/tor/torProvider.go +++ b/tor/torProvider.go @@ -303,6 +303,12 @@ func (tp *torProvider) SetStatusCallback(callback func(int, string)) { tp.statusCallback = callback } +func (tp *torProvider) Callback() func(int, string) { + tp.lock.Lock() + defer tp.lock.Unlock() + return tp.statusCallback +} + func (tp *torProvider) callStatusCallback(prog int, status string) { tp.lock.Lock() if tp.statusCallback != nil { -- 2.25.1 From 13045e3d984674be0f9bdec478049eb4a8415648 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 11 Jan 2022 12:17:26 -0800 Subject: [PATCH 4/8] Use staticcheck in drone --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 5a4de5c..2c4a583 100644 --- a/.drone.yml +++ b/.drone.yml @@ -10,10 +10,11 @@ pipeline: event: [ push, pull_request ] image: golang commands: + - go install honnef.co/go/tools/cmd/staticcheck@latest - wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor -P tmp/ - chmod a+x tmp/tor - go mod download - - go get -u golang.org/x/lint/golint + - go get -u quality: when: repo: openprivacy/connectivity @@ -21,8 +22,7 @@ pipeline: event: [ push, pull_request ] image: golang commands: - - go list ./... | xargs go vet - - go list ./... | xargs golint -set_exit_status + - staticcheck ./... units-tests: when: repo: openprivacy/connectivity -- 2.25.1 From 5d4cf85d26ea54d850c262f8e5f526c5743698b8 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 11 Jan 2022 15:17:54 -0800 Subject: [PATCH 5/8] Add Lock to ReplaceACN. Minor Drone fixups --- .drone.yml | 1 - .gitignore | 1 - proxy_acn.go | 7 +++++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 2c4a583..c55c25d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -14,7 +14,6 @@ pipeline: - wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor -P tmp/ - chmod a+x tmp/tor - go mod download - - go get -u quality: when: repo: openprivacy/connectivity diff --git a/.gitignore b/.gitignore index a0787cc..abeeb44 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ tor/tor/ vendor/ *.cover.out tmp/ -testing/tor/torrc testing/tor/* \ No newline at end of file diff --git a/proxy_acn.go b/proxy_acn.go index 0a3958a..809f5b6 100644 --- a/proxy_acn.go +++ b/proxy_acn.go @@ -2,6 +2,7 @@ package connectivity import ( "net" + "sync" ) // ProxyACN because there is rarely a problem that can't be solved by another layer of indirection. @@ -11,6 +12,10 @@ import ( // ProxyACN - a wrapper around an ACN that allows safe replacement of a running ACN that is transparent to callers. type ProxyACN struct { acn ACN + + // All operations on the underlying acn are assumed to be thread safe, however changing the actual + // acn in ReplaceACN will lock to force an ordering of Close and Callback + lock sync.Mutex } func NewProxyACN(acn ACN) ProxyACN { @@ -21,6 +26,8 @@ func NewProxyACN(acn ACN) ProxyACN { // ReplaceACN closes down the current ACN and replaces it with a new ACN. func (p *ProxyACN) ReplaceACN(acn ACN) { + p.lock.Lock() + defer p.lock.Unlock() p.acn.Close() acn.SetStatusCallback(p.acn.Callback()) p.acn = acn -- 2.25.1 From 384d59e9ef30790c87002f94ed89b91ddec0f0f7 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 11 Jan 2022 15:41:56 -0800 Subject: [PATCH 6/8] WaitTillBootstrapped can now Error --- acn.go | 2 +- error_acn.go | 4 +++- localProvider.go | 3 ++- proxy_acn.go | 4 ++-- testing/launch_tor_integration_test.go | 6 +++++- tor/torProvider.go | 10 ++++++---- 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/acn.go b/acn.go index af5f497..9ca82df 100644 --- a/acn.go +++ b/acn.go @@ -38,7 +38,7 @@ type ACN interface { // On ACN error state it returns -2 GetBootstrapStatus() (int, string) // WaitTillBootstrapped Blocks until underlying network is bootstrapped - WaitTillBootstrapped() + WaitTillBootstrapped() error // Sets the callback function to be called when ACN status changes SetStatusCallback(callback func(int, string)) diff --git a/error_acn.go b/error_acn.go index 425356c..d9dbb2b 100644 --- a/error_acn.go +++ b/error_acn.go @@ -1,6 +1,7 @@ package connectivity import ( + "errors" "fmt" "net" ) @@ -19,7 +20,8 @@ func (e ErrorACN) GetBootstrapStatus() (int, string) { return -1, "error initializing tor" } -func (e ErrorACN) WaitTillBootstrapped() { +func (e ErrorACN) WaitTillBootstrapped() error { + return errors.New("error initializing tor") } func (e *ErrorACN) SetStatusCallback(callback func(int, string)) { diff --git a/localProvider.go b/localProvider.go index 6c66499..656d0ba 100644 --- a/localProvider.go +++ b/localProvider.go @@ -56,7 +56,8 @@ func (lp *localProvider) GetVersion() string { } // WaitTillBootstrapped Blocks until underlying network is bootstrapped -func (lp *localProvider) WaitTillBootstrapped() { +func (lp *localProvider) WaitTillBootstrapped() error { + return nil } func (lp *localProvider) Listen(identity PrivateKey, port int) (ListenService, error) { diff --git a/proxy_acn.go b/proxy_acn.go index 809f5b6..0161085 100644 --- a/proxy_acn.go +++ b/proxy_acn.go @@ -37,8 +37,8 @@ func (p *ProxyACN) GetBootstrapStatus() (int, string) { return p.acn.GetBootstrapStatus() } -func (p *ProxyACN) WaitTillBootstrapped() { - p.acn.WaitTillBootstrapped() +func (p *ProxyACN) WaitTillBootstrapped() error { + return p.acn.WaitTillBootstrapped() } func (p *ProxyACN) SetStatusCallback(callback func(int, string)) { diff --git a/testing/launch_tor_integration_test.go b/testing/launch_tor_integration_test.go index da031b1..7611c74 100644 --- a/testing/launch_tor_integration_test.go +++ b/testing/launch_tor_integration_test.go @@ -32,7 +32,11 @@ func TestLaunchTor(t *testing.T) { if err != nil { t.Fatalf("tor failed to start: %v", err) } else { - acn.WaitTillBootstrapped() + err := acn.WaitTillBootstrapped() + if err != nil { + t.Fatalf("error bootstrapping tor %v", err) + } + if pid, err := acn.GetPID(); err == nil { t.Logf("tor pid: %v", pid) } else { diff --git a/tor/torProvider.go b/tor/torProvider.go index 2a3e182..ee2c6ef 100644 --- a/tor/torProvider.go +++ b/tor/torProvider.go @@ -72,6 +72,7 @@ type torProvider struct { statusCallback func(int, string) lastRestartTime time.Time authenticator tor.Authenticator + isClosed bool } func (ols *onionListenService) AddressFull() string { @@ -157,14 +158,15 @@ func (tp *torProvider) GetVersion() string { } // WaitTillBootstrapped Blocks until underlying network is bootstrapped -func (tp *torProvider) WaitTillBootstrapped() { - for { +func (tp *torProvider) WaitTillBootstrapped() error { + for !tp.isClosed { progress, _ := tp.GetBootstrapStatus() if progress == 100 { - break + return nil } time.Sleep(100 * time.Millisecond) } + return errors.New("close called before bootstrap") } func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (connectivity.ListenService, error) { @@ -287,9 +289,9 @@ func (tp *torProvider) Close() { for _, child := range tp.childListeners { child.Close() } - tp.lock.Lock() defer tp.lock.Unlock() + tp.isClosed = true tp.breakChan <- true if tp.t != nil { tp.t.Close() -- 2.25.1 From 35247bd04499de999edf72322de56537d1343edd Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Wed, 12 Jan 2022 11:44:45 -0800 Subject: [PATCH 7/8] Clean up ACN Closing Logic --- acn.go | 3 --- tor/torProvider.go | 46 ++++++++++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/acn.go b/acn.go index 9ca82df..5afabb8 100644 --- a/acn.go +++ b/acn.go @@ -21,9 +21,6 @@ type PrivateKey interface{} // ListenService is an address that was opened with Listen() and can Accept() new connections type ListenService interface { - // AddressIdentity is the core "identity" part of an address, ex: rsjeuxzlexy4fvo75vrdtj37nrvlmvbw57n5mhypcjpzv3xkka3l4yyd - AddressIdentity() string - // AddressFull is the full network address, ex: rsjeuxzlexy4fvo75vrdtj37nrvlmvbw57n5mhypcjpzv3xkka3l4yyd.onion:9878 AddressFull() string diff --git a/tor/torProvider.go b/tor/torProvider.go index ee2c6ef..d79f268 100644 --- a/tor/torProvider.go +++ b/tor/torProvider.go @@ -81,21 +81,13 @@ func (ols *onionListenService) AddressFull() string { return ols.os.Addr().String() } -func (ols *onionListenService) AddressIdentity() string { - ols.lock.Lock() - defer ols.lock.Unlock() - return ols.os.Addr().String()[:56] -} - func (ols *onionListenService) Accept() (net.Conn, error) { return ols.os.Accept() } func (ols *onionListenService) Close() { - address := ols.AddressIdentity() ols.lock.Lock() defer ols.lock.Unlock() - ols.tp.unregisterListener(address) ols.os.Close() } @@ -157,9 +149,15 @@ func (tp *torProvider) GetVersion() string { return "No Tor" } +func (tp *torProvider) closed() bool { + tp.lock.Lock() + defer tp.lock.Unlock() + return tp.isClosed +} + // WaitTillBootstrapped Blocks until underlying network is bootstrapped func (tp *torProvider) WaitTillBootstrapped() error { - for !tp.isClosed { + for !tp.closed() { progress, _ := tp.GetBootstrapStatus() if progress == 100 { return nil @@ -170,9 +168,6 @@ func (tp *torProvider) WaitTillBootstrapped() error { } func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (connectivity.ListenService, error) { - var onion = "" - var privkey ed25519.PrivateKey - tp.lock.Lock() defer tp.lock.Unlock() @@ -180,6 +175,9 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne return nil, errors.New("tor provider closed") } + var onion string + var privkey ed25519.PrivateKey + switch pk := identity.(type) { case ed25519.PrivateKey: privkey = pk @@ -187,7 +185,11 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne switch pubk := gpubk.(type) { case ed25519.PublicKey: onion = GetTorV3Hostname(pubk) + default: + return nil, fmt.Errorf("unknown public key type %v", pubk) } + default: + return nil, fmt.Errorf("unknown private key type %v", pk) } // Hack around tor detached onions not having a more obvious resume mechanism @@ -223,7 +225,7 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne os.CloseLocalListenerOnClose = true ols := &onionListenService{os: os, tp: tp} - tp.childListeners[ols.AddressIdentity()] = ols + tp.childListeners[ols.AddressFull()] = ols return ols, nil } @@ -286,11 +288,17 @@ func (tp *torProvider) Open(hostname string) (net.Conn, string, error) { } func (tp *torProvider) Close() { - for _, child := range tp.childListeners { - child.Close() - } tp.lock.Lock() defer tp.lock.Unlock() + + // Unregister Child Listeners + for addr, child := range tp.childListeners { + child.Close() + delete(tp.childListeners, addr) + } + + // Break out of any background checks and close + // the underlying tor connection tp.isClosed = true tp.breakChan <- true if tp.t != nil { @@ -438,12 +446,6 @@ func (tp *torProvider) GetPID() (int, error) { return 0, err } -func (tp *torProvider) unregisterListener(id string) { - tp.lock.Lock() - defer tp.lock.Unlock() - delete(tp.childListeners, id) -} - func (tp *torProvider) monitorRestart() { lastBootstrapProgress := networkUnknown interval := minStatusIntervalMs -- 2.25.1 From 023d1a6e5dacbf2af8d6643c650b92c64d571a0f Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Wed, 12 Jan 2022 12:05:07 -0800 Subject: [PATCH 8/8] Avoid deadlock on double close --- tor/torProvider.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tor/torProvider.go b/tor/torProvider.go index d79f268..2fe156c 100644 --- a/tor/torProvider.go +++ b/tor/torProvider.go @@ -297,13 +297,15 @@ func (tp *torProvider) Close() { delete(tp.childListeners, addr) } - // Break out of any background checks and close - // the underlying tor connection - tp.isClosed = true - tp.breakChan <- true - if tp.t != nil { - tp.t.Close() - tp.t = nil + if !tp.isClosed { + // Break out of any background checks and close + // the underlying tor connection + tp.isClosed = true + tp.breakChan <- true + if tp.t != nil { + tp.t.Close() + tp.t = nil + } } } -- 2.25.1