Merge branch 'tapir-peer' of cwtch.im/cwtch into master

final comment from review board has been addressed!
This is good to go
This commit is contained in:
Dan Ballard 2019-07-29 12:57:19 -07:00 committed by Gogs
commit 51d2c49c71
14 changed files with 223 additions and 725 deletions

View File

@ -6,7 +6,7 @@ import (
"time" "time"
) )
// WaitGetPeer is a helper function for utility apps not writen using the event bus // WaitGetPeer is a helper function for utility apps not written using the event bus
// Proper use of an App is to call CreatePeer and then process the NewPeer event // Proper use of an App is to call CreatePeer and then process the NewPeer event
// however for small utility use, this function which polls the app until the peer is created // however for small utility use, this function which polls the app until the peer is created
// may fill that usecase better // may fill that usecase better

View File

@ -41,6 +41,11 @@ const (
SendMessageToPeer = Type("SendMessageToPeer") SendMessageToPeer = Type("SendMessageToPeer")
NewMessageFromPeer = Type("NewMessageFromPeer") NewMessageFromPeer = Type("NewMessageFromPeer")
// Peer acknowledges a previously sent message
// attributes
// EventID: The original event id that the peer is responding too.
PeerAcknowledgement = Type("PeerAcknowledgement")
// attributes: // attributes:
// RemotePeer: [eg "chpr7qm6op5vfcg2pi4vllco3h6aa7exexc4rqwnlupqhoogx2zgd6qd"] // RemotePeer: [eg "chpr7qm6op5vfcg2pi4vllco3h6aa7exexc4rqwnlupqhoogx2zgd6qd"]
// Error: string describing the error // Error: string describing the error
@ -154,11 +159,20 @@ const (
Error = Field("Error") Error = Field("Error")
Progreess = Field("Progress") Progreess = Field("Progress")
Status = Field("Status") Status = Field("Status")
EventID = Field("EventID")
EventContext = Field("EventContext")
) )
// Defining Common errors // Defining Common errors
const ( const (
AppErrLoaded0 = "Loaded 0 profiles" AppErrLoaded0 = "Loaded 0 profiles"
) )
// Defining Protocol Contexts
const (
ContextAck = "im.cwtch.acknowledgement"
ContextInvite = "im.cwtch.invite"
ContextRaw = "im.cwtch.raw"
)

13
go.mod
View File

@ -1,16 +1,15 @@
module cwtch.im/cwtch module cwtch.im/cwtch
require ( require (
cwtch.im/tapir v0.1.5
git.openprivacy.ca/openprivacy/libricochet-go v1.0.4 git.openprivacy.ca/openprivacy/libricochet-go v1.0.4
github.com/c-bata/go-prompt v0.2.3 github.com/c-bata/go-prompt v0.2.3
github.com/golang/protobuf v1.2.0 github.com/golang/protobuf v1.3.2
github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f // indirect github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859 // indirect
github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 // indirect github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 // indirect
github.com/struCoder/pidusage v0.1.2 github.com/struCoder/pidusage v0.1.2
golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 golang.org/x/net v0.0.0-20190628185345-da137c7871d7
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb // indirect
) )

35
go.sum
View File

