修复无法正常使用的严重问题
This commit is contained in:
56
.gitignore
vendored
56
.gitignore
vendored
@@ -1,23 +1,35 @@
|
|||||||
/.idea/
|
# Binaries
|
||||||
/vendor/
|
sing-box
|
||||||
/*.json
|
sing-box.exe
|
||||||
/*.srs
|
*.exe
|
||||||
/*.db
|
*.dll
|
||||||
/site/
|
*.so
|
||||||
/bin/
|
*.dylib
|
||||||
/dist/
|
|
||||||
/sing-box
|
# Environment
|
||||||
/sing-box.exe
|
.env
|
||||||
/build/
|
.env.local
|
||||||
/*.jar
|
|
||||||
/*.aar
|
# Build & Cache
|
||||||
/*.xcframework/
|
go.sum
|
||||||
/experimental/libbox/*.aar
|
bin/
|
||||||
/experimental/libbox/*.xcframework/
|
dist/
|
||||||
/experimental/libbox/*.nupkg
|
/var/lib/sing-box/*
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
/var/log/sing-box/*
|
||||||
|
|
||||||
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/config.d/
|
Thumbs.db
|
||||||
/venv/
|
|
||||||
CLAUDE.md
|
# Antigravity/Gemini Artifacts
|
||||||
AGENTS.md
|
.gemini/
|
||||||
/.claude/
|
artifacts/
|
||||||
|
scratch/
|
||||||
|
implementation_plan*.md
|
||||||
|
walkthrough*.md
|
||||||
|
task.md
|
||||||
|
|
||||||
|
V2bX/
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -83,34 +85,87 @@ type XBoardServiceOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type XNodeConfig struct {
|
type XNodeConfig struct {
|
||||||
NodeType string `json:"node_type"`
|
NodeType string `json:"node_type"`
|
||||||
NodeType_ string `json:"nodeType"`
|
NodeType_ string `json:"nodeType"`
|
||||||
ServerConfig json.RawMessage `json:"server_config"`
|
ServerConfig json.RawMessage `json:"server_config"`
|
||||||
ServerConfig_ json.RawMessage `json:"serverConfig"`
|
ServerConfig_ json.RawMessage `json:"serverConfig"`
|
||||||
Config json.RawMessage `json:"config"`
|
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 {
|
|
||||||
ListenIP string `json:"listen_ip"`
|
ListenIP string `json:"listen_ip"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
ServerPort int `json:"server_port"`
|
ServerPort int `json:"server_port"`
|
||||||
Protocol string `json:"protocol"`
|
Protocol string `json:"protocol"`
|
||||||
Settings json.RawMessage `json:"settings"`
|
|
||||||
StreamSettings json.RawMessage `json:"streamSettings"`
|
|
||||||
Cipher string `json:"cipher"`
|
Cipher string `json:"cipher"`
|
||||||
ServerKey string `json:"server_key"`
|
ServerKey string `json:"server_key"`
|
||||||
TLS int `json:"tls"`
|
TLS int `json:"tls"`
|
||||||
Flow string `json:"flow"`
|
Flow string `json:"flow"`
|
||||||
TLSSettings *XTLSSettings `json:"tls_settings"`
|
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 {
|
type XTLSSettings struct {
|
||||||
@@ -121,6 +176,7 @@ type XTLSSettings struct {
|
|||||||
ShortID string `json:"short_id"`
|
ShortID string `json:"short_id"`
|
||||||
ShortIDs []string `json:"short_ids"`
|
ShortIDs []string `json:"short_ids"`
|
||||||
AllowInsecure bool `json:"allow_insecure"`
|
AllowInsecure bool `json:"allow_insecure"`
|
||||||
|
Dest string `json:"dest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type XRealitySettings struct {
|
type XRealitySettings struct {
|
||||||
@@ -227,6 +283,99 @@ func (s *Service) Start(stage adapter.StartStage) error {
|
|||||||
return nil
|
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 {
|
func (s *Service) setupNode() error {
|
||||||
s.logger.Info("Xboard fetching node config...")
|
s.logger.Info("Xboard fetching node config...")
|
||||||
config, err := s.fetchConfig()
|
config, err := s.fetchConfig()
|
||||||
@@ -236,7 +385,7 @@ func (s *Service) setupNode() error {
|
|||||||
|
|
||||||
inboundTag := "xboard-inbound"
|
inboundTag := "xboard-inbound"
|
||||||
|
|
||||||
// Resolve nested config
|
// Resolve nested config (V2bX compatibility: server_config / serverConfig / config)
|
||||||
var inner XInnerConfig
|
var inner XInnerConfig
|
||||||
if len(config.ServerConfig) > 0 {
|
if len(config.ServerConfig) > 0 {
|
||||||
json.Unmarshal(config.ServerConfig, &inner)
|
json.Unmarshal(config.ServerConfig, &inner)
|
||||||
@@ -246,24 +395,28 @@ func (s *Service) setupNode() error {
|
|||||||
json.Unmarshal(config.Config, &inner)
|
json.Unmarshal(config.Config, &inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to flat if still empty
|
// Fallback flat fields from top-level config to inner
|
||||||
if inner.ListenIP == "" {
|
if inner.ListenIP == "" {
|
||||||
inner.ListenIP = config.ListenIP
|
inner.ListenIP = config.ListenIP
|
||||||
}
|
}
|
||||||
if inner.ListenIP == "" {
|
if inner.ListenIP == "" {
|
||||||
inner.ListenIP = "0.0.0.0"
|
inner.ListenIP = "0.0.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
if inner.TLSSettings == nil {
|
if inner.TLSSettings == nil {
|
||||||
inner.TLSSettings = config.TLSSettings
|
inner.TLSSettings = config.TLSSettings
|
||||||
}
|
}
|
||||||
|
if inner.TLSSettings == nil {
|
||||||
|
inner.TLSSettings = config.TLSSettings_
|
||||||
|
}
|
||||||
|
if inner.TLSSettings_ == nil {
|
||||||
|
inner.TLSSettings_ = config.TLSSettings_
|
||||||
|
}
|
||||||
if inner.TLS == 0 {
|
if inner.TLS == 0 {
|
||||||
inner.TLS = config.TLS
|
inner.TLS = config.TLS
|
||||||
}
|
}
|
||||||
if inner.Flow == "" {
|
if inner.Flow == "" {
|
||||||
inner.Flow = config.Flow
|
inner.Flow = config.Flow
|
||||||
}
|
}
|
||||||
|
|
||||||
if inner.Protocol == "" {
|
if inner.Protocol == "" {
|
||||||
inner.Protocol = config.Protocol
|
inner.Protocol = config.Protocol
|
||||||
}
|
}
|
||||||
@@ -280,7 +433,17 @@ func (s *Service) setupNode() error {
|
|||||||
if inner.ServerKey == "" {
|
if inner.ServerKey == "" {
|
||||||
inner.ServerKey = config.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
|
protocol := inner.Protocol
|
||||||
if protocol == "" {
|
if protocol == "" {
|
||||||
protocol = config.NodeType
|
protocol = config.NodeType
|
||||||
@@ -288,7 +451,6 @@ func (s *Service) setupNode() error {
|
|||||||
if protocol == "" {
|
if protocol == "" {
|
||||||
protocol = config.NodeType_
|
protocol = config.NodeType_
|
||||||
}
|
}
|
||||||
|
|
||||||
if protocol == "" {
|
if protocol == "" {
|
||||||
s.logger.Error("Xboard setup error: could not identify protocol. Please check debug logs for raw JSON.")
|
s.logger.Error("Xboard setup error: could not identify protocol. Please check debug logs for raw JSON.")
|
||||||
return fmt.Errorf("unsupported protocol: empty")
|
return fmt.Errorf("unsupported protocol: empty")
|
||||||
@@ -303,81 +465,133 @@ func (s *Service) setupNode() error {
|
|||||||
listenAddr = badoption.Addr(netip.IPv4Unspecified())
|
listenAddr = badoption.Addr(netip.IPv4Unspecified())
|
||||||
}
|
}
|
||||||
|
|
||||||
var tlsOptions *option.InboundTLSOptions
|
listen := option.ListenOptions{
|
||||||
if inner.TLS > 0 && inner.TLSSettings != nil {
|
Listen: &listenAddr,
|
||||||
tlsOptions = &option.InboundTLSOptions{
|
ListenPort: uint16(inner.Port),
|
||||||
Enabled: true,
|
}
|
||||||
ServerName: inner.TLSSettings.ServerName,
|
|
||||||
|
// ── 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
|
case 2: // Reality
|
||||||
shortIDs := inner.TLSSettings.ShortIDs
|
if tlsSettings != nil {
|
||||||
if len(shortIDs) == 0 && inner.TLSSettings.ShortID != "" {
|
tlsOptions.Enabled = true
|
||||||
shortIDs = []string{inner.TLSSettings.ShortID}
|
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{
|
tlsOptions.Reality = &option.InboundRealityOptions{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Handshake: option.InboundRealityHandshakeOptions{
|
Handshake: option.InboundRealityHandshakeOptions{
|
||||||
ServerOptions: option.ServerOptions{
|
ServerOptions: option.ServerOptions{
|
||||||
Server: inner.TLSSettings.ServerName,
|
Server: dest,
|
||||||
ServerPort: 443,
|
ServerPort: serverPort,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PrivateKey: inner.TLSSettings.PrivateKey,
|
PrivateKey: tlsSettings.PrivateKey,
|
||||||
ShortID: badoption.Listable[string](shortIDs),
|
ShortID: badoption.Listable[string](shortIDs),
|
||||||
}
|
}
|
||||||
// Fallback if empty
|
s.logger.Info("Xboard REALITY configured. Dest: ", dest, ":", serverPort)
|
||||||
if tlsOptions.Reality.Handshake.Server == "" {
|
|
||||||
tlsOptions.Reality.Handshake.Server = "www.microsoft.com"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
var inboundOptions any
|
||||||
switch protocol {
|
switch protocol {
|
||||||
case "vless":
|
case "vmess", "vless":
|
||||||
vlessOptions := option.VLESSInboundOptions{}
|
// Transport for vmess/vless
|
||||||
vlessOptions.Listen = &listenAddr
|
transport, err := getInboundTransport(networkType, networkSettings)
|
||||||
vlessOptions.ListenPort = uint16(inner.Port)
|
if err != nil {
|
||||||
vlessOptions.TLS = tlsOptions
|
return fmt.Errorf("build transport for %s: %w", protocol, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle Reality
|
if protocol == "vless" {
|
||||||
if inner.StreamSettings != nil {
|
opts := &option.VLESSInboundOptions{
|
||||||
var streamSettings XStreamSettings
|
ListenOptions: listen,
|
||||||
json.Unmarshal(inner.StreamSettings, &streamSettings)
|
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||||
reality := streamSettings.GetReality()
|
TLS: &tlsOptions,
|
||||||
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 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":
|
case "shadowsocks":
|
||||||
method := inner.Cipher
|
method := inner.Cipher
|
||||||
serverKey := inner.ServerKey
|
serverKey := inner.ServerKey
|
||||||
@@ -387,7 +601,7 @@ func (s *Service) setupNode() error {
|
|||||||
if method == "" {
|
if method == "" {
|
||||||
method = "aes-256-gcm"
|
method = "aes-256-gcm"
|
||||||
}
|
}
|
||||||
// Hardening for Shadowsocks 2022
|
// V2bX SS2022 key handling
|
||||||
if len(method) >= 4 && method[:4] == "2022" {
|
if len(method) >= 4 && method[:4] == "2022" {
|
||||||
keyLen := 32
|
keyLen := 32
|
||||||
if len(method) >= 18 && method[13:16] == "128" {
|
if len(method) >= 18 && method[13:16] == "128" {
|
||||||
@@ -395,27 +609,117 @@ func (s *Service) setupNode() error {
|
|||||||
}
|
}
|
||||||
serverKey = fixSSKey(serverKey, keyLen)
|
serverKey = fixSSKey(serverKey, keyLen)
|
||||||
}
|
}
|
||||||
ssOptions := option.ShadowsocksInboundOptions{
|
|
||||||
ListenOptions: option.ListenOptions{
|
ssOptions := &option.ShadowsocksInboundOptions{
|
||||||
Listen: &listenAddr,
|
ListenOptions: listen,
|
||||||
ListenPort: uint16(inner.Port),
|
Method: method,
|
||||||
},
|
Password: serverKey,
|
||||||
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)
|
||||||
|
}
|
||||||
|
case "trojan":
|
||||||
|
// Trojan supports ws/grpc transport like V2bX
|
||||||
|
transport, err := getInboundTransport(networkType, networkSettings)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("build transport for trojan: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no colon is used in client, we might need a fallback.
|
opts := &option.TrojanInboundOptions{
|
||||||
// We'll leave it to be updated dynamically when users sync.
|
ListenOptions: listen,
|
||||||
inboundOptions = &ssOptions
|
InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{
|
||||||
s.logger.Info("Xboard Shadowsocks 2022 setup. Method: ", method, " PSK preview: ", serverKey[:8], "...")
|
TLS: &tlsOptions,
|
||||||
case "trojan":
|
|
||||||
trojanOptions := option.TrojanInboundOptions{
|
|
||||||
ListenOptions: option.ListenOptions{
|
|
||||||
Listen: &listenAddr,
|
|
||||||
ListenPort: uint16(inner.Port),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
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:
|
default:
|
||||||
return fmt.Errorf("unsupported protocol: %s", protocol)
|
return fmt.Errorf("unsupported protocol: %s", protocol)
|
||||||
}
|
}
|
||||||
@@ -450,7 +754,7 @@ func (s *Service) setupNode() error {
|
|||||||
s.inboundTags = []string{inboundTag}
|
s.inboundTags = []string{inboundTag}
|
||||||
s.access.Unlock()
|
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
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user