2018-10-05 22:44:20 +00:00
|
|
|
package multiplex
|
|
|
|
|
|
|
|
import (
|
2018-10-23 19:47:58 +00:00
|
|
|
"errors"
|
2019-08-16 22:47:15 +00:00
|
|
|
"fmt"
|
2020-04-14 00:53:28 +00:00
|
|
|
"github.com/cbeuw/Cloak/internal/common"
|
2018-10-05 22:44:20 +00:00
|
|
|
"net"
|
|
|
|
"sync"
|
2018-10-23 19:47:58 +00:00
|
|
|
"sync/atomic"
|
2019-03-23 12:45:12 +00:00
|
|
|
"time"
|
2019-08-05 13:33:20 +00:00
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
2018-10-05 22:44:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2020-12-29 00:16:24 +00:00
|
|
|
acceptBacklog = 1024
|
2020-10-18 14:51:57 +00:00
|
|
|
defaultInactivityTimeout = 30 * time.Second
|
2020-12-29 00:16:24 +00:00
|
|
|
defaultMaxOnWireSize = 1<<14 + 256 // https://tools.ietf.org/html/rfc8446#section-5.2
|
2018-10-05 22:44:20 +00:00
|
|
|
)
|
|
|
|
|
2018-10-27 14:27:43 +00:00
|
|
|
var ErrBrokenSession = errors.New("broken session")
|
|
|
|
var errRepeatSessionClosing = errors.New("trying to close a closed session")
|
2020-04-20 22:54:41 +00:00
|
|
|
var errRepeatStreamClosing = errors.New("trying to close a closed stream")
|
2020-10-15 20:32:38 +00:00
|
|
|
var errNoMultiplex = errors.New("a singleplexing session can have only one stream")
|
2018-10-27 14:27:43 +00:00
|
|
|
|
2019-08-11 23:22:15 +00:00
|
|
|
type SessionConfig struct {
|
2020-04-10 15:09:05 +00:00
|
|
|
Obfuscator
|
2018-10-05 22:44:20 +00:00
|
|
|
|
2020-10-20 23:54:36 +00:00
|
|
|
// Valve is used to limit transmission rates, and record and limit usage
|
2019-08-11 23:22:15 +00:00
|
|
|
Valve
|
|
|
|
|
2019-08-11 23:48:20 +00:00
|
|
|
Unordered bool
|
2020-04-08 14:07:35 +00:00
|
|
|
|
2020-10-20 23:54:36 +00:00
|
|
|
// A Singleplexing session always has just one stream
|
2020-10-15 20:32:38 +00:00
|
|
|
Singleplex bool
|
|
|
|
|
2020-10-18 13:42:47 +00:00
|
|
|
// maximum size of an obfuscated frame, including headers and overhead
|
|
|
|
MsgOnWireSizeLimit int
|
|
|
|
|
2020-10-18 14:51:57 +00:00
|
|
|
// InactivityTimeout sets the duration a Session waits while it has no active streams before it closes itself
|
|
|
|
InactivityTimeout time.Duration
|
2019-08-11 23:22:15 +00:00
|
|
|
}
|
|
|
|
|
2020-10-21 15:37:32 +00:00
|
|
|
// A Session represents a self-contained communication chain between local and remote. It manages its streams,
|
|
|
|
// controls serialisation and encryption of data sent and received using the supplied Obfuscator, and send and receive
|
|
|
|
// data through a manged connection pool filled with underlying connections added to it.
|
2019-08-11 23:22:15 +00:00
|
|
|
type Session struct {
|
|
|
|
id uint32
|
|
|
|
|
2020-04-08 14:07:35 +00:00
|
|
|
SessionConfig
|
2019-08-02 00:01:19 +00:00
|
|
|
|
2018-10-27 22:35:46 +00:00
|
|
|
// atomic
|
2018-10-23 19:47:58 +00:00
|
|
|
nextStreamID uint32
|
2018-10-05 22:44:20 +00:00
|
|
|
|
2020-01-23 20:30:31 +00:00
|
|
|
// atomic
|
|
|
|
activeStreamCount uint32
|
2020-12-22 20:07:17 +00:00
|
|
|
|
|
|
|
streamsM sync.Mutex
|
|
|
|
streams map[uint32]*Stream
|
2020-12-25 23:16:57 +00:00
|
|
|
// For accepting new streams
|
|
|
|
acceptCh chan *Stream
|
2018-10-05 22:44:20 +00:00
|
|
|
|
2020-12-22 14:45:29 +00:00
|
|
|
// a pool of heap allocated frame objects so we don't have to allocate a new one each time we receive a frame
|
|
|
|
recvFramePool sync.Pool
|
|
|
|
|
2020-12-24 11:35:29 +00:00
|
|
|
streamObfsBufPool sync.Pool
|
|
|
|
|
2018-10-05 22:44:20 +00:00
|
|
|
// Switchboard manages all connections to remote
|
|
|
|
sb *switchboard
|
|
|
|
|
2019-10-08 22:11:16 +00:00
|
|
|
// Used for LocalAddr() and RemoteAddr() etc.
|
2019-07-25 11:17:29 +00:00
|
|
|
addrs atomic.Value
|
|
|
|
|
2019-07-28 22:27:59 +00:00
|
|
|
closed uint32
|
2019-07-24 13:25:57 +00:00
|
|
|
|
2021-01-12 20:59:30 +00:00
|
|
|
terminalMsgSetter sync.Once
|
|
|
|
terminalMsg string
|
2020-04-10 17:48:36 +00:00
|
|
|
|
2020-10-15 21:51:36 +00:00
|
|
|
// the max size passed to Write calls before it splits it into multiple frames
|
2020-10-18 13:42:47 +00:00
|
|
|
// i.e. the max size a piece of data can fit into a Frame.Payload
|
2020-10-15 21:51:36 +00:00
|
|
|
maxStreamUnitWrite int
|
2020-12-29 00:16:24 +00:00
|
|
|
// streamSendBufferSize sets the buffer size used to send data from a Stream (Stream.obfsBuf)
|
|
|
|
streamSendBufferSize int
|
|
|
|
// connReceiveBufferSize sets the buffer size used to receive data from an underlying Conn (allocated in
|
|
|
|
// switchboard.deplex)
|
|
|
|
connReceiveBufferSize int
|
2018-10-05 22:44:20 +00:00
|
|
|
}
|
|
|
|
|
2020-04-08 14:07:35 +00:00
|
|
|
func MakeSession(id uint32, config SessionConfig) *Session {
|
2018-10-05 22:44:20 +00:00
|
|
|
sesh := &Session{
|
2019-08-11 23:22:15 +00:00
|
|
|
id: id,
|
|
|
|
SessionConfig: config,
|
|
|
|
nextStreamID: 1,
|
|
|
|
acceptCh: make(chan *Stream, acceptBacklog),
|
2020-12-22 14:45:29 +00:00
|
|
|
recvFramePool: sync.Pool{New: func() interface{} { return &Frame{} }},
|
2020-12-22 20:07:17 +00:00
|
|
|
streams: map[uint32]*Stream{},
|
2018-10-05 22:44:20 +00:00
|
|
|
}
|
2019-07-25 11:17:29 +00:00
|
|
|
sesh.addrs.Store([]net.Addr{nil, nil})
|
2019-08-11 23:22:15 +00:00
|
|
|
|
|
|
|
if config.Valve == nil {
|
2020-04-08 14:13:49 +00:00
|
|
|
sesh.Valve = UNLIMITED_VALVE
|
2019-08-11 23:22:15 +00:00
|
|
|
}
|
2020-10-18 13:42:47 +00:00
|
|
|
if config.MsgOnWireSizeLimit <= 0 {
|
2020-12-29 00:16:24 +00:00
|
|
|
sesh.MsgOnWireSizeLimit = defaultMaxOnWireSize
|
2020-04-09 15:37:46 +00:00
|
|
|
}
|
2020-10-18 14:51:57 +00:00
|
|
|
if config.InactivityTimeout == 0 {
|
|
|
|
sesh.InactivityTimeout = defaultInactivityTimeout
|
|
|
|
}
|
2020-12-29 00:16:24 +00:00
|
|
|
|
|
|
|
sesh.maxStreamUnitWrite = sesh.MsgOnWireSizeLimit - frameHeaderLength - sesh.maxOverhead
|
|
|
|
sesh.streamSendBufferSize = sesh.MsgOnWireSizeLimit
|
|
|
|
sesh.connReceiveBufferSize = 20480 // for backwards compatibility
|
2019-08-11 23:48:20 +00:00
|
|
|
|
2020-12-24 11:35:29 +00:00
|
|
|
sesh.streamObfsBufPool = sync.Pool{New: func() interface{} {
|
2020-12-29 00:16:24 +00:00
|
|
|
b := make([]byte, sesh.streamSendBufferSize)
|
2020-12-24 11:35:29 +00:00
|
|
|
return &b
|
|
|
|
}}
|
|
|
|
|
2020-10-18 13:42:47 +00:00
|
|
|
sesh.sb = makeSwitchboard(sesh)
|
2020-12-05 21:38:16 +00:00
|
|
|
time.AfterFunc(sesh.InactivityTimeout, sesh.checkTimeout)
|
2018-10-05 22:44:20 +00:00
|
|
|
return sesh
|
|
|
|
}
|
|
|
|
|
2020-12-28 12:15:01 +00:00
|
|
|
func (sesh *Session) GetSessionKey() [32]byte {
|
|
|
|
return sesh.sessionKey
|
|
|
|
}
|
|
|
|
|
2020-01-23 20:30:31 +00:00
|
|
|
func (sesh *Session) streamCountIncr() uint32 {
|
|
|
|
return atomic.AddUint32(&sesh.activeStreamCount, 1)
|
|
|
|
}
|
|
|
|
func (sesh *Session) streamCountDecr() uint32 {
|
|
|
|
return atomic.AddUint32(&sesh.activeStreamCount, ^uint32(0))
|
|
|
|
}
|
|
|
|
func (sesh *Session) streamCount() uint32 {
|
|
|
|
return atomic.LoadUint32(&sesh.activeStreamCount)
|
|
|
|
}
|
|
|
|
|
2020-10-20 23:54:36 +00:00
|
|
|
// AddConnection is used to add an underlying connection to the connection pool
|
2018-10-07 17:09:45 +00:00
|
|
|
func (sesh *Session) AddConnection(conn net.Conn) {
|
2018-10-28 21:22:38 +00:00
|
|
|
sesh.sb.addConn(conn)
|
2019-07-25 11:17:29 +00:00
|
|
|
addrs := []net.Addr{conn.LocalAddr(), conn.RemoteAddr()}
|
|
|
|
sesh.addrs.Store(addrs)
|
2018-10-07 17:09:45 +00:00
|
|
|
}
|
|
|
|
|
2020-10-20 23:54:36 +00:00
|
|
|
// OpenStream is similar to net.Dial. It opens up a new stream
|
2018-10-05 22:44:20 +00:00
|
|
|
func (sesh *Session) OpenStream() (*Stream, error) {
|
2019-07-28 22:27:59 +00:00
|
|
|
if sesh.IsClosed() {
|
2018-11-07 21:16:13 +00:00
|
|
|
return nil, ErrBrokenSession
|
|
|
|
}
|
|
|
|
id := atomic.AddUint32(&sesh.nextStreamID, 1) - 1
|
|
|
|
// Because atomic.AddUint32 returns the value after incrementation
|
2020-10-15 20:32:38 +00:00
|
|
|
if sesh.Singleplex && id > 1 {
|
|
|
|
// if there are more than one streams, which shouldn't happen if we are
|
|
|
|
// singleplexing
|
|
|
|
return nil, errNoMultiplex
|
|
|
|
}
|
2020-04-12 11:51:00 +00:00
|
|
|
stream := makeStream(sesh, id)
|
2020-12-22 20:07:17 +00:00
|
|
|
sesh.streamsM.Lock()
|
|
|
|
sesh.streams[id] = stream
|
|
|
|
sesh.streamsM.Unlock()
|
2020-01-23 20:30:31 +00:00
|
|
|
sesh.streamCountIncr()
|
2019-08-06 10:19:47 +00:00
|
|
|
log.Tracef("stream %v of session %v opened", id, sesh.id)
|
2018-10-05 22:44:20 +00:00
|
|
|
return stream, nil
|
|
|
|
}
|
|
|
|
|
2020-10-20 23:54:36 +00:00
|
|
|
// Accept is similar to net.Listener's Accept(). It blocks and returns an incoming stream
|
2019-07-23 10:06:49 +00:00
|
|
|
func (sesh *Session) Accept() (net.Conn, error) {
|
2019-07-28 22:27:59 +00:00
|
|
|
if sesh.IsClosed() {
|
2018-10-27 14:27:43 +00:00
|
|
|
return nil, ErrBrokenSession
|
2018-10-23 19:47:58 +00:00
|
|
|
}
|
2019-07-28 22:27:59 +00:00
|
|
|
stream := <-sesh.acceptCh
|
|
|
|
if stream == nil {
|
|
|
|
return nil, ErrBrokenSession
|
|
|
|
}
|
2019-08-06 10:19:47 +00:00
|
|
|
log.Tracef("stream %v of session %v accepted", stream.id, sesh.id)
|
2019-07-28 22:27:59 +00:00
|
|
|
return stream, nil
|
2018-10-05 22:44:20 +00:00
|
|
|
}
|
|
|
|
|
2019-10-14 14:34:14 +00:00
|
|
|
func (sesh *Session) closeStream(s *Stream, active bool) error {
|
2020-12-25 23:16:57 +00:00
|
|
|
if !atomic.CompareAndSwapUint32(&s.closed, 0, 1) {
|
2020-04-20 22:54:41 +00:00
|
|
|
return fmt.Errorf("closing stream %v: %w", s.id, errRepeatStreamClosing)
|
2019-10-15 21:06:11 +00:00
|
|
|
}
|
2020-12-31 23:53:22 +00:00
|
|
|
_ = s.recvBuf.Close() // recvBuf.Close should not return error
|
2019-10-14 14:34:14 +00:00
|
|
|
|
|
|
|
if active {
|
2020-12-24 11:35:29 +00:00
|
|
|
tmpBuf := sesh.streamObfsBufPool.Get().(*[]byte)
|
2020-12-23 17:28:28 +00:00
|
|
|
|
2019-10-14 14:34:14 +00:00
|
|
|
// Notify remote that this stream is closed
|
2020-12-24 11:35:29 +00:00
|
|
|
common.CryptoRandRead((*tmpBuf)[:1])
|
|
|
|
padLen := int((*tmpBuf)[0]) + 1
|
|
|
|
payload := (*tmpBuf)[frameHeaderLength : padLen+frameHeaderLength]
|
2020-12-23 17:28:28 +00:00
|
|
|
common.CryptoRandRead(payload)
|
2020-04-11 23:49:49 +00:00
|
|
|
|
2020-12-23 22:33:01 +00:00
|
|
|
// must be holding s.wirtingM on entry
|
2020-12-23 17:28:28 +00:00
|
|
|
s.writingFrame.Closing = closingStream
|
|
|
|
s.writingFrame.Payload = payload
|
2020-12-22 13:16:48 +00:00
|
|
|
|
2020-12-24 11:35:29 +00:00
|
|
|
err := s.obfuscateAndSend(*tmpBuf, frameHeaderLength)
|
|
|
|
sesh.streamObfsBufPool.Put(tmpBuf)
|
2019-10-14 14:34:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-12-22 13:16:48 +00:00
|
|
|
log.Tracef("stream %v actively closed.", s.id)
|
2019-10-14 14:34:14 +00:00
|
|
|
} else {
|
|
|
|
log.Tracef("stream %v passively closed", s.id)
|
|
|
|
}
|
|
|
|
|
2020-12-04 22:27:24 +00:00
|
|
|
// We set it as nil to signify that the stream id had existed before.
|
|
|
|
// If we Delete(s.id) straight away, later on in recvDataFromRemote, it will not be able to tell
|
|
|
|
// if the frame it received was from a new stream or a dying stream whose frame arrived late
|
2020-12-22 20:07:17 +00:00
|
|
|
sesh.streamsM.Lock()
|
|
|
|
sesh.streams[s.id] = nil
|
|
|
|
sesh.streamsM.Unlock()
|
2020-01-23 20:30:31 +00:00
|
|
|
if sesh.streamCountDecr() == 0 {
|
2020-10-15 20:32:38 +00:00
|
|
|
if sesh.Singleplex {
|
|
|
|
return sesh.Close()
|
|
|
|
} else {
|
|
|
|
log.Debugf("session %v has no active stream left", sesh.id)
|
2020-12-05 21:38:16 +00:00
|
|
|
time.AfterFunc(sesh.InactivityTimeout, sesh.checkTimeout)
|
2020-10-15 20:32:38 +00:00
|
|
|
}
|
2019-03-23 12:45:12 +00:00
|
|
|
}
|
2019-10-14 14:34:14 +00:00
|
|
|
return nil
|
2018-10-05 22:44:20 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 21:12:32 +00:00
|
|
|
// recvDataFromRemote deobfuscate the frame and read the Closing field. If it is a closing frame, it writes the frame
|
|
|
|
// to the stream buffer, otherwise it fetches the desired stream instance, or creates and stores one if it's a new
|
|
|
|
// stream and then writes to the stream buffer
|
2019-08-16 22:47:15 +00:00
|
|
|
func (sesh *Session) recvDataFromRemote(data []byte) error {
|
2020-12-22 14:45:29 +00:00
|
|
|
frame := sesh.recvFramePool.Get().(*Frame)
|
|
|
|
defer sesh.recvFramePool.Put(frame)
|
|
|
|
|
2020-12-26 00:49:36 +00:00
|
|
|
err := sesh.deobfuscate(frame, data)
|
2019-08-05 13:33:20 +00:00
|
|
|
if err != nil {
|
2019-08-16 22:47:15 +00:00
|
|
|
return fmt.Errorf("Failed to decrypt a frame for session %v: %v", sesh.id, err)
|
2019-08-05 13:33:20 +00:00
|
|
|
}
|
|
|
|
|
2020-10-21 15:42:24 +00:00
|
|
|
if frame.Closing == closingSession {
|
2020-01-22 21:30:30 +00:00
|
|
|
sesh.SetTerminalMsg("Received a closing notification frame")
|
|
|
|
return sesh.passiveClose()
|
2020-01-23 20:30:31 +00:00
|
|
|
}
|
|
|
|
|
2020-12-22 20:07:17 +00:00
|
|
|
sesh.streamsM.Lock()
|
2020-12-25 23:16:57 +00:00
|
|
|
if sesh.IsClosed() {
|
|
|
|
sesh.streamsM.Unlock()
|
|
|
|
return ErrBrokenSession
|
|
|
|
}
|
2020-12-22 20:07:17 +00:00
|
|
|
existingStream, existing := sesh.streams[frame.StreamID]
|
2020-01-23 20:30:31 +00:00
|
|
|
if existing {
|
2020-12-22 20:07:17 +00:00
|
|
|
sesh.streamsM.Unlock()
|
|
|
|
if existingStream == nil {
|
2020-01-23 20:30:31 +00:00
|
|
|
// this is when the stream existed before but has since been closed. We do nothing
|
|
|
|
return nil
|
2018-11-24 00:55:26 +00:00
|
|
|
}
|
2020-12-22 20:07:17 +00:00
|
|
|
return existingStream.recvFrame(frame)
|
2020-01-23 20:30:31 +00:00
|
|
|
} else {
|
2020-12-22 20:07:17 +00:00
|
|
|
newStream := makeStream(sesh, frame.StreamID)
|
|
|
|
sesh.streams[frame.StreamID] = newStream
|
2020-12-25 23:16:57 +00:00
|
|
|
sesh.acceptCh <- newStream
|
2020-12-22 20:07:17 +00:00
|
|
|
sesh.streamsM.Unlock()
|
2020-03-15 23:56:45 +00:00
|
|
|
// new stream
|
2020-01-23 20:30:31 +00:00
|
|
|
sesh.streamCountIncr()
|
2020-12-22 19:39:13 +00:00
|
|
|
return newStream.recvFrame(frame)
|
2018-11-24 00:55:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-24 13:25:57 +00:00
|
|
|
func (sesh *Session) SetTerminalMsg(msg string) {
|
2021-01-12 20:59:30 +00:00
|
|
|
sesh.terminalMsgSetter.Do(func() {
|
|
|
|
sesh.terminalMsg = msg
|
|
|
|
})
|
2019-07-24 13:25:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sesh *Session) TerminalMsg() string {
|
2021-01-12 20:59:30 +00:00
|
|
|
return sesh.terminalMsg
|
2019-07-24 13:25:57 +00:00
|
|
|
}
|
|
|
|
|
2020-12-25 23:16:57 +00:00
|
|
|
func (sesh *Session) closeSession() error {
|
|
|
|
if !atomic.CompareAndSwapUint32(&sesh.closed, 0, 1) {
|
2019-08-19 22:23:41 +00:00
|
|
|
log.Debugf("session %v has already been closed", sesh.id)
|
|
|
|
return errRepeatSessionClosing
|
|
|
|
}
|
2019-10-14 14:34:14 +00:00
|
|
|
|
2020-12-22 20:07:17 +00:00
|
|
|
sesh.streamsM.Lock()
|
2020-12-25 23:16:57 +00:00
|
|
|
close(sesh.acceptCh)
|
2020-12-22 20:07:17 +00:00
|
|
|
for id, stream := range sesh.streams {
|
2020-12-28 01:10:24 +00:00
|
|
|
if stream != nil && atomic.CompareAndSwapUint32(&stream.closed, 0, 1) {
|
2020-12-31 23:53:22 +00:00
|
|
|
_ = stream.recvBuf.Close() // will not block
|
2020-12-28 01:10:24 +00:00
|
|
|
delete(sesh.streams, id)
|
|
|
|
sesh.streamCountDecr()
|
2020-01-23 20:30:31 +00:00
|
|
|
}
|
2020-12-22 20:07:17 +00:00
|
|
|
}
|
|
|
|
sesh.streamsM.Unlock()
|
2020-12-06 10:50:45 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sesh *Session) passiveClose() error {
|
|
|
|
log.Debugf("attempting to passively close session %v", sesh.id)
|
2020-12-25 23:16:57 +00:00
|
|
|
err := sesh.closeSession()
|
2020-12-06 10:50:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-12-25 23:16:57 +00:00
|
|
|
sesh.sb.closeAll()
|
2019-08-05 13:33:20 +00:00
|
|
|
log.Debugf("session %v closed gracefully", sesh.id)
|
2018-10-23 19:47:58 +00:00
|
|
|
return nil
|
2019-10-14 14:34:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (sesh *Session) Close() error {
|
|
|
|
log.Debugf("attempting to actively close session %v", sesh.id)
|
2020-12-25 23:16:57 +00:00
|
|
|
err := sesh.closeSession()
|
2020-12-06 10:50:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-10-14 14:34:14 +00:00
|
|
|
}
|
2020-10-15 21:51:36 +00:00
|
|
|
// we send a notice frame telling remote to close the session
|
2020-12-23 17:28:28 +00:00
|
|
|
|
2020-12-24 13:42:22 +00:00
|
|
|
buf := sesh.streamObfsBufPool.Get().(*[]byte)
|
|
|
|
common.CryptoRandRead((*buf)[:1])
|
|
|
|
padLen := int((*buf)[0]) + 1
|
|
|
|
payload := (*buf)[frameHeaderLength : padLen+frameHeaderLength]
|
2020-12-23 17:28:28 +00:00
|
|
|
common.CryptoRandRead(payload)
|
|
|
|
|
2019-10-14 14:34:14 +00:00
|
|
|
f := &Frame{
|
|
|
|
StreamID: 0xffffffff,
|
|
|
|
Seq: 0,
|
2020-10-21 15:42:24 +00:00
|
|
|
Closing: closingSession,
|
2020-12-23 17:28:28 +00:00
|
|
|
Payload: payload,
|
2019-10-14 14:34:14 +00:00
|
|
|
}
|
2020-12-26 00:49:36 +00:00
|
|
|
i, err := sesh.obfuscate(f, *buf, frameHeaderLength)
|
2019-10-14 14:34:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-12-28 01:10:24 +00:00
|
|
|
_, err = sesh.sb.send((*buf)[:i], new(net.Conn))
|
2019-10-14 14:34:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
sesh.sb.closeAll()
|
|
|
|
log.Debugf("session %v closed gracefully", sesh.id)
|
|
|
|
return nil
|
2018-10-23 19:47:58 +00:00
|
|
|
}
|
2019-01-20 12:13:29 +00:00
|
|
|
|
2019-07-28 22:27:59 +00:00
|
|
|
func (sesh *Session) IsClosed() bool {
|
|
|
|
return atomic.LoadUint32(&sesh.closed) == 1
|
2019-01-20 12:13:29 +00:00
|
|
|
}
|
2019-03-23 12:45:12 +00:00
|
|
|
|
2020-12-05 21:38:16 +00:00
|
|
|
func (sesh *Session) checkTimeout() {
|
2020-01-23 20:30:31 +00:00
|
|
|
if sesh.streamCount() == 0 && !sesh.IsClosed() {
|
2019-07-25 19:57:02 +00:00
|
|
|
sesh.SetTerminalMsg("timeout")
|
2019-03-23 12:45:12 +00:00
|
|
|
sesh.Close()
|
|
|
|
}
|
|
|
|
}
|
2019-07-23 10:06:49 +00:00
|
|
|
|
2019-07-25 11:17:29 +00:00
|
|
|
func (sesh *Session) Addr() net.Addr { return sesh.addrs.Load().([]net.Addr)[0] }
|