Browse Source

Following libricochetgo's migration to bine and a generic Mixnet

interface.
pull/158/head
Dan Ballard 1 year ago
parent
commit
85a2c44891
16 changed files with 163 additions and 309 deletions
  1. +13
    -52
      app/app.go
  2. +17
    -12
      app/cli/main.go
  3. +0
    -168
      connectivity/tor/tormanager.go
  4. +0
    -31
      connectivity/tor/tormanager_test.go
  5. +6
    -3
      peer/connections/connectionsmanager.go
  6. +2
    -1
      peer/connections/connectsmanager_test.go
  7. +5
    -2
      peer/connections/peerpeerconnection.go
  8. +4
    -2
      peer/connections/peerpeerconnection_test.go
  9. +5
    -2
      peer/connections/peerserverconnection.go
  10. +2
    -1
      peer/connections/peerserverconnection_test.go
  11. +36
    -17
      peer/cwtch_peer.go
  12. +3
    -0
      peer/cwtch_peer_test.go
  13. +10
    -1
      server/app/main.go
  14. +8
    -6
      server/server.go
  15. +52
    -10
      testing/cwtch_peer_server_intergration_test.go
  16. +0
    -1
      testing/tests.sh

+ 13
- 52
app/app.go View File

@@ -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()
}

+ 17
- 12
app/cli/main.go View File

@@ -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)

}

+ 0
- 168
connectivity/tor/tormanager.go View File

@@ -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
}

+ 0
- 31
connectivity/tor/tormanager_test.go View File

@@ -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()
}

+ 6
- 3
peer/connections/connectionsmanager.go View File

@@ -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


+ 2
- 1
peer/connections/connectsmanager_test.go View File

@@ -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())
}

+ 5
- 2
peer/connections/peerpeerconnection.go View File

@@ -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


+ 4
- 2
peer/connections/peerpeerconnection_test.go View File

@@ -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")
}

}

+ 5
- 2
peer/connections/peerserverconnection.go View File

@@ -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


+ 2
- 1
peer/connections/peerserverconnection_test.go View File

@@ -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++


+ 36
- 17
peer/cwtch_peer.go View File

@@ -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()


+ 3
- 0
peer/cwtch_peer_test.go View File

@@ -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


+ 10
- 1
server/app/main.go View File

@@ -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
- 6
server/server.go View File

@@ -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


+ 52
- 10
testing/cwtch_peer_server_intergration_test.go View File

@@ -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])


+ 0
- 1
testing/tests.sh View File

@@ -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…
Cancel
Save