Compare commits

..

No commits in common. "master" and "v1.7.0" have entirely different histories.

15 changed files with 170 additions and 414 deletions

View File

@ -1,76 +1,63 @@
--- workspace:
kind: pipeline base: /go
type: docker path: src/git.openprivacy.ca/openprivacy/connectivity
name: linux-test
steps: pipeline:
- name: fetch fetch:
image: golang:1.19.1 when:
volumes: repo: openprivacy/connectivity
- name: deps branch: master
path: /go event: [ push, pull_request ]
image: golang
commands: commands:
- go install honnef.co/go/tools/cmd/staticcheck@latest - go install honnef.co/go/tools/cmd/staticcheck@latest
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor -P tmp/ - wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor -P tmp/
- chmod a+x tmp/tor - chmod a+x tmp/tor
- go mod download - go mod download
- name: quality quality:
image: golang:1.19.1 when:
volumes: repo: openprivacy/connectivity
- name: deps branch: master
path: /go event: [ push, pull_request ]
image: golang
commands: commands:
- staticcheck ./... - staticcheck ./...
- name: units-tests units-tests:
image: golang:1.19.1 when:
volumes: repo: openprivacy/connectivity
- name: deps branch: master
path: /go event: [ push, pull_request ]
image: golang
commands: commands:
- export PATH=`pwd`:$PATH - export PATH=$PATH:/go/src/git.openprivacy.ca/openprivacy/connectivity
- ./tmp/tor -f ./testing/torrc - ./tmp/tor -f ./testing/torrc
- sleep 15 - sleep 15
- sh testing/tests.sh - sh testing/tests.sh
- pkill -9 tor - pkill -9 tor
- name: integration-tests integration-tests:
image: golang:1.19.1 when:
volumes: repo: openprivacy/connectivity
- name: deps branch: master
path: /go event: [ push, pull_request ]
image: golang
commands: commands:
- export PATH=`pwd`:$PATH
- go test -race -v ./testing/launch_tor_integration_test.go - go test -race -v ./testing/launch_tor_integration_test.go
- name: notify-email notify-email:
image: drillster/drone-email image: drillster/drone-email
pull: if-not-exists
host: build.openprivacy.ca host: build.openprivacy.ca
port: 25 port: 25
skip_verify: true skip_verify: true
from: drone@openprivacy.ca from: drone@openprivacy.ca
when: when:
repo: openprivacy/connectivity
branch: master
status: [ failure ] status: [ failure ]
- name: notify-gogs notify-gogs:
image: openpriv/drone-gogs image: openpriv/drone-gogs
pull: if-not-exists
when: when:
repo: openprivacy/connectivity
branch: master
event: pull_request event: pull_request
status: [ success, changed, failure ] status: [ success, changed, failure ]
environment: secrets: [gogs_account_token]
GOGS_ACCOUNT_TOKEN: gogs_url: https://git.openprivacy.ca
from_secret: gogs_account_token
settings:
gogs_url: https://git.openprivacy.ca
volumes:
# gopath where bin and pkg lives to persist across steps
- name: deps
temp: {}
trigger:
repo: openprivacy/connectivity
branch: master
event:
- push
- pull_request
- tag

4
.gitignore vendored
View File

