First Cut of Applications + Bugs, Formatting
This commit is contained in:
parent
1cf7c2b7c7
commit
22cbf5d738
|
@ -0,0 +1,23 @@
|
||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AcceptAllContactManager implements the contact manager interface an presumes
|
||||||
|
// all connections are allowed.
|
||||||
|
type AcceptAllContactManager struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupContact returns that a contact is known and allowed to communicate for all cases.
|
||||||
|
func (aacm *AcceptAllContactManager) LookupContact(hostname string, publicKey rsa.PublicKey) (allowed, known bool) {
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aacm *AcceptAllContactManager) GetContactDetails() (string, string) {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aacm *AcceptAllContactManager) ContactRequest(name string, message string) string {
|
||||||
|
return "Accepted"
|
||||||
|
}
|
|
@ -1,36 +1,134 @@
|
||||||
package application
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"crypto/rsa"
|
||||||
|
"github.com/s-rah/go-ricochet"
|
||||||
"github.com/s-rah/go-ricochet/channels"
|
"github.com/s-rah/go-ricochet/channels"
|
||||||
"github.com/s-rah/go-ricochet/connection"
|
"github.com/s-rah/go-ricochet/connection"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RicochetApplication bundles many useful constructs that are
|
// RicochetApplication bundles many useful constructs that are
|
||||||
// likely standard in a ricochet application
|
// likely standard in a ricochet application
|
||||||
type RicochetApplication struct {
|
type RicochetApplication struct {
|
||||||
|
contactManager ContactManagerInterface
|
||||||
|
privateKey *rsa.PrivateKey
|
||||||
|
chatMessageHandler func(*RicochetApplicationInstance, uint32, time.Time, string)
|
||||||
|
chatMessageAckHandler func(*RicochetApplicationInstance, uint32)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RicochetApplicationInstance struct {
|
||||||
|
connection.AutoConnectionHandler
|
||||||
connection *connection.Connection
|
connection *connection.Connection
|
||||||
|
RemoteHostname string
|
||||||
|
ChatMessageHandler func(*RicochetApplicationInstance, uint32, time.Time, string)
|
||||||
|
ChatMessageAckHandler func(*RicochetApplicationInstance, uint32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRicochetApplication ...
|
func (rai *RicochetApplicationInstance) GetContactDetails() (string, string) {
|
||||||
func NewRicochetApplication(connection *connection.Connection) *RicochetApplication {
|
return "EchoBot", "I LIVE 😈😈!!!!"
|
||||||
ra := new(RicochetApplication)
|
|
||||||
ra.connection = connection
|
|
||||||
return ra
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMessage ...
|
func (rai *RicochetApplicationInstance) ContactRequest(name string, message string) string {
|
||||||
func (ra *RicochetApplication) SendChatMessage(message string) error {
|
return "Accepted"
|
||||||
return ra.connection.Do(func() error {
|
}
|
||||||
channel := ra.connection.Channel("im.ricochet.chat", channels.Outbound)
|
|
||||||
|
func (rai *RicochetApplicationInstance) ContactRequestRejected() {
|
||||||
|
}
|
||||||
|
func (rai *RicochetApplicationInstance) ContactRequestAccepted() {
|
||||||
|
}
|
||||||
|
func (rai *RicochetApplicationInstance) ContactRequestError() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rai *RicochetApplicationInstance) SendChatMessage(message string) {
|
||||||
|
|
||||||
|
// Technically this errors afte the second time but we can ignore it.
|
||||||
|
rai.connection.RequestOpenChannel("im.ricochet.chat", rai)
|
||||||
|
|
||||||
|
rai.connection.Do(func() error {
|
||||||
|
channel := rai.connection.Channel("im.ricochet.chat", channels.Outbound)
|
||||||
if channel != nil {
|
if channel != nil {
|
||||||
chatchannel, ok := (*channel.Handler).(*channels.ChatChannel)
|
chatchannel, ok := (*channel.Handler).(*channels.ChatChannel)
|
||||||
if ok {
|
if ok {
|
||||||
chatchannel.SendMessage(message)
|
chatchannel.SendMessage(message)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return errors.New("")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rai *RicochetApplicationInstance) ChatMessage(messageID uint32, when time.Time, message string) bool {
|
||||||
|
go rai.ChatMessageHandler(rai, messageID, when, message)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rai *RicochetApplicationInstance) ChatMessageAck(messageID uint32) {
|
||||||
|
rai.ChatMessageAckHandler(rai, messageID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *RicochetApplication) Init(pk *rsa.PrivateKey, cm ContactManagerInterface) {
|
||||||
|
ra.privateKey = pk
|
||||||
|
ra.contactManager = cm
|
||||||
|
ra.chatMessageHandler = func(*RicochetApplicationInstance, uint32, time.Time, string) {}
|
||||||
|
ra.chatMessageAckHandler = func(*RicochetApplicationInstance, uint32) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *RicochetApplication) OnChatMessage(call func(*RicochetApplicationInstance, uint32, time.Time, string)) {
|
||||||
|
ra.chatMessageHandler = call
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *RicochetApplication) OnChatMessageAck(call func(*RicochetApplicationInstance, uint32)) {
|
||||||
|
ra.chatMessageAckHandler = call
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *RicochetApplication) handleConnection(conn net.Conn) {
|
||||||
|
rc, err := goricochet.NegotiateVersionInbound(conn)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("There was an error")
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ich := connection.HandleInboundConnection(rc)
|
||||||
|
|
||||||
|
err = ich.ProcessAuthAsServer(ra.privateKey, ra.contactManager.LookupContact)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("There was an error")
|
||||||
|
conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rai := new(RicochetApplicationInstance)
|
||||||
|
rai.Init(ra.privateKey, "")
|
||||||
|
rai.RemoteHostname = rc.RemoteHostname
|
||||||
|
rai.connection = rc
|
||||||
|
rai.ChatMessageHandler = ra.chatMessageHandler
|
||||||
|
rai.ChatMessageAckHandler = ra.chatMessageAckHandler
|
||||||
|
|
||||||
|
rai.RegisterChannelHandler("im.ricochet.contact.request", func() channels.Handler {
|
||||||
|
contact := new(channels.ContactRequestChannel)
|
||||||
|
contact.Handler = rai
|
||||||
|
return contact
|
||||||
|
})
|
||||||
|
rai.RegisterChannelHandler("im.ricochet.chat", func() channels.Handler {
|
||||||
|
chat := new(channels.ChatChannel)
|
||||||
|
chat.Handler = rai
|
||||||
|
return chat
|
||||||
|
})
|
||||||
|
rc.Process(rai)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *RicochetApplication) Run(l net.Listener) {
|
||||||
|
if ra.privateKey == nil || ra.contactManager == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err == nil {
|
||||||
|
go ra.handleConnection(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContactManagerInterface provides a mechanism for autonous applications
|
||||||
|
// to make decisions on what connections to accept or reject.
|
||||||
|
type ContactManagerInterface interface {
|
||||||
|
LookupContact(hostname string, publicKey rsa.PublicKey) (allowed, known bool)
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/s-rah/go-ricochet/application"
|
||||||
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
echobot := new(application.RicochetApplication)
|
||||||
|
pk, err := utils.LoadPrivateKeyFromFile("./testing/private_key")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error reading private key file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := application.SetupOnion("127.0.0.1:9051", "", pk, 9878)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error setting up onion service: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
echobot.Init(pk, new(application.AcceptAllContactManager))
|
||||||
|
echobot.OnChatMessage(func(rai *application.RicochetApplicationInstance, id uint32, timestamp time.Time, message string) {
|
||||||
|
log.Printf("message from %v - %v", rai.RemoteHostname, message)
|
||||||
|
rai.SendChatMessage(message)
|
||||||
|
})
|
||||||
|
log.Printf("echobot listening on %s", l.Addr().String())
|
||||||
|
echobot.Run(l)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"github.com/yawning/bulb"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetupOnion(proxyServer string, authentication string, pk *rsa.PrivateKey, onionport uint16) (net.Listener, error) {
|
||||||
|
c, err := bulb.Dial("tcp4", proxyServer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Authenticate(authentication); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &bulb.NewOnionConfig{
|
||||||
|
DiscardPK: true,
|
||||||
|
PrivateKey: pk,
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.NewListener(cfg, onionport)
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ const (
|
||||||
|
|
||||||
// AuthChannelResult captures the result of an authentication flow
|
// AuthChannelResult captures the result of an authentication flow
|
||||||
type AuthChannelResult struct {
|
type AuthChannelResult struct {
|
||||||
|
Hostname string
|
||||||
Accepted bool
|
Accepted bool
|
||||||
IsKnownContact bool
|
IsKnownContact bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
package channels
|
package channels
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"github.com/s-rah/go-ricochet/wire/contact"
|
"github.com/s-rah/go-ricochet/wire/contact"
|
||||||
"github.com/s-rah/go-ricochet/wire/control"
|
"github.com/s-rah/go-ricochet/wire/control"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Defining Versions
|
||||||
|
const (
|
||||||
|
InvalidContactNameError = utils.Error("InvalidContactNameError")
|
||||||
|
InvalidContactMessageError = utils.Error("InvalidContactMessageError")
|
||||||
|
InvalidContactRequestError = utils.Error("InvalidContactRequestError")
|
||||||
|
)
|
||||||
|
|
||||||
// ContactRequestChannel implements the ChannelHandler interface for a channel of
|
// ContactRequestChannel implements the ChannelHandler interface for a channel of
|
||||||
// type "im.ricochet.contact.request". The channel may be inbound or outbound.
|
// type "im.ricochet.contact.request". The channel may be inbound or outbound.
|
||||||
// a ContactRequestChannelHandler implementation to handle chat events.
|
// a ContactRequestChannelHandler implementation to handle chat events.
|
||||||
|
@ -73,12 +79,12 @@ func (crc *ContactRequestChannel) OpenInbound(channel *Channel, oc *Protocol_Dat
|
||||||
|
|
||||||
if len(contactRequest.GetNickname()) > int(Protocol_Data_ContactRequest.Limits_NicknameMaxCharacters) {
|
if len(contactRequest.GetNickname()) > int(Protocol_Data_ContactRequest.Limits_NicknameMaxCharacters) {
|
||||||
// Violation of the Protocol
|
// Violation of the Protocol
|
||||||
return nil, errors.New("invalid nickname")
|
return nil, InvalidContactNameError
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(contactRequest.GetMessageText()) > int(Protocol_Data_ContactRequest.Limits_MessageMaxCharacters) {
|
if len(contactRequest.GetMessageText()) > int(Protocol_Data_ContactRequest.Limits_MessageMaxCharacters) {
|
||||||
// Violation of the Protocol
|
// Violation of the Protocol
|
||||||
return nil, errors.New("invalid message")
|
return nil, InvalidContactMessageError
|
||||||
}
|
}
|
||||||
|
|
||||||
result := crc.Handler.ContactRequest(contactRequest.GetNickname(), contactRequest.GetMessageText())
|
result := crc.Handler.ContactRequest(contactRequest.GetNickname(), contactRequest.GetMessageText())
|
||||||
|
@ -86,7 +92,7 @@ func (crc *ContactRequestChannel) OpenInbound(channel *Channel, oc *Protocol_Dat
|
||||||
return messageBuilder.ReplyToContactRequestOnResponse(channel.ID, result), nil
|
return messageBuilder.ReplyToContactRequestOnResponse(channel.ID, result), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, errors.New("could not parse contact request extension")
|
return nil, InvalidContactRequestError
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenOutbound is the first method called for an outbound channel request.
|
// OpenOutbound is the first method called for an outbound channel request.
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"errors"
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"github.com/s-rah/go-ricochet/wire/auth"
|
"github.com/s-rah/go-ricochet/wire/auth"
|
||||||
|
@ -15,6 +14,10 @@ import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
InvalidClientCookieError = utils.Error("InvalidClientCookieError")
|
||||||
|
)
|
||||||
|
|
||||||
// HiddenServiceAuthChannel wraps implementation of im.ricochet.auth.hidden-service"
|
// HiddenServiceAuthChannel wraps implementation of im.ricochet.auth.hidden-service"
|
||||||
type HiddenServiceAuthChannel struct {
|
type HiddenServiceAuthChannel struct {
|
||||||
// Methods of Handler are called for events on this channel
|
// Methods of Handler are called for events on this channel
|
||||||
|
@ -83,7 +86,7 @@ func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_D
|
||||||
clientCookie, _ := proto.GetExtension(oc, Protocol_Data_AuthHiddenService.E_ClientCookie)
|
clientCookie, _ := proto.GetExtension(oc, Protocol_Data_AuthHiddenService.E_ClientCookie)
|
||||||
if len(clientCookie.([]byte)[:]) != 16 {
|
if len(clientCookie.([]byte)[:]) != 16 {
|
||||||
// reutrn without opening channel.
|
// reutrn without opening channel.
|
||||||
return nil, errors.New("invalid client cookie")
|
return nil, InvalidClientCookieError
|
||||||
}
|
}
|
||||||
ah.AddClientCookie(clientCookie.([]byte)[:])
|
ah.AddClientCookie(clientCookie.([]byte)[:])
|
||||||
messageBuilder := new(utils.MessageBuilder)
|
messageBuilder := new(utils.MessageBuilder)
|
||||||
|
@ -101,7 +104,6 @@ func (ah *HiddenServiceAuthChannel) OpenOutbound(channel *Channel) ([]byte, erro
|
||||||
return nil, utils.PrivateKeyNotSetError
|
return nil, utils.PrivateKeyNotSetError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ah.channel = channel
|
ah.channel = channel
|
||||||
messageBuilder := new(utils.MessageBuilder)
|
messageBuilder := new(utils.MessageBuilder)
|
||||||
return messageBuilder.OpenAuthenticationChannel(ah.channel.ID, ah.GenClientCookie()), nil
|
return messageBuilder.OpenAuthenticationChannel(ah.channel.ID, ah.GenClientCookie()), nil
|
||||||
|
|
|
@ -65,7 +65,7 @@ func (ach *AutoConnectionHandler) ClientAuthResult(accepted bool, isKnownContact
|
||||||
func (ach *AutoConnectionHandler) ServerAuthValid(hostname string, publicKey rsa.PublicKey) (allowed, known bool) {
|
func (ach *AutoConnectionHandler) ServerAuthValid(hostname string, publicKey rsa.PublicKey) (allowed, known bool) {
|
||||||
// Do something
|
// Do something
|
||||||
accepted, isKnownContact := ach.sach(hostname, publicKey)
|
accepted, isKnownContact := ach.sach(hostname, publicKey)
|
||||||
ach.authResultChannel <- channels.AuthChannelResult{Accepted: accepted, IsKnownContact: isKnownContact}
|
ach.authResultChannel <- channels.AuthChannelResult{Hostname: hostname, Accepted: accepted, IsKnownContact: isKnownContact}
|
||||||
return accepted, isKnownContact
|
return accepted, isKnownContact
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package connection
|
package connection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"github.com/s-rah/go-ricochet/channels"
|
"github.com/s-rah/go-ricochet/channels"
|
||||||
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChannelManager encapsulates the logic for server and client side assignment
|
// ChannelManager encapsulates the logic for server and client side assignment
|
||||||
|
@ -38,7 +38,7 @@ func NewServerChannelManager() *ChannelManager {
|
||||||
func (cm *ChannelManager) OpenChannelRequest(chandler channels.Handler) (*channels.Channel, error) {
|
func (cm *ChannelManager) OpenChannelRequest(chandler channels.Handler) (*channels.Channel, error) {
|
||||||
// Some channels only allow us to open one of them per connection
|
// Some channels only allow us to open one of them per connection
|
||||||
if chandler.Singleton() && cm.Channel(chandler.Type(), channels.Outbound) != nil {
|
if chandler.Singleton() && cm.Channel(chandler.Type(), channels.Outbound) != nil {
|
||||||
return nil, errors.New("Connection already has channel of type " + chandler.Type())
|
return nil, utils.AttemptToOpenMoreThanOneSingletonChannelError
|
||||||
}
|
}
|
||||||
|
|
||||||
channel := new(channels.Channel)
|
channel := new(channels.Channel)
|
||||||
|
@ -57,20 +57,20 @@ func (cm *ChannelManager) OpenChannelRequest(chandler channels.Handler) (*channe
|
||||||
func (cm *ChannelManager) OpenChannelRequestFromPeer(channelID int32, chandler channels.Handler) (*channels.Channel, error) {
|
func (cm *ChannelManager) OpenChannelRequestFromPeer(channelID int32, chandler channels.Handler) (*channels.Channel, error) {
|
||||||
if cm.isClient && (channelID%2) != 0 {
|
if cm.isClient && (channelID%2) != 0 {
|
||||||
// Server is trying to open odd numbered channels
|
// Server is trying to open odd numbered channels
|
||||||
return nil, errors.New("server may only open even numbered channels")
|
return nil, utils.ServerAttemptedToOpenEvenNumberedChannelError
|
||||||
} else if !cm.isClient && (channelID%2) == 0 {
|
} else if !cm.isClient && (channelID%2) == 0 {
|
||||||
// Server is trying to open odd numbered channels
|
// Server is trying to open odd numbered channels
|
||||||
return nil, errors.New("client may only open odd numbered channels")
|
return nil, utils.ClientAttemptedToOpenOddNumberedChannelError
|
||||||
}
|
}
|
||||||
|
|
||||||
_, exists := cm.channels[channelID]
|
_, exists := cm.channels[channelID]
|
||||||
if exists {
|
if exists {
|
||||||
return nil, errors.New("channel id is already in use")
|
return nil, utils.ChannelIDIsAlreadyInUseError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some channels only allow us to open one of them per connection
|
// Some channels only allow us to open one of them per connection
|
||||||
if chandler.Singleton() && cm.Channel(chandler.Type(), channels.Inbound) != nil {
|
if chandler.Singleton() && cm.Channel(chandler.Type(), channels.Inbound) != nil {
|
||||||
return nil, errors.New("Connection already has channel of type " + chandler.Type())
|
return nil, utils.AttemptToOpenMoreThanOneSingletonChannelError
|
||||||
}
|
}
|
||||||
|
|
||||||
channel := new(channels.Channel)
|
channel := new(channels.Channel)
|
||||||
|
|
|
@ -2,14 +2,13 @@ package connection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/s-rah/go-ricochet/channels"
|
"github.com/s-rah/go-ricochet/channels"
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"github.com/s-rah/go-ricochet/wire/control"
|
"github.com/s-rah/go-ricochet/wire/control"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Connection encapsulates the state required to maintain a connection to
|
// Connection encapsulates the state required to maintain a connection to
|
||||||
|
@ -116,7 +115,7 @@ func (rc *Connection) RequestOpenChannel(ctype string, handler Handler) error {
|
||||||
chandler, err := handler.OnOpenChannelRequest(ctype)
|
chandler, err := handler.OnOpenChannelRequest(ctype)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rc.traceLog(fmt.Sprintf("failed to reqeust open channel of type %v", err))
|
rc.traceLog(fmt.Sprintf("failed to request open channel of type %v", err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +124,7 @@ func (rc *Connection) RequestOpenChannel(ctype string, handler Handler) error {
|
||||||
// Enforce Authentication Check.
|
// Enforce Authentication Check.
|
||||||
_, authed := rc.Authentication[chandler.RequiresAuthentication()]
|
_, authed := rc.Authentication[chandler.RequiresAuthentication()]
|
||||||
if !authed {
|
if !authed {
|
||||||
return errors.New("connection is not auth'd")
|
return utils.UnauthorizedActionError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +173,6 @@ func (rc *Connection) Process(handler Handler) error {
|
||||||
for !breaked {
|
for !breaked {
|
||||||
|
|
||||||
var packet utils.RicochetData
|
var packet utils.RicochetData
|
||||||
tick := time.Tick(30 * time.Second)
|
|
||||||
select {
|
select {
|
||||||
case <-rc.unlockChannel:
|
case <-rc.unlockChannel:
|
||||||
<-rc.unlockResponseChannel
|
<-rc.unlockResponseChannel
|
||||||
|
@ -189,12 +187,8 @@ func (rc *Connection) Process(handler Handler) error {
|
||||||
rc.Conn.Close()
|
rc.Conn.Close()
|
||||||
handler.OnClosed(err)
|
handler.OnClosed(err)
|
||||||
return err
|
return err
|
||||||
case <-tick:
|
|
||||||
rc.traceLog("peer timed out")
|
|
||||||
return errors.New("peer timed out")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if packet.Channel == 0 {
|
if packet.Channel == 0 {
|
||||||
rc.traceLog(fmt.Sprintf("received control packet on channel %d", packet.Channel))
|
rc.traceLog(fmt.Sprintf("received control packet on channel %d", packet.Channel))
|
||||||
res := new(Protocol_Data_Control.Packet)
|
res := new(Protocol_Data_Control.Packet)
|
||||||
|
@ -209,7 +203,7 @@ func (rc *Connection) Process(handler Handler) error {
|
||||||
if len(packet.Data) == 0 {
|
if len(packet.Data) == 0 {
|
||||||
rc.traceLog(fmt.Sprintf("removing channel %d", packet.Channel))
|
rc.traceLog(fmt.Sprintf("removing channel %d", packet.Channel))
|
||||||
rc.channelManager.RemoveChannel(packet.Channel)
|
rc.channelManager.RemoveChannel(packet.Channel)
|
||||||
(*channel.Handler).Closed(errors.New("channel closed by peer"))
|
(*channel.Handler).Closed(utils.ChannelClosedByPeerError)
|
||||||
} else {
|
} else {
|
||||||
rc.traceLog(fmt.Sprintf("received packet on %v channel %d", (*channel.Handler).Type(), packet.Channel))
|
rc.traceLog(fmt.Sprintf("received packet on %v channel %d", (*channel.Handler).Type(), packet.Channel))
|
||||||
// Send The Ricochet Packet to the Handler
|
// Send The Ricochet Packet to the Handler
|
||||||
|
|
|
@ -56,6 +56,7 @@ func (ich *InboundConnectionHandler) ProcessAuthAsServer(privateKey *rsa.Private
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if authResult.Accepted == true {
|
if authResult.Accepted == true {
|
||||||
|
ich.connection.RemoteHostname = authResult.Hostname
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return utils.ClientFailedToAuthenticateError
|
return utils.ClientFailedToAuthenticateError
|
||||||
|
|
|
@ -2,10 +2,9 @@ package connection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"errors"
|
|
||||||
"github.com/s-rah/go-ricochet/channels"
|
"github.com/s-rah/go-ricochet/channels"
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
|
||||||
"github.com/s-rah/go-ricochet/policies"
|
"github.com/s-rah/go-ricochet/policies"
|
||||||
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OutboundConnectionHandler is a convieniance wrapper for handling outbound
|
// OutboundConnectionHandler is a convieniance wrapper for handling outbound
|
||||||
|
@ -61,5 +60,5 @@ func (och *OutboundConnectionHandler) ProcessAuthAsClient(privateKey *rsa.Privat
|
||||||
return result.IsKnownContact, nil
|
return result.IsKnownContact, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, errors.New("authentication was not accepted by the server")
|
return false, utils.ServerRejectedClientConnectionError
|
||||||
}
|
}
|
||||||
|
|
10
ricochet.go
10
ricochet.go
|
@ -1,11 +1,12 @@
|
||||||
package goricochet
|
package goricochet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
|
||||||
"github.com/s-rah/go-ricochet/connection"
|
"github.com/s-rah/go-ricochet/connection"
|
||||||
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Open establishes a protocol session on an established net.Conn, and returns a new
|
// Open establishes a protocol session on an established net.Conn, and returns a new
|
||||||
// OpenConnection instance representing this connection. On error, the connection
|
// OpenConnection instance representing this connection. On error, the connection
|
||||||
// will be closed. This function blocks until version negotiation has completed.
|
// will be closed. This function blocks until version negotiation has completed.
|
||||||
|
@ -27,7 +28,6 @@ func Open(remoteHostname string) (*connection.Connection, error) {
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// negotiate version takes an open network connection and executes
|
// negotiate version takes an open network connection and executes
|
||||||
// the ricochet version negotiation procedure.
|
// the ricochet version negotiation procedure.
|
||||||
func negotiateVersion(conn net.Conn, remoteHostname string) (*connection.Connection, error) {
|
func negotiateVersion(conn net.Conn, remoteHostname string) (*connection.Connection, error) {
|
||||||
|
@ -44,11 +44,10 @@ func negotiateVersion(conn net.Conn, remoteHostname string) (*connection.Connect
|
||||||
if res[0] != 0x01 {
|
if res[0] != 0x01 {
|
||||||
return nil, utils.VersionNegotiationFailed
|
return nil, utils.VersionNegotiationFailed
|
||||||
}
|
}
|
||||||
rc := connection.NewOutboundConnection(conn,remoteHostname)
|
rc := connection.NewOutboundConnection(conn, remoteHostname)
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NegotiateVersionInbound takes in a connection and performs version negotiation
|
// NegotiateVersionInbound takes in a connection and performs version negotiation
|
||||||
// as if that connection was a client. Returns a ricochet connection if successful
|
// as if that connection was a client. Returns a ricochet connection if successful
|
||||||
// error otherwise.
|
// error otherwise.
|
||||||
|
@ -89,6 +88,3 @@ func NegotiateVersionInbound(conn net.Conn) (*connection.Connection, error) {
|
||||||
rc := connection.NewInboundConnection(conn)
|
rc := connection.NewInboundConnection(conn)
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
package goricochet
|
package goricochet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"net"
|
"net"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func SimpleServer() {
|
func SimpleServer() {
|
||||||
ln,_ := net.Listen("tcp", "127.0.0.1:11000")
|
ln, _ := net.Listen("tcp", "127.0.0.1:11000")
|
||||||
conn,_ := ln.Accept()
|
conn, _ := ln.Accept()
|
||||||
b := make([]byte, 4)
|
b := make([]byte, 4)
|
||||||
n,err := conn.Read(b)
|
n, err := conn.Read(b)
|
||||||
if n == 4 && err == nil {
|
if n == 4 && err == nil {
|
||||||
conn.Write([]byte{0x01})
|
conn.Write([]byte{0x01})
|
||||||
}
|
}
|
||||||
|
@ -20,16 +19,16 @@ func SimpleServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BadVersionNegotiation() {
|
func BadVersionNegotiation() {
|
||||||
ln,_ := net.Listen("tcp", "127.0.0.1:11001")
|
ln, _ := net.Listen("tcp", "127.0.0.1:11001")
|
||||||
conn,_ := ln.Accept()
|
conn, _ := ln.Accept()
|
||||||
// We are already testing negotiation bytes, we don't care, just send a termination.
|
// We are already testing negotiation bytes, we don't care, just send a termination.
|
||||||
conn.Write([]byte{0x00})
|
conn.Write([]byte{0x00})
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotRicochetServer() {
|
func NotRicochetServer() {
|
||||||
ln,_ := net.Listen("tcp", "127.0.0.1:11002")
|
ln, _ := net.Listen("tcp", "127.0.0.1:11002")
|
||||||
conn,_ := ln.Accept()
|
conn, _ := ln.Accept()
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +37,7 @@ func TestRicochet(t *testing.T) {
|
||||||
// Wait for Server to Initialize
|
// Wait for Server to Initialize
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
rc,err := Open("127.0.0.1:11000|abcdefghijklmno.onion")
|
rc, err := Open("127.0.0.1:11000|abcdefghijklmno.onion")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if rc.IsInbound {
|
if rc.IsInbound {
|
||||||
t.Errorf("RicochetConnection declares itself as an Inbound connection after an Outbound attempt...that shouldn't happen")
|
t.Errorf("RicochetConnection declares itself as an Inbound connection after an Outbound attempt...that shouldn't happen")
|
||||||
|
@ -48,22 +47,21 @@ func TestRicochet(t *testing.T) {
|
||||||
t.Errorf("RicochetProtocol: Open Failed: %v", err)
|
t.Errorf("RicochetProtocol: Open Failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBadVersionNegotiation(t*testing.T) {
|
func TestBadVersionNegotiation(t *testing.T) {
|
||||||
go BadVersionNegotiation()
|
go BadVersionNegotiation()
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
_,err := Open("127.0.0.1:11001|abcdefghijklmno.onion")
|
_, err := Open("127.0.0.1:11001|abcdefghijklmno.onion")
|
||||||
if err != utils.VersionNegotiationFailed {
|
if err != utils.VersionNegotiationFailed {
|
||||||
t.Errorf("RicochetProtocol: Server Had No Correct Version - Should Have Failed: err = %v", err)
|
t.Errorf("RicochetProtocol: Server Had No Correct Version - Should Have Failed: err = %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNotARicochetServer(t *testing.T) {
|
||||||
func TestNotARicochetServer(t*testing.T) {
|
|
||||||
go NotRicochetServer()
|
go NotRicochetServer()
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
_,err := Open("127.0.0.1:11002|abcdefghijklmno.onion")
|
_, err := Open("127.0.0.1:11002|abcdefghijklmno.onion")
|
||||||
if err != utils.VersionNegotiationError {
|
if err != utils.VersionNegotiationError {
|
||||||
t.Errorf("RicochetProtocol: Server Had No Correct Version - Should Have Failed: err = %v", err)
|
t.Errorf("RicochetProtocol: Server Had No Correct Version - Should Have Failed: err = %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,13 @@ import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
InvalidPrivateKeyFileError = Error("InvalidPrivateKeyFileError")
|
||||||
|
)
|
||||||
|
|
||||||
// LoadPrivateKeyFromFile loads a private key from a file...
|
// LoadPrivateKeyFromFile loads a private key from a file...
|
||||||
func LoadPrivateKeyFromFile(filename string) (*rsa.PrivateKey, error) {
|
func LoadPrivateKeyFromFile(filename string) (*rsa.PrivateKey, error) {
|
||||||
pemData, err := ioutil.ReadFile(filename)
|
pemData, err := ioutil.ReadFile(filename)
|
||||||
|
@ -18,7 +21,7 @@ func LoadPrivateKeyFromFile(filename string) (*rsa.PrivateKey, error) {
|
||||||
|
|
||||||
block, _ := pem.Decode(pemData)
|
block, _ := pem.Decode(pemData)
|
||||||
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
||||||
return nil, errors.New("not a private key")
|
return nil, InvalidPrivateKeyFileError
|
||||||
}
|
}
|
||||||
|
|
||||||
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
|
|
@ -20,11 +20,24 @@ const (
|
||||||
UnknownChannelTypeError = Error("UnknownChannelTypeError")
|
UnknownChannelTypeError = Error("UnknownChannelTypeError")
|
||||||
UnauthorizedChannelTypeError = Error("UnauthorizedChannelTypeError")
|
UnauthorizedChannelTypeError = Error("UnauthorizedChannelTypeError")
|
||||||
|
|
||||||
|
// Timeout Errors
|
||||||
ActionTimedOutError = Error("ActionTimedOutError")
|
ActionTimedOutError = Error("ActionTimedOutError")
|
||||||
|
PeerTimedOutError = Error("PeerTimedOutError")
|
||||||
|
|
||||||
|
// Authentication Errors
|
||||||
ClientFailedToAuthenticateError = Error("ClientFailedToAuthenticateError")
|
ClientFailedToAuthenticateError = Error("ClientFailedToAuthenticateError")
|
||||||
|
ServerRejectedClientConnectionError = Error("ServerRejectedClientConnectionError")
|
||||||
|
|
||||||
|
UnauthorizedActionError = Error("UnauthorizedActionError")
|
||||||
|
ChannelClosedByPeerError = Error("ChannelClosedByPeerError")
|
||||||
|
|
||||||
|
// Channel Management Errors
|
||||||
|
ServerAttemptedToOpenEvenNumberedChannelError = Error("ServerAttemptedToOpenEvenNumberedChannelError")
|
||||||
|
ClientAttemptedToOpenOddNumberedChannelError = Error("ClientAttemptedToOpenOddNumberedChannelError")
|
||||||
|
ChannelIDIsAlreadyInUseError = Error("ChannelIDIsAlreadyInUseError")
|
||||||
|
AttemptToOpenMoreThanOneSingletonChannelError = Error("AttemptToOpenMoreThanOneSingletonChannelError")
|
||||||
|
|
||||||
|
// Library Use Errors
|
||||||
PrivateKeyNotSetError = Error("ClientFailedToAuthenticateError")
|
PrivateKeyNotSetError = Error("ClientFailedToAuthenticateError")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,14 @@ package utils
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
InvalidPacketLengthError = Error("InvalidPacketLengthError")
|
||||||
|
InvalidChannelIDError = Error("InvalidChannelIDError")
|
||||||
|
)
|
||||||
|
|
||||||
// RicochetData is a structure containing the raw data and the channel it the
|
// RicochetData is a structure containing the raw data and the channel it the
|
||||||
// message originated on.
|
// message originated on.
|
||||||
type RicochetData struct {
|
type RicochetData struct {
|
||||||
|
@ -36,11 +40,11 @@ type RicochetNetwork struct {
|
||||||
func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data []byte) error {
|
func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data []byte) error {
|
||||||
packet := make([]byte, 4+len(data))
|
packet := make([]byte, 4+len(data))
|
||||||
if len(packet) > 65535 {
|
if len(packet) > 65535 {
|
||||||
return errors.New("packet too large")
|
return InvalidPacketLengthError
|
||||||
}
|
}
|
||||||
binary.BigEndian.PutUint16(packet[0:2], uint16(len(packet)))
|
binary.BigEndian.PutUint16(packet[0:2], uint16(len(packet)))
|
||||||
if channel < 0 || channel > 65535 {
|
if channel < 0 || channel > 65535 {
|
||||||
return errors.New("invalid channel ID")
|
return InvalidChannelIDError
|
||||||
}
|
}
|
||||||
binary.BigEndian.PutUint16(packet[2:4], uint16(channel))
|
binary.BigEndian.PutUint16(packet[2:4], uint16(channel))
|
||||||
copy(packet[4:], data[:])
|
copy(packet[4:], data[:])
|
||||||
|
@ -68,7 +72,7 @@ func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, e
|
||||||
|
|
||||||
size := int(binary.BigEndian.Uint16(header[0:2]))
|
size := int(binary.BigEndian.Uint16(header[0:2]))
|
||||||
if size < 4 {
|
if size < 4 {
|
||||||
return packet, errors.New("invalid packet length")
|
return packet, InvalidPacketLengthError
|
||||||
}
|
}
|
||||||
|
|
||||||
packet.Channel = int32(binary.BigEndian.Uint16(header[2:4]))
|
packet.Channel = int32(binary.BigEndian.Uint16(header[2:4]))
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CannotResolveLocalTCPAddressError = Error("CannotResolveLocalTCPAddressError")
|
||||||
|
CannotDialLocalTCPAddressError = Error("CannotDialLocalTCPAddressError")
|
||||||
|
CannotDialRicochetAddressError = Error("CannotDialRicochetAddressError")
|
||||||
|
)
|
||||||
|
|
||||||
// NetworkResolver allows a client to resolve various hostnames to connections
|
// NetworkResolver allows a client to resolve various hostnames to connections
|
||||||
// The supported types are onions address are:
|
// The supported types are onions address are:
|
||||||
// * ricochet:jlq67qzo6s4yp3sp
|
// * ricochet:jlq67qzo6s4yp3sp
|
||||||
|
@ -21,11 +26,11 @@ func (nr *NetworkResolver) Resolve(hostname string) (net.Conn, string, error) {
|
||||||
addrParts := strings.Split(hostname, "|")
|
addrParts := strings.Split(hostname, "|")
|
||||||
tcpAddr, err := net.ResolveTCPAddr("tcp", addrParts[0])
|
tcpAddr, err := net.ResolveTCPAddr("tcp", addrParts[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.New("Cannot Resolve Local TCP Address")
|
return nil, "", CannotResolveLocalTCPAddressError
|
||||||
}
|
}
|
||||||
conn, err := net.DialTCP("tcp", nil, tcpAddr)
|
conn, err := net.DialTCP("tcp", nil, tcpAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.New("Cannot Dial Local TCP Address")
|
return nil, "", CannotDialLocalTCPAddressError
|
||||||
}
|
}
|
||||||
|
|
||||||
// return just the onion address, not the local override for the hostname
|
// return just the onion address, not the local override for the hostname
|
||||||
|
@ -45,7 +50,7 @@ func (nr *NetworkResolver) Resolve(hostname string) (net.Conn, string, error) {
|
||||||
|
|
||||||
conn, err := torDialer.Dial("tcp", resolvedHostname+".onion:9878")
|
conn, err := torDialer.Dial("tcp", resolvedHostname+".onion:9878")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.New("Cannot Dial Remote Ricochet Address")
|
return nil, "", CannotDialRicochetAddressError
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn, resolvedHostname, nil
|
return conn, resolvedHostname, nil
|
||||||
|
|
Loading…
Reference in New Issue