@ -1,7 +1,6 @@
git.openprivacy.ca/openprivacy/libricochet-go v1.0.2 h1:U5tufewB3O0L2EKjUyHXxJkvByx4rBSvn5s1M9ma+f4= cwtch.im/tapir v0.1.5 h1:SSHJ104t46pWg08FLmGR28y6pEQTbq8gwkaBzmAbCbA=
git.openprivacy.ca/openprivacy/libricochet-go v1.0.2/go.mod h1:yMSG1gBaP4f1U+RMZXN85d29D39OK5s8aTpyVRoH5FY= cwtch.im/tapir v0.1.5/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34=
git.openprivacy.ca/openprivacy/libricochet-go v1.0.3 h1:LHnhK9hzkqMY+iEE3TZ0FjZsYal05YDiamKmxDOXuts= git.openprivacy.ca/openprivacy/libricochet-go v1.0.4 h1:GWLMJ5jBSIC/gFXzdbbeVz7fIAn2FTgW8+wBci6/3Ek=
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= 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 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
@ -13,14 +12,16 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f h1:4P7Ul+TAnk92vTeVkXs6VLjmf1EhrYtDRa03PCYY6VM= github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859 h1:smQbSzmT3EHl4EUwtFwFGmGIpiYgIiiPeVv1uguIQEE=
github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 h1:A7GG7zcGjl3jqAqGPmcNjd/D9hzL95SuoOQAaFNdLU0= github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 h1:A7GG7zcGjl3jqAqGPmcNjd/D9hzL95SuoOQAaFNdLU0=
github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -32,9 +33,19 @@ github.com/struCoder/pidusage v0.1.2 h1:fFPTThlcWFQyizv3xKs5Lyq1lpG5lZ36arEGNhWz
github.com/struCoder/pidusage v0.1.2/go.mod h1:pWBlW3YuSwRl6h7R5KbvA4N8oOqe9LjaKW5CwT1SPjI= github.com/struCoder/pidusage v0.1.2/go.mod h1:pWBlW3YuSwRl6h7R5KbvA4N8oOqe9LjaKW5CwT1SPjI=
golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b h1:Ib/yptP38nXZFMwqWSip+OKuMP9OkyDe3p+DssP8n9w= golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b h1:Ib/yptP38nXZFMwqWSip+OKuMP9OkyDe3p+DssP8n9w=
golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb h1:1w588/yEchbPNpa9sEvOcMZYbWHedwJjg4VOAdDHWHk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -30,7 +30,7 @@ type cwtchPeer struct {
// directly implement a cwtchPeer. // directly implement a cwtchPeer.
type CwtchPeer interface { type CwtchPeer interface {
Init(event.Manager) Init(event.Manager)
PeerWithOnion(string) *connections.PeerPeerConnection PeerWithOnion(string)
InviteOnionToGroup(string, string) error InviteOnionToGroup(string, string) error
SendMessageToPeer(string, string) string SendMessageToPeer(string, string) string
@ -197,9 +197,8 @@ func (cp *cwtchPeer) GetGroupState(groupid string) connections.ConnectionState {
} }
// PeerWithOnion is the entry point for cwtchPeer relationships // PeerWithOnion is the entry point for cwtchPeer relationships
func (cp *cwtchPeer) PeerWithOnion(onion string) *connections.PeerPeerConnection { func (cp *cwtchPeer) PeerWithOnion(onion string) {
cp.eventBus.Publish(event.NewEvent(event.PeerRequest, map[event.Field]string{event.RemotePeer: onion})) cp.eventBus.Publish(event.NewEvent(event.PeerRequest, map[event.Field]string{event.RemotePeer: onion}))
return nil
} }
// InviteOnionToGroup kicks off the invite process // InviteOnionToGroup kicks off the invite process

View File

@ -11,7 +11,6 @@ import (
// Manager encapsulates all the logic necessary to manage outgoing peer and server connections. // Manager encapsulates all the logic necessary to manage outgoing peer and server connections.
type Manager struct { type Manager struct {
peerConnections map[string]*PeerPeerConnection
serverConnections map[string]*PeerServerConnection serverConnections map[string]*PeerServerConnection
lock sync.Mutex lock sync.Mutex
breakChannel chan bool breakChannel chan bool
@ -22,27 +21,11 @@ type Manager struct {
func NewConnectionsManager(acn connectivity.ACN) *Manager { func NewConnectionsManager(acn connectivity.ACN) *Manager {
m := new(Manager) m := new(Manager)
m.acn = acn m.acn = acn
m.peerConnections = make(map[string]*PeerPeerConnection)
m.serverConnections = make(map[string]*PeerServerConnection) m.serverConnections = make(map[string]*PeerServerConnection)
m.breakChannel = make(chan bool) m.breakChannel = make(chan bool)
return m return m
} }
// ManagePeerConnection creates a new PeerConnection for the given Host and Profile.
func (m *Manager) ManagePeerConnection(host string, engine Engine) *PeerPeerConnection {
m.lock.Lock()
defer m.lock.Unlock()
_, exists := m.peerConnections[host]
if !exists {
ppc := NewPeerPeerConnection(host, engine)
go ppc.Run()
m.peerConnections[host] = ppc
return ppc
}
return m.peerConnections[host]
}
// ManageServerConnection creates a new ServerConnection for Host with the given callback handler. // ManageServerConnection creates a new ServerConnection for Host with the given callback handler.
// If there is an establish connection, it is replaced with a new one, assuming this came from // If there is an establish connection, it is replaced with a new one, assuming this came from
// a new JoinServer from a new Group being joined. If it is still connecting to a server, the second request will be abandonded // a new JoinServer from a new Group being joined. If it is still connecting to a server, the second request will be abandonded
@ -75,14 +58,6 @@ func (m *Manager) SetServerSynced(onion string) {
m.serverConnections[onion].setState(SYNCED) m.serverConnections[onion].setState(SYNCED)
} }
// GetPeerPeerConnectionForOnion safely returns a given peer connection
func (m *Manager) GetPeerPeerConnectionForOnion(host string) (ppc *PeerPeerConnection) {
m.lock.Lock()
ppc = m.peerConnections[host]
m.lock.Unlock()
return
}
// GetPeerServerConnectionForOnion safely returns a given host connection // GetPeerServerConnectionForOnion safely returns a given host connection
func (m *Manager) GetPeerServerConnectionForOnion(host string) (psc *PeerServerConnection) { func (m *Manager) GetPeerServerConnectionForOnion(host string) (psc *PeerServerConnection) {
m.lock.Lock() m.lock.Lock()
@ -99,14 +74,6 @@ func (m *Manager) AttemptReconnections() {
for { for {
select { select {
case <-time.After(timeout): case <-time.After(timeout):
m.lock.Lock()
for _, ppc := range m.peerConnections {
if ppc.GetState() == FAILED {
go ppc.Run()
}
}
m.lock.Unlock()
m.lock.Lock() m.lock.Lock()
for _, psc := range m.serverConnections { for _, psc := range m.serverConnections {
if psc.GetState() == FAILED { if psc.GetState() == FAILED {
@ -126,25 +93,10 @@ func (m *Manager) AttemptReconnections() {
} }
} }
// ClosePeerConnection closes an existing peer connection
func (m *Manager) ClosePeerConnection(onion string) {
m.lock.Lock()
pc, ok := m.peerConnections[onion]
if ok {
pc.Close()
delete(m.peerConnections, onion)
}
m.lock.Unlock()
}
// Shutdown closes all connections under management (freeing their goroutines) // Shutdown closes all connections under management (freeing their goroutines)
func (m *Manager) Shutdown() { func (m *Manager) Shutdown() {
m.breakChannel <- true m.breakChannel <- true
m.lock.Lock() m.lock.Lock()
for onion, ppc := range m.peerConnections {
ppc.Close()
delete(m.peerConnections, onion)
}
for onion, psc := range m.serverConnections { for onion, psc := range m.serverConnections {
psc.Close() psc.Close()
delete(m.serverConnections, onion) delete(m.serverConnections, onion)

View File

@ -1,16 +1,13 @@
package connections package connections
import ( import (
"crypto/rsa"
"cwtch.im/cwtch/event" "cwtch.im/cwtch/event"
"cwtch.im/cwtch/protocol" "cwtch.im/cwtch/protocol"
"cwtch.im/cwtch/protocol/connections/peer" "cwtch.im/tapir"
"cwtch.im/tapir/networks/tor"
"errors" "errors"
"git.openprivacy.ca/openprivacy/libricochet-go/application"
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go/identity" "git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
"sync" "sync"
@ -25,8 +22,6 @@ type engine struct {
identity identity.Identity identity identity.Identity
acn connectivity.ACN acn connectivity.ACN
app *application.RicochetApplication
// Engine State // Engine State
started bool started bool
@ -36,8 +31,13 @@ type engine struct {
// Pointer to the Global Event Manager // Pointer to the Global Event Manager
eventManager event.Manager eventManager event.Manager
// Nextgen Tapir Service
service tapir.Service
// Required for listen(), inaccessible from identity // Required for listen(), inaccessible from identity
privateKey ed25519.PrivateKey privateKey ed25519.PrivateKey
shuttingDown bool
} }
// Engine (ProtocolEngine) encapsulates the logic necessary to make and receive Cwtch connections. // Engine (ProtocolEngine) encapsulates the logic necessary to make and receive Cwtch connections.
@ -46,13 +46,6 @@ type Engine interface {
Identity() identity.Identity Identity() identity.Identity
ACN() connectivity.ACN ACN() connectivity.ACN
EventManager() event.Manager EventManager() event.Manager
GetPeerHandler(string) *CwtchPeerHandler
ContactRequest(string, string) string
LookupContact(string, rsa.PublicKey) (bool, bool)
LookupContactV3(string, ed25519.PublicKey) (bool, bool)
Shutdown() Shutdown()
} }
@ -68,6 +61,10 @@ func NewProtocolEngine(identity identity.Identity, privateKey ed25519.PrivateKey
engine.connectionsManager = NewConnectionsManager(engine.acn) engine.connectionsManager = NewConnectionsManager(engine.acn)
go engine.connectionsManager.AttemptReconnections() go engine.connectionsManager.AttemptReconnections()
// Init the Server running the Simple App.
engine.service = new(tor.BaseOnionService)
engine.service.Init(acn, privateKey, identity)
engine.eventManager = eventManager engine.eventManager = eventManager
engine.eventManager.Subscribe(event.ProtocolEngineStartListen, engine.queue.EventChannel) engine.eventManager.Subscribe(event.ProtocolEngineStartListen, engine.queue.EventChannel)
@ -104,31 +101,29 @@ func (e *engine) eventHandler() {
case event.StatusRequest: case event.StatusRequest:
e.eventManager.Publish(event.Event{EventType: event.ProtocolEngineStatus, EventID: ev.EventID}) e.eventManager.Publish(event.Event{EventType: event.ProtocolEngineStatus, EventID: ev.EventID})
case event.PeerRequest: case event.PeerRequest:
e.peerWithOnion(ev.Data[event.RemotePeer]) go e.peerWithOnion(ev.Data[event.RemotePeer])
case event.InvitePeerToGroup: case event.InvitePeerToGroup:
e.inviteOnionToGroup(ev.Data[event.RemotePeer], []byte(ev.Data[event.GroupInvite])) e.sendMessageToPeer(ev.EventID, ev.Data[event.RemotePeer], event.ContextInvite, []byte(ev.Data[event.GroupInvite]))
case event.JoinServer: case event.JoinServer:
e.joinServer(ev.Data[event.GroupServer]) e.joinServer(ev.Data[event.GroupServer])
case event.SendMessageToGroup: case event.SendMessageToGroup:
e.sendMessageToGroup(ev.Data[event.GroupServer], []byte(ev.Data[event.Ciphertext]), []byte(ev.Data[event.Signature])) e.sendMessageToGroup(ev.Data[event.GroupServer], []byte(ev.Data[event.Ciphertext]), []byte(ev.Data[event.Signature]))
case event.SendMessageToPeer: case event.SendMessageToPeer:
log.Debugf("Sending Message to Peer.....") // TODO: remove this passthrough once the UI is integrated.
ppc := e.connectionsManager.GetPeerPeerConnectionForOnion(ev.Data[event.RemotePeer]) context, ok := ev.Data[event.EventContext]
if ppc != nil && ppc.GetState() == AUTHENTICATED { if !ok {
err := ppc.SendPacket([]byte(ev.Data[event.Data])) context = event.ContextRaw
if err != nil { }
e.eventManager.Publish(event.NewEvent(event.SendMessageToPeerError, map[event.Field]string{event.RemotePeer: ev.Data[event.RemotePeer], event.Signature: ev.EventID, event.Error: err.Error()})) err := e.sendMessageToPeer(ev.EventID, ev.Data[event.RemotePeer], context, []byte(ev.Data[event.GroupInvite]))
} if err != nil {
} else {
e.eventManager.Publish(event.NewEvent(event.SendMessageToPeerError, map[event.Field]string{event.RemotePeer: ev.Data[event.RemotePeer], event.Signature: ev.EventID, event.Error: "peer is offline or the connection has yet to finalize"})) e.eventManager.Publish(event.NewEvent(event.SendMessageToPeerError, map[event.Field]string{event.RemotePeer: ev.Data[event.RemotePeer], event.Signature: ev.EventID, event.Error: "peer is offline or the connection has yet to finalize"}))
} }
case event.BlockPeer: case event.BlockPeer:
e.blocked.Store(ev.Data[event.RemotePeer], true) e.blocked.Store(ev.Data[event.RemotePeer], true)
ppc := e.connectionsManager.GetPeerPeerConnectionForOnion(ev.Data[event.RemotePeer]) connection, err := e.service.GetConnection(ev.Data[event.RemotePeer])
if ppc != nil { if err != nil {
ppc.Close() connection.Close()
} }
e.app.Close(ev.Data[event.RemotePeer])
case event.ProtocolEngineStartListen: case event.ProtocolEngineStartListen:
go e.listenFn() go e.listenFn()
default: default:
@ -137,99 +132,86 @@ func (e *engine) eventHandler() {
} }
} }
// GetPeerHandler is an external interface function that allows callers access to a CwtchPeerHandler func (e *engine) createPeerTemplate() *PeerApp {
// TODO: There is likely a slightly better way to encapsulate this behavior peerAppTemplate := new(PeerApp)
func (e *engine) GetPeerHandler(remotePeerHostname string) *CwtchPeerHandler { peerAppTemplate.MessageHandler = e.handlePeerMessage
return &CwtchPeerHandler{Onion: remotePeerHostname, EventBus: e.eventManager} peerAppTemplate.OnAcknowledgement = e.ignoreOnShutdown(e.peerAck)
peerAppTemplate.OnAuth = e.ignoreOnShutdown(e.peerAuthed)
peerAppTemplate.OnConnecting = e.ignoreOnShutdown(e.peerConnecting)
peerAppTemplate.OnClose = e.ignoreOnShutdown(e.peerDisconnected)
return peerAppTemplate
} }
// Listen sets up an onion listener to process incoming cwtch messages // Listen sets up an onion listener to process incoming cwtch messages
func (e *engine) listenFn() { func (e *engine) listenFn() {
ra := new(application.RicochetApplication) err := e.service.Listen(e.createPeerTemplate())
onionService, err := e.acn.Listen(e.privateKey, application.RicochetPort) if !e.shuttingDown {
if err != nil /*&& fmt.Sprintf("%v", err) != "550 Unspecified Tor error: Onion address collision"*/ {
e.eventManager.Publish(event.NewEvent(event.ProtocolEngineStopped, map[event.Field]string{event.Identity: e.identity.Hostname(), event.Error: err.Error()})) e.eventManager.Publish(event.NewEvent(event.ProtocolEngineStopped, map[event.Field]string{event.Identity: e.identity.Hostname(), event.Error: err.Error()}))
return
} }
af := application.InstanceFactory{}
af.Init()
af.AddHandler("im.cwtch.peer", func(rai *application.Instance) func() channels.Handler {
cpi := new(CwtchPeerInstance)
cpi.Init(rai, ra)
return func() channels.Handler {
cpc := new(peer.CwtchPeerChannel)
cpc.Handler = e.GetPeerHandler(rai.RemoteHostname)
return cpc
}
})
af.AddHandler("im.cwtch.peer.data", func(rai *application.Instance) func() channels.Handler {
cpi := new(CwtchPeerInstance)
cpi.Init(rai, ra)
return func() channels.Handler {
cpc := new(peer.CwtchPeerDataChannel)
cpc.Handler = e.GetPeerHandler(rai.RemoteHostname)
return cpc
}
})
ra.Init(e.ACN(), e.identity.Name, e.identity, af, e)
log.Infof("Running cwtch peer on %v", onionService.AddressFull())
e.started = true
e.app = ra
ra.Run(onionService)
e.eventManager.Publish(event.NewEvent(event.ProtocolEngineStopped, map[event.Field]string{event.Identity: e.identity.Hostname()}))
return return
} }
// LookupContact is a V2 API Call, we want to reject all V2 Peers
// TODO Deprecate
func (e *engine) LookupContact(hostname string, publicKey rsa.PublicKey) (allowed, known bool) {
return false, false
}
// ContactRequest is a V2 API Call needed to implement ContactRequestHandler Interface
// TODO Deprecate
func (e *engine) ContactRequest(name string, message string) string {
return "Rejected"
}
// LookupContactV3 returns that a contact is known and allowed to communicate for all cases.
func (e *engine) LookupContactV3(hostname string, publicKey ed25519.PublicKey) (allowed, known bool) {
// TODO: We want to autoblock those that are blocked, The known parameter has no use anymore and should be
// disregarded by peers, so we set it to false.
if _, blocked := e.blocked.Load(hostname); blocked {
return false, false
}
return true, false
}
// Shutdown tears down the eventHandler goroutine // Shutdown tears down the eventHandler goroutine
func (e *engine) Shutdown() { func (e *engine) Shutdown() {
e.shuttingDown = true
e.connectionsManager.Shutdown() e.connectionsManager.Shutdown()
e.app.Shutdown() e.service.Shutdown()
e.queue.Shutdown() e.queue.Shutdown()
} }
// peerWithOnion is the entry point for cwtchPeer relationships // peerWithOnion is the entry point for cwtchPeer relationships
func (e *engine) peerWithOnion(onion string) *PeerPeerConnection { // needs to be run in a goroutine as will block on Open.
return e.connectionsManager.ManagePeerConnection(onion, e) func (e *engine) peerWithOnion(onion string) {
e.ignoreOnShutdown(e.peerConnecting)(onion)
_,err := e.service.Connect(onion, e.createPeerTemplate())
if err != nil {
e.ignoreOnShutdown(e.peerDisconnected)(onion)
}
} }
// inviteOnionToGroup kicks off the invite process func (e * engine) ignoreOnShutdown(f func(string)) func(string){
func (e *engine) inviteOnionToGroup(onion string, invite []byte) error { return func (x string) {if !e.shuttingDown{f(x)} }
ppc := e.connectionsManager.GetPeerPeerConnectionForOnion(onion) }
if ppc == nil {
return errors.New("peer connection not setup for onion. peers must be trusted before sending") func (e *engine) peerAuthed(onion string) {
e.eventManager.Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
event.RemotePeer: string(onion),
event.ConnectionState: ConnectionStateName[AUTHENTICATED],
}))
}
func (e *engine) peerConnecting(onion string) {
e.eventManager.Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
event.RemotePeer: string(onion),
event.ConnectionState: ConnectionStateName[CONNECTING],
}))
}
func (e *engine) peerAck(eventID string) {
e.eventManager.Publish(event.NewEvent(event.PeerAcknowledgement, map[event.Field]string{
event.EventID: eventID,
}))
}
func (e *engine) peerDisconnected(onion string) {
e.eventManager.Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
event.RemotePeer: string(onion),
event.ConnectionState: ConnectionStateName[DISCONNECTED],
}))
}
// sendMessageToPeer sends a message to a peer under a given context
func (e *engine) sendMessageToPeer(eventID string, onion string, context string, message []byte) error {
conn, err := e.service.GetConnection(onion)
if err == nil {
peerApp, ok := conn.App.(*PeerApp)
if ok {
peerApp.SendMessage(PeerMessage{eventID, context, message})
return nil
}
return errors.New("failed type assertion conn.App != PeerApp")
} }
if ppc.GetState() == AUTHENTICATED { return err
log.Infof("Got connection for group: %v - Sending Invite\n", ppc)
ppc.SendGroupInvite(invite)
} else {
return errors.New("cannot send invite to onion: peer connection is not ready")
}
return nil
} }
// receiveGroupMessage is a callback function that processes GroupMessages from a given server // receiveGroupMessage is a callback function that processes GroupMessages from a given server
@ -261,36 +243,15 @@ func (e *engine) sendMessageToGroup(server string, ct []byte, sig []byte) {
} }
} }
// CwtchPeerInstance encapsulates incoming peer connections func (e *engine) handlePeerMessage(hostname string, message []byte) {
type CwtchPeerInstance struct { cpp := &protocol.CwtchPeerPacket{}
rai *application.Instance err := proto.Unmarshal(message, cpp)
ra *application.RicochetApplication
}
// Init sets up a CwtchPeerInstance
func (cpi *CwtchPeerInstance) Init(rai *application.Instance, ra *application.RicochetApplication) {
cpi.rai = rai
cpi.ra = ra
}
// CwtchPeerHandler encapsulates handling of incoming CwtchPackets
type CwtchPeerHandler struct {
Onion string
EventBus event.Manager
DataHandler func(string, []byte) []byte
}
// HandleGroupInvite handles incoming GroupInvites
func (cph *CwtchPeerHandler) HandleGroupInvite(gci *protocol.GroupChatInvite) {
log.Debugf("Received GroupID from %v %v\n", cph.Onion, gci.String())
marshal, err := proto.Marshal(gci)
if err == nil { if err == nil {
cph.EventBus.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: cph.Onion, event.GroupInvite: string(marshal)})) if cpp.GetGroupChatInvite() != nil {
marshal, _ := proto.Marshal(cpp.GetGroupChatInvite())
e.eventManager.Publish(event.NewEvent(event.NewGroupInvite, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: hostname, event.GroupInvite: string(marshal)}))
}
} else {
e.eventManager.Publish(event.NewEvent(event.NewMessageFromPeer, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: hostname, event.Data: string(message)}))
} }
} }
// HandlePacket handles the Cwtch cwtchPeer Data Channel
func (cph *CwtchPeerHandler) HandlePacket(data []byte) []byte {
cph.EventBus.Publish(event.NewEvent(event.NewMessageFromPeer, map[event.Field]string{event.TimestampReceived: time.Now().Format(time.RFC3339Nano), event.RemotePeer: cph.Onion, event.Data: string(data)}))
return []byte{} // TODO remove this
}

