Files
MasterHttpRelayVPN/internal/client/session.go
T

245 lines
5.1 KiB
Go

// ==============================================================================
// MasterHttpRelayVPN
// Author: MasterkinG32
// Github: https://github.com/masterking32
// Year: 2026
// ==============================================================================
package client
import (
"context"
"encoding/hex"
"net"
"sync"
"sync/atomic"
"time"
)
type SOCKSConnection struct {
ID uint64
ClientSessionKey string
ChunkPolicy ChunkPolicy
CreatedAt time.Time
LastActivityAt time.Time
ClientAddress string
TargetHost string
TargetPort uint16
TargetAddressType byte
InitialPayload []byte
BufferedBytes int
NextSequence uint64
SOCKSAuthMethod byte
SOCKSUsername string
HandshakeDone bool
ConnectAccepted bool
ConnectFailure string
CloseReadSent bool
CloseWriteSent bool
ResetSent bool
LocalConn net.Conn
localWriteMu sync.Mutex
localCloseMu sync.Mutex
localReadEOF bool
localWriteEOF bool
closedC chan struct{}
closeOnce sync.Once
connectResultC chan error
queueMu sync.Mutex
OutboundQueue []*SOCKSOutboundQueueItem
QueuedBytes int
InFlight map[string]*SOCKSOutboundQueueItem
}
func (s *SOCKSConnection) InitialPayloadHex() string {
if len(s.InitialPayload) == 0 {
return ""
}
return hex.EncodeToString(s.InitialPayload)
}
type SOCKSConnectionStore struct {
nextID atomic.Uint64
mu sync.RWMutex
items map[uint64]*SOCKSConnection
}
func NewSOCKSConnectionStore() *SOCKSConnectionStore {
return &SOCKSConnectionStore{
items: make(map[uint64]*SOCKSConnection),
}
}
func (s *SOCKSConnectionStore) New(clientSessionKey string, clientAddress string, chunkPolicy ChunkPolicy) *SOCKSConnection {
id := s.nextID.Add(1)
now := time.Now()
socksConn := &SOCKSConnection{
ID: id,
ClientSessionKey: clientSessionKey,
ChunkPolicy: chunkPolicy,
CreatedAt: now,
LastActivityAt: now,
ClientAddress: clientAddress,
closedC: make(chan struct{}),
connectResultC: make(chan error, 1),
InFlight: make(map[string]*SOCKSOutboundQueueItem),
}
s.mu.Lock()
s.items[id] = socksConn
s.mu.Unlock()
return socksConn
}
func (s *SOCKSConnection) WaitForConnect(ctx context.Context, timeout time.Duration) error {
timer := time.NewTimer(timeout)
defer timer.Stop()
select {
case err := <-s.connectResultC:
return err
case <-timer.C:
return ErrSOCKSConnectTimeout
case <-ctx.Done():
return ctx.Err()
}
}
func (s *SOCKSConnection) CompleteConnect(err error) {
select {
case s.connectResultC <- err:
default:
}
}
func (s *SOCKSConnection) WriteToLocal(payload []byte) error {
s.localWriteMu.Lock()
defer s.localWriteMu.Unlock()
if s.LocalConn == nil || len(payload) == 0 {
return nil
}
_, err := s.LocalConn.Write(payload)
return err
}
func (s *SOCKSConnection) CloseLocal() error {
var err error
s.closeOnce.Do(func() {
s.localWriteMu.Lock()
defer s.localWriteMu.Unlock()
if s.LocalConn != nil {
err = s.LocalConn.Close()
}
close(s.closedC)
})
return err
}
func (s *SOCKSConnection) CloseLocalWrite() error {
s.localCloseMu.Lock()
defer s.localCloseMu.Unlock()
if s.localWriteEOF {
return nil
}
s.localWriteEOF = true
if tcpConn, ok := s.LocalConn.(*net.TCPConn); ok {
return tcpConn.CloseWrite()
}
return s.CloseLocal()
}
func (s *SOCKSConnection) CloseLocalRead() error {
s.localCloseMu.Lock()
defer s.localCloseMu.Unlock()
if s.localReadEOF {
return nil
}
s.localReadEOF = true
if tcpConn, ok := s.LocalConn.(*net.TCPConn); ok {
return tcpConn.CloseRead()
}
return nil
}
func (s *SOCKSConnection) MarkLocalReadEOF() {
s.localCloseMu.Lock()
s.localReadEOF = true
s.localCloseMu.Unlock()
}
func (s *SOCKSConnection) BothLocalSidesClosed() bool {
s.localCloseMu.Lock()
defer s.localCloseMu.Unlock()
return s.localReadEOF && s.localWriteEOF
}
func (s *SOCKSConnection) WaitUntilClosed(ctx context.Context) {
select {
case <-ctx.Done():
case <-s.closedC:
}
}
func (s *SOCKSConnection) ResetTransportState() {
s.queueMu.Lock()
for i := range s.OutboundQueue {
s.OutboundQueue[i] = nil
}
s.OutboundQueue = nil
s.QueuedBytes = 0
clear(s.InFlight)
s.queueMu.Unlock()
s.InitialPayload = nil
s.BufferedBytes = 0
}
func (s *SOCKSConnectionStore) Get(id uint64) *SOCKSConnection {
s.mu.RLock()
defer s.mu.RUnlock()
return s.items[id]
}
func (s *SOCKSConnectionStore) Delete(id uint64) {
s.mu.Lock()
item := s.items[id]
delete(s.items, id)
s.mu.Unlock()
if item != nil {
item.ResetTransportState()
_ = item.CloseLocal()
}
}
func (s *SOCKSConnectionStore) CloseAll() {
s.mu.Lock()
items := make([]*SOCKSConnection, 0, len(s.items))
for _, item := range s.items {
items = append(items, item)
}
s.items = make(map[uint64]*SOCKSConnection)
s.mu.Unlock()
for _, item := range items {
item.ResetTransportState()
_ = item.CloseLocal()
}
}
func (s *SOCKSConnectionStore) Snapshot() []*SOCKSConnection {
s.mu.RLock()
defer s.mu.RUnlock()
items := make([]*SOCKSConnection, 0, len(s.items))
for _, item := range s.items {
items = append(items, item)
}
return items
}