2017-12-29 04:16:20 +00:00
package yggdrasil
// This is the session manager
// It's responsible for keeping track of open sessions to other nodes
// The session information consists of crypto keys and coords
2018-07-30 13:44:46 +00:00
import (
"bytes"
2019-04-18 22:38:23 +00:00
"sync"
2018-07-30 13:44:46 +00:00
"time"
2018-12-15 02:49:18 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
2019-08-04 02:46:18 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/util"
2019-08-24 01:05:18 +00:00
"github.com/Arceliar/phony"
2018-07-30 13:44:46 +00:00
)
2017-12-29 04:16:20 +00:00
2019-08-16 23:37:16 +00:00
// Duration that we keep track of old nonces per session, to allow some out-of-order packet delivery
const nonceWindow = time . Second
2018-06-10 23:03:28 +00:00
// All the information we know about an active session.
// This includes coords, permanent and ephemeral keys, handles and nonces, various sorts of timing information for timeout and maintenance, and some metadata for the admin API.
2017-12-29 04:16:20 +00:00
type sessionInfo struct {
2019-08-31 22:39:05 +00:00
phony . Inbox // Protects all of the below, use it any time you read/change the contents of a session
sessions * sessions //
theirAddr address . Address //
theirSubnet address . Subnet //
theirPermPub crypto . BoxPubKey //
theirSesPub crypto . BoxPubKey //
mySesPub crypto . BoxPubKey //
mySesPriv crypto . BoxPrivKey //
sharedPermKey crypto . BoxSharedKey // used for session pings
sharedSesKey crypto . BoxSharedKey // derived from session keys
theirHandle crypto . Handle //
myHandle crypto . Handle //
theirNonce crypto . BoxNonce //
myNonce crypto . BoxNonce //
2020-01-05 17:27:54 +00:00
theirMTU MTU //
myMTU MTU //
2019-08-31 22:39:05 +00:00
wasMTUFixed bool // Was the MTU fixed by a receive error?
2019-11-29 09:45:02 +00:00
timeOpened time . Time // Time the session was opened
2019-08-31 22:39:05 +00:00
time time . Time // Time we last received a packet
mtuTime time . Time // time myMTU was last changed
pingTime time . Time // time the first ping was sent since the last received packet
coords [ ] byte // coords of destination
reset bool // reset if coords change
tstamp int64 // ATOMIC - tstamp from their last session ping, replay attack mitigation
bytesSent uint64 // Bytes of real traffic sent in this session
bytesRecvd uint64 // Bytes of real traffic received in this session
init chan struct { } // Closed when the first session pong arrives, used to signal that the session is ready for initial use
cancel util . Cancellation // Used to terminate workers
conn * Conn // The associated Conn object
callbacks [ ] chan func ( ) // Finished work from crypto workers
2019-04-22 01:38:14 +00:00
}
2019-11-29 09:45:02 +00:00
// Represents a session ping/pong packet, and includes information like public keys, a session handle, coords, a timestamp to prevent replays, and the tun/tap MTU.
2017-12-29 04:16:20 +00:00
type sessionPing struct {
2018-12-15 02:49:18 +00:00
SendPermPub crypto . BoxPubKey // Sender's permanent key
Handle crypto . Handle // Random number to ID session
SendSesPub crypto . BoxPubKey // Session key to use
2019-04-21 10:50:41 +00:00
Coords [ ] byte //
Tstamp int64 // unix time, but the only real requirement is that it increases
IsPong bool //
2020-01-05 17:27:54 +00:00
MTU MTU //
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Updates session info in response to a ping, after checking that the ping is OK.
// Returns true if the session was updated, or false otherwise.
2019-08-24 01:05:18 +00:00
func ( s * sessionInfo ) _update ( p * sessionPing ) bool {
2019-04-23 10:46:16 +00:00
if ! ( p . Tstamp > s . tstamp ) {
2018-01-26 23:30:51 +00:00
// To protect against replay attacks
2018-01-04 22:37:51 +00:00
return false
}
2018-06-02 21:19:42 +00:00
if p . SendPermPub != s . theirPermPub {
2018-01-26 23:30:51 +00:00
// Should only happen if two sessions got the same handle
// That shouldn't be allowed anyway, but if it happens then let one time out
2018-01-04 22:37:51 +00:00
return false
2018-01-26 23:30:51 +00:00
}
2018-06-02 21:19:42 +00:00
if p . SendSesPub != s . theirSesPub {
s . theirSesPub = p . SendSesPub
s . theirHandle = p . Handle
2018-12-15 02:49:18 +00:00
s . sharedSesKey = * crypto . GetSharedKey ( & s . mySesPriv , & s . theirSesPub )
s . theirNonce = crypto . BoxNonce { }
2018-01-04 22:37:51 +00:00
}
2018-06-02 21:19:42 +00:00
if p . MTU >= 1280 || p . MTU == 0 {
s . theirMTU = p . MTU
2019-08-24 05:17:37 +00:00
if s . conn != nil {
s . conn . setMTU ( s , s . _getMTU ( ) )
}
2018-02-11 23:09:05 +00:00
}
2018-07-30 13:44:46 +00:00
if ! bytes . Equal ( s . coords , p . Coords ) {
// allocate enough space for additional coords
s . coords = append ( make ( [ ] byte , 0 , len ( p . Coords ) + 11 ) , p . Coords ... )
}
2019-04-22 01:38:14 +00:00
s . time = time . Now ( )
s . tstamp = p . Tstamp
2019-06-29 21:10:02 +00:00
s . reset = false
defer func ( ) { recover ( ) } ( ) // Recover if the below panics
select {
case <- s . init :
default :
// Unblock anything waiting for the session to initialize
close ( s . init )
}
2018-01-04 22:37:51 +00:00
return true
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Struct of all active sessions.
// Sessions are indexed by handle.
// Additionally, stores maps of address/subnet onto keys, and keys onto handles.
2017-12-29 04:16:20 +00:00
type sessions struct {
2019-08-24 01:53:00 +00:00
router * router
2019-06-11 09:52:21 +00:00
listener * Listener
listenerMutex sync . Mutex
lastCleanup time . Time
isAllowedHandler func ( pubkey * crypto . BoxPubKey , initiator bool ) bool // Returns true or false if session setup is allowed
isAllowedMutex sync . RWMutex // Protects the above
2020-01-05 17:27:54 +00:00
myMaximumMTU MTU // Maximum allowed session MTU
2019-06-11 09:52:21 +00:00
permShared map [ crypto . BoxPubKey ] * crypto . BoxSharedKey // Maps known permanent keys to their shared key, used by DHT a lot
2019-06-29 00:21:44 +00:00
sinfos map [ crypto . Handle ] * sessionInfo // Maps handle onto session info
2019-06-11 09:52:21 +00:00
byTheirPerm map [ crypto . BoxPubKey ] * crypto . Handle // Maps theirPermPub onto handle
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Initializes the session struct.
2019-08-24 01:53:00 +00:00
func ( ss * sessions ) init ( r * router ) {
ss . router = r
2018-12-15 02:49:18 +00:00
ss . permShared = make ( map [ crypto . BoxPubKey ] * crypto . BoxSharedKey )
ss . sinfos = make ( map [ crypto . Handle ] * sessionInfo )
ss . byTheirPerm = make ( map [ crypto . BoxPubKey ] * crypto . Handle )
2018-06-22 01:31:30 +00:00
ss . lastCleanup = time . Now ( )
2019-11-21 00:02:39 +00:00
ss . myMaximumMTU = 65535
2017-12-29 04:16:20 +00:00
}
2019-08-28 18:31:04 +00:00
func ( ss * sessions ) reconfigure ( ) {
2019-11-21 09:54:36 +00:00
ss . router . Act ( nil , func ( ) {
for _ , session := range ss . sinfos {
2019-11-22 01:23:44 +00:00
sinfo , mtu := session , ss . myMaximumMTU
sinfo . Act ( ss . router , func ( ) {
sinfo . myMTU = mtu
} )
2019-11-21 09:54:36 +00:00
session . ping ( ss . router )
}
} )
2019-08-25 17:10:59 +00:00
}
2018-10-07 16:13:41 +00:00
// Determines whether the session with a given publickey is allowed based on
// session firewall rules.
2018-12-15 02:49:18 +00:00
func ( ss * sessions ) isSessionAllowed ( pubkey * crypto . BoxPubKey , initiator bool ) bool {
2019-06-11 09:52:21 +00:00
ss . isAllowedMutex . RLock ( )
defer ss . isAllowedMutex . RUnlock ( )
2019-01-14 18:24:35 +00:00
2019-06-11 09:52:21 +00:00
if ss . isAllowedHandler == nil {
2018-10-07 16:13:41 +00:00
return true
}
2019-06-11 09:52:21 +00:00
return ss . isAllowedHandler ( pubkey , initiator )
2018-10-07 16:13:41 +00:00
}
2018-06-10 23:03:28 +00:00
// Gets the session corresponding to a given handle.
2018-12-15 02:49:18 +00:00
func ( ss * sessions ) getSessionForHandle ( handle * crypto . Handle ) ( * sessionInfo , bool ) {
2018-01-04 22:37:51 +00:00
sinfo , isIn := ss . sinfos [ * handle ]
return sinfo , isIn
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Gets a session corresponding to a permanent key used by the remote node.
2018-12-15 02:49:18 +00:00
func ( ss * sessions ) getByTheirPerm ( key * crypto . BoxPubKey ) ( * sessionInfo , bool ) {
2018-01-04 22:37:51 +00:00
h , isIn := ss . byTheirPerm [ * key ]
if ! isIn {
return nil , false
}
sinfo , isIn := ss . getSessionForHandle ( h )
return sinfo , isIn
2017-12-29 04:16:20 +00:00
}
2019-04-22 19:06:39 +00:00
// Creates a new session and lazily cleans up old existing sessions. This
2019-11-29 09:45:02 +00:00
// includes initializing session info to sane defaults (e.g. lowest supported
2019-04-22 19:06:39 +00:00
// MTU).
2018-12-15 02:49:18 +00:00
func ( ss * sessions ) createSession ( theirPermKey * crypto . BoxPubKey ) * sessionInfo {
2019-06-13 22:37:53 +00:00
// TODO: this check definitely needs to be moved
2019-01-14 18:24:35 +00:00
if ! ss . isSessionAllowed ( theirPermKey , true ) {
return nil
2018-10-08 18:51:51 +00:00
}
2018-01-04 22:37:51 +00:00
sinfo := sessionInfo { }
2019-08-24 01:53:00 +00:00
sinfo . sessions = ss
2018-01-04 22:37:51 +00:00
sinfo . theirPermPub = * theirPermKey
2019-08-24 01:53:00 +00:00
sinfo . sharedPermKey = * ss . getSharedKey ( & ss . router . core . boxPriv , & sinfo . theirPermPub )
2018-12-15 02:49:18 +00:00
pub , priv := crypto . NewBoxKeys ( )
2018-01-04 22:37:51 +00:00
sinfo . mySesPub = * pub
sinfo . mySesPriv = * priv
2018-12-15 02:49:18 +00:00
sinfo . myNonce = * crypto . NewBoxNonce ( )
2018-02-11 23:09:05 +00:00
sinfo . theirMTU = 1280
2019-11-21 00:02:39 +00:00
sinfo . myMTU = ss . myMaximumMTU
2018-04-22 20:31:30 +00:00
now := time . Now ( )
2019-05-29 11:59:36 +00:00
sinfo . timeOpened = now
2019-04-22 01:38:14 +00:00
sinfo . time = now
sinfo . mtuTime = now
sinfo . pingTime = now
2019-06-29 21:10:02 +00:00
sinfo . init = make ( chan struct { } )
2019-08-06 00:11:28 +00:00
sinfo . cancel = util . NewCancellation ( )
2018-01-04 22:37:51 +00:00
higher := false
2019-08-24 01:53:00 +00:00
for idx := range ss . router . core . boxPub {
if ss . router . core . boxPub [ idx ] > sinfo . theirPermPub [ idx ] {
2018-01-04 22:37:51 +00:00
higher = true
break
2019-08-24 01:53:00 +00:00
} else if ss . router . core . boxPub [ idx ] < sinfo . theirPermPub [ idx ] {
2018-01-04 22:37:51 +00:00
break
}
}
if higher {
// higher => odd nonce
sinfo . myNonce [ len ( sinfo . myNonce ) - 1 ] |= 0x01
} else {
// lower => even nonce
sinfo . myNonce [ len ( sinfo . myNonce ) - 1 ] &= 0xfe
}
2018-12-15 02:49:18 +00:00
sinfo . myHandle = * crypto . NewHandle ( )
sinfo . theirAddr = * address . AddrForNodeID ( crypto . GetNodeID ( & sinfo . theirPermPub ) )
sinfo . theirSubnet = * address . SubnetForNodeID ( crypto . GetNodeID ( & sinfo . theirPermPub ) )
2018-01-04 22:37:51 +00:00
ss . sinfos [ sinfo . myHandle ] = & sinfo
ss . byTheirPerm [ sinfo . theirPermPub ] = & sinfo . myHandle
return & sinfo
2017-12-29 04:16:20 +00:00
}
2018-06-22 01:31:30 +00:00
func ( ss * sessions ) cleanup ( ) {
// Time thresholds almost certainly could use some adjusting
2018-11-25 18:25:38 +00:00
for k := range ss . permShared {
// Delete a key, to make sure this eventually shrinks to 0
delete ( ss . permShared , k )
break
}
2018-06-22 01:31:30 +00:00
if time . Since ( ss . lastCleanup ) < time . Minute {
return
}
2018-12-15 02:49:18 +00:00
permShared := make ( map [ crypto . BoxPubKey ] * crypto . BoxSharedKey , len ( ss . permShared ) )
2018-11-25 18:25:38 +00:00
for k , v := range ss . permShared {
permShared [ k ] = v
}
ss . permShared = permShared
2018-12-15 02:49:18 +00:00
sinfos := make ( map [ crypto . Handle ] * sessionInfo , len ( ss . sinfos ) )
2018-11-25 18:25:38 +00:00
for k , v := range ss . sinfos {
sinfos [ k ] = v
}
ss . sinfos = sinfos
2018-12-15 02:49:18 +00:00
byTheirPerm := make ( map [ crypto . BoxPubKey ] * crypto . Handle , len ( ss . byTheirPerm ) )
2018-11-25 18:25:38 +00:00
for k , v := range ss . byTheirPerm {
byTheirPerm [ k ] = v
}
ss . byTheirPerm = byTheirPerm
2018-06-22 01:31:30 +00:00
ss . lastCleanup = time . Now ( )
}
2019-08-26 00:13:47 +00:00
func ( sinfo * sessionInfo ) doRemove ( ) {
2019-08-28 00:43:54 +00:00
sinfo . sessions . router . Act ( nil , func ( ) {
2019-08-26 00:13:47 +00:00
sinfo . sessions . removeSession ( sinfo )
} )
}
2019-07-27 23:10:32 +00:00
// Closes a session, removing it from sessions maps.
2019-08-24 01:05:18 +00:00
func ( ss * sessions ) removeSession ( sinfo * sessionInfo ) {
2019-08-24 01:53:00 +00:00
if s := sinfo . sessions . sinfos [ sinfo . myHandle ] ; s == sinfo {
delete ( sinfo . sessions . sinfos , sinfo . myHandle )
delete ( sinfo . sessions . byTheirPerm , sinfo . theirPermPub )
2019-06-29 22:44:28 +00:00
}
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Returns a session ping appropriate for the given session info.
2019-08-24 01:05:18 +00:00
func ( sinfo * sessionInfo ) _getPing ( ) sessionPing {
2019-08-24 01:53:00 +00:00
loc := sinfo . sessions . router . core . switchTable . getLocator ( )
2018-01-04 22:37:51 +00:00
coords := loc . getCoords ( )
2019-08-24 01:05:18 +00:00
ping := sessionPing {
2019-08-24 01:53:00 +00:00
SendPermPub : sinfo . sessions . router . core . boxPub ,
2018-06-02 21:19:42 +00:00
Handle : sinfo . myHandle ,
SendSesPub : sinfo . mySesPub ,
Tstamp : time . Now ( ) . Unix ( ) ,
Coords : coords ,
MTU : sinfo . myMTU ,
2018-01-04 22:37:51 +00:00
}
2018-12-15 02:49:18 +00:00
sinfo . myNonce . Increment ( )
2019-08-24 01:05:18 +00:00
return ping
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Gets the shared key for a pair of box keys.
// Used to cache recently used shared keys for protocol traffic.
// This comes up with dht req/res and session ping/pong traffic.
2018-12-15 02:49:18 +00:00
func ( ss * sessions ) getSharedKey ( myPriv * crypto . BoxPrivKey ,
theirPub * crypto . BoxPubKey ) * crypto . BoxSharedKey {
2019-06-30 00:32:15 +00:00
return crypto . GetSharedKey ( myPriv , theirPub )
// FIXME concurrency issues with the below, so for now we just burn the CPU every time
2018-01-04 22:37:51 +00:00
if skey , isIn := ss . permShared [ * theirPub ] ; isIn {
return skey
}
// First do some cleanup
2018-10-20 19:48:07 +00:00
const maxKeys = 1024
2018-01-04 22:37:51 +00:00
for key := range ss . permShared {
// Remove a random key until the store is small enough
if len ( ss . permShared ) < maxKeys {
break
}
delete ( ss . permShared , key )
}
2018-12-15 02:49:18 +00:00
ss . permShared [ * theirPub ] = crypto . GetSharedKey ( myPriv , theirPub )
2018-01-04 22:37:51 +00:00
return ss . permShared [ * theirPub ]
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Sends a session ping by calling sendPingPong in ping mode.
2019-08-25 15:36:09 +00:00
func ( sinfo * sessionInfo ) ping ( from phony . Actor ) {
2019-08-28 00:43:54 +00:00
sinfo . Act ( from , func ( ) {
2019-08-24 01:05:18 +00:00
sinfo . _sendPingPong ( false )
} )
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Calls getPing, sets the appropriate ping/pong flag, encodes to wire format, and send it.
// Updates the time the last ping was sent in the session info.
2019-08-24 01:05:18 +00:00
func ( sinfo * sessionInfo ) _sendPingPong ( isPong bool ) {
ping := sinfo . _getPing ( )
2018-06-02 21:19:42 +00:00
ping . IsPong = isPong
2018-01-04 22:37:51 +00:00
bs := ping . encode ( )
2019-08-24 01:05:18 +00:00
payload , nonce := crypto . BoxSeal ( & sinfo . sharedPermKey , bs , nil )
2018-01-04 22:37:51 +00:00
p := wire_protoTrafficPacket {
2018-06-02 20:21:05 +00:00
Coords : sinfo . coords ,
ToKey : sinfo . theirPermPub ,
2019-08-24 01:53:00 +00:00
FromKey : sinfo . sessions . router . core . boxPub ,
2018-06-02 20:21:05 +00:00
Nonce : * nonce ,
Payload : payload ,
2018-01-04 22:37:51 +00:00
}
packet := p . encode ( )
2019-08-24 01:05:18 +00:00
// TODO rewrite the below if/when the peer struct becomes an actor, to not go through the router first
2019-08-28 00:43:54 +00:00
sinfo . sessions . router . Act ( sinfo , func ( ) { sinfo . sessions . router . out ( packet ) } )
2019-06-29 01:02:58 +00:00
if sinfo . pingTime . Before ( sinfo . time ) {
sinfo . pingTime = time . Now ( )
2018-04-22 20:31:30 +00:00
}
2017-12-29 04:16:20 +00:00
}
2019-08-25 15:36:09 +00:00
func ( sinfo * sessionInfo ) setConn ( from phony . Actor , conn * Conn ) {
2019-08-28 00:43:54 +00:00
sinfo . Act ( from , func ( ) {
2019-08-24 05:17:37 +00:00
sinfo . conn = conn
sinfo . conn . setMTU ( sinfo , sinfo . _getMTU ( ) )
} )
}
2018-06-10 23:03:28 +00:00
// Handles a session ping, creating a session if needed and calling update, then possibly responding with a pong if the ping was in ping mode and the update was successful.
// If the session has a packet cached (common when first setting up a session), it will be sent.
2017-12-29 04:16:20 +00:00
func ( ss * sessions ) handlePing ( ping * sessionPing ) {
2018-01-04 22:37:51 +00:00
// Get the corresponding session (or create a new session)
2018-06-02 21:19:42 +00:00
sinfo , isIn := ss . getByTheirPerm ( & ping . SendPermPub )
2019-08-12 23:22:30 +00:00
switch {
2019-08-24 01:05:18 +00:00
case ping . IsPong : // This is a response, not an initial ping, so ignore it.
2019-08-12 23:22:30 +00:00
case isIn : // Session already exists
case ! ss . isSessionAllowed ( & ping . SendPermPub , false ) : // Session is not allowed
default :
2019-04-19 21:57:52 +00:00
ss . listenerMutex . Lock ( )
2019-08-12 23:22:30 +00:00
if ss . listener != nil {
// This is a ping from an allowed node for which no session exists, and we have a listener ready to handle sessions.
// We need to create a session and pass it to the listener.
sinfo = ss . createSession ( & ping . SendPermPub )
if s , _ := ss . getByTheirPerm ( & ping . SendPermPub ) ; s != sinfo {
panic ( "This should not happen" )
}
2019-08-24 01:53:00 +00:00
conn := newConn ( ss . router . core , crypto . GetNodeID ( & sinfo . theirPermPub ) , & crypto . NodeID { } , sinfo )
2019-04-19 21:57:52 +00:00
for i := range conn . nodeMask {
conn . nodeMask [ i ] = 0xFF
}
2019-08-24 05:17:37 +00:00
sinfo . setConn ( ss . router , conn )
2019-08-12 23:22:30 +00:00
c := ss . listener . conn
go func ( ) { c <- conn } ( )
2019-04-19 21:57:52 +00:00
}
ss . listenerMutex . Unlock ( )
2018-01-04 22:37:51 +00:00
}
2019-08-12 23:22:30 +00:00
if sinfo != nil {
2019-08-28 00:43:54 +00:00
sinfo . Act ( ss . router , func ( ) {
2019-08-12 23:22:30 +00:00
// Update the session
2019-08-24 01:05:18 +00:00
if ! sinfo . _update ( ping ) { /*panic("Should not happen in testing")*/
2019-08-12 23:22:30 +00:00
return
}
if ! ping . IsPong {
2019-08-24 01:05:18 +00:00
sinfo . _sendPingPong ( true )
2019-08-12 23:22:30 +00:00
}
} )
}
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Get the MTU of the session.
// Will be equal to the smaller of this node's MTU or the remote node's MTU.
// If sending over links with a maximum message size (this was a thing with the old UDP code), it could be further lowered, to a minimum of 1280.
2020-01-05 17:27:54 +00:00
func ( sinfo * sessionInfo ) _getMTU ( ) MTU {
2018-05-18 17:56:33 +00:00
if sinfo . theirMTU == 0 || sinfo . myMTU == 0 {
return 0
}
2018-02-11 23:09:05 +00:00
if sinfo . theirMTU < sinfo . myMTU {
return sinfo . theirMTU
}
return sinfo . myMTU
}
2018-06-10 23:03:28 +00:00
// Checks if a packet's nonce is recent enough to fall within the window of allowed packets, and not already received.
2019-08-24 01:05:18 +00:00
func ( sinfo * sessionInfo ) _nonceIsOK ( theirNonce * crypto . BoxNonce ) bool {
2018-01-04 22:37:51 +00:00
// The bitmask is to allow for some non-duplicate out-of-order packets
2019-08-16 23:37:16 +00:00
if theirNonce . Minus ( & sinfo . theirNonce ) > 0 {
// This is newer than the newest nonce we've seen
2018-01-04 22:37:51 +00:00
return true
}
2019-08-31 22:39:05 +00:00
return time . Since ( sinfo . time ) < nonceWindow
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Updates the nonce mask by (possibly) shifting the bitmask and setting the bit corresponding to this nonce to 1, and then updating the most recent nonce
2019-08-24 01:05:18 +00:00
func ( sinfo * sessionInfo ) _updateNonce ( theirNonce * crypto . BoxNonce ) {
2019-08-16 23:37:16 +00:00
if theirNonce . Minus ( & sinfo . theirNonce ) > 0 {
// This nonce is the newest we've seen, so make a note of that
2018-06-10 23:03:28 +00:00
sinfo . theirNonce = * theirNonce
2019-08-31 22:39:05 +00:00
sinfo . time = time . Now ( )
2018-01-04 22:37:51 +00:00
}
2017-12-29 04:16:20 +00:00
}
2018-06-10 23:03:28 +00:00
// Resets all sessions to an uninitialized state.
2019-11-29 09:45:02 +00:00
// Called after coord changes, so attempts to use a session will trigger a new ping and notify the remote end of the coord change.
2019-09-18 13:03:31 +00:00
// Only call this from the router actor.
2019-08-24 01:26:15 +00:00
func ( ss * sessions ) reset ( ) {
2019-09-18 23:33:51 +00:00
for _ , _sinfo := range ss . sinfos {
sinfo := _sinfo // So we can safely put it in a closure
sinfo . Act ( ss . router , func ( ) {
sinfo . reset = true
} )
2018-01-04 22:37:51 +00:00
}
2017-12-29 04:16:20 +00:00
}
2019-08-04 02:46:18 +00:00
////////////////////////////////////////////////////////////////////////////////
//////////////////////////// Worker Functions Below ////////////////////////////
////////////////////////////////////////////////////////////////////////////////
2019-08-28 01:01:37 +00:00
type sessionCryptoManager struct {
phony . Inbox
}
func ( m * sessionCryptoManager ) workerGo ( from phony . Actor , f func ( ) ) {
m . Act ( from , func ( ) {
util . WorkerGo ( f )
} )
}
var manager = sessionCryptoManager { }
2019-08-07 00:25:55 +00:00
type FlowKeyMessage struct {
FlowKey uint64
Message [ ] byte
}
2019-08-25 15:36:09 +00:00
func ( sinfo * sessionInfo ) recv ( from phony . Actor , packet * wire_trafficPacket ) {
2019-08-28 00:43:54 +00:00
sinfo . Act ( from , func ( ) {
2019-08-24 03:23:01 +00:00
sinfo . _recvPacket ( packet )
} )
}
func ( sinfo * sessionInfo ) _recvPacket ( p * wire_trafficPacket ) {
select {
case <- sinfo . init :
default :
// TODO find a better way to drop things until initialized
util . PutBytes ( p . Payload )
return
}
2019-08-24 06:52:21 +00:00
if ! sinfo . _nonceIsOK ( & p . Nonce ) {
2019-08-24 03:23:01 +00:00
util . PutBytes ( p . Payload )
return
}
k := sinfo . sharedSesKey
var isOK bool
var bs [ ] byte
ch := make ( chan func ( ) , 1 )
poolFunc := func ( ) {
bs , isOK = crypto . BoxOpen ( & k , p . Payload , & p . Nonce )
callback := func ( ) {
util . PutBytes ( p . Payload )
if ! isOK || k != sinfo . sharedSesKey || ! sinfo . _nonceIsOK ( & p . Nonce ) {
2019-09-18 12:37:01 +00:00
// Either we failed to decrypt, or the session was updated, or we
// received this packet in the mean time
util . PutBytes ( bs )
return
}
2019-08-24 03:23:01 +00:00
sinfo . _updateNonce ( & p . Nonce )
sinfo . bytesRecvd += uint64 ( len ( bs ) )
2019-08-24 06:52:21 +00:00
sinfo . conn . recvMsg ( sinfo , bs )
2019-08-24 03:23:01 +00:00
}
ch <- callback
sinfo . checkCallbacks ( )
}
sinfo . callbacks = append ( sinfo . callbacks , ch )
2019-08-28 01:01:37 +00:00
manager . workerGo ( sinfo , poolFunc )
2019-08-24 03:23:01 +00:00
}
func ( sinfo * sessionInfo ) _send ( msg FlowKeyMessage ) {
select {
case <- sinfo . init :
default :
// TODO find a better way to drop things until initialized
util . PutBytes ( msg . Message )
return
}
sinfo . bytesSent += uint64 ( len ( msg . Message ) )
coords := append ( [ ] byte ( nil ) , sinfo . coords ... )
if msg . FlowKey != 0 {
coords = append ( coords , 0 )
coords = append ( coords , wire_encode_uint64 ( msg . FlowKey ) ... )
}
p := wire_trafficPacket {
Coords : coords ,
Handle : sinfo . theirHandle ,
Nonce : sinfo . myNonce ,
}
sinfo . myNonce . Increment ( )
k := sinfo . sharedSesKey
ch := make ( chan func ( ) , 1 )
poolFunc := func ( ) {
p . Payload , _ = crypto . BoxSeal ( & k , msg . Message , & p . Nonce )
callback := func ( ) {
// Encoding may block on a util.GetBytes(), so kept out of the worker pool
packet := p . encode ( )
// Cleanup
util . PutBytes ( msg . Message )
util . PutBytes ( p . Payload )
// Send the packet
// TODO replace this with a send to the peer struct if that becomes an actor
2019-08-28 00:43:54 +00:00
sinfo . sessions . router . Act ( sinfo , func ( ) {
2019-08-24 03:23:01 +00:00
sinfo . sessions . router . out ( packet )
} )
}
ch <- callback
sinfo . checkCallbacks ( )
}
sinfo . callbacks = append ( sinfo . callbacks , ch )
2019-08-28 01:01:37 +00:00
manager . workerGo ( sinfo , poolFunc )
2019-08-24 03:23:01 +00:00
}
func ( sinfo * sessionInfo ) checkCallbacks ( ) {
2019-08-28 00:43:54 +00:00
sinfo . Act ( nil , func ( ) {
2019-08-24 03:23:01 +00:00
if len ( sinfo . callbacks ) > 0 {
select {
case callback := <- sinfo . callbacks [ 0 ] :
sinfo . callbacks = sinfo . callbacks [ 1 : ]
callback ( )
sinfo . checkCallbacks ( )
default :
}
}
} )
}