View File

@ -1,106 +0,0 @@
package peer
import (
"cwtch.im/cwtch/protocol"
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"git.openprivacy.ca/openprivacy/libricochet-go/wire/control"
"github.com/golang/protobuf/proto"
)
// CwtchPeerChannel implements the ChannelHandler interface for a channel of
// type "im.ricochet.Cwtch". The channel may be inbound or outbound.
//
// CwtchPeerChannel implements protocol-level sanity and state validation, but
// does not handle or acknowledge Cwtch messages. The application must provide
// a CwtchPeerChannelHandler implementation to handle Cwtch events.
type CwtchPeerChannel struct {
// Methods of Handler are called for Cwtch events on this channel
Handler CwtchPeerChannelHandler
channel *channels.Channel
}
// CwtchPeerChannelHandler is implemented by an application type to receive
// events from a CwtchPeerChannel.
type CwtchPeerChannelHandler interface {
HandleGroupInvite(*protocol.GroupChatInvite)
}
// SendMessage sends a raw message on this channel
func (cpc *CwtchPeerChannel) SendMessage(data []byte) {
cpc.channel.SendMessage(data)
}
// Type returns the type string for this channel, e.g. "im.ricochet.Cwtch".
func (cpc *CwtchPeerChannel) Type() string {
return "im.cwtch.peer"
}
// Closed is called when the channel is closed for any reason.
func (cpc *CwtchPeerChannel) Closed(err error) {
}
// OnlyClientCanOpen - for Cwtch channels any side can open
func (cpc *CwtchPeerChannel) OnlyClientCanOpen() bool {
return false
}
// Singleton - for Cwtch channels there can only be one instance per direction
func (cpc *CwtchPeerChannel) Singleton() bool {
return true
}
// Bidirectional - for Cwtch channels are not bidrectional
func (cpc *CwtchPeerChannel) Bidirectional() bool {
return false
}
// RequiresAuthentication - Cwtch channels require hidden service auth
func (cpc *CwtchPeerChannel) RequiresAuthentication() string {
return "im.ricochet.auth.3dh"
}
// OpenInbound is the first method called for an inbound channel request.
// If an error is returned, the channel is rejected. If a RawMessage is
// returned, it will be sent as the ChannelResult message.
func (cpc *CwtchPeerChannel) OpenInbound(channel *channels.Channel, raw *Protocol_Data_Control.OpenChannel) ([]byte, error) {
cpc.channel = channel
messageBuilder := new(utils.MessageBuilder)
return messageBuilder.AckOpenChannel(channel.ID), nil
}
// OpenOutbound is the first method called for an outbound channel request.
// If an error is returned, the channel is not opened. If a RawMessage is
// returned, it will be sent as the OpenChannel message.
func (cpc *CwtchPeerChannel) OpenOutbound(channel *channels.Channel) ([]byte, error) {
cpc.channel = channel
messageBuilder := new(utils.MessageBuilder)
return messageBuilder.OpenChannel(channel.ID, cpc.Type()), nil
}
// OpenOutboundResult is called when a response is received for an
// outbound OpenChannel request. If `err` is non-nil, the channel was
// rejected and Closed will be called immediately afterwards. `raw`
// contains the raw protocol message including any extension data.
func (cpc *CwtchPeerChannel) OpenOutboundResult(err error, crm *Protocol_Data_Control.ChannelResult) {
if err == nil {
if crm.GetOpened() {
cpc.channel.Pending = false
}
}
}
// Packet is called for each raw packet received on this channel.
func (cpc *CwtchPeerChannel) Packet(data []byte) {
cpp := &protocol.CwtchPeerPacket{}
err := proto.Unmarshal(data, cpp)
if err == nil {
if cpp.GetGroupChatInvite() != nil {
cpc.Handler.HandleGroupInvite(cpp.GetGroupChatInvite())
}
} else {
log.Errorf("Error Receivng Packet %v\n", err)
}
}