@ -4,6 +4,4 @@ tor/tor/
vendor/ vendor/
*.cover.out *.cover.out
tmp/ tmp/
testing/tor/* testing/tor/*
tor/data-dir*
testing/data-dir*

View File

@ -7,12 +7,6 @@ A library providing an ACN (Anonymous Communication Network
* Tor v3 Onion Services * Tor v3 Onion Services
## Environment Variables
- `TOR_LD_LIBRARY_PATH` - override the library path given to the Tor process as different from the one given to the parent process.
- `CWTCH_RESTRICT_PORTS` - forces connectivity to bind to a subset of ports `15000-15378`
- `CWTCH_BIND_EXTERNAL_WHONIX` - forces connectivity to bind to external interfaces (only supported/recommended on certain Whonix-based setups. Please open an issue if you think this should be expanded.)
## Requirements for ACN Support ## Requirements for ACN Support
* Reference an EndPoint via a string / hostname * Reference an EndPoint via a string / hostname
@ -56,4 +50,4 @@ service:
acn.Restart() acn.Restart()
and and
acn.Close() acn.Close()

9
acn.go
View File

@ -39,13 +39,6 @@ type ACN interface {
// Sets the callback function to be called when ACN status changes // Sets the callback function to be called when ACN status changes
SetStatusCallback(callback func(int, string)) SetStatusCallback(callback func(int, string))
GetStatusCallback() func(int, string)
// Sets the callback function to be called when ACN reboots to emit the version
SetVersionCallback(callback func(string))
GetVersionCallback() func(string)
// Restarts the underlying connection // Restarts the underlying connection
Restart() Restart()
@ -62,6 +55,8 @@ type ACN interface {
// GetVersion returns a string of what the ACN returns when asked for a version // GetVersion returns a string of what the ACN returns when asked for a version
GetVersion() string GetVersion() string
Callback() func(int, string)
GetInfo(onion string) (map[string]string, error) GetInfo(onion string) (map[string]string, error)
Close() Close()

View File

@ -1,72 +1,57 @@
package connectivity package connectivity
import ( import (
"errors"
"fmt"
"net" "net"
) )
const acnError = "error initializing anonymous communication network"
// ErrorACN - a status-callback safe errored ACN. Use this when ACN construction goes wrong // 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. // and you need a safe substitute that can later be replaced with a working ACN without impacting calling clients.
type ErrorACN struct { type ErrorACN struct {
acnError error statusCallbackCache func(int, string)
statusCallbackCache func(int, string)
versionCallbackCache func(string)
} }
func NewErrorACN(err error) ErrorACN { func (e ErrorACN) Callback() func(int, string) {
return ErrorACN{
acnError: err,
statusCallbackCache: func(int, string) {},
versionCallbackCache: func(string) {},
}
}
func (e *ErrorACN) GetStatusCallback() func(int, string) {
return e.statusCallbackCache return e.statusCallbackCache
} }
func (e *ErrorACN) GetVersionCallback() func(string) {
return e.versionCallbackCache
}
func (e *ErrorACN) GetInfo(addr string) (map[string]string, error) { func (e *ErrorACN) GetInfo(addr string) (map[string]string, error) {
return nil, e.acnError return nil, errors.New(acnError)
} }
func (e *ErrorACN) GetBootstrapStatus() (int, string) { func (e ErrorACN) GetBootstrapStatus() (int, string) {
return -1, e.acnError.Error() return -1, acnError
} }
func (e *ErrorACN) WaitTillBootstrapped() error { func (e ErrorACN) WaitTillBootstrapped() error {
return e.acnError return errors.New(acnError)
} }
func (e *ErrorACN) SetStatusCallback(callback func(int, string)) { func (e *ErrorACN) SetStatusCallback(callback func(int, string)) {
e.statusCallbackCache = callback e.statusCallbackCache = callback
} }
func (e *ErrorACN) SetVersionCallback(callback func(string)) { func (e ErrorACN) Restart() {
e.versionCallbackCache = callback
} }
func (e *ErrorACN) Restart() { func (e ErrorACN) Open(hostname string) (net.Conn, string, error) {
return nil, "", fmt.Errorf(acnError)
} }
func (e *ErrorACN) Open(hostname string) (net.Conn, string, error) { func (e ErrorACN) Listen(identity PrivateKey, port int) (ListenService, error) {
return nil, "", e.acnError return nil, fmt.Errorf(acnError)
} }
func (e *ErrorACN) Listen(identity PrivateKey, port int) (ListenService, error) { func (e ErrorACN) GetPID() (int, error) {
return nil, e.acnError return -1, fmt.Errorf(acnError)
} }
func (e *ErrorACN) GetPID() (int, error) { func (e ErrorACN) GetVersion() string {
return -1, e.acnError return acnError
} }
func (e *ErrorACN) GetVersion() string { func (e ErrorACN) Close() {
return e.acnError.Error()
}
func (e *ErrorACN) Close() {
// nothing to do...
} }

16
go.mod
View File

@ -1,15 +1,11 @@
module git.openprivacy.ca/openprivacy/connectivity module git.openprivacy.ca/openprivacy/connectivity
go 1.17 go 1.13
require ( require (
filippo.io/edwards25519 v1.0.0 filippo.io/edwards25519 v1.0.0-rc.1
git.openprivacy.ca/openprivacy/bine v0.0.5 git.openprivacy.ca/openprivacy/bine v0.0.4
git.openprivacy.ca/openprivacy/log v1.0.3 git.openprivacy.ca/openprivacy/log v1.0.2
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
) golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
require (
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
) )

26
go.sum
View File

@ -1,9 +1,9 @@
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.openprivacy.ca/openprivacy/bine v0.0.5 h1:DJs5gqw3SkvLSgRDvroqJxZ7F+YsbxbBRg5t0rU5gYE= git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
git.openprivacy.ca/openprivacy/bine v0.0.5/go.mod h1:fwdeq6RO08WDkV0k7HfArsjRvurVULoUQmT//iaABZM= git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0= git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw= git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -13,25 +13,23 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -18,14 +18,10 @@ func NewLocalACN() ACN {
return &localProvider{} return &localProvider{}
} }
func (lp *localProvider) GetStatusCallback() func(int, string) { func (lp *localProvider) Callback() func(int, string) {
return func(int, string) {} return func(int, string) {}
} }
func (lp *localProvider) GetVersionCallback() func(string) {
return func(string) {}
}
func (ls *localListenService) AddressFull() string { func (ls *localListenService) AddressFull() string {
return ls.l.Addr().String() return ls.l.Addr().String()
} }
@ -55,10 +51,6 @@ func (lp *localProvider) SetStatusCallback(callback func(int, string)) {
// nop // nop
} }
func (lp *localProvider) SetVersionCallback(callback func(string)) {
// nop
}
func (lp *localProvider) GetPID() (int, error) { func (lp *localProvider) GetPID() (int, error) {
return 0, nil return 0, nil
} }

View File

@ -29,8 +29,7 @@ func (p *ProxyACN) ReplaceACN(acn ACN) {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
p.acn.Close() p.acn.Close()
acn.SetStatusCallback(p.acn.GetStatusCallback()) acn.SetStatusCallback(p.acn.Callback())
acn.SetVersionCallback(p.acn.GetVersionCallback())
p.acn = acn p.acn = acn
} }
@ -50,10 +49,6 @@ func (p *ProxyACN) SetStatusCallback(callback func(int, string)) {
p.acn.SetStatusCallback(callback) p.acn.SetStatusCallback(callback)
} }
func (p *ProxyACN) SetVersionCallback(callback func(string)) {
p.acn.SetVersionCallback(callback)
}
func (p *ProxyACN) Restart() { func (p *ProxyACN) Restart() {
p.acn.Restart() p.acn.Restart()
} }
@ -78,10 +73,6 @@ func (p *ProxyACN) Close() {
p.acn.Close() p.acn.Close()
} }
func (p *ProxyACN) GetStatusCallback() func(int, string) { func (p *ProxyACN) Callback() func(int, string) {
return p.acn.GetStatusCallback() return p.acn.Callback()
}
func (p *ProxyACN) GetVersionCallback() func(string) {
return p.acn.GetVersionCallback()
} }

View File

@ -5,15 +5,12 @@ import (
"git.openprivacy.ca/openprivacy/log" "git.openprivacy.ca/openprivacy/log"
"math/rand" "math/rand"
"os" "os"
path "path/filepath" "path"
"runtime"
"runtime/pprof"
"testing" "testing"
"time" "time"
) )
func TestLaunchTor(t *testing.T) { func TestLaunchTor(t *testing.T) {
goRoutineStart := runtime.NumGoroutine()
log.SetLevel(log.LevelDebug) log.SetLevel(log.LevelDebug)
rand.Seed(int64(time.Now().Nanosecond())) rand.Seed(int64(time.Now().Nanosecond()))
@ -28,15 +25,10 @@ func TestLaunchTor(t *testing.T) {
t.Fatalf("failed to create torrc file: %v", err) t.Fatalf("failed to create torrc file: %v", err)
} }
dataDir := ""
if dataDir, err = os.MkdirTemp(path.Join("..", "testing"), "data-dir-"); err != nil {
t.Fatalf("could not create data dir")
}
// Get the current working director, clean the paths to remove relative references // Get the current working director, clean the paths to remove relative references
wd, _ := os.Getwd() wd, _ := os.Getwd()
t.Logf("Launching bundled tor at %v", path.Clean(wd+"/../tmp/tor")) t.Logf("Launching bundled tor at %v", path.Clean(wd+"/../tmp/tor"))
acn, err := tor.NewTorACNWithAuth(path.Clean(wd+"/../tmp/data"), path.Clean(wd+"/../tmp/tor"), dataDir, controlPort, tor.HashedPasswordAuthenticator{Password: password}) acn, err := tor.NewTorACNWithAuth(path.Clean(wd+"/../tmp/data"), path.Clean(wd+"/../tmp/tor"), controlPort, tor.HashedPasswordAuthenticator{Password: password})
if err != nil { if err != nil {
t.Fatalf("tor failed to start: %v", err) t.Fatalf("tor failed to start: %v", err)
} else { } else {
@ -53,15 +45,4 @@ func TestLaunchTor(t *testing.T) {
t.Log("we have bootstrapped!") t.Log("we have bootstrapped!")
acn.Close() acn.Close()
} }
time.Sleep(time.Second * 5)
goRoutineEnd := runtime.NumGoroutine()
if goRoutineEnd != goRoutineStart {
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
t.Fatalf("goroutine leak in ACN: %v %v", goRoutineStart, goRoutineEnd)
}
} }

View File

@ -1,4 +1,3 @@
//go:build !windows
// +build !windows // +build !windows
package tor package tor

View File

@ -1,4 +1,3 @@
//go:build windows
// +build windows // +build windows
package tor package tor

View File

@ -1,7 +1,6 @@
package tor package tor
import ( import (
"bytes"
"context" "context"
"errors" "errors"
"fmt" "fmt"
@ -13,11 +12,12 @@ import (
"git.openprivacy.ca/openprivacy/log" "git.openprivacy.ca/openprivacy/log"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
"io/ioutil"
"net" "net"
"net/textproto" "net/textproto"
"os" "os"
"os/exec" "os/exec"
path "path/filepath" "path"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -70,13 +70,9 @@ type torProvider struct {
breakChan chan bool breakChan chan bool
childListeners map[string]*onionListenService childListeners map[string]*onionListenService
statusCallback func(int, string) statusCallback func(int, string)
versionCallback func(string)
lastRestartTime time.Time lastRestartTime time.Time
authenticator tor.Authenticator authenticator tor.Authenticator
isClosed bool isClosed bool
dataDir string
version string
bootProgress int
} }
func (ols *onionListenService) AddressFull() string { func (ols *onionListenService) AddressFull() string {
@ -166,13 +162,9 @@ func (tp *torProvider) GetInfo(onion string) (map[string]string, error) {
return nil, err return nil, err
} }
var progRe = regexp.MustCompile("PROGRESS=([0-9]*)")
var sumRe = regexp.MustCompile("SUMMARY=\"(.*)\"$")
// GetBootstrapStatus returns an int 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 -1 on network disconnected // returns -2 on error
// returns -2 on error
func (tp *torProvider) GetBootstrapStatus() (int, string) { func (tp *torProvider) GetBootstrapStatus() (int, string) {
tp.lock.Lock() tp.lock.Lock()
defer tp.lock.Unlock() defer tp.lock.Unlock()
@ -198,6 +190,8 @@ func (tp *torProvider) GetBootstrapStatus() (int, string) {
status := "" status := ""
if len(kvs) > 0 { if len(kvs) > 0 {
progRe := regexp.MustCompile("PROGRESS=([0-9]*)")
sumRe := regexp.MustCompile("SUMMARY=\"(.*)\"$")
if progMatches := progRe.FindStringSubmatch(kvs[0].Val); len(progMatches) > 1 { if progMatches := progRe.FindStringSubmatch(kvs[0].Val); len(progMatches) > 1 {
progress, _ = strconv.Atoi(progMatches[1]) progress, _ = strconv.Atoi(progMatches[1])
@ -207,14 +201,23 @@ func (tp *torProvider) GetBootstrapStatus() (int, string) {
status = statusMatches[1] status = statusMatches[1]
} }
} }
tp.bootProgress = progress
return progress, status return progress, status
} }
func (tp *torProvider) GetVersion() string { func (tp *torProvider) GetVersion() string {
tp.lock.Lock() tp.lock.Lock()
defer tp.lock.Unlock() defer tp.lock.Unlock()
return tp.version
if tp.t == nil {
return "No Tor"
}
pinfo, err := tp.t.Control.ProtocolInfo()
if err == nil {
return pinfo.TorVersion
}
return "No Tor"
} }
func (tp *torProvider) closed() bool { func (tp *torProvider) closed() bool {
@ -268,28 +271,7 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne
localport += 1024 localport += 1024
} }
var localListener net.Listener localListener, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(localport))
var err error
if cwtchRestrictPorts := os.Getenv("CWTCH_RESTRICT_PORTS"); strings.ToLower(cwtchRestrictPorts) == "true" {
// for whonix like systems we tightly restrict possible listen...
// pick a random port between 15000 and 15378
// cwtch = 63 *77 *74* 63* 68 = 1537844616
log.Infof("using restricted ports, CWTCH_RESTRICT_PORTS=true");
localport = 15000 + (localport % 378)
}
if bindExternal := os.Getenv("CWTCH_BIND_EXTERNAL_WHONIX"); strings.ToLower(bindExternal) == "true" {
if _, ferr := os.Stat("/usr/share/anon-ws-base-files/workstation"); !os.IsNotExist(ferr) {
log.Infof("WARNING: binding to external interfaces. This is potentially unsafe outside of a containerized environment.");
localListener, err = net.Listen("tcp", "0.0.0.0:"+strconv.Itoa(localport))
} else {
log.Errorf("CWTCH_BIND_EXTERNAL_WHONIX flag set, but /usr/share/anon-ws-base-files/workstation does not exist. Defaulting to binding to local ports");
localListener, err = net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(localport))
}
} else {
localListener, err = net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(localport))
}
if err != nil { if err != nil {
return nil, err return nil, err
@ -311,8 +293,6 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne
return nil, err return nil, err
} }
// We need to set os.ID here, otherwise os.Close() may not shut down the onion service properly...
os.ID = onion
os.CloseLocalListenerOnClose = true os.CloseLocalListenerOnClose = true
ols := &onionListenService{os: os, tp: tp} ols := &onionListenService{os: os, tp: tp}
@ -324,12 +304,12 @@ func (tp *torProvider) Restart() {
log.Debugf("launching restart...") log.Debugf("launching restart...")
tp.lock.Lock() tp.lock.Lock()
defer tp.lock.Unlock()
log.Debugf("checking last restart time") log.Debugf("checking last restart time")
if time.Since(tp.lastRestartTime) < restartCooldown { if time.Since(tp.lastRestartTime) < restartCooldown {
tp.lock.Unlock()
return return
} }
tp.lock.Unlock()
go tp.restart() go tp.restart()
} }
@ -342,23 +322,17 @@ func (tp *torProvider) restart() {
// preserve status callback after shutdown // preserve status callback after shutdown
statusCallback := tp.statusCallback statusCallback := tp.statusCallback
versionCallback := tp.versionCallback
tp.t = nil tp.t = nil
log.Debugf("Restarting Tor Process") log.Debugf("Restarting Tor Process")
newTp, err := startTor(tp.appDirectory, tp.bundeledTorPath, tp.dataDir, tp.controlPort, tp.authenticator) newTp, err := startTor(tp.appDirectory, tp.bundeledTorPath, tp.controlPort, tp.authenticator)
if err == nil { if err == nil {
// we need to reassign tor, dialer and callback which will have changed by swapping out // we need to reassign tor, dialer and callback which will have changed by swapping out
// the underlying connection. // the underlying connection.
tp.t = newTp.t tp.t = newTp.t
tp.dialer = newTp.dialer tp.dialer = newTp.dialer
tp.statusCallback = statusCallback tp.statusCallback = statusCallback
tp.versionCallback = versionCallback
if tp.versionCallback != nil {
tp.versionCallback(tp.version)
}
tp.lastRestartTime = time.Now() tp.lastRestartTime = time.Now()
tp.isClosed = false
go tp.monitorRestart() go tp.monitorRestart()
} else { } else {
log.Errorf("Error restarting Tor process: %v", err) log.Errorf("Error restarting Tor process: %v", err)
@ -372,10 +346,6 @@ func (tp *torProvider) Open(hostname string) (net.Conn, string, error) {
tp.lock.Unlock() tp.lock.Unlock()
return nil, hostname, errors.New("tor is offline") return nil, hostname, errors.New("tor is offline")
} }
if tp.bootProgress != 100 {
tp.lock.Unlock()
return nil, hostname, fmt.Errorf("tor not online, bootstrap progress only %v", tp.bootProgress)
}
tp.lock.Unlock() tp.lock.Unlock()
resolvedHostname := hostname resolvedHostname := hostname
@ -383,24 +353,14 @@ func (tp *torProvider) Open(hostname string) (net.Conn, string, error) {
addrParts := strings.Split(hostname, ":") addrParts := strings.Split(hostname, ":")
resolvedHostname = addrParts[1] resolvedHostname = addrParts[1]
} }
conn, err := tp.dialer.Dial("tcp", resolvedHostname+".onion:9878") conn, err := tp.dialer.Dial("tcp", resolvedHostname+".onion:9878")
return conn, resolvedHostname, err return conn, resolvedHostname, err
} }
func (tp *torProvider) Close() { func (tp *torProvider) Close() {
closing := false
tp.lock.Lock() tp.lock.Lock()
defer tp.lock.Unlock() defer tp.lock.Unlock()
if !tp.isClosed {
// Break out of any background checks and close
// the underlying tor connection
tp.isClosed = true
tp.breakChan <- true
// wiggle lock to make sure if monitorRestart is waiting for the lock, it gets it, and finished that branch and gets the channel request to exit
tp.lock.Unlock()
tp.lock.Lock()
closing = true
}
// Unregister Child Listeners // Unregister Child Listeners
for addr, child := range tp.childListeners { for addr, child := range tp.childListeners {
@ -408,9 +368,11 @@ func (tp *torProvider) Close() {
delete(tp.childListeners, addr) delete(tp.childListeners, addr)
} }
log.Debugf("shutting down acn threads..(is already closed: %v)", tp.isClosed) if !tp.isClosed {
// Break out of any background checks and close
if closing { // the underlying tor connection
tp.isClosed = true
tp.breakChan <- true
if tp.t != nil { if tp.t != nil {
tp.t.Close() tp.t.Close()
tp.t = nil tp.t = nil
@ -424,42 +386,34 @@ func (tp *torProvider) SetStatusCallback(callback func(int, string)) {
tp.statusCallback = callback tp.statusCallback = callback
} }
func (tp *torProvider) SetVersionCallback(callback func(string)) { func (tp *torProvider) Callback() func(int, string) {
tp.lock.Lock()
defer tp.lock.Unlock()
tp.versionCallback = callback
}
func (tp *torProvider) GetStatusCallback() func(int, string) {
tp.lock.Lock() tp.lock.Lock()
defer tp.lock.Unlock() defer tp.lock.Unlock()
return tp.statusCallback return tp.statusCallback
} }
func (tp *torProvider) GetVersionCallback() func(string) {
tp.lock.Lock()
defer tp.lock.Unlock()
return tp.versionCallback
}
func (tp *torProvider) callStatusCallback(prog int, status string) { func (tp *torProvider) callStatusCallback(prog int, status string) {
tp.lock.Lock() tp.lock.Lock()
defer tp.lock.Unlock()
if tp.statusCallback != nil { if tp.statusCallback != nil {
tp.statusCallback(prog, status) tp.statusCallback(prog, status)
} }
tp.lock.Unlock()
} }
// NewTorACNWithAuth creates/starts a Tor ACN and returns a usable ACN object // NewTorACNWithAuth creates/starts a Tor ACN and returns a usable ACN object
func NewTorACNWithAuth(appDirectory string, bundledTorPath string, dataDir string, controlPort int, authenticator tor.Authenticator) (connectivity.ACN, error) { func NewTorACNWithAuth(appDirectory string, bundledTorPath string, controlPort int, authenticator tor.Authenticator) (connectivity.ACN, error) {
tp, err := startTor(appDirectory, bundledTorPath, dataDir, controlPort, authenticator) tp, err := startTor(appDirectory, bundledTorPath, controlPort, authenticator)
if err == nil { if err == nil {
tp.isClosed = false
go tp.monitorRestart() go tp.monitorRestart()
} }
return tp, err return tp, err
} }
// NewTorACN creates/starts a Tor ACN and returns a usable ACN object with a NullAuthenticator - this will fail.
func NewTorACN(appDirectory string, bundledTorPath string) (connectivity.ACN, error) {
return NewTorACNWithAuth(appDirectory, bundledTorPath, 9051, NullAuthenticator{})
}
// newHideCmd creates a Creator function for bine which generates a cmd that one windows will hide the dosbox // newHideCmd creates a Creator function for bine which generates a cmd that one windows will hide the dosbox
func newHideCmd(exePath string) process.Creator { func newHideCmd(exePath string) process.Creator {
return process.CmdCreatorFunc(func(ctx context.Context, args ...string) (*exec.Cmd, error) { return process.CmdCreatorFunc(func(ctx context.Context, args ...string) (*exec.Cmd, error) {
@ -470,19 +424,11 @@ func newHideCmd(exePath string) process.Creator {
cmd.Stdout = loggerDebug cmd.Stdout = loggerDebug
cmd.Stderr = loggerError cmd.Stderr = loggerError
cmd.SysProcAttr = sysProcAttr cmd.SysProcAttr = sysProcAttr
// override tor ld_library_path if requested
torLdLibPath, exists := os.LookupEnv("TOR_LD_LIBRARY_PATH")
if exists {
ldLibPath := fmt.Sprintf("LD_LIBRARY_PATH=%v", torLdLibPath)
cmd.Env = append([]string{ldLibPath}, os.Environ()...)
}
return cmd, nil return cmd, nil
}) })
} }
func (tp *torProvider) checkVersion() (string, error) { func (tp *torProvider) checkVersion() error {
// attempt connect to system tor // attempt connect to system tor
log.Debugf("dialing tor control port") log.Debugf("dialing tor control port")
controlport, err := dialControlPort(tp.controlPort) controlport, err := dialControlPort(tp.controlPort)
@ -495,32 +441,36 @@ func (tp *torProvider) checkVersion() (string, error) {
if err == nil { if err == nil {
if minTorVersionReqs(pinfo.TorVersion) { if minTorVersionReqs(pinfo.TorVersion) {
log.Debugln("OK version " + pinfo.TorVersion) log.Debugln("OK version " + pinfo.TorVersion)
return pinfo.TorVersion, nil return nil
} }
return pinfo.TorVersion, fmt.Errorf("tor version not supported: %v", pinfo.TorVersion) return fmt.Errorf("tor version not supported: %v", pinfo.TorVersion)
} }
} }
} }
return "", err return err
} }
func startTor(appDirectory string, bundledTorPath string, dataDir string, controlPort int, authenticator tor.Authenticator) (*torProvider, error) { func startTor(appDirectory string, bundledTorPath string, controlPort int, authenticator tor.Authenticator) (*torProvider, error) {
torDir := path.Join(appDirectory, "tor") torDir := path.Join(appDirectory, "tor")
os.MkdirAll(torDir, 0700) os.MkdirAll(torDir, 0700)
dataDir := ""
var err error
if dataDir, err = ioutil.TempDir(torDir, "data-dir-"); err != nil {
return nil, fmt.Errorf("unable to create temp data dir: %v", err)
}
tp := &torProvider{authenticator: authenticator, controlPort: controlPort, appDirectory: appDirectory, bundeledTorPath: bundledTorPath, childListeners: make(map[string]*onionListenService), breakChan: make(chan bool, 1), statusCallback: nil, versionCallback: nil, lastRestartTime: time.Now().Add(-restartCooldown), bootProgress: -1} tp := &torProvider{authenticator: authenticator, controlPort: controlPort, appDirectory: appDirectory, bundeledTorPath: bundledTorPath, childListeners: make(map[string]*onionListenService), breakChan: make(chan bool), statusCallback: nil, lastRestartTime: time.Now().Add(-restartCooldown)}
log.Debugf("checking if there is a running system tor") log.Debugf("checking if there is a running system tor")
if version, err := tp.checkVersion(); err == nil { if err := tp.checkVersion(); err == nil {
tp.version = version
controlport, err := dialControlPort(tp.controlPort) controlport, err := dialControlPort(tp.controlPort)
if err == nil { if err == nil {
log.Debugf("creating tor handler from system tor") log.Debugf("creating tor handler from system tor")
tp.t = createFromExisting(controlport, dataDir) tp.t = createFromExisting(controlport, dataDir)
tp.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator})
return tp, err
} }
tp.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator})
return tp, err
} }
// check if the torrc file is present where expected // check if the torrc file is present where expected
@ -530,36 +480,32 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
return nil, err return nil, err
} }
// if not, try bundled tor, then running system tor // if not, try running system tor
log.Debugln("checking if we can run bundled tor or system installed tor") log.Debugln("checking if we can run system installed tor or bundled tor")
if version, pass := checkCmdlineTorVersion(bundledTorPath); pass { if checkCmdlineTorVersion("tor") {
log.Debugln("bundled tor appears viable, attempting to use '" + bundledTorPath + "'") t, err := tor.Start(context.TODO(), &tor.StartConf{ControlPort: tp.controlPort, DisableCookieAuth: true, UseEmbeddedControlConn: false, DisableEagerAuth: true, EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(torDir, "torrc"), DebugWriter: nil, ProcessCreator: newHideCmd("tor")})
t, err := tor.Start(context.TODO(), &tor.StartConf{ControlPort: tp.controlPort, NoAutoSocksPort: true, DisableCookieAuth: true, UseEmbeddedControlConn: false, DisableEagerAuth: true, EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(torDir, "torrc"), ExePath: bundledTorPath, DebugWriter: nil, ProcessCreator: newHideCmd(bundledTorPath)})
if err != nil {
log.Debugf("Error running bundled tor %v\n", err)
return nil, err
}
tp.t = t
tp.version = version
} else if version, pass := checkCmdlineTorVersion("tor"); pass {
t, err := tor.Start(context.TODO(), &tor.StartConf{ControlPort: tp.controlPort, NoAutoSocksPort: true, DisableCookieAuth: true, UseEmbeddedControlConn: false, DisableEagerAuth: true, EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(torDir, "torrc"), DebugWriter: nil, ProcessCreator: newHideCmd("tor")})
if err != nil { if err != nil {
log.Debugf("Error connecting to self-run system tor: %v\n", err) log.Debugf("Error connecting to self-run system tor: %v\n", err)
return nil, err return nil, err
} }
tp.t = t tp.t = t
tp.version = version } else if bundledTorPath != "" && checkCmdlineTorVersion(bundledTorPath) {
log.Debugln("bundled tor appears viable, attempting to use '" + bundledTorPath + "'")
t, err := tor.Start(context.TODO(), &tor.StartConf{ControlPort: tp.controlPort, DisableCookieAuth: true, UseEmbeddedControlConn: false, DisableEagerAuth: true, EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(torDir, "torrc"), ExePath: bundledTorPath, DebugWriter: nil, ProcessCreator: newHideCmd(bundledTorPath)})
if err != nil {
log.Debugf("Error running bundled tor %v\n", err)
return nil, err
}
tp.t = t
} else { } else {
log.Debugln("Could not find a viable tor running or to run") 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)")
} }
version, err := tp.checkVersion() err = tp.checkVersion()
if err == nil { if err == nil {
tp.t.DeleteDataDirOnClose = false // caller is responsible for dealing with cached information... tp.t.DeleteDataDirOnClose = true
tp.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator}) tp.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator})
tp.version = version
tp.t.Control.TakeOwnership()
return tp, err return tp, err
} }
return nil, fmt.Errorf("could not connect to running tor: %v", err) return nil, fmt.Errorf("could not connect to running tor: %v", err)
@ -587,6 +533,7 @@ func (tp *torProvider) monitorRestart() {
prog, status := tp.GetBootstrapStatus() prog, status := tp.GetBootstrapStatus()
if prog == torDown && tp.t != nil { if prog == torDown && tp.t != nil {
log.Warnf("monitorRestart calling tp.restart() with prog:%v\n", prog)
tp.restart() tp.restart()
return return
} }
@ -618,7 +565,7 @@ func createFromExisting(controlport *control.Conn, datadir string) *tor.Tor {
Control: controlport, Control: controlport,
ProcessCancelFunc: nil, ProcessCancelFunc: nil,
DataDir: datadir, DataDir: datadir,
DeleteDataDirOnClose: false, DeleteDataDirOnClose: true,
DebugWriter: nil, DebugWriter: nil,
StopProcessOnClose: false, StopProcessOnClose: false,
GeoIPCreatedFile: "", GeoIPCreatedFile: "",
@ -629,57 +576,25 @@ func createFromExisting(controlport *control.Conn, datadir string) *tor.Tor {
return t return t
} }
func checkCmdlineTorVersion(torCmd string) (string, bool) { func checkCmdlineTorVersion(torCmd string) bool {
if torCmd == "" {
return "", false
}
// ideally we would use CommandContext with Timeout here
// but it doesn't work with programs that may launch other processes via scripts e.g. exec
// and the workout is more complex than just implementing the logic ourselves...
cmd := exec.Command(torCmd, "--version") cmd := exec.Command(torCmd, "--version")
var outb bytes.Buffer cmd.SysProcAttr = sysProcAttr
cmd.Stdout = &outb out, err := cmd.CombinedOutput()
re := regexp.MustCompile(`[0-1]\.[0-9]\.[0-9]\.[0-9]`)
waiting := make(chan error, 1) sysTorVersion := re.Find(out)
log.Infoln("tor version: " + string(sysTorVersion))
// try running the tor process return err == nil && minTorVersionReqs(string(sysTorVersion))
go func() {
log.Debugf("running tor process: %v", torCmd)
cmd.Run()
waiting <- nil
}()
// timeout function
go func() {
<-time.After(time.Second * 5)
waiting <- errors.New("timeout")
}()
err := <-waiting
if err != nil {
log.Debugf("tor process timed out")
return "", false
}
re := regexp.MustCompile(`[0-1]+\.[0-9]+\.[0-9]+\.[0-9]+`)
sysTorVersion := re.Find(outb.Bytes())
log.Infof("tor version: %v", string(sysTorVersion))
return string(sysTorVersion), minTorVersionReqs(string(sysTorVersion))
} }
// returns true if supplied version meets our min requirments // returns true if supplied version meets our min requirments
// min requirement: 0.3.5.x // min requirement: 0.3.5.x
func minTorVersionReqs(torversion string) bool { func minTorVersionReqs(torversion string) bool {
torversions := strings.Split(torversion, ".") //eg: 0.3.4.8 or 0.3.5.1-alpha torversions := strings.Split(torversion, ".") //eg: 0.3.4.8 or 0.3.5.1-alpha
if len(torversions) >= 3 { log.Debugf("torversions: %v", torversions)
log.Debugf("torversions: %v", torversions) tva, _ := strconv.Atoi(torversions[0])
tva, _ := strconv.Atoi(torversions[0]) tvb, _ := strconv.Atoi(torversions[1])
tvb, _ := strconv.Atoi(torversions[1]) tvc, _ := strconv.Atoi(torversions[2])
tvc, _ := strconv.Atoi(torversions[2]) return tva > 0 || (tva == 0 && (tvb > 3 || (tvb == 3 && tvc >= 5)))
return tva > 0 || (tva == 0 && (tvb > 3 || (tvb == 3 && tvc >= 5)))
}
return false
} }
func dialControlPort(port int) (*control.Conn, error) { func dialControlPort(port int) (*control.Conn, error) {

View File

@ -3,12 +3,8 @@ package tor
import ( import (
"fmt" "fmt"
"git.openprivacy.ca/openprivacy/log" "git.openprivacy.ca/openprivacy/log"
"os"
path "path/filepath" path "path/filepath"
"runtime"
"runtime/pprof"
"testing" "testing"
"time"
) )
func getStatusCallback(progChan chan int) func(int, string) { func getStatusCallback(progChan chan int) func(int, string) {
@ -18,39 +14,20 @@ func getStatusCallback(progChan chan int) func(int, string) {
} }
} }
func getVersionCallback(verChan chan string) func(string) {
return func(version string) {
fmt.Printf("version: %v\n", version)
verChan <- version
}
}
func TestTorProvider(t *testing.T) { func TestTorProvider(t *testing.T) {
progChan := make(chan int)
goRoutineStart := runtime.NumGoroutine()
progChan := make(chan int, 10)
verChan := make(chan string, 10)
log.SetLevel(log.LevelDebug) log.SetLevel(log.LevelDebug)
torpath := path.Join("..", "tmp/tor") torpath := path.Join("..", "tmp/tor")
NewTorrc().WithControlPort(9051).WithHashedPassword("examplehashedpassword").Build(path.Join("..", "testing", "tor", "torrc")) NewTorrc().WithControlPort(9051).WithHashedPassword("examplehashedpassword").Build(path.Join("..", "testing", "tor", "torrc"))
log.Debugf("setting tor path %v", torpath) log.Debugf("setting tor path %v", torpath)
acn, err := NewTorACNWithAuth(path.Join("../testing/"), torpath, 9051, HashedPasswordAuthenticator{"examplehashedpassword"})
dataDir := ""
var err error
if dataDir, err = os.MkdirTemp(path.Join("..", "testing"), "data-dir-"); err != nil {
t.Fatalf("could not create data dir")
}
acn, err := NewTorACNWithAuth(path.Join("../testing/"), torpath, dataDir, 9051, HashedPasswordAuthenticator{"examplehashedpassword"})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
acn.SetStatusCallback(getStatusCallback(progChan)) acn.SetStatusCallback(getStatusCallback(progChan))
acn.SetVersionCallback(getVersionCallback(verChan))
progress := 0 progress := 0
for progress < 100 { for progress < 100 {
@ -65,13 +42,6 @@ func TestTorProvider(t *testing.T) {
progress = <-progChan progress = <-progChan
t.Logf("progress: %v", progress) t.Logf("progress: %v", progress)
} }
log.Debugf("Pulling tor version from version callback chan...\n")
version := <-verChan
if version == "" {
t.Errorf("failed to get tor version, got empty string\n")
} else {
log.Debugf("Tor version: %v\n", version)
}
// Test opening the OP Server // Test opening the OP Server
_, _, err = acn.Open("isbr2t6bflul2zyi6hjtnuezb2xvfr42svzjg2q3gyqfgg3wmnrbkkqd") _, _, err = acn.Open("isbr2t6bflul2zyi6hjtnuezb2xvfr42svzjg2q3gyqfgg3wmnrbkkqd")
@ -88,7 +58,7 @@ func TestTorProvider(t *testing.T) {
_, err = acn.GetInfo("not_a_real_onion") _, err = acn.GetInfo("not_a_real_onion")
if err == nil { if err == nil {
t.Fatalf("GetInfo for non existent onion should have errored") t.Fatalf("GetInfo for non existant onion should have errored")
} }
} else { } else {
@ -101,15 +71,4 @@ func TestTorProvider(t *testing.T) {
acn.Restart() acn.Restart()
acn.Close() acn.Close()
time.Sleep(time.Second * 10)
goRoutineEnd := runtime.NumGoroutine()
if goRoutineEnd != goRoutineStart {
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
t.Fatalf("goroutine leak in ACN: %v %v", goRoutineStart, goRoutineEnd)
}
} }

View File

@ -6,18 +6,10 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io" "io"
"os" "io/ioutil"
"strings" "strings"
) )
type TorLogLevel string
const TorLogLevelDebug TorLogLevel = "debug"
const TorLogLevelNotice TorLogLevel = "notice"
const TorLogLevelInfo TorLogLevel = "info"
const TorLogLevelWarn TorLogLevel = "warn"
const TorLogLevelErr TorLogLevel = "err"
// TorrcBuilder is a a helper for building torrc files // TorrcBuilder is a a helper for building torrc files
type TorrcBuilder struct { type TorrcBuilder struct {
lines []string lines []string
@ -42,28 +34,9 @@ func (tb *TorrcBuilder) WithControlPort(port int) *TorrcBuilder {
return tb return tb
} }
// WithLog sets the Log to file directive to the specified file with the specified log level // WithCustom clobbers the torrc builder and allows the client to set any option they want, while benefiting
func (tb *TorrcBuilder) WithLog(logfile string, level TorLogLevel) *TorrcBuilder {
tb.lines = append(tb.lines, fmt.Sprintf("Log %v file %v", level, logfile))
return tb
}
// WithSocksTimeout adjusts how long before a timeout error is generated trying to connect to the SOCKS port
func (tb *TorrcBuilder) WithSocksTimeout(timeOutSecs int) *TorrcBuilder {
tb.lines = append(tb.lines, fmt.Sprintf("SocksTimeout %v", timeOutSecs))
return tb
}
// WithCustom appends to the torrc builder and allows the client to set any option they want, while benefiting
// from other configuration options. // from other configuration options.
func (tb *TorrcBuilder) WithCustom(lines []string) *TorrcBuilder { func (tb *TorrcBuilder) WithCustom(lines []string) *TorrcBuilder {
tb.lines = append(tb.lines, lines...)
return tb
}
// UseCustom clobbers the torrc builder and allows the client to set any option they want, while benefiting
// from other configuration options.
func (tb *TorrcBuilder) UseCustom(lines []string) *TorrcBuilder {
tb.lines = lines tb.lines = lines
return tb return tb
} }
@ -79,12 +52,6 @@ func (tb *TorrcBuilder) WithOnionTrafficOnly() *TorrcBuilder {
return tb return tb
} }
// WithOwningPid adds a __OwningControllerProcess line to the config that will attempt to have tor monitor parent PID health and die when parent dies
func (tb *TorrcBuilder) WithOwningPid(pid int) *TorrcBuilder {
tb.lines = append(tb.lines, fmt.Sprintf("__OwningControllerProcess %v", pid))
return tb
}
// WithHashedPassword sets a password for the control port. // WithHashedPassword sets a password for the control port.
func (tb *TorrcBuilder) WithHashedPassword(password string) *TorrcBuilder { func (tb *TorrcBuilder) WithHashedPassword(password string) *TorrcBuilder {
var salt [8]byte var salt [8]byte
@ -98,7 +65,7 @@ func (tb *TorrcBuilder) WithHashedPassword(password string) *TorrcBuilder {
// Build finalizes the torrc contents and write a file // Build finalizes the torrc contents and write a file
func (tb *TorrcBuilder) Build(path string) error { func (tb *TorrcBuilder) Build(path string) error {
return os.WriteFile(path, []byte(strings.Join(tb.lines, "\n")), 0600) 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. // Preview provides a string representation of the torrc file without writing it to a file location.