Compare commits

...

28 Commits

Author SHA1 Message Date
Sarah Jamie Lewis 1524e78a4a Merge pull request 'Clarified and Split Apart Environment Variables that alter port binding behaviour.' (#47) from whonix into master
continuous-integration/drone/push Build is pending Details
Reviewed-on: #47
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-08-18 21:03:41 +00:00
Sarah Jamie Lewis cd87779e87 Merge branch 'master' into whonix
continuous-integration/drone/pr Build is pending Details
2023-08-18 21:03:33 +00:00
Sarah Jamie Lewis d8dd82d065 Update Docs
continuous-integration/drone/pr Build is pending Details
2023-08-16 10:59:31 -07:00
Sarah Jamie Lewis 932f99fac8 Expand Useable Ports...these apply to hosted servers too..
continuous-integration/drone/pr Build is pending Details
2023-08-16 10:56:43 -07:00
Sarah Jamie Lewis bbacb5539d Documentation
continuous-integration/drone/pr Build is pending Details
2023-08-16 10:49:25 -07:00
Sarah Jamie Lewis 2c9ec9d894 Clean up and seperate flags 2023-08-16 10:46:02 -07:00
Sarah Jamie Lewis c9ea1e4464 Comment os.ID 2023-08-16 10:33:12 -07:00
Sarah Jamie Lewis 61ced82cb4 Restrict Ports when BINE_WHONIX is enabled. 2023-08-16 10:31:48 -07:00
Sarah Jamie Lewis 91c41e2005 Merge pull request 'Support Whonix' (#46) from whonix into master
continuous-integration/drone/push Build is pending Details
Reviewed-on: #46
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-08-15 17:21:08 +00:00
Sarah Jamie Lewis caca121441 Support Whonix
continuous-integration/drone/pr Build is passing Details
2023-08-14 13:59:58 -07:00
Sarah Jamie Lewis 9beff8a10a Require error to construct an ErrorACN
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2023-05-29 10:22:36 -07:00
Sarah Jamie Lewis dedcbdd3cb Merge pull request 'Fix errorAcn reference issues + add support for Tor specific shared library path' (#43) from tor-updates into master
continuous-integration/drone/push Build is pending Details
Reviewed-on: #43
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-05-24 19:24:20 +00:00
Sarah Jamie Lewis c18cd719a1 update readme
continuous-integration/drone/pr Build is pending Details
2023-05-24 12:19:15 -07:00
Sarah Jamie Lewis 380fd1834a Fix errorAcn reference issues + add support for Tor specific shared library path
continuous-integration/drone/pr Build is passing Details
2023-05-24 11:11:18 -07:00
Sarah Jamie Lewis 1162cf4168 Upgrade Bine
continuous-integration/drone/push Build is pending Details
2023-04-05 02:38:26 +00:00
Sarah Jamie Lewis 4a2eeed072 Merge pull request 'add TakeOwnership call once tor started to help ensure tor ends; add support for __OwningControllerProcess' (#39) from takeownpid into master
continuous-integration/drone/push Build is pending Details
Reviewed-on: #39
2022-10-08 22:45:46 +00:00
Dan Ballard 2f3860eb89 drone use go 1.19.1
continuous-integration/drone/pr Build is passing Details
2022-10-08 15:26:39 -07:00
Dan Ballard bbe4198a41 add TakeOwnership call once tor started to help ensure tor ends; add support for __OwningControllerProcess 2022-10-08 11:58:42 -07:00
Sarah Jamie Lewis 51029af959 Merge pull request 'enable logging support for torrc builder' (#36) from logging into master
continuous-integration/drone/push Build is pending Details
Reviewed-on: #36
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2022-09-21 21:39:19 +00:00
Dan Ballard 1f52dc7138 enable logging support for torrc builder
continuous-integration/drone/pr Build is passing Details
2022-09-21 13:06:15 -07:00
Sarah Jamie Lewis 72f689de26 Merge pull request 'update .drone.yml to new format' (#35) from updateDrone into master
continuous-integration/drone/push Build is passing Details
Reviewed-on: #35
2022-09-07 15:39:47 +00:00
Dan Ballard 69085fc721 update .drone.yml to new format
continuous-integration/drone/pr Build is passing Details
2022-09-06 19:14:42 -07:00
Sarah Jamie Lewis 98b15bd105 Merge pull request 'store bootsrap version, make available; fix tor version parsing for double digit versions' (#33) from verStatus into master
continuous-integration/drone/push Build was killed Details
continuous-integration/drone/tag Build was killed Details
Reviewed-on: #33
2022-08-29 02:56:41 +00:00
Dan Ballard 789de52589 store bootsrap version, make available; fix tor version parsing for double digit versions
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/pr Build is passing Details
2022-08-28 19:50:01 -07:00
Dan Ballard 1741e63424 Merge pull request 'Upgrade Dependencies. Prevent socks default behaviour in bine' (#34) from binefix into master
continuous-integration/drone/push Build is passing Details
Reviewed-on: #34
2022-08-29 02:05:30 +00:00
Sarah Jamie Lewis 478df967fc Upgrade Dependencies. Prevent socks default behaviour in bine
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build is passing Details
2022-08-28 18:57:45 -07:00
Sarah Jamie Lewis 8e5dc44400 Merge pull request 'Get x Callbacks' (#32) from gets into master
continuous-integration/drone/push Build was killed Details
continuous-integration/drone/tag Build was killed Details
Reviewed-on: #32
2022-08-08 19:33:00 +00:00
Dan Ballard 6122ad437d Get x Callbacks
continuous-integration/drone/pr Build is passing Details
continuous-integration/drone/push Build was killed Details
2022-08-08 12:24:16 -07:00
13 changed files with 237 additions and 109 deletions

View File

@ -1,63 +1,76 @@
workspace:
base: /go
path: src/git.openprivacy.ca/openprivacy/connectivity
---
kind: pipeline
type: docker
name: linux-test
pipeline:
fetch:
when:
repo: openprivacy/connectivity
branch: master
event: [ push, pull_request ]
image: golang
steps:
- name: fetch
image: golang:1.19.1
volumes:
- name: deps
path: /go
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
quality:
when:
repo: openprivacy/connectivity
branch: master
event: [ push, pull_request ]
image: golang
- name: quality
image: golang:1.19.1
volumes:
- name: deps
path: /go
commands:
- staticcheck ./...
units-tests:
when:
repo: openprivacy/connectivity
branch: master
event: [ push, pull_request ]
image: golang
- name: units-tests
image: golang:1.19.1
volumes:
- name: deps
path: /go
commands:
- export PATH=$PATH:/go/src/git.openprivacy.ca/openprivacy/connectivity
- export PATH=`pwd`:$PATH
- ./tmp/tor -f ./testing/torrc
- sleep 15
- sh testing/tests.sh
- pkill -9 tor
integration-tests:
when:
repo: openprivacy/connectivity
branch: master
event: [ push, pull_request ]
image: golang
- name: integration-tests
image: golang:1.19.1
volumes:
- name: deps
path: /go
commands:
- export PATH=`pwd`:$PATH
- go test -race -v ./testing/launch_tor_integration_test.go
notify-email:
- name: notify-email
image: drillster/drone-email
pull: if-not-exists
host: build.openprivacy.ca
port: 25
skip_verify: true
from: drone@openprivacy.ca
when:
repo: openprivacy/connectivity
branch: master
status: [ failure ]
notify-gogs:
- name: notify-gogs
image: openpriv/drone-gogs
pull: if-not-exists
when:
repo: openprivacy/connectivity
branch: master
event: pull_request
status: [ success, changed, failure ]
secrets: [gogs_account_token]
gogs_url: https://git.openprivacy.ca
environment:
GOGS_ACCOUNT_TOKEN:
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,4 +4,6 @@ tor/tor/
vendor/
*.cover.out
tmp/
testing/tor/*
testing/tor/*
tor/data-dir*
testing/data-dir*

View File

@ -7,6 +7,12 @@ A library providing an ACN (Anonymous Communication Network
* 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
* Reference an EndPoint via a string / hostname
@ -50,4 +56,4 @@ service:
acn.Restart()
and
acn.Close()
acn.Close()

7
acn.go
View File

@ -38,9 +38,14 @@ type ACN interface {
WaitTillBootstrapped() error
// Sets the callback function to be called when ACN status changes
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
Restart()
@ -57,8 +62,6 @@ type ACN interface {
// GetVersion returns a string of what the ACN returns when asked for a version
GetVersion() string
Callback() func(int, string)
GetInfo(onion string) (map[string]string, error)
Close()

View File

@ -1,34 +1,43 @@
package connectivity
import (
"errors"
"fmt"
"net"
)
const acnError = "error initializing anonymous communication network"
// 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 {
acnError error
statusCallbackCache func(int, string)
versionCallbackCache func(string)
}
func (e ErrorACN) Callback() func(int, string) {
func NewErrorACN(err error) ErrorACN {
return ErrorACN{
acnError: err,
statusCallbackCache: func(int, string) {},
versionCallbackCache: func(string) {},
}
}
func (e *ErrorACN) GetStatusCallback() func(int, string) {
return e.statusCallbackCache
}
func (e *ErrorACN) GetVersionCallback() func(string) {
return e.versionCallbackCache
}
func (e *ErrorACN) GetInfo(addr string) (map[string]string, error) {
return nil, errors.New(acnError)
return nil, e.acnError
}
func (e ErrorACN) GetBootstrapStatus() (int, string) {
return -1, acnError
func (e *ErrorACN) GetBootstrapStatus() (int, string) {
return -1, e.acnError.Error()
}
func (e ErrorACN) WaitTillBootstrapped() error {
return errors.New(acnError)
func (e *ErrorACN) WaitTillBootstrapped() error {
return e.acnError
}
func (e *ErrorACN) SetStatusCallback(callback func(int, string)) {
@ -39,24 +48,25 @@ func (e *ErrorACN) SetVersionCallback(callback func(string)) {
e.versionCallbackCache = callback
}
func (e ErrorACN) Restart() {
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) {
return nil, "", e.acnError
}
func (e ErrorACN) Listen(identity PrivateKey, port int) (ListenService, error) {
return nil, fmt.Errorf(acnError)
func (e *ErrorACN) Listen(identity PrivateKey, port int) (ListenService, error) {
return nil, e.acnError
}
func (e ErrorACN) GetPID() (int, error) {
return -1, fmt.Errorf(acnError)
func (e *ErrorACN) GetPID() (int, error) {
return -1, e.acnError
}
func (e ErrorACN) GetVersion() string {
return acnError
func (e *ErrorACN) GetVersion() string {
return e.acnError.Error()
}
func (e ErrorACN) Close() {
func (e *ErrorACN) Close() {
// nothing to do...
}

14
go.mod
View File

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

24
go.sum
View File

@ -1,9 +1,7 @@
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.openprivacy.ca/openprivacy/bine v0.0.5 h1:DJs5gqw3SkvLSgRDvroqJxZ7F+YsbxbBRg5t0rU5gYE=
git.openprivacy.ca/openprivacy/bine v0.0.5/go.mod h1:fwdeq6RO08WDkV0k7HfArsjRvurVULoUQmT//iaABZM=
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
@ -15,23 +13,25 @@ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd
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-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-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-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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-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-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
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/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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/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=

View File

@ -18,10 +18,14 @@ func NewLocalACN() ACN {
return &localProvider{}
}
func (lp *localProvider) Callback() func(int, string) {
func (lp *localProvider) GetStatusCallback() func(int, string) {
return func(int, string) {}
}
func (lp *localProvider) GetVersionCallback() func(string) {
return func(string) {}
}
func (ls *localListenService) AddressFull() string {
return ls.l.Addr().String()
}

View File

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

View File

@ -3,7 +3,6 @@ package testing
import (
"git.openprivacy.ca/openprivacy/connectivity/tor"
"git.openprivacy.ca/openprivacy/log"
"io/ioutil"
"math/rand"
"os"
path "path/filepath"
@ -30,7 +29,7 @@ func TestLaunchTor(t *testing.T) {
}
dataDir := ""
if dataDir, err = ioutil.TempDir(path.Join("..", "testing"), "data-dir-"); err != nil {
if dataDir, err = os.MkdirTemp(path.Join("..", "testing"), "data-dir-"); err != nil {
t.Fatalf("could not create data dir")
}

View File

@ -76,6 +76,7 @@ type torProvider struct {
isClosed bool
dataDir string
version string
bootProgress int
}
func (ols *onionListenService) AddressFull() string {
@ -169,8 +170,9 @@ 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
// returns -1 on network disconnected
// returns -2 on error
//
// returns -1 on network disconnected
// returns -2 on error
func (tp *torProvider) GetBootstrapStatus() (int, string) {
tp.lock.Lock()
defer tp.lock.Unlock()
@ -205,6 +207,7 @@ func (tp *torProvider) GetBootstrapStatus() (int, string) {
status = statusMatches[1]
}
}
tp.bootProgress = progress
return progress, status
}
@ -265,7 +268,28 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne
localport += 1024
}
localListener, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(localport))
var localListener net.Listener
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 {
return nil, err
@ -287,6 +311,8 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne
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
ols := &onionListenService{os: os, tp: tp}
@ -298,12 +324,12 @@ func (tp *torProvider) Restart() {
log.Debugf("launching restart...")
tp.lock.Lock()
defer tp.lock.Unlock()
log.Debugf("checking last restart time")
if time.Since(tp.lastRestartTime) < restartCooldown {
tp.lock.Unlock()
return
}
tp.lock.Unlock()
go tp.restart()
}
@ -346,6 +372,10 @@ func (tp *torProvider) Open(hostname string) (net.Conn, string, error) {
tp.lock.Unlock()
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()
resolvedHostname := hostname
@ -353,14 +383,24 @@ func (tp *torProvider) Open(hostname string) (net.Conn, string, error) {
addrParts := strings.Split(hostname, ":")
resolvedHostname = addrParts[1]
}
conn, err := tp.dialer.Dial("tcp", resolvedHostname+".onion:9878")
return conn, resolvedHostname, err
}
func (tp *torProvider) Close() {
closing := false
tp.lock.Lock()
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
for addr, child := range tp.childListeners {
@ -370,11 +410,7 @@ func (tp *torProvider) Close() {
log.Debugf("shutting down acn threads..(is already closed: %v)", tp.isClosed)
if !tp.isClosed {
// Break out of any background checks and close
// the underlying tor connection
tp.isClosed = true
tp.breakChan <- true
if closing {
if tp.t != nil {
tp.t.Close()
tp.t = nil
@ -394,12 +430,18 @@ func (tp *torProvider) SetVersionCallback(callback func(string)) {
tp.versionCallback = callback
}
func (tp *torProvider) Callback() func(int, string) {
func (tp *torProvider) GetStatusCallback() func(int, string) {
tp.lock.Lock()
defer tp.lock.Unlock()
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) {
tp.lock.Lock()
defer tp.lock.Unlock()
@ -428,6 +470,14 @@ func newHideCmd(exePath string) process.Creator {
cmd.Stdout = loggerDebug
cmd.Stderr = loggerError
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
})
}
@ -459,7 +509,7 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
os.MkdirAll(torDir, 0700)
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)}
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}
log.Debugf("checking if there is a running system tor")
if version, err := tp.checkVersion(); err == nil {
@ -468,9 +518,9 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
if err == nil {
log.Debugf("creating tor handler from system tor")
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
@ -484,7 +534,7 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
log.Debugln("checking if we can run bundled tor or system installed tor")
if version, pass := checkCmdlineTorVersion(bundledTorPath); pass {
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)})
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
@ -492,7 +542,7 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
tp.t = t
tp.version = version
} else if version, pass := checkCmdlineTorVersion("tor"); pass {
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"), DebugWriter: nil, ProcessCreator: newHideCmd("tor")})
if err != nil {
log.Debugf("Error connecting to self-run system tor: %v\n", err)
return nil, err
@ -509,6 +559,7 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
tp.t.DeleteDataDirOnClose = false // caller is responsible for dealing with cached information...
tp.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator})
tp.version = version
tp.t.Control.TakeOwnership()
return tp, err
}
return nil, fmt.Errorf("could not connect to running tor: %v", err)
@ -536,7 +587,6 @@ func (tp *torProvider) monitorRestart() {
prog, status := tp.GetBootstrapStatus()
if prog == torDown && tp.t != nil {
log.Warnf("monitorRestart calling tp.restart() with prog:%v\n", prog)
tp.restart()
return
}
@ -612,7 +662,7 @@ func checkCmdlineTorVersion(torCmd string) (string, bool) {
return "", false
}
re := regexp.MustCompile(`[0-1]\.[0-9]\.[0-9]\.[0-9]`)
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))

View File

@ -3,7 +3,6 @@ package tor
import (
"fmt"
"git.openprivacy.ca/openprivacy/log"
"io/ioutil"
"os"
path "path/filepath"
"runtime"
@ -41,7 +40,7 @@ func TestTorProvider(t *testing.T) {
dataDir := ""
var err error
if dataDir, err = ioutil.TempDir(path.Join("..", "testing"), "data-dir-"); err != nil {
if dataDir, err = os.MkdirTemp(path.Join("..", "testing"), "data-dir-"); err != nil {
t.Fatalf("could not create data dir")
}
@ -103,7 +102,7 @@ func TestTorProvider(t *testing.T) {
acn.Close()
time.Sleep(time.Second * 5)
time.Sleep(time.Second * 10)
goRoutineEnd := runtime.NumGoroutine()

View File

@ -6,10 +6,18 @@ import (
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
"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
type TorrcBuilder struct {
lines []string
@ -34,9 +42,28 @@ 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
// WithLog sets the Log to file directive to the specified file with the specified log level
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.
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
return tb
}
@ -52,6 +79,12 @@ func (tb *TorrcBuilder) WithOnionTrafficOnly() *TorrcBuilder {
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.
func (tb *TorrcBuilder) WithHashedPassword(password string) *TorrcBuilder {
var salt [8]byte
@ -65,7 +98,7 @@ func (tb *TorrcBuilder) WithHashedPassword(password string) *TorrcBuilder {
// Build finalizes the torrc contents and write a file
func (tb *TorrcBuilder) Build(path string) error {
return ioutil.WriteFile(path, []byte(strings.Join(tb.lines, "\n")), 0600)
return os.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.