View File

@ -1,102 +0,0 @@
package peer
import (
"cwtch.im/cwtch/protocol"
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/wire/control"
"github.com/golang/protobuf/proto"
"testing"
)
func TestPeerChannelAttributes(t *testing.T) {
cssc := new(CwtchPeerChannel)
if cssc.Type() != "im.cwtch.peer" {
t.Errorf("cwtch channel type is incorrect %v", cssc.Type())
}
if cssc.OnlyClientCanOpen() {
t.Errorf("either side should be able to open im.cwtch.peer channel")
}
if cssc.Bidirectional() {
t.Errorf("im.cwtch.peer should not be bidirectional")
}
if !cssc.Singleton() {
t.Errorf("im.cwtch.server.listen should be a Singleton")
}
if cssc.RequiresAuthentication() != "im.ricochet.auth.3dh" {
t.Errorf("cwtch channel required auth is incorrect %v", cssc.RequiresAuthentication())
}
}
type TestHandler struct {
Received bool
ReceviedGroupInvite bool
}
func (th *TestHandler) ClientIdentity(ci *protocol.CwtchIdentity) {
if ci.GetName() == "hello" {
th.Received = true
}
}
func (th *TestHandler) HandleGroupInvite(ci *protocol.GroupChatInvite) {
///if ci.GetName() == "hello" {
th.ReceviedGroupInvite = true
//}
}
func (th *TestHandler) GetClientIdentityPacket() []byte {
return nil
}
func TestPeerChannel(t *testing.T) {
th := new(TestHandler)
cpc := new(CwtchPeerChannel)
cpc.Handler = th
channel := new(channels.Channel)
channel.ID = 3
result, err := cpc.OpenOutbound(channel)
if err != nil {
t.Errorf("should have send open channel request instead %v, %v", result, err)
}
cpc2 := new(CwtchPeerChannel)
channel2 := new(channels.Channel)
channel2.ID = 3
sent := false
channel2.SendMessage = func(message []byte) {
sent = true
}
control := new(Protocol_Data_Control.Packet)
proto.Unmarshal(result[:], control)
ack, err := cpc2.OpenInbound(channel2, control.GetOpenChannel())
if err != nil {
t.Errorf("should have ack open channel request instead %v, %v", ack, err)
}
ackpacket := new(Protocol_Data_Control.Packet)
proto.Unmarshal(ack[:], ackpacket)
cpc.OpenOutboundResult(nil, ackpacket.GetChannelResult())
if channel.Pending != false {
t.Errorf("Channel should no longer be pending")
}
gci := &protocol.GroupChatInvite{
GroupName: "hello",
GroupSharedKey: []byte{},
ServerHost: "abc.onion",
}
cpp := &protocol.CwtchPeerPacket{
GroupChatInvite: gci,
}
packet, _ := proto.Marshal(cpp)
cpc.Packet(packet)
if sent && th.ReceviedGroupInvite == false {
t.Errorf("Should have sent invite packet to handler")
}
}

