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 { | ||||||
| 	connection *connection.Connection | 	contactManager        ContactManagerInterface | ||||||
|  | 	privateKey            *rsa.PrivateKey | ||||||
|  | 	chatMessageHandler    func(*RicochetApplicationInstance, uint32, time.Time, string) | ||||||
|  | 	chatMessageAckHandler func(*RicochetApplicationInstance, uint32) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewRicochetApplication ...
 | type RicochetApplicationInstance struct { | ||||||
| func NewRicochetApplication(connection *connection.Connection) *RicochetApplication { | 	connection.AutoConnectionHandler | ||||||
| 	ra := new(RicochetApplication) | 	connection            *connection.Connection | ||||||
| 	ra.connection = connection | 	RemoteHostname        string | ||||||
| 	return ra | 	ChatMessageHandler    func(*RicochetApplicationInstance, uint32, time.Time, string) | ||||||
|  | 	ChatMessageAckHandler func(*RicochetApplicationInstance, uint32) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SendMessage ...
 | func (rai *RicochetApplicationInstance) GetContactDetails() (string, string) { | ||||||
| func (ra *RicochetApplication) SendChatMessage(message string) error { | 	return "EchoBot", "I LIVE 😈😈!!!!" | ||||||
| 	return ra.connection.Do(func() error { | } | ||||||
| 		channel := ra.connection.Channel("im.ricochet.chat", channels.Outbound) | 
 | ||||||
|  | func (rai *RicochetApplicationInstance) ContactRequest(name string, message string) string { | ||||||
|  | 	return "Accepted" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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
 | ||||||
|  | @ -75,15 +78,15 @@ func (ah *HiddenServiceAuthChannel) Closed(err error) { | ||||||
| // Remote -> [Open Authentication Channel] -> Local
 | // Remote -> [Open Authentication Channel] -> Local
 | ||||||
| func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_Data_Control.OpenChannel) ([]byte, error) { | func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_Data_Control.OpenChannel) ([]byte, error) { | ||||||
| 
 | 
 | ||||||
|         if ah.PrivateKey == nil { | 	if ah.PrivateKey == nil { | ||||||
|                 return nil, utils.PrivateKeyNotSetError | 		return nil, utils.PrivateKeyNotSetError | ||||||
|         } | 	} | ||||||
| 
 | 
 | ||||||
| 	ah.channel = channel | 	ah.channel = channel | ||||||
| 	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) | ||||||
|  | @ -97,10 +100,9 @@ func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_D | ||||||
| // Local -> [Open Authentication Channel] -> Remote
 | // Local -> [Open Authentication Channel] -> Remote
 | ||||||
| func (ah *HiddenServiceAuthChannel) OpenOutbound(channel *Channel) ([]byte, error) { | func (ah *HiddenServiceAuthChannel) OpenOutbound(channel *Channel) ([]byte, error) { | ||||||
| 
 | 
 | ||||||
|         if ah.PrivateKey == nil { | 	if ah.PrivateKey == nil { | ||||||
|                 return nil, utils.PrivateKeyNotSetError | 		return nil, utils.PrivateKeyNotSetError | ||||||
|         } | 	} | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| 	ah.channel = channel | 	ah.channel = channel | ||||||
| 	messageBuilder := new(utils.MessageBuilder) | 	messageBuilder := new(utils.MessageBuilder) | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
|  | @ -30,7 +29,7 @@ type Connection struct { | ||||||
| 	unlockResponseChannel chan bool | 	unlockResponseChannel chan bool | ||||||
| 
 | 
 | ||||||
| 	messageBuilder utils.MessageBuilder | 	messageBuilder utils.MessageBuilder | ||||||
| 	trace           bool | 	trace          bool | ||||||
| 
 | 
 | ||||||
| 	Conn           io.ReadWriteCloser | 	Conn           io.ReadWriteCloser | ||||||
| 	IsInbound      bool | 	IsInbound      bool | ||||||
|  | @ -77,7 +76,7 @@ func NewOutboundConnection(conn io.ReadWriteCloser, remoteHostname string) *Conn | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (rc *Connection) TraceLog(enabled bool) { | func (rc *Connection) TraceLog(enabled bool) { | ||||||
|         rc.trace = enabled | 	rc.trace = enabled | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // start
 | // start
 | ||||||
|  | @ -111,12 +110,12 @@ func (rc *Connection) Do(do func() error) error { | ||||||
| // are not met on the local side (a nill error return does not mean the
 | // are not met on the local side (a nill error return does not mean the
 | ||||||
| // channel was opened successfully)
 | // channel was opened successfully)
 | ||||||
| func (rc *Connection) RequestOpenChannel(ctype string, handler Handler) error { | func (rc *Connection) RequestOpenChannel(ctype string, handler Handler) error { | ||||||
|         rc.traceLog(fmt.Sprintf("requesting open channel of type %s", ctype)) | 	rc.traceLog(fmt.Sprintf("requesting open channel of type %s", ctype)) | ||||||
| 	return rc.Do(func() error { | 	return rc.Do(func() 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,14 +124,14 @@ 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 | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		channel, err := rc.channelManager.OpenChannelRequest(chandler) | 		channel, err := rc.channelManager.OpenChannelRequest(chandler) | ||||||
| 
 | 
 | ||||||
| 		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 reqeust open channel of type %v", err)) | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -148,10 +147,10 @@ func (rc *Connection) RequestOpenChannel(ctype string, handler Handler) error { | ||||||
| 		} | 		} | ||||||
| 		response, err := chandler.OpenOutbound(channel) | 		response, err := chandler.OpenOutbound(channel) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 		        rc.traceLog(fmt.Sprintf("requested open channel of type %s", ctype)) | 			rc.traceLog(fmt.Sprintf("requested open channel of type %s", ctype)) | ||||||
| 			rc.SendRicochetPacket(rc.Conn, 0, response) | 			rc.SendRicochetPacket(rc.Conn, 0, response) | ||||||
| 		} else { | 		} else { | ||||||
| 		        rc.traceLog(fmt.Sprintf("failed to reqeust open channel of type %v", err)) | 			rc.traceLog(fmt.Sprintf("failed to reqeust open channel of type %v", err)) | ||||||
| 			rc.channelManager.RemoveChannel(channel.ID) | 			rc.channelManager.RemoveChannel(channel.ID) | ||||||
| 		} | 		} | ||||||
| 		return nil | 		return nil | ||||||
|  | @ -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,14 +187,10 @@ 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) | ||||||
| 			err := proto.Unmarshal(packet.Data[:], res) | 			err := proto.Unmarshal(packet.Data[:], res) | ||||||
| 			if err == nil { | 			if err == nil { | ||||||
|  | @ -209,9 +203,9 @@ 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
 | ||||||
| 					(*channel.Handler).Packet(packet.Data[:]) | 					(*channel.Handler).Packet(packet.Data[:]) | ||||||
| 				} | 				} | ||||||
|  | @ -219,7 +213,7 @@ func (rc *Connection) Process(handler Handler) error { | ||||||
| 				// When a non-zero packet is received for an unknown
 | 				// When a non-zero packet is received for an unknown
 | ||||||
| 				// channel, the recipient responds by closing
 | 				// channel, the recipient responds by closing
 | ||||||
| 				// that channel.
 | 				// that channel.
 | ||||||
| 			        rc.traceLog(fmt.Sprintf("received packet on unknown channel %d. closing.", packet.Channel)) | 				rc.traceLog(fmt.Sprintf("received packet on unknown channel %d. closing.", packet.Channel)) | ||||||
| 				if len(packet.Data) != 0 { | 				if len(packet.Data) != 0 { | ||||||
| 					rc.SendRicochetPacket(rc.Conn, packet.Channel, []byte{}) | 					rc.SendRicochetPacket(rc.Conn, packet.Channel, []byte{}) | ||||||
| 				} | 				} | ||||||
|  | @ -248,7 +242,7 @@ func (rc *Connection) controlPacket(handler Handler, res *Protocol_Data_Control. | ||||||
| 
 | 
 | ||||||
| 		// Check that we have the authentication already
 | 		// Check that we have the authentication already
 | ||||||
| 		if chandler.RequiresAuthentication() != "none" { | 		if chandler.RequiresAuthentication() != "none" { | ||||||
| 		        rc.traceLog(fmt.Sprintf("channel %v requires authorization of type %v", chandler.Type(), chandler.RequiresAuthentication())) | 			rc.traceLog(fmt.Sprintf("channel %v requires authorization of type %v", chandler.Type(), chandler.RequiresAuthentication())) | ||||||
| 			// Enforce Authentication Check.
 | 			// Enforce Authentication Check.
 | ||||||
| 			_, authed := rc.Authentication[chandler.RequiresAuthentication()] | 			_, authed := rc.Authentication[chandler.RequiresAuthentication()] | ||||||
| 			if !authed { | 			if !authed { | ||||||
|  | @ -276,10 +270,10 @@ func (rc *Connection) controlPacket(handler Handler, res *Protocol_Data_Control. | ||||||
| 
 | 
 | ||||||
| 			response, err := chandler.OpenInbound(channel, opm) | 			response, err := chandler.OpenInbound(channel, opm) | ||||||
| 			if err == nil && channel.Pending == false { | 			if err == nil && channel.Pending == false { | ||||||
| 			        rc.traceLog(fmt.Sprintf("opening channel %v on %v", channel.Type, channel.ID)) | 				rc.traceLog(fmt.Sprintf("opening channel %v on %v", channel.Type, channel.ID)) | ||||||
| 				rc.SendRicochetPacket(rc.Conn, 0, response) | 				rc.SendRicochetPacket(rc.Conn, 0, response) | ||||||
| 			} else { | 			} else { | ||||||
| 			        rc.traceLog(fmt.Sprintf("removing channel %v", channel.ID)) | 				rc.traceLog(fmt.Sprintf("removing channel %v", channel.ID)) | ||||||
| 				rc.channelManager.RemoveChannel(channel.ID) | 				rc.channelManager.RemoveChannel(channel.ID) | ||||||
| 				rc.SendRicochetPacket(rc.Conn, 0, []byte{}) | 				rc.SendRicochetPacket(rc.Conn, 0, []byte{}) | ||||||
| 			} | 			} | ||||||
|  | @ -297,15 +291,15 @@ func (rc *Connection) controlPacket(handler Handler, res *Protocol_Data_Control. | ||||||
| 		channel, found := rc.channelManager.GetChannel(id) | 		channel, found := rc.channelManager.GetChannel(id) | ||||||
| 
 | 
 | ||||||
| 		if !found { | 		if !found { | ||||||
| 		        rc.traceLog(fmt.Sprintf("channel result recived for unknown channel: %v", channel.Type, id)) | 			rc.traceLog(fmt.Sprintf("channel result recived for unknown channel: %v", channel.Type, id)) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if cr.GetOpened() { | 		if cr.GetOpened() { | ||||||
| 		        rc.traceLog(fmt.Sprintf("channel of type %v opened on %v", channel.Type, id)) | 			rc.traceLog(fmt.Sprintf("channel of type %v opened on %v", channel.Type, id)) | ||||||
| 			(*channel.Handler).OpenOutboundResult(nil, cr) | 			(*channel.Handler).OpenOutboundResult(nil, cr) | ||||||
| 		} else { | 		} else { | ||||||
| 		        rc.traceLog(fmt.Sprintf("channel of type %v rejected on %v", channel.Type, id)) | 			rc.traceLog(fmt.Sprintf("channel of type %v rejected on %v", channel.Type, id)) | ||||||
| 			(*channel.Handler).OpenOutboundResult(errors.New(""), cr) | 			(*channel.Handler).OpenOutboundResult(errors.New(""), cr) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | @ -321,27 +315,27 @@ func (rc *Connection) controlPacket(handler Handler, res *Protocol_Data_Control. | ||||||
| 			rc.SendRicochetPacket(rc.Conn, 0, raw) | 			rc.SendRicochetPacket(rc.Conn, 0, raw) | ||||||
| 		} | 		} | ||||||
| 	} else if res.GetEnableFeatures() != nil { | 	} else if res.GetEnableFeatures() != nil { | ||||||
| 	        rc.traceLog("received features enabled packet") | 		rc.traceLog("received features enabled packet") | ||||||
| 		messageBuilder := new(utils.MessageBuilder) | 		messageBuilder := new(utils.MessageBuilder) | ||||||
| 		raw := messageBuilder.FeaturesEnabled([]string{}) | 		raw := messageBuilder.FeaturesEnabled([]string{}) | ||||||
| 	        rc.traceLog("sending featured enabled empty response") | 		rc.traceLog("sending featured enabled empty response") | ||||||
| 		rc.SendRicochetPacket(rc.Conn, 0, raw) | 		rc.SendRicochetPacket(rc.Conn, 0, raw) | ||||||
| 	} else if res.GetFeaturesEnabled() != nil { | 	} else if res.GetFeaturesEnabled() != nil { | ||||||
| 		// TODO We should never send out an enabled features
 | 		// TODO We should never send out an enabled features
 | ||||||
| 		// request.
 | 		// request.
 | ||||||
| 		 rc.traceLog("sending unsolicited features enabled response") | 		rc.traceLog("sending unsolicited features enabled response") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (rc *Connection) traceLog(message string) { | func (rc *Connection) traceLog(message string) { | ||||||
|         if rc.trace { | 	if rc.trace { | ||||||
|                 log.Printf(message) | 		log.Printf(message) | ||||||
|         } | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Break causes Process() to return, but does not close the underlying connection
 | // Break causes Process() to return, but does not close the underlying connection
 | ||||||
| func (rc *Connection) Break() { | func (rc *Connection) Break() { | ||||||
|         rc.traceLog("breaking out of process loop") | 	rc.traceLog("breaking out of process loop") | ||||||
| 	rc.breakChannel <- true | 	rc.breakChannel <- true | ||||||
| 	<-rc.breakResultChannel // Wait for Process to End
 | 	<-rc.breakResultChannel // Wait for Process to End
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -35,9 +35,9 @@ func HandleInboundConnection(c *Connection) *InboundConnectionHandler { | ||||||
| // assume they are required to send a contact request before any other activity.
 | // assume they are required to send a contact request before any other activity.
 | ||||||
| func (ich *InboundConnectionHandler) ProcessAuthAsServer(privateKey *rsa.PrivateKey, sach func(hostname string, publicKey rsa.PublicKey) (allowed, known bool)) error { | func (ich *InboundConnectionHandler) ProcessAuthAsServer(privateKey *rsa.PrivateKey, sach func(hostname string, publicKey rsa.PublicKey) (allowed, known bool)) error { | ||||||
| 
 | 
 | ||||||
|         if privateKey == nil { | 	if privateKey == nil { | ||||||
|                 return utils.PrivateKeyNotSetError | 		return utils.PrivateKeyNotSetError | ||||||
|         } | 	} | ||||||
| 
 | 
 | ||||||
| 	ach := new(AutoConnectionHandler) | 	ach := new(AutoConnectionHandler) | ||||||
| 	ach.Init(privateKey, ich.connection.RemoteHostname) | 	ach.Init(privateKey, ich.connection.RemoteHostname) | ||||||
|  | @ -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
 | ||||||
|  | @ -34,9 +33,9 @@ func HandleOutboundConnection(c *Connection) *OutboundConnectionHandler { | ||||||
| // request before any other activity.
 | // request before any other activity.
 | ||||||
| func (och *OutboundConnectionHandler) ProcessAuthAsClient(privateKey *rsa.PrivateKey) (bool, error) { | func (och *OutboundConnectionHandler) ProcessAuthAsClient(privateKey *rsa.PrivateKey) (bool, error) { | ||||||
| 
 | 
 | ||||||
|         if privateKey == nil { | 	if privateKey == nil { | ||||||
|                 return false, utils.PrivateKeyNotSetError | 		return false, utils.PrivateKeyNotSetError | ||||||
|         } | 	} | ||||||
| 
 | 
 | ||||||
| 	ach := new(AutoConnectionHandler) | 	ach := new(AutoConnectionHandler) | ||||||
| 	ach.Init(privateKey, och.connection.RemoteHostname) | 	ach.Init(privateKey, och.connection.RemoteHostname) | ||||||
|  | @ -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 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ func (echobot *RicochetEchoBot) Connect(privateKeyFile string, hostname string) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	rc, _ := goricochet.Open(hostname) | 	rc, _ := goricochet.Open(hostname) | ||||||
| 	known, err  := connection.HandleOutboundConnection(rc).ProcessAuthAsClient(privateKey) | 	known, err := connection.HandleOutboundConnection(rc).ProcessAuthAsClient(privateKey) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 
 | 
 | ||||||
| 		go rc.Process(echobot) | 		go rc.Process(echobot) | ||||||
|  |  | ||||||
							
								
								
									
										66
									
								
								ricochet.go
								
								
								
								
							
							
						
						
									
										66
									
								
								ricochet.go
								
								
								
								
							|  | @ -1,60 +1,59 @@ | ||||||
| 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.
 | ||||||
| // The application should call Process() on the returned OpenConnection to continue
 | // The application should call Process() on the returned OpenConnection to continue
 | ||||||
| // handling protocol messages.
 | // handling protocol messages.
 | ||||||
| func Open(remoteHostname string) (*connection.Connection, error) { | func Open(remoteHostname string) (*connection.Connection, error) { | ||||||
|     networkResolver := utils.NetworkResolver{} | 	networkResolver := utils.NetworkResolver{} | ||||||
|     conn, remoteHostname, err := networkResolver.Resolve(remoteHostname) | 	conn, remoteHostname, err := networkResolver.Resolve(remoteHostname) | ||||||
| 
 | 
 | ||||||
|     if err != nil { | 	if err != nil { | ||||||
|         return nil, err | 		return nil, err | ||||||
|     } | 	} | ||||||
| 
 | 
 | ||||||
|     rc, err := negotiateVersion(conn, remoteHostname) | 	rc, err := negotiateVersion(conn, remoteHostname) | ||||||
|     if err != nil { | 	if err != nil { | ||||||
|         conn.Close() | 		conn.Close() | ||||||
|         return nil, err | 		return nil, err | ||||||
|     } | 	} | ||||||
|     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) { | ||||||
|     versions := []byte{0x49, 0x4D, 0x01, 0x01} | 	versions := []byte{0x49, 0x4D, 0x01, 0x01} | ||||||
|     if n, err := conn.Write(versions); err != nil || n < len(versions) { | 	if n, err := conn.Write(versions); err != nil || n < len(versions) { | ||||||
|         return nil, utils.VersionNegotiationError | 		return nil, utils.VersionNegotiationError | ||||||
|     } | 	} | ||||||
| 
 | 
 | ||||||
|     res := make([]byte, 1) | 	res := make([]byte, 1) | ||||||
|     if _, err := io.ReadAtLeast(conn, res, len(res)); err != nil { | 	if _, err := io.ReadAtLeast(conn, res, len(res)); err != nil { | ||||||
|         return nil, utils.VersionNegotiationError | 		return nil, utils.VersionNegotiationError | ||||||
|     } | 	} | ||||||
| 
 | 
 | ||||||
|     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.
 | ||||||
| func NegotiateVersionInbound(conn net.Conn) (*connection.Connection, error) { | func NegotiateVersionInbound(conn net.Conn) (*connection.Connection, error) { | ||||||
|         versions := []byte{0x49, 0x4D, 0x01, 0x01} | 	versions := []byte{0x49, 0x4D, 0x01, 0x01} | ||||||
|         // Read version response header
 | 	// Read version response header
 | ||||||
| 	header := make([]byte, 3) | 	header := make([]byte, 3) | ||||||
| 	if _, err := io.ReadAtLeast(conn, header, len(header)); err != nil { | 	if _, err := io.ReadAtLeast(conn, header, len(header)); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|  | @ -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,70 +1,68 @@ | ||||||
| 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}) | ||||||
|     } | 	} | ||||||
|     conn.Close() | 	conn.Close() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestRicochet(t *testing.T) { | func TestRicochet(t *testing.T) { | ||||||
|     go SimpleServer() | 	go SimpleServer() | ||||||
|     // 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") | ||||||
|         } | 		} | ||||||
|         return | 		return | ||||||
|     } | 	} | ||||||
|     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) { | ||||||
|  | 	go NotRicochetServer() | ||||||
|  | 	time.Sleep(time.Second) | ||||||
| 
 | 
 | ||||||
| func TestNotARicochetServer(t*testing.T) { | 	_, err := Open("127.0.0.1:11002|abcdefghijklmno.onion") | ||||||
|     go NotRicochetServer() | 	if err != utils.VersionNegotiationError { | ||||||
|     time.Sleep(time.Second) | 		t.Errorf("RicochetProtocol: Server Had No Correct Version - Should Have Failed: err = %v", err) | ||||||
| 
 | 	} | ||||||
|     _,err := Open("127.0.0.1:11002|abcdefghijklmno.onion") |  | ||||||
|     if err != utils.VersionNegotiationError { |  | ||||||
|         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,12 +20,25 @@ const ( | ||||||
| 	UnknownChannelTypeError      = Error("UnknownChannelTypeError") | 	UnknownChannelTypeError      = Error("UnknownChannelTypeError") | ||||||
| 	UnauthorizedChannelTypeError = Error("UnauthorizedChannelTypeError") | 	UnauthorizedChannelTypeError = Error("UnauthorizedChannelTypeError") | ||||||
| 
 | 
 | ||||||
|  | 	// Timeout Errors
 | ||||||
| 	ActionTimedOutError = Error("ActionTimedOutError") | 	ActionTimedOutError = Error("ActionTimedOutError") | ||||||
|  | 	PeerTimedOutError   = Error("PeerTimedOutError") | ||||||
| 
 | 
 | ||||||
| 	ClientFailedToAuthenticateError = Error("ClientFailedToAuthenticateError") | 	// Authentication Errors
 | ||||||
|  | 	ClientFailedToAuthenticateError     = Error("ClientFailedToAuthenticateError") | ||||||
|  | 	ServerRejectedClientConnectionError = Error("ServerRejectedClientConnectionError") | ||||||
| 
 | 
 | ||||||
|  | 	UnauthorizedActionError  = Error("UnauthorizedActionError") | ||||||
|  | 	ChannelClosedByPeerError = Error("ChannelClosedByPeerError") | ||||||
| 
 | 
 | ||||||
|         PrivateKeyNotSetError = Error("ClientFailedToAuthenticateError") | 	// Channel Management Errors
 | ||||||
|  | 	ServerAttemptedToOpenEvenNumberedChannelError = Error("ServerAttemptedToOpenEvenNumberedChannelError") | ||||||
|  | 	ClientAttemptedToOpenOddNumberedChannelError  = Error("ClientAttemptedToOpenOddNumberedChannelError") | ||||||
|  | 	ChannelIDIsAlreadyInUseError                  = Error("ChannelIDIsAlreadyInUseError") | ||||||
|  | 	AttemptToOpenMoreThanOneSingletonChannelError = Error("AttemptToOpenMoreThanOneSingletonChannelError") | ||||||
|  | 
 | ||||||
|  | 	// Library Use Errors
 | ||||||
|  | 	PrivateKeyNotSetError = Error("ClientFailedToAuthenticateError") | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // CheckError is a helper function for panicing on errors which we need to handle
 | // CheckError is a helper function for panicing on errors which we need to handle
 | ||||||
|  |  | ||||||
|  | @ -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