91 lines
2.8 KiB
Go
91 lines
2.8 KiB
Go
|
package connection
|
||
|
|
||
|
import (
|
||
|
"crypto/rsa"
|
||
|
"github.com/s-rah/go-ricochet/channels"
|
||
|
"github.com/s-rah/go-ricochet/policies"
|
||
|
"github.com/s-rah/go-ricochet/utils"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
// OutboundConnectionHandler is a convieniance wrapper for handling outbound
|
||
|
// connections
|
||
|
type OutboundConnectionHandler struct {
|
||
|
connection *Connection
|
||
|
}
|
||
|
|
||
|
// HandleOutboundConnection returns an OutboundConnectionHandler given a connection
|
||
|
func HandleOutboundConnection(c *Connection) *OutboundConnectionHandler {
|
||
|
och := new(OutboundConnectionHandler)
|
||
|
och.connection = c
|
||
|
return och
|
||
|
}
|
||
|
|
||
|
// ProcessAuthAsClient blocks until authentication has succeeded or failed with the
|
||
|
// provided privateKey, or the connection is closed. A non-nil error is returned in all
|
||
|
// cases other than successful authentication.
|
||
|
//
|
||
|
// ProcessAuthAsClient cannot be called at the same time as any other call to a Porcess
|
||
|
// function. Another Process function must be called after this function successfully
|
||
|
// returns to continue handling connection events.
|
||
|
//
|
||
|
// For successful authentication, the `known` return value indicates whether the peer
|
||
|
// accepts us as a known contact. Unknown contacts will generally need to send a contact
|
||
|
// request before any other activity.
|
||
|
func (och *OutboundConnectionHandler) ProcessAuthAsClient(privateKey *rsa.PrivateKey) (bool, error) {
|
||
|
|
||
|
if privateKey == nil {
|
||
|
return false, utils.PrivateKeyNotSetError
|
||
|
}
|
||
|
|
||
|
ach := new(AutoConnectionHandler)
|
||
|
ach.Init()
|
||
|
|
||
|
// Make sure that calls to Break in this function cannot race
|
||
|
var breakOnce sync.Once
|
||
|
|
||
|
var accepted, isKnownContact bool
|
||
|
authCallback := func(accept, known bool) {
|
||
|
accepted = accept
|
||
|
isKnownContact = known
|
||
|
// Cause the Process() call below to return.
|
||
|
// If Break() is called from here, it _must_ use go, because this will
|
||
|
// execute in the Process goroutine, and Break() will deadlock.
|
||
|
breakOnce.Do(func() { go och.connection.Break() })
|
||
|
}
|
||
|
|
||
|
processResult := make(chan error, 1)
|
||
|
go func() {
|
||
|
// Break Process() if timed out; no-op if Process returned a conn error
|
||
|
defer func() { breakOnce.Do(func() { och.connection.Break() }) }()
|
||
|
policy := policies.UnknownPurposeTimeout
|
||
|
err := policy.ExecuteAction(func() error {
|
||
|
return och.connection.Process(ach)
|
||
|
})
|
||
|
processResult <- err
|
||
|
}()
|
||
|
|
||
|
err := och.connection.Do(func() error {
|
||
|
_, err := och.connection.RequestOpenChannel("im.ricochet.auth.hidden-service",
|
||
|
&channels.HiddenServiceAuthChannel{
|
||
|
PrivateKey: privateKey,
|
||
|
ServerHostname: och.connection.RemoteHostname,
|
||
|
ClientAuthResult: authCallback,
|
||
|
})
|
||
|
return err
|
||
|
})
|
||
|
if err != nil {
|
||
|
breakOnce.Do(func() { och.connection.Break() })
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
if err = <-processResult; err != nil {
|
||
|
return false, err
|
||
|
}
|
||
|
|
||
|
if accepted == true {
|
||
|
return isKnownContact, nil
|
||
|
}
|
||
|
return false, utils.ServerRejectedClientConnectionError
|
||
|
}
|