178 lines
3.5 KiB
Go
178 lines
3.5 KiB
Go
package core
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
const DefaultSocksPort = 10808
|
|
|
|
// GenerateXrayConfig generates a full JSON config for xray-core.
|
|
// Xray acts as backend: listens SOCKS5 on a local port, connects to the remote server.
|
|
func GenerateXrayConfig(link *ProxyLink, socksPort int) (string, error) {
|
|
if socksPort == 0 {
|
|
socksPort = DefaultSocksPort
|
|
}
|
|
|
|
config := map[string]any{
|
|
"log": map[string]any{
|
|
"loglevel": "warning",
|
|
},
|
|
"inbounds": []any{
|
|
buildSocksInbound(socksPort),
|
|
},
|
|
"outbounds": []any{
|
|
buildProxyOutbound(link),
|
|
buildDirectOutbound(),
|
|
},
|
|
}
|
|
|
|
data, err := json.MarshalIndent(config, "", " ")
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to marshal xray config: %w", err)
|
|
}
|
|
return string(data), nil
|
|
}
|
|
|
|
func buildSocksInbound(port int) map[string]any {
|
|
return map[string]any{
|
|
"port": port,
|
|
"listen": "127.0.0.1",
|
|
"protocol": "socks",
|
|
"settings": map[string]any{
|
|
"auth": "noauth",
|
|
"udp": true,
|
|
},
|
|
"tag": "socks-in",
|
|
}
|
|
}
|
|
|
|
func buildProxyOutbound(link *ProxyLink) map[string]any {
|
|
outbound := map[string]any{
|
|
"tag": "proxy",
|
|
"protocol": link.Protocol,
|
|
}
|
|
|
|
switch link.Protocol {
|
|
case "trojan":
|
|
outbound["settings"] = buildTrojanSettings(link)
|
|
case "vless":
|
|
outbound["settings"] = buildVlessSettings(link)
|
|
}
|
|
|
|
outbound["streamSettings"] = buildStreamSettings(link)
|
|
return outbound
|
|
}
|
|
|
|
func buildTrojanSettings(link *ProxyLink) map[string]any {
|
|
return map[string]any{
|
|
"servers": []any{
|
|
map[string]any{
|
|
"address": link.Address,
|
|
"port": link.Port,
|
|
"password": link.Credential,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func buildVlessSettings(link *ProxyLink) map[string]any {
|
|
user := map[string]any{
|
|
"id": link.Credential,
|
|
"encryption": "none",
|
|
}
|
|
if link.Flow != "" {
|
|
user["flow"] = link.Flow
|
|
}
|
|
|
|
return map[string]any{
|
|
"vnext": []any{
|
|
map[string]any{
|
|
"address": link.Address,
|
|
"port": link.Port,
|
|
"users": []any{user},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func buildStreamSettings(link *ProxyLink) map[string]any {
|
|
stream := map[string]any{
|
|
"network": link.Transport,
|
|
}
|
|
|
|
// Security
|
|
if link.Security != "" && link.Security != "none" {
|
|
stream["security"] = link.Security
|
|
}
|
|
|
|
// Transport-specific settings
|
|
switch link.Transport {
|
|
case "grpc":
|
|
stream["grpcSettings"] = map[string]any{
|
|
"serviceName": link.ServiceName,
|
|
}
|
|
case "xhttp":
|
|
xhttp := map[string]any{
|
|
"path": defaultIfEmpty(link.Path, "/"),
|
|
}
|
|
if link.Host != "" {
|
|
xhttp["host"] = link.Host
|
|
}
|
|
stream["xhttpSettings"] = xhttp
|
|
case "ws":
|
|
ws := map[string]any{
|
|
"path": defaultIfEmpty(link.Path, "/"),
|
|
}
|
|
if link.Host != "" {
|
|
ws["headers"] = map[string]any{
|
|
"Host": link.Host,
|
|
}
|
|
}
|
|
stream["wsSettings"] = ws
|
|
case "h2", "http":
|
|
h2 := map[string]any{
|
|
"path": defaultIfEmpty(link.Path, "/"),
|
|
}
|
|
if link.Host != "" {
|
|
h2["host"] = []string{link.Host}
|
|
}
|
|
stream["httpSettings"] = h2
|
|
}
|
|
|
|
// Security-specific settings
|
|
switch link.Security {
|
|
case "reality":
|
|
stream["realitySettings"] = map[string]any{
|
|
"publicKey": link.PublicKey,
|
|
"shortId": link.ShortId,
|
|
"serverName": link.Sni,
|
|
"fingerprint": link.Fingerprint,
|
|
}
|
|
case "tls":
|
|
tls := map[string]any{
|
|
"fingerprint": link.Fingerprint,
|
|
}
|
|
if link.Sni != "" {
|
|
tls["serverName"] = link.Sni
|
|
}
|
|
stream["tlsSettings"] = tls
|
|
}
|
|
|
|
return stream
|
|
}
|
|
|
|
func buildDirectOutbound() map[string]any {
|
|
return map[string]any{
|
|
"tag": "direct",
|
|
"protocol": "freedom",
|
|
}
|
|
}
|
|
|
|
func defaultIfEmpty(s, def string) string {
|
|
if s == "" {
|
|
return def
|
|
}
|
|
return s
|
|
}
|