Following libricochetgo's migration to bine and a generic Mixnet
interface.
This commit is contained in:
parent
07ffd780b0
commit
85a2c44891
65
app/app.go
65
app/app.go
|
@ -2,12 +2,12 @@ package app
|
|||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"cwtch.im/cwtch/connectivity/tor"
|
||||
"cwtch.im/cwtch/peer"
|
||||
"cwtch.im/cwtch/storage"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
type application struct {
|
||||
peers map[string]peer.CwtchPeer
|
||||
torManager *tor.Manager
|
||||
mn connectivity.Mixnet
|
||||
directory string
|
||||
mutex sync.Mutex
|
||||
primaryonion string
|
||||
|
@ -35,23 +35,17 @@ type Application interface {
|
|||
GetPeer(onion string) peer.CwtchPeer
|
||||
ListPeers() map[string]string
|
||||
|
||||
GetTorStatus() (map[string]string, error)
|
||||
//GetTorStatus() (map[string]string, error)
|
||||
|
||||
Shutdown()
|
||||
}
|
||||
|
||||
// NewApp creates a new app with some environment awareness and initializes a Tor Manager
|
||||
func NewApp(appDirectory string, torPath string) (Application, error) {
|
||||
log.Printf("NewApp(%v, %v)\n", appDirectory, torPath)
|
||||
app := &application{peers: make(map[string]peer.CwtchPeer), storage: make(map[string]storage.ProfileStore), directory: appDirectory}
|
||||
os.MkdirAll(path.Join(appDirectory, "tor"), 0700)
|
||||
func NewApp(mn connectivity.Mixnet, appDirectory string) Application {
|
||||
log.Printf("NewApp(%v)\n", appDirectory)
|
||||
app := &application{peers: make(map[string]peer.CwtchPeer), storage: make(map[string]storage.ProfileStore), directory: appDirectory, mn: mn}
|
||||
os.Mkdir(path.Join(app.directory, "profiles"), 0700)
|
||||
|
||||
err := app.startTor(torPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return app, nil
|
||||
return app
|
||||
}
|
||||
|
||||
func generateRandomFilename() string {
|
||||
|
@ -78,7 +72,8 @@ func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.startPeer(p)
|
||||
p.Init(app.mn)
|
||||
p.Listen()
|
||||
_, exists := app.peers[p.GetProfile().Onion]
|
||||
if exists {
|
||||
p.Shutdown()
|
||||
|
@ -104,7 +99,6 @@ func (app *application) LoadProfiles(password string) error {
|
|||
|
||||
p, err := fileStore.Load()
|
||||
if err != nil {
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -114,7 +108,8 @@ func (app *application) LoadProfiles(password string) error {
|
|||
log.Printf("Error: profile for onion %v already exists", p.GetProfile().Onion)
|
||||
continue
|
||||
}
|
||||
app.startPeer(p)
|
||||
p.Init(app.mn)
|
||||
p.Listen()
|
||||
app.mutex.Lock()
|
||||
app.peers[p.GetProfile().Onion] = p
|
||||
app.storage[p.GetProfile().Onion] = fileStore
|
||||
|
@ -126,40 +121,6 @@ func (app *application) LoadProfiles(password string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// startTor will create a local torrc if needed
|
||||
func (app *application) startTor(torPath string) error {
|
||||
// Creating a local cwtch tor server config for the user
|
||||
// creating $app.directory/torrc file
|
||||
// SOCKSPort socksPort
|
||||
// ControlPort controlPort
|
||||
torrc := path.Join(app.directory, "tor", "torrc")
|
||||
if _, err := os.Stat(torrc); os.IsNotExist(err) {
|
||||
log.Printf("writing torrc to: %v\n", torrc)
|
||||
file, err := os.Create(torrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nCookieAuthentication 0\nSafeSocks 1\n", 9050, 9051)
|
||||
file.Close()
|
||||
}
|
||||
|
||||
tm, err := tor.NewTorManager(9050, 9051, torPath, torrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app.torManager = tm
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *application) startPeer(peer peer.CwtchPeer) {
|
||||
go func() {
|
||||
e := peer.Listen()
|
||||
if e != nil {
|
||||
log.Fatalf("ERROR: peer %v has crashed with: %v\n", peer.GetProfile().Onion, e)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// ListPeers returns a map of onions to their profile's Name
|
||||
func (app *application) ListPeers() map[string]string {
|
||||
keys := map[string]string{}
|
||||
|
@ -182,15 +143,15 @@ func (app *application) GetPeer(onion string) peer.CwtchPeer {
|
|||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
// GetTorStatus returns tor control port bootstrap-phase status info in a map
|
||||
func (app *application) GetTorStatus() (map[string]string, error) {
|
||||
return app.torManager.GetStatus()
|
||||
}
|
||||
}*/
|
||||
|
||||
// Shutdown shutsdown all peers of an app and then the tormanager
|
||||
func (app *application) Shutdown() {
|
||||
for _, peer := range app.peers {
|
||||
peer.Shutdown()
|
||||
}
|
||||
app.torManager.Shutdown()
|
||||
}
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/peer/connections"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"github.com/c-bata/go-prompt"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path"
|
||||
"strings"
|
||||
|
@ -250,17 +250,16 @@ func main() {
|
|||
|
||||
quit := false
|
||||
|
||||
torPath, err := exec.LookPath("tor")
|
||||
if err != nil {
|
||||
log.Fatal("tor could not be found on this system. Please install it in the system $PATH")
|
||||
}
|
||||
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatalf("\nError: could not load current user: %v\n", err)
|
||||
}
|
||||
|
||||
app, err = app2.NewApp(path.Join(usr.HomeDir, ".cwtch"), torPath)
|
||||
mn, err := connectivity.StartTor(path.Join(usr.HomeDir, ".cwtch"), "")
|
||||
if err != nil {
|
||||
log.Fatalf("\nError connecting to Tor: %v\n", err)
|
||||
}
|
||||
app = app2.NewApp(mn, path.Join(usr.HomeDir, ".cwtch"))
|
||||
if err != nil {
|
||||
log.Fatalf("Error initializing application: %v", err)
|
||||
}
|
||||
|
@ -307,6 +306,12 @@ func main() {
|
|||
quit = true
|
||||
case "/new-profile":
|
||||
if len(commands) == 2 {
|
||||
name := strings.Trim(commands[1], " ")
|
||||
if name == "" {
|
||||
fmt.Printf("Error creating profile, usage: %v\n", usages[commands[0]])
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Print("** WARNING: PASSWORDS CANNOT BE RECOVERED! **\n")
|
||||
|
||||
password := ""
|
||||
|
@ -329,17 +334,17 @@ func main() {
|
|||
}
|
||||
|
||||
if failcount >= 3 {
|
||||
fmt.Printf("Error creating profile for %v: Your password entries must match!\n", commands[1])
|
||||
fmt.Printf("Error creating profile for %v: Your password entries must match!\n", name)
|
||||
} else {
|
||||
p, err := app.CreatePeer(commands[1], password)
|
||||
p, err := app.CreatePeer(name, password)
|
||||
if err == nil {
|
||||
stopGroupFollow()
|
||||
fmt.Printf("\nNew profile created for %v\n", commands[1])
|
||||
fmt.Printf("\nNew profile created for %v\n", name)
|
||||
peer = p
|
||||
suggestions = append(suggestionsBase, suggestionsSelectedProfile...)
|
||||
|
||||
} else {
|
||||
fmt.Printf("\nError creating profile for %v: %v\n", commands[1], err)
|
||||
fmt.Printf("\nError creating profile for %v: %v\n", name, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -597,6 +602,6 @@ func main() {
|
|||
}
|
||||
|
||||
app.Shutdown()
|
||||
mn.Close()
|
||||
os.Exit(0)
|
||||
|
||||
}
|
||||
|
|
|
@ -1,168 +0,0 @@
|
|||
package tor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/yawning/bulb"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Manager checks connectivity of the Tor process used to support Cwtch
|
||||
type Manager struct {
|
||||
socksPort int
|
||||
controlPort int
|
||||
process *exec.Cmd
|
||||
}
|
||||
|
||||
// NewTorManager Instantiates a new connection manager, returns non-nil error if it fails to connect to a tor daemon on the given ports.
|
||||
func NewTorManager(socksPort int, controlPort int, torPath string, torrc string) (*Manager, error) {
|
||||
torManager := new(Manager)
|
||||
torManager.socksPort = socksPort
|
||||
torManager.controlPort = controlPort
|
||||
|
||||
err := torManager.TestConnection()
|
||||
|
||||
if err == nil {
|
||||
log.Printf("using existing tor proxy")
|
||||
return torManager, nil
|
||||
}
|
||||
|
||||
// try to start tor
|
||||
|
||||
cmd := exec.Command(torPath, "-f", torrc)
|
||||
|
||||
// on Android, home can be set to '/' which is not writeable
|
||||
if os.Getenv("HOME") == "" {
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("HOME=%s", path.Dir(torrc)))
|
||||
}
|
||||
|
||||
log.Printf("starting local tor proxy")
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Printf("starting tor failed %v", err)
|
||||
return nil, err
|
||||
}
|
||||
torManager.process = cmd
|
||||
|
||||
// for 30 seconds check every 5 if tor is up and working
|
||||
for i := 0; i < 6; i++ {
|
||||
time.Sleep(time.Second * 5)
|
||||
err = torManager.TestConnection()
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return torManager, err
|
||||
}
|
||||
|
||||
type proxyStatus int
|
||||
|
||||
const (
|
||||
proxyStatusOK proxyStatus = iota
|
||||
proxyStatusWrongType
|
||||
proxyStatusCannotConnect
|
||||
proxyStatusTimeout
|
||||
)
|
||||
|
||||
// Shutdown kills the managed Tor Process
|
||||
func (tm *Manager) Shutdown() {
|
||||
if tm.process != nil {
|
||||
if err := tm.process.Process.Kill(); err != nil {
|
||||
log.Fatal("failed to kill process: ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Detect whether a proxy is connectable and is a Tor proxy
|
||||
func checkTorProxy(proxyAddress string) proxyStatus {
|
||||
// A trick to do this without making an outward connection is,
|
||||
// paradoxically, to try to open it as http.
|
||||
// This is documented in section 4 here: https://github.com/torproject/torspec/blob/master/socks-extensions.txt
|
||||
client := &http.Client{Timeout: 2 * time.Second}
|
||||
response, err := client.Get("http://" + proxyAddress + "/")
|
||||
if err != nil {
|
||||
switch t := err.(type) {
|
||||
case *url.Error:
|
||||
switch t.Err.(type) {
|
||||
case *net.OpError: // Network-level error. Will in turn contain a os.SyscallError
|
||||
return proxyStatusCannotConnect
|
||||
default:
|
||||
// http.error unfortunately not exported, need to match on string
|
||||
// net/http: request canceled
|
||||
if strings.Index(t.Err.Error(), "request canceled") != -1 {
|
||||
return proxyStatusTimeout
|
||||
}
|
||||
}
|
||||
}
|
||||
// Protocol-level errors mean that http failed, so it's not Tor
|
||||
return proxyStatusWrongType
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.Status != "501 Tor is not an HTTP Proxy" {
|
||||
return proxyStatusWrongType
|
||||
}
|
||||
return proxyStatusOK
|
||||
}
|
||||
|
||||
func proxyStatusMessage(status proxyStatus) string {
|
||||
switch status {
|
||||
case proxyStatusWrongType:
|
||||
return "Proxy specified is not a Tor proxy"
|
||||
case proxyStatusCannotConnect:
|
||||
return "Cannot connect to Tor proxy"
|
||||
case proxyStatusTimeout:
|
||||
return "Proxy timeout"
|
||||
default:
|
||||
return "Unknown proxy error"
|
||||
}
|
||||
}
|
||||
|
||||
// TestConnection returns nil if both the socks and control ports of the Tor connection are active, otherwise it returns an error.
|
||||
func (tm *Manager) TestConnection() error {
|
||||
proxyStatus := checkTorProxy(fmt.Sprintf("127.0.0.1:%d", tm.socksPort))
|
||||
controlAddress := fmt.Sprintf("127.0.0.1:%d", tm.controlPort)
|
||||
if proxyStatus == proxyStatusOK {
|
||||
c, err := bulb.Dial("tcp4", controlAddress)
|
||||
if c != nil {
|
||||
c.Close()
|
||||
}
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("could not connect to Tor Control Port %v %v", tm.controlPort, err)
|
||||
}
|
||||
return errors.New(proxyStatusMessage(proxyStatus))
|
||||
}
|
||||
|
||||
// GetStatus returns tor control port bootstrap-phase status info in a map
|
||||
func (tm *Manager) GetStatus() (map[string]string, error) {
|
||||
controlAddress := fmt.Sprintf("127.0.0.1:%d", tm.controlPort)
|
||||
c, err := bulb.Dial("tcp4", controlAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.Close()
|
||||
c.Request("AUTHENTICATE \"\"")
|
||||
resp, err := c.Request("GETINFO status/bootstrap-phase")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := strings.SplitN(resp.RawLines[0], " ", 5)
|
||||
var resps = make(map[string]string)
|
||||
for _, l := range lines {
|
||||
kv := strings.Split(l, "=")
|
||||
if len(kv) == 2 {
|
||||
resps[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
return resps, nil
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package tor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTorManager(t *testing.T) {
|
||||
tor, err := exec.LookPath("tor")
|
||||
if err != nil {
|
||||
t.Errorf("tor not found in PATH")
|
||||
}
|
||||
os.Remove("/tmp/torrc")
|
||||
file, _ := os.Create("/tmp/torrc")
|
||||
fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nDataDirectory /tmp/tor\n", 10050, 10051)
|
||||
file.Close()
|
||||
tm, err := NewTorManager(10050, 10051, tor, "/tmp/torrc")
|
||||
if err != nil {
|
||||
t.Errorf("creating a new tor manager failed: %v", err)
|
||||
} else {
|
||||
|
||||
tm2, err := NewTorManager(10050, 10051, tor, "/tmp/torrc")
|
||||
if err != nil {
|
||||
t.Errorf("creating a new tor manager failed: %v", err)
|
||||
}
|
||||
tm2.Shutdown() // should not noop
|
||||
}
|
||||
tm.Shutdown()
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -14,11 +15,13 @@ type Manager struct {
|
|||
serverConnections map[string]*PeerServerConnection
|
||||
lock sync.Mutex
|
||||
breakChannel chan bool
|
||||
mn connectivity.Mixnet
|
||||
}
|
||||
|
||||
// NewConnectionsManager creates a new instance of Manager.
|
||||
func NewConnectionsManager() *Manager {
|
||||
func NewConnectionsManager(mn connectivity.Mixnet) *Manager {
|
||||
m := new(Manager)
|
||||
m.mn = mn
|
||||
m.peerConnections = make(map[string]*PeerPeerConnection)
|
||||
m.serverConnections = make(map[string]*PeerServerConnection)
|
||||
m.breakChannel = make(chan bool)
|
||||
|
@ -32,7 +35,7 @@ func (m *Manager) ManagePeerConnection(host string, profile *model.Profile, data
|
|||
|
||||
_, exists := m.peerConnections[host]
|
||||
if !exists {
|
||||
ppc := NewPeerPeerConnection(host, profile, dataHandler, aif)
|
||||
ppc := NewPeerPeerConnection(m.mn, host, profile, dataHandler, aif)
|
||||
go ppc.Run()
|
||||
m.peerConnections[host] = ppc
|
||||
return ppc
|
||||
|
@ -46,7 +49,7 @@ func (m *Manager) ManageServerConnection(host string, handler func(string, *prot
|
|||
|
||||
_, exists := m.serverConnections[host]
|
||||
if !exists {
|
||||
psc := NewPeerServerConnection(host)
|
||||
psc := NewPeerServerConnection(m.mn, host)
|
||||
go psc.Run()
|
||||
psc.GroupMessageHandler = handler
|
||||
m.serverConnections[host] = psc
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package connections
|
||||
|
||||
import (
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConnectionsManager(t *testing.T) {
|
||||
// TODO We need to encapsulate connections behind a well defined interface for tesintg
|
||||
NewConnectionsManager()
|
||||
NewConnectionsManager(connectivity.LocalProvider())
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||
"log"
|
||||
"time"
|
||||
|
@ -22,11 +23,13 @@ type PeerPeerConnection struct {
|
|||
profile *model.Profile
|
||||
dataHandler func(string, []byte) []byte
|
||||
aif application.ApplicationInstanceFactory
|
||||
mn connectivity.Mixnet
|
||||
}
|
||||
|
||||
// NewPeerPeerConnection creates a new peer connection for the given hostname and profile.
|
||||
func NewPeerPeerConnection(peerhostname string, profile *model.Profile, dataHandler func(string, []byte) []byte, aif application.ApplicationInstanceFactory) *PeerPeerConnection {
|
||||
func NewPeerPeerConnection(mn connectivity.Mixnet, peerhostname string, profile *model.Profile, dataHandler func(string, []byte) []byte, aif application.ApplicationInstanceFactory) *PeerPeerConnection {
|
||||
ppc := new(PeerPeerConnection)
|
||||
ppc.mn = mn
|
||||
ppc.PeerHostname = peerhostname
|
||||
ppc.profile = profile
|
||||
ppc.dataHandler = dataHandler
|
||||
|
@ -107,7 +110,7 @@ func (ppc *PeerPeerConnection) WaitTilAuthenticated() {
|
|||
// Run manages the setup and teardown of a peer->peer connection
|
||||
func (ppc *PeerPeerConnection) Run() error {
|
||||
ppc.state = CONNECTING
|
||||
rc, err := goricochet.Open(ppc.PeerHostname)
|
||||
rc, err := goricochet.Open(ppc.mn, ppc.PeerHostname)
|
||||
if err == nil {
|
||||
rc.TraceLog(false)
|
||||
ppc.connection = rc
|
||||
|
|
|
@ -5,10 +5,12 @@ import (
|
|||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/peer/peer"
|
||||
"cwtch.im/cwtch/protocol"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"net"
|
||||
|
@ -59,13 +61,14 @@ func TestPeerPeerConnection(t *testing.T) {
|
|||
|
||||
profile := model.GenerateNewProfile("alice")
|
||||
hostname := identity.Hostname()
|
||||
ppc := NewPeerPeerConnection("127.0.0.1:5452|"+hostname, profile, nil, application.ApplicationInstanceFactory{})
|
||||
ppc := NewPeerPeerConnection(connectivity.LocalProvider(), "127.0.0.1:5452|"+hostname, profile, nil, application.ApplicationInstanceFactory{})
|
||||
|
||||
tp := new(TestPeer)
|
||||
tp.Init()
|
||||
go runtestpeer(t, tp, identity)
|
||||
state := ppc.GetState()
|
||||
if state != DISCONNECTED {
|
||||
fmt.Println("ERROR state should be disconnected")
|
||||
t.Errorf("new connections should start in disconnected state")
|
||||
}
|
||||
go ppc.Run()
|
||||
|
@ -80,5 +83,4 @@ func TestPeerPeerConnection(t *testing.T) {
|
|||
if tp.ReceivedGroupInvite == false {
|
||||
t.Errorf("should have received an group invite packet")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"git.openprivacy.ca/openprivacy/libricochet-go"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"log"
|
||||
|
@ -22,13 +23,15 @@ type PeerServerConnection struct {
|
|||
Server string
|
||||
state ConnectionState
|
||||
connection *connection.Connection
|
||||
mn connectivity.Mixnet
|
||||
|
||||
GroupMessageHandler func(string, *protocol.GroupMessage)
|
||||
}
|
||||
|
||||
// NewPeerServerConnection creates a new Peer->Server outbound connection
|
||||
func NewPeerServerConnection(serverhostname string) *PeerServerConnection {
|
||||
func NewPeerServerConnection(mn connectivity.Mixnet, serverhostname string) *PeerServerConnection {
|
||||
psc := new(PeerServerConnection)
|
||||
psc.mn = mn
|
||||
psc.Server = serverhostname
|
||||
psc.Init()
|
||||
return psc
|
||||
|
@ -52,7 +55,7 @@ func (psc *PeerServerConnection) WaitTilAuthenticated() {
|
|||
// Run manages the setup and teardown of a peer server connection
|
||||
func (psc *PeerServerConnection) Run() error {
|
||||
log.Printf("Connecting to %v", psc.Server)
|
||||
rc, err := goricochet.Open(psc.Server)
|
||||
rc, err := goricochet.Open(psc.mn, psc.Server)
|
||||
if err == nil {
|
||||
rc.TraceLog(true)
|
||||
psc.connection = rc
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"git.openprivacy.ca/openprivacy/libricochet-go"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"net"
|
||||
|
@ -72,7 +73,7 @@ func TestPeerServerConnection(t *testing.T) {
|
|||
go runtestserver(t, ts, identity)
|
||||
onionAddr := identity.Hostname()
|
||||
|
||||
psc := NewPeerServerConnection("127.0.0.1:5451|" + onionAddr)
|
||||
psc := NewPeerServerConnection(connectivity.LocalProvider(), "127.0.0.1:5451|"+onionAddr)
|
||||
numcalls := 0
|
||||
psc.GroupMessageHandler = func(s string, gm *protocol.GroupMessage) {
|
||||
numcalls++
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
@ -19,6 +20,7 @@ import (
|
|||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// cwtchPeer manages incoming and outgoing connections and all processing for a Cwtch cwtchPeer
|
||||
|
@ -26,17 +28,19 @@ type cwtchPeer struct {
|
|||
connection.AutoConnectionHandler
|
||||
Profile *model.Profile
|
||||
app *application.RicochetApplication
|
||||
mn connectivity.Mixnet
|
||||
mutex sync.Mutex
|
||||
connectionsManager *connections.Manager
|
||||
dataHandler func(string, []byte) []byte
|
||||
//handlers map[string]func(*application.ApplicationInstance) func() channels.Handler
|
||||
aif application.ApplicationInstanceFactory
|
||||
aif application.ApplicationInstanceFactory
|
||||
shutdown bool
|
||||
}
|
||||
|
||||
// CwtchPeer provides us with a way of testing systems built on top of cwtch without having to
|
||||
// directly implement a cwtchPeer.
|
||||
type CwtchPeer interface {
|
||||
Init()
|
||||
Init(connectivity.Mixnet)
|
||||
PeerWithOnion(string) *connections.PeerPeerConnection
|
||||
InviteOnionToGroup(string, string) error
|
||||
|
||||
|
@ -66,7 +70,7 @@ type CwtchPeer interface {
|
|||
SetApplicationInstanceFactory(factory application.ApplicationInstanceFactory)
|
||||
SetPeerDataHandler(func(string, []byte) []byte)
|
||||
|
||||
Listen() error
|
||||
Listen()
|
||||
Shutdown()
|
||||
}
|
||||
|
||||
|
@ -74,7 +78,7 @@ type CwtchPeer interface {
|
|||
func NewCwtchPeer(name string) CwtchPeer {
|
||||
cp := new(cwtchPeer)
|
||||
cp.Profile = model.GenerateNewProfile(name)
|
||||
cp.Init()
|
||||
cp.shutdown = false
|
||||
return cp
|
||||
}
|
||||
|
||||
|
@ -82,13 +86,13 @@ func NewCwtchPeer(name string) CwtchPeer {
|
|||
func FromProfile(profile *model.Profile) CwtchPeer {
|
||||
cp := new(cwtchPeer)
|
||||
cp.Profile = profile
|
||||
cp.Init()
|
||||
return cp
|
||||
}
|
||||
|
||||
// Init instantiates a cwtchPeer
|
||||
func (cp *cwtchPeer) Init() {
|
||||
cp.connectionsManager = connections.NewConnectionsManager()
|
||||
func (cp *cwtchPeer) Init(mn connectivity.Mixnet) {
|
||||
cp.mn = mn
|
||||
cp.connectionsManager = connections.NewConnectionsManager(cp.mn)
|
||||
go cp.connectionsManager.AttemptReconnections()
|
||||
}
|
||||
|
||||
|
@ -295,11 +299,25 @@ func (cp *cwtchPeer) ContactRequest(name string, message string) string {
|
|||
return "Accepted"
|
||||
}
|
||||
|
||||
func (cp *cwtchPeer) Listen() {
|
||||
go func() {
|
||||
for !cp.shutdown {
|
||||
e := cp.listenFn()
|
||||
if e != nil {
|
||||
// TODO: was panic, then fatal
|
||||
fmt.Printf("ERROR: peer %v has crashed with: %v\n", cp.GetProfile().Onion, e)
|
||||
}
|
||||
// listenFn failed, wait 5 seconds and try again
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Listen sets up an onion listener to process incoming cwtch messages
|
||||
func (cp *cwtchPeer) Listen() error {
|
||||
cwtchpeer := new(application.RicochetApplication)
|
||||
l, err := application.SetupOnionV3("127.0.0.1:9051", "tcp4", "", cp.Profile.Ed25519PrivateKey, cp.GetProfile().Onion, 9878)
|
||||
if err != nil && fmt.Sprintf("%v", err) != "550 Unspecified Tor error: Onion address collision" {
|
||||
func (cp *cwtchPeer) listenFn() error {
|
||||
ra := new(application.RicochetApplication)
|
||||
onionService, err := cp.mn.Listen(cp.Profile.Ed25519PrivateKey, application.RicochetPort)
|
||||
if err != nil /*&& fmt.Sprintf("%v", err) != "550 Unspecified Tor error: Onion address collision"*/ {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -307,7 +325,7 @@ func (cp *cwtchPeer) Listen() error {
|
|||
af.Init()
|
||||
af.AddHandler("im.cwtch.peer", func(rai *application.ApplicationInstance) func() channels.Handler {
|
||||
cpi := new(CwtchPeerInstance)
|
||||
cpi.Init(rai, cwtchpeer)
|
||||
cpi.Init(rai, ra)
|
||||
return func() channels.Handler {
|
||||
cpc := new(peer.CwtchPeerChannel)
|
||||
cpc.Handler = &CwtchPeerHandler{Onion: rai.RemoteHostname, Peer: cp}
|
||||
|
@ -318,7 +336,7 @@ func (cp *cwtchPeer) Listen() error {
|
|||
if cp.dataHandler != nil {
|
||||
af.AddHandler("im.cwtch.peer.data", func(rai *application.ApplicationInstance) func() channels.Handler {
|
||||
cpi := new(CwtchPeerInstance)
|
||||
cpi.Init(rai, cwtchpeer)
|
||||
cpi.Init(rai, ra)
|
||||
return func() channels.Handler {
|
||||
cpc := new(peer.CwtchPeerDataChannel)
|
||||
cpc.Handler = &CwtchPeerHandler{Onion: rai.RemoteHostname, Peer: cp, DataHandler: cp.dataHandler}
|
||||
|
@ -332,15 +350,16 @@ func (cp *cwtchPeer) Listen() error {
|
|||
af.AddHandler(handlers[i], cp.aif.GetHandler(handlers[i]))
|
||||
}
|
||||
|
||||
cwtchpeer.InitV3(cp.Profile.Name, identity.InitializeV3(cp.Profile.Name, &cp.Profile.Ed25519PrivateKey, &cp.Profile.Ed25519PublicKey), af, cp)
|
||||
log.Printf("Running cwtch peer on %v", l.Addr().String())
|
||||
cp.app = cwtchpeer
|
||||
cwtchpeer.Run(l)
|
||||
ra.Init(cp.mn, cp.Profile.Name, identity.InitializeV3(cp.Profile.Name, &cp.Profile.Ed25519PrivateKey, &cp.Profile.Ed25519PublicKey), af, cp)
|
||||
log.Printf("Running cwtch peer on %v", onionService.AddressFull())
|
||||
cp.app = ra
|
||||
ra.Run(onionService)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown kills all connections and cleans up all goroutines for the peer
|
||||
func (cp *cwtchPeer) Shutdown() {
|
||||
cp.shutdown = true
|
||||
cp.connectionsManager.Shutdown()
|
||||
if cp.app != nil {
|
||||
cp.app.Shutdown()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package peer
|
||||
|
||||
import (
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -21,7 +22,9 @@ func TestCwtchPeerGenerate(t *testing.T) {
|
|||
func TestTrustPeer(t *testing.T) {
|
||||
groupName := "test.server"
|
||||
alice := NewCwtchPeer("alice")
|
||||
alice.Init(connectivity.LocalProvider())
|
||||
bob := NewCwtchPeer("bob")
|
||||
bob.Init(connectivity.LocalProvider())
|
||||
|
||||
bobOnion := bob.GetProfile().Onion
|
||||
aliceOnion := alice.GetProfile().Onion
|
||||
|
|
|
@ -2,8 +2,10 @@ package main
|
|||
|
||||
import (
|
||||
cwtchserver "cwtch.im/cwtch/server"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -15,9 +17,16 @@ func main() {
|
|||
|
||||
serverConfig := cwtchserver.LoadConfig(configDir, serverConfigFile)
|
||||
|
||||
mn, err := connectivity.StartTor(path.Join(configDir, "tor"), "")
|
||||
if err != nil {
|
||||
log.Fatalf("\nError connecting to Tor: %v\n", err)
|
||||
}
|
||||
defer mn.Close()
|
||||
|
||||
server := new(cwtchserver.Server)
|
||||
log.Printf("starting cwtch server...")
|
||||
|
||||
// TODO load params from .cwtch/server.conf or command line flag
|
||||
server.Run(serverConfig)
|
||||
// TODO: respond to HUP so t.Close is gracefully called
|
||||
server.Run(mn, serverConfig)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"cwtch.im/cwtch/storage"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/application"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"log"
|
||||
)
|
||||
|
||||
|
@ -21,12 +21,14 @@ type Server struct {
|
|||
|
||||
// Run starts a server with the given privateKey
|
||||
// TODO: surface errors
|
||||
func (s *Server) Run(serverConfig Config) {
|
||||
// TODO: handle HUP/KILL signals to exit and close Tor gracefully
|
||||
// TODO: handle user input to exit
|
||||
func (s *Server) Run(mn connectivity.Mixnet, serverConfig Config) {
|
||||
s.config = serverConfig
|
||||
cwtchserver := new(application.RicochetApplication)
|
||||
s.metricsPack.Start(cwtchserver, serverConfig.ConfigDir, s.config.ServerReporting.LogMetricsToFile)
|
||||
|
||||
l, err := application.SetupOnionV3("127.0.0.1:9051", "tcp4", "", s.config.PrivateKey, utils.GetTorV3Hostname(s.config.PublicKey), 9878)
|
||||
listenService, err := mn.Listen(s.config.PrivateKey, application.RicochetPort)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("error setting up onion service: %v", err)
|
||||
|
@ -66,10 +68,10 @@ func (s *Server) Run(serverConfig Config) {
|
|||
}
|
||||
})
|
||||
|
||||
cwtchserver.InitV3("cwtch server for "+l.Addr().String(), s.config.Identity(), af, new(application.AcceptAllContactManager))
|
||||
log.Printf("cwtch server running on cwtch:%s", l.Addr().String())
|
||||
cwtchserver.Init(mn, "cwtch server for "+listenService.AddressIdentity(), s.config.Identity(), af, new(application.AcceptAllContactManager))
|
||||
log.Printf("cwtch server running on cwtch:%s", listenService.AddressFull())
|
||||
s.app = cwtchserver
|
||||
s.app.Run(l)
|
||||
s.app.Run(listenService)
|
||||
}
|
||||
|
||||
// Shutdown kills the app closing all connections and freeing all goroutines
|
||||
|
|
|
@ -6,9 +6,11 @@ import (
|
|||
"cwtch.im/cwtch/peer/connections"
|
||||
cwtchserver "cwtch.im/cwtch/server"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"golang.org/x/net/proxy"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -52,7 +54,7 @@ func serverCheck(t *testing.T, serverAddr string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func waitForPeerConnection(t *testing.T, peer peer.CwtchPeer, server string) {
|
||||
func waitForPeerServerConnection(t *testing.T, peer peer.CwtchPeer, server string) {
|
||||
for {
|
||||
servers := peer.GetServers()
|
||||
state, ok := servers[server]
|
||||
|
@ -61,6 +63,7 @@ func waitForPeerConnection(t *testing.T, peer peer.CwtchPeer, server string) {
|
|||
t.Fatalf("%v could not connect to %v", peer.GetProfile().Onion, server)
|
||||
}
|
||||
if state != connections.AUTHENTICATED {
|
||||
fmt.Printf("peer %v waiting connect to server %v, currently: %v\n", peer.GetProfile().Onion, server, connections.ConnectionStateName[state])
|
||||
time.Sleep(time.Second * 10)
|
||||
continue
|
||||
}
|
||||
|
@ -72,11 +75,37 @@ func waitForPeerConnection(t *testing.T, peer peer.CwtchPeer, server string) {
|
|||
return
|
||||
}
|
||||
|
||||
func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) {
|
||||
for {
|
||||
peers := peera.GetPeers()
|
||||
state, ok := peers[peerb.GetProfile().Onion]
|
||||
if ok {
|
||||
if state == connections.FAILED {
|
||||
t.Fatalf("%v could not connect to %v", peera.GetProfile().Onion, peerb.GetProfile().Onion)
|
||||
}
|
||||
if state != connections.AUTHENTICATED {
|
||||
fmt.Printf("peer% v waiting connect to peer %v, currently: %v\n", peera.GetProfile().Onion, peerb.GetProfile().Onion, connections.ConnectionStateName[state])
|
||||
time.Sleep(time.Second * 10)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("peer peer connectiond %v should have entry for server %v", peers, peerb.GetProfile().Onion)
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestCwtchPeerIntegration(t *testing.T) {
|
||||
// Hide logging "noise"
|
||||
log.SetOutput(ioutil.Discard)
|
||||
numGoRoutinesStart := runtime.NumGoroutine()
|
||||
|
||||
mn, err := connectivity.StartTor(".", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not start Tor: %v", err)
|
||||
}
|
||||
|
||||
// ***** Cwtch Server managment *****
|
||||
var server *cwtchserver.Server
|
||||
|
||||
|
@ -88,10 +117,11 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
fmt.Println("No server found!")
|
||||
server = new(cwtchserver.Server)
|
||||
fmt.Println("Starting cwtch server...")
|
||||
os.Remove("server-test.json")
|
||||
config := cwtchserver.LoadConfig(".", "server-test.json")
|
||||
identity := config.Identity()
|
||||
serverAddr = identity.Hostname()
|
||||
go server.Run(config)
|
||||
go server.Run(mn, config)
|
||||
|
||||
// let tor get established
|
||||
fmt.Printf("Establishing Tor hidden service: %v...\n", serverAddr)
|
||||
|
@ -105,17 +135,20 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
|
||||
fmt.Println("Creating Alice...")
|
||||
alice := peer.NewCwtchPeer("Alice")
|
||||
go alice.Listen()
|
||||
alice.Init(mn)
|
||||
alice.Listen()
|
||||
fmt.Println("Alice created:", alice.GetProfile().Onion)
|
||||
|
||||
fmt.Println("Creating Bob...")
|
||||
bob := peer.NewCwtchPeer("Bob")
|
||||
go bob.Listen()
|
||||
bob.Init(mn)
|
||||
bob.Listen()
|
||||
fmt.Println("Bob created:", bob.GetProfile().Onion)
|
||||
|
||||
fmt.Println("Creating Carol...")
|
||||
carol := peer.NewCwtchPeer("Carol")
|
||||
go carol.Listen()
|
||||
carol.Init(mn)
|
||||
carol.Listen()
|
||||
fmt.Println("Carol created:", carol.GetProfile().Onion)
|
||||
|
||||
fmt.Println("Waiting for Alice, Bob, and Carol to connection with onion network...")
|
||||
|
@ -142,8 +175,17 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
fmt.Println("Bob joining server...")
|
||||
bob.JoinServer(serverAddr)
|
||||
|
||||
fmt.Println("Waiting for peerings and server joins...")
|
||||
time.Sleep(time.Second * 240)
|
||||
fmt.Println("Waiting for alice to join server...")
|
||||
waitForPeerServerConnection(t, alice, serverAddr)
|
||||
|
||||
fmt.Println("Waiting for bob to join server...")
|
||||
waitForPeerServerConnection(t, bob, serverAddr)
|
||||
|
||||
fmt.Println("Waiting for alice and Bob to peer...")
|
||||
waitForPeerPeerConnection(t, alice, bob)
|
||||
|
||||
/*fmt.Println("Waiting for peerings and server joins...")
|
||||
time.Sleep(time.Second * 240)*/
|
||||
|
||||
fmt.Println("Alice inviting Bob to group...")
|
||||
err = alice.InviteOnionToGroup(bob.GetProfile().Onion, groupID)
|
||||
|
@ -193,8 +235,8 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
time.Sleep(time.Second * 110)
|
||||
*/
|
||||
// Wait for them to join the server
|
||||
waitForPeerConnection(t, alice, serverAddr)
|
||||
waitForPeerConnection(t, bob, serverAddr)
|
||||
waitForPeerServerConnection(t, alice, serverAddr)
|
||||
waitForPeerServerConnection(t, bob, serverAddr)
|
||||
//numGouRoutinesPostServerConnect := runtime.NumGoroutine()
|
||||
|
||||
// ***** Conversation *****
|
||||
|
@ -246,7 +288,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
|
||||
fmt.Println("Carol joining server...")
|
||||
carol.JoinServer(serverAddr)
|
||||
waitForPeerConnection(t, carol, serverAddr)
|
||||
waitForPeerServerConnection(t, carol, serverAddr)
|
||||
numGoRotinesPostCarolConnect := runtime.NumGoroutine()
|
||||
|
||||
fmt.Println("Bob> ", bobLines[2])
|
||||
|
|
|
@ -16,7 +16,6 @@ go test ${1} -coverprofile=server.listen.cover.out -v ./server/listen
|
|||
go test ${1} -coverprofile=server.send.cover.out -v ./server/send
|
||||
go test ${1} -coverprofile=server.metrics.cover.out -v ./server/metrics
|
||||
go test ${1} -coverprofile=server.cover.out -v ./server
|
||||
go test ${1} -coverprofile=tor.cover.out -v ./connectivity/tor
|
||||
echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \
|
||||
awk '{if($1 != last) {print $0;last=$1}}' >> coverage.out
|
||||
rm -rf *.cover.out
|
||||
|
|
Loading…
Reference in New Issue