Browse Source

Android use changes: dedup peer start code; peer and app track write directory and files, do not use supplied filenames for Save(); rework tormanager to take a path to tor binary

Dan Ballard 10 months ago
parent
commit
c289052aae

+ 1 - 1
.drone.yml

@@ -36,7 +36,7 @@ pipeline:
       when:
         status: [ success, changed, failure ]
   notify-gogs:
-    image: mindstab/drone-gogs
+    image: openpriv/drone-gogs
     when:
       event: pull_request
     secrets: [gogs_account_token]

+ 39 - 42
app/app.go

@@ -3,10 +3,10 @@ package app
 import (
 	"cwtch.im/cwtch/connectivity/tor"
 	"cwtch.im/cwtch/peer"
+	"errors"
 	"fmt"
 	"log"
 	"os"
-	"os/user"
 	"path"
 )
 
@@ -14,54 +14,53 @@ import (
 type Application struct {
 	Peer       peer.CwtchPeerInterface
 	TorManager *tor.Manager
+	directory  string
+}
+
+// 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{Peer: nil, directory: appDirectory}
+	os.MkdirAll(path.Join(appDirectory, "tor"), 0700)
+	err := app.startTor(torPath)
+	if err != nil {
+		return nil, err
+	}
+	return app, nil
 }
 
 // NewProfile creates a new cwtchPeer with a given name.
