mirror of
https://github.com/cmz0228/hysteria-dev.git
synced 2025-06-23 12:59:57 +00:00
wip: core client & server rework
This commit is contained in:
parent
ca3de154ba
commit
e3c3088596
@ -11,6 +11,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/pmtud_fix"
|
"github.com/HyNetwork/hysteria/pkg/pmtud_fix"
|
||||||
"github.com/HyNetwork/hysteria/pkg/redirect"
|
"github.com/HyNetwork/hysteria/pkg/redirect"
|
||||||
"github.com/oschwald/geoip2-golang"
|
"github.com/oschwald/geoip2-golang"
|
||||||
@ -19,7 +21,6 @@ import (
|
|||||||
"github.com/HyNetwork/hysteria/pkg/acl"
|
"github.com/HyNetwork/hysteria/pkg/acl"
|
||||||
"github.com/HyNetwork/hysteria/pkg/core"
|
"github.com/HyNetwork/hysteria/pkg/core"
|
||||||
hyHTTP "github.com/HyNetwork/hysteria/pkg/http"
|
hyHTTP "github.com/HyNetwork/hysteria/pkg/http"
|
||||||
"github.com/HyNetwork/hysteria/pkg/obfs"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/relay"
|
"github.com/HyNetwork/hysteria/pkg/relay"
|
||||||
"github.com/HyNetwork/hysteria/pkg/socks5"
|
"github.com/HyNetwork/hysteria/pkg/socks5"
|
||||||
"github.com/HyNetwork/hysteria/pkg/tproxy"
|
"github.com/HyNetwork/hysteria/pkg/tproxy"
|
||||||
@ -28,6 +29,14 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var clientPacketConnFuncFactoryMap = map[string]pktconns.ClientPacketConnFuncFactory{
|
||||||
|
"": pktconns.NewClientUDPConnFunc,
|
||||||
|
"udp": pktconns.NewClientUDPConnFunc,
|
||||||
|
"wechat": pktconns.NewClientWeChatConnFunc,
|
||||||
|
"wechat-video": pktconns.NewClientWeChatConnFunc,
|
||||||
|
"faketcp": pktconns.NewClientFakeTCPConnFunc,
|
||||||
|
}
|
||||||
|
|
||||||
func client(config *clientConfig) {
|
func client(config *clientConfig) {
|
||||||
logrus.WithField("config", config.String()).Info("Client configuration loaded")
|
logrus.WithField("config", config.String()).Info("Client configuration loaded")
|
||||||
// Resolver
|
// Resolver
|
||||||
@ -102,11 +111,14 @@ func client(config *clientConfig) {
|
|||||||
} else {
|
} else {
|
||||||
auth = []byte(config.AuthString)
|
auth = []byte(config.AuthString)
|
||||||
}
|
}
|
||||||
// Obfuscator
|
// Packet conn
|
||||||
var obfuscator obfs.Obfuscator
|
pktConnFuncFactory := clientPacketConnFuncFactoryMap[config.Protocol]
|
||||||
if len(config.Obfs) > 0 {
|
if pktConnFuncFactory == nil {
|
||||||
obfuscator = obfs.NewXPlusObfuscator([]byte(config.Obfs))
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"protocol": config.Protocol,
|
||||||
|
}).Fatal("Unsupported protocol")
|
||||||
}
|
}
|
||||||
|
pktConnFunc := pktConnFuncFactory(config.Obfs)
|
||||||
// Resolve preference
|
// Resolve preference
|
||||||
if len(config.ResolvePreference) > 0 {
|
if len(config.ResolvePreference) > 0 {
|
||||||
pref, err := transport.ResolvePreferenceFromString(config.ResolvePreference)
|
pref, err := transport.ResolvePreferenceFromString(config.ResolvePreference)
|
||||||
@ -142,8 +154,7 @@ func client(config *clientConfig) {
|
|||||||
up, down, _ := config.Speed()
|
up, down, _ := config.Speed()
|
||||||
for {
|
for {
|
||||||
try += 1
|
try += 1
|
||||||
c, err := core.NewClient(config.Server, config.Protocol, auth, tlsConfig, quicConfig,
|
c, err := core.NewClient(config.Server, auth, tlsConfig, quicConfig, pktConnFunc, up, down,
|
||||||
transport.DefaultClientTransport, up, down, obfuscator,
|
|
||||||
func(err error) {
|
func(err error) {
|
||||||
if config.QuitOnDisconnect {
|
if config.QuitOnDisconnect {
|
||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
@ -154,7 +165,7 @@ func client(config *clientConfig) {
|
|||||||
logrus.WithFields(logrus.Fields{
|
logrus.WithFields(logrus.Fields{
|
||||||
"addr": config.Server,
|
"addr": config.Server,
|
||||||
"error": err,
|
"error": err,
|
||||||
}).Info("Connection to server lost, reconnecting...")
|
}).Error("Connection to server lost, reconnecting...")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,10 +8,11 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/cmd/auth"
|
"github.com/HyNetwork/hysteria/cmd/auth"
|
||||||
"github.com/HyNetwork/hysteria/pkg/acl"
|
"github.com/HyNetwork/hysteria/pkg/acl"
|
||||||
"github.com/HyNetwork/hysteria/pkg/core"
|
"github.com/HyNetwork/hysteria/pkg/core"
|
||||||
"github.com/HyNetwork/hysteria/pkg/obfs"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/pmtud_fix"
|
"github.com/HyNetwork/hysteria/pkg/pmtud_fix"
|
||||||
"github.com/HyNetwork/hysteria/pkg/sockopt"
|
"github.com/HyNetwork/hysteria/pkg/sockopt"
|
||||||
"github.com/HyNetwork/hysteria/pkg/transport"
|
"github.com/HyNetwork/hysteria/pkg/transport"
|
||||||
@ -23,6 +24,14 @@ import (
|
|||||||
"github.com/yosuke-furukawa/json5/encoding/json5"
|
"github.com/yosuke-furukawa/json5/encoding/json5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var serverPacketConnFuncFactoryMap = map[string]pktconns.ServerPacketConnFuncFactory{
|
||||||
|
"": pktconns.NewServerUDPConnFunc,
|
||||||
|
"udp": pktconns.NewServerUDPConnFunc,
|
||||||
|
"wechat": pktconns.NewServerWeChatConnFunc,
|
||||||
|
"wechat-video": pktconns.NewServerWeChatConnFunc,
|
||||||
|
"faketcp": pktconns.NewServerFakeTCPConnFunc,
|
||||||
|
}
|
||||||
|
|
||||||
func server(config *serverConfig) {
|
func server(config *serverConfig) {
|
||||||
logrus.WithField("config", config.String()).Info("Server configuration loaded")
|
logrus.WithField("config", config.String()).Info("Server configuration loaded")
|
||||||
// Resolver
|
// Resolver
|
||||||
@ -141,11 +150,12 @@ func server(config *serverConfig) {
|
|||||||
}
|
}
|
||||||
return ok, msg
|
return ok, msg
|
||||||
}
|
}
|
||||||
// Obfuscator
|
// Packet conn
|
||||||
var obfuscator obfs.Obfuscator
|
pktConnFuncFactory := serverPacketConnFuncFactoryMap[config.Protocol]
|
||||||
if len(config.Obfs) > 0 {
|
if pktConnFuncFactory == nil {
|
||||||
obfuscator = obfs.NewXPlusObfuscator([]byte(config.Obfs))
|
logrus.WithField("protocol", config.Protocol).Fatal("Unsupported protocol")
|
||||||
}
|
}
|
||||||
|
pktConnFunc := pktConnFuncFactory(config.Obfs)
|
||||||
// Resolve preference
|
// Resolve preference
|
||||||
if len(config.ResolvePreference) > 0 {
|
if len(config.ResolvePreference) > 0 {
|
||||||
pref, err := transport.ResolvePreferenceFromString(config.ResolvePreference)
|
pref, err := transport.ResolvePreferenceFromString(config.ResolvePreference)
|
||||||
@ -221,9 +231,9 @@ func server(config *serverConfig) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
up, down, _ := config.Speed()
|
up, down, _ := config.Speed()
|
||||||
server, err := core.NewServer(config.Listen, config.Protocol, tlsConfig, quicConfig, transport.DefaultServerTransport,
|
server, err := core.NewServer(config.Listen, tlsConfig, quicConfig, pktConnFunc,
|
||||||
up, down, config.DisableUDP, aclEngine, obfuscator, connectFunc, disconnectFunc,
|
transport.DefaultServerTransport, up, down, config.DisableUDP, aclEngine,
|
||||||
tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, promReg)
|
connectFunc, disconnectFunc, tcpRequestFunc, tcpErrorFunc, udpRequestFunc, udpErrorFunc, promReg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithField("error", err).Fatal("Failed to initialize server")
|
logrus.WithField("error", err).Fatal("Failed to initialize server")
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/congestion"
|
"github.com/HyNetwork/hysteria/pkg/congestion"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/obfs"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/pmtud_fix"
|
"github.com/HyNetwork/hysteria/pkg/pmtud_fix"
|
||||||
"github.com/HyNetwork/hysteria/pkg/transport"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/utils"
|
"github.com/HyNetwork/hysteria/pkg/utils"
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
"github.com/lunixbochs/struc"
|
"github.com/lunixbochs/struc"
|
||||||
@ -25,18 +25,18 @@ import (
|
|||||||
var ErrClosed = errors.New("closed")
|
var ErrClosed = errors.New("closed")
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
transport *transport.ClientTransport
|
|
||||||
serverAddr string
|
serverAddr string
|
||||||
protocol string
|
|
||||||
sendBPS, recvBPS uint64
|
sendBPS, recvBPS uint64
|
||||||
auth []byte
|
auth []byte
|
||||||
obfuscator obfs.Obfuscator
|
|
||||||
|
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
quicConfig *quic.Config
|
quicConfig *quic.Config
|
||||||
|
|
||||||
quicSession quic.Connection
|
pktConnFunc pktconns.ClientPacketConnFunc
|
||||||
|
|
||||||
reconnectMutex sync.Mutex
|
reconnectMutex sync.Mutex
|
||||||
|
pktConn net.PacketConn
|
||||||
|
quicConn quic.Connection
|
||||||
closed bool
|
closed bool
|
||||||
|
|
||||||
udpSessionMutex sync.RWMutex
|
udpSessionMutex sync.RWMutex
|
||||||
@ -46,59 +46,78 @@ type Client struct {
|
|||||||
quicReconnectFunc func(err error)
|
quicReconnectFunc func(err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config,
|
func NewClient(serverAddr string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config,
|
||||||
transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, obfuscator obfs.Obfuscator,
|
pktConnFunc pktconns.ClientPacketConnFunc, sendBPS uint64, recvBPS uint64, quicReconnectFunc func(err error),
|
||||||
quicReconnectFunc func(err error),
|
|
||||||
) (*Client, error) {
|
) (*Client, error) {
|
||||||
quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery
|
quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery
|
||||||
c := &Client{
|
c := &Client{
|
||||||
transport: transport,
|
|
||||||
serverAddr: serverAddr,
|
serverAddr: serverAddr,
|
||||||
protocol: protocol,
|
|
||||||
sendBPS: sendBPS,
|
sendBPS: sendBPS,
|
||||||
recvBPS: recvBPS,
|
recvBPS: recvBPS,
|
||||||
auth: auth,
|
auth: auth,
|
||||||
obfuscator: obfuscator,
|
|
||||||
tlsConfig: tlsConfig,
|
tlsConfig: tlsConfig,
|
||||||
quicConfig: quicConfig,
|
quicConfig: quicConfig,
|
||||||
|
pktConnFunc: pktConnFunc,
|
||||||
quicReconnectFunc: quicReconnectFunc,
|
quicReconnectFunc: quicReconnectFunc,
|
||||||
}
|
}
|
||||||
if err := c.connectToServer(); err != nil {
|
if err := c.connect(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) connectToServer() error {
|
func (c *Client) connect() error {
|
||||||
qs, err := c.transport.QUICDial(c.protocol, c.serverAddr, c.tlsConfig, c.quicConfig, c.obfuscator)
|
// Clear previous connection
|
||||||
|
if c.quicConn != nil {
|
||||||
|
_ = c.quicConn.CloseWithError(0, "")
|
||||||
|
}
|
||||||
|
if c.pktConn != nil {
|
||||||
|
_ = c.pktConn.Close()
|
||||||
|
}
|
||||||
|
// New connection
|
||||||
|
pktConn, err := c.pktConnFunc(c.serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
serverUDPAddr, err := net.ResolveUDPAddr("udp", c.serverAddr)
|
||||||
|
if err != nil {
|
||||||
|
_ = pktConn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
quicConn, err := quic.Dial(pktConn, serverUDPAddr, c.serverAddr, c.tlsConfig, c.quicConfig)
|
||||||
|
if err != nil {
|
||||||
|
_ = pktConn.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Control stream
|
// Control stream
|
||||||
ctx, ctxCancel := context.WithTimeout(context.Background(), protocolTimeout)
|
ctx, ctxCancel := context.WithTimeout(context.Background(), protocolTimeout)
|
||||||
stream, err := qs.OpenStreamSync(ctx)
|
stream, err := quicConn.OpenStreamSync(ctx)
|
||||||
ctxCancel()
|
ctxCancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = qs.CloseWithError(closeErrorCodeProtocol, "protocol error")
|
_ = quicConn.CloseWithError(closeErrorCodeProtocol, "protocol error")
|
||||||
|
_ = pktConn.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ok, msg, err := c.handleControlStream(qs, stream)
|
ok, msg, err := c.handleControlStream(quicConn, stream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = qs.CloseWithError(closeErrorCodeProtocol, "protocol error")
|
_ = quicConn.CloseWithError(closeErrorCodeProtocol, "protocol error")
|
||||||
|
_ = pktConn.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
_ = qs.CloseWithError(closeErrorCodeAuth, "auth error")
|
_ = quicConn.CloseWithError(closeErrorCodeAuth, "auth error")
|
||||||
|
_ = pktConn.Close()
|
||||||
return fmt.Errorf("auth error: %s", msg)
|
return fmt.Errorf("auth error: %s", msg)
|
||||||
}
|
}
|
||||||
// All good
|
// All good
|
||||||
c.udpSessionMap = make(map[uint32]chan *udpMessage)
|
c.udpSessionMap = make(map[uint32]chan *udpMessage)
|
||||||
go c.handleMessage(qs)
|
go c.handleMessage(quicConn)
|
||||||
c.quicSession = qs
|
c.pktConn = pktConn
|
||||||
|
c.quicConn = quicConn
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) handleControlStream(qs quic.Connection, stream quic.Stream) (bool, string, error) {
|
func (c *Client) handleControlStream(qc quic.Connection, stream quic.Stream) (bool, string, error) {
|
||||||
// Send protocol version
|
// Send protocol version
|
||||||
_, err := stream.Write([]byte{protocolVersion})
|
_, err := stream.Write([]byte{protocolVersion})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -123,14 +142,14 @@ func (c *Client) handleControlStream(qs quic.Connection, stream quic.Stream) (bo
|
|||||||
}
|
}
|
||||||
// Set the congestion accordingly
|
// Set the congestion accordingly
|
||||||
if sh.OK {
|
if sh.OK {
|
||||||
qs.SetCongestionControl(congestion.NewBrutalSender(sh.Rate.RecvBPS))
|
qc.SetCongestionControl(congestion.NewBrutalSender(sh.Rate.RecvBPS))
|
||||||
}
|
}
|
||||||
return sh.OK, sh.Message, nil
|
return sh.OK, sh.Message, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) handleMessage(qs quic.Connection) {
|
func (c *Client) handleMessage(qc quic.Connection) {
|
||||||
for {
|
for {
|
||||||
msg, err := qs.ReceiveMessage()
|
msg, err := qc.ReceiveMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -163,10 +182,10 @@ func (c *Client) openStreamWithReconnect() (quic.Connection, quic.Stream, error)
|
|||||||
if c.closed {
|
if c.closed {
|
||||||
return nil, nil, ErrClosed
|
return nil, nil, ErrClosed
|
||||||
}
|
}
|
||||||
stream, err := c.quicSession.OpenStream()
|
stream, err := c.quicConn.OpenStream()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// All good
|
// All good
|
||||||
return c.quicSession, &wrappedQUICStream{stream}, nil
|
return c.quicConn, &wrappedQUICStream{stream}, nil
|
||||||
}
|
}
|
||||||
// Something is wrong
|
// Something is wrong
|
||||||
if nErr, ok := err.(net.Error); ok && nErr.Temporary() {
|
if nErr, ok := err.(net.Error); ok && nErr.Temporary() {
|
||||||
@ -175,13 +194,13 @@ func (c *Client) openStreamWithReconnect() (quic.Connection, quic.Stream, error)
|
|||||||
}
|
}
|
||||||
c.quicReconnectFunc(err)
|
c.quicReconnectFunc(err)
|
||||||
// Permanent error, need to reconnect
|
// Permanent error, need to reconnect
|
||||||
if err := c.connectToServer(); err != nil {
|
if err := c.connect(); err != nil {
|
||||||
// Still error, oops
|
// Still error, oops
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// We are not going to try again even if it still fails the second time
|
// We are not going to try again even if it still fails the second time
|
||||||
stream, err = c.quicSession.OpenStream()
|
stream, err = c.quicConn.OpenStream()
|
||||||
return c.quicSession, &wrappedQUICStream{stream}, err
|
return c.quicConn, &wrappedQUICStream{stream}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DialTCP(addr string) (net.Conn, error) {
|
func (c *Client) DialTCP(addr string) (net.Conn, error) {
|
||||||
@ -250,7 +269,7 @@ func (c *Client) DialUDP() (UDPConn, error) {
|
|||||||
c.udpSessionMutex.Lock()
|
c.udpSessionMutex.Lock()
|
||||||
nCh := make(chan *udpMessage, 1024)
|
nCh := make(chan *udpMessage, 1024)
|
||||||
// Store the current session map for CloseFunc below
|
// Store the current session map for CloseFunc below
|
||||||
// to ensures that we are adding and removing sessions on the same map,
|
// to ensure that we are adding and removing sessions on the same map,
|
||||||
// as reconnecting will reassign the map
|
// as reconnecting will reassign the map
|
||||||
sessionMap := c.udpSessionMap
|
sessionMap := c.udpSessionMap
|
||||||
sessionMap[sr.UDPSessionID] = nCh
|
sessionMap[sr.UDPSessionID] = nCh
|
||||||
@ -277,7 +296,8 @@ func (c *Client) DialUDP() (UDPConn, error) {
|
|||||||
func (c *Client) Close() error {
|
func (c *Client) Close() error {
|
||||||
c.reconnectMutex.Lock()
|
c.reconnectMutex.Lock()
|
||||||
defer c.reconnectMutex.Unlock()
|
defer c.reconnectMutex.Unlock()
|
||||||
err := c.quicSession.CloseWithError(closeErrorCodeGeneric, "")
|
err := c.quicConn.CloseWithError(closeErrorCodeGeneric, "")
|
||||||
|
_ = c.pktConn.Close()
|
||||||
c.closed = true
|
c.closed = true
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/congestion"
|
"github.com/HyNetwork/hysteria/pkg/congestion"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/acl"
|
"github.com/HyNetwork/hysteria/pkg/acl"
|
||||||
"github.com/HyNetwork/hysteria/pkg/obfs"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/pmtud_fix"
|
"github.com/HyNetwork/hysteria/pkg/pmtud_fix"
|
||||||
"github.com/HyNetwork/hysteria/pkg/transport"
|
"github.com/HyNetwork/hysteria/pkg/transport"
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
@ -43,21 +44,29 @@ type Server struct {
|
|||||||
upCounterVec, downCounterVec *prometheus.CounterVec
|
upCounterVec, downCounterVec *prometheus.CounterVec
|
||||||
connGaugeVec *prometheus.GaugeVec
|
connGaugeVec *prometheus.GaugeVec
|
||||||
|
|
||||||
|
pktConn net.PacketConn
|
||||||
listener quic.Listener
|
listener quic.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(addr string, protocol string, tlsConfig *tls.Config, quicConfig *quic.Config, transport *transport.ServerTransport,
|
func NewServer(addr string, tlsConfig *tls.Config, quicConfig *quic.Config,
|
||||||
|
pktConnFunc pktconns.ServerPacketConnFunc, transport *transport.ServerTransport,
|
||||||
sendBPS uint64, recvBPS uint64, disableUDP bool, aclEngine *acl.Engine,
|
sendBPS uint64, recvBPS uint64, disableUDP bool, aclEngine *acl.Engine,
|
||||||
obfuscator obfs.Obfuscator, connectFunc ConnectFunc, disconnectFunc DisconnectFunc,
|
connectFunc ConnectFunc, disconnectFunc DisconnectFunc,
|
||||||
tcpRequestFunc TCPRequestFunc, tcpErrorFunc TCPErrorFunc,
|
tcpRequestFunc TCPRequestFunc, tcpErrorFunc TCPErrorFunc,
|
||||||
udpRequestFunc UDPRequestFunc, udpErrorFunc UDPErrorFunc, promRegistry *prometheus.Registry,
|
udpRequestFunc UDPRequestFunc, udpErrorFunc UDPErrorFunc, promRegistry *prometheus.Registry,
|
||||||
) (*Server, error) {
|
) (*Server, error) {
|
||||||
quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery
|
quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery
|
||||||
listener, err := transport.QUICListen(protocol, addr, tlsConfig, quicConfig, obfuscator)
|
pktConn, err := pktConnFunc(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
listener, err := quic.Listen(pktConn, tlsConfig, quicConfig)
|
||||||
|
if err != nil {
|
||||||
|
_ = pktConn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
s := &Server{
|
s := &Server{
|
||||||
|
pktConn: pktConn,
|
||||||
listener: listener,
|
listener: listener,
|
||||||
transport: transport,
|
transport: transport,
|
||||||
sendBPS: sendBPS,
|
sendBPS: sendBPS,
|
||||||
@ -97,7 +106,9 @@ func (s *Server) Serve() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Close() error {
|
func (s *Server) Close() error {
|
||||||
return s.listener.Close()
|
err := s.listener.Close()
|
||||||
|
_ = s.pktConn.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleClient(cs quic.Connection) {
|
func (s *Server) handleClient(cs quic.Connection) {
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
type DummyObfuscator struct{}
|
|
||||||
|
|
||||||
func NewDummyObfuscator() *DummyObfuscator {
|
|
||||||
return &DummyObfuscator{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *DummyObfuscator) Deobfuscate(in []byte, out []byte) int {
|
|
||||||
if len(out) < len(in) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return copy(out, in)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *DummyObfuscator) Obfuscate(in []byte, out []byte) int {
|
|
||||||
return copy(out, in)
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
type Obfuscator interface {
|
|
||||||
Deobfuscate(in []byte, out []byte) int
|
|
||||||
Obfuscate(in []byte, out []byte) int
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"math/rand"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// [salt][obfuscated payload]
|
|
||||||
|
|
||||||
const saltLen = 16
|
|
||||||
|
|
||||||
type XPlusObfuscator struct {
|
|
||||||
Key []byte
|
|
||||||
RandSrc *rand.Rand
|
|
||||||
|
|
||||||
lk sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewXPlusObfuscator(key []byte) *XPlusObfuscator {
|
|
||||||
return &XPlusObfuscator{
|
|
||||||
Key: key,
|
|
||||||
RandSrc: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int {
|
|
||||||
pLen := len(in) - saltLen
|
|
||||||
if pLen <= 0 || len(out) < pLen {
|
|
||||||
// Invalid
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
key := sha256.Sum256(append(x.Key, in[:saltLen]...))
|
|
||||||
// Deobfuscate the payload
|
|
||||||
for i, c := range in[saltLen:] {
|
|
||||||
out[i] = c ^ key[i%sha256.Size]
|
|
||||||
}
|
|
||||||
return pLen
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int {
|
|
||||||
x.lk.Lock()
|
|
||||||
_, _ = x.RandSrc.Read(out[:saltLen]) // salt
|
|
||||||
x.lk.Unlock()
|
|
||||||
// Obfuscate the payload
|
|
||||||
key := sha256.Sum256(append(x.Key, out[:saltLen]...))
|
|
||||||
for i, c := range in {
|
|
||||||
out[i+saltLen] = c ^ key[i%sha256.Size]
|
|
||||||
}
|
|
||||||
return len(in) + saltLen
|
|
||||||
}
|
|
@ -1,16 +1,8 @@
|
|||||||
package transport
|
package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/conns/faketcp"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/conns/udp"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/conns/wechat"
|
|
||||||
obfsPkg "github.com/HyNetwork/hysteria/pkg/obfs"
|
|
||||||
"github.com/lucas-clemente/quic-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClientTransport struct {
|
type ClientTransport struct {
|
||||||
@ -25,61 +17,6 @@ var DefaultClientTransport = &ClientTransport{
|
|||||||
ResolvePreference: ResolvePreferenceDefault,
|
ResolvePreference: ResolvePreferenceDefault,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfsPkg.Obfuscator) (net.PacketConn, error) {
|
|
||||||
if len(proto) == 0 || proto == "udp" {
|
|
||||||
conn, err := net.ListenUDP("udp", nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if obfs != nil {
|
|
||||||
oc := udp.NewObfsUDPConn(conn, obfs)
|
|
||||||
return oc, nil
|
|
||||||
} else {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
} else if proto == "wechat-video" {
|
|
||||||
conn, err := net.ListenUDP("udp", nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if obfs == nil {
|
|
||||||
obfs = obfsPkg.NewDummyObfuscator()
|
|
||||||
}
|
|
||||||
return wechat.NewObfsWeChatUDPConn(conn, obfs), nil
|
|
||||||
} else if proto == "faketcp" {
|
|
||||||
var conn *faketcp.TCPConn
|
|
||||||
conn, err := faketcp.Dial("tcp", server)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if obfs != nil {
|
|
||||||
oc := faketcp.NewObfsFakeTCPConn(conn, obfs)
|
|
||||||
return oc, nil
|
|
||||||
} else {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("unsupported protocol: %s", proto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator) (quic.Connection, error) {
|
|
||||||
serverUDPAddr, err := net.ResolveUDPAddr("udp", server)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pktConn, err := ct.quicPacketConn(proto, server, obfs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
qs, err := quic.Dial(pktConn, serverUDPAddr, server, tlsConfig, quicConfig)
|
|
||||||
if err != nil {
|
|
||||||
_ = pktConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return qs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ct *ClientTransport) ResolveIPAddr(address string) (*net.IPAddr, error) {
|
func (ct *ClientTransport) ResolveIPAddr(address string) (*net.IPAddr, error) {
|
||||||
return resolveIPAddrWithPreference(address, ct.ResolvePreference)
|
return resolveIPAddrWithPreference(address, ct.ResolvePreference)
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,12 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/obfs"
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns/obfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const udpBufferSize = 65535
|
const udpBufferSize = 65535
|
||||||
|
|
||||||
type ObfsFakeTCPConn struct {
|
type ObfsFakeTCPPacketConn struct {
|
||||||
orig *TCPConn
|
orig *TCPConn
|
||||||
obfs obfs.Obfuscator
|
obfs obfs.Obfuscator
|
||||||
|
|
||||||
@ -21,8 +21,8 @@ type ObfsFakeTCPConn struct {
|
|||||||
writeMutex sync.Mutex
|
writeMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewObfsFakeTCPConn(orig *TCPConn, obfs obfs.Obfuscator) *ObfsFakeTCPConn {
|
func NewObfsFakeTCPConn(orig *TCPConn, obfs obfs.Obfuscator) *ObfsFakeTCPPacketConn {
|
||||||
return &ObfsFakeTCPConn{
|
return &ObfsFakeTCPPacketConn{
|
||||||
orig: orig,
|
orig: orig,
|
||||||
obfs: obfs,
|
obfs: obfs,
|
||||||
readBuf: make([]byte, udpBufferSize),
|
readBuf: make([]byte, udpBufferSize),
|
||||||
@ -30,7 +30,7 @@ func NewObfsFakeTCPConn(orig *TCPConn, obfs obfs.Obfuscator) *ObfsFakeTCPConn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
func (c *ObfsFakeTCPPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
||||||
for {
|
for {
|
||||||
c.readMutex.Lock()
|
c.readMutex.Lock()
|
||||||
n, addr, err := c.orig.ReadFrom(c.readBuf)
|
n, addr, err := c.orig.ReadFrom(c.readBuf)
|
||||||
@ -50,7 +50,7 @@ func (c *ObfsFakeTCPConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
func (c *ObfsFakeTCPPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
c.writeMutex.Lock()
|
c.writeMutex.Lock()
|
||||||
bn := c.obfs.Obfuscate(p, c.writeBuf)
|
bn := c.obfs.Obfuscate(p, c.writeBuf)
|
||||||
_, err = c.orig.WriteTo(c.writeBuf[:bn], addr)
|
_, err = c.orig.WriteTo(c.writeBuf[:bn], addr)
|
||||||
@ -62,34 +62,34 @@ func (c *ObfsFakeTCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) Close() error {
|
func (c *ObfsFakeTCPPacketConn) Close() error {
|
||||||
return c.orig.Close()
|
return c.orig.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) LocalAddr() net.Addr {
|
func (c *ObfsFakeTCPPacketConn) LocalAddr() net.Addr {
|
||||||
return c.orig.LocalAddr()
|
return c.orig.LocalAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) SetDeadline(t time.Time) error {
|
func (c *ObfsFakeTCPPacketConn) SetDeadline(t time.Time) error {
|
||||||
return c.orig.SetDeadline(t)
|
return c.orig.SetDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) SetReadDeadline(t time.Time) error {
|
func (c *ObfsFakeTCPPacketConn) SetReadDeadline(t time.Time) error {
|
||||||
return c.orig.SetReadDeadline(t)
|
return c.orig.SetReadDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) SetWriteDeadline(t time.Time) error {
|
func (c *ObfsFakeTCPPacketConn) SetWriteDeadline(t time.Time) error {
|
||||||
return c.orig.SetWriteDeadline(t)
|
return c.orig.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) SetReadBuffer(bytes int) error {
|
func (c *ObfsFakeTCPPacketConn) SetReadBuffer(bytes int) error {
|
||||||
return c.orig.SetReadBuffer(bytes)
|
return c.orig.SetReadBuffer(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) SetWriteBuffer(bytes int) error {
|
func (c *ObfsFakeTCPPacketConn) SetWriteBuffer(bytes int) error {
|
||||||
return c.orig.SetWriteBuffer(bytes)
|
return c.orig.SetWriteBuffer(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsFakeTCPConn) SyscallConn() (syscall.RawConn, error) {
|
func (c *ObfsFakeTCPPacketConn) SyscallConn() (syscall.RawConn, error) {
|
||||||
return c.orig.SyscallConn()
|
return c.orig.SyscallConn()
|
||||||
}
|
}
|
146
pkg/transport/pktconns/func.go
Normal file
146
pkg/transport/pktconns/func.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package pktconns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns/faketcp"
|
||||||
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns/obfs"
|
||||||
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns/udp"
|
||||||
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns/wechat"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
ClientPacketConnFunc func(server string) (net.PacketConn, error)
|
||||||
|
ServerPacketConnFunc func(listen string) (net.PacketConn, error)
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
ClientPacketConnFuncFactory func(obfsPassword string) ClientPacketConnFunc
|
||||||
|
ServerPacketConnFuncFactory func(obfsPassword string) ServerPacketConnFunc
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewClientUDPConnFunc(obfsPassword string) ClientPacketConnFunc {
|
||||||
|
if obfsPassword == "" {
|
||||||
|
return func(server string) (net.PacketConn, error) {
|
||||||
|
return net.ListenUDP("udp", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return func(server string) (net.PacketConn, error) {
|
||||||
|
obfs := obfs.NewXPlusObfuscator([]byte(obfsPassword))
|
||||||
|
udpConn, err := net.ListenUDP("udp", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return udp.NewObfsUDPConn(udpConn, obfs), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientWeChatConnFunc(obfsPassword string) ClientPacketConnFunc {
|
||||||
|
if obfsPassword == "" {
|
||||||
|
return func(server string) (net.PacketConn, error) {
|
||||||
|
udpConn, err := net.ListenUDP("udp", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return wechat.NewObfsWeChatUDPConn(udpConn, nil), nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return func(server string) (net.PacketConn, error) {
|
||||||
|
obfs := obfs.NewXPlusObfuscator([]byte(obfsPassword))
|
||||||
|
udpConn, err := net.ListenUDP("udp", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return wechat.NewObfsWeChatUDPConn(udpConn, obfs), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientFakeTCPConnFunc(obfsPassword string) ClientPacketConnFunc {
|
||||||
|
if obfsPassword == "" {
|
||||||
|
return func(server string) (net.PacketConn, error) {
|
||||||
|
return faketcp.Dial("tcp", server)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return func(server string) (net.PacketConn, error) {
|
||||||
|
obfs := obfs.NewXPlusObfuscator([]byte(obfsPassword))
|
||||||
|
fakeTCPConn, err := faketcp.Dial("tcp", server)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return faketcp.NewObfsFakeTCPConn(fakeTCPConn, obfs), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerUDPConnFunc(obfsPassword string) ServerPacketConnFunc {
|
||||||
|
if obfsPassword == "" {
|
||||||
|
return func(listen string) (net.PacketConn, error) {
|
||||||
|
laddrU, err := net.ResolveUDPAddr("udp", listen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return net.ListenUDP("udp", laddrU)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return func(listen string) (net.PacketConn, error) {
|
||||||
|
obfs := obfs.NewXPlusObfuscator([]byte(obfsPassword))
|
||||||
|
laddrU, err := net.ResolveUDPAddr("udp", listen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
udpConn, err := net.ListenUDP("udp", laddrU)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return udp.NewObfsUDPConn(udpConn, obfs), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerWeChatConnFunc(obfsPassword string) ServerPacketConnFunc {
|
||||||
|
if obfsPassword == "" {
|
||||||
|
return func(listen string) (net.PacketConn, error) {
|
||||||
|
laddrU, err := net.ResolveUDPAddr("udp", listen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
udpConn, err := net.ListenUDP("udp", laddrU)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return wechat.NewObfsWeChatUDPConn(udpConn, nil), nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return func(listen string) (net.PacketConn, error) {
|
||||||
|
obfs := obfs.NewXPlusObfuscator([]byte(obfsPassword))
|
||||||
|
laddrU, err := net.ResolveUDPAddr("udp", listen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
udpConn, err := net.ListenUDP("udp", laddrU)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return wechat.NewObfsWeChatUDPConn(udpConn, obfs), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerFakeTCPConnFunc(obfsPassword string) ServerPacketConnFunc {
|
||||||
|
if obfsPassword == "" {
|
||||||
|
return func(listen string) (net.PacketConn, error) {
|
||||||
|
return faketcp.Listen("tcp", listen)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return func(listen string) (net.PacketConn, error) {
|
||||||
|
obfs := obfs.NewXPlusObfuscator([]byte(obfsPassword))
|
||||||
|
fakeTCPListener, err := faketcp.Listen("tcp", listen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return faketcp.NewObfsFakeTCPConn(fakeTCPListener, obfs), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
pkg/transport/pktconns/obfs/obfs.go
Normal file
58
pkg/transport/pktconns/obfs/obfs.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package obfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Obfuscator interface {
|
||||||
|
Deobfuscate(in []byte, out []byte) int
|
||||||
|
Obfuscate(in []byte, out []byte) int
|
||||||
|
}
|
||||||
|
|
||||||
|
const xpSaltLen = 16
|
||||||
|
|
||||||
|
// XPlusObfuscator obfuscates payload using one-time keys generated from hashing a pre-shared key and random salt.
|
||||||
|
// Packet format: [salt][obfuscated payload]
|
||||||
|
type XPlusObfuscator struct {
|
||||||
|
Key []byte
|
||||||
|
RandSrc *rand.Rand
|
||||||
|
|
||||||
|
lk sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewXPlusObfuscator(key []byte) *XPlusObfuscator {
|
||||||
|
return &XPlusObfuscator{
|
||||||
|
Key: key,
|
||||||
|
RandSrc: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int {
|
||||||
|
outLen := len(in) - xpSaltLen
|
||||||
|
if outLen <= 0 || len(out) < outLen {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
key := sha256.Sum256(append(x.Key, in[:xpSaltLen]...))
|
||||||
|
for i, c := range in[xpSaltLen:] {
|
||||||
|
out[i] = c ^ key[i%sha256.Size]
|
||||||
|
}
|
||||||
|
return outLen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int {
|
||||||
|
outLen := len(in) + xpSaltLen
|
||||||
|
if len(out) < outLen {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
x.lk.Lock()
|
||||||
|
_, _ = x.RandSrc.Read(out[:xpSaltLen])
|
||||||
|
x.lk.Unlock()
|
||||||
|
key := sha256.Sum256(append(x.Key, out[:xpSaltLen]...))
|
||||||
|
for i, c := range in {
|
||||||
|
out[i+xpSaltLen] = c ^ key[i%sha256.Size]
|
||||||
|
}
|
||||||
|
return outLen
|
||||||
|
}
|
@ -7,12 +7,12 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/obfs"
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns/obfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const udpBufferSize = 65535
|
const udpBufferSize = 65535
|
||||||
|
|
||||||
type ObfsUDPConn struct {
|
type ObfsUDPPacketConn struct {
|
||||||
orig *net.UDPConn
|
orig *net.UDPConn
|
||||||
obfs obfs.Obfuscator
|
obfs obfs.Obfuscator
|
||||||
|
|
||||||
@ -22,8 +22,8 @@ type ObfsUDPConn struct {
|
|||||||
writeMutex sync.Mutex
|
writeMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewObfsUDPConn(orig *net.UDPConn, obfs obfs.Obfuscator) *ObfsUDPConn {
|
func NewObfsUDPConn(orig *net.UDPConn, obfs obfs.Obfuscator) *ObfsUDPPacketConn {
|
||||||
return &ObfsUDPConn{
|
return &ObfsUDPPacketConn{
|
||||||
orig: orig,
|
orig: orig,
|
||||||
obfs: obfs,
|
obfs: obfs,
|
||||||
readBuf: make([]byte, udpBufferSize),
|
readBuf: make([]byte, udpBufferSize),
|
||||||
@ -31,7 +31,7 @@ func NewObfsUDPConn(orig *net.UDPConn, obfs obfs.Obfuscator) *ObfsUDPConn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
func (c *ObfsUDPPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
||||||
for {
|
for {
|
||||||
c.readMutex.Lock()
|
c.readMutex.Lock()
|
||||||
n, addr, err := c.orig.ReadFrom(c.readBuf)
|
n, addr, err := c.orig.ReadFrom(c.readBuf)
|
||||||
@ -51,7 +51,7 @@ func (c *ObfsUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
func (c *ObfsUDPPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
c.writeMutex.Lock()
|
c.writeMutex.Lock()
|
||||||
bn := c.obfs.Obfuscate(p, c.writeBuf)
|
bn := c.obfs.Obfuscate(p, c.writeBuf)
|
||||||
_, err = c.orig.WriteTo(c.writeBuf[:bn], addr)
|
_, err = c.orig.WriteTo(c.writeBuf[:bn], addr)
|
||||||
@ -63,38 +63,38 @@ func (c *ObfsUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) Close() error {
|
func (c *ObfsUDPPacketConn) Close() error {
|
||||||
return c.orig.Close()
|
return c.orig.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) LocalAddr() net.Addr {
|
func (c *ObfsUDPPacketConn) LocalAddr() net.Addr {
|
||||||
return c.orig.LocalAddr()
|
return c.orig.LocalAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) SetDeadline(t time.Time) error {
|
func (c *ObfsUDPPacketConn) SetDeadline(t time.Time) error {
|
||||||
return c.orig.SetDeadline(t)
|
return c.orig.SetDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) SetReadDeadline(t time.Time) error {
|
func (c *ObfsUDPPacketConn) SetReadDeadline(t time.Time) error {
|
||||||
return c.orig.SetReadDeadline(t)
|
return c.orig.SetReadDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) SetWriteDeadline(t time.Time) error {
|
func (c *ObfsUDPPacketConn) SetWriteDeadline(t time.Time) error {
|
||||||
return c.orig.SetWriteDeadline(t)
|
return c.orig.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) SetReadBuffer(bytes int) error {
|
func (c *ObfsUDPPacketConn) SetReadBuffer(bytes int) error {
|
||||||
return c.orig.SetReadBuffer(bytes)
|
return c.orig.SetReadBuffer(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) SetWriteBuffer(bytes int) error {
|
func (c *ObfsUDPPacketConn) SetWriteBuffer(bytes int) error {
|
||||||
return c.orig.SetWriteBuffer(bytes)
|
return c.orig.SetWriteBuffer(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) SyscallConn() (syscall.RawConn, error) {
|
func (c *ObfsUDPPacketConn) SyscallConn() (syscall.RawConn, error) {
|
||||||
return c.orig.SyscallConn()
|
return c.orig.SyscallConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsUDPConn) File() (f *os.File, err error) {
|
func (c *ObfsUDPPacketConn) File() (f *os.File, err error) {
|
||||||
return c.orig.File()
|
return c.orig.File()
|
||||||
}
|
}
|
@ -9,12 +9,14 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/obfs"
|
"github.com/HyNetwork/hysteria/pkg/transport/pktconns/obfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const udpBufferSize = 65535
|
const udpBufferSize = 65535
|
||||||
|
|
||||||
type ObfsWeChatUDPConn struct {
|
// ObfsWeChatUDPPacketConn is still a UDP packet conn, but it adds WeChat video call header to each packet.
|
||||||
|
// Obfs in this case can be nil
|
||||||
|
type ObfsWeChatUDPPacketConn struct {
|
||||||
orig *net.UDPConn
|
orig *net.UDPConn
|
||||||
obfs obfs.Obfuscator
|
obfs obfs.Obfuscator
|
||||||
|
|
||||||
@ -25,8 +27,8 @@ type ObfsWeChatUDPConn struct {
|
|||||||
sn uint32
|
sn uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewObfsWeChatUDPConn(orig *net.UDPConn, obfs obfs.Obfuscator) *ObfsWeChatUDPConn {
|
func NewObfsWeChatUDPConn(orig *net.UDPConn, obfs obfs.Obfuscator) *ObfsWeChatUDPPacketConn {
|
||||||
return &ObfsWeChatUDPConn{
|
return &ObfsWeChatUDPPacketConn{
|
||||||
orig: orig,
|
orig: orig,
|
||||||
obfs: obfs,
|
obfs: obfs,
|
||||||
readBuf: make([]byte, udpBufferSize),
|
readBuf: make([]byte, udpBufferSize),
|
||||||
@ -35,7 +37,7 @@ func NewObfsWeChatUDPConn(orig *net.UDPConn, obfs obfs.Obfuscator) *ObfsWeChatUD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
func (c *ObfsWeChatUDPPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
||||||
for {
|
for {
|
||||||
c.readMutex.Lock()
|
c.readMutex.Lock()
|
||||||
n, addr, err := c.orig.ReadFrom(c.readBuf)
|
n, addr, err := c.orig.ReadFrom(c.readBuf)
|
||||||
@ -43,7 +45,12 @@ func (c *ObfsWeChatUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
|||||||
c.readMutex.Unlock()
|
c.readMutex.Unlock()
|
||||||
return 0, addr, err
|
return 0, addr, err
|
||||||
}
|
}
|
||||||
newN := c.obfs.Deobfuscate(c.readBuf[13:n], p)
|
var newN int
|
||||||
|
if c.obfs != nil {
|
||||||
|
newN = c.obfs.Deobfuscate(c.readBuf[13:n], p)
|
||||||
|
} else {
|
||||||
|
newN = copy(p, c.readBuf[13:n])
|
||||||
|
}
|
||||||
c.readMutex.Unlock()
|
c.readMutex.Unlock()
|
||||||
if newN > 0 {
|
if newN > 0 {
|
||||||
// Valid packet
|
// Valid packet
|
||||||
@ -55,7 +62,7 @@ func (c *ObfsWeChatUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
func (c *ObfsWeChatUDPPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
c.writeMutex.Lock()
|
c.writeMutex.Lock()
|
||||||
c.writeBuf[0] = 0xa1
|
c.writeBuf[0] = 0xa1
|
||||||
c.writeBuf[1] = 0x08
|
c.writeBuf[1] = 0x08
|
||||||
@ -68,7 +75,12 @@ func (c *ObfsWeChatUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error)
|
|||||||
c.writeBuf[10] = 0x30
|
c.writeBuf[10] = 0x30
|
||||||
c.writeBuf[11] = 0x22
|
c.writeBuf[11] = 0x22
|
||||||
c.writeBuf[12] = 0x30
|
c.writeBuf[12] = 0x30
|
||||||
bn := c.obfs.Obfuscate(p, c.writeBuf[13:])
|
var bn int
|
||||||
|
if c.obfs != nil {
|
||||||
|
bn = c.obfs.Obfuscate(p, c.writeBuf[13:])
|
||||||
|
} else {
|
||||||
|
bn = copy(c.writeBuf[13:], p)
|
||||||
|
}
|
||||||
_, err = c.orig.WriteTo(c.writeBuf[:13+bn], addr)
|
_, err = c.orig.WriteTo(c.writeBuf[:13+bn], addr)
|
||||||
c.writeMutex.Unlock()
|
c.writeMutex.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -78,38 +90,38 @@ func (c *ObfsWeChatUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) Close() error {
|
func (c *ObfsWeChatUDPPacketConn) Close() error {
|
||||||
return c.orig.Close()
|
return c.orig.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) LocalAddr() net.Addr {
|
func (c *ObfsWeChatUDPPacketConn) LocalAddr() net.Addr {
|
||||||
return c.orig.LocalAddr()
|
return c.orig.LocalAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) SetDeadline(t time.Time) error {
|
func (c *ObfsWeChatUDPPacketConn) SetDeadline(t time.Time) error {
|
||||||
return c.orig.SetDeadline(t)
|
return c.orig.SetDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) SetReadDeadline(t time.Time) error {
|
func (c *ObfsWeChatUDPPacketConn) SetReadDeadline(t time.Time) error {
|
||||||
return c.orig.SetReadDeadline(t)
|
return c.orig.SetReadDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) SetWriteDeadline(t time.Time) error {
|
func (c *ObfsWeChatUDPPacketConn) SetWriteDeadline(t time.Time) error {
|
||||||
return c.orig.SetWriteDeadline(t)
|
return c.orig.SetWriteDeadline(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) SetReadBuffer(bytes int) error {
|
func (c *ObfsWeChatUDPPacketConn) SetReadBuffer(bytes int) error {
|
||||||
return c.orig.SetReadBuffer(bytes)
|
return c.orig.SetReadBuffer(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) SetWriteBuffer(bytes int) error {
|
func (c *ObfsWeChatUDPPacketConn) SetWriteBuffer(bytes int) error {
|
||||||
return c.orig.SetWriteBuffer(bytes)
|
return c.orig.SetWriteBuffer(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) SyscallConn() (syscall.RawConn, error) {
|
func (c *ObfsWeChatUDPPacketConn) SyscallConn() (syscall.RawConn, error) {
|
||||||
return c.orig.SyscallConn()
|
return c.orig.SyscallConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ObfsWeChatUDPConn) File() (f *os.File, err error) {
|
func (c *ObfsWeChatUDPPacketConn) File() (f *os.File, err error) {
|
||||||
return c.orig.File()
|
return c.orig.File()
|
||||||
}
|
}
|
@ -1,19 +1,12 @@
|
|||||||
package transport
|
package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/conns/faketcp"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/conns/udp"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/conns/wechat"
|
|
||||||
obfsPkg "github.com/HyNetwork/hysteria/pkg/obfs"
|
|
||||||
"github.com/HyNetwork/hysteria/pkg/sockopt"
|
"github.com/HyNetwork/hysteria/pkg/sockopt"
|
||||||
"github.com/HyNetwork/hysteria/pkg/utils"
|
"github.com/HyNetwork/hysteria/pkg/utils"
|
||||||
"github.com/lucas-clemente/quic-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerTransport struct {
|
type ServerTransport struct {
|
||||||
@ -76,64 +69,6 @@ var DefaultServerTransport = &ServerTransport{
|
|||||||
ResolvePreference: ResolvePreferenceDefault,
|
ResolvePreference: ResolvePreferenceDefault,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *ServerTransport) quicPacketConn(proto string, laddr string, obfs obfsPkg.Obfuscator) (net.PacketConn, error) {
|
|
||||||
if len(proto) == 0 || proto == "udp" {
|
|
||||||
laddrU, err := net.ResolveUDPAddr("udp", laddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn, err := net.ListenUDP("udp", laddrU)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if obfs != nil {
|
|
||||||
oc := udp.NewObfsUDPConn(conn, obfs)
|
|
||||||
return oc, nil
|
|
||||||
} else {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
} else if proto == "wechat-video" {
|
|
||||||
laddrU, err := net.ResolveUDPAddr("udp", laddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn, err := net.ListenUDP("udp", laddrU)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if obfs == nil {
|
|
||||||
obfs = obfsPkg.NewDummyObfuscator()
|
|
||||||
}
|
|
||||||
return wechat.NewObfsWeChatUDPConn(conn, obfs), nil
|
|
||||||
} else if proto == "faketcp" {
|
|
||||||
conn, err := faketcp.Listen("tcp", laddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if obfs != nil {
|
|
||||||
oc := faketcp.NewObfsFakeTCPConn(conn, obfs)
|
|
||||||
return oc, nil
|
|
||||||
} else {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("unsupported protocol: %s", proto)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *ServerTransport) QUICListen(proto string, listen string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator) (quic.Listener, error) {
|
|
||||||
pktConn, err := st.quicPacketConn(proto, listen, obfs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
l, err := quic.Listen(pktConn, tlsConfig, quicConfig)
|
|
||||||
if err != nil {
|
|
||||||
_ = pktConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return l, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *ServerTransport) ResolveIPAddr(address string) (*net.IPAddr, bool, error) {
|
func (st *ServerTransport) ResolveIPAddr(address string) (*net.IPAddr, bool, error) {
|
||||||
ip, zone := utils.ParseIPZone(address)
|
ip, zone := utils.ParseIPZone(address)
|
||||||
if ip != nil {
|
if ip != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user