diff --git a/client/client.go b/peer/client.go similarity index 99% rename from client/client.go rename to peer/client.go index 9d94d95..3abe7c6 100644 --- a/client/client.go +++ b/peer/client.go @@ -1,4 +1,4 @@ -package client +package peer import ( "crypto/rand" diff --git a/peer/connections/peerpeerconnection.go b/peer/connections/peerpeerconnection.go new file mode 100644 index 0000000..71110a6 --- /dev/null +++ b/peer/connections/peerpeerconnection.go @@ -0,0 +1,42 @@ +package connections + +import ( + //"git.mascherari.press/cwtch/peer/peer" + //"git.mascherari.press/cwtch/protocol" + "github.com/s-rah/go-ricochet" + //"github.com/s-rah/go-ricochet/channels" + "github.com/s-rah/go-ricochet/connection" + //"github.com/s-rah/go-ricochet/identity" + //"github.com/s-rah/go-ricochet/utils" + //"time" +) + +type PeerPeerConnection struct { + connection.AutoConnectionHandler + PeerHostname string + state ConnectionState + connection connection.Connection +} + +func NewPeerPeerConnection(peerhostname string) *PeerPeerConnection { + ppc := new(PeerPeerConnection) + ppc.PeerHostname = peerhostname + ppc.Init() + return ppc +} + +// GetState returns the current connection state +func (ppc *PeerPeerConnection) GetState() ConnectionState { + return ppc.state +} + +// Run manages the setup and teardown of a peer->peer connection +func (ppc *PeerPeerConnection) Run() error { + rc, err := goricochet.Open(ppc.PeerHostname) + if err == nil { + rc.TraceLog(true) + ppc.connection = *rc + ppc.state = CONNECTED + } + return err +} diff --git a/peer/connections/peerpeerconnection_test.go b/peer/connections/peerpeerconnection_test.go new file mode 100644 index 0000000..ff08628 --- /dev/null +++ b/peer/connections/peerpeerconnection_test.go @@ -0,0 +1,51 @@ +package connections + +import ( + "crypto/rsa" + "github.com/s-rah/go-ricochet" + "github.com/s-rah/go-ricochet/connection" + "github.com/s-rah/go-ricochet/identity" + "github.com/s-rah/go-ricochet/utils" + "net" + "testing" + "time" +) + +func PeerAuthValid(hostname string, publicKey rsa.PublicKey) (allowed, known bool) { + return true, true +} + +func runtestserver(t *testing.T) { + ln, _ := net.Listen("tcp", "127.0.0.1:5452") + conn, _ := ln.Accept() + defer conn.Close() + privateKey, err := utils.LoadPrivateKeyFromFile("../../testing/private_key") + if err != nil { + t.Errorf("Private Key Error %v", err) + } + rc, err := goricochet.NegotiateVersionInbound(conn) + if err != nil { + t.Errorf("Negotiate Version Error: %v", err) + } + rc.TraceLog(true) + err = connection.HandleInboundConnection(rc).ProcessAuthAsServer(identity.Initialize("", privateKey), PeerAuthValid) + if err != nil { + t.Errorf("ServerAuth Error: %v", err) + } +} + +func TestPeerPeerConnection(t *testing.T) { + ppc := NewPeerPeerConnection("127.0.0.1:5452|kwke2hntvyfqm7dr") + //numcalls := 0 + go runtestserver(t) + state := ppc.GetState() + if state != DISCONNECTED { + t.Errorf("new connections should start in disconnected state") + } + go ppc.Run() + time.Sleep(time.Second * 1) + state = ppc.GetState() + if state != CONNECTED { + t.Errorf("connection state should be connected, was instead %v", state) + } +} diff --git a/client/connections/peerserverconnection.go b/peer/connections/peerserverconnection.go similarity index 96% rename from client/connections/peerserverconnection.go rename to peer/connections/peerserverconnection.go index 85f9fe9..ba20359 100644 --- a/client/connections/peerserverconnection.go +++ b/peer/connections/peerserverconnection.go @@ -1,8 +1,8 @@ package connections import ( - "git.mascherari.press/cwtch/client/fetch" - "git.mascherari.press/cwtch/client/send" + "git.mascherari.press/cwtch/peer/fetch" + "git.mascherari.press/cwtch/peer/send" "git.mascherari.press/cwtch/protocol" "github.com/s-rah/go-ricochet" "github.com/s-rah/go-ricochet/channels" diff --git a/client/connections/peerserverconnection_test.go b/peer/connections/peerserverconnection_test.go similarity index 100% rename from client/connections/peerserverconnection_test.go rename to peer/connections/peerserverconnection_test.go diff --git a/client/connections/state.go b/peer/connections/state.go similarity index 100% rename from client/connections/state.go rename to peer/connections/state.go diff --git a/client/cwtch_peer.go b/peer/cwtch_peer.go similarity index 95% rename from client/cwtch_peer.go rename to peer/cwtch_peer.go index bb4c9ab..b172d67 100644 --- a/client/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -1,4 +1,4 @@ -package client +package peer import ( "encoding/json" @@ -14,10 +14,6 @@ import ( ) /** -ServerConnectionPool - background thread running Process() - pointer to connection - Probably worthwhile defining a type called PeerServerConnection to manage background() and other commmands! PeerConnectionPool Background thread running Process() diff --git a/client/cwtch_peer_test.go b/peer/cwtch_peer_test.go similarity index 95% rename from client/cwtch_peer_test.go rename to peer/cwtch_peer_test.go index a1ea28f..918a6a8 100644 --- a/client/cwtch_peer_test.go +++ b/peer/cwtch_peer_test.go @@ -1,4 +1,4 @@ -package client +package peer import ( "testing" diff --git a/client/fetch/peer_fetch_channel.go b/peer/fetch/peer_fetch_channel.go similarity index 100% rename from client/fetch/peer_fetch_channel.go rename to peer/fetch/peer_fetch_channel.go diff --git a/client/fetch/peer_fetch_channel_test.go b/peer/fetch/peer_fetch_channel_test.go similarity index 100% rename from client/fetch/peer_fetch_channel_test.go rename to peer/fetch/peer_fetch_channel_test.go diff --git a/client/listen/peer_listen_channel.go b/peer/listen/peer_listen_channel.go similarity index 100% rename from client/listen/peer_listen_channel.go rename to peer/listen/peer_listen_channel.go diff --git a/client/listen/peer_listen_channel_test.go b/peer/listen/peer_listen_channel_test.go similarity index 100% rename from client/listen/peer_listen_channel_test.go rename to peer/listen/peer_listen_channel_test.go diff --git a/peer/peer/peer_channel.go b/peer/peer/peer_channel.go new file mode 100644 index 0000000..a3dfc04 --- /dev/null +++ b/peer/peer/peer_channel.go @@ -0,0 +1,104 @@ +package peer + +import ( + "git.mascherari.press/cwtch/protocol" + "github.com/golang/protobuf/proto" + "github.com/s-rah/go-ricochet/channels" + "github.com/s-rah/go-ricochet/utils" + "github.com/s-rah/go-ricochet/wire/control" +) + +// 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 { + ClientIdentity(*protocol.CwtchIdentity) + HandleGroupInvite() +} + +// Type returns the type string for this channel, e.g. "im.ricochet.Cwtch". +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.hidden-service" +} + +// 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.GetCwtchIdentify() != nil { + cpc.Handler.ClientIdentity(cpp.GetCwtchIdentify()) + } + } +} diff --git a/peer/peer/peer_channel_test.go b/peer/peer/peer_channel_test.go new file mode 100644 index 0000000..7f15009 --- /dev/null +++ b/peer/peer/peer_channel_test.go @@ -0,0 +1,99 @@ +package peer + +import ( + "git.mascherari.press/cwtch/protocol" + "github.com/golang/protobuf/proto" + "github.com/s-rah/go-ricochet/channels" + "github.com/s-rah/go-ricochet/wire/control" + "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.hidden-service" { + t.Errorf("cwtch channel required auth is incorrect %v", cssc.RequiresAuthentication()) + } +} + +type TestHandler struct { + Received bool +} + +func (th *TestHandler) ClientIdentity(ci *protocol.CwtchIdentity) { + if ci.GetName() == "hello" { + th.Received = true + } +} + +func (th *TestHandler) HandleGroupInvite() { +} + +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") + } + + gm := &protocol.CwtchIdentity{ + Name: "hello", + Ed25519PublicKey: []byte{}, + } + + cpp := &protocol.CwtchPeerPacket{ + CwtchIdentify: gm, + } + packet, _ := proto.Marshal(cpp) + cpc.Packet(packet) + if th.Received == false { + t.Errorf("Should have sent packet to handler") + } + + cpc2.SendMessage(packet) + if sent == false { + t.Errorf("Should have sent packet to channel") + } + +} diff --git a/client/send/peer_send_channel.go b/peer/send/peer_send_channel.go similarity index 100% rename from client/send/peer_send_channel.go rename to peer/send/peer_send_channel.go diff --git a/client/send/peer_send_channel_test.go b/peer/send/peer_send_channel_test.go similarity index 100% rename from client/send/peer_send_channel_test.go rename to peer/send/peer_send_channel_test.go diff --git a/client/test_profile b/peer/test_profile similarity index 100% rename from client/test_profile rename to peer/test_profile