Compare commits
22 Commits
Author | SHA1 | Date |
---|---|---|
Sarah Jamie Lewis | 1524e78a4a | |
Sarah Jamie Lewis | cd87779e87 | |
Sarah Jamie Lewis | d8dd82d065 | |
Sarah Jamie Lewis | 932f99fac8 | |
Sarah Jamie Lewis | bbacb5539d | |
Sarah Jamie Lewis | 2c9ec9d894 | |
Sarah Jamie Lewis | c9ea1e4464 | |
Sarah Jamie Lewis | 61ced82cb4 | |
Sarah Jamie Lewis | 91c41e2005 | |
Sarah Jamie Lewis | caca121441 | |
Sarah Jamie Lewis | 9beff8a10a | |
Sarah Jamie Lewis | dedcbdd3cb | |
Sarah Jamie Lewis | c18cd719a1 | |
Sarah Jamie Lewis | 380fd1834a | |
Sarah Jamie Lewis | 1162cf4168 | |
Sarah Jamie Lewis | 4a2eeed072 | |
Dan Ballard | 2f3860eb89 | |
Dan Ballard | bbe4198a41 | |
Sarah Jamie Lewis | 51029af959 | |
Dan Ballard | 1f52dc7138 | |
Sarah Jamie Lewis | 72f689de26 | |
Dan Ballard | 69085fc721 |
85
.drone.yml
85
.drone.yml
|
@ -1,63 +1,76 @@
|
||||||
workspace:
|
---
|
||||||
base: /go
|
kind: pipeline
|
||||||
path: src/git.openprivacy.ca/openprivacy/connectivity
|
type: docker
|
||||||
|
name: linux-test
|
||||||
|
|
||||||
pipeline:
|
steps:
|
||||||
fetch:
|
- name: fetch
|
||||||
when:
|
image: golang:1.19.1
|
||||||
repo: openprivacy/connectivity
|
volumes:
|
||||||
branch: master
|
- name: deps
|
||||||
event: [ push, pull_request ]
|
path: /go
|
||||||
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
|
||||||
quality:
|
- name: quality
|
||||||
when:
|
image: golang:1.19.1
|
||||||
repo: openprivacy/connectivity
|
volumes:
|
||||||
branch: master
|
- name: deps
|
||||||
event: [ push, pull_request ]
|
path: /go
|
||||||
image: golang
|
|
||||||
commands:
|
commands:
|
||||||
- staticcheck ./...
|
- staticcheck ./...
|
||||||
units-tests:
|
- name: units-tests
|
||||||
when:
|
image: golang:1.19.1
|
||||||
repo: openprivacy/connectivity
|
volumes:
|
||||||
branch: master
|
- name: deps
|
||||||
event: [ push, pull_request ]
|
path: /go
|
||||||
image: golang
|
|
||||||
commands:
|
commands:
|
||||||
- export PATH=$PATH:/go/src/git.openprivacy.ca/openprivacy/connectivity
|
- export PATH=`pwd`:$PATH
|
||||||
- ./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
|
||||||
integration-tests:
|
- name: integration-tests
|
||||||
when:
|
image: golang:1.19.1
|
||||||
repo: openprivacy/connectivity
|
volumes:
|
||||||
branch: master
|
- name: deps
|
||||||
event: [ push, pull_request ]
|
path: /go
|
||||||
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
|
||||||
notify-email:
|
- name: 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 ]
|
||||||
notify-gogs:
|
- name: 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 ]
|
||||||
secrets: [gogs_account_token]
|
environment:
|
||||||
|
GOGS_ACCOUNT_TOKEN:
|
||||||
|
from_secret: gogs_account_token
|
||||||
|
settings:
|
||||||
gogs_url: https://git.openprivacy.ca
|
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
|
|
@ -5,3 +5,5 @@ vendor/
|
||||||
*.cover.out
|
*.cover.out
|
||||||
tmp/
|
tmp/
|
||||||
testing/tor/*
|
testing/tor/*
|
||||||
|
tor/data-dir*
|
||||||
|
testing/data-dir*
|
|
@ -7,6 +7,12 @@ 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
|
||||||
|
|
48
error_acn.go
48
error_acn.go
|
@ -1,38 +1,43 @@
|
||||||
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)
|
versionCallbackCache func(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) GetStatusCallback() 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
|
return e.statusCallbackCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) GetVersionCallback() func(string) {
|
func (e *ErrorACN) GetVersionCallback() func(string) {
|
||||||
return e.versionCallbackCache
|
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, errors.New(acnError)
|
return nil, e.acnError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) GetBootstrapStatus() (int, string) {
|
func (e *ErrorACN) GetBootstrapStatus() (int, string) {
|
||||||
return -1, acnError
|
return -1, e.acnError.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) WaitTillBootstrapped() error {
|
func (e *ErrorACN) WaitTillBootstrapped() error {
|
||||||
return errors.New(acnError)
|
return e.acnError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ErrorACN) SetStatusCallback(callback func(int, string)) {
|
func (e *ErrorACN) SetStatusCallback(callback func(int, string)) {
|
||||||
|
@ -43,24 +48,25 @@ func (e *ErrorACN) SetVersionCallback(callback func(string)) {
|
||||||
e.versionCallbackCache = callback
|
e.versionCallbackCache = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) Restart() {
|
func (e *ErrorACN) Restart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) Open(hostname string) (net.Conn, string, error) {
|
func (e *ErrorACN) Open(hostname string) (net.Conn, string, error) {
|
||||||
return nil, "", fmt.Errorf(acnError)
|
return nil, "", e.acnError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) Listen(identity PrivateKey, port int) (ListenService, error) {
|
func (e *ErrorACN) Listen(identity PrivateKey, port int) (ListenService, error) {
|
||||||
return nil, fmt.Errorf(acnError)
|
return nil, e.acnError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) GetPID() (int, error) {
|
func (e *ErrorACN) GetPID() (int, error) {
|
||||||
return -1, fmt.Errorf(acnError)
|
return -1, e.acnError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) GetVersion() string {
|
func (e *ErrorACN) GetVersion() string {
|
||||||
return acnError
|
return e.acnError.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) Close() {
|
func (e *ErrorACN) Close() {
|
||||||
|
// nothing to do...
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -4,7 +4,7 @@ go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.0.0
|
filippo.io/edwards25519 v1.0.0
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.4
|
git.openprivacy.ca/openprivacy/bine v0.0.5
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3
|
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
|
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1,7 +1,7 @@
|
||||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
filippo.io/edwards25519 v1.0.0/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.5 h1:DJs5gqw3SkvLSgRDvroqJxZ7F+YsbxbBRg5t0rU5gYE=
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
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 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
git.openprivacy.ca/openprivacy/log v1.0.3/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=
|
||||||
|
|
|
@ -3,7 +3,6 @@ package testing
|
||||||
import (
|
import (
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
path "path/filepath"
|
path "path/filepath"
|
||||||
|
@ -30,7 +29,7 @@ func TestLaunchTor(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dataDir := ""
|
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")
|
t.Fatalf("could not create data dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,7 @@ var progRe = regexp.MustCompile("PROGRESS=([0-9]*)")
|
||||||
var sumRe = regexp.MustCompile("SUMMARY=\"(.*)\"$")
|
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) {
|
||||||
|
@ -267,7 +268,28 @@ func (tp *torProvider) Listen(identity connectivity.PrivateKey, port int) (conne
|
||||||
localport += 1024
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -289,6 +311,8 @@ 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}
|
||||||
|
@ -446,6 +470,14 @@ 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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -486,10 +518,10 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
|
||||||
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})
|
tp.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator})
|
||||||
return tp, err
|
return tp, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if the torrc file is present where expected
|
// check if the torrc file is present where expected
|
||||||
if _, err := os.Stat(path.Join(torDir, "torrc")); os.IsNotExist(err) {
|
if _, err := os.Stat(path.Join(torDir, "torrc")); os.IsNotExist(err) {
|
||||||
|
@ -527,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.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.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator})
|
||||||
tp.version = version
|
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)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package tor
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
path "path/filepath"
|
path "path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -41,7 +40,7 @@ func TestTorProvider(t *testing.T) {
|
||||||
|
|
||||||
dataDir := ""
|
dataDir := ""
|
||||||
var err error
|
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")
|
t.Fatalf("could not create data dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,18 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"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
|
||||||
|
@ -34,9 +42,28 @@ func (tb *TorrcBuilder) WithControlPort(port int) *TorrcBuilder {
|
||||||
return tb
|
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.
|
// 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
|
||||||
}
|
}
|
||||||
|
@ -52,6 +79,12 @@ 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
|
||||||
|
@ -65,7 +98,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 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.
|
// Preview provides a string representation of the torrc file without writing it to a file location.
|
||||||
|
|
Loading…
Reference in New Issue