View File

@ -1,98 +0,0 @@
package peer
import (
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"git.openprivacy.ca/openprivacy/libricochet-go/wire/control"
)
// CwtchPeerDataChannel implements the ChannelHandler interface for a channel of
// type "im.cwtch.peer.data The channel may be inbound or outbound.
//
// CwtchPeerChannel implements protocol-level sanity and state validation, but
// does not handle or acknowledge Cwtch messages. The application must provide
// a CwtchPeerChannelHandler implementation to handle Cwtch events.
type CwtchPeerDataChannel struct {
// Methods of Handler are called for Cwtch events on this channel
Handler CwtchPeerDataHandler
channel *channels.Channel
}
// CwtchPeerDataHandler is implemented by an application type to receive
// events from a CwtchPeerChannel.
type CwtchPeerDataHandler interface {
HandlePacket([]byte) []byte
}
// SendMessage sends a raw message on this channel
func (cpc *CwtchPeerDataChannel) SendMessage(data []byte) {
cpc.channel.SendMessage(data)
}
// Type returns the type string for this channel, e.g. "im.ricochet.Cwtch".
func (cpc *CwtchPeerDataChannel) Type() string {
return "im.cwtch.peer.data"
}
// Closed is called when the channel is closed for any reason.
func (cpc *CwtchPeerDataChannel) Closed(err error) {
}
// OnlyClientCanOpen - for Cwtch channels any side can open
func (cpc *CwtchPeerDataChannel) OnlyClientCanOpen() bool {
return false
}
// Singleton - for Cwtch channels there can only be one instance per direction
func (cpc *CwtchPeerDataChannel) Singleton() bool {
return true
}
// Bidirectional - for Cwtch channels are bidirectional
func (cpc *CwtchPeerDataChannel) Bidirectional() bool {
return true
}
// RequiresAuthentication - Cwtch channels require hidden service auth
func (cpc *CwtchPeerDataChannel) RequiresAuthentication() string {
return "im.ricochet.auth.3dh"
}
// OpenInbound is the first method called for an inbound channel request.
// If an error is returned, the channel is rejected. If a RawMessage is
// returned, it will be sent as the ChannelResult message.
func (cpc *CwtchPeerDataChannel) OpenInbound(channel *channels.Channel, raw *Protocol_Data_Control.OpenChannel) ([]byte, error) {
cpc.channel = channel
messageBuilder := new(utils.MessageBuilder)
return messageBuilder.AckOpenChannel(channel.ID), nil
}
// OpenOutbound is the first method called for an outbound channel request.
// If an error is returned, the channel is not opened. If a RawMessage is
// returned, it will be sent as the OpenChannel message.
func (cpc *CwtchPeerDataChannel) OpenOutbound(channel *channels.Channel) ([]byte, error) {
cpc.channel = channel
messageBuilder := new(utils.MessageBuilder)
return messageBuilder.OpenChannel(channel.ID, cpc.Type()), nil
}
// OpenOutboundResult is called when a response is received for an
// outbound OpenChannel request. If `err` is non-nil, the channel was
// rejected and Closed will be called immediately afterwards. `raw`
// contains the raw protocol message including any extension data.
func (cpc *CwtchPeerDataChannel) OpenOutboundResult(err error, crm *Protocol_Data_Control.ChannelResult) {
if err == nil {
if crm.GetOpened() {
cpc.channel.Pending = false
}
}
}
// Packet is called for each raw packet received on this channel.
func (cpc *CwtchPeerDataChannel) Packet(data []byte) {
ret := cpc.Handler.HandlePacket(data)
if len(ret) > 0 {
cpc.channel.SendMessage(ret)
}
}

