2017-05-02 23:33:51 +00:00
|
|
|
package connection
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"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"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"time"
|
2017-06-27 19:24:04 +00:00
|
|
|
"fmt"
|
2017-05-02 23:33:51 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Connection encapsulates the state required to maintain a connection to
|
|
|
|
// a ricochet service.
|
|
|
|
type Connection struct {
|
|
|
|
utils.RicochetNetwork
|
|
|
|
|
|
|
|
channelManager *ChannelManager
|
|
|
|
|
|
|
|
// Ricochet Network Loop
|
|
|
|
packetChannel chan utils.RicochetData
|
|
|
|
errorChannel chan error
|
|
|
|
|
|
|
|
breakChannel chan bool
|
|
|
|
breakResultChannel chan bool
|
|
|
|
|
|
|
|
unlockChannel chan bool
|
|
|
|
unlockResponseChannel chan bool
|
|
|
|
|
|
|
|
messageBuilder utils.MessageBuilder
|
2017-06-27 19:24:04 +00:00
|
|
|
trace bool
|
2017-05-02 23:33:51 +00:00
|
|
|
|
|
|
|
Conn io.ReadWriteCloser
|
|
|
|
IsInbound bool
|
|
|
|
Authentication map[string]bool
|
|
|
|
RemoteHostname string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rc *Connection) init() {
|
|
|
|
|
|
|
|
rc.packetChannel = make(chan utils.RicochetData)
|
|
|
|
rc.errorChannel = make(chan error)
|
|
|
|
|
|
|
|
rc.breakChannel = make(chan bool)
|
|
|
|
rc.breakResultChannel = make(chan bool)
|
|
|
|
|
|
|
|
rc.unlockChannel = make(chan bool)
|
|
|
|
rc.unlockResponseChannel = make(chan bool)
|
|
|
|
|
|
|
|
rc.Authentication = make(map[string]bool)
|
|
|
|
go rc.start()
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewInboundConnection creates a new Connection struct
|
|
|
|
// modelling an Inbound Connection
|
|
|
|
func NewInboundConnection(conn io.ReadWriteCloser) *Connection {
|
|
|
|
rc := new(Connection)
|
|
|
|
rc.Conn = conn
|
|
|
|
rc.IsInbound = true
|
|
|
|
rc.init()
|
|
|
|
rc.channelManager = NewServerChannelManager()
|
|
|
|
return rc
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewOutboundConnection creates a new Connection struct
|
|
|
|
// modelling an Inbound Connection
|
|
|
|
func NewOutboundConnection(conn io.ReadWriteCloser, remoteHostname string) *Connection {
|
|
|
|
rc := new(Connection)
|
|
|
|
rc.Conn = conn
|
|
|
|
rc.IsInbound = false
|
|
|
|
rc.init()
|
|
|
|
rc.RemoteHostname = remoteHostname
|
|
|
|
rc.channelManager = NewClientChannelManager()
|
|
|
|
return rc
|
|
|
|
}
|
|
|
|
|
2017-06-27 19:24:04 +00:00
|
|
|
func (rc *Connection) TraceLog(enabled bool) {
|
|
|
|
rc.trace = enabled
|
|
|
|
}
|
|
|
|
|
2017-05-02 23:33:51 +00:00
|
|
|
// start
|
|
|
|
func (rc *Connection) start() {
|
|
|
|
for {
|
|
|
|
packet, err := rc.RecvRicochetPacket(rc.Conn)
|
|
|
|
if err != nil {
|
|
|
|
rc.errorChannel <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
rc.packetChannel <- packet
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do allows any function utilizing Connection to be run safetly.
|
|
|
|
// All operations which require access to Connection managed resources should
|
|
|
|
// use Do()
|
|
|
|
func (rc *Connection) Do(do func() error) error {
|
|
|
|
// Force process to soft-break so we can lock
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("request unlocking of process loop for do()")
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.unlockChannel <- true
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("process loop is unlocked for do()")
|
2017-05-02 23:33:51 +00:00
|
|
|
ret := do()
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("giving up lock process loop after do() ")
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.unlockResponseChannel <- true
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// RequestOpenChannel sends an OpenChannel message to the remote client.
|
|
|
|
// and error is returned only if the requirements for opening this channel
|
|
|
|
// are not met on the local side (a nill error return does not mean the
|
|
|
|
// channel was opened successfully)
|
|
|
|
func (rc *Connection) RequestOpenChannel(ctype string, handler Handler) error {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("requesting open channel of type %s", ctype))
|
2017-05-02 23:33:51 +00:00
|
|
|
return rc.Do(func() error {
|
|
|
|
chandler, err := handler.OnOpenChannelRequest(ctype)
|
|
|
|
|
|
|
|
if err != nil {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("failed to reqeust open channel of type %v", err))
|
2017-05-02 23:33:51 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we have the authentication already
|
|
|
|
if chandler.RequiresAuthentication() != "none" {
|
|
|
|
// Enforce Authentication Check.
|
|
|
|
_, authed := rc.Authentication[chandler.RequiresAuthentication()]
|
|
|
|
if !authed {
|
|
|
|
return errors.New("connection is not auth'd")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
channel, err := rc.channelManager.OpenChannelRequest(chandler)
|
|
|
|
|
|
|
|
if err != nil {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("failed to reqeust open channel of type %v", err))
|
2017-05-02 23:33:51 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
channel.SendMessage = func(message []byte) {
|
|
|
|
rc.SendRicochetPacket(rc.Conn, channel.ID, message)
|
|
|
|
}
|
|
|
|
channel.DelegateAuthorization = func() {
|
|
|
|
rc.Authentication[chandler.Type()] = true
|
|
|
|
}
|
|
|
|
channel.CloseChannel = func() {
|
|
|
|
rc.SendRicochetPacket(rc.Conn, channel.ID, []byte{})
|
|
|
|
rc.channelManager.RemoveChannel(channel.ID)
|
|
|
|
}
|
|
|
|
response, err := chandler.OpenOutbound(channel)
|
|
|
|
if err == nil {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("requested open channel of type %s", ctype))
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.SendRicochetPacket(rc.Conn, 0, response)
|
|
|
|
} else {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("failed to reqeust open channel of type %v", err))
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.channelManager.RemoveChannel(channel.ID)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process receives socket and protocol events for the connection. Methods
|
|
|
|
// of the application-provided `handler` will be called from this goroutine
|
|
|
|
// for all events.
|
|
|
|
//
|
|
|
|
// Process must be running in order to handle any events on the connection,
|
|
|
|
// including connection close.
|
|
|
|
//
|
|
|
|
// Process blocks until the connection is closed or until Break() is called.
|
|
|
|
// If the connection is closed, a non-nil error is returned.
|
|
|
|
func (rc *Connection) Process(handler Handler) error {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("entering process loop")
|
2017-05-02 23:33:51 +00:00
|
|
|
handler.OnReady(rc)
|
|
|
|
breaked := false
|
|
|
|
for !breaked {
|
|
|
|
|
|
|
|
var packet utils.RicochetData
|
|
|
|
tick := time.Tick(30 * time.Second)
|
|
|
|
select {
|
|
|
|
case <-rc.unlockChannel:
|
|
|
|
<-rc.unlockResponseChannel
|
|
|
|
continue
|
|
|
|
case <-rc.breakChannel:
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("process has ended after break")
|
2017-05-02 23:33:51 +00:00
|
|
|
breaked = true
|
|
|
|
continue
|
|
|
|
case packet = <-rc.packetChannel:
|
|
|
|
break
|
|
|
|
case err := <-rc.errorChannel:
|
|
|
|
rc.Conn.Close()
|
|
|
|
handler.OnClosed(err)
|
|
|
|
return err
|
|
|
|
case <-tick:
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("peer timed out")
|
2017-05-02 23:33:51 +00:00
|
|
|
return errors.New("peer timed out")
|
|
|
|
}
|
|
|
|
|
2017-06-27 19:24:04 +00:00
|
|
|
|
2017-05-02 23:33:51 +00:00
|
|
|
if packet.Channel == 0 {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("received control packet on channel %d", packet.Channel))
|
2017-05-02 23:33:51 +00:00
|
|
|
res := new(Protocol_Data_Control.Packet)
|
|
|
|
err := proto.Unmarshal(packet.Data[:], res)
|
|
|
|
if err == nil {
|
|
|
|
rc.controlPacket(handler, res)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Let's check to see if we have defined this channel.
|
|
|
|
channel, found := rc.channelManager.GetChannel(packet.Channel)
|
|
|
|
if found {
|
|
|
|
if len(packet.Data) == 0 {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("removing channel %d", packet.Channel))
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.channelManager.RemoveChannel(packet.Channel)
|
|
|
|
(*channel.Handler).Closed(errors.New("channel closed by peer"))
|
|
|
|
} else {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("received packet on %v channel %d", (*channel.Handler).Type(), packet.Channel))
|
2017-05-02 23:33:51 +00:00
|
|
|
// Send The Ricochet Packet to the Handler
|
|
|
|
(*channel.Handler).Packet(packet.Data[:])
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// When a non-zero packet is received for an unknown
|
|
|
|
// channel, the recipient responds by closing
|
|
|
|
// that channel.
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("received packet on unknown channel %d. closing.", packet.Channel))
|
2017-05-02 23:33:51 +00:00
|
|
|
if len(packet.Data) != 0 {
|
|
|
|
rc.SendRicochetPacket(rc.Conn, packet.Channel, []byte{})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rc.breakResultChannel <- true
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rc *Connection) controlPacket(handler Handler, res *Protocol_Data_Control.Packet) {
|
|
|
|
|
|
|
|
if res.GetOpenChannel() != nil {
|
|
|
|
|
|
|
|
opm := res.GetOpenChannel()
|
|
|
|
chandler, err := handler.OnOpenChannelRequest(opm.GetChannelType())
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
response := rc.messageBuilder.RejectOpenChannel(opm.GetChannelIdentifier(), "UnknownTypeError")
|
|
|
|
rc.SendRicochetPacket(rc.Conn, 0, response)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we have the authentication already
|
|
|
|
if chandler.RequiresAuthentication() != "none" {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("channel %v requires authorization of type %v", chandler.Type(), chandler.RequiresAuthentication()))
|
2017-05-02 23:33:51 +00:00
|
|
|
// Enforce Authentication Check.
|
|
|
|
_, authed := rc.Authentication[chandler.RequiresAuthentication()]
|
|
|
|
if !authed {
|
|
|
|
rc.SendRicochetPacket(rc.Conn, 0, []byte{})
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("do not have required authorization to open channel type %v", chandler.Type()))
|
2017-05-02 23:33:51 +00:00
|
|
|
return
|
|
|
|
}
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("succeeded authorization check")
|
2017-05-02 23:33:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
channel, err := rc.channelManager.OpenChannelRequestFromPeer(opm.GetChannelIdentifier(), chandler)
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
|
|
channel.SendMessage = func(message []byte) {
|
|
|
|
rc.SendRicochetPacket(rc.Conn, channel.ID, message)
|
|
|
|
}
|
|
|
|
channel.DelegateAuthorization = func() {
|
|
|
|
rc.Authentication[chandler.Type()] = true
|
|
|
|
}
|
|
|
|
channel.CloseChannel = func() {
|
|
|
|
rc.SendRicochetPacket(rc.Conn, channel.ID, []byte{})
|
|
|
|
rc.channelManager.RemoveChannel(channel.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
response, err := chandler.OpenInbound(channel, opm)
|
|
|
|
if err == nil && channel.Pending == false {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("opening channel %v on %v", channel.Type, channel.ID))
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.SendRicochetPacket(rc.Conn, 0, response)
|
|
|
|
} else {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("removing channel %v", channel.ID))
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.channelManager.RemoveChannel(channel.ID)
|
|
|
|
rc.SendRicochetPacket(rc.Conn, 0, []byte{})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Send Error Packet
|
|
|
|
response := rc.messageBuilder.RejectOpenChannel(opm.GetChannelIdentifier(), "GenericError")
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("sending reject open channel for %v", opm.GetChannelIdentifier()))
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.SendRicochetPacket(rc.Conn, 0, response)
|
|
|
|
|
|
|
|
}
|
|
|
|
} else if res.GetChannelResult() != nil {
|
|
|
|
cr := res.GetChannelResult()
|
|
|
|
id := cr.GetChannelIdentifier()
|
|
|
|
|
|
|
|
channel, found := rc.channelManager.GetChannel(id)
|
|
|
|
|
|
|
|
if !found {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("channel result recived for unknown channel: %v", channel.Type, id))
|
2017-05-02 23:33:51 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if cr.GetOpened() {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("channel of type %v opened on %v", channel.Type, id))
|
2017-05-02 23:33:51 +00:00
|
|
|
(*channel.Handler).OpenOutboundResult(nil, cr)
|
|
|
|
} else {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog(fmt.Sprintf("channel of type %v rejected on %v", channel.Type, id))
|
2017-05-02 23:33:51 +00:00
|
|
|
(*channel.Handler).OpenOutboundResult(errors.New(""), cr)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if res.GetKeepAlive() != nil {
|
|
|
|
// XXX Though not currently part of the protocol
|
|
|
|
// We should likely put these calls behind
|
|
|
|
// authentication.
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("received keep alive packet")
|
2017-05-02 23:33:51 +00:00
|
|
|
if res.GetKeepAlive().GetResponseRequested() {
|
|
|
|
messageBuilder := new(utils.MessageBuilder)
|
|
|
|
raw := messageBuilder.KeepAlive(true)
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("sending keep alive response")
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.SendRicochetPacket(rc.Conn, 0, raw)
|
|
|
|
}
|
|
|
|
} else if res.GetEnableFeatures() != nil {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("received features enabled packet")
|
2017-05-02 23:33:51 +00:00
|
|
|
messageBuilder := new(utils.MessageBuilder)
|
|
|
|
raw := messageBuilder.FeaturesEnabled([]string{})
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("sending featured enabled empty response")
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.SendRicochetPacket(rc.Conn, 0, raw)
|
|
|
|
} else if res.GetFeaturesEnabled() != nil {
|
|
|
|
// TODO We should never send out an enabled features
|
|
|
|
// request.
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("sending unsolicited features enabled response")
|
2017-05-02 23:33:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-27 19:24:04 +00:00
|
|
|
func (rc *Connection) traceLog(message string) {
|
|
|
|
if rc.trace {
|
|
|
|
log.Printf(message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 23:33:51 +00:00
|
|
|
// Break causes Process() to return, but does not close the underlying connection
|
|
|
|
func (rc *Connection) Break() {
|
2017-06-27 19:24:04 +00:00
|
|
|
rc.traceLog("breaking out of process loop")
|
2017-05-02 23:33:51 +00:00
|
|
|
rc.breakChannel <- true
|
|
|
|
<-rc.breakResultChannel // Wait for Process to End
|
|
|
|
}
|
|
|
|
|
|
|
|
// Channel is a convienciance method for returning a given channel to the caller
|
|
|
|
// of Process() - TODO - this is kind of ugly.
|
|
|
|
func (rc *Connection) Channel(ctype string, way channels.Direction) *channels.Channel {
|
|
|
|
return rc.channelManager.Channel(ctype, way)
|
|
|
|
}
|