修复无法正常使用的严重问题
This commit is contained in:
56
.gitignore
vendored
56
.gitignore
vendored
@@ -1,23 +1,35 @@
|
||||
/.idea/
|
||||
/vendor/
|
||||
/*.json
|
||||
/*.srs
|
||||
/*.db
|
||||
/site/
|
||||
/bin/
|
||||
/dist/
|
||||
/sing-box
|
||||
/sing-box.exe
|
||||
/build/
|
||||
/*.jar
|
||||
/*.aar
|
||||
/*.xcframework/
|
||||
/experimental/libbox/*.aar
|
||||
/experimental/libbox/*.xcframework/
|
||||
/experimental/libbox/*.nupkg
|
||||
# Binaries
|
||||
sing-box
|
||||
sing-box.exe
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# Build & Cache
|
||||
go.sum
|
||||
bin/
|
||||
dist/
|
||||
/var/lib/sing-box/*
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
/var/log/sing-box/*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
/config.d/
|
||||
/venv/
|
||||
CLAUDE.md
|
||||
AGENTS.md
|
||||
/.claude/
|
||||
Thumbs.db
|
||||
|
||||
# Antigravity/Gemini Artifacts
|
||||
.gemini/
|
||||
artifacts/
|
||||
scratch/
|
||||
implementation_plan*.md
|
||||
walkthrough*.md
|
||||
task.md
|
||||
|
||||
V2bX/
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -83,34 +85,87 @@ type XBoardServiceOptions struct {
|
||||
}
|
||||
|
||||
type XNodeConfig struct {
|
||||
NodeType string `json:"node_type"`
|
||||
NodeType_ string `json:"nodeType"`
|
||||
ServerConfig json.RawMessage `json:"server_config"`
|
||||
ServerConfig_ json.RawMessage `json:"serverConfig"`
|
||||
Config json.RawMessage `json:"config"`
|
||||
ListenIP string `json:"listen_ip"`
|
||||
Port int `json:"port"`
|
||||
ServerPort int `json:"server_port"`
|
||||
Protocol string `json:"protocol"`
|
||||
Cipher string `json:"cipher"`
|
||||
ServerKey string `json:"server_key"`
|
||||
TLS int `json:"tls"`
|
||||
Flow string `json:"flow"`
|
||||
TLSSettings *XTLSSettings `json:"tls_settings"`
|
||||
}
|
||||
|
||||
type XInnerConfig struct {
|
||||
NodeType string `json:"node_type"`
|
||||
NodeType_ string `json:"nodeType"`
|
||||
ServerConfig json.RawMessage `json:"server_config"`
|
||||
ServerConfig_ json.RawMessage `json:"serverConfig"`
|
||||
Config json.RawMessage `json:"config"`
|
||||
ListenIP string `json:"listen_ip"`
|
||||
Port int `json:"port"`
|
||||
ServerPort int `json:"server_port"`
|
||||
Protocol string `json:"protocol"`
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
StreamSettings json.RawMessage `json:"streamSettings"`
|
||||
Cipher string `json:"cipher"`
|
||||
ServerKey string `json:"server_key"`
|
||||
TLS int `json:"tls"`
|
||||
Flow string `json:"flow"`
|
||||
TLSSettings *XTLSSettings `json:"tls_settings"`
|
||||
TLSSettings_ *XTLSSettings `json:"tlsSettings"`
|
||||
Network string `json:"network"`
|
||||
NetworkSettings json.RawMessage `json:"network_settings"`
|
||||
NetworkSettings_ json.RawMessage `json:"networkSettings"`
|
||||
|
||||
// Hysteria / Hysteria2
|
||||
UpMbps int `json:"up_mbps"`
|
||||
DownMbps int `json:"down_mbps"`
|
||||
Obfs string `json:"obfs"`
|
||||
ObfsPassword string `json:"obfs-password"`
|
||||
Ignore_Client_Bandwidth bool `json:"ignore_client_bandwidth"`
|
||||
|
||||
// Tuic
|
||||
CongestionControl string `json:"congestion_control"`
|
||||
ZeroRTTHandshake bool `json:"zero_rtt_handshake"`
|
||||
|
||||
// AnyTls
|
||||
PaddingScheme []string `json:"padding_scheme"`
|
||||
}
|
||||
|
||||
type XInnerConfig struct {
|
||||
ListenIP string `json:"listen_ip"`
|
||||
Port int `json:"port"`
|
||||
ServerPort int `json:"server_port"`
|
||||
Protocol string `json:"protocol"`
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
StreamSettings json.RawMessage `json:"streamSettings"`
|
||||
Cipher string `json:"cipher"`
|
||||
ServerKey string `json:"server_key"`
|
||||
TLS int `json:"tls"`
|
||||
Flow string `json:"flow"`
|
||||
TLSSettings *XTLSSettings `json:"tls_settings"`
|
||||
TLSSettings_ *XTLSSettings `json:"tlsSettings"`
|
||||
Network string `json:"network"`
|
||||
NetworkSettings json.RawMessage `json:"network_settings"`
|
||||
NetworkSettings_ json.RawMessage `json:"networkSettings"`
|
||||
}
|
||||
|
||||
type HttpNetworkConfig struct {
|
||||
Header struct {
|
||||
Type string `json:"type"`
|
||||
Request *json.RawMessage `json:"request"`
|
||||
Response *json.RawMessage `json:"response"`
|
||||
} `json:"header"`
|
||||
}
|
||||
|
||||
type HttpRequest struct {
|
||||
Version string `json:"version"`
|
||||
Method string `json:"method"`
|
||||
Path []string `json:"path"`
|
||||
Headers struct {
|
||||
Host []string `json:"Host"`
|
||||
} `json:"headers"`
|
||||
}
|
||||
|
||||
type WsNetworkConfig struct {
|
||||
Path string `json:"path"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
}
|
||||
|
||||
type GrpcNetworkConfig struct {
|
||||
ServiceName string `json:"serviceName"`
|
||||
}
|
||||
|
||||
type HttpupgradeNetworkConfig struct {
|
||||
Path string `json:"path"`
|
||||
Host string `json:"host"`
|
||||
}
|
||||
|
||||
type XTLSSettings struct {
|
||||
@@ -121,6 +176,7 @@ type XTLSSettings struct {
|
||||
ShortID string `json:"short_id"`
|
||||
ShortIDs []string `json:"short_ids"`
|
||||
AllowInsecure bool `json:"allow_insecure"`
|
||||
Dest string `json:"dest"`
|
||||
}
|
||||
|
||||
type XRealitySettings struct {
|
||||
@@ -227,6 +283,99 @@ func (s *Service) Start(stage adapter.StartStage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getInboundTransport(network string, settings json.RawMessage) (*option.V2RayTransportOptions, error) {
|
||||
if network == "" {
|
||||
return nil, nil
|
||||
}
|
||||
t := &option.V2RayTransportOptions{
|
||||
Type: network,
|
||||
}
|
||||
switch network {
|
||||
case "tcp":
|
||||
if len(settings) != 0 {
|
||||
var networkConfig HttpNetworkConfig
|
||||
err := json.Unmarshal(settings, &networkConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode NetworkSettings error: %s", err)
|
||||
}
|
||||
if networkConfig.Header.Type == "http" {
|
||||
t.Type = networkConfig.Header.Type
|
||||
if networkConfig.Header.Request != nil {
|
||||
var request HttpRequest
|
||||
err = json.Unmarshal(*networkConfig.Header.Request, &request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode HttpRequest error: %s", err)
|
||||
}
|
||||
t.HTTPOptions.Host = request.Headers.Host
|
||||
if len(request.Path) > 0 {
|
||||
t.HTTPOptions.Path = request.Path[0]
|
||||
}
|
||||
t.HTTPOptions.Method = request.Method
|
||||
}
|
||||
} else {
|
||||
t.Type = ""
|
||||
}
|
||||
} else {
|
||||
t.Type = ""
|
||||
}
|
||||
case "ws":
|
||||
var (
|
||||
path string
|
||||
ed int
|
||||
headers map[string]badoption.Listable[string]
|
||||
)
|
||||
if len(settings) != 0 {
|
||||
var networkConfig WsNetworkConfig
|
||||
err := json.Unmarshal(settings, &networkConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode NetworkSettings error: %s", err)
|
||||
}
|
||||
u, err := url.Parse(networkConfig.Path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse WS path error: %s", err)
|
||||
}
|
||||
path = u.Path
|
||||
ed, _ = strconv.Atoi(u.Query().Get("ed"))
|
||||
if len(networkConfig.Headers) > 0 {
|
||||
headers = make(map[string]badoption.Listable[string], len(networkConfig.Headers))
|
||||
for k, v := range networkConfig.Headers {
|
||||
headers[k] = badoption.Listable[string]{v}
|
||||
}
|
||||
}
|
||||
}
|
||||
t.WebsocketOptions = option.V2RayWebsocketOptions{
|
||||
Path: path,
|
||||
EarlyDataHeaderName: "Sec-WebSocket-Protocol",
|
||||
MaxEarlyData: uint32(ed),
|
||||
Headers: headers,
|
||||
}
|
||||
case "grpc":
|
||||
if len(settings) != 0 {
|
||||
var networkConfig GrpcNetworkConfig
|
||||
err := json.Unmarshal(settings, &networkConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode gRPC settings error: %s", err)
|
||||
}
|
||||
t.GRPCOptions = option.V2RayGRPCOptions{
|
||||
ServiceName: networkConfig.ServiceName,
|
||||
}
|
||||
}
|
||||
case "httpupgrade":
|
||||
if len(settings) != 0 {
|
||||
var networkConfig HttpupgradeNetworkConfig
|
||||
err := json.Unmarshal(settings, &networkConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode HttpUpgrade settings error: %s", err)
|
||||
}
|
||||
t.HTTPUpgradeOptions = option.V2RayHTTPUpgradeOptions{
|
||||
Path: networkConfig.Path,
|
||||
Host: networkConfig.Host,
|
||||
}
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (s *Service) setupNode() error {
|
||||
s.logger.Info("Xboard fetching node config...")
|
||||
config, err := s.fetchConfig()
|
||||
@@ -235,8 +384,8 @@ func (s *Service) setupNode() error {
|
||||
}
|
||||
|
||||
inboundTag := "xboard-inbound"
|
||||
|
||||
// Resolve nested config
|
||||
|
||||
// Resolve nested config (V2bX compatibility: server_config / serverConfig / config)
|
||||
var inner XInnerConfig
|
||||
if len(config.ServerConfig) > 0 {
|
||||
json.Unmarshal(config.ServerConfig, &inner)
|
||||
@@ -245,25 +394,29 @@ func (s *Service) setupNode() error {
|
||||
} else if len(config.Config) > 0 {
|
||||
json.Unmarshal(config.Config, &inner)
|
||||
}
|
||||
|
||||
// Fallback to flat if still empty
|
||||
|
||||
// Fallback flat fields from top-level config to inner
|
||||
if inner.ListenIP == "" {
|
||||
inner.ListenIP = config.ListenIP
|
||||
}
|
||||
if inner.ListenIP == "" {
|
||||
inner.ListenIP = "0.0.0.0"
|
||||
}
|
||||
|
||||
if inner.TLSSettings == nil {
|
||||
inner.TLSSettings = config.TLSSettings
|
||||
}
|
||||
if inner.TLSSettings == nil {
|
||||
inner.TLSSettings = config.TLSSettings_
|
||||
}
|
||||
if inner.TLSSettings_ == nil {
|
||||
inner.TLSSettings_ = config.TLSSettings_
|
||||
}
|
||||
if inner.TLS == 0 {
|
||||
inner.TLS = config.TLS
|
||||
}
|
||||
if inner.Flow == "" {
|
||||
inner.Flow = config.Flow
|
||||
}
|
||||
|
||||
if inner.Protocol == "" {
|
||||
inner.Protocol = config.Protocol
|
||||
}
|
||||
@@ -280,7 +433,17 @@ func (s *Service) setupNode() error {
|
||||
if inner.ServerKey == "" {
|
||||
inner.ServerKey = config.ServerKey
|
||||
}
|
||||
if inner.Network == "" {
|
||||
inner.Network = config.Network
|
||||
}
|
||||
if len(inner.NetworkSettings) == 0 {
|
||||
inner.NetworkSettings = config.NetworkSettings
|
||||
}
|
||||
if len(inner.NetworkSettings_) == 0 {
|
||||
inner.NetworkSettings_ = config.NetworkSettings_
|
||||
}
|
||||
|
||||
// Resolve protocol
|
||||
protocol := inner.Protocol
|
||||
if protocol == "" {
|
||||
protocol = config.NodeType
|
||||
@@ -288,12 +451,11 @@ func (s *Service) setupNode() error {
|
||||
if protocol == "" {
|
||||
protocol = config.NodeType_
|
||||
}
|
||||
|
||||
if protocol == "" {
|
||||
s.logger.Error("Xboard setup error: could not identify protocol. Please check debug logs for raw JSON.")
|
||||
return fmt.Errorf("unsupported protocol: empty")
|
||||
}
|
||||
|
||||
|
||||
s.logger.Info("Xboard protocol identified: ", protocol)
|
||||
|
||||
var listenAddr badoption.Addr
|
||||
@@ -302,82 +464,134 @@ func (s *Service) setupNode() error {
|
||||
} else {
|
||||
listenAddr = badoption.Addr(netip.IPv4Unspecified())
|
||||
}
|
||||
|
||||
var tlsOptions *option.InboundTLSOptions
|
||||
if inner.TLS > 0 && inner.TLSSettings != nil {
|
||||
tlsOptions = &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: inner.TLSSettings.ServerName,
|
||||
|
||||
listen := option.ListenOptions{
|
||||
Listen: &listenAddr,
|
||||
ListenPort: uint16(inner.Port),
|
||||
}
|
||||
|
||||
// ── TLS / Reality handling (matching V2bX panel.Security constants) ──
|
||||
// V2bX: 0=None, 1=TLS, 2=Reality
|
||||
var tlsOptions option.InboundTLSOptions
|
||||
securityType := inner.TLS
|
||||
tlsSettings := inner.TLSSettings
|
||||
if tlsSettings == nil {
|
||||
tlsSettings = inner.TLSSettings_
|
||||
}
|
||||
|
||||
switch securityType {
|
||||
case 1: // TLS
|
||||
tlsOptions.Enabled = true
|
||||
if tlsSettings != nil {
|
||||
tlsOptions.ServerName = tlsSettings.ServerName
|
||||
}
|
||||
if inner.TLS == 2 { // Reality
|
||||
shortIDs := inner.TLSSettings.ShortIDs
|
||||
if len(shortIDs) == 0 && inner.TLSSettings.ShortID != "" {
|
||||
shortIDs = []string{inner.TLSSettings.ShortID}
|
||||
case 2: // Reality
|
||||
if tlsSettings != nil {
|
||||
tlsOptions.Enabled = true
|
||||
tlsOptions.ServerName = tlsSettings.ServerName
|
||||
shortIDs := tlsSettings.ShortIDs
|
||||
if len(shortIDs) == 0 && tlsSettings.ShortID != "" {
|
||||
shortIDs = []string{tlsSettings.ShortID}
|
||||
}
|
||||
dest := tlsSettings.Dest
|
||||
if dest == "" {
|
||||
dest = tlsSettings.ServerName
|
||||
}
|
||||
if dest == "" {
|
||||
dest = "www.microsoft.com"
|
||||
}
|
||||
serverPort := uint16(443)
|
||||
if tlsSettings.ServerPort != "" {
|
||||
if port, err := strconv.Atoi(tlsSettings.ServerPort); err == nil && port > 0 {
|
||||
serverPort = uint16(port)
|
||||
}
|
||||
}
|
||||
tlsOptions.Reality = &option.InboundRealityOptions{
|
||||
Enabled: true,
|
||||
Handshake: option.InboundRealityHandshakeOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: inner.TLSSettings.ServerName,
|
||||
ServerPort: 443,
|
||||
Server: dest,
|
||||
ServerPort: serverPort,
|
||||
},
|
||||
},
|
||||
PrivateKey: inner.TLSSettings.PrivateKey,
|
||||
PrivateKey: tlsSettings.PrivateKey,
|
||||
ShortID: badoption.Listable[string](shortIDs),
|
||||
}
|
||||
// Fallback if empty
|
||||
if tlsOptions.Reality.Handshake.Server == "" {
|
||||
tlsOptions.Reality.Handshake.Server = "www.microsoft.com"
|
||||
}
|
||||
s.logger.Info("Xboard REALITY configured. Dest: ", dest, ":", serverPort)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Also check streamSettings for Reality (legacy Xboard format)
|
||||
if inner.StreamSettings != nil && securityType == 0 {
|
||||
var streamSettings XStreamSettings
|
||||
json.Unmarshal(inner.StreamSettings, &streamSettings)
|
||||
reality := streamSettings.GetReality()
|
||||
if streamSettings.Security == "reality" && reality != nil {
|
||||
serverNames := reality.GetServerNames()
|
||||
serverName := ""
|
||||
if len(serverNames) > 0 {
|
||||
serverName = serverNames[0]
|
||||
}
|
||||
tlsOptions = option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: serverName,
|
||||
Reality: &option.InboundRealityOptions{
|
||||
Enabled: true,
|
||||
Handshake: option.InboundRealityHandshakeOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: reality.Dest,
|
||||
ServerPort: 443,
|
||||
},
|
||||
},
|
||||
PrivateKey: reality.GetPrivateKey(),
|
||||
ShortID: badoption.Listable[string](reality.GetShortIds()),
|
||||
},
|
||||
}
|
||||
securityType = 2
|
||||
s.logger.Info("Xboard REALITY config from streamSettings")
|
||||
}
|
||||
}
|
||||
|
||||
// ── Resolve network transport settings (V2bX style) ──
|
||||
networkType := inner.Network
|
||||
networkSettings := inner.NetworkSettings
|
||||
if len(networkSettings) == 0 {
|
||||
networkSettings = inner.NetworkSettings_
|
||||
}
|
||||
|
||||
// ── Build inbound per protocol (matching V2bX core/sing/node.go) ──
|
||||
var inboundOptions any
|
||||
switch protocol {
|
||||
case "vless":
|
||||
vlessOptions := option.VLESSInboundOptions{}
|
||||
vlessOptions.Listen = &listenAddr
|
||||
vlessOptions.ListenPort = uint16(inner.Port)
|
||||
vlessOptions.TLS = tlsOptions
|
||||
case "vmess", "vless":
|
||||
// Transport for vmess/vless
|
||||
transport, err := getInboundTransport(networkType, networkSettings)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build transport for %s: %w", protocol, err)
|
||||
}
|
||||
|
||||
// Handle Reality
|
||||
if inner.StreamSettings != nil {
|
||||
var streamSettings XStreamSettings
|
||||
json.Unmarshal(inner.StreamSettings, &streamSettings)
|
||||
reality := streamSettings.GetReality()
|
||||
if streamSettings.Security == "reality" && reality != nil {
|
||||
serverNames := reality.GetServerNames()
|
||||
serverName := ""
|
||||
if len(serverNames) > 0 {
|
||||
serverName = serverNames[0]
|
||||
}
|
||||
vlessOptions.TLS = &option.InboundTLSOptions{
|
||||
Enabled: true,
|
||||
ServerName: serverName,
|
||||
Reality: &option.InboundRealityOptions{
|
||||
Enabled: true,
|
||||
Handshake: option.InboundRealityHandshakeOptions{
|
||||
ServerOptions: option.ServerOptions{
|
||||
Server: reality.Dest,
|
||||
ServerPort: 443,
|
||||
},
|
||||
},
|
||||
PrivateKey: reality.GetPrivateKey(),
|
||||
ShortID: badoption.Listable[string](reality.GetShortIds()),
|
||||
},
|
||||
}
|
||||
s.logger.Info("Xboard REALITY config from streamSettings. PrivateKey preview: ", reality.GetPrivateKey()[:4], "...")
|
||||
if protocol == "vless" {
|
||||
opts := &option.VLESSInboundOptions{
|
||||
ListenOptions: listen,
|
||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||
TLS: &tlsOptions,
|
||||
},
|
||||
}
|
||||
if transport != nil {
|
||||
opts.Transport = transport
|
||||
}
|
||||
inboundOptions = opts
|
||||
} else {
|
||||
opts := &option.VMessInboundOptions{
|
||||
ListenOptions: listen,
|
||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||
TLS: &tlsOptions,
|
||||
},
|
||||
}
|
||||
if transport != nil {
|
||||
opts.Transport = transport
|
||||
}
|
||||
inboundOptions = opts
|
||||
}
|
||||
inboundOptions = &vlessOptions
|
||||
case "vmess":
|
||||
vmessOptions := option.VMessInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: &listenAddr,
|
||||
ListenPort: uint16(inner.Port),
|
||||
},
|
||||
}
|
||||
inboundOptions = &vmessOptions
|
||||
case "shadowsocks":
|
||||
method := inner.Cipher
|
||||
serverKey := inner.ServerKey
|
||||
@@ -387,7 +601,7 @@ func (s *Service) setupNode() error {
|
||||
if method == "" {
|
||||
method = "aes-256-gcm"
|
||||
}
|
||||
// Hardening for Shadowsocks 2022
|
||||
// V2bX SS2022 key handling
|
||||
if len(method) >= 4 && method[:4] == "2022" {
|
||||
keyLen := 32
|
||||
if len(method) >= 18 && method[13:16] == "128" {
|
||||
@@ -395,27 +609,117 @@ func (s *Service) setupNode() error {
|
||||
}
|
||||
serverKey = fixSSKey(serverKey, keyLen)
|
||||
}
|
||||
ssOptions := option.ShadowsocksInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: &listenAddr,
|
||||
ListenPort: uint16(inner.Port),
|
||||
},
|
||||
Method: method,
|
||||
Password: serverKey,
|
||||
|
||||
ssOptions := &option.ShadowsocksInboundOptions{
|
||||
ListenOptions: listen,
|
||||
Method: method,
|
||||
Password: serverKey,
|
||||
}
|
||||
inboundOptions = ssOptions
|
||||
if len(serverKey) >= 8 {
|
||||
s.logger.Info("Xboard Shadowsocks setup. Method: ", method, " PSK preview: ", serverKey[:8], "...")
|
||||
} else {
|
||||
s.logger.Info("Xboard Shadowsocks setup. Method: ", method)
|
||||
}
|
||||
|
||||
// If no colon is used in client, we might need a fallback.
|
||||
// We'll leave it to be updated dynamically when users sync.
|
||||
inboundOptions = &ssOptions
|
||||
s.logger.Info("Xboard Shadowsocks 2022 setup. Method: ", method, " PSK preview: ", serverKey[:8], "...")
|
||||
case "trojan":
|
||||
trojanOptions := option.TrojanInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: &listenAddr,
|
||||
ListenPort: uint16(inner.Port),
|
||||
// Trojan supports ws/grpc transport like V2bX
|
||||
transport, err := getInboundTransport(networkType, networkSettings)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build transport for trojan: %w", err)
|
||||
}
|
||||
|
||||
opts := &option.TrojanInboundOptions{
|
||||
ListenOptions: listen,
|
||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||
TLS: &tlsOptions,
|
||||
},
|
||||
}
|
||||
inboundOptions = &trojanOptions
|
||||
if transport != nil {
|
||||
opts.Transport = transport
|
||||
}
|
||||
inboundOptions = opts
|
||||
case "tuic":
|
||||
// V2bX: TUIC always uses TLS with h3 ALPN
|
||||
tuicTLS := tlsOptions
|
||||
tuicTLS.Enabled = true
|
||||
tuicTLS.ALPN = append(tuicTLS.ALPN, "h3")
|
||||
|
||||
congestionControl := config.CongestionControl
|
||||
if congestionControl == "" {
|
||||
congestionControl = "bbr"
|
||||
}
|
||||
|
||||
opts := &option.TUICInboundOptions{
|
||||
ListenOptions: listen,
|
||||
CongestionControl: congestionControl,
|
||||
ZeroRTTHandshake: config.ZeroRTTHandshake,
|
||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||
TLS: &tuicTLS,
|
||||
},
|
||||
}
|
||||
inboundOptions = opts
|
||||
s.logger.Info("Xboard TUIC configured. CongestionControl: ", congestionControl)
|
||||
case "hysteria":
|
||||
// V2bX: Hysteria always uses TLS
|
||||
hyTLS := tlsOptions
|
||||
hyTLS.Enabled = true
|
||||
|
||||
opts := &option.HysteriaInboundOptions{
|
||||
ListenOptions: listen,
|
||||
UpMbps: config.UpMbps,
|
||||
DownMbps: config.DownMbps,
|
||||
Obfs: config.Obfs,
|
||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||
TLS: &hyTLS,
|
||||
},
|
||||
}
|
||||
inboundOptions = opts
|
||||
s.logger.Info("Xboard Hysteria configured. Up: ", config.UpMbps, " Down: ", config.DownMbps)
|
||||
case "hysteria2":
|
||||
// V2bX: Hysteria2 always uses TLS, optional obfs
|
||||
hy2TLS := tlsOptions
|
||||
hy2TLS.Enabled = true
|
||||
|
||||
var obfs *option.Hysteria2Obfs
|
||||
if config.Obfs != "" && config.ObfsPassword != "" {
|
||||
obfs = &option.Hysteria2Obfs{
|
||||
Type: config.Obfs,
|
||||
Password: config.ObfsPassword,
|
||||
}
|
||||
} else if config.Obfs != "" {
|
||||
// V2bX compat: if only obfs type given, treat as salamander with obfs as password
|
||||
obfs = &option.Hysteria2Obfs{
|
||||
Type: "salamander",
|
||||
Password: config.Obfs,
|
||||
}
|
||||
}
|
||||
|
||||
opts := &option.Hysteria2InboundOptions{
|
||||
ListenOptions: listen,
|
||||
UpMbps: config.UpMbps,
|
||||
DownMbps: config.DownMbps,
|
||||
IgnoreClientBandwidth: config.Ignore_Client_Bandwidth,
|
||||
Obfs: obfs,
|
||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||
TLS: &hy2TLS,
|
||||
},
|
||||
}
|
||||
inboundOptions = opts
|
||||
s.logger.Info("Xboard Hysteria2 configured. Up: ", config.UpMbps, " Down: ", config.DownMbps, " IgnoreClientBW: ", config.Ignore_Client_Bandwidth)
|
||||
case "anytls":
|
||||
// V2bX: AnyTLS always uses TLS
|
||||
anyTLS := tlsOptions
|
||||
anyTLS.Enabled = true
|
||||
|
||||
opts := &option.AnyTLSInboundOptions{
|
||||
ListenOptions: listen,
|
||||
PaddingScheme: config.PaddingScheme,
|
||||
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||
TLS: &anyTLS,
|
||||
},
|
||||
}
|
||||
inboundOptions = opts
|
||||
s.logger.Info("Xboard AnyTLS configured")
|
||||
default:
|
||||
return fmt.Errorf("unsupported protocol: %s", protocol)
|
||||
}
|
||||
@@ -428,11 +732,11 @@ func (s *Service) setupNode() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
s.access.Lock()
|
||||
s.inboundTags = []string{inboundTag}
|
||||
s.access.Unlock()
|
||||
|
||||
|
||||
s.logger.Info("Xboard dynamic inbound [", inboundTag, "] created on port ", inner.Port, " (protocol: ", protocol, ")")
|
||||
|
||||
// Register the new inbound in our managed list
|
||||
@@ -442,15 +746,15 @@ func (s *Service) setupNode() error {
|
||||
traffic := ssmapi.NewTrafficManager()
|
||||
managedServer.SetTracker(traffic)
|
||||
user := ssmapi.NewUserManager(managedServer, traffic)
|
||||
|
||||
|
||||
s.access.Lock()
|
||||
s.traffics[inboundTag] = traffic
|
||||
s.users[inboundTag] = user
|
||||
s.servers[inboundTag] = managedServer
|
||||
s.inboundTags = []string{inboundTag}
|
||||
s.access.Unlock()
|
||||
|
||||
s.logger.Info("Xboard dynamic inbound [", inboundTag, "] created on port ", inner.Port, " (protocol: ", protocol, ")")
|
||||
|
||||
s.logger.Info("Xboard managed inbound [", inboundTag, "] registered (protocol: ", protocol, ")")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user