View File

@ -0,0 +1,82 @@
package connections
import (
"cwtch.im/cwtch/event"
"cwtch.im/tapir"
"cwtch.im/tapir/applications"
"encoding/json"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
)
// PeerApp encapsulates the behaviour of a Cwtch Peer
type PeerApp struct {
applications.AuthApp
connection *tapir.Connection
MessageHandler func(string, []byte)
OnAcknowledgement func(string)
OnAuth func(string)
OnClose func(string)
OnConnecting func(string)
}
// PeerMessage is an encapsulation that can be used by higher level applications
type PeerMessage struct {
ID string // A unique Message ID (primarily used for acknowledgments)
Context string // A unique context identifier i.e. im.cwtch.chat
Data []byte // The serialized data packet.
}
// NewInstance should always return a new instantiation of the application.
func (pa PeerApp) NewInstance() tapir.Application {
newApp := new(PeerApp)
newApp.MessageHandler = pa.MessageHandler
newApp.OnAcknowledgement = pa.OnAcknowledgement
newApp.OnAuth = pa.OnAuth
newApp.OnClose = pa.OnClose
newApp.OnConnecting = pa.OnConnecting
return newApp
}
// Init is run when the connection is first started.
func (pa *PeerApp) Init(connection *tapir.Connection) {
// First run the Authentication App
pa.AuthApp.Init(connection)
if connection.HasCapability(applications.AuthCapability) {
pa.connection = connection
pa.OnAuth(connection.Hostname)
go pa.listen()
} else {
pa.OnClose(connection.Hostname)
}
}
func (pa PeerApp) listen() {
for {
message := pa.connection.Expect()
if len(message) == 0 {
log.Errorf("0 byte read, socket has likely failed. Closing the listen goroutine")
pa.OnClose(pa.connection.Hostname)
return
}
var peerMessage PeerMessage
err := json.Unmarshal(message, &peerMessage)
if err == nil {
if peerMessage.Context == event.ContextAck {
pa.OnAcknowledgement(peerMessage.ID)
} else {
pa.MessageHandler(pa.connection.Hostname, peerMessage.Data)
}
} else {
log.Errorf("Error unmarshalling PeerMessage package: %x %v", message, err)
}
}
}
// SendMessage sends the peer a preformatted message
// NOTE: This is a stub, we will likely want to extend this to better reflect the desired protocol
func (pa PeerApp) SendMessage(message PeerMessage) {
serialized, _ := json.Marshal(message)
pa.connection.Send(serialized)
}

