forked from openprivacy/connectivity
Compare commits
3 Commits
master
...
i2p-provid
Author | SHA1 | Date |
---|---|---|
idk | 0cc0fb9a99 | |
idk | 683120487d | |
idk | 38253594d7 |
9
go.mod
9
go.mod
|
@ -4,6 +4,11 @@ go 1.13
|
|||
|
||||
require (
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.4
|
||||
git.openprivacy.ca/openprivacy/log v1.0.1
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2
|
||||
github.com/eyedeekay/go-i2pcontrol v0.0.0-20201227222421-6e9f31a29a91
|
||||
github.com/eyedeekay/sam3 v0.32.32
|
||||
github.com/ybbus/jsonrpc v1.1.1 // indirect
|
||||
github.com/ybbus/jsonrpc/v2 v2.1.6 // indirect
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
|
||||
)
|
||||
|
|
30
go.sum
30
go.sum
|
@ -2,34 +2,64 @@ git.openprivacy.ca/openprivacy/bine v0.0.2 h1:2uJyxOYfcYvpQAuRt5XWc81ZXrHuubdFsk
|
|||
git.openprivacy.ca/openprivacy/bine v0.0.2/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.3 h1:PSHUmNqaW7BZUX8n2eTDeNbjsuRe+t5Ae0Og+P+jDM0=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.3/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
||||
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.0 h1:Rvqm1weUdR4AOnJ79b1upHCc9vC/QF1rhSD2Um7sr1Y=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.0/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.1 h1:NWV5oBTatvlSzUE6wtB+UQCulgyMOtm4BXGd34evMys=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.1/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||
git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
|
||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eyedeekay/go-i2pcontrol v0.0.0-20201227215921-ea7ec65dc211 h1:YxUxfF1n6dnBF+pxH1mzCi6bqulpKAQLpWnCT5y9dKk=
|
||||
github.com/eyedeekay/go-i2pcontrol v0.0.0-20201227215921-ea7ec65dc211/go.mod h1:bhIQsVpbNNXMtcoZ9UF4hLQleOjaCgKGXiRRhNc8TOA=
|
||||
github.com/eyedeekay/go-i2pcontrol v0.0.0-20201227222421-6e9f31a29a91 h1:GjdUYLGSC4SBTKuuQ3K3+iLbFihCpBIO8VRjF7GNEYw=
|
||||
github.com/eyedeekay/go-i2pcontrol v0.0.0-20201227222421-6e9f31a29a91/go.mod h1:bhIQsVpbNNXMtcoZ9UF4hLQleOjaCgKGXiRRhNc8TOA=
|
||||
github.com/eyedeekay/sam3 v0.32.32 h1:9Ea1Ere5O8Clx8zYxKnvhrWy7R96Q4FvxlPskYf8VW0=
|
||||
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ybbus/jsonrpc v1.1.1 h1:43DAq5ijbxDPEXmlNpAwT74vFBR5VxQRqRWhWQPz9O0=
|
||||
github.com/ybbus/jsonrpc v1.1.1/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE=
|
||||
github.com/ybbus/jsonrpc/v2 v2.1.6 h1:++pboiaaD6TZ9FJ1JOBBRB/tPtR1njYzqz1iSZGv+3Y=
|
||||
github.com/ybbus/jsonrpc/v2 v2.1.6/go.mod h1:rIuG1+ORoiqocf9xs/v+ecaAVeo3zcZHQgInyKFMeg0=
|
||||
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-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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 h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
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/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
|
|
|
@ -0,0 +1,455 @@
|
|||
package tor
|
||||
|
||||
import (
|
||||
// "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/eyedeekay/go-i2pcontrol"
|
||||
"github.com/eyedeekay/sam3"
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
//"github.com/eyedeekay/sam3/helper"
|
||||
"git.openprivacy.ca/openprivacy/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
minStatusIntervalMs = 200
|
||||
maxStatusIntervalMs = 2000
|
||||
restartCooldown = time.Second * 30
|
||||
)
|
||||
|
||||
const (
|
||||
// CannotDialRicochetAddressError is thrown when a connection to a ricochet address fails.
|
||||
CannotDialRicochetAddressError = connectivity.Error("CannotDialRicochetAddressError")
|
||||
)
|
||||
|
||||
const (
|
||||
networkUnknown = -3
|
||||
torDown = -2
|
||||
networkDown = -1
|
||||
networkUp = 0
|
||||
)
|
||||
|
||||
// NoTorrcError is a typed error thrown to indicate start could not complete due to lack of a torrc file
|
||||
type NoTorrcError struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (e *NoTorrcError) Error() string { return fmt.Sprintf("torrc file does not exist at %v", e.path) }
|
||||
|
||||
type logWriter struct {
|
||||
level log.Level
|
||||
}
|
||||
|
||||
func (l *logWriter) Write(p []byte) (int, error) {
|
||||
log.Printf(l.level, "tor: %v", string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type garlicListenService struct {
|
||||
os *sam3.StreamListener
|
||||
tp *i2pProvider
|
||||
}
|
||||
|
||||
type i2pProvider struct {
|
||||
controlPort int
|
||||
//t *sam3.SAMConn
|
||||
dialer *sam3.StreamSession
|
||||
appDirectory string
|
||||
bundeledTorPath string
|
||||
lock sync.Mutex
|
||||
breakChan chan bool
|
||||
childListeners map[string]*garlicListenService
|
||||
statusCallback func(int, string)
|
||||
lastRestartTime time.Time
|
||||
//authenticator tor.Authenticator
|
||||
}
|
||||
|
||||
func (ols *garlicListenService) AddressFull() string {
|
||||
return ols.os.Addr().String()
|
||||
}
|
||||
|
||||
func (ols *garlicListenService) AddressIdentity() string {
|
||||
return ols.os.Addr().String()[:56]
|
||||
}
|
||||
|
||||
func (ols *garlicListenService) Accept() (net.Conn, error) {
|
||||
return ols.os.Accept()
|
||||
}
|
||||
|
||||
func (ols *garlicListenService) Close() {
|
||||
ols.tp.unregisterListener(ols.AddressIdentity())
|
||||
ols.os.Close()
|
||||
}
|
||||
|
||||
// GetBootstrapStatus returns an int 0-100 on the percent the bootstrapping of the underlying network is at and an optional string message
|
||||
// returns -1 on network disconnected
|
||||
// returns -2 on error
|
||||
func (tp *i2pProvider) GetBootstrapStatus() (int, string) {
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
|
||||
reseeding, err := i2pcontrol.Reseeding()
|
||||
if err != nil {
|
||||
return -2, err.Error()
|
||||
}
|
||||
if reseeding {
|
||||
return 25, "I2P Router is Reseeding"
|
||||
}
|
||||
|
||||
stat, err := i2pcontrol.Status()
|
||||
if err != nil {
|
||||
return -2, err.Error()
|
||||
}
|
||||
if strings.Contains(stat, "Rejecting") {
|
||||
return 50, "I2P Router is rejecting tunnels"
|
||||
}
|
||||
|
||||
netstat, err := i2pcontrol.Status()
|
||||
if err != nil {
|
||||
return -2, err.Error()
|
||||
}
|
||||
if strings.Contains(netstat, "ERROR") {
|
||||
return -1, netstat
|
||||
}
|
||||
return 100, ""
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) GetVersion() string {
|
||||
return "3.1"
|
||||
}
|
||||
|
||||
// WaitTillBootstrapped Blocks until underlying network is bootstrapped
|
||||
func (tp *i2pProvider) WaitTillBootstrapped() {
|
||||
for true {
|
||||
progress, _ := tp.GetBootstrapStatus()
|
||||
if progress == 100 {
|
||||
break
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) Listen(identity connectivity.PrivateKey, port int) (connectivity.ListenService, error) {
|
||||
var garlic = ""
|
||||
var privkey ed25519.PrivateKey
|
||||
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
|
||||
//if tp.t == nil {
|
||||
//return nil, errors.New("Tor Provider closed")
|
||||
//}
|
||||
|
||||
switch pk := identity.(type) {
|
||||
case ed25519.PrivateKey:
|
||||
privkey = pk
|
||||
gpubk := pk.Public()
|
||||
switch pubk := gpubk.(type) {
|
||||
case i2pkeys.I2PKeys:
|
||||
garlic = pubk.Addr().String()
|
||||
}
|
||||
}
|
||||
|
||||
// Hack around tor detached garlics not having a more obvious resume mechanism
|
||||
// So we use deterministic ports
|
||||
seedbytes := sha3.New224().Sum([]byte(garlic))
|
||||
localport := int(seedbytes[0]) + (int(seedbytes[1]) << 8)
|
||||
if localport < 1024 { // this is not uniformly random, but we don't need it to be
|
||||
localport += 1024
|
||||
}
|
||||
|
||||
localListener, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(localport))
|
||||
|
||||
// conf := &tor.ListenConf{NoWait: true, Version3: true, Key: identity, RemotePorts: []int{port}, Detach: true, DiscardKey: true, LocalListener: localListener}
|
||||
// os, err := tp.t.Listen(nil, conf)
|
||||
// if err != nil && strings.Contains(err.Error(), "550 Unspecified Tor error: garlic address collision") {
|
||||
// os = &tor.garlicService{Tor: tp.t, LocalListener: localListener, ID: garlic, Version3: true, Key: bineed255192.FromCryptoPrivateKey(privkey), ClientAuths: make(map[string]string, 0), RemotePorts: []int{port}}
|
||||
// err = nil
|
||||
// }
|
||||
// Not set in t.Listen if supplied, we want it to handle this however
|
||||
//os.CloseLocalListenerOnClose = true
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ols := &garlicListenService{os: garlic, tp: tp}
|
||||
tp.childListeners[ols.AddressIdentity()] = ols
|
||||
return ols, nil
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) Restart() {
|
||||
tp.callStatusCallback(0, "rebooting")
|
||||
tp.restart()
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) Open(hostname string) (net.Conn, string, error) {
|
||||
tp.lock.Lock()
|
||||
|
||||
if tp.t == nil {
|
||||
tp.lock.Unlock()
|
||||
return nil, hostname, errors.New("Tor is offline")
|
||||
}
|
||||
tp.lock.Unlock()
|
||||
|
||||
resolvedHostname := hostname
|
||||
if strings.HasPrefix(hostname, "ricochet:") {
|
||||
addrParts := strings.Split(hostname, ":")
|
||||
resolvedHostname = addrParts[1]
|
||||
}
|
||||
|
||||
conn, err := tp.dialer.Dial("tcp", resolvedHostname+".garlic:9878")
|
||||
return conn, resolvedHostname, err
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) Close() {
|
||||
for _, child := range tp.childListeners {
|
||||
child.Close()
|
||||
}
|
||||
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
tp.breakChan <- true
|
||||
if tp.t != nil {
|
||||
tp.t.Close()
|
||||
tp.t = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) SetStatusCallback(callback func(int, string)) {
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
tp.statusCallback = callback
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) callStatusCallback(prog int, status string) {
|
||||
tp.lock.Lock()
|
||||
if tp.statusCallback != nil {
|
||||
tp.statusCallback(prog, status)
|
||||
}
|
||||
tp.lock.Unlock()
|
||||
}
|
||||
|
||||
// NewTorACNWithAuth creates/starts a Tor ACN and returns a usable ACN object
|
||||
func NewTorACNWithAuth(appDirectory string, bundledTorPath string, controlPort int, authenticator tor.Authenticator) (connectivity.ACN, error) {
|
||||
tp, err := startTor(appDirectory, bundledTorPath, controlPort, authenticator)
|
||||
if err == nil {
|
||||
tp.dialer, err = tp.t.Dialer(nil, &tor.DialConf{Authenticator: authenticator})
|
||||
if err == nil {
|
||||
go tp.monitorRestart()
|
||||
}
|
||||
}
|
||||
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{})
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) checkVersion() error {
|
||||
// attempt connect to system tor
|
||||
log.Debugf("dialing system tor control port")
|
||||
/*controlport, err := dialControlPort(tp.controlPort)
|
||||
if err == nil {
|
||||
defer controlport.Close()
|
||||
err := tp.authenticator.Authenticate(controlport)
|
||||
if err == nil {
|
||||
log.Debugln("connected to control port")
|
||||
pinfo, err := controlport.ProtocolInfo()
|
||||
if err == nil {
|
||||
if minTorVersionReqs(pinfo.TorVersion) {
|
||||
log.Debugln("OK version " + pinfo.TorVersion)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Tor version not supported: %v", pinfo.TorVersion)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
return err
|
||||
}
|
||||
|
||||
func startTor(appDirectory string, bundledTorPath string, controlPort int, authenticator tor.Authenticator) (*i2pProvider, error) {
|
||||
torDir := path.Join(appDirectory, "tor")
|
||||
|
||||
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 := &i2pProvider{authenticator: authenticator, controlPort: controlPort, appDirectory: appDirectory, bundeledTorPath: bundledTorPath, childListeners: make(map[string]*garlicListenService), breakChan: make(chan bool), statusCallback: nil, lastRestartTime: time.Now().Add(-restartCooldown)}
|
||||
|
||||
log.Debugf("launching system tor")
|
||||
if err := tp.checkVersion(); err == nil {
|
||||
controlport, err := dialControlPort(tp.controlPort)
|
||||
if err == nil {
|
||||
log.Debugf("creating tor handler fom system tor")
|
||||
tp.t = createFromExisting(controlport, dataDir)
|
||||
}
|
||||
return tp, nil
|
||||
}
|
||||
|
||||
// check if the torrc file is present where expected
|
||||
if _, err := os.Stat(path.Join(torDir, "torrc")); os.IsNotExist(err) {
|
||||
err = &NoTorrcError{path.Join(torDir, "torrc")}
|
||||
log.Debugln(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if not, try running system tor
|
||||
if checkCmdlineTorVersion("tor") {
|
||||
t, err := tor.Start(nil, &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")})
|
||||
if err != nil {
|
||||
log.Debugf("Error connecting to self-run system tor: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
tp.t = t
|
||||
} else if bundledTorPath != "" && checkCmdlineTorVersion(bundledTorPath) {
|
||||
log.Debugln("attempting using bundled tor '" + bundledTorPath + "'")
|
||||
t, err := tor.Start(nil, &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
|
||||
}
|
||||
|
||||
err = tp.checkVersion()
|
||||
if err == nil {
|
||||
tp.t.DeleteDataDirOnClose = true
|
||||
return tp, nil
|
||||
}
|
||||
return nil, fmt.Errorf("could not connect to or start Tor that met requirments (min Tor version 0.3.5.x): %v", err)
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) GetPID() (int, error) {
|
||||
val, err := tp.t.Control.GetInfo("process/pid")
|
||||
if err == nil {
|
||||
return strconv.Atoi(val[0].Val)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) unregisterListener(id string) {
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
delete(tp.childListeners, id)
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) monitorRestart() {
|
||||
lastBootstrapProgress := networkUnknown
|
||||
interval := minStatusIntervalMs
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Millisecond * time.Duration(interval)):
|
||||
if interval < maxStatusIntervalMs {
|
||||
interval *= 2
|
||||
}
|
||||
|
||||
prog, status := tp.GetBootstrapStatus()
|
||||
|
||||
if prog == torDown && tp.t != nil {
|
||||
log.Warnf("monitorRestart calling tp.restart() with prog:%v\n", prog)
|
||||
tp.restart()
|
||||
}
|
||||
|
||||
if prog != lastBootstrapProgress {
|
||||
tp.callStatusCallback(prog, status)
|
||||
interval = minStatusIntervalMs
|
||||
lastBootstrapProgress = prog
|
||||
}
|
||||
|
||||
case <-tp.breakChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *i2pProvider) restart() {
|
||||
tp.lock.Lock()
|
||||
defer tp.lock.Unlock()
|
||||
if time.Now().Sub(tp.lastRestartTime) < restartCooldown {
|
||||
return
|
||||
}
|
||||
tp.lastRestartTime = time.Now()
|
||||
|
||||
for _, child := range tp.childListeners {
|
||||
delete(tp.childListeners, child.AddressIdentity())
|
||||
child.os.Close()
|
||||
}
|
||||
|
||||
tp.t.Close()
|
||||
tp.t = nil
|
||||
|
||||
for {
|
||||
newTp, err := startTor(tp.appDirectory, tp.bundeledTorPath, tp.controlPort, tp.authenticator)
|
||||
if err == nil {
|
||||
tp.t = newTp.t
|
||||
tp.dialer, _ = tp.t.Dialer(nil, &tor.DialConf{})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createFromExisting(controlport *control.Conn, datadir string) *tor.Tor {
|
||||
t := &tor.Tor{
|
||||
Process: nil,
|
||||
Control: controlport,
|
||||
ProcessCancelFunc: nil,
|
||||
DataDir: datadir,
|
||||
DeleteDataDirOnClose: true,
|
||||
DebugWriter: nil,
|
||||
StopProcessOnClose: false,
|
||||
GeoIPCreatedFile: "",
|
||||
GeoIPv6CreatedFile: "",
|
||||
}
|
||||
t.Control.DebugWriter = t.DebugWriter
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func checkCmdlineTorVersion(torCmd string) bool {
|
||||
cmd := exec.Command(torCmd, "--version")
|
||||
cmd.SysProcAttr = sysProcAttr
|
||||
out, err := cmd.CombinedOutput()
|
||||
re := regexp.MustCompile("[0-1]\\.[0-9]\\.[0-9]\\.[0-9]")
|
||||
sysTorVersion := re.Find(out)
|
||||
log.Infoln("tor version: " + string(sysTorVersion))
|
||||
return err == nil && minTorVersionReqs(string(sysTorVersion))
|
||||
}
|
||||
|
||||
// returns true if supplied version meets our min requirments
|
||||
// min requirement: 0.3.5.x
|
||||
func minTorVersionReqs(torversion string) bool {
|
||||
torversions := strings.Split(torversion, ".") //eg: 0.3.4.8 or 0.3.5.1-alpha
|
||||
log.Debugf("torversions: %v", torversions)
|
||||
tva, _ := strconv.Atoi(torversions[0])
|
||||
tvb, _ := strconv.Atoi(torversions[1])
|
||||
tvc, _ := strconv.Atoi(torversions[2])
|
||||
return tva > 0 || (tva == 0 && (tvb > 3 || (tvb == 3 && tvc >= 5)))
|
||||
}
|
||||
|
||||
func dialControlPort(port int) (*control.Conn, error) {
|
||||
textConn, err := textproto.Dial("tcp", "127.0.0.1:"+strconv.Itoa(port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return control.NewConn(textConn), nil
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package tor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"path"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getStatusCallback(progChan chan int) func(int, string) {
|
||||
return func(prog int, status string) {
|
||||
fmt.Printf("%v %v\n", prog, status)
|
||||
progChan <- prog
|
||||
}
|
||||
}
|
||||
|
||||
func TestTorProvider(t *testing.T) {
|
||||
progChan := make(chan int)
|
||||
log.SetLevel(log.LevelDebug)
|
||||
torpath := path.Join("..", "tmp/tor")
|
||||
log.Debugf("setting tor path %v", torpath)
|
||||
acn, err := NewTorACNWithAuth(path.Join("../testing/"), torpath, 9051, HashedPasswordAuthenticator{"examplehashedpassword"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
acn.SetStatusCallback(getStatusCallback(progChan))
|
||||
|
||||
progress := 0
|
||||
for progress < 100 {
|
||||
progress = <-progChan
|
||||
t.Logf("progress: %v", progress)
|
||||
}
|
||||
|
||||
acn.Close()
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package tor
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
// "errors"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getCheckdigits(pub ed25519.PublicKey) []byte {
|
||||
// Calculate checksum sha3(".onion checksum" || publicKey || version)
|
||||
checkstr := []byte(salt)
|
||||
checkstr = append(checkstr, pub...)
|
||||
checkstr = append(checkstr, version)
|
||||
checksum := sha3.Sum256(checkstr)
|
||||
return checksum[:2]
|
||||
}
|
||||
|
||||
// IsValidHostname returns true if the given address is a valid onion v3 address
|
||||
func IsValidHostname(address string) bool {
|
||||
if len(address) == V3HostnameLength {
|
||||
data, err := base32.StdEncoding.DecodeString(strings.ToUpper(address))
|
||||
if err == nil {
|
||||
pubkey := data[0:ed25519.PublicKeySize]
|
||||
if GetTorV3Hostname(ed25519.PublicKey(pubkey)) == address {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package tor
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerateHashedPassword(t *testing.T) {
|
||||
// 16:C15305F97789414B601259E3EC5E76B8E55FC56A9F562B713F3D2BA257
|
||||
hp := generateHashedPassword([8]byte{0xC1, 0x53, 0x05, 0xF9, 0x77, 0x89, 0x41, 0x4B}, "examplehashedpassword")
|
||||
if hp != "16:C15305F97789414B601259E3EC5E76B8E55FC56A9F562B713F3D2BA257" {
|
||||
t.Fatalf("hashed passwords do not match. Expected %s, got %s", "16:C15305F97789414B601259E3EC5E76B8E55FC56A9F562B713F3D2BA257", hp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateTorrc(t *testing.T) {
|
||||
path := "./torrc.test"
|
||||
password := "examplehashedpassword"
|
||||
err := NewTorrc().WithHashedPassword(password).Build(path)
|
||||
if err != nil {
|
||||
t.Errorf("Torrc file could not be written")
|
||||
}
|
||||
os.Remove(path)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// +build !windows
|
||||
|
||||
package tor
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var sysProcAttr = &syscall.SysProcAttr{}
|
|
@ -0,0 +1,9 @@
|
|||
// +build windows
|
||||
|
||||
package tor
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var sysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
Loading…
Reference in New Issue