Protocol Basics.

This commit is contained in:
Amin.MasterkinG
2026-04-20 17:53:50 +03:30
parent 4ddbc15737
commit e4e29201c2
6 changed files with 443 additions and 46 deletions
+2 -2
View File
@@ -23,7 +23,7 @@ type Client struct {
cfg config.Config cfg config.Config
log *logger.Logger log *logger.Logger
clientSessionKey string clientSessionKey string
streams *StreamStore socksConnections *SOCKSConnectionStore
connMu sync.Mutex connMu sync.Mutex
conns map[net.Conn]struct{} conns map[net.Conn]struct{}
@@ -36,7 +36,7 @@ func New(cfg config.Config, lg *logger.Logger) *Client {
cfg: cfg, cfg: cfg,
log: lg, log: lg,
clientSessionKey: clientSessionKey, clientSessionKey: clientSessionKey,
streams: NewStreamStore(), socksConnections: NewSOCKSConnectionStore(),
conns: make(map[net.Conn]struct{}), conns: make(map[net.Conn]struct{}),
} }
} }
+16 -12
View File
@@ -13,7 +13,7 @@ import (
"time" "time"
) )
type Stream struct { type SOCKSConnection struct {
ID uint64 ID uint64
ClientSessionKey string ClientSessionKey string
CreatedAt time.Time CreatedAt time.Time
@@ -24,35 +24,39 @@ type Stream struct {
TargetAddressType byte TargetAddressType byte
InitialPayload []byte InitialPayload []byte
BufferedBytes int BufferedBytes int
NextSequence uint64
SOCKSAuthMethod byte SOCKSAuthMethod byte
SOCKSUsername string SOCKSUsername string
HandshakeDone bool HandshakeDone bool
ConnectAccepted bool ConnectAccepted bool
CloseReadSent bool
CloseWriteSent bool
ResetSent bool
} }
func (s *Stream) InitialPayloadHex() string { func (s *SOCKSConnection) InitialPayloadHex() string {
if len(s.InitialPayload) == 0 { if len(s.InitialPayload) == 0 {
return "" return ""
} }
return hex.EncodeToString(s.InitialPayload) return hex.EncodeToString(s.InitialPayload)
} }
type StreamStore struct { type SOCKSConnectionStore struct {
nextID atomic.Uint64 nextID atomic.Uint64
mu sync.RWMutex mu sync.RWMutex
items map[uint64]*Stream items map[uint64]*SOCKSConnection
} }
func NewStreamStore() *StreamStore { func NewSOCKSConnectionStore() *SOCKSConnectionStore {
return &StreamStore{ return &SOCKSConnectionStore{
items: make(map[uint64]*Stream), items: make(map[uint64]*SOCKSConnection),
} }
} }
func (s *StreamStore) New(clientSessionKey string, clientAddress string) *Stream { func (s *SOCKSConnectionStore) New(clientSessionKey string, clientAddress string) *SOCKSConnection {
id := s.nextID.Add(1) id := s.nextID.Add(1)
now := time.Now() now := time.Now()
stream := &Stream{ socksConn := &SOCKSConnection{
ID: id, ID: id,
ClientSessionKey: clientSessionKey, ClientSessionKey: clientSessionKey,
CreatedAt: now, CreatedAt: now,
@@ -61,12 +65,12 @@ func (s *StreamStore) New(clientSessionKey string, clientAddress string) *Stream
} }
s.mu.Lock() s.mu.Lock()
s.items[id] = stream s.items[id] = socksConn
s.mu.Unlock() s.mu.Unlock()
return stream return socksConn
} }
func (s *StreamStore) Delete(id uint64) { func (s *SOCKSConnectionStore) Delete(id uint64) {
s.mu.Lock() s.mu.Lock()
delete(s.items, id) delete(s.items, id)
s.mu.Unlock() s.mu.Unlock()
+32 -32
View File
@@ -46,21 +46,21 @@ func (c *Client) handleConn(ctx context.Context, conn net.Conn) {
defer c.unregisterConn(conn) defer c.unregisterConn(conn)
defer conn.Close() defer conn.Close()
stream := c.streams.New(c.clientSessionKey, conn.RemoteAddr().String()) socksConn := c.socksConnections.New(c.clientSessionKey, conn.RemoteAddr().String())
defer c.streams.Delete(stream.ID) defer c.socksConnections.Delete(socksConn.ID)
c.log.Infof( c.log.Infof(
"<green>accepted client <cyan>%s</cyan> stream=<cyan>%d</cyan> client_session_key=<cyan>%s</cyan></green>", "<green>accepted client <cyan>%s</cyan> socks_id=<cyan>%d</cyan> client_session_key=<cyan>%s</cyan></green>",
conn.RemoteAddr(), stream.ID, stream.ClientSessionKey, conn.RemoteAddr(), socksConn.ID, socksConn.ClientSessionKey,
) )
if err := c.handleSOCKS5(ctx, conn, stream); err != nil { if err := c.handleSOCKS5(ctx, conn, socksConn); err != nil {
c.log.Errorf("<red>stream=<cyan>%d</cyan> closed: <cyan>%v</cyan></red>", stream.ID, err) c.log.Errorf("<red>socks_id=<cyan>%d</cyan> closed: <cyan>%v</cyan></red>", socksConn.ID, err)
return return
} }
} }
func (c *Client) handleSOCKS5(ctx context.Context, conn net.Conn, stream *Stream) error { func (c *Client) handleSOCKS5(ctx context.Context, conn net.Conn, socksConn *SOCKSConnection) error {
version := make([]byte, 1) version := make([]byte, 1)
if _, err := io.ReadFull(conn, version); err != nil { if _, err := io.ReadFull(conn, version); err != nil {
return err return err
@@ -69,13 +69,13 @@ func (c *Client) handleSOCKS5(ctx context.Context, conn net.Conn, stream *Stream
return fmt.Errorf("<red>unsupported SOCKS version: <cyan>%d</cyan></red>", version[0]) return fmt.Errorf("<red>unsupported SOCKS version: <cyan>%d</cyan></red>", version[0])
} }
method, err := c.negotiateAuth(conn, stream) method, err := c.negotiateAuth(conn, socksConn)
if err != nil { if err != nil {
return err return err
} }
if method == socksMethodUserPass { if method == socksMethodUserPass {
if err := c.handleUserPassAuth(conn, stream); err != nil { if err := c.handleUserPassAuth(conn, socksConn); err != nil {
return err return err
} }
} }
@@ -85,26 +85,26 @@ func (c *Client) handleSOCKS5(ctx context.Context, conn net.Conn, stream *Stream
return err return err
} }
stream.TargetHost = targetHost socksConn.TargetHost = targetHost
stream.TargetPort = targetPort socksConn.TargetPort = targetPort
stream.TargetAddressType = atyp socksConn.TargetAddressType = atyp
stream.ConnectAccepted = true socksConn.ConnectAccepted = true
stream.HandshakeDone = true socksConn.HandshakeDone = true
stream.LastActivityAt = time.Now() socksConn.LastActivityAt = time.Now()
if err := writeSocksReply(conn, socksReplySuccess); err != nil { if err := writeSocksReply(conn, socksReplySuccess); err != nil {
return err return err
} }
c.log.Infof( c.log.Infof(
"<green>stream=<cyan>%d</cyan> CONNECT target=<cyan>%s:%d</cyan> auth_method=<cyan>%d</cyan> client_session_key=<cyan>%s</cyan></green>", "<green>socks_id=<cyan>%d</cyan> CONNECT target=<cyan>%s:%d</cyan> auth_method=<cyan>%d</cyan> client_session_key=<cyan>%s</cyan></green>",
stream.ID, stream.TargetHost, stream.TargetPort, stream.SOCKSAuthMethod, stream.ClientSessionKey, socksConn.ID, socksConn.TargetHost, socksConn.TargetPort, socksConn.SOCKSAuthMethod, socksConn.ClientSessionKey,
) )
return c.captureInitialPayload(ctx, conn, stream) return c.captureInitialPayload(ctx, conn, socksConn)
} }
func (c *Client) negotiateAuth(conn net.Conn, stream *Stream) (byte, error) { func (c *Client) negotiateAuth(conn net.Conn, socksConn *SOCKSConnection) (byte, error) {
countBuf := make([]byte, 1) countBuf := make([]byte, 1)
if _, err := io.ReadFull(conn, countBuf); err != nil { if _, err := io.ReadFull(conn, countBuf); err != nil {
return 0, err return 0, err
@@ -134,11 +134,11 @@ func (c *Client) negotiateAuth(conn net.Conn, stream *Stream) (byte, error) {
return 0, errors.New("no acceptable auth method") return 0, errors.New("no acceptable auth method")
} }
stream.SOCKSAuthMethod = selected socksConn.SOCKSAuthMethod = selected
return selected, nil return selected, nil
} }
func (c *Client) handleUserPassAuth(conn net.Conn, stream *Stream) error { func (c *Client) handleUserPassAuth(conn net.Conn, socksConn *SOCKSConnection) error {
header := make([]byte, 2) header := make([]byte, 2)
if _, err := io.ReadFull(conn, header); err != nil { if _, err := io.ReadFull(conn, header); err != nil {
return err return err
@@ -163,7 +163,7 @@ func (c *Client) handleUserPassAuth(conn net.Conn, stream *Stream) error {
} }
ok := string(username) == c.cfg.SOCKSUsername && string(password) == c.cfg.SOCKSPassword ok := string(username) == c.cfg.SOCKSUsername && string(password) == c.cfg.SOCKSPassword
stream.SOCKSUsername = string(username) socksConn.SOCKSUsername = string(username)
if ok { if ok {
_, err := conn.Write([]byte{socksUserPassVersion, socksAuthSuccess}) _, err := conn.Write([]byte{socksUserPassVersion, socksAuthSuccess})
return err return err
@@ -247,7 +247,7 @@ func writeSocksReply(conn net.Conn, reply byte) error {
return err return err
} }
func (c *Client) captureInitialPayload(ctx context.Context, conn net.Conn, stream *Stream) error { func (c *Client) captureInitialPayload(ctx context.Context, conn net.Conn, socksConn *SOCKSConnection) error {
peekTimeout := 2 * time.Second peekTimeout := 2 * time.Second
idleTimeout := 30 * time.Second idleTimeout := 30 * time.Second
buf := make([]byte, 32*1024) buf := make([]byte, 32*1024)
@@ -258,12 +258,12 @@ func (c *Client) captureInitialPayload(ctx context.Context, conn net.Conn, strea
n, err := conn.Read(buf) n, err := conn.Read(buf)
if err == nil && n > 0 { if err == nil && n > 0 {
stream.InitialPayload = append([]byte(nil), buf[:n]...) socksConn.InitialPayload = append([]byte(nil), buf[:n]...)
stream.BufferedBytes += n socksConn.BufferedBytes += n
stream.LastActivityAt = time.Now() socksConn.LastActivityAt = time.Now()
c.log.Debugf( c.log.Debugf(
"<green>stream=<cyan>%d</cyan> captured initial payload bytes=<cyan>%d</cyan> target=<cyan>%s</cyan> client_session_key=<cyan>%s</cyan></green>", "<green>socks_id=<cyan>%d</cyan> captured initial payload bytes=<cyan>%d</cyan> target=<cyan>%s</cyan> client_session_key=<cyan>%s</cyan></green>",
stream.ID, n, net.JoinHostPort(stream.TargetHost, strconv.Itoa(int(stream.TargetPort))), stream.ClientSessionKey, socksConn.ID, n, net.JoinHostPort(socksConn.TargetHost, strconv.Itoa(int(socksConn.TargetPort))), socksConn.ClientSessionKey,
) )
} else if ne, ok := err.(net.Error); !ok || !ne.Timeout() { } else if ne, ok := err.(net.Error); !ok || !ne.Timeout() {
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
@@ -287,11 +287,11 @@ func (c *Client) captureInitialPayload(ctx context.Context, conn net.Conn, strea
n, err := conn.Read(buf) n, err := conn.Read(buf)
if n > 0 { if n > 0 {
stream.BufferedBytes += n socksConn.BufferedBytes += n
stream.LastActivityAt = time.Now() socksConn.LastActivityAt = time.Now()
c.log.Debugf( c.log.Debugf(
"<green>stream=<cyan>%d</cyan> buffered payload chunk=<cyan>%d</cyan> total=<cyan>%d</cyan> client_session_key=<cyan>%s</cyan></green>", "<green>socks_id=<cyan>%d</cyan> buffered payload chunk=<cyan>%d</cyan> total=<cyan>%d</cyan> client_session_key=<cyan>%s</cyan></green>",
stream.ID, n, stream.BufferedBytes, stream.ClientSessionKey, socksConn.ID, n, socksConn.BufferedBytes, socksConn.ClientSessionKey,
) )
} }
+69
View File
@@ -0,0 +1,69 @@
// ==============================================================================
// MasterHttpRelayVPN
// Author: MasterkinG32
// Github: https://github.com/masterking32
// Year: 2026
// ==============================================================================
package client
import "masterhttprelayvpn/internal/protocol"
func (s *SOCKSConnection) nextSequence() uint64 {
s.NextSequence++
return s.NextSequence
}
func (s *SOCKSConnection) BuildSOCKSConnectPacket() protocol.Packet {
packet := protocol.NewPacket(s.ClientSessionKey, protocol.PacketTypeSOCKSConnect)
packet.SOCKSID = s.ID
packet.Target = &protocol.Target{
Host: s.TargetHost,
Port: s.TargetPort,
AddressType: s.TargetAddressType,
}
if len(s.InitialPayload) > 0 {
packet.Payload = append([]byte(nil), s.InitialPayload...)
}
return packet
}
func (s *SOCKSConnection) BuildSOCKSDataPacket(payload []byte, final bool) protocol.Packet {
packet := protocol.NewPacket(s.ClientSessionKey, protocol.PacketTypeSOCKSData)
packet.SOCKSID = s.ID
packet.Sequence = s.nextSequence()
packet.Final = final
if len(payload) > 0 {
packet.Payload = append([]byte(nil), payload...)
}
return packet
}
func (s *SOCKSConnection) BuildSOCKSCloseReadPacket() protocol.Packet {
s.CloseReadSent = true
packet := protocol.NewPacket(s.ClientSessionKey, protocol.PacketTypeSOCKSCloseRead)
packet.SOCKSID = s.ID
packet.Sequence = s.nextSequence()
packet.Final = true
return packet
}
func (s *SOCKSConnection) BuildSOCKSCloseWritePacket() protocol.Packet {
s.CloseWriteSent = true
packet := protocol.NewPacket(s.ClientSessionKey, protocol.PacketTypeSOCKSCloseWrite)
packet.SOCKSID = s.ID
packet.Sequence = s.nextSequence()
packet.Final = true
return packet
}
func (s *SOCKSConnection) BuildSOCKSRSTPacket() protocol.Packet {
s.ResetSent = true
packet := protocol.NewPacket(s.ClientSessionKey, protocol.PacketTypeSOCKSRST)
packet.SOCKSID = s.ID
packet.Sequence = s.nextSequence()
packet.Final = true
return packet
}
+218
View File
@@ -0,0 +1,218 @@
// ==============================================================================
// MasterHttpRelayVPN
// Author: MasterkinG32
// Github: https://github.com/masterking32
// Year: 2026
// ==============================================================================
package protocol
import (
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"net"
"strconv"
"strings"
"time"
)
const CurrentVersion = 1
var (
ErrInvalidPacketType = errors.New("invalid packet type")
ErrMissingClientKey = errors.New("missing client session key")
ErrMissingSOCKSID = errors.New("missing socks id")
ErrMissingSequence = errors.New("missing sequence number")
ErrPayloadNotAllowed = errors.New("payload not allowed for packet type")
ErrInvalidTargetPort = errors.New("invalid target port")
ErrInvalidTargetHost = errors.New("invalid target host")
ErrTargetNotAllowed = errors.New("target not allowed for packet type")
ErrMissingBatchID = errors.New("missing batch id")
ErrEmptyBatch = errors.New("empty batch")
ErrMixedClientKeyBatch = errors.New("batch contains multiple client session keys")
)
type Target struct {
Host string `json:"host"`
Port uint16 `json:"port"`
AddressType byte `json:"address_type,omitempty"`
}
func (t Target) Address() string {
if t.Host == "" || t.Port == 0 {
return ""
}
return net.JoinHostPort(t.Host, strconv.Itoa(int(t.Port)))
}
func (t Target) Validate() error {
if strings.TrimSpace(t.Host) == "" {
return ErrInvalidTargetHost
}
if t.Port == 0 {
return ErrInvalidTargetPort
}
return nil
}
type Packet struct {
Version int `json:"v"`
ClientSessionKey string `json:"client_session_key"`
SOCKSID uint64 `json:"socks_id,omitempty"`
Type PacketType `json:"type"`
Sequence uint64 `json:"seq,omitempty"`
FragmentID uint32 `json:"fragment_id,omitempty"`
TotalFragments uint32 `json:"total_fragments,omitempty"`
Final bool `json:"final,omitempty"`
CreatedAtUnixMS int64 `json:"created_at_unix_ms"`
Target *Target `json:"target,omitempty"`
Payload []byte `json:"payload,omitempty"`
}
func NewPacket(clientSessionKey string, packetType PacketType) Packet {
return Packet{
Version: CurrentVersion,
ClientSessionKey: clientSessionKey,
Type: packetType,
CreatedAtUnixMS: time.Now().UnixMilli(),
}
}
func (p Packet) Validate() error {
if p.Version <= 0 {
return fmt.Errorf("invalid packet version: %d", p.Version)
}
if !p.Type.Valid() {
return ErrInvalidPacketType
}
if strings.TrimSpace(p.ClientSessionKey) == "" {
return ErrMissingClientKey
}
if PacketTypeNeedsStreamID(p.Type) && p.SOCKSID == 0 {
return ErrMissingSOCKSID
}
if PacketTypeNeedsSequence(p.Type) && p.Sequence == 0 {
return ErrMissingSequence
}
if !PacketTypeAllowsPayload(p.Type) && len(p.Payload) > 0 {
return ErrPayloadNotAllowed
}
switch p.Type {
case PacketTypeSOCKSConnect:
if p.Target == nil {
return ErrTargetNotAllowed
}
if err := p.Target.Validate(); err != nil {
return err
}
case PacketTypeSOCKSData,
PacketTypeSOCKSDataAck,
PacketTypeSOCKSCloseRead,
PacketTypeSOCKSCloseWrite,
PacketTypeSOCKSRST,
PacketTypeSOCKSConnectAck,
PacketTypeSOCKSConnectFail,
PacketTypeSOCKSRuleSetDenied,
PacketTypeSOCKSNetworkUnreachable,
PacketTypeSOCKSHostUnreachable,
PacketTypeSOCKSConnectionRefused,
PacketTypeSOCKSTTLExpired,
PacketTypeSOCKSCommandUnsupported,
PacketTypeSOCKSAddressTypeUnsupported,
PacketTypeSOCKSAuthFailed,
PacketTypeSOCKSUpstreamUnavailable:
if p.Target != nil {
return ErrTargetNotAllowed
}
}
if p.TotalFragments > 0 && p.FragmentID >= p.TotalFragments {
return fmt.Errorf("invalid fragment range: id=%d total=%d", p.FragmentID, p.TotalFragments)
}
return nil
}
func (p Packet) IdentityKey() string {
return fmt.Sprintf("%s:%d:%s:%d:%d", p.ClientSessionKey, p.SOCKSID, p.Type, p.Sequence, p.FragmentID)
}
func PacketIdentityKey(clientSessionKey string, socksID uint64, packetType PacketType, sequence uint64, fragmentID uint32) string {
switch packetType {
case PacketTypeSOCKSData, PacketTypeSOCKSDataAck:
return fmt.Sprintf("%s:%d:%s:%d:%d", clientSessionKey, socksID, packetType, sequence, fragmentID)
case PacketTypeSOCKSCloseRead,
PacketTypeSOCKSCloseWrite,
PacketTypeSOCKSRST,
PacketTypeSOCKSConnect,
PacketTypeSOCKSConnectAck,
PacketTypeSOCKSConnectFail,
PacketTypeSOCKSRuleSetDenied,
PacketTypeSOCKSNetworkUnreachable,
PacketTypeSOCKSHostUnreachable,
PacketTypeSOCKSConnectionRefused,
PacketTypeSOCKSTTLExpired,
PacketTypeSOCKSCommandUnsupported,
PacketTypeSOCKSAddressTypeUnsupported,
PacketTypeSOCKSAuthFailed,
PacketTypeSOCKSUpstreamUnavailable:
return fmt.Sprintf("%s:%d:%s:%d", clientSessionKey, socksID, packetType, sequence)
default:
return fmt.Sprintf("%s:%d:%s", clientSessionKey, socksID, packetType)
}
}
type Batch struct {
Version int `json:"v"`
BatchID string `json:"batch_id"`
ClientSessionKey string `json:"client_session_key"`
CreatedAtUnixMS int64 `json:"created_at_unix_ms"`
Packets []Packet `json:"packets"`
}
func NewBatch(clientSessionKey string, batchID string, packets []Packet) Batch {
return Batch{
Version: CurrentVersion,
BatchID: batchID,
ClientSessionKey: clientSessionKey,
CreatedAtUnixMS: time.Now().UnixMilli(),
Packets: packets,
}
}
func NewBatchID() string {
now := time.Now().UTC().Format("20060102T150405.000000000Z")
random := make([]byte, 8)
if _, err := rand.Read(random); err != nil {
return fmt.Sprintf("%s_batch", now)
}
return fmt.Sprintf("%s_%s", now, hex.EncodeToString(random))
}
func (b Batch) Validate() error {
if b.Version <= 0 {
return fmt.Errorf("invalid batch version: %d", b.Version)
}
if strings.TrimSpace(b.BatchID) == "" {
return ErrMissingBatchID
}
if strings.TrimSpace(b.ClientSessionKey) == "" {
return ErrMissingClientKey
}
if len(b.Packets) == 0 {
return ErrEmptyBatch
}
for _, packet := range b.Packets {
if err := packet.Validate(); err != nil {
return err
}
if packet.ClientSessionKey != b.ClientSessionKey {
return ErrMixedClientKeyBatch
}
}
return nil
}
+106
View File
@@ -0,0 +1,106 @@
// ==============================================================================
// MasterHttpRelayVPN
// Author: MasterkinG32
// Github: https://github.com/masterking32
// Year: 2026
// ==============================================================================
package protocol
type PacketType string
const (
PacketTypeSOCKSConnect PacketType = "socks_connect"
PacketTypeSOCKSConnectAck PacketType = "socks_connect_ack"
PacketTypeSOCKSConnectFail PacketType = "socks_connect_fail"
PacketTypeSOCKSRuleSetDenied PacketType = "socks_ruleset_denied"
PacketTypeSOCKSNetworkUnreachable PacketType = "socks_network_unreachable"
PacketTypeSOCKSHostUnreachable PacketType = "socks_host_unreachable"
PacketTypeSOCKSConnectionRefused PacketType = "socks_connection_refused"
PacketTypeSOCKSTTLExpired PacketType = "socks_ttl_expired"
PacketTypeSOCKSCommandUnsupported PacketType = "socks_command_unsupported"
PacketTypeSOCKSAddressTypeUnsupported PacketType = "socks_address_type_unsupported"
PacketTypeSOCKSAuthFailed PacketType = "socks_auth_failed"
PacketTypeSOCKSUpstreamUnavailable PacketType = "socks_upstream_unavailable"
PacketTypeSOCKSData PacketType = "socks_data"
PacketTypeSOCKSDataAck PacketType = "socks_data_ack"
PacketTypeSOCKSCloseRead PacketType = "socks_close_read"
PacketTypeSOCKSCloseWrite PacketType = "socks_close_write"
PacketTypeSOCKSRST PacketType = "socks_rst"
PacketTypePing PacketType = "ping"
PacketTypePong PacketType = "pong"
)
func (p PacketType) String() string {
return string(p)
}
func (p PacketType) Valid() bool {
switch p {
case PacketTypeSOCKSConnect,
PacketTypeSOCKSConnectAck,
PacketTypeSOCKSConnectFail,
PacketTypeSOCKSRuleSetDenied,
PacketTypeSOCKSNetworkUnreachable,
PacketTypeSOCKSHostUnreachable,
PacketTypeSOCKSConnectionRefused,
PacketTypeSOCKSTTLExpired,
PacketTypeSOCKSCommandUnsupported,
PacketTypeSOCKSAddressTypeUnsupported,
PacketTypeSOCKSAuthFailed,
PacketTypeSOCKSUpstreamUnavailable,
PacketTypeSOCKSData,
PacketTypeSOCKSDataAck,
PacketTypeSOCKSCloseRead,
PacketTypeSOCKSCloseWrite,
PacketTypeSOCKSRST,
PacketTypePing,
PacketTypePong:
return true
default:
return false
}
}
func PacketTypeNeedsStreamID(packetType PacketType) bool {
switch packetType {
case PacketTypePing, PacketTypePong:
return false
default:
return packetType.Valid()
}
}
func PacketTypeNeedsSequence(packetType PacketType) bool {
switch packetType {
case PacketTypeSOCKSData,
PacketTypeSOCKSDataAck,
PacketTypeSOCKSCloseRead,
PacketTypeSOCKSCloseWrite,
PacketTypeSOCKSRST:
return true
default:
return false
}
}
func PacketTypeAllowsPayload(packetType PacketType) bool {
switch packetType {
case PacketTypeSOCKSConnect,
PacketTypeSOCKSConnectFail,
PacketTypeSOCKSRuleSetDenied,
PacketTypeSOCKSNetworkUnreachable,
PacketTypeSOCKSHostUnreachable,
PacketTypeSOCKSConnectionRefused,
PacketTypeSOCKSTTLExpired,
PacketTypeSOCKSCommandUnsupported,
PacketTypeSOCKSAddressTypeUnsupported,
PacketTypeSOCKSAuthFailed,
PacketTypeSOCKSUpstreamUnavailable,
PacketTypeSOCKSData,
PacketTypePing,
PacketTypePong:
return true
default:
return false
}
}