View File

@ -1,123 +0,0 @@
package connections
import (
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/protocol/connections/peer"
"errors"
"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/log"
"time"
)
// PeerPeerConnection encapsulates a single outgoing cwtchPeer->cwtchPeer connection
type PeerPeerConnection struct {
connection.AutoConnectionHandler
PeerHostname string
state ConnectionState
connection *connection.Connection
protocolEngine Engine
}
// NewPeerPeerConnection creates a new peer connection for the given hostname and profile.
func NewPeerPeerConnection(peerhostname string, protocolEngine Engine) *PeerPeerConnection {
ppc := new(PeerPeerConnection)
ppc.PeerHostname = peerhostname
ppc.protocolEngine = protocolEngine
ppc.Init()
return ppc
}
func (ppc *PeerPeerConnection) setState(state ConnectionState) {
ppc.state = state
ppc.protocolEngine.EventManager().Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
event.RemotePeer: string(ppc.PeerHostname),
event.ConnectionState: ConnectionStateName[state],
}))
}
// GetState returns the current connection state
func (ppc *PeerPeerConnection) GetState() ConnectionState {
return ppc.state
}
// SendPacket sends data packets on the optional data channel
func (ppc *PeerPeerConnection) SendPacket(data []byte) error {
ppc.WaitTilAuthenticated()
return ppc.connection.Do(func() error {
channel := ppc.connection.Channel("im.cwtch.peer.data", channels.Outbound)
if channel != nil {
peerchannel, ok := channel.Handler.(*peer.CwtchPeerDataChannel)
if ok {
log.Debugf("Sending packet\n")
peerchannel.SendMessage(data)
return nil
}
}
return errors.New("failed to send packet to peer")
})
}
// SendGroupInvite sends the given serialized invite packet to the Peer
func (ppc *PeerPeerConnection) SendGroupInvite(invite []byte) {
ppc.WaitTilAuthenticated()
ppc.connection.Do(func() error {
channel := ppc.connection.Channel("im.cwtch.peer", channels.Outbound)
if channel != nil {
peerchannel, ok := channel.Handler.(*peer.CwtchPeerChannel)
if ok {
log.Debugf("Sending group invite packet\n")
peerchannel.SendMessage(invite)
}
}
return nil
})
}
// WaitTilAuthenticated waits until the underlying connection is authenticated
func (ppc *PeerPeerConnection) WaitTilAuthenticated() {
for {
if ppc.GetState() == AUTHENTICATED {
break
}
time.Sleep(time.Second * 1)
}
}
// Run manages the setup and teardown of a peer->peer connection
func (ppc *PeerPeerConnection) Run() error {
ppc.setState(CONNECTING)
rc, err := goricochet.Open(ppc.protocolEngine.ACN(), ppc.PeerHostname)
if err == nil {
ppc.connection = rc
ppc.setState(CONNECTED)
_, err := connection.HandleOutboundConnection(ppc.connection).ProcessAuthAsV3Client(ppc.protocolEngine.Identity())
if err == nil {
ppc.setState(AUTHENTICATED)
go func() {
ppc.connection.Do(func() error {
ppc.connection.RequestOpenChannel("im.cwtch.peer", &peer.CwtchPeerChannel{Handler: ppc.protocolEngine.GetPeerHandler(ppc.PeerHostname)})
return nil
})
ppc.connection.Do(func() error {
ppc.connection.RequestOpenChannel("im.cwtch.peer.data", &peer.CwtchPeerDataChannel{Handler: ppc.protocolEngine.GetPeerHandler(ppc.PeerHostname)})
return nil
})
}()
ppc.connection.Process(ppc)
}
}
ppc.setState(FAILED)
return err
}
// Close closes the connection
func (ppc *PeerPeerConnection) Close() {
ppc.setState(KILLED)
if ppc.connection != nil {
ppc.connection.Close()
}
}