-func (app *Application) NewProfile(name string, filename string, password string) error {
-	profile := peer.NewCwtchPeer(name, password)
-	app.Peer = profile
-	err := profile.Save(filename)
+func (app *Application) NewProfile(name string, password string) error {
+	log.Printf("NewProfile(%v, %v)\n", name, password)
+	if app.Peer != nil {
+		return errors.New("Profile already created")
+	}
+	app.Peer = peer.NewCwtchPeer(name, password, path.Join(app.directory, name+".json"))
+	err := app.Peer.Save()
 	if err == nil {
-
-		err := app.startTor()
-		if err != nil {
-			return err
-		}
-		go func() {
-			err := app.Peer.Listen()
-			if err != nil {
-				log.Panic(err)
-			}
-		}()
+		err = app.startPeer()
 	}
 	return err
 }
 
-func (app *Application) startTor() error {
-
+// 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
-	usr, err := user.Current()
-	if err != nil {
-		return err
-	}
-
-	// creating /home/<usr>/.cwtch/torrc file
+	// creating $app.directory/torrc file
 	// SOCKSPort  socksPort
 	// ControlPort controlPort
-	torrc := path.Join(usr.HomeDir, ".cwtch", "torrc")
+	torrc := path.Join(app.directory, "tor", "torrc")
 	if _, err := os.Stat(torrc); os.IsNotExist(err) {
-
-		os.MkdirAll(path.Join(usr.HomeDir, ".cwtch"), 0700)
-
+		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\n", 9050, 9051)
+		fmt.Fprintf(file, "SOCKSPort %d\nControlPort %d\nCookieAuthentication 0\nSafeSocks 1\n", 9050, 9051)
 		file.Close()
 	}
 
-	tm, err := tor.NewTorManager(9050, 9051, torrc)
+	tm, err := tor.NewTorManager(9050, 9051, torPath, torrc)
 	if err != nil {
 		return err
 	}
@@ -76,20 +75,18 @@ func (app *Application) SetProfile(filename string, password string) error {
 		return err
 	}
 	app.Peer = profile
-	if err == nil {
+	return app.startPeer()
+}
 
-		err := app.startTor()
-		if err != nil {
-			return err
+func (app *Application) startPeer() error {
+	go func() {
+		e := app.Peer.Listen()
+		if e != nil {
+			log.Panic(e)
 		}
-		go func() {
-			err := app.Peer.Listen()
-			if err != nil {
-				log.Panic(err)
-			}
-		}()
-	}
-	return err
+	}()
+
+	return nil
 }
 
 // PeerRequest attempts to setup peer relationship with the given onion address.`

+ 28 - 18
app/cli/main.go

@@ -10,14 +10,18 @@ import (
 
 	"bytes"
 	"golang.org/x/crypto/ssh/terminal"
+	"log"
 	"os"
+	"os/exec"
+	"os/user"
+	"path"
 	"syscall"
 )
 
-var app app2.Application
+var app *app2.Application
 
 var suggestions = []prompt.Suggest{
-	{Text: "new-profile", Description: "create a new profile"},
+	{Text: "new-profile", Description: "create a new profile in ~/.cwtch/$USERNAME.json"},
 	{Text: "load-profile", Description: "load a new profile"},
 	{Text: "quit", Description: "quit cwtch"},
 	{Text: "info", Description: "show user info"},
@@ -37,7 +41,7 @@ var suggestions = []prompt.Suggest{
 }
 
 var usages = map[string]string{
-	"new-profile":     "new-profile [name] [filename]",
+	"new-profile":     "new-profile [name]",
 	"load-profile":    "load-profile [filename]",
 	"quit":            "",
 	"servers":         "",
@@ -163,8 +167,18 @@ func main() {
 	fmt.Printf("%v\n\n", cwtch)
 
 	quit := false
-	app = app2.Application{}
-	profilefile := ""
+
+	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)
 	var history []string
 	for !quit {
 		profile := "unset"
@@ -181,10 +195,10 @@ func main() {
 		history = append(history, text)
 		switch commands[0] {
 		case "quit":
-			app.Peer.Save(profilefile)
+			app.Peer.Save()
 			quit = true
 		case "new-profile":
-			if len(commands) == 3 {
+			if len(commands) == 2 {
 				fmt.Print("** WARNING: PASSWORDS CANNOT BE RECOVERED! **\n")
 
 				password := ""
@@ -209,8 +223,7 @@ func main() {
 				if failcount >= 3 {
 					fmt.Printf("Error creating profile for %v: Your password entries must match!\n", commands[1])
 				} else {
-					err := app.NewProfile(commands[1], commands[2], password)
-					profilefile = commands[2]
+					err := app.NewProfile(commands[1], password)
 					if err == nil {
 						fmt.Printf("\nNew profile created for %v\n", commands[1])
 					} else {
@@ -224,10 +237,9 @@ func main() {
 			if len(commands) == 2 {
 				fmt.Print("Enter a password to decrypt the profile: ")
 				bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
-				err = app.SetProfile(commands[1], string(bytePassword))
+				err = app.SetProfile(commands[1]+".json", string(bytePassword))
 				if err == nil {
 					fmt.Printf("\nLoaded profile for %v\n", commands[1])
-					profilefile = commands[1]
 				} else {
 					fmt.Printf("Error loading profile for %v: %v\n", commands[1], err)
 				}
@@ -288,7 +300,7 @@ func main() {
 				if err != nil {
 					fmt.Printf("Error: %v\n", err)
 				} else {
-					app.Peer.Save(profilefile)
+					app.Peer.Save()
 					group := app.Peer.GetGroup(groupID)
 					if group == nil {
 						fmt.Printf("Error: group does not exist\n")
@@ -315,7 +327,7 @@ func main() {
 				id, _, err := app.Peer.StartGroup(commands[1])
 				if err == nil {
 					fmt.Printf("New Group [%v] created for server %v\n", id, commands[1])
-					app.Peer.Save(profilefile)
+					app.Peer.Save()
 					group := app.Peer.GetGroup(id)
 					if group == nil {
 						fmt.Printf("Error: group does not exist\n")
@@ -378,7 +390,7 @@ func main() {
 				fmt.Printf("Error reading timeline from group, usage: %s\n", usages["timeline"])
 			}
 		case "save":
-			app.Peer.Save(profilefile)
+			app.Peer.Save()
 		case "help":
 			for _, command := range suggestions {
 				fmt.Printf("%-18s%-56s%s\n", command.Text, command.Description, usages[command.Text])
@@ -428,10 +440,8 @@ func main() {
 				}
 			}
 		}
-		if profilefile != "" {
-			if app.Peer != nil {
-				app.Peer.Save(profilefile)
-			}
+		if app.Peer != nil {
+			app.Peer.Save()
 		}
 	}
 

+ 21 - 4
connectivity/tor/tormanager.go

@@ -8,7 +8,9 @@ import (
 	"net"
 	"net/http"
 	"net/url"
+	"os"
 	"os/exec"
+	"path"
 	"strings"
 	"time"
 )
@@ -21,10 +23,11 @@ type Manager struct {
 }
 
 // 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, torrc string) (*Manager, error) {
+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 {
@@ -33,16 +36,30 @@ func NewTorManager(socksPort int, controlPort int, torrc string) (*Manager, erro
 	}
 
 	// try to start tor
-	cmd := exec.Command("tor", "-f", torrc)
+
+	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
 	}
-	time.Sleep(time.Second * 5)
 	torManager.process = cmd
-	err = torManager.TestConnection()
+
+	// 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
 }
 

+ 7 - 3
connectivity/tor/tormanager_test.go

@@ -3,21 +3,25 @@ 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, "/tmp/torrc")
+	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, "/tmp/torrc")
+		tm2, err := NewTorManager(10050, 10051, tor, "/tmp/torrc")
 		if err != nil {
 			t.Errorf("creating a new tor manager failed: %v", err)
 		}

+ 8 - 8
peer/cwtch_peer.go

@@ -43,7 +43,7 @@ type cwtchPeer struct {
 // CwtchPeerInterface provides us with a way of testing systems built on top of cwtch without having to
 // directly implement a cwtchPeer.
 type CwtchPeerInterface interface {
-	Save(string) error
+	Save() error
 	PeerWithOnion(string)
 	InviteOnionToGroup(string, string) error
 
@@ -145,8 +145,9 @@ func (cp *cwtchPeer) setup() {
 }
 
 // NewCwtchPeer creates and returns a new cwtchPeer with the given name.
-func NewCwtchPeer(name string, password string) CwtchPeerInterface {
+func NewCwtchPeer(name string, password string, profilefile string) CwtchPeerInterface {
 	cp := new(cwtchPeer)
+	cp.profilefile = profilefile
 	cp.Profile = model.GenerateNewProfile(name)
 	cp.setup()
 	key, salt := createKey(password)
@@ -156,14 +157,13 @@ func NewCwtchPeer(name string, password string) CwtchPeerInterface {
 }
 
 // Save saves the cwtchPeer profile state to a file.
-func (cp *cwtchPeer) Save(profilefile string) error {
+func (cp *cwtchPeer) Save() error {
 	cp.mutex.Lock()
 	encryptedbytes := encryptProfile(cp, cp.key)
 
 	// the salt for the derived key is appended to the front of  the file
 	encryptedbytes = append(cp.salt[:], encryptedbytes...)
-	err := ioutil.WriteFile(profilefile, encryptedbytes, 0600)
-	cp.profilefile = profilefile
+	err := ioutil.WriteFile(cp.profilefile, encryptedbytes, 0600)
 	cp.mutex.Unlock()
 	return err
 }
@@ -171,7 +171,6 @@ func (cp *cwtchPeer) Save(profilefile string) error {
 // LoadCwtchPeer loads an existing cwtchPeer from a file.
 func LoadCwtchPeer(profilefile string, password string) (CwtchPeerInterface, error) {
 	encryptedbytes, err := ioutil.ReadFile(profilefile)
-
 	if err == nil {
 		var dkr [32]byte
 		var salty [128]byte
@@ -184,7 +183,8 @@ func LoadCwtchPeer(profilefile string, password string) (CwtchPeerInterface, err
 		copy(dkr[:], dk)
 		copy(salty[:], salt)
 
-		cp, err := decryptProfile(encryptedbytes, dkr)
+		var cp *cwtchPeer
+		cp, err = decryptProfile(encryptedbytes, dkr)
 		if err == nil {
 			cp.setup()
 			cp.profilefile = profilefile
@@ -429,7 +429,7 @@ type CwtchPeerHandler struct {
 func (cph *CwtchPeerHandler) ClientIdentity(ci *protocol.CwtchIdentity) {
 	log.Printf("Received Client Identity from %v %v\n", cph.Onion, ci.String())
 	cph.Peer.Profile.AddCwtchIdentity(cph.Onion, ci)
-	cph.Peer.Save(cph.Peer.profilefile)
+	cph.Peer.Save()
 }
 
 // HandleGroupInvite handles incoming GroupInvites

+ 5 - 5
peer/cwtch_peer_test.go

@@ -6,10 +6,10 @@ import (
 
 func TestCwtchPeerGenerate(t *testing.T) {
 
-	alice := NewCwtchPeer("alice", "testpass")
-	alice.Save("./test_profile")
+	alice := NewCwtchPeer("alice", "testpass", "./alice.json")
+	alice.Save()
 
-	aliceLoaded, err := LoadCwtchPeer("./test_profile", "testpass")
+	aliceLoaded, err := LoadCwtchPeer("./alice.json", "testpass")
 	if err != nil || aliceLoaded.GetProfile().Name != "alice" {
 		t.Errorf("something went wrong saving and loading profiles %v %v", err, aliceLoaded)
 	}
@@ -26,8 +26,8 @@ func TestCwtchPeerGenerate(t *testing.T) {
 
 func TestTrustPeer(t *testing.T) {
 	groupName := "test.server"
-	alice := NewCwtchPeer("alice", "alicepass")
-	bob := NewCwtchPeer("bob", "bobpass")
+	alice := NewCwtchPeer("alice", "alicepass", "")
+	bob := NewCwtchPeer("bob", "bobpass", "")
 
 	bobOnion := bob.GetProfile().Onion
 	aliceOnion := alice.GetProfile().Onion

+ 3 - 3
testing/cwtch_peer_server_intergration_test.go

@@ -143,17 +143,17 @@ func TestCwtchPeerIntegration(t *testing.T) {
 	// ***** Peer setup *****
 
 	fmt.Println("Creating Alice...")
-	alice := peer.NewCwtchPeer("Alice", "alicepass")
+	alice := peer.NewCwtchPeer("Alice", "alicepass", "")
 	go alice.Listen()
 	fmt.Println("Alice created:", alice.GetProfile().Onion)
 
 	fmt.Println("Creating Bob...")
-	bob := peer.NewCwtchPeer("Bob", "bobpass")
+	bob := peer.NewCwtchPeer("Bob", "bobpass", "")
 	go bob.Listen()
 	fmt.Println("Bob created:", bob.GetProfile().Onion)
 
 	fmt.Println("Creating Carol...")
-	carol := peer.NewCwtchPeer("Carol", "carolpass")
+	carol := peer.NewCwtchPeer("Carol", "carolpass", "")
 	go carol.Listen()
 	fmt.Println("Carol created:", carol.GetProfile().Onion)