Browse Source

Build out pipeBridge to have a connection manager and base64 encode binary data; add support for tor acn status callback/events

pull/251/head
Dan Ballard 1 year ago
parent
commit
2246c6b3bc
11 changed files with 236 additions and 105 deletions
  1. +43
    -10
      app/app.go
  2. +1
    -1
      app/appBridge.go
  3. +4
    -2
      app/appClient.go
  4. +8
    -3
      app/appService.go
  5. +3
    -3
      event/bridge/goChanBridge.go
  6. +167
    -78
      event/bridge/pipeBridge.go
  7. +1
    -6
      event/bridge/pipeBridge_test.go
  8. +6
    -0
      event/common.go
  9. +1
    -1
      event/ipc.go
  10. +1
    -1
      go.mod
  11. +1
    -0
      go.sum

+ 43
- 10
app/app.go View File

@@ -8,6 +8,7 @@ import (
"cwtch.im/cwtch/storage"
"fmt"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"strconv"

"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
@@ -24,14 +25,18 @@ type applicationCore struct {
mutex sync.Mutex
}

type applicationPeers struct {
type appletPeers struct {
peers map[string]peer.CwtchPeer
}

type appletACN struct {
acn connectivity.ACN
}

type application struct {
applicationCore
applicationPeers
acn connectivity.ACN
appletPeers
appletACN
storage map[string]storage.ProfileStore
engines map[string]connections.Engine
appBus event.Manager
@@ -62,10 +67,32 @@ func newAppCore(appDirectory string) *applicationCore {
return appCore
}

func (ap *appletPeers) init() {
ap.peers = make(map[string]peer.CwtchPeer)
}

func (a *appletACN) init(acn connectivity.ACN, publish func(int, string)) {
a.acn = acn
acn.SetStatusCallback(publish)
prog, status := acn.GetBootstrapStatus()
publish(prog, status)
}

func (a *appletACN) Shutdown() {
a.acn.Close()
}

// NewApp creates a new app with some environment awareness and initializes a Tor Manager
func NewApp(acn connectivity.ACN, appDirectory string) Application {
log.Debugf("NewApp(%v)\n", appDirectory)
app := &application{acn: acn, storage: make(map[string]storage.ProfileStore), applicationPeers: applicationPeers{peers: make(map[string]peer.CwtchPeer)}, engines: make(map[string]connections.Engine), applicationCore: *newAppCore(appDirectory), appBus: event.NewEventManager()}
app := &application{storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), applicationCore: *newAppCore(appDirectory), appBus: event.NewEventManager()}
app.appletPeers.init()

fn := func(progress int, status string) {
progStr := strconv.Itoa(progress)
app.appBus.Publish(event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status))
}
app.appletACN.init(acn, fn)
return app
}

@@ -151,6 +178,7 @@ func (ac *applicationCore) LoadProfiles(password string, loadProfileFn LoadProfi

// LoadProfiles takes a password and attempts to load any profiles it can from storage with it and create Peers for them
func (app *application) LoadProfiles(password string) {
count := 0
app.applicationCore.LoadProfiles(password, func(profile *model.Profile, profileStore storage.ProfileStore) {
peer := peer.FromProfile(profile)
peer.Init(app.eventBuses[profile.Onion])
@@ -164,7 +192,12 @@ func (app *application) LoadProfiles(password string) {
app.engines[profile.Onion] = engine
app.mutex.Unlock()
app.appBus.Publish(event.NewEvent(event.NewPeer, map[event.Field]string{event.Identity: profile.Onion}))
count++
})
if count == 0 {
message := event.NewEventList(event.AppError, event.Error, event.AppErrLoaded0)
app.appBus.Publish(message)
}
}

// GetPrimaryBus returns the bus the Application uses for events that aren't peer specific
@@ -173,8 +206,8 @@ func (app *application) GetPrimaryBus() event.Manager {
}

// LaunchPeers starts each peer Listening and connecting to peers and groups
func (appPeers *applicationPeers) LaunchPeers() {
for _, p := range appPeers.peers {
func (ap *appletPeers) LaunchPeers() {
for _, p := range ap.peers {
if !p.IsStarted() {
p.Listen()
p.StartPeersConnections()
@@ -184,17 +217,17 @@ func (appPeers *applicationPeers) LaunchPeers() {
}

// ListPeers returns a map of onions to their profile's Name
func (appPeers *applicationPeers) ListPeers() map[string]string {
func (ap *appletPeers) ListPeers() map[string]string {
keys := map[string]string{}
for k, p := range appPeers.peers {
for k, p := range ap.peers {
keys[k] = p.GetProfile().Name
}
return keys
}

// GetPeer returns a cwtchPeer for a given onion address
func (appPeers *applicationPeers) GetPeer(onion string) peer.CwtchPeer {
if peer, ok := appPeers.peers[onion]; ok {
func (ap *appletPeers) GetPeer(onion string) peer.CwtchPeer {
if peer, ok := ap.peers[onion]; ok {
return peer
}
return nil


+ 1
- 1
app/appBridge.go View File

@@ -14,7 +14,7 @@ func (ab *applicationBridge) listen() {
log.Infoln("ab.listen()")
for {
ipcMessage, ok := ab.bridge.Read()
log.Infof("listen() got %v for %v\n", ipcMessage.Message.EventType, ipcMessage.Dest)
log.Debugf("listen() got %v for %v\n", ipcMessage.Message.EventType, ipcMessage.Dest)
if !ok {
return
}


+ 4
- 2
app/appClient.go View File

@@ -12,14 +12,14 @@ import (

type applicationClient struct {
applicationBridge
applicationPeers
appletPeers

appBus event.Manager
}

// NewAppClient returns an Application that acts as a client to a AppService, connected by the IPCBridge supplied
func NewAppClient(appDirectory string, bridge event.IPCBridge) Application {
appClient := &applicationClient{applicationPeers: applicationPeers{peers: make(map[string]peer.CwtchPeer)}, applicationBridge: applicationBridge{applicationCore: *newAppCore(appDirectory), bridge: bridge}, appBus: event.NewEventManager()}
appClient := &applicationClient{appletPeers: appletPeers{peers: make(map[string]peer.CwtchPeer)}, applicationBridge: applicationBridge{applicationCore: *newAppCore(appDirectory), bridge: bridge}, appBus: event.NewEventManager()}
appClient.handle = appClient.handleEvent

go appClient.listen()
@@ -43,6 +43,8 @@ func (ac *applicationClient) handleEvent(ev *event.Event) {
ac.appBus.Publish(*ev)
case event.AppError:
ac.appBus.Publish(*ev)
case event.ACNStatus:
ac.appBus.Publish(*ev)
}
}



+ 8
- 3
app/appService.go View File

@@ -9,6 +9,7 @@ import (
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"path"
"strconv"
)

const (
@@ -18,8 +19,8 @@ const (

type applicationService struct {
applicationBridge
appletACN

acn connectivity.ACN
storage map[string]storage.ProfileStore
engines map[string]connections.Engine
}
@@ -31,7 +32,12 @@ type ApplicationService interface {

// NewAppService returns an ApplicationService that runs the backend of an app and communicates with a client by the supplied IPCBridge
func NewAppService(acn connectivity.ACN, appDirectory string, bridge event.IPCBridge) ApplicationService {
appService := &applicationService{acn: acn, storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), applicationBridge: applicationBridge{applicationCore: *newAppCore(appDirectory), bridge: bridge}}
appService := &applicationService{storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), applicationBridge: applicationBridge{applicationCore: *newAppCore(appDirectory), bridge: bridge}}
fn := func(progress int, status string) {
progStr := strconv.Itoa(progress)
appService.bridge.Write(&event.IPCMessage{Dest: DestApp, Message: event.NewEventList(event.ACNStatus, event.Progreess, progStr, event.Status, status)})
}
appService.appletACN.init(acn, fn)
appService.handle = appService.handleEvent

go appService.listen()
@@ -96,7 +102,6 @@ func (as *applicationService) loadProfiles(password string) {
if count == 0 {
message := event.IPCMessage{Dest: DestApp, Message: event.NewEventList(event.AppError, event.Error, event.AppErrLoaded0)}
as.bridge.Write(&message)

}
}



+ 3
- 3
event/bridge/goChanBridge.go View File

@@ -34,9 +34,9 @@ func monitor(a, b *goChanBridge) {
a.closedChan <- true
}

func (pb *goChanBridge) Read() (message event.IPCMessage, ok bool) {
message, ok = <-pb.in
return
func (pb *goChanBridge) Read() (*event.IPCMessage, bool) {
message, ok := <-pb.in
return &message, ok
}

func (pb *goChanBridge) Write(message *event.IPCMessage) {


+ 167
- 78
event/bridge/pipeBridge.go View File

@@ -1,6 +1,7 @@
package bridge

import (
"cwtch.im/cwtch/protocol/connections"
"encoding/base64"
"encoding/binary"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
@@ -16,119 +17,207 @@ import (
Needs a call to new client and service to fully successfully open
*/

const maxBufferSize = 1000

type pipeBridge struct {
in, out *os.File
closedChan chan bool
closed bool
lock sync.Mutex
infile, outfile string
in, out *os.File
read, write chan event.IPCMessage
closedChan chan bool
state connections.ConnectionState
lock sync.Mutex
}

// NewPipeBridgeClient returns a pipe backed IPCBridge for a client
func NewPipeBridgeClient(inFilename, outFilename string) (event.IPCBridge, error) {
log.Debugf("Making new PipeBridge Client...\n")
func newPipeBridge(inFilename, outFilename string) *pipeBridge {
syscall.Mkfifo(inFilename, 0600)
in, err := os.OpenFile(inFilename, os.O_RDONLY, 0600)
if err != nil {
return nil, err
}

syscall.Mkfifo(outFilename, 0600)
out, err := os.OpenFile(outFilename, os.O_WRONLY, 0600)
if err != nil {
return nil, err
}
pb := &pipeBridge{infile: inFilename, outfile: outFilename, state: connections.DISCONNECTED}
pb.read = make(chan event.IPCMessage, maxBufferSize)
pb.write = make(chan event.IPCMessage, maxBufferSize)
return pb
}

pb := &pipeBridge{in: in, out: out, closedChan: make(chan bool), closed: false}
log.Debugf("Successfully created new PipeBridge Client!\n")
return pb, nil
// NewPipeBridgeClient returns a pipe backed IPCBridge for a client
func NewPipeBridgeClient(inFilename, outFilename string) event.IPCBridge {
log.Debugf("Making new PipeBridge Client...\n")
pb := newPipeBridge(inFilename, outFilename)
go pb.clientConnectionManager()

return pb
}

// NewPipeBridgeService returns a pipe backed IPCBridge for a service
func NewPipeBridgeService(inFilename, outFilename string) (event.IPCBridge, error) {
log.Debugf("Making new PipeBridge Service...\n")
pb := newPipeBridge(inFilename, outFilename)

syscall.Mkfifo(outFilename, 0600)
out, err := os.OpenFile(outFilename, os.O_WRONLY, 0600)
if err != nil {
return nil, err
}

syscall.Mkfifo(inFilename, 0600)
in, err := os.OpenFile(inFilename, os.O_RDONLY, 0600)
if err != nil {
return nil, err
}
go pb.serviceConnectionManager()

pb := &pipeBridge{in: in, out: out, closedChan: make(chan bool), closed: false}
log.Debugf("Successfully created new PipeBridge Service!\n")
return pb, nil
}

func (pb *pipeBridge) Read() (message event.IPCMessage, ok bool) {
var n int
size := make([]byte, 2)
n, err := pb.in.Read(size)
if err != nil || n != 2 {
log.Errorf("Could not read len int from stream: %v\n", err)
return message, false
}
func (pb *pipeBridge) clientConnectionManager() {
for pb.state != connections.KILLED {
pb.state = connections.CONNECTING

n = int(binary.LittleEndian.Uint16(size))
pos := 0
buffer := make([]byte, n)
for n > 0 {
m, err := pb.in.Read(buffer[pos:])
var err error
pb.in, err = os.OpenFile(pb.infile, os.O_RDONLY, 0600)
if err != nil {
log.Errorf("Reading into buffer from pipe: %v\n", err)
return message, false
pb.state = connections.DISCONNECTED
continue
}
n -= m
pos += m

pb.out, err = os.OpenFile(pb.outfile, os.O_WRONLY, 0600)
if err != nil {
pb.state = connections.DISCONNECTED
continue
}

log.Debugf("Successfully connected PipeBridge Client!\n")

pb.handleConns()
}
}

func (pb *pipeBridge) serviceConnectionManager() {
for pb.state != connections.KILLED {
pb.state = connections.CONNECTING

var err error
pb.out, err = os.OpenFile(pb.outfile, os.O_WRONLY, 0600)
if err != nil {
pb.state = connections.DISCONNECTED
continue
}

pb.in, err = os.OpenFile(pb.infile, os.O_RDONLY, 0600)
if err != nil {
pb.state = connections.DISCONNECTED
continue
}

err = json.Unmarshal(buffer, &message)
if err != nil {
log.Errorf("Read error: %v --value: %v", err, message)
return event.IPCMessage{}, false
log.Debugf("Successfully connected PipeBridge Service!\n")

pb.handleConns()
}
for k, v := range message.Message.Data {
val, _ := base64.StdEncoding.DecodeString(v)
message.Message.Data[k] = string(val)
}

func (pb *pipeBridge) handleConns() {

// auth?
pb.state = connections.AUTHENTICATED

pb.closedChan = make(chan bool, 5)

log.Debugf("handleConns authed, 2xgo\n")

go pb.handleRead()
go pb.handleWrite()

<-pb.closedChan
log.Debugf("handleConns CLOSEDCHAN!!!!\n")
if pb.state != connections.KILLED {
pb.state = connections.FAILED
}
return message, true
pb.in.Close()
pb.out.Close()
close(pb.write)
close(pb.read)
pb.read = make(chan event.IPCMessage, maxBufferSize)
pb.write = make(chan event.IPCMessage, maxBufferSize)
log.Debugf("handleConns done, exit\n")
}

func (pb *pipeBridge) Write(message *event.IPCMessage) {
pb.lock.Lock()
defer pb.lock.Unlock()
if !pb.closed {
encMessage := &event.IPCMessage{Dest: message.Dest, Message: event.Event{EventType: message.Message.EventType, EventID: message.Message.EventID, Data: make(map[event.Field]string)}}
for k, v := range message.Message.Data {
encMessage.Message.Data[k] = base64.StdEncoding.EncodeToString([]byte(v))
func (pb *pipeBridge) handleWrite() {
for {
select {
case message := <-pb.write:
log.Debugf("handleWrite <- message: %v\n", message)
if pb.state == connections.AUTHENTICATED {
encMessage := &event.IPCMessage{Dest: message.Dest, Message: event.Event{EventType: message.Message.EventType, EventID: message.Message.EventID, Data: make(map[event.Field]string)}}
for k, v := range message.Message.Data {
encMessage.Message.Data[k] = base64.StdEncoding.EncodeToString([]byte(v))
}

messageJSON, _ := json.Marshal(encMessage)
size := make([]byte, 2)
binary.LittleEndian.PutUint16(size, uint16(len(messageJSON)))
pb.out.Write(size)

for pos := 0; pos < len(messageJSON); {
n, err := pb.out.Write(messageJSON)
if err != nil {
log.Errorf("Writing out on pipeBridge: %v\n", err)
pb.closedChan <- true
return
}
pos += n
}
} else {
return
}
}
}
}

messageJSON, _ := json.Marshal(encMessage)
size := make([]byte, 2)
binary.LittleEndian.PutUint16(size, uint16(len(messageJSON)))
pb.out.Write(size)
func (pb *pipeBridge) handleRead() {
var n int
size := make([]byte, 2)
var err error
for {
log.Debugf("Waiting to handleRead()...\n")
n, err = pb.in.Read(size)
if err != nil || n != 2 {
log.Errorf("Could not read len int from stream: %v\n", err)
pb.closedChan <- true
return
}

for pos := 0; pos < len(messageJSON); {
n, err := pb.out.Write(messageJSON)
n = int(binary.LittleEndian.Uint16(size))
pos := 0
buffer := make([]byte, n)
for n > 0 {
m, err := pb.in.Read(buffer[pos:])
if err != nil {
log.Errorf("Writing out on pipeBridge: %v\n", err)
log.Errorf("Reading into buffer from pipe: %v\n", err)
pb.closedChan <- true
return
}
pos += n
n -= m
pos += m
}

var message event.IPCMessage
err = json.Unmarshal(buffer, &message)
if err != nil {
log.Errorf("Read error: %v --value: %v", err, message)
continue // signal error?
}
for k, v := range message.Message.Data {
val, _ := base64.StdEncoding.DecodeString(v)
message.Message.Data[k] = string(val)
}
log.Debugf("handleRead read<-: %v\n", message)
pb.read <- message
log.Debugf("handleRead wrote\n")
}
}

func (pb *pipeBridge) Read() (*event.IPCMessage, bool) {
log.Debugf("Read()...\n")
message := <-pb.read
log.Debugf("Read: %v\n", message)
return &message, true
}

func (pb *pipeBridge) Write(message *event.IPCMessage) {
log.Debugf("Write: %v\n", message)
pb.write <- *message
log.Debugf("Wrote\n")
}

func (pb *pipeBridge) Shutdown() {
pb.lock.Lock()
defer pb.lock.Unlock()
if !pb.closed {
pb.in.Close()
pb.out.Close()
pb.closed = true
}
pb.state = connections.KILLED
pb.closedChan <- true
}

+ 1
- 6
event/bridge/pipeBridge_test.go View File

@@ -11,12 +11,7 @@ var (
)

func clientHelper(t *testing.T, in, out string, messageOrig *event.IPCMessage, done chan bool) {
client, err := NewPipeBridgeClient(in, out)
if err != nil {
t.Errorf("Error opening %v pipe: %v", in, err)
done <- true
return
}
client := NewPipeBridgeClient(in, out)

messageAfter, ok := client.Read()
if !ok {


+ 6
- 0
event/common.go View File

@@ -115,6 +115,9 @@ const (

// Error(err)
AppError = Type("AppError")

// Progress, Status
ACNStatus = Type("ACNStatus")
)

// Field defines common event attributes
@@ -144,6 +147,9 @@ const (
Data = Field("Data")

Error = Field("Error")

Progreess = Field("Progress")
Status = Field("Status")
)

// Defining Common errors


+ 1
- 1
event/ipc.go View File

@@ -8,7 +8,7 @@ type IPCMessage struct {

// IPCBridge is an interface to a IPC construct used to communicate IPCMessages
type IPCBridge interface {
Read() (IPCMessage, bool)
Read() (*IPCMessage, bool)
Write(message *IPCMessage)
Shutdown()
}

+ 1
- 1
go.mod View File

@@ -1,7 +1,7 @@
module cwtch.im/cwtch

require (
git.openprivacy.ca/openprivacy/libricochet-go v1.0.3
git.openprivacy.ca/openprivacy/libricochet-go v1.0.4
github.com/c-bata/go-prompt v0.2.3
github.com/golang/protobuf v1.2.0
github.com/mattn/go-colorable v0.0.9 // indirect


+ 1
- 0
go.sum View File

@@ -2,6 +2,7 @@ git.openprivacy.ca/openprivacy/libricochet-go v1.0.2 h1:U5tufewB3O0L2EKjUyHXxJkv
git.openprivacy.ca/openprivacy/libricochet-go v1.0.2/go.mod h1:yMSG1gBaP4f1U+RMZXN85d29D39OK5s8aTpyVRoH5FY=
git.openprivacy.ca/openprivacy/libricochet-go v1.0.3 h1:LHnhK9hzkqMY+iEE3TZ0FjZsYal05YDiamKmxDOXuts=
git.openprivacy.ca/openprivacy/libricochet-go v1.0.3/go.mod h1:yMSG1gBaP4f1U+RMZXN85d29D39OK5s8aTpyVRoH5FY=
git.openprivacy.ca/openprivacy/libricochet-go v1.0.4/go.mod h1:yMSG1gBaP4f1U+RMZXN85d29D39OK5s8aTpyVRoH5FY=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
github.com/c-bata/go-prompt v0.2.3 h1:jjCS+QhG/sULBhAaBdjb2PlMRVaKXQgn+4yzaauvs2s=


Loading…
Cancel
Save