mirror of
https://github.com/hackdiaz-dev/free-turn-proxy.git
synced 2026-10-12 09:41:53 +02:00
refactor: переименовать флаги и поля CLI/конфига
- wrap → obf: -wrap/-wrap-key/-gen-wrap-key → -obf/-obf-key/-gen-obf-key;
ObfOpts.WrapMode/WrapKey/GenWrapKey → Mode/Key/GenKey
- vless → mode: -vless/-vless-bond → -mode tcp / -bond;
clientProxyMode/serverProxyMode принимают string вместо bool
- -udp → -transport tcp|udp; TURNOpts.UDP → TransportUDP (везде)
- -vk-link → -link; -dns → -dns-mode; DNSModeUDP → DNSModePlain ("plain")
- KCP: убрать LoadProfileFromEnv/LoadFECFromEnv — хардкодить DefaultProfile/FEC{}
- Добавить валидацию -transport/-mode/-bond/-dns-mode в ParseClient/ParseServer
This commit is contained in:
+13
-13
@@ -75,10 +75,10 @@ func main() {
|
||||
}
|
||||
appDialer := dnsdial.AppDialer(cfg.DNS.Mode)
|
||||
dnsdial.InstallGlobalResolver(cfg.DNS.Mode)
|
||||
if cfg.Obf.GenWrapKey {
|
||||
if cfg.Obf.GenKey {
|
||||
key, gerr := srtpmimicry.GenKeyHex()
|
||||
if gerr != nil {
|
||||
logger.Errorf("gen-wrap-key: %v", gerr)
|
||||
logger.Errorf("gen-obf-key: %v", gerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(key)
|
||||
@@ -89,8 +89,8 @@ func main() {
|
||||
logger.Errorf("resolve peer addr: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if cfg.Obf.WrapMode {
|
||||
logger.Infof("WRAP mode enabled: peer server must use matching -wrap-key")
|
||||
if cfg.Obf.Mode {
|
||||
logger.Infof("OBF mode enabled: peer server must use matching -obf-key")
|
||||
}
|
||||
|
||||
var connectedStreams atomic.Int32
|
||||
@@ -105,27 +105,27 @@ func main() {
|
||||
})
|
||||
|
||||
if cfg.Proxy.Mode != config.ProxyModeUDP {
|
||||
vlessDtlsDialer := &dtlsdial.Dialer{
|
||||
tcpDtlsDialer := &dtlsdial.Dialer{
|
||||
HandshakeTimeout: 30 * time.Second,
|
||||
HandshakeSem: make(chan struct{}, 3),
|
||||
}
|
||||
bondH := &bondclient.Handler{Deps: bondclient.Deps{Log: logger}}
|
||||
vlessDeps := &tcpfwd.Deps{
|
||||
DTLSDialer: vlessDtlsDialer,
|
||||
tcpDeps := &tcpfwd.Deps{
|
||||
DTLSDialer: tcpDtlsDialer,
|
||||
Log: logger,
|
||||
BondHandler: bondH.Handle,
|
||||
}
|
||||
vlessParams := &tcpfwd.Params{
|
||||
tcpParams := &tcpfwd.Params{
|
||||
Host: cfg.TURN.Host,
|
||||
Port: cfg.TURN.Port,
|
||||
Link: cfg.VK.Link,
|
||||
UDP: cfg.TURN.UDP,
|
||||
WrapKey: cfg.Obf.WrapKey,
|
||||
TransportUDP: cfg.TURN.TransportUDP,
|
||||
ObfKey: cfg.Obf.Key,
|
||||
GetCreds: tcpfwd.GetCredsFunc(vkAuth.GetCredentials),
|
||||
KCPProfile: cfg.KCP.Profile,
|
||||
KCPFEC: cfg.KCP.FEC,
|
||||
}
|
||||
if err := tcpfwd.Run(ctx, vlessDeps, vlessParams, peer, cfg.Proxy.Listen, cfg.TURN.N, cfg.Proxy.Mode == config.ProxyModeTCPFwdBond); err != nil {
|
||||
if err := tcpfwd.Run(ctx, tcpDeps, tcpParams, peer, cfg.Proxy.Listen, cfg.TURN.N, cfg.Proxy.Mode == config.ProxyModeTCPFwdBond); err != nil {
|
||||
logger.Errorf("tcpfwd: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -140,8 +140,8 @@ func main() {
|
||||
Host: cfg.TURN.Host,
|
||||
Port: cfg.TURN.Port,
|
||||
Link: cfg.VK.Link,
|
||||
UDP: cfg.TURN.UDP,
|
||||
WrapKey: cfg.Obf.WrapKey,
|
||||
TransportUDP: cfg.TURN.TransportUDP,
|
||||
ObfKey: cfg.Obf.Key,
|
||||
GetCreds: udprelay.GetCredsFunc(vkAuth.GetCredentials),
|
||||
}
|
||||
if err := udprelay.Run(ctx, udpDtlsDialer, vkAuth, logger, &connectedStreams, udpParams, peer, cfg.Proxy.Listen, cfg.TURN.N); err != nil {
|
||||
|
||||
+9
-9
@@ -29,10 +29,10 @@ func main() {
|
||||
}
|
||||
logger := logx.New(cfg.Log.Debug)
|
||||
|
||||
if cfg.Obf.GenWrapKey {
|
||||
if cfg.Obf.GenKey {
|
||||
key, gerr := srtpmimicry.GenKeyHex()
|
||||
if gerr != nil {
|
||||
logger.Errorf("gen-wrap-key: %v", gerr)
|
||||
logger.Errorf("gen-obf-key: %v", gerr)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(key)
|
||||
@@ -61,10 +61,10 @@ func main() {
|
||||
logger.Errorf("resolve listen addr: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.Infof("Starting server listen=%s connect=%s vless=%t wrap=%t bond-autodetect=true",
|
||||
cfg.Proxy.Listen, cfg.Proxy.Connect, cfg.Proxy.Mode == config.ProxyModeTCPFwd, cfg.Obf.WrapMode)
|
||||
if !cfg.Obf.WrapMode {
|
||||
logger.Warnf("running without -wrap: any client reaching %s can relay to %s (no shared-key auth)", cfg.Proxy.Listen, cfg.Proxy.Connect)
|
||||
logger.Infof("Starting server listen=%s connect=%s mode=%s obf=%t bond-autodetect=true",
|
||||
cfg.Proxy.Listen, cfg.Proxy.Connect, cfg.Proxy.Mode, cfg.Obf.Mode)
|
||||
if !cfg.Obf.Mode {
|
||||
logger.Warnf("running without -obf: any client reaching %s can relay to %s (no shared-key auth)", cfg.Proxy.Listen, cfg.Proxy.Connect)
|
||||
}
|
||||
|
||||
certificate, genErr := dtlsdial.GenerateSelfSignedCert()
|
||||
@@ -80,9 +80,9 @@ func main() {
|
||||
dtls.WithConnectionIDGenerator(dtls.RandomCIDGenerator(8)),
|
||||
}
|
||||
var listener net.Listener
|
||||
if cfg.Obf.WrapMode {
|
||||
logger.Infof("WRAP mode enabled: listener only accepts clients with matching -wrap-key")
|
||||
wrapListener, werr := srtpmimicry.Listen(addr, cfg.Obf.WrapKey)
|
||||
if cfg.Obf.Mode {
|
||||
logger.Infof("OBF mode enabled: listener only accepts clients with matching -obf-key")
|
||||
wrapListener, werr := srtpmimicry.Listen(addr, cfg.Obf.Key)
|
||||
if werr != nil {
|
||||
logger.Errorf("wrap listen: %v", werr)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -364,7 +364,7 @@ func dohForwarderDial(r *DohResolver) dialFunc {
|
||||
}
|
||||
|
||||
const (
|
||||
DNSModeUDP = "udp"
|
||||
DNSModePlain = "plain"
|
||||
DNSModeDoH = "doh"
|
||||
DNSModeAuto = "auto"
|
||||
)
|
||||
@@ -417,7 +417,7 @@ type dialFunc = func(context.Context, string, string) (net.Conn, error)
|
||||
// залипает процесс на DoH до конца его жизни.
|
||||
func buildDialer(mode string, r *DohResolver) net.Dialer {
|
||||
switch mode {
|
||||
case DNSModeUDP:
|
||||
case DNSModePlain:
|
||||
return newAppDialer(udpDNSDial)
|
||||
case DNSModeDoH:
|
||||
return newAppDialer(dohForwarderDial(r))
|
||||
|
||||
+72
-54
@@ -20,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
dnsModeUDP = "udp"
|
||||
dnsModePlain = "plain"
|
||||
dnsModeDoH = "doh"
|
||||
dnsModeAuto = "auto"
|
||||
defaultStreamsPerCache = 10
|
||||
@@ -32,24 +32,24 @@ const (
|
||||
type ProxyMode string
|
||||
|
||||
const (
|
||||
ProxyModeUDP ProxyMode = "udp" // -vless=false: UDP-релей пакетов (WireGuard)
|
||||
ProxyModeTCPFwd ProxyMode = "tcpfwd" // -vless=true: TCP-форвардер через smux
|
||||
ProxyModeTCPFwdBond ProxyMode = "tcpfwd-bond" // -vless=true -vless-bond=true: bond TCP по N smux-сессиям
|
||||
ProxyModeUDP ProxyMode = "udp" // -mode udp (default): UDP-релей пакетов (WireGuard)
|
||||
ProxyModeTCPFwd ProxyMode = "tcpfwd" // -mode tcp: TCP-форвардер через smux
|
||||
ProxyModeTCPFwdBond ProxyMode = "tcpfwd-bond" // -mode tcp -bond: bond TCP по N smux-сессиям
|
||||
)
|
||||
|
||||
// TURNOpts — опции TURN-сервера (куда и как подключаться).
|
||||
type TURNOpts struct {
|
||||
Host string // -turn: переопределить IP/host TURN-сервера
|
||||
Port string // -port: переопределить порт TURN
|
||||
UDP bool // -udp: подключение к TURN по UDP (по умолчанию TCP/TLS)
|
||||
N int // -n: число TURN-потоков (только клиент)
|
||||
Host string // -turn: переопределить IP/host TURN-сервера
|
||||
Port string // -port: переопределить порт TURN
|
||||
TransportUDP bool // -transport udp: подключение к TURN по UDP (по умолчанию TCP/TLS)
|
||||
N int // -n: число TURN-потоков (только клиент)
|
||||
}
|
||||
|
||||
// ObfOpts — опции обфускации TURN-payload (SRTP-mimicry wrap).
|
||||
// ObfOpts — опции обфускации TURN-payload (SRTP-mimicry).
|
||||
type ObfOpts struct {
|
||||
WrapMode bool // -wrap: включить SRTP-mimicry AEAD-обёртку
|
||||
WrapKey []byte // -wrap-key (декодированный): 32-байтовый общий ключ; nil если WrapMode=false
|
||||
GenWrapKey bool // -gen-wrap-key: напечатать новый ключ и выйти
|
||||
Mode bool // -obf: включить SRTP-mimicry AEAD-обёртку
|
||||
Key []byte // -obf-key (декодированный): 32-байтовый общий ключ; nil если Mode=false
|
||||
GenKey bool // -gen-obf-key: напечатать новый ключ и выйти
|
||||
}
|
||||
|
||||
// ProxyOpts — опции прокси прикладного уровня.
|
||||
@@ -62,14 +62,14 @@ type ProxyOpts struct {
|
||||
|
||||
// VKOpts — опции VK-учёток и captcha (только клиент).
|
||||
type VKOpts struct {
|
||||
Link string // -vk-link (нормализован до join-кода)
|
||||
Link string // -link (нормализован до join-кода)
|
||||
StreamsPerCred int // -streams-per-cred
|
||||
ManualCaptcha bool // -manual-captcha
|
||||
}
|
||||
|
||||
// DNSOpts — опции DNS-резолвинга (только клиент).
|
||||
type DNSOpts struct {
|
||||
Mode string // -dns: udp | doh | auto
|
||||
Mode string // -dns-mode: plain | doh | auto
|
||||
Servers []string // -dns-servers (через запятую); nil если флаг пуст
|
||||
}
|
||||
|
||||
@@ -78,8 +78,7 @@ type LogOpts struct {
|
||||
Debug bool // -debug
|
||||
}
|
||||
|
||||
// KCPOpts — опции KCP-туннеля. Обе стороны должны согласовать Profile и FEC;
|
||||
// значения берутся из переменных окружения VK_TURN_KCP_*.
|
||||
// KCPOpts — параметры KCP-туннеля, хардкодятся из DefaultProfile/FEC{}.
|
||||
type KCPOpts struct {
|
||||
Profile kcptun.Profile
|
||||
FEC kcptun.FEC
|
||||
@@ -115,19 +114,19 @@ func ParseClient(args []string, errOut io.Writer) (*Client, error) {
|
||||
host := fs.String("turn", "", "переопределить IP TURN-сервера (по умолчанию берётся из VK Calls ссылки)")
|
||||
port := fs.String("port", "", "переопределить порт TURN-сервера (по умолчанию берётся из VK Calls ссылки)")
|
||||
listen := fs.String("listen", "127.0.0.1:9000", "локальный адрес ip:port, куда подключается WireGuard или Xray клиент")
|
||||
vklink := fs.String("vk-link", "", "ссылка VK Calls вида https://vk.com/call/join/... (обязательно)")
|
||||
vklink := fs.String("link", "", "ссылка VK Calls вида https://vk.com/call/join/... (обязательно)")
|
||||
peerAddr := fs.String("peer", "", "адрес сервера VK TURN Proxy на VPS, host:port (обязательно)")
|
||||
n := fs.Int("n", 10, "количество параллельных TURN-потоков (соединений к TURN-реле)")
|
||||
udp := fs.Bool("udp", false, "подключаться к TURN-реле по UDP (по умолчанию TCP/TLS)")
|
||||
vlessMode := fs.Bool("vless", false, "режим TCP-форвардера (VLESS/Xray) вместо UDP-релея для WireGuard")
|
||||
vlessBond := fs.Bool("vless-bond", false, "распределять одно VLESS TCP-соединение по всем активным smux-сессиям (только с -vless)")
|
||||
wrapMode := fs.Bool("wrap", false, "маскировать TURN-payload под SRTP (RTP/opus + ChaCha20-Poly1305 AEAD) для обхода content-filter VK; ключ должен совпадать на клиенте и сервере")
|
||||
wrapKeyHex := fs.String("wrap-key", "", "общий ключ для -wrap, 32 байта в hex (64 символа)")
|
||||
genWrapKey := fs.Bool("gen-wrap-key", false, "напечатать новый ключ для -wrap-key и выйти")
|
||||
transportFlag := fs.String("transport", "tcp", "транспорт до TURN-реле: tcp (TCP/TLS, default) | udp")
|
||||
modeFlag := fs.String("mode", "udp", "режим туннеля: udp (UDP-релей для WireGuard, default) | tcp (TCP-форвардер для Xray/sing-box)")
|
||||
bondFlag := fs.Bool("bond", false, "распределять одно TCP-соединение по всем активным smux-сессиям (только с -mode tcp)")
|
||||
wrapMode := fs.Bool("obf", false, "маскировать TURN-payload под SRTP (RTP/opus + ChaCha20-Poly1305 AEAD) для обхода content-filter VK; ключ должен совпадать на клиенте и сервере")
|
||||
wrapKeyHex := fs.String("obf-key", "", "общий ключ для -obf, 32 байта в hex (64 символа)")
|
||||
genWrapKey := fs.Bool("gen-obf-key", false, "напечатать новый ключ для -obf-key и выйти")
|
||||
streamsPerCredFlag := fs.Int("streams-per-cred", defaultStreamsPerCache, "сколько TURN-потоков делят один кеш VK-учёток")
|
||||
debugFlag := fs.Bool("debug", false, "включить подробные debug-логи")
|
||||
manualCaptchaFlag := fs.Bool("manual-captcha", false, "пропустить авто-решение VK captcha и сразу открыть ручной режим в локальном браузере")
|
||||
dnsFlag := fs.String("dns", dnsModeAuto, "режим DNS-резолвинга: udp | doh | auto (auto: сначала UDP/53, sticky-fallback на DoH при полном отказе)")
|
||||
dnsFlag := fs.String("dns-mode", dnsModeAuto, "транспорт резолвера клиента: plain (UDP/53) | doh (DNS-over-HTTPS) | auto (UDP/53 → sticky DoH при отказе)")
|
||||
dnsServersFlag := fs.String("dns-servers", "", "список UDP/53 DNS-серверов через запятую вместо встроенных (напр. резолверы оператора из Android LinkProperties). Формат: ip[:port][,ip[:port]...]")
|
||||
|
||||
if err := fs.Parse(args); err != nil {
|
||||
@@ -138,15 +137,15 @@ func ParseClient(args []string, errOut io.Writer) (*Client, error) {
|
||||
TURN: TURNOpts{
|
||||
Host: *host,
|
||||
Port: *port,
|
||||
UDP: *udp,
|
||||
TransportUDP: *transportFlag == "udp",
|
||||
N: *n,
|
||||
},
|
||||
Obf: ObfOpts{
|
||||
WrapMode: *wrapMode,
|
||||
GenWrapKey: *genWrapKey,
|
||||
Mode: *wrapMode,
|
||||
GenKey: *genWrapKey,
|
||||
},
|
||||
Proxy: ProxyOpts{
|
||||
Mode: clientProxyMode(*vlessMode, *vlessBond),
|
||||
Mode: clientProxyMode(*modeFlag, *bondFlag),
|
||||
Listen: *listen,
|
||||
Peer: *peerAddr,
|
||||
},
|
||||
@@ -161,21 +160,34 @@ func ParseClient(args []string, errOut io.Writer) (*Client, error) {
|
||||
Debug: *debugFlag,
|
||||
},
|
||||
KCP: KCPOpts{
|
||||
Profile: kcptun.LoadProfileFromEnv(),
|
||||
FEC: kcptun.LoadFECFromEnv(),
|
||||
Profile: kcptun.DefaultProfile(),
|
||||
FEC: kcptun.FEC{},
|
||||
},
|
||||
}
|
||||
|
||||
switch c.DNS.Mode {
|
||||
case dnsModeUDP, dnsModeDoH, dnsModeAuto:
|
||||
switch *transportFlag {
|
||||
case "tcp", "udp":
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid -dns value %q: must be udp | doh | auto", c.DNS.Mode)
|
||||
return nil, fmt.Errorf("invalid -transport value %q: must be tcp | udp", *transportFlag)
|
||||
}
|
||||
switch *modeFlag {
|
||||
case "udp", "tcp":
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid -mode value %q: must be udp | tcp", *modeFlag)
|
||||
}
|
||||
if *bondFlag && *modeFlag != "tcp" {
|
||||
return nil, fmt.Errorf("-bond requires -mode tcp")
|
||||
}
|
||||
switch c.DNS.Mode {
|
||||
case dnsModePlain, dnsModeDoH, dnsModeAuto:
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid -dns-mode value %q: must be plain | doh | auto", c.DNS.Mode)
|
||||
}
|
||||
if *dnsServersFlag != "" {
|
||||
c.DNS.Servers = strings.Split(*dnsServersFlag, ",")
|
||||
}
|
||||
|
||||
if c.Obf.GenWrapKey {
|
||||
if c.Obf.GenKey {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
@@ -183,13 +195,13 @@ func ParseClient(args []string, errOut io.Writer) (*Client, error) {
|
||||
return nil, errors.New("need peer address")
|
||||
}
|
||||
if *vklink == "" {
|
||||
return nil, errors.New("need vk-link")
|
||||
return nil, errors.New("need -link")
|
||||
}
|
||||
key, err := srtpmimicry.DecodeKey(c.Obf.WrapMode, *wrapKeyHex)
|
||||
key, err := srtpmimicry.DecodeKey(c.Obf.Mode, *wrapKeyHex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Obf.WrapKey = key
|
||||
c.Obf.Key = key
|
||||
if c.VK.StreamsPerCred <= 0 {
|
||||
return nil, fmt.Errorf("-streams-per-cred must be positive")
|
||||
}
|
||||
@@ -216,10 +228,10 @@ func ParseServer(args []string, errOut io.Writer) (*Server, error) {
|
||||
|
||||
listen := fs.String("listen", "0.0.0.0:56000", "локальный адрес прослушивания ip:port")
|
||||
connect := fs.String("connect", "", "адрес локального бэкенда, host:port (обязательно: WireGuard 127.0.0.1:51820 или Xray 127.0.0.1:443)")
|
||||
vlessMode := fs.Bool("vless", false, "режим TCP-форвардера (VLESS/Xray) вместо UDP-релея для WireGuard; bond определяется автоматически по magic-префиксу в стриме")
|
||||
wrapMode := fs.Bool("wrap", false, "маскировать TURN-payload под SRTP (RTP/opus + ChaCha20-Poly1305 AEAD) для обхода content-filter VK; ключ должен совпадать с клиентом")
|
||||
wrapKeyHex := fs.String("wrap-key", "", "общий ключ для -wrap, 32 байта в hex (64 символа)")
|
||||
genWrapKey := fs.Bool("gen-wrap-key", false, "напечатать новый ключ для -wrap-key и выйти")
|
||||
modeFlag := fs.String("mode", "udp", "режим туннеля: udp (UDP-релей для WireGuard, default) | tcp (TCP-форвардер для Xray/sing-box; bond определяется автоматически)")
|
||||
wrapMode := fs.Bool("obf", false, "маскировать TURN-payload под SRTP (RTP/opus + ChaCha20-Poly1305 AEAD) для обхода content-filter VK; ключ должен совпадать с клиентом")
|
||||
wrapKeyHex := fs.String("obf-key", "", "общий ключ для -obf, 32 байта в hex (64 символа)")
|
||||
genWrapKey := fs.Bool("gen-obf-key", false, "напечатать новый ключ для -obf-key и выйти")
|
||||
debugFlag := fs.Bool("debug", false, "включить подробные debug-логи")
|
||||
|
||||
if err := fs.Parse(args); err != nil {
|
||||
@@ -228,11 +240,11 @@ func ParseServer(args []string, errOut io.Writer) (*Server, error) {
|
||||
|
||||
s := &Server{
|
||||
Obf: ObfOpts{
|
||||
WrapMode: *wrapMode,
|
||||
GenWrapKey: *genWrapKey,
|
||||
Mode: *wrapMode,
|
||||
GenKey: *genWrapKey,
|
||||
},
|
||||
Proxy: ProxyOpts{
|
||||
Mode: serverProxyMode(*vlessMode),
|
||||
Mode: serverProxyMode(*modeFlag),
|
||||
Listen: *listen,
|
||||
Connect: *connect,
|
||||
},
|
||||
@@ -240,40 +252,46 @@ func ParseServer(args []string, errOut io.Writer) (*Server, error) {
|
||||
Debug: *debugFlag,
|
||||
},
|
||||
KCP: KCPOpts{
|
||||
Profile: kcptun.LoadProfileFromEnv(),
|
||||
FEC: kcptun.LoadFECFromEnv(),
|
||||
Profile: kcptun.DefaultProfile(),
|
||||
FEC: kcptun.FEC{},
|
||||
},
|
||||
}
|
||||
|
||||
if s.Obf.GenWrapKey {
|
||||
switch *modeFlag {
|
||||
case "udp", "tcp":
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid -mode value %q: must be udp | tcp", *modeFlag)
|
||||
}
|
||||
|
||||
if s.Obf.GenKey {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
if s.Proxy.Connect == "" {
|
||||
return nil, fmt.Errorf("server address is required")
|
||||
}
|
||||
key, err := srtpmimicry.DecodeKey(s.Obf.WrapMode, *wrapKeyHex)
|
||||
key, err := srtpmimicry.DecodeKey(s.Obf.Mode, *wrapKeyHex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Obf.WrapKey = key
|
||||
s.Obf.Key = key
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func clientProxyMode(vless, bond bool) ProxyMode {
|
||||
func clientProxyMode(mode string, bond bool) ProxyMode {
|
||||
switch {
|
||||
case vless && bond:
|
||||
case mode == "tcp" && bond:
|
||||
return ProxyModeTCPFwdBond
|
||||
case vless:
|
||||
case mode == "tcp":
|
||||
return ProxyModeTCPFwd
|
||||
default:
|
||||
return ProxyModeUDP
|
||||
}
|
||||
}
|
||||
|
||||
func serverProxyMode(vless bool) ProxyMode {
|
||||
if vless {
|
||||
func serverProxyMode(mode string) ProxyMode {
|
||||
if mode == "tcp" {
|
||||
return ProxyModeTCPFwd
|
||||
}
|
||||
return ProxyModeUDP
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func validClientArgs() []string {
|
||||
return []string{
|
||||
"-peer", "1.2.3.4:5000",
|
||||
"-vk-link", "https://vk.com/call/join/abcdef",
|
||||
"-link", "https://vk.com/call/join/abcdef",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ func TestParseClient_Defaults(t *testing.T) {
|
||||
if c.VK.Link != "abcdef" {
|
||||
t.Errorf("VK.Link: %q (expected abcdef)", c.VK.Link)
|
||||
}
|
||||
if c.Obf.WrapKey != nil {
|
||||
t.Errorf("Obf.WrapKey should be nil when -wrap absent")
|
||||
if c.Obf.Key != nil {
|
||||
t.Errorf("Obf.Key should be nil when -obf absent")
|
||||
}
|
||||
if c.Proxy.Mode != ProxyModeUDP {
|
||||
t.Errorf("Proxy.Mode default: %q (expected udp)", c.Proxy.Mode)
|
||||
@@ -47,7 +47,7 @@ func TestParseClient_Defaults(t *testing.T) {
|
||||
func TestParseClient_VKLinkStrip(t *testing.T) {
|
||||
args := []string{
|
||||
"-peer", "1.2.3.4:5000",
|
||||
"-vk-link", "https://vk.com/call/join/CODE123?foo=bar",
|
||||
"-link", "https://vk.com/call/join/CODE123?foo=bar",
|
||||
}
|
||||
c, err := ParseClient(args, io.Discard)
|
||||
if err != nil {
|
||||
@@ -59,7 +59,7 @@ func TestParseClient_VKLinkStrip(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseClient_MissingPeer(t *testing.T) {
|
||||
_, err := ParseClient([]string{"-vk-link", "https://vk.com/call/join/X"}, io.Discard)
|
||||
_, err := ParseClient([]string{"-link", "https://vk.com/call/join/X"}, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "peer") {
|
||||
t.Errorf("expected peer error, got %v", err)
|
||||
}
|
||||
@@ -67,35 +67,43 @@ func TestParseClient_MissingPeer(t *testing.T) {
|
||||
|
||||
func TestParseClient_MissingVKLink(t *testing.T) {
|
||||
_, err := ParseClient([]string{"-peer", "1.2.3.4:5000"}, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "vk-link") {
|
||||
if err == nil || !strings.Contains(err.Error(), "-link") {
|
||||
t.Errorf("expected vk-link error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseClient_InvalidDNS(t *testing.T) {
|
||||
args := append(validClientArgs(), "-dns", "garbage")
|
||||
args := append(validClientArgs(), "-dns-mode", "garbage")
|
||||
_, err := ParseClient(args, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "invalid -dns") {
|
||||
if err == nil || !strings.Contains(err.Error(), "invalid -dns-mode") {
|
||||
t.Errorf("expected dns error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseClient_WrapMissingKey(t *testing.T) {
|
||||
args := append(validClientArgs(), "-wrap")
|
||||
func TestParseClient_BondWithoutTCPMode(t *testing.T) {
|
||||
args := append(validClientArgs(), "-bond")
|
||||
_, err := ParseClient(args, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "-wrap requires -wrap-key") {
|
||||
t.Errorf("expected wrap-key error, got %v", err)
|
||||
if err == nil || !strings.Contains(err.Error(), "-bond requires -mode tcp") {
|
||||
t.Errorf("expected bond error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseClient_WrapKeyOK(t *testing.T) {
|
||||
args := append(validClientArgs(), "-wrap", "-wrap-key", strings.Repeat("ab", 32))
|
||||
func TestParseClient_ObfMissingKey(t *testing.T) {
|
||||
args := append(validClientArgs(), "-obf")
|
||||
_, err := ParseClient(args, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "-obf requires -obf-key") {
|
||||
t.Errorf("expected obf-key error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseClient_ObfKeyOK(t *testing.T) {
|
||||
args := append(validClientArgs(), "-obf", "-obf-key", strings.Repeat("ab", 32))
|
||||
c, err := ParseClient(args, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(c.Obf.WrapKey) != 32 {
|
||||
t.Errorf("Obf.WrapKey len: %d", len(c.Obf.WrapKey))
|
||||
if len(c.Obf.Key) != 32 {
|
||||
t.Errorf("Obf.Key len: %d", len(c.Obf.Key))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,13 +137,13 @@ func TestParseClient_DNSServersSplit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseClient_GenWrapKeySkipsPeerCheck(t *testing.T) {
|
||||
c, err := ParseClient([]string{"-gen-wrap-key"}, io.Discard)
|
||||
func TestParseClient_GenObfKeySkipsPeerCheck(t *testing.T) {
|
||||
c, err := ParseClient([]string{"-gen-obf-key"}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatalf("gen-wrap-key should not require peer/vk-link: %v", err)
|
||||
t.Fatalf("gen-obf-key should not require peer/vk-link: %v", err)
|
||||
}
|
||||
if !c.Obf.GenWrapKey {
|
||||
t.Errorf("Obf.GenWrapKey not set")
|
||||
if !c.Obf.GenKey {
|
||||
t.Errorf("Obf.GenKey not set")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,9 +162,9 @@ func TestParseClient_ProxyMode(t *testing.T) {
|
||||
want ProxyMode
|
||||
}{
|
||||
{"default-udp", nil, ProxyModeUDP},
|
||||
{"vless", []string{"-vless"}, ProxyModeTCPFwd},
|
||||
{"vless-bond", []string{"-vless", "-vless-bond"}, ProxyModeTCPFwdBond},
|
||||
{"bond-without-vless-ignored", []string{"-vless-bond"}, ProxyModeUDP},
|
||||
{"tcp", []string{"-mode", "tcp"}, ProxyModeTCPFwd},
|
||||
{"tcp-bond", []string{"-mode", "tcp", "-bond"}, ProxyModeTCPFwdBond},
|
||||
{"default-udp-no-bond", nil, ProxyModeUDP},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -195,42 +203,42 @@ func TestParseServer_MissingConnect(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServer_WrapMissingKey(t *testing.T) {
|
||||
_, err := ParseServer([]string{"-connect", "x:1", "-wrap"}, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "-wrap requires -wrap-key") {
|
||||
t.Errorf("expected wrap-key error, got %v", err)
|
||||
func TestParseServer_ObfMissingKey(t *testing.T) {
|
||||
_, err := ParseServer([]string{"-connect", "x:1", "-obf"}, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "-obf requires -obf-key") {
|
||||
t.Errorf("expected obf-key error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServer_WrapKeyBadHex(t *testing.T) {
|
||||
_, err := ParseServer([]string{"-connect", "x:1", "-wrap", "-wrap-key", "zz"}, io.Discard)
|
||||
func TestParseServer_ObfKeyBadHex(t *testing.T) {
|
||||
_, err := ParseServer([]string{"-connect", "x:1", "-obf", "-obf-key", "zz"}, io.Discard)
|
||||
if err == nil || !strings.Contains(err.Error(), "invalid hex") {
|
||||
t.Errorf("expected hex error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServer_WrapKeyOK(t *testing.T) {
|
||||
s, err := ParseServer([]string{"-connect", "x:1", "-wrap", "-wrap-key", strings.Repeat("cd", 32)}, io.Discard)
|
||||
func TestParseServer_ObfKeyOK(t *testing.T) {
|
||||
s, err := ParseServer([]string{"-connect", "x:1", "-obf", "-obf-key", strings.Repeat("cd", 32)}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(s.Obf.WrapKey) != 32 {
|
||||
t.Errorf("Obf.WrapKey len: %d", len(s.Obf.WrapKey))
|
||||
if len(s.Obf.Key) != 32 {
|
||||
t.Errorf("Obf.Key len: %d", len(s.Obf.Key))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServer_GenWrapKeySkipsConnectCheck(t *testing.T) {
|
||||
s, err := ParseServer([]string{"-gen-wrap-key"}, io.Discard)
|
||||
func TestParseServer_GenObfKeySkipsConnectCheck(t *testing.T) {
|
||||
s, err := ParseServer([]string{"-gen-obf-key"}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatalf("gen-wrap-key should not require -connect: %v", err)
|
||||
t.Fatalf("gen-obf-key should not require -connect: %v", err)
|
||||
}
|
||||
if !s.Obf.GenWrapKey {
|
||||
t.Errorf("Obf.GenWrapKey not set")
|
||||
if !s.Obf.GenKey {
|
||||
t.Errorf("Obf.GenKey not set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServer_ProxyMode(t *testing.T) {
|
||||
s, err := ParseServer([]string{"-connect", "x:1", "-vless"}, io.Discard)
|
||||
s, err := ParseServer([]string{"-connect", "x:1", "-mode", "tcp"}, io.Discard)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func DialTURN(ctx context.Context, host, port string, udp bool, peer *net.UDPAdd
|
||||
return turndial.Open(ctx, turndial.Config{
|
||||
HostOverride: host,
|
||||
PortOverride: port,
|
||||
UDP: udp,
|
||||
TransportUDP: udp,
|
||||
}, peer, user, pass, rawURL)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ type Params struct {
|
||||
Host string
|
||||
Port string
|
||||
Link string
|
||||
UDP bool
|
||||
WrapKey []byte
|
||||
TransportUDP bool
|
||||
ObfKey []byte
|
||||
GetCreds GetCredsFunc
|
||||
KCPProfile kcptun.Profile
|
||||
KCPFEC kcptun.FEC
|
||||
@@ -38,7 +38,7 @@ type Params struct {
|
||||
// Реализация — internal/proxy/bondclient.
|
||||
type BondHandler func(ctx context.Context, tcpConn net.Conn, connID uint64, lanes []*PooledSession)
|
||||
|
||||
// Deps — зависимости хост-процесса для VLESS-цикла.
|
||||
// Deps — зависимости хост-процесса для TCP-forward цикла.
|
||||
type Deps struct {
|
||||
DTLSDialer *dtlsdial.Dialer
|
||||
Log logx.Logger
|
||||
@@ -52,7 +52,7 @@ func (d *Deps) log() logx.Logger {
|
||||
return d.Log
|
||||
}
|
||||
|
||||
// Run — точка входа VLESS-режима. Запускает numSessions maintainer-горутин, ждёт
|
||||
// Run — точка входа TCP-forward режима. Запускает numSessions maintainer-горутин, ждёт
|
||||
// первого подключения, затем принимает локальные TCP-соединения и форвардит
|
||||
// каждое как smux-поток (round-robin) или bonded по всем активным сессиям.
|
||||
func Run(ctx context.Context, deps *Deps, params *Params, peer *net.UDPAddr, listenAddr string, numSessions int, useBond bool) error {
|
||||
@@ -70,7 +70,7 @@ func Run(ctx context.Context, deps *Deps, params *Params, peer *net.UDPAddr, lis
|
||||
})
|
||||
}
|
||||
|
||||
deps.log().Infof("VLESS mode: waiting for sessions to connect (total: %d)...", numSessions)
|
||||
deps.log().Infof("TCP mode: waiting for sessions to connect (total: %d)...", numSessions)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
wgMaint.Wait()
|
||||
@@ -92,9 +92,9 @@ func Run(ctx context.Context, deps *Deps, params *Params, peer *net.UDPAddr, lis
|
||||
|
||||
context.AfterFunc(ctx, func() { _ = wrappedListener.Close() })
|
||||
if useBond {
|
||||
deps.log().Infof("VLESS bond mode: listening on %s (striping each TCP connection across active sessions)", listenAddr)
|
||||
deps.log().Infof("TCP bond mode: listening on %s (striping each TCP connection across active sessions)", listenAddr)
|
||||
} else {
|
||||
deps.log().Infof("VLESS mode: listening on %s (round-robin across %d sessions)", listenAddr, numSessions)
|
||||
deps.log().Infof("TCP mode: listening on %s (round-robin across %d sessions)", listenAddr, numSessions)
|
||||
}
|
||||
|
||||
var wgConn sync.WaitGroup
|
||||
@@ -232,7 +232,7 @@ func createSmuxSession(ctx context.Context, deps *Deps, params *Params, peer *ne
|
||||
}
|
||||
}
|
||||
|
||||
stream, err := common.DialTURN(ctx, params.Host, params.Port, params.UDP, peer, params.Link, id, params.GetCreds)
|
||||
stream, err := common.DialTURN(ctx, params.Host, params.Port, params.TransportUDP, peer, params.Link, id, params.GetCreds)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -241,7 +241,7 @@ func createSmuxSession(ctx context.Context, deps *Deps, params *Params, peer *ne
|
||||
deps.log().Debugf("[session %d] TURN server IP: %s", id, stream.ServerUDPAddr.IP)
|
||||
deps.log().Debugf("relayed-address=%s", relayConn.LocalAddr().String())
|
||||
|
||||
relayWC, err := common.NewClientWrap(params.WrapKey)
|
||||
relayWC, err := common.NewClientWrap(params.ObfKey)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return nil, nil, fmt.Errorf("wrap init: %w", err)
|
||||
@@ -258,7 +258,7 @@ func createSmuxSession(ctx context.Context, deps *Deps, params *Params, peer *ne
|
||||
statsCtx, statsCancel := context.WithCancel(ctx)
|
||||
cleanupFns = append(cleanupFns, statsCancel)
|
||||
st := stats.New(deps.log().DebugEnabled())
|
||||
go st.LogEvery(statsCtx, deps.log().Debugf, fmt.Sprintf("[session %d] VLESS", id), "to-turn", "from-turn")
|
||||
go st.LogEvery(statsCtx, deps.log().Debugf, fmt.Sprintf("[session %d] TCP", id), "to-turn", "from-turn")
|
||||
|
||||
kcpSess, err := kcptun.NewKCPOverDTLS(&stats.CountingConn{Conn: dtlsConn, Stats: st}, false, params.KCPProfile, params.KCPFEC)
|
||||
if err != nil {
|
||||
|
||||
@@ -218,7 +218,7 @@ func oneTURN(ctx context.Context, deps *Deps, params *Params, peer *net.UDPAddr,
|
||||
err = ctx.Err()
|
||||
return
|
||||
}
|
||||
stream, err1 := common.DialTURN(ctx, params.Host, params.Port, params.UDP, peer, params.Link, streamID, params.GetCreds)
|
||||
stream, err1 := common.DialTURN(ctx, params.Host, params.Port, params.TransportUDP, peer, params.Link, streamID, params.GetCreds)
|
||||
if err1 != nil {
|
||||
if deps.Auth.IsAuthError(err1) {
|
||||
deps.Auth.HandleAuthError(streamID)
|
||||
@@ -254,7 +254,7 @@ func oneTURN(ctx context.Context, deps *Deps, params *Params, peer *net.UDPAddr,
|
||||
}
|
||||
})
|
||||
var internalPipeAddr atomic.Value
|
||||
wc, wcErr := common.NewClientWrap(params.WrapKey)
|
||||
wc, wcErr := common.NewClientWrap(params.ObfKey)
|
||||
if wcErr != nil {
|
||||
deps.log().Errorf("[STREAM %d] WRAP init failed: %v", streamID, wcErr)
|
||||
turncancel()
|
||||
|
||||
@@ -36,8 +36,8 @@ type Params struct {
|
||||
Host string
|
||||
Port string
|
||||
Link string
|
||||
UDP bool
|
||||
WrapKey []byte
|
||||
TransportUDP bool
|
||||
ObfKey []byte
|
||||
GetCreds GetCredsFunc
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@ package kcptun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xtaci/kcp-go/v5"
|
||||
@@ -43,75 +40,6 @@ func DefaultProfile() Profile {
|
||||
}
|
||||
}
|
||||
|
||||
// LoadProfileFromEnv читает VK_TURN_KCP_PROFILE и per-field переопределения
|
||||
// VK_TURN_KCP_*. Неизвестное имя профиля → DefaultProfile.
|
||||
func LoadProfileFromEnv() Profile {
|
||||
name := strings.ToLower(strings.TrimSpace(os.Getenv("VK_TURN_KCP_PROFILE")))
|
||||
var p Profile
|
||||
switch name {
|
||||
case "legacy", "fast":
|
||||
p = Profile{NoDelay: 1, Interval: 10, Resend: 2, NC: 1, SndWnd: 4096, RcvWnd: 4096, MTU: 1280, ACKNoDelay: true}
|
||||
case "cc", "balanced":
|
||||
p = Profile{NoDelay: 1, Interval: 20, Resend: 2, NC: 0, SndWnd: 512, RcvWnd: 512, MTU: 1200, ACKNoDelay: true}
|
||||
case "slow", "conservative":
|
||||
p = Profile{NoDelay: 0, Interval: 40, Resend: 2, NC: 0, SndWnd: 256, RcvWnd: 256, MTU: 1150, ACKNoDelay: false}
|
||||
default:
|
||||
p = DefaultProfile()
|
||||
}
|
||||
p.NoDelay = envInt("VK_TURN_KCP_NODELAY", p.NoDelay)
|
||||
p.Interval = envInt("VK_TURN_KCP_INTERVAL", p.Interval)
|
||||
p.Resend = envInt("VK_TURN_KCP_RESEND", p.Resend)
|
||||
p.NC = envInt("VK_TURN_KCP_NC", p.NC)
|
||||
p.SndWnd = envInt("VK_TURN_KCP_SNDWND", p.SndWnd)
|
||||
p.RcvWnd = envInt("VK_TURN_KCP_RCVWND", p.RcvWnd)
|
||||
p.MTU = envInt("VK_TURN_KCP_MTU", p.MTU)
|
||||
p.ACKNoDelay = envBool("VK_TURN_KCP_ACK_NODELAY", p.ACKNoDelay)
|
||||
return p
|
||||
}
|
||||
|
||||
// LoadFECFromEnv парсит VK_TURN_KCP_FEC как "data:parity" (напр. "10:3").
|
||||
// Пустое/невалидное → отключено.
|
||||
func LoadFECFromEnv() FEC {
|
||||
raw := strings.TrimSpace(os.Getenv("VK_TURN_KCP_FEC"))
|
||||
if raw == "" {
|
||||
return FEC{}
|
||||
}
|
||||
parts := strings.SplitN(raw, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return FEC{}
|
||||
}
|
||||
d, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
|
||||
p, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
|
||||
if err1 != nil || err2 != nil || d <= 0 || p <= 0 {
|
||||
return FEC{}
|
||||
}
|
||||
return FEC{Data: d, Parity: p}
|
||||
}
|
||||
|
||||
func envInt(name string, fallback int) int {
|
||||
raw := strings.TrimSpace(os.Getenv(name))
|
||||
if raw == "" {
|
||||
return fallback
|
||||
}
|
||||
value, err := strconv.Atoi(raw)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func envBool(name string, fallback bool) bool {
|
||||
raw := strings.ToLower(strings.TrimSpace(os.Getenv(name)))
|
||||
switch raw {
|
||||
case "1", "true", "yes", "on":
|
||||
return true
|
||||
case "0", "false", "no", "off":
|
||||
return false
|
||||
default:
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
// DtlsPacketConn оборачивает net.Conn (DTLS) как net.PacketConn для KCP.
|
||||
// Каждый DTLS Read/Write сохраняет границы сообщений (datagram семантика).
|
||||
type DtlsPacketConn struct {
|
||||
|
||||
@@ -24,8 +24,8 @@ type Config struct {
|
||||
HostOverride string
|
||||
// PortOverride, если непустой, заменяет port из lookup credentials.
|
||||
PortOverride string
|
||||
// UDP=true — dial TURN по UDP; иначе по TCP через STUNConn.
|
||||
UDP bool
|
||||
// TransportUDP=true — dial TURN по UDP; иначе по TCP через STUNConn.
|
||||
TransportUDP bool
|
||||
// DialTimeout ограничивает TCP dial. Ноль → 5s.
|
||||
DialTimeout time.Duration
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func Open(ctx context.Context, cfg Config, peer *net.UDPAddr, user, pass, rawAdd
|
||||
turnConn net.PacketConn
|
||||
closeConn func() error
|
||||
)
|
||||
if cfg.UDP {
|
||||
if cfg.TransportUDP {
|
||||
c, derr := net.DialUDP("udp", nil, turnServerUDPAddr) //nolint:noctx
|
||||
if derr != nil {
|
||||
return nil, fmt.Errorf("dial TURN (udp): %w", derr)
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestOpen_HostOverrideApplied(t *testing.T) {
|
||||
peer := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 1}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
_, err := Open(ctx, Config{HostOverride: "127.0.0.1", PortOverride: "1", UDP: false, DialTimeout: 200 * time.Millisecond}, peer, "u", "p", "8.8.8.8:443")
|
||||
_, err := Open(ctx, Config{HostOverride: "127.0.0.1", PortOverride: "1", TransportUDP: false,DialTimeout: 200 * time.Millisecond}, peer, "u", "p", "8.8.8.8:443")
|
||||
if err == nil {
|
||||
t.Fatal("expected dial error against unreachable :1")
|
||||
}
|
||||
|
||||
@@ -190,14 +190,14 @@ func DecodeKey(enabled bool, raw string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
if raw == "" {
|
||||
return nil, errors.New("-wrap requires -wrap-key")
|
||||
return nil, errors.New("-obf requires -obf-key")
|
||||
}
|
||||
key, err := hex.DecodeString(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("-wrap-key invalid hex: %w", err)
|
||||
return nil, fmt.Errorf("-obf-key invalid hex: %w", err)
|
||||
}
|
||||
if len(key) != KeyLen {
|
||||
return nil, fmt.Errorf("-wrap-key must decode to %d bytes (got %d)", KeyLen, len(key))
|
||||
return nil, fmt.Errorf("-obf-key must decode to %d bytes (got %d)", KeyLen, len(key))
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user