core: Attach protocol connections to Contact
This commit is contained in:
parent
6e68f861d8
commit
a52de9078c
|
@ -2,19 +2,24 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
protocol "github.com/s-rah/go-ricochet"
|
||||||
|
"github.com/special/notricochet/rpc"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// XXX There is generally a lot of duplication and boilerplate between
|
// XXX There is generally a lot of duplication and boilerplate between
|
||||||
// Contact, ConfigContact, and rpc.Contact. This should be reduced somehow.
|
// Contact, ConfigContact, and rpc.Contact. This should be reduced somehow.
|
||||||
|
|
||||||
// XXX This is threadsafe only because it can't be modified right now.
|
|
||||||
|
|
||||||
type Contact struct {
|
type Contact struct {
|
||||||
id int
|
id int
|
||||||
|
|
||||||
data ConfigContact
|
data ConfigContact
|
||||||
|
|
||||||
|
mutex sync.Mutex
|
||||||
|
|
||||||
|
connection *protocol.OpenConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
func ContactFromConfig(id int, data ConfigContact) (*Contact, error) {
|
func ContactFromConfig(id int, data ConfigContact) (*Contact, error) {
|
||||||
|
@ -37,23 +42,107 @@ func (c *Contact) Id() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Contact) Nickname() string {
|
func (c *Contact) Nickname() string {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
return c.data.Nickname
|
return c.data.Nickname
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Contact) Address() string {
|
func (c *Contact) Address() string {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
return "ricochet:" + c.data.Hostname[0:16]
|
return "ricochet:" + c.data.Hostname[0:16]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Contact) Hostname() string {
|
func (c *Contact) Hostname() string {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
return c.data.Hostname
|
return c.data.Hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Contact) LastConnected() time.Time {
|
func (c *Contact) LastConnected() time.Time {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
time, _ := time.Parse(time.RFC3339, c.data.LastConnected)
|
time, _ := time.Parse(time.RFC3339, c.data.LastConnected)
|
||||||
return time
|
return time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Contact) WhenCreated() time.Time {
|
func (c *Contact) WhenCreated() time.Time {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
time, _ := time.Parse(time.RFC3339, c.data.WhenCreated)
|
time, _ := time.Parse(time.RFC3339, c.data.WhenCreated)
|
||||||
return time
|
return time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Contact) Status() ricochet.Contact_Status {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
if c.connection == nil {
|
||||||
|
return ricochet.Contact_UNKNOWN
|
||||||
|
} else {
|
||||||
|
return ricochet.Contact_ONLINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Contact) SetConnection(conn *protocol.OpenConnection) error {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if conn == c.connection {
|
||||||
|
return fmt.Errorf("Duplicate assignment of connection %v to contact %v", conn, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !conn.IsAuthed || conn.Closed {
|
||||||
|
return fmt.Errorf("Connection %v is not in a valid state to assign to contact %v", conn, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.data.Hostname[0:16] != conn.OtherHostname {
|
||||||
|
return fmt.Errorf("Connection hostname %s doesn't match contact hostname %s when assigning connection", conn.OtherHostname, c.data.Hostname[0:16])
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.connection != nil && c.connection.Closed {
|
||||||
|
log.Printf("Replacing dead connection %v for contact %v", c.connection, c)
|
||||||
|
c.connection = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide whether to replace an existing connection with this one
|
||||||
|
if c.connection != nil {
|
||||||
|
// If the existing connection is in the same direction, always use the new one
|
||||||
|
if c.connection.Client == conn.Client {
|
||||||
|
log.Printf("Replacing existing same-direction connection %v with new connection %v for contact %v", c.connection, conn, c)
|
||||||
|
c.connection.Close()
|
||||||
|
c.connection = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the existing connection is more than 30 seconds old, use the new one
|
||||||
|
// XXX implement this
|
||||||
|
|
||||||
|
// Fall back to string comparison of hostnames for a stable resolution
|
||||||
|
preferOutbound := conn.MyHostname < conn.OtherHostname
|
||||||
|
if preferOutbound == conn.Client {
|
||||||
|
// New connection wins
|
||||||
|
log.Printf("Replacing existing connection %v with new connection %v for contact %v according to fallback order", c.connection, conn, c)
|
||||||
|
c.connection.Close()
|
||||||
|
c.connection = nil
|
||||||
|
} else {
|
||||||
|
// Old connection wins
|
||||||
|
log.Printf("Keeping existing connection %v instead of new connection %v for contact %v according to fallback order", c.connection, conn, c)
|
||||||
|
conn.Close()
|
||||||
|
return fmt.Errorf("Using existing connection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this connection is inbound and there's an outbound attempt, keep this
|
||||||
|
// connection and cancel outbound if we haven't sent authentication yet, or
|
||||||
|
// if the outbound connection will lose the fallback comparison above.
|
||||||
|
// XXX implement this
|
||||||
|
|
||||||
|
c.connection = conn
|
||||||
|
log.Printf("Assigned connection %v to contact %v", c.connection, c)
|
||||||
|
|
||||||
|
// XXX implicit accept contact requests
|
||||||
|
// XXX update connected date
|
||||||
|
// XXX signal state and data changes
|
||||||
|
// XXX react to connection state changes
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -100,6 +100,10 @@ func (me *Identity) setPrivateKey(key *rsa.PrivateKey) error {
|
||||||
|
|
||||||
// BUG(special): No error handling for failures under publishService
|
// BUG(special): No error handling for failures under publishService
|
||||||
func (me *Identity) publishService(key *rsa.PrivateKey) {
|
func (me *Identity) publishService(key *rsa.PrivateKey) {
|
||||||
|
// This call will block until a control connection is available and the
|
||||||
|
// ADD_ONION command has returned. After creating the listener, it will
|
||||||
|
// be automatically re-published if the control connection is lost and
|
||||||
|
// later reconnected.
|
||||||
service, listener, err := me.core.Network.NewOnionListener(9878, key)
|
service, listener, err := me.core.Network.NewOnionListener(9878, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Identity listener failed: %v", err)
|
log.Printf("Identity listener failed: %v", err)
|
||||||
|
@ -108,6 +112,12 @@ func (me *Identity) publishService(key *rsa.PrivateKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if key == nil {
|
if key == nil {
|
||||||
|
if service.PrivateKey == nil {
|
||||||
|
log.Printf("Setting private key failed: no key returned")
|
||||||
|
// XXX handle
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err := me.setPrivateKey(service.PrivateKey.(*rsa.PrivateKey))
|
err := me.setPrivateKey(service.PrivateKey.(*rsa.PrivateKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Setting private key failed: %v", err)
|
log.Printf("Setting private key failed: %v", err)
|
||||||
|
|
|
@ -76,10 +76,26 @@ func (handler *protocolHandler) OnAuthenticationChallenge(oc *protocol.OpenConne
|
||||||
|
|
||||||
func (handler *protocolHandler) OnAuthenticationProof(oc *protocol.OpenConnection, channelID int32, publicKey []byte, signature []byte, isKnownContact bool) {
|
func (handler *protocolHandler) OnAuthenticationProof(oc *protocol.OpenConnection, channelID int32, publicKey []byte, signature []byte, isKnownContact bool) {
|
||||||
result := oc.ValidateProof(channelID, publicKey, signature)
|
result := oc.ValidateProof(channelID, publicKey, signature)
|
||||||
log.Printf("protocol: OnAuthenticationProof, result: %v", result)
|
|
||||||
|
var contact *Contact
|
||||||
|
if result {
|
||||||
|
if len(oc.OtherHostname) != 16 {
|
||||||
|
log.Printf("protocol: Invalid format for hostname '%s' in authentication proof", oc.OtherHostname)
|
||||||
|
result = false
|
||||||
|
} else {
|
||||||
|
contact = handler.p.core.Identity.ContactList().ContactByAddress("ricochet:" + oc.OtherHostname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isKnownContact = (contact != nil)
|
||||||
|
|
||||||
oc.SendAuthenticationResult(channelID, result, isKnownContact)
|
oc.SendAuthenticationResult(channelID, result, isKnownContact)
|
||||||
oc.IsAuthed = result
|
oc.IsAuthed = result
|
||||||
oc.CloseChannel(channelID)
|
oc.CloseChannel(channelID)
|
||||||
|
|
||||||
|
log.Printf("protocol: OnAuthenticationProof, result: %v, contact: %v", result, contact)
|
||||||
|
if result && contact != nil {
|
||||||
|
contact.SetConnection(oc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *protocolHandler) OnAuthenticationResult(oc *protocol.OpenConnection, channelID int32, result bool, isKnownContact bool) {
|
func (handler *protocolHandler) OnAuthenticationResult(oc *protocol.OpenConnection, channelID int32, result bool, isKnownContact bool) {
|
||||||
|
@ -89,7 +105,8 @@ func (handler *protocolHandler) OnAuthenticationResult(oc *protocol.OpenConnecti
|
||||||
|
|
||||||
// Contact Management
|
// Contact Management
|
||||||
func (handler *protocolHandler) IsKnownContact(hostname string) bool {
|
func (handler *protocolHandler) IsKnownContact(hostname string) bool {
|
||||||
return true
|
contact := handler.p.core.Identity.ContactList().ContactByAddress("ricochet:" + hostname)
|
||||||
|
return contact != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *protocolHandler) OnContactRequest(oc *protocol.OpenConnection, channelID int32, nick string, message string) {
|
func (handler *protocolHandler) OnContactRequest(oc *protocol.OpenConnection, channelID int32, nick string, message string) {
|
||||||
|
|
Loading…
Reference in New Issue