forked from mirror/serenity
Add tempalte.extended
, template.<rule_set>.<type=github/owner/repo/branch/rule_set>
, template.block_tag
This commit is contained in:
parent
404de85ab1
commit
9ae4f644f1
6
constant/rule_set.go
Normal file
6
constant/rule_set.go
Normal file
@ -0,0 +1,6 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
RuleSetTypeDefault = "default"
|
||||
RuleSetTypeGitHub = "github"
|
||||
)
|
@ -57,7 +57,7 @@
|
||||
"custom_geoip": {},
|
||||
"custom_geosite": {},
|
||||
"custom_rule_set": [],
|
||||
"post_custom_rule_set": [],
|
||||
"post_rule_set": [],
|
||||
|
||||
// Experimental
|
||||
|
||||
@ -253,7 +253,7 @@ List of [RuleSet](https://sing-box.sagernet.org/configuration/rule-set/).
|
||||
|
||||
Default rule sets will not be generated if not empty.
|
||||
|
||||
#### post_custom_rule_set
|
||||
#### post_rule_set
|
||||
|
||||
List of [RuleSet](https://sing-box.sagernet.org/configuration/rule-set/).
|
||||
|
||||
|
@ -2,13 +2,17 @@ package option
|
||||
|
||||
import (
|
||||
"github.com/sagernet/serenity/common/semver"
|
||||
C "github.com/sagernet/serenity/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-dns"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
)
|
||||
|
||||
type Template struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Extend string `json:"extend,omitempty"`
|
||||
|
||||
// Global
|
||||
|
||||
@ -37,6 +41,7 @@ type Template struct {
|
||||
ExtraGroups []ExtraGroup `json:"extra_groups,omitempty"`
|
||||
GenerateGlobalURLTest bool `json:"generate_global_urltest,omitempty"`
|
||||
DirectTag string `json:"direct_tag,omitempty"`
|
||||
BlockTag string `json:"block_tag,omitempty"`
|
||||
DefaultTag string `json:"default_tag,omitempty"`
|
||||
URLTestTag string `json:"urltest_tag,omitempty"`
|
||||
CustomDirect *option.DirectOutboundOptions `json:"custom_direct,omitempty"`
|
||||
@ -51,8 +56,8 @@ type Template struct {
|
||||
EnableJSDelivr bool `json:"enable_jsdelivr,omitempty"`
|
||||
CustomGeoIP *option.GeoIPOptions `json:"custom_geoip,omitempty"`
|
||||
CustomGeosite *option.GeositeOptions `json:"custom_geosite,omitempty"`
|
||||
CustomRuleSet []option.RuleSet `json:"custom_rule_set,omitempty"`
|
||||
PostCustomRuleSet []option.RuleSet `json:"post_custom_rule_set,omitempty"`
|
||||
CustomRuleSet []RuleSet `json:"custom_rule_set,omitempty"`
|
||||
PostRuleSet []RuleSet `json:"post_rule_set,omitempty"`
|
||||
|
||||
// Experimental
|
||||
DisableCacheFile bool `json:"disable_cache_file,omitempty"`
|
||||
@ -70,6 +75,53 @@ type Template struct {
|
||||
MemoryLimit option.MemoryBytes `json:"memory_limit,omitempty"`
|
||||
}
|
||||
|
||||
type _RuleSet struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
DefaultOptions option.RuleSet `json:"-"`
|
||||
GitHubOptions GitHubRuleSetOptions `json:"-"`
|
||||
}
|
||||
|
||||
type RuleSet _RuleSet
|
||||
|
||||
func (r *RuleSet) RawOptions() (any, error) {
|
||||
switch r.Type {
|
||||
case C.RuleSetTypeDefault, "":
|
||||
r.Type = ""
|
||||
return &r.DefaultOptions, nil
|
||||
case C.RuleSetTypeGitHub:
|
||||
return &r.GitHubOptions, nil
|
||||
default:
|
||||
return nil, E.New("unknown rule set type", r.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RuleSet) MarshalJSON() ([]byte, error) {
|
||||
rawOptions, err := r.RawOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return option.MarshallObjects((*_RuleSet)(r), rawOptions)
|
||||
}
|
||||
|
||||
func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
|
||||
err := json.Unmarshal(bytes, (*_RuleSet)(r))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawOptions, err := r.RawOptions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return option.UnmarshallExcluded(bytes, (*_RuleSet)(r), rawOptions)
|
||||
}
|
||||
|
||||
type GitHubRuleSetOptions struct {
|
||||
Owner string `json:"owner,omitempty"`
|
||||
Repo string `json:"repo,omitempty"`
|
||||
Branch string `json:"branch,omitempty"`
|
||||
RuleSet option.Listable[string] `json:"rule_set,omitempty"`
|
||||
}
|
||||
|
||||
func (t Template) DisableIPv6() bool {
|
||||
return t.DomainStrategy == option.DomainStrategy(dns.DomainStrategyUseIPv4)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/sagernet/serenity/option"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
@ -16,12 +17,49 @@ type Manager struct {
|
||||
templates []*Template
|
||||
}
|
||||
|
||||
func extendTemplate(rawTemplates []option.Template, root, current option.Template) (option.Template, error) {
|
||||
if current.Extend == "" {
|
||||
return current, nil
|
||||
} else if root.Name == current.Extend {
|
||||
return option.Template{}, E.New("initialize template[", current.Name, "]: circular extend detected: ", current.Extend)
|
||||
}
|
||||
var next option.Template
|
||||
for _, it := range rawTemplates {
|
||||
if it.Name == current.Extend {
|
||||
next = it
|
||||
break
|
||||
}
|
||||
}
|
||||
if next.Name == "" {
|
||||
return option.Template{}, E.New("initialize template[", current.Name, "]: extended template not found: ", current.Extend)
|
||||
}
|
||||
if next.Extend != "" {
|
||||
newNext, err := extendTemplate(rawTemplates, root, next)
|
||||
if err != nil {
|
||||
return option.Template{}, E.Cause(err, next.Extend)
|
||||
}
|
||||
next = newNext
|
||||
}
|
||||
newTemplate, err := badjson.Merge(current, next)
|
||||
if err != nil {
|
||||
return option.Template{}, E.Cause(err, "initialize template[", current.Name, "]: merge extended template: ", current.Extend)
|
||||
}
|
||||
return newTemplate, nil
|
||||
}
|
||||
|
||||
func NewManager(ctx context.Context, logger logger.Logger, rawTemplates []option.Template) (*Manager, error) {
|
||||
var templates []*Template
|
||||
for templateIndex, template := range rawTemplates {
|
||||
if template.Name == "" {
|
||||
return nil, E.New("initialize template[", templateIndex, "]: missing name")
|
||||
}
|
||||
if template.Extend != "" {
|
||||
newTemplate, err := extendTemplate(rawTemplates, template, template)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
template = newTemplate
|
||||
}
|
||||
var groups []*ExtraGroup
|
||||
for groupIndex, group := range template.ExtraGroups {
|
||||
if group.Tag == "" {
|
||||
|
@ -37,6 +37,10 @@ func (t *Template) renderDNS(metadata M.Metadata, options *option.Options) error
|
||||
if dnsLocal == "" {
|
||||
dnsLocal = DefaultDNSLocal
|
||||
}
|
||||
directTag := t.DirectTag
|
||||
if directTag == "" {
|
||||
directTag = DefaultDirectTag
|
||||
}
|
||||
defaultDNSOptions := option.DNSServerOptions{
|
||||
Tag: DNSDefaultTag,
|
||||
Address: dnsDefault,
|
||||
@ -58,7 +62,7 @@ func (t *Template) renderDNS(metadata M.Metadata, options *option.Options) error
|
||||
localDNSOptions = option.DNSServerOptions{
|
||||
Tag: DNSLocalTag,
|
||||
Address: dnsLocal,
|
||||
Detour: DefaultDirectTag,
|
||||
Detour: directTag,
|
||||
}
|
||||
if dnsLocalUrl, err := url.Parse(dnsLocal); err == nil && BM.IsDomainName(dnsLocalUrl.Hostname()) {
|
||||
localDNSOptions.AddressResolver = DNSLocalSetupTag
|
||||
|
@ -3,11 +3,13 @@ package template
|
||||
import (
|
||||
M "github.com/sagernet/serenity/common/metadata"
|
||||
"github.com/sagernet/serenity/common/semver"
|
||||
"github.com/sagernet/serenity/constant"
|
||||
"github.com/sagernet/serenity/option"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
boxOption "github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func (t *Template) renderGeoResources(metadata M.Metadata, options *option.Options) {
|
||||
func (t *Template) renderGeoResources(metadata M.Metadata, options *boxOption.Options) {
|
||||
if t.DisableRuleSet || (metadata.Version != nil && metadata.Version.LessThan(semver.ParseVersion("1.8.0-alpha.10"))) {
|
||||
var (
|
||||
geoipDownloadURL string
|
||||
@ -27,13 +29,13 @@ func (t *Template) renderGeoResources(metadata M.Metadata, options *option.Optio
|
||||
geositeDownloadURL = "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite-cn.db"
|
||||
}
|
||||
if t.CustomGeoIP == nil {
|
||||
options.Route.GeoIP = &option.GeoIPOptions{
|
||||
options.Route.GeoIP = &boxOption.GeoIPOptions{
|
||||
DownloadURL: geoipDownloadURL,
|
||||
DownloadDetour: downloadDetour,
|
||||
}
|
||||
}
|
||||
if t.CustomGeosite == nil {
|
||||
options.Route.Geosite = &option.GeositeOptions{
|
||||
options.Route.Geosite = &boxOption.GeositeOptions{
|
||||
DownloadURL: geositeDownloadURL,
|
||||
DownloadDetour: downloadDetour,
|
||||
}
|
||||
@ -57,12 +59,12 @@ func (t *Template) renderGeoResources(metadata M.Metadata, options *option.Optio
|
||||
downloadURL = "https://raw.githubusercontent.com/"
|
||||
branchSplit = "/"
|
||||
}
|
||||
options.Route.RuleSet = []option.RuleSet{
|
||||
options.Route.RuleSet = []boxOption.RuleSet{
|
||||
{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geoip-cn",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
RemoteOptions: boxOption.RemoteRuleSet{
|
||||
URL: downloadURL + "SagerNet/sing-geoip" + branchSplit + "rule-set/geoip-cn.srs",
|
||||
DownloadDetour: downloadDetour,
|
||||
},
|
||||
@ -71,7 +73,7 @@ func (t *Template) renderGeoResources(metadata M.Metadata, options *option.Optio
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geosite-geolocation-cn",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
RemoteOptions: boxOption.RemoteRuleSet{
|
||||
URL: downloadURL + "SagerNet/sing-geosite" + branchSplit + "rule-set/geosite-geolocation-cn.srs",
|
||||
DownloadDetour: downloadDetour,
|
||||
},
|
||||
@ -80,13 +82,58 @@ func (t *Template) renderGeoResources(metadata M.Metadata, options *option.Optio
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: "geosite-geolocation-!cn",
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: option.RemoteRuleSet{
|
||||
RemoteOptions: boxOption.RemoteRuleSet{
|
||||
URL: downloadURL + "SagerNet/sing-geosite" + branchSplit + "rule-set/geosite-geolocation-!cn.srs",
|
||||
DownloadDetour: downloadDetour,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
options.Route.RuleSet = append(options.Route.RuleSet, t.PostCustomRuleSet...)
|
||||
options.Route.RuleSet = append(options.Route.RuleSet, t.renderRuleSet(t.PostRuleSet)...)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Template) renderRuleSet(ruleSets []option.RuleSet) []boxOption.RuleSet {
|
||||
var result []boxOption.RuleSet
|
||||
for _, ruleSet := range ruleSets {
|
||||
switch ruleSet.Type {
|
||||
case constant.RuleSetTypeDefault, "":
|
||||
result = append(result, ruleSet.DefaultOptions)
|
||||
case constant.RuleSetTypeGitHub:
|
||||
var (
|
||||
downloadURL string
|
||||
downloadDetour string
|
||||
branchSplit string
|
||||
)
|
||||
if t.EnableJSDelivr {
|
||||
downloadURL = "https://testingcf.jsdelivr.net/gh/"
|
||||
if t.DirectTag != "" {
|
||||
downloadDetour = t.DirectTag
|
||||
} else {
|
||||
downloadDetour = DefaultDirectTag
|
||||
}
|
||||
branchSplit = "@"
|
||||
} else {
|
||||
downloadURL = "https://raw.githubusercontent.com/"
|
||||
branchSplit = "/"
|
||||
}
|
||||
for _, code := range ruleSet.GitHubOptions.RuleSet {
|
||||
result = append(result, boxOption.RuleSet{
|
||||
Type: C.RuleSetTypeRemote,
|
||||
Tag: code,
|
||||
Format: C.RuleSetFormatBinary,
|
||||
RemoteOptions: boxOption.RemoteRuleSet{
|
||||
URL: downloadURL +
|
||||
ruleSet.GitHubOptions.Owner + "/" +
|
||||
ruleSet.GitHubOptions.Repo + "/" +
|
||||
branchSplit +
|
||||
ruleSet.GitHubOptions.Branch + "/" +
|
||||
code + ".srs",
|
||||
DownloadDetour: downloadDetour,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -20,6 +20,10 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
|
||||
if directTag == "" {
|
||||
directTag = DefaultDirectTag
|
||||
}
|
||||
blockTag := t.BlockTag
|
||||
if blockTag == "" {
|
||||
blockTag = DefaultBlockTag
|
||||
}
|
||||
options.Outbounds = []option.Outbound{
|
||||
{
|
||||
Tag: directTag,
|
||||
@ -27,7 +31,7 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
|
||||
DirectOptions: common.PtrValueOrDefault(t.CustomDirect),
|
||||
},
|
||||
{
|
||||
Tag: BlockTag,
|
||||
Tag: blockTag,
|
||||
Type: C.TypeBlock,
|
||||
},
|
||||
{
|
||||
@ -85,7 +89,7 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
|
||||
selectorOutbound := option.Outbound{
|
||||
Type: C.TypeSelector,
|
||||
Tag: it.Name,
|
||||
SelectorOptions: common.PtrValueOrDefault(t.CustomSelector),
|
||||
SelectorOptions: common.PtrValueOrDefault(it.CustomSelector),
|
||||
}
|
||||
selectorOutbound.SelectorOptions.Outbounds = append(selectorOutbound.SelectorOptions.Outbounds, joinOutbounds...)
|
||||
allGroups = append(allGroups, selectorOutbound)
|
||||
@ -171,9 +175,9 @@ func groupJoin(outbounds []option.Outbound, groupTag string, groupOutbounds ...s
|
||||
groupOutbound := outbounds[groupIndex]
|
||||
switch groupOutbound.Type {
|
||||
case C.TypeSelector:
|
||||
groupOutbound.SelectorOptions.Outbounds = append(groupOutbound.SelectorOptions.Outbounds, groupOutbounds...)
|
||||
groupOutbound.SelectorOptions.Outbounds = common.Dup(append(groupOutbound.SelectorOptions.Outbounds, groupOutbounds...))
|
||||
case C.TypeURLTest:
|
||||
groupOutbound.URLTestOptions.Outbounds = append(groupOutbound.URLTestOptions.Outbounds, groupOutbounds...)
|
||||
groupOutbound.URLTestOptions.Outbounds = common.Dup(append(groupOutbound.URLTestOptions.Outbounds, groupOutbounds...))
|
||||
}
|
||||
outbounds[groupIndex] = groupOutbound
|
||||
return outbounds
|
||||
|
@ -13,7 +13,7 @@ func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) err
|
||||
options.Route = &option.RouteOptions{
|
||||
GeoIP: t.CustomGeoIP,
|
||||
Geosite: t.CustomGeosite,
|
||||
RuleSet: t.CustomRuleSet,
|
||||
RuleSet: t.renderRuleSet(t.CustomRuleSet),
|
||||
}
|
||||
}
|
||||
if !t.DisableTrafficBypass {
|
||||
@ -45,6 +45,10 @@ func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) err
|
||||
},
|
||||
}
|
||||
if !t.DisableTrafficBypass && !t.DisableDefaultRules {
|
||||
blockTag := t.BlockTag
|
||||
if blockTag == "" {
|
||||
blockTag = DefaultBlockTag
|
||||
}
|
||||
options.Route.Rules = append(options.Route.Rules, option.Rule{
|
||||
Type: C.RuleTypeLogical,
|
||||
LogicalOptions: option.LogicalRule{
|
||||
@ -64,7 +68,7 @@ func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) err
|
||||
},
|
||||
},
|
||||
},
|
||||
Outbound: BlockTag,
|
||||
Outbound: blockTag,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ const (
|
||||
DNSFakeIPTag = "remote"
|
||||
DefaultDNS = "tls://8.8.8.8"
|
||||
DefaultDNSLocal = "https://223.5.5.5/dns-query"
|
||||
DefaultDefaultTag = "Default"
|
||||
DefaultDefaultTag = "default"
|
||||
DefaultDirectTag = "direct"
|
||||
BlockTag = "block"
|
||||
DefaultBlockTag = "block"
|
||||
DNSTag = "dns"
|
||||
DefaultURLTestTag = "URLTest"
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user