mirror of
https://github.com/wyx2685/V2bX.git
synced 2025-01-23 10:28:13 -05:00
d76c6a73eb
add conditional compilation support add multi core support
308 lines
8.3 KiB
Go
308 lines
8.3 KiB
Go
package hy
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/yosuke-furukawa/json5/encoding/json5"
|
|
"regexp"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
mbpsToBps = 125000
|
|
minSpeedBPS = 16384
|
|
|
|
DefaultALPN = "hysteria"
|
|
|
|
DefaultStreamReceiveWindow = 16777216 // 16 MB
|
|
DefaultConnectionReceiveWindow = DefaultStreamReceiveWindow * 5 / 2 // 40 MB
|
|
|
|
DefaultMaxIncomingStreams = 1024
|
|
|
|
DefaultMMDBFilename = "GeoLite2-Country.mmdb"
|
|
|
|
ServerMaxIdleTimeoutSec = 60
|
|
DefaultClientIdleTimeoutSec = 20
|
|
|
|
DefaultClientHopIntervalSec = 10
|
|
)
|
|
|
|
var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`)
|
|
|
|
type serverConfig struct {
|
|
Listen string `json:"listen"`
|
|
Protocol string `json:"protocol"`
|
|
ACME struct {
|
|
Domains []string `json:"domains"`
|
|
Email string `json:"email"`
|
|
DisableHTTPChallenge bool `json:"disable_http"`
|
|
DisableTLSALPNChallenge bool `json:"disable_tlsalpn"`
|
|
AltHTTPPort int `json:"alt_http_port"`
|
|
AltTLSALPNPort int `json:"alt_tlsalpn_port"`
|
|
} `json:"acme"`
|
|
CertFile string `json:"cert"`
|
|
KeyFile string `json:"key"`
|
|
// Optional below
|
|
Up string `json:"up"`
|
|
UpMbps int `json:"up_mbps"`
|
|
Down string `json:"down"`
|
|
DownMbps int `json:"down_mbps"`
|
|
DisableUDP bool `json:"disable_udp"`
|
|
ACL string `json:"acl"`
|
|
MMDB string `json:"mmdb"`
|
|
Obfs string `json:"obfs"`
|
|
Auth struct {
|
|
Mode string `json:"mode"`
|
|
Config json5.RawMessage `json:"config"`
|
|
} `json:"auth"`
|
|
ALPN string `json:"alpn"`
|
|
PrometheusListen string `json:"prometheus_listen"`
|
|
ReceiveWindowConn uint64 `json:"recv_window_conn"`
|
|
ReceiveWindowClient uint64 `json:"recv_window_client"`
|
|
MaxConnClient int `json:"max_conn_client"`
|
|
DisableMTUDiscovery bool `json:"disable_mtu_discovery"`
|
|
Resolver string `json:"resolver"`
|
|
ResolvePreference string `json:"resolve_preference"`
|
|
SOCKS5Outbound struct {
|
|
Server string `json:"server"`
|
|
User string `json:"user"`
|
|
Password string `json:"password"`
|
|
} `json:"socks5_outbound"`
|
|
BindOutbound struct {
|
|
Address string `json:"address"`
|
|
Device string `json:"device"`
|
|
} `json:"bind_outbound"`
|
|
}
|
|
|
|
func (c *serverConfig) Speed() (uint64, uint64, error) {
|
|
var up, down uint64
|
|
if len(c.Up) > 0 {
|
|
up = stringToBps(c.Up)
|
|
if up == 0 {
|
|
return 0, 0, errors.New("invalid speed format")
|
|
}
|
|
} else {
|
|
up = uint64(c.UpMbps) * mbpsToBps
|
|
}
|
|
if len(c.Down) > 0 {
|
|
down = stringToBps(c.Down)
|
|
if down == 0 {
|
|
return 0, 0, errors.New("invalid speed format")
|
|
}
|
|
} else {
|
|
down = uint64(c.DownMbps) * mbpsToBps
|
|
}
|
|
return up, down, nil
|
|
}
|
|
|
|
func (c *serverConfig) Check() error {
|
|
if len(c.Listen) == 0 {
|
|
return errors.New("missing listen address")
|
|
}
|
|
if len(c.ACME.Domains) == 0 && (len(c.CertFile) == 0 || len(c.KeyFile) == 0) {
|
|
return errors.New("need either ACME info or cert/key files")
|
|
}
|
|
if len(c.ACME.Domains) > 0 && (len(c.CertFile) > 0 || len(c.KeyFile) > 0) {
|
|
return errors.New("cannot use both ACME and cert/key files, they are mutually exclusive")
|
|
}
|
|
if up, down, err := c.Speed(); err != nil || (up != 0 && up < minSpeedBPS) || (down != 0 && down < minSpeedBPS) {
|
|
return errors.New("invalid speed")
|
|
}
|
|
if (c.ReceiveWindowConn != 0 && c.ReceiveWindowConn < 65536) ||
|
|
(c.ReceiveWindowClient != 0 && c.ReceiveWindowClient < 65536) {
|
|
return errors.New("invalid receive window size")
|
|
}
|
|
if c.MaxConnClient < 0 {
|
|
return errors.New("invalid max connections per client")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *serverConfig) Fill() {
|
|
if len(c.ALPN) == 0 {
|
|
c.ALPN = DefaultALPN
|
|
}
|
|
if c.ReceiveWindowConn == 0 {
|
|
c.ReceiveWindowConn = DefaultStreamReceiveWindow
|
|
}
|
|
if c.ReceiveWindowClient == 0 {
|
|
c.ReceiveWindowClient = DefaultConnectionReceiveWindow
|
|
}
|
|
if c.MaxConnClient == 0 {
|
|
c.MaxConnClient = DefaultMaxIncomingStreams
|
|
}
|
|
if len(c.MMDB) == 0 {
|
|
c.MMDB = DefaultMMDBFilename
|
|
}
|
|
}
|
|
|
|
func (c *serverConfig) String() string {
|
|
return fmt.Sprintf("%+v", *c)
|
|
}
|
|
|
|
type Relay struct {
|
|
Listen string `json:"listen"`
|
|
Remote string `json:"remote"`
|
|
Timeout int `json:"timeout"`
|
|
}
|
|
|
|
func (r *Relay) Check() error {
|
|
if len(r.Listen) == 0 {
|
|
return errors.New("missing relay listen address")
|
|
}
|
|
if len(r.Remote) == 0 {
|
|
return errors.New("missing relay remote address")
|
|
}
|
|
if r.Timeout != 0 && r.Timeout < 4 {
|
|
return errors.New("invalid relay timeout")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type clientConfig struct {
|
|
Server string `json:"server"`
|
|
Protocol string `json:"protocol"`
|
|
Up string `json:"up"`
|
|
UpMbps int `json:"up_mbps"`
|
|
Down string `json:"down"`
|
|
DownMbps int `json:"down_mbps"`
|
|
// Optional below
|
|
Retry int `json:"retry"`
|
|
RetryInterval *int `json:"retry_interval"`
|
|
QuitOnDisconnect bool `json:"quit_on_disconnect"`
|
|
HandshakeTimeout int `json:"handshake_timeout"`
|
|
IdleTimeout int `json:"idle_timeout"`
|
|
HopInterval int `json:"hop_interval"`
|
|
SOCKS5 struct {
|
|
Listen string `json:"listen"`
|
|
Timeout int `json:"timeout"`
|
|
DisableUDP bool `json:"disable_udp"`
|
|
User string `json:"user"`
|
|
Password string `json:"password"`
|
|
} `json:"socks5"`
|
|
HTTP struct {
|
|
Listen string `json:"listen"`
|
|
Timeout int `json:"timeout"`
|
|
User string `json:"user"`
|
|
Password string `json:"password"`
|
|
Cert string `json:"cert"`
|
|
Key string `json:"key"`
|
|
} `json:"http"`
|
|
TUN struct {
|
|
Name string `json:"name"`
|
|
Timeout int `json:"timeout"`
|
|
MTU uint32 `json:"mtu"`
|
|
TCPSendBufferSize string `json:"tcp_sndbuf"`
|
|
TCPReceiveBufferSize string `json:"tcp_rcvbuf"`
|
|
TCPModerateReceiveBuffer bool `json:"tcp_autotuning"`
|
|
} `json:"tun"`
|
|
TCPRelays []Relay `json:"relay_tcps"`
|
|
TCPRelay Relay `json:"relay_tcp"` // deprecated, but we still support it for backward compatibility
|
|
UDPRelays []Relay `json:"relay_udps"`
|
|
UDPRelay Relay `json:"relay_udp"` // deprecated, but we still support it for backward compatibility
|
|
TCPTProxy struct {
|
|
Listen string `json:"listen"`
|
|
Timeout int `json:"timeout"`
|
|
} `json:"tproxy_tcp"`
|
|
UDPTProxy struct {
|
|
Listen string `json:"listen"`
|
|
Timeout int `json:"timeout"`
|
|
} `json:"tproxy_udp"`
|
|
TCPRedirect struct {
|
|
Listen string `json:"listen"`
|
|
Timeout int `json:"timeout"`
|
|
} `json:"redirect_tcp"`
|
|
ACL string `json:"acl"`
|
|
MMDB string `json:"mmdb"`
|
|
Obfs string `json:"obfs"`
|
|
Auth []byte `json:"auth"`
|
|
AuthString string `json:"auth_str"`
|
|
ALPN string `json:"alpn"`
|
|
ServerName string `json:"server_name"`
|
|
Insecure bool `json:"insecure"`
|
|
CustomCA string `json:"ca"`
|
|
ReceiveWindowConn uint64 `json:"recv_window_conn"`
|
|
ReceiveWindow uint64 `json:"recv_window"`
|
|
DisableMTUDiscovery bool `json:"disable_mtu_discovery"`
|
|
FastOpen bool `json:"fast_open"`
|
|
LazyStart bool `json:"lazy_start"`
|
|
Resolver string `json:"resolver"`
|
|
ResolvePreference string `json:"resolve_preference"`
|
|
}
|
|
|
|
func (c *clientConfig) Speed() (uint64, uint64, error) {
|
|
var up, down uint64
|
|
if len(c.Up) > 0 {
|
|
up = stringToBps(c.Up)
|
|
if up == 0 {
|
|
return 0, 0, errors.New("invalid speed format")
|
|
}
|
|
} else {
|
|
up = uint64(c.UpMbps) * mbpsToBps
|
|
}
|
|
if len(c.Down) > 0 {
|
|
down = stringToBps(c.Down)
|
|
if down == 0 {
|
|
return 0, 0, errors.New("invalid speed format")
|
|
}
|
|
} else {
|
|
down = uint64(c.DownMbps) * mbpsToBps
|
|
}
|
|
return up, down, nil
|
|
}
|
|
|
|
func (c *clientConfig) Fill() {
|
|
if len(c.ALPN) == 0 {
|
|
c.ALPN = DefaultALPN
|
|
}
|
|
if c.ReceiveWindowConn == 0 {
|
|
c.ReceiveWindowConn = DefaultStreamReceiveWindow
|
|
}
|
|
if c.ReceiveWindow == 0 {
|
|
c.ReceiveWindow = DefaultConnectionReceiveWindow
|
|
}
|
|
if len(c.MMDB) == 0 {
|
|
c.MMDB = DefaultMMDBFilename
|
|
}
|
|
if c.IdleTimeout == 0 {
|
|
c.IdleTimeout = DefaultClientIdleTimeoutSec
|
|
}
|
|
if c.HopInterval == 0 {
|
|
c.HopInterval = DefaultClientHopIntervalSec
|
|
}
|
|
}
|
|
|
|
func (c *clientConfig) String() string {
|
|
return fmt.Sprintf("%+v", *c)
|
|
}
|
|
|
|
func stringToBps(s string) uint64 {
|
|
if s == "" {
|
|
return 0
|
|
}
|
|
m := rateStringRegexp.FindStringSubmatch(s)
|
|
if m == nil {
|
|
return 0
|
|
}
|
|
var n uint64
|
|
switch m[2] {
|
|
case "K":
|
|
n = 1 << 10
|
|
case "M":
|
|
n = 1 << 20
|
|
case "G":
|
|
n = 1 << 30
|
|
case "T":
|
|
n = 1 << 40
|
|
default:
|
|
n = 1
|
|
}
|
|
v, _ := strconv.ParseUint(m[1], 10, 64)
|
|
n = v * n
|
|
if m[3] == "b" {
|
|
// Bits, need to convert to bytes
|
|
n = n >> 3
|
|
}
|
|
return n
|
|
}
|