View File

@ -1,90 +0,0 @@
package connections
import (
"crypto/rand"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/protocol"
"cwtch.im/cwtch/protocol/connections/peer"
"fmt"
"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"
"testing"
"time"
)
func PeerAuthValid(hostname string, key ed25519.PublicKey) (allowed, known bool) {
return true, true
}
func runtestpeer(t *testing.T, tp *TestPeer, identity identity.Identity, listenChan chan bool) {
ln, _ := net.Listen("tcp", "127.0.0.1:5452")
listenChan <- true
conn, _ := ln.Accept()
defer conn.Close()
rc, err := goricochet.NegotiateVersionInbound(conn)
if err != nil {
t.Errorf("Negotiate Version Error: %v", err)
}
err = connection.HandleInboundConnection(rc).ProcessAuthAsV3Server(identity, PeerAuthValid)
if err != nil {
t.Errorf("ServerAuth Error: %v", err)
}
tp.RegisterChannelHandler("im.cwtch.peer", func() channels.Handler {
cpc := new(peer.CwtchPeerChannel)
cpc.Handler = tp
return cpc
})
rc.Process(tp)
}
type TestPeer struct {
connection.AutoConnectionHandler
ReceivedIdentityPacket bool
ReceivedGroupInvite bool
}
func (tp *TestPeer) HandleGroupInvite(gci *protocol.GroupChatInvite) {
tp.ReceivedGroupInvite = true
}
func TestPeerPeerConnection(t *testing.T) {
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
identity := identity.InitializeV3("", &priv, &pub)
profile := model.GenerateNewProfile("alice")
hostname := identity.Hostname()
manager := event.NewEventManager()
engine := NewProtocolEngine(identity, priv, connectivity.LocalProvider(), manager, nil)
ppc := NewPeerPeerConnection("127.0.0.1:5452|"+hostname, engine)
tp := new(TestPeer)
tp.Init()
listenChan := make(chan bool)
go runtestpeer(t, tp, identity, listenChan)
<-listenChan
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()
time.Sleep(time.Second * 5)
state = ppc.GetState()
if state != AUTHENTICATED {
t.Errorf("connection state should be authenticated(3), was instead %v", state)
}
_, invite, _ := profile.StartGroup("2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd")
ppc.SendGroupInvite(invite)
time.Sleep(time.Second * 3)
if tp.ReceivedGroupInvite == false {
t.Errorf("should have received an group invite packet")
}
}

View File

@ -9,7 +9,6 @@ go test ${1} -coverprofile=peer.connections.cover.out -v ./protocol/connections
go test ${1} -coverprofile=protocol.spam.cover.out -v ./protocol/connections/spam go test ${1} -coverprofile=protocol.spam.cover.out -v ./protocol/connections/spam
go test ${1} -coverprofile=peer.fetch.cover.out -v ./protocol/connections/fetch go test ${1} -coverprofile=peer.fetch.cover.out -v ./protocol/connections/fetch
go test ${1} -coverprofile=peer.listen.cover.out -v ./protocol/connections/listen go test ${1} -coverprofile=peer.listen.cover.out -v ./protocol/connections/listen
go test ${1} -coverprofile=peer.peer.cover.out -v ./protocol/connections/peer
go test ${1} -coverprofile=peer.send.cover.out -v ./protocol/connections/send go test ${1} -coverprofile=peer.send.cover.out -v ./protocol/connections/send
go test ${1} -coverprofile=peer.cover.out -v ./peer go test ${1} -coverprofile=peer.cover.out -v ./peer
go test ${1} -coverprofile=server.fetch.cover.out -v ./server/fetch go test ${1} -coverprofile=server.fetch.cover.out -v ./server/fetch