feat: support fast_open for hysteria, and unified parameter naming

This commit is contained in:
Skyxim 2022-11-26 19:37:32 +08:00
parent 7f40645934
commit a5ae2e891c
2 changed files with 79 additions and 44 deletions

View file

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"net" "net"
"os" "os"
"reflect"
"regexp" "regexp"
"strconv" "strconv"
"time" "time"
@ -97,17 +98,23 @@ type HysteriaOption struct {
Down string `proxy:"down"` Down string `proxy:"down"`
DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash
Auth string `proxy:"auth,omitempty"` Auth string `proxy:"auth,omitempty"`
AuthString string `proxy:"auth_str,omitempty"` OldAuthString string `proxy:"auth_str,omitempty"`
AuthString string `proxy:"auth-str,omitempty"`
Obfs string `proxy:"obfs,omitempty"` Obfs string `proxy:"obfs,omitempty"`
SNI string `proxy:"sni,omitempty"` SNI string `proxy:"sni,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
Fingerprint string `proxy:"fingerprint,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"`
ALPN []string `proxy:"alpn,omitempty"` ALPN []string `proxy:"alpn,omitempty"`
CustomCA string `proxy:"ca,omitempty"` CustomCA string `proxy:"ca,omitempty"`
CustomCAString string `proxy:"ca_str,omitempty"` OldCustomCAString string `proxy:"ca_str,omitempty"`
ReceiveWindowConn int `proxy:"recv_window_conn,omitempty"` CustomCAString string `proxy:"ca-str,omitempty"`
ReceiveWindow int `proxy:"recv_window,omitempty"` OldReceiveWindowConn int `proxy:"recv_window_conn,omitempty"`
DisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"` ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"`
OldReceiveWindow int `proxy:"recv_window,omitempty"`
ReceiveWindow int `proxy:"recv-window,omitempty"`
OldDisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"`
DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"`
FastOpen bool `proxy:"fast-open,omitempty"`
} }
func (c *HysteriaOption) Speed() (uint64, uint64, error) { func (c *HysteriaOption) Speed() (uint64, uint64, error) {
@ -151,8 +158,8 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err) return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err)
} }
} else if option.CustomCAString != "" { } else if compatibilityValue(option.CustomCAString, option.OldCustomCAString) != "" {
bs = []byte(option.CustomCAString) bs = []byte(compatibilityValue(option.CustomCAString, option.OldCustomCAString))
} }
if len(bs) > 0 { if len(bs) > 0 {
@ -182,14 +189,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
} else { } else {
tlsConfig.NextProtos = []string{DefaultALPN} tlsConfig.NextProtos = []string{DefaultALPN}
} }
quicConfig := &quic.Config{ quicConfig := &quic.Config{
InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn), InitialStreamReceiveWindow: uint64(compatibilityValue(option.ReceiveWindowConn, option.OldReceiveWindow)),
MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn), MaxStreamReceiveWindow: uint64(compatibilityValue(option.ReceiveWindowConn, option.OldReceiveWindow)),
InitialConnectionReceiveWindow: uint64(option.ReceiveWindow), InitialConnectionReceiveWindow: uint64(compatibilityValue(option.ReceiveWindow, option.OldReceiveWindow)),
MaxConnectionReceiveWindow: uint64(option.ReceiveWindow), MaxConnectionReceiveWindow: uint64(compatibilityValue(option.ReceiveWindow, option.OldReceiveWindow)),
KeepAlivePeriod: 10 * time.Second, KeepAlivePeriod: 10 * time.Second,
DisablePathMTUDiscovery: option.DisableMTUDiscovery, DisablePathMTUDiscovery: compatibilityValue(option.DisableMTUDiscovery, option.OldDisableMTUDiscovery),
EnableDatagrams: true, EnableDatagrams: true,
} }
if option.ObfsProtocol != "" { if option.ObfsProtocol != "" {
@ -198,11 +204,11 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
if option.Protocol == "" { if option.Protocol == "" {
option.Protocol = DefaultProtocol option.Protocol = DefaultProtocol
} }
if option.ReceiveWindowConn == 0 { if compatibilityValue( option.ReceiveWindowConn, option.OldReceiveWindowConn)== 0 {
quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10
quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow
} }
if option.ReceiveWindow == 0 { if compatibilityValue( option.ReceiveWindow,option.OldReceiveWindow) == 0 {
quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10 quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10
quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow
} }
@ -210,7 +216,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
log.Infoln("hysteria: Path MTU Discovery is not yet supported on this platform") log.Infoln("hysteria: Path MTU Discovery is not yet supported on this platform")
} }
var auth = []byte(option.AuthString) var auth = []byte(compatibilityValue(option.AuthString, option.OldAuthString))
if option.Auth != "" { if option.Auth != "" {
auth, err = base64.StdEncoding.DecodeString(option.Auth) auth, err = base64.StdEncoding.DecodeString(option.Auth)
if err != nil { if err != nil {
@ -235,7 +241,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
client, err := core.NewClient( client, err := core.NewClient(
addr, option.Protocol, auth, tlsConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl { addr, option.Protocol, auth, tlsConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl {
return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS)) return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS))
}, obfuscator, }, obfuscator, option.FastOpen,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("hysteria %s create error: %w", addr, err) return nil, fmt.Errorf("hysteria %s create error: %w", addr, err)
@ -254,6 +260,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
}, nil }, nil
} }
func compatibilityValue[T any](prefer T, fallback T) T {
if reflect.ValueOf(prefer).IsZero() {
return fallback
} else {
return prefer
}
}
func stringToBps(s string) uint64 { func stringToBps(s string) uint64 {
if s == "" { if s == "" {
return 0 return 0

View file

@ -45,11 +45,12 @@ type Client struct {
udpSessionMutex sync.RWMutex udpSessionMutex sync.RWMutex
udpSessionMap map[uint32]chan *udpMessage udpSessionMap map[uint32]chan *udpMessage
udpDefragger defragger udpDefragger defragger
fastOpen bool
} }
func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config, func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config,
transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory,
obfuscator obfs.Obfuscator) (*Client, error) { obfuscator obfs.Obfuscator,fastOpen bool) (*Client, error) {
quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery
c := &Client{ c := &Client{
transport: transport, transport: transport,
@ -62,6 +63,7 @@ func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.C
obfuscator: obfuscator, obfuscator: obfuscator,
tlsConfig: tlsConfig, tlsConfig: tlsConfig,
quicConfig: quicConfig, quicConfig: quicConfig,
fastOpen: fastOpen,
} }
return c, nil return c, nil
} }
@ -205,6 +207,9 @@ func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn,
_ = stream.Close() _ = stream.Close()
return nil, err return nil, err
} }
// If fast open is enabled, we return the stream immediately
// and defer the response handling to the first Read() call
if !c.fastOpen {
// Read response // Read response
var sr serverResponse var sr serverResponse
err = struc.Unpack(stream, &sr) err = struc.Unpack(stream, &sr)
@ -216,10 +221,13 @@ func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn,
_ = stream.Close() _ = stream.Close()
return nil, fmt.Errorf("connection rejected: %s", sr.Message) return nil, fmt.Errorf("connection rejected: %s", sr.Message)
} }
}
return &quicConn{ return &quicConn{
Orig: stream, Orig: stream,
PseudoLocalAddr: session.LocalAddr(), PseudoLocalAddr: session.LocalAddr(),
PseudoRemoteAddr: session.RemoteAddr(), PseudoRemoteAddr: session.RemoteAddr(),
Established: !c.fastOpen,
}, nil }, nil
} }
@ -288,9 +296,23 @@ type quicConn struct {
Orig quic.Stream Orig quic.Stream
PseudoLocalAddr net.Addr PseudoLocalAddr net.Addr
PseudoRemoteAddr net.Addr PseudoRemoteAddr net.Addr
Established bool
} }
func (w *quicConn) Read(b []byte) (n int, err error) { func (w *quicConn) Read(b []byte) (n int, err error) {
if !w.Established {
var sr serverResponse
err := struc.Unpack(w.Orig, &sr)
if err != nil {
_ = w.Close()
return 0, err
}
if !sr.OK {
_ = w.Close()
return 0, fmt.Errorf("connection rejected: %s", sr.Message)
}
w.Established = true
}
return w.Orig.Read(b) return w.Orig.Read(b)
} }