202 lines
4.7 KiB
Go
202 lines
4.7 KiB
Go
package singbox
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/perfect-panel/server/pkg/adapter/proxy"
|
|
"github.com/perfect-panel/server/pkg/logger"
|
|
)
|
|
|
|
func BuildSingbox(adapter proxy.Adapter, uuid string) ([]byte, error) {
|
|
// build outbounds type is Proxy
|
|
var proxies []Proxy
|
|
// build outbound group
|
|
for _, group := range adapter.Group {
|
|
if group.Type == proxy.GroupTypeSelect {
|
|
selector := Proxy{
|
|
Type: Selector,
|
|
Tag: group.Name,
|
|
SelectorOptions: &SelectorOutboundOptions{
|
|
OutboundOptions: OutboundOptions{
|
|
Tag: group.Name,
|
|
Type: Selector,
|
|
},
|
|
Outbounds: group.Proxies,
|
|
Default: group.Proxies[0],
|
|
InterruptExistConnections: false,
|
|
},
|
|
}
|
|
proxies = append(proxies, selector)
|
|
} else if group.Type == proxy.GroupTypeURLTest {
|
|
selector := Proxy{
|
|
Type: URLTest,
|
|
Tag: group.Name,
|
|
URLTestOptions: &URLTestOutboundOptions{
|
|
OutboundOptions: OutboundOptions{
|
|
Tag: group.Name,
|
|
Type: URLTest,
|
|
},
|
|
Outbounds: group.Proxies,
|
|
URL: group.URL,
|
|
},
|
|
}
|
|
proxies = append(proxies, selector)
|
|
} else {
|
|
logger.Errorf("[sing-box] Unknown group type: %s, group name: %s", group.Type, group.Name)
|
|
}
|
|
}
|
|
|
|
// build outbounds
|
|
for _, data := range adapter.Proxies {
|
|
p := buildProxy(data, uuid)
|
|
if p == nil {
|
|
continue
|
|
}
|
|
proxies = append(proxies, *p)
|
|
}
|
|
|
|
// add direct outbound
|
|
direct := Proxy{
|
|
Type: Direct,
|
|
Tag: "DIRECT",
|
|
}
|
|
// add block outbound
|
|
block := Proxy{
|
|
Type: Block,
|
|
Tag: "block",
|
|
}
|
|
// add dns outbound
|
|
dns := Proxy{
|
|
Type: DNS,
|
|
Tag: "dns-out",
|
|
}
|
|
proxies = append(proxies, direct, block, dns)
|
|
|
|
var rawConfig map[string]any
|
|
if err := json.Unmarshal([]byte(DefaultTemplate), &rawConfig); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rawConfig["outbounds"] = proxies
|
|
route := RouteOptions{
|
|
Final: adapter.Default,
|
|
Rules: []Rule{
|
|
{
|
|
Inbound: []string{
|
|
"tun-in",
|
|
"mixed-in",
|
|
},
|
|
Action: "sniff",
|
|
},
|
|
{
|
|
Type: "logical",
|
|
Mode: "or",
|
|
Rules: []Rule{
|
|
{
|
|
Port: []uint16{53},
|
|
},
|
|
{
|
|
Protocol: []string{"dns"},
|
|
},
|
|
},
|
|
Action: "hijack-dns",
|
|
},
|
|
{
|
|
RuleSet: []string{
|
|
"geosite-category-ads-all",
|
|
},
|
|
ClashMode: "rule",
|
|
Action: "reject",
|
|
},
|
|
{
|
|
ClashMode: "direct",
|
|
Outbound: "DIRECT",
|
|
},
|
|
{
|
|
ClashMode: "global",
|
|
Outbound: adapter.Default,
|
|
},
|
|
{
|
|
IPIsPrivate: true,
|
|
Outbound: "DIRECT",
|
|
},
|
|
{
|
|
RuleSet: []string{
|
|
"geosite-private",
|
|
},
|
|
Outbound: "DIRECT",
|
|
},
|
|
},
|
|
RuleSet: []RuleSet{
|
|
{
|
|
Tag: "geoip-cn",
|
|
Type: "remote",
|
|
Format: "binary",
|
|
URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geoip/cn.srs",
|
|
DownloadDetour: "DIRECT",
|
|
},
|
|
{
|
|
Tag: "geosite-cn",
|
|
Type: "remote",
|
|
Format: "binary",
|
|
URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/cn.srs",
|
|
DownloadDetour: "DIRECT",
|
|
},
|
|
{
|
|
Tag: "geosite-private",
|
|
Type: "remote",
|
|
Format: "binary",
|
|
URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/private.srs",
|
|
DownloadDetour: "DIRECT",
|
|
},
|
|
{
|
|
Tag: "geosite-category-ads-all",
|
|
Type: "remote",
|
|
Format: "binary",
|
|
URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/category-ads-all.srs",
|
|
DownloadDetour: "DIRECT",
|
|
},
|
|
{
|
|
Tag: "geosite-geolocation-!cn",
|
|
Type: "remote",
|
|
Format: "binary",
|
|
URL: "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@sing/geo/geosite/geolocation-!cn.srs",
|
|
DownloadDetour: "DIRECT",
|
|
},
|
|
},
|
|
AutoDetectInterface: true,
|
|
}
|
|
route.Rules = append(route.Rules, adapterToSingboxRule(adapter.Rules)...)
|
|
rawConfig["route"] = route
|
|
return json.Marshal(rawConfig)
|
|
}
|
|
|
|
func buildProxy(data proxy.Proxy, uuid string) *Proxy {
|
|
var p *Proxy
|
|
var err error
|
|
switch data.Protocol {
|
|
case VLESS:
|
|
p, err = ParseVless(data, uuid)
|
|
case Shadowsocks:
|
|
p, err = ParseShadowsocks(data, uuid)
|
|
case Trojan:
|
|
p, err = ParseTrojan(data, uuid)
|
|
case VMess:
|
|
p, err = ParseVMess(data, uuid)
|
|
|
|
case Hysteria2:
|
|
p, err = ParseHysteria2(data, uuid)
|
|
|
|
case TUIC:
|
|
p, err = ParseTUIC(data, uuid)
|
|
|
|
default:
|
|
logger.Error("Unknown protocol", logger.Field("protocol", data.Protocol), logger.Field("server", data.Name))
|
|
}
|
|
if err != nil {
|
|
logger.Error("ParseVless", logger.Field("error", err.Error()), logger.Field("server", data.Name), logger.Field("protocol", data.Protocol))
|
|
return nil
|
|
}
|
|
return p
|
|
}
|