forked from mirror/serenity
Add support for rule actions
This commit is contained in:
parent
d2f1118c2e
commit
1649ae16e4
@ -5,7 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
box "github.com/sagernet/sing-box"
|
"github.com/sagernet/sing-box"
|
||||||
"github.com/sagernet/sing-box/include"
|
"github.com/sagernet/sing-box/include"
|
||||||
_ "github.com/sagernet/sing-box/include"
|
_ "github.com/sagernet/sing-box/include"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -31,7 +31,7 @@ RuleSet generate configuration.
|
|||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"repository": "SagerNet/sing-geosite",
|
"repository": "SagerNet/sing-geosite",
|
||||||
"path": "rule-set",
|
"path": "rule-set/geosite-",
|
||||||
"prefix": "geosite-",
|
"prefix": "geosite-",
|
||||||
"rule_set": [
|
"rule_set": [
|
||||||
"apple",
|
"apple",
|
||||||
@ -47,7 +47,7 @@ RuleSet generate configuration.
|
|||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
"repository": "MetaCubeX/meta-rules-dat",
|
"repository": "MetaCubeX/meta-rules-dat",
|
||||||
"path": "sing/geo/geosite",
|
"path": "sing/geo/geosite/",
|
||||||
"prefix": "geosite-",
|
"prefix": "geosite-",
|
||||||
"rule_set": [
|
"rule_set": [
|
||||||
"apple",
|
"apple",
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
"domain_strategy": "",
|
"domain_strategy": "",
|
||||||
"domain_strategy_local": "",
|
"domain_strategy_local": "",
|
||||||
"disable_traffic_bypass": false,
|
"disable_traffic_bypass": false,
|
||||||
"disable_rule_set": false,
|
"disable_sniff": false,
|
||||||
|
"disable_rule_action": false,
|
||||||
"remote_resolve": false,
|
"remote_resolve": false,
|
||||||
|
|
||||||
// DNS
|
// DNS
|
||||||
@ -113,9 +114,13 @@ Local sing-box domain strategy.
|
|||||||
|
|
||||||
`prefer_ipv4` is used by default.
|
`prefer_ipv4` is used by default.
|
||||||
|
|
||||||
#### disable_rule_set
|
#### disable_sniff
|
||||||
|
|
||||||
Use `geoip` and `geosite` for traffic bypassing instead of rule sets.
|
Don`t generate protocol sniffing options.
|
||||||
|
|
||||||
|
#### disable_rule_action
|
||||||
|
|
||||||
|
Don`t generate rule action options.
|
||||||
|
|
||||||
#### disable_traffic_bypass
|
#### disable_traffic_bypass
|
||||||
|
|
||||||
|
22
go.mod
22
go.mod
@ -11,13 +11,13 @@ require (
|
|||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
github.com/miekg/dns v1.1.62
|
github.com/miekg/dns v1.1.62
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||||
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f
|
github.com/sagernet/sing v0.6.0-alpha.16
|
||||||
github.com/sagernet/sing-box v1.10.2-0.20241107134520-fead0e42505f
|
github.com/sagernet/sing-box v1.11.0-alpha.17
|
||||||
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab
|
github.com/sagernet/sing-dns v0.4.0-alpha.2
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
golang.org/x/mod v0.21.0
|
golang.org/x/mod v0.21.0
|
||||||
golang.org/x/net v0.30.0
|
golang.org/x/net v0.31.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -68,12 +68,12 @@ require (
|
|||||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||||
github.com/sagernet/quic-go v0.48.1-beta.1 // indirect
|
github.com/sagernet/quic-go v0.48.1-beta.1 // indirect
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec // indirect
|
github.com/sagernet/sing-mux v0.3.0-alpha.1 // indirect
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.2 // indirect
|
github.com/sagernet/sing-quic v0.4.0-alpha.3 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
|
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
|
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f // indirect
|
github.com/sagernet/sing-tun v0.6.0-alpha.8 // indirect
|
||||||
github.com/sagernet/sing-vmess v0.1.12 // indirect
|
github.com/sagernet/sing-vmess v0.1.12 // indirect
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||||
github.com/sagernet/utls v1.6.7 // indirect
|
github.com/sagernet/utls v1.6.7 // indirect
|
||||||
@ -91,11 +91,11 @@ require (
|
|||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.27.0 // indirect
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/crypto v0.28.0 // indirect
|
golang.org/x/crypto v0.29.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.9.0 // indirect
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.27.0 // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
golang.org/x/text v0.20.0 // indirect
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.24.0 // indirect
|
golang.org/x/tools v0.24.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||||
|
48
go.sum
48
go.sum
@ -121,24 +121,24 @@ github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/
|
|||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f h1:A6+OeV5P1mok0eEEbLh4PidymZ6VZnTZ2uHwfapXgdU=
|
github.com/sagernet/sing v0.6.0-alpha.16 h1:Ml+nJa8J9d+Svqv2pBvJoet+8PF302Snb6wcMUXaoy4=
|
||||||
github.com/sagernet/sing v0.5.1-0.20241107131656-6e1285b5d82f/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.0-alpha.16/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-box v1.10.2-0.20241107134520-fead0e42505f h1:CkGSNvQvZlzm5VuKmc23DiY2WxxHbp1jYux0fh3xRW4=
|
github.com/sagernet/sing-box v1.11.0-alpha.17 h1:j7+LwRgXvcs1qqmNcj0ckQTzBnEl9FO3eEc5AQOdj3g=
|
||||||
github.com/sagernet/sing-box v1.10.2-0.20241107134520-fead0e42505f/go.mod h1:3Ujxk8+gNR1Kve68iZFjzxDHLGrFODdsvnth8oakrwk=
|
github.com/sagernet/sing-box v1.11.0-alpha.17/go.mod h1:5Gekm6mVOgaL+9JTmNu0vngMuSltxwjLUiHLCQ6gc6w=
|
||||||
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab h1:djP4EY/KM5T62xscormLflVi7eDlHv6p7md1FHMSArE=
|
github.com/sagernet/sing-dns v0.4.0-alpha.2 h1:0x5WjrO+Ifk9sqJlHRz/tKENHwoEinQ8HQCHAhpJHAQ=
|
||||||
github.com/sagernet/sing-dns v0.3.1-0.20241105104342-1914f319ddab/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M=
|
github.com/sagernet/sing-dns v0.4.0-alpha.2/go.mod h1:ZiXcacKL54jSSYZMbYF3qKNFkkW674Jt+85YCmK64K8=
|
||||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec h1:6Fd/VsEsw9qIjaGi1IBTZSb4b4v5JYtNcoiBtGsQC48=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||||
github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec/go.mod h1:RSwqqHwbtTOX3vs6ms8vMtBGH/0ZNyLm/uwt6TlmR84=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1/go.mod h1:FTcImmdfW38Lz7b+HQ+mxxOth1lz4ao8uEnz+MwIJQE=
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.2 h1:7vcC4bdS1GBJzHZhfmJiH0CfzQ4mYLUW51Z2RNHcGwc=
|
github.com/sagernet/sing-quic v0.4.0-alpha.3 h1:2svvOqgQCJg7FNrIrLTaRB6oDzXPiIyWIt9csjZxD6Q=
|
||||||
github.com/sagernet/sing-quic v0.3.0-rc.2/go.mod h1:3UOq51WVqzra7eCgod7t4hqnTaOiZzFUci9avMrtOqs=
|
github.com/sagernet/sing-quic v0.4.0-alpha.3/go.mod h1:Fmnpy0XoyYdjJrxNqEyl3LC9uLibMNNbxG7dt6HATY4=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f h1:gQwTgN/E4oHe3VlseD3/RhPs866cWcTsPG4dP6a8f8o=
|
github.com/sagernet/sing-tun v0.6.0-alpha.8 h1:HhXyUvXxtaXgT+IILZMq6kbrAyDbUwbN+Df/XxpL7Vo=
|
||||||
github.com/sagernet/sing-tun v0.4.0-rc.5.0.20241107062822-5a91eb99c90f/go.mod h1:Ehs5mZ3T8tTgV3H1Tx4Va5ixvyKjTAUPJ3G11dq7B/g=
|
github.com/sagernet/sing-tun v0.6.0-alpha.8/go.mod h1:JkgiLLnQUXln1zLGVoJqUwAulJGT0xoiPU4/pYF1fhU=
|
||||||
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
|
||||||
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
|
||||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
|
||||||
@ -188,18 +188,18 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
|||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -210,15 +210,15 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -38,7 +38,7 @@ func (o *Options) UnmarshalJSONContext(ctx context.Context, content []byte) erro
|
|||||||
type User struct {
|
type User struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Profile option.Listable[string] `json:"profile,omitempty"`
|
Profile badoption.Listable[string] `json:"profile,omitempty"`
|
||||||
DefaultProfile string `json:"default_profile,omitempty"`
|
DefaultProfile string `json:"default_profile,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ type Subscription struct {
|
|||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
UserAgent string `json:"user_agent,omitempty"`
|
UserAgent string `json:"user_agent,omitempty"`
|
||||||
UpdateInterval option.Duration `json:"update_interval,omitempty"`
|
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`
|
||||||
Process option.Listable[OutboundProcessOptions] `json:"process,omitempty"`
|
Process badoption.Listable[OutboundProcessOptions] `json:"process,omitempty"`
|
||||||
DeDuplication bool `json:"deduplication,omitempty"`
|
DeDuplication bool `json:"deduplication,omitempty"`
|
||||||
GenerateSelector bool `json:"generate_selector,omitempty"`
|
GenerateSelector bool `json:"generate_selector,omitempty"`
|
||||||
GenerateURLTest bool `json:"generate_urltest,omitempty"`
|
GenerateURLTest bool `json:"generate_urltest,omitempty"`
|
||||||
@ -61,10 +61,10 @@ type Subscription struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OutboundProcessOptions struct {
|
type OutboundProcessOptions struct {
|
||||||
Filter option.Listable[string] `json:"filter,omitempty"`
|
Filter badoption.Listable[string] `json:"filter,omitempty"`
|
||||||
Exclude option.Listable[string] `json:"exclude,omitempty"`
|
Exclude badoption.Listable[string] `json:"exclude,omitempty"`
|
||||||
FilterType option.Listable[string] `json:"filter_type,omitempty"`
|
FilterType badoption.Listable[string] `json:"filter_type,omitempty"`
|
||||||
ExcludeType option.Listable[string] `json:"exclude_type,omitempty"`
|
ExcludeType badoption.Listable[string] `json:"exclude_type,omitempty"`
|
||||||
Invert bool `json:"invert,omitempty"`
|
Invert bool `json:"invert,omitempty"`
|
||||||
Remove bool `json:"remove,omitempty"`
|
Remove bool `json:"remove,omitempty"`
|
||||||
Rename *badjson.TypedMap[string, string] `json:"rename,omitempty"`
|
Rename *badjson.TypedMap[string, string] `json:"rename,omitempty"`
|
||||||
@ -77,6 +77,6 @@ type Profile struct {
|
|||||||
Template string `json:"template,omitempty"`
|
Template string `json:"template,omitempty"`
|
||||||
TemplateForPlatform *badjson.TypedMap[string, string] `json:"template_for_platform,omitempty"`
|
TemplateForPlatform *badjson.TypedMap[string, string] `json:"template_for_platform,omitempty"`
|
||||||
TemplateForUserAgent *badjson.TypedMap[string, string] `json:"template_for_user_agent,omitempty"`
|
TemplateForUserAgent *badjson.TypedMap[string, string] `json:"template_for_user_agent,omitempty"`
|
||||||
Outbound option.Listable[string] `json:"outbound,omitempty"`
|
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
|
||||||
Subscription option.Listable[string] `json:"subscription,omitempty"`
|
Subscription badoption.Listable[string] `json:"subscription,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json"
|
"github.com/sagernet/sing/common/json"
|
||||||
"github.com/sagernet/sing/common/json/badjson"
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
)
|
)
|
||||||
|
|
||||||
type _Template struct {
|
type _Template struct {
|
||||||
@ -22,7 +23,8 @@ type _Template struct {
|
|||||||
DomainStrategy option.DomainStrategy `json:"domain_strategy,omitempty"`
|
DomainStrategy option.DomainStrategy `json:"domain_strategy,omitempty"`
|
||||||
DomainStrategyLocal option.DomainStrategy `json:"domain_strategy_local,omitempty"`
|
DomainStrategyLocal option.DomainStrategy `json:"domain_strategy_local,omitempty"`
|
||||||
DisableTrafficBypass bool `json:"disable_traffic_bypass,omitempty"`
|
DisableTrafficBypass bool `json:"disable_traffic_bypass,omitempty"`
|
||||||
DisableRuleSet bool `json:"disable_rule_set,omitempty"`
|
DisableSniff bool `json:"disable_sniff,omitempty"`
|
||||||
|
DisableRuleAction bool `json:"disable_rule_action,omitempty"`
|
||||||
RemoteResolve bool `json:"remote_resolve,omitempty"`
|
RemoteResolve bool `json:"remote_resolve,omitempty"`
|
||||||
|
|
||||||
// DNS
|
// DNS
|
||||||
@ -57,8 +59,6 @@ type _Template struct {
|
|||||||
PreRules []option.Rule `json:"pre_rules,omitempty"`
|
PreRules []option.Rule `json:"pre_rules,omitempty"`
|
||||||
CustomRules []option.Rule `json:"custom_rules,omitempty"`
|
CustomRules []option.Rule `json:"custom_rules,omitempty"`
|
||||||
EnableJSDelivr bool `json:"enable_jsdelivr,omitempty"`
|
EnableJSDelivr bool `json:"enable_jsdelivr,omitempty"`
|
||||||
CustomGeoIP *option.GeoIPOptions `json:"custom_geoip,omitempty"`
|
|
||||||
CustomGeosite *option.GeositeOptions `json:"custom_geosite,omitempty"`
|
|
||||||
CustomRuleSet []RuleSet `json:"custom_rule_set,omitempty"`
|
CustomRuleSet []RuleSet `json:"custom_rule_set,omitempty"`
|
||||||
PostRuleSet []RuleSet `json:"post_rule_set,omitempty"`
|
PostRuleSet []RuleSet `json:"post_rule_set,omitempty"`
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ type GitHubRuleSetOptions struct {
|
|||||||
Repository string `json:"repository,omitempty"`
|
Repository string `json:"repository,omitempty"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
Prefix string `json:"prefix,omitempty"`
|
Prefix string `json:"prefix,omitempty"`
|
||||||
RuleSet option.Listable[string] `json:"rule_set,omitempty"`
|
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Template) DisableIPv6() bool {
|
func (t Template) DisableIPv6() bool {
|
||||||
@ -137,8 +137,8 @@ type ExtraGroup struct {
|
|||||||
Target ExtraGroupTarget `json:"target,omitempty"`
|
Target ExtraGroupTarget `json:"target,omitempty"`
|
||||||
TagPerSubscription string `json:"tag_per_subscription,omitempty"`
|
TagPerSubscription string `json:"tag_per_subscription,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
Filter option.Listable[string] `json:"filter,omitempty"`
|
Filter badoption.Listable[string] `json:"filter,omitempty"`
|
||||||
Exclude option.Listable[string] `json:"exclude,omitempty"`
|
Exclude badoption.Listable[string] `json:"exclude,omitempty"`
|
||||||
CustomSelector *option.SelectorOutboundOptions `json:"custom_selector,omitempty"`
|
CustomSelector *option.SelectorOutboundOptions `json:"custom_selector,omitempty"`
|
||||||
CustomURLTest *option.URLTestOutboundOptions `json:"custom_urltest,omitempty"`
|
CustomURLTest *option.URLTestOutboundOptions `json:"custom_urltest,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,16 @@ func (p *Profile) Render(metadata metadata.Metadata) (*boxOption.Options, error)
|
|||||||
outbounds := common.Filter(p.manager.outbounds, func(it []boxOption.Outbound) bool {
|
outbounds := common.Filter(p.manager.outbounds, func(it []boxOption.Outbound) bool {
|
||||||
return common.Contains(p.Outbound, it[0].Tag)
|
return common.Contains(p.Outbound, it[0].Tag)
|
||||||
})
|
})
|
||||||
subscriptions := common.Filter(p.manager.subscription.Subscriptions(), func(it *subscription.Subscription) bool {
|
var subscriptions []*subscription.Subscription
|
||||||
return common.Contains(p.Subscription, it.Name)
|
for _, subscriptionName := range p.Subscription {
|
||||||
|
subscription := common.Find(p.manager.subscription.Subscriptions(), func(it *subscription.Subscription) bool {
|
||||||
|
return it.Name == subscriptionName
|
||||||
})
|
})
|
||||||
|
if subscription == nil {
|
||||||
|
return nil, E.New("render profile[", p.Name, "]: subscription not found: ", subscriptionName)
|
||||||
|
}
|
||||||
|
subscriptions = append(subscriptions, subscription)
|
||||||
|
}
|
||||||
options, err := selectedTemplate.Render(p.manager.ctx, metadata, p.Name, outbounds, subscriptions)
|
options, err := selectedTemplate.Render(p.manager.ctx, metadata, p.Name, outbounds, subscriptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/format"
|
"github.com/sagernet/sing/common/format"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter"
|
"github.com/Dreamacro/clash/adapter"
|
||||||
@ -224,10 +225,10 @@ func clashPluginOptions(plugin string, opts map[string]any) string {
|
|||||||
func clashTransport(network string, httpOpts clash_outbound.HTTPOptions, h2Opts clash_outbound.HTTP2Options, grpcOpts clash_outbound.GrpcOptions, wsOpts clash_outbound.WSOptions) *option.V2RayTransportOptions {
|
func clashTransport(network string, httpOpts clash_outbound.HTTPOptions, h2Opts clash_outbound.HTTP2Options, grpcOpts clash_outbound.GrpcOptions, wsOpts clash_outbound.WSOptions) *option.V2RayTransportOptions {
|
||||||
switch network {
|
switch network {
|
||||||
case "http":
|
case "http":
|
||||||
var headers map[string]option.Listable[string]
|
var headers map[string]badoption.Listable[string]
|
||||||
for key, values := range httpOpts.Headers {
|
for key, values := range httpOpts.Headers {
|
||||||
if headers == nil {
|
if headers == nil {
|
||||||
headers = make(map[string]option.Listable[string])
|
headers = make(map[string]badoption.Listable[string])
|
||||||
}
|
}
|
||||||
headers[key] = values
|
headers[key] = values
|
||||||
}
|
}
|
||||||
@ -255,10 +256,10 @@ func clashTransport(network string, httpOpts clash_outbound.HTTPOptions, h2Opts
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
case "ws":
|
case "ws":
|
||||||
var headers map[string]option.Listable[string]
|
var headers map[string]badoption.Listable[string]
|
||||||
for key, value := range wsOpts.Headers {
|
for key, value := range wsOpts.Headers {
|
||||||
if headers == nil {
|
if headers == nil {
|
||||||
headers = make(map[string]option.Listable[string])
|
headers = make(map[string]badoption.Listable[string])
|
||||||
}
|
}
|
||||||
headers[key] = []string{value}
|
headers[key] = []string{value}
|
||||||
}
|
}
|
||||||
|
@ -19,22 +19,36 @@ func TestFilter1100(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
DefaultOptions: option.DefaultDNSRule{
|
DefaultOptions: option.DefaultDNSRule{
|
||||||
|
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||||
RuleSet: []string{"test"},
|
RuleSet: []string{"test"},
|
||||||
|
},
|
||||||
|
DNSRuleAction: option.DNSRuleAction{
|
||||||
|
Action: C.RuleActionTypeRoute,
|
||||||
|
RouteOptions: option.DNSRouteActionOptions{
|
||||||
Server: "test",
|
Server: "test",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
Route: &option.RouteOptions{
|
Route: &option.RouteOptions{
|
||||||
Rules: []option.Rule{
|
Rules: []option.Rule{
|
||||||
{
|
{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
DefaultOptions: option.DefaultRule{
|
DefaultOptions: option.DefaultRule{
|
||||||
RuleSet: []string{"test"},
|
RawDefaultRule: option.RawDefaultRule{
|
||||||
|
Domain: []string{"example.com"},
|
||||||
|
},
|
||||||
|
RuleAction: option.RuleAction{
|
||||||
|
Action: C.RuleActionTypeRoute,
|
||||||
|
RouteOptions: option.RouteActionOptions{
|
||||||
Outbound: "test",
|
Outbound: "test",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
RuleSet: []option.RuleSet{
|
RuleSet: []option.RuleSet{
|
||||||
{
|
{
|
||||||
Type: C.RuleSetTypeInline,
|
Type: C.RuleSetTypeInline,
|
||||||
@ -61,22 +75,36 @@ func TestFilter1100(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
DefaultOptions: option.DefaultDNSRule{
|
DefaultOptions: option.DefaultDNSRule{
|
||||||
|
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||||
Domain: []string{"example.com"},
|
Domain: []string{"example.com"},
|
||||||
|
},
|
||||||
|
DNSRuleAction: option.DNSRuleAction{
|
||||||
|
Action: C.RuleActionTypeRoute,
|
||||||
|
RouteOptions: option.DNSRouteActionOptions{
|
||||||
Server: "test",
|
Server: "test",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
Route: &option.RouteOptions{
|
Route: &option.RouteOptions{
|
||||||
Rules: []option.Rule{
|
Rules: []option.Rule{
|
||||||
{
|
{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
DefaultOptions: option.DefaultRule{
|
DefaultOptions: option.DefaultRule{
|
||||||
|
RawDefaultRule: option.RawDefaultRule{
|
||||||
Domain: []string{"example.com"},
|
Domain: []string{"example.com"},
|
||||||
|
},
|
||||||
|
RuleAction: option.RuleAction{
|
||||||
|
Action: C.RuleActionTypeRoute,
|
||||||
|
RouteOptions: option.RouteActionOptions{
|
||||||
Outbound: "test",
|
Outbound: "test",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -167,22 +167,7 @@ func (t *Template) renderDNS(metadata M.Metadata, options *option.Options) error
|
|||||||
options.DNS.Rules = append(options.DNS.Rules, t.PreDNSRules...)
|
options.DNS.Rules = append(options.DNS.Rules, t.PreDNSRules...)
|
||||||
if len(t.CustomDNSRules) == 0 {
|
if len(t.CustomDNSRules) == 0 {
|
||||||
if !t.DisableTrafficBypass {
|
if !t.DisableTrafficBypass {
|
||||||
if t.DisableRuleSet || (metadata.Version != nil && metadata.Version.LessThan(semver.ParseVersion("1.8.0-alpha.10"))) {
|
|
||||||
options.DNS.Rules = append(options.DNS.Rules, option.DNSRule{
|
|
||||||
Type: C.RuleTypeDefault,
|
|
||||||
DefaultOptions: option.DefaultDNSRule{
|
|
||||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
|
||||||
Geosite: []string{"geolocation-cn"},
|
|
||||||
},
|
|
||||||
DNSRuleAction: option.DNSRuleAction{
|
|
||||||
Action: C.RuleActionTypeRoute,
|
|
||||||
RouteOptions: option.DNSRouteActionOptions{
|
|
||||||
Server: DNSLocalTag,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
options.DNS.Rules = append(options.DNS.Rules, option.DNSRule{
|
options.DNS.Rules = append(options.DNS.Rules, option.DNSRule{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
DefaultOptions: option.DefaultDNSRule{
|
DefaultOptions: option.DefaultDNSRule{
|
||||||
@ -197,7 +182,6 @@ func (t *Template) renderDNS(metadata M.Metadata, options *option.Options) error
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
|
||||||
if !t.DisableDNSLeak && (metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.9.0-alpha.1"))) {
|
if !t.DisableDNSLeak && (metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.9.0-alpha.1"))) {
|
||||||
options.DNS.Rules = append(options.DNS.Rules, option.DNSRule{
|
options.DNS.Rules = append(options.DNS.Rules, option.DNSRule{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
@ -224,12 +208,6 @@ func (t *Template) renderDNS(metadata M.Metadata, options *option.Options) error
|
|||||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||||
RuleSet: []string{"geosite-geolocation-!cn"},
|
RuleSet: []string{"geosite-geolocation-!cn"},
|
||||||
},
|
},
|
||||||
DNSRuleAction: option.DNSRuleAction{
|
|
||||||
Action: C.RuleActionTypeRoute,
|
|
||||||
RouteOptions: option.DNSRouteActionOptions{
|
|
||||||
Server: DNSDefaultTag,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -238,12 +216,6 @@ func (t *Template) renderDNS(metadata M.Metadata, options *option.Options) error
|
|||||||
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
RawDefaultDNSRule: option.RawDefaultDNSRule{
|
||||||
RuleSet: []string{"geoip-cn"},
|
RuleSet: []string{"geoip-cn"},
|
||||||
},
|
},
|
||||||
DNSRuleAction: option.DNSRuleAction{
|
|
||||||
Action: C.RuleActionTypeRoute,
|
|
||||||
RouteOptions: option.DNSRouteActionOptions{
|
|
||||||
Server: DNSLocalTag,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,6 @@ package template
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
M "github.com/sagernet/serenity/common/metadata"
|
M "github.com/sagernet/serenity/common/metadata"
|
||||||
"github.com/sagernet/serenity/common/semver"
|
|
||||||
"github.com/sagernet/serenity/constant"
|
"github.com/sagernet/serenity/constant"
|
||||||
"github.com/sagernet/serenity/option"
|
"github.com/sagernet/serenity/option"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@ -10,37 +9,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (t *Template) renderGeoResources(metadata M.Metadata, options *boxOption.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
|
|
||||||
geositeDownloadURL string
|
|
||||||
downloadDetour string
|
|
||||||
)
|
|
||||||
if t.EnableJSDelivr {
|
|
||||||
geoipDownloadURL = "https://testingcf.jsdelivr.net/gh/SagerNet/sing-geoip@release/geoip-cn.db"
|
|
||||||
geositeDownloadURL = "https://testingcf.jsdelivr.net/gh/SagerNet/sing-geosite@release/geosite-cn.db"
|
|
||||||
if t.DirectTag != "" {
|
|
||||||
downloadDetour = t.DirectTag
|
|
||||||
} else {
|
|
||||||
downloadDetour = DefaultDirectTag
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
geoipDownloadURL = "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip-cn.db"
|
|
||||||
geositeDownloadURL = "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite-cn.db"
|
|
||||||
}
|
|
||||||
if t.CustomGeoIP == nil {
|
|
||||||
options.Route.GeoIP = &boxOption.GeoIPOptions{
|
|
||||||
DownloadURL: geoipDownloadURL,
|
|
||||||
DownloadDetour: downloadDetour,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t.CustomGeosite == nil {
|
|
||||||
options.Route.Geosite = &boxOption.GeositeOptions{
|
|
||||||
DownloadURL: geositeDownloadURL,
|
|
||||||
DownloadDetour: downloadDetour,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(t.CustomRuleSet) == 0 {
|
if len(t.CustomRuleSet) == 0 {
|
||||||
var (
|
var (
|
||||||
downloadURL string
|
downloadURL string
|
||||||
@ -91,7 +59,6 @@ func (t *Template) renderGeoResources(metadata M.Metadata, options *boxOption.Op
|
|||||||
}
|
}
|
||||||
options.Route.RuleSet = append(options.Route.RuleSet, t.renderRuleSet(t.PostRuleSet)...)
|
options.Route.RuleSet = append(options.Route.RuleSet, t.renderRuleSet(t.PostRuleSet)...)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Template) renderRuleSet(ruleSets []option.RuleSet) []boxOption.RuleSet {
|
func (t *Template) renderRuleSet(ruleSets []option.RuleSet) []boxOption.RuleSet {
|
||||||
var result []boxOption.RuleSet
|
var result []boxOption.RuleSet
|
||||||
|
@ -9,16 +9,14 @@ import (
|
|||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/json/badjson"
|
"github.com/sagernet/sing/common/json/badjson"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *Template) renderInbounds(metadata M.Metadata, options *option.Options) error {
|
func (t *Template) renderInbounds(metadata M.Metadata, options *option.Options) error {
|
||||||
options.Inbounds = t.Inbounds
|
options.Inbounds = t.Inbounds
|
||||||
var needSniff bool
|
|
||||||
if !t.DisableTrafficBypass {
|
|
||||||
needSniff = true
|
|
||||||
}
|
|
||||||
var domainStrategy option.DomainStrategy
|
var domainStrategy option.DomainStrategy
|
||||||
if !t.RemoteResolve {
|
if !t.RemoteResolve {
|
||||||
if t.DomainStrategy != option.DomainStrategy(dns.DomainStrategyAsIS) {
|
if t.DomainStrategy != option.DomainStrategy(dns.DomainStrategyAsIS) {
|
||||||
@ -27,6 +25,7 @@ func (t *Template) renderInbounds(metadata M.Metadata, options *option.Options)
|
|||||||
domainStrategy = option.DomainStrategy(dns.DomainStrategyPreferIPv4)
|
domainStrategy = option.DomainStrategy(dns.DomainStrategyPreferIPv4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
disableRuleAction := t.DisableRuleAction || (metadata.Version != nil && metadata.Version.LessThan(semver.ParseVersion("1.11.0-alpha.7")))
|
||||||
autoRedirect := t.AutoRedirect &&
|
autoRedirect := t.AutoRedirect &&
|
||||||
!metadata.Platform.IsApple() &&
|
!metadata.Platform.IsApple() &&
|
||||||
(metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.10.0-alpha.2")))
|
(metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.10.0-alpha.2")))
|
||||||
@ -40,9 +39,6 @@ func (t *Template) renderInbounds(metadata M.Metadata, options *option.Options)
|
|||||||
tunOptions := &option.TunInboundOptions{
|
tunOptions := &option.TunInboundOptions{
|
||||||
AutoRoute: true,
|
AutoRoute: true,
|
||||||
Address: address,
|
Address: address,
|
||||||
InboundOptions: option.InboundOptions{
|
|
||||||
SniffEnabled: needSniff,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
tunInbound := option.Inbound{
|
tunInbound := option.Inbound{
|
||||||
Type: C.TypeTun,
|
Type: C.TypeTun,
|
||||||
@ -54,11 +50,16 @@ func (t *Template) renderInbounds(metadata M.Metadata, options *option.Options)
|
|||||||
tunOptions.RouteExcludeAddressSet = []string{"geoip-cn"}
|
tunOptions.RouteExcludeAddressSet = []string{"geoip-cn"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if metadata.Platform == M.PlatformUnknown {
|
||||||
|
tunOptions.StrictRoute = true
|
||||||
|
}
|
||||||
|
if disableRuleAction {
|
||||||
|
tunOptions.InboundOptions = option.InboundOptions{
|
||||||
|
SniffEnabled: !t.DisableSniff,
|
||||||
|
}
|
||||||
if t.EnableFakeIP {
|
if t.EnableFakeIP {
|
||||||
tunOptions.DomainStrategy = domainStrategy
|
tunOptions.DomainStrategy = domainStrategy
|
||||||
}
|
}
|
||||||
if metadata.Platform == M.PlatformUnknown {
|
|
||||||
tunOptions.StrictRoute = true
|
|
||||||
}
|
}
|
||||||
if !t.DisableSystemProxy && metadata.Platform != M.PlatformUnknown {
|
if !t.DisableSystemProxy && metadata.Platform != M.PlatformUnknown {
|
||||||
var httpPort uint16
|
var httpPort uint16
|
||||||
@ -90,15 +91,17 @@ func (t *Template) renderInbounds(metadata M.Metadata, options *option.Options)
|
|||||||
if disableTun || !t.DisableSystemProxy {
|
if disableTun || !t.DisableSystemProxy {
|
||||||
mixedOptions := &option.HTTPMixedInboundOptions{
|
mixedOptions := &option.HTTPMixedInboundOptions{
|
||||||
ListenOptions: option.ListenOptions{
|
ListenOptions: option.ListenOptions{
|
||||||
Listen: option.NewListenAddress(netip.AddrFrom4([4]byte{127, 0, 0, 1})),
|
Listen: common.Ptr(badoption.Addr(netip.AddrFrom4([4]byte{127, 0, 0, 1}))),
|
||||||
ListenPort: DefaultMixedPort,
|
ListenPort: DefaultMixedPort,
|
||||||
InboundOptions: option.InboundOptions{
|
|
||||||
SniffEnabled: needSniff,
|
|
||||||
DomainStrategy: domainStrategy,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
SetSystemProxy: metadata.Platform == M.PlatformUnknown && disableTun && !t.DisableSystemProxy,
|
SetSystemProxy: metadata.Platform == M.PlatformUnknown && disableTun && !t.DisableSystemProxy,
|
||||||
}
|
}
|
||||||
|
if disableRuleAction {
|
||||||
|
mixedOptions.InboundOptions = option.InboundOptions{
|
||||||
|
SniffEnabled: !t.DisableSniff,
|
||||||
|
DomainStrategy: domainStrategy,
|
||||||
|
}
|
||||||
|
}
|
||||||
mixedInbound := option.Inbound{
|
mixedInbound := option.Inbound{
|
||||||
Type: C.TypeMixed,
|
Type: C.TypeMixed,
|
||||||
Options: mixedOptions,
|
Options: mixedOptions,
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
M "github.com/sagernet/serenity/common/metadata"
|
M "github.com/sagernet/serenity/common/metadata"
|
||||||
|
"github.com/sagernet/serenity/common/semver"
|
||||||
"github.com/sagernet/serenity/option"
|
"github.com/sagernet/serenity/option"
|
||||||
"github.com/sagernet/serenity/subscription"
|
"github.com/sagernet/serenity/subscription"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@ -16,6 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (t *Template) renderOutbounds(metadata M.Metadata, options *boxOption.Options, outbounds [][]boxOption.Outbound, subscriptions []*subscription.Subscription) error {
|
func (t *Template) renderOutbounds(metadata M.Metadata, options *boxOption.Options, outbounds [][]boxOption.Outbound, subscriptions []*subscription.Subscription) error {
|
||||||
|
disableRuleAction := t.DisableRuleAction || (metadata.Version != nil && metadata.Version.LessThan(semver.ParseVersion("1.11.0-alpha.7")))
|
||||||
defaultTag := t.DefaultTag
|
defaultTag := t.DefaultTag
|
||||||
if defaultTag == "" {
|
if defaultTag == "" {
|
||||||
defaultTag = DefaultDefaultTag
|
defaultTag = DefaultDefaultTag
|
||||||
@ -35,22 +37,26 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *boxOption.Optio
|
|||||||
Type: C.TypeDirect,
|
Type: C.TypeDirect,
|
||||||
Options: common.Ptr(common.PtrValueOrDefault(t.CustomDirect)),
|
Options: common.Ptr(common.PtrValueOrDefault(t.CustomDirect)),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Tag: blockTag,
|
|
||||||
Type: C.TypeBlock,
|
|
||||||
Options: &boxOption.StubOptions{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Tag: DNSTag,
|
|
||||||
Type: C.TypeDNS,
|
|
||||||
Options: &boxOption.StubOptions{},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Tag: defaultTag,
|
Tag: defaultTag,
|
||||||
Type: C.TypeSelector,
|
Type: C.TypeSelector,
|
||||||
Options: common.Ptr(common.PtrValueOrDefault(t.CustomSelector)),
|
Options: common.Ptr(common.PtrValueOrDefault(t.CustomSelector)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if disableRuleAction {
|
||||||
|
options.Outbounds = append(options.Outbounds,
|
||||||
|
boxOption.Outbound{
|
||||||
|
Tag: blockTag,
|
||||||
|
Type: C.TypeBlock,
|
||||||
|
Options: &boxOption.StubOptions{},
|
||||||
|
},
|
||||||
|
boxOption.Outbound{
|
||||||
|
Tag: DNSTag,
|
||||||
|
Type: C.TypeDNS,
|
||||||
|
Options: &boxOption.StubOptions{},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
urlTestTag := t.URLTestTag
|
urlTestTag := t.URLTestTag
|
||||||
if urlTestTag == "" {
|
if urlTestTag == "" {
|
||||||
urlTestTag = DefaultURLTestTag
|
urlTestTag = DefaultURLTestTag
|
||||||
|
@ -11,15 +11,14 @@ import (
|
|||||||
func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) error {
|
func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) error {
|
||||||
if options.Route == nil {
|
if options.Route == nil {
|
||||||
options.Route = &option.RouteOptions{
|
options.Route = &option.RouteOptions{
|
||||||
GeoIP: t.CustomGeoIP,
|
|
||||||
Geosite: t.CustomGeosite,
|
|
||||||
RuleSet: t.renderRuleSet(t.CustomRuleSet),
|
RuleSet: t.renderRuleSet(t.CustomRuleSet),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !t.DisableTrafficBypass {
|
if !t.DisableTrafficBypass {
|
||||||
t.renderGeoResources(metadata, options)
|
t.renderGeoResources(metadata, options)
|
||||||
}
|
}
|
||||||
disable18Features := metadata.Version != nil && metadata.Version.LessThan(semver.ParseVersion("1.8.0-alpha.10"))
|
disableRuleAction := t.DisableRuleAction || (metadata.Version != nil && metadata.Version.LessThan(semver.ParseVersion("1.11.0-alpha.7")))
|
||||||
|
if disableRuleAction {
|
||||||
options.Route.Rules = []option.Rule{
|
options.Route.Rules = []option.Rule{
|
||||||
{
|
{
|
||||||
Type: C.RuleTypeLogical,
|
Type: C.RuleTypeLogical,
|
||||||
@ -55,6 +54,48 @@ func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) err
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
options.Route.Rules = []option.Rule{
|
||||||
|
{
|
||||||
|
Type: C.RuleTypeDefault,
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
RuleAction: option.RuleAction{
|
||||||
|
Action: C.RuleActionTypeSniff,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.RuleTypeLogical,
|
||||||
|
LogicalOptions: option.LogicalRule{
|
||||||
|
RawLogicalRule: option.RawLogicalRule{
|
||||||
|
Mode: C.LogicalTypeOr,
|
||||||
|
Rules: []option.Rule{
|
||||||
|
{
|
||||||
|
Type: C.RuleTypeDefault,
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
RawDefaultRule: option.RawDefaultRule{
|
||||||
|
Network: []string{N.NetworkUDP},
|
||||||
|
Port: []uint16{53},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.RuleTypeDefault,
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
RawDefaultRule: option.RawDefaultRule{
|
||||||
|
Protocol: []string{C.ProtocolDNS},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RuleAction: option.RuleAction{
|
||||||
|
Action: C.RuleActionTypeHijackDNS,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
directTag := t.DirectTag
|
directTag := t.DirectTag
|
||||||
defaultTag := t.DefaultTag
|
defaultTag := t.DefaultTag
|
||||||
if directTag == "" {
|
if directTag == "" {
|
||||||
@ -63,22 +104,6 @@ func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) err
|
|||||||
if defaultTag == "" {
|
if defaultTag == "" {
|
||||||
defaultTag = DefaultDefaultTag
|
defaultTag = DefaultDefaultTag
|
||||||
}
|
}
|
||||||
if disable18Features {
|
|
||||||
options.Route.Rules = append(options.Route.Rules, option.Rule{
|
|
||||||
Type: C.RuleTypeDefault,
|
|
||||||
DefaultOptions: option.DefaultRule{
|
|
||||||
RawDefaultRule: option.RawDefaultRule{
|
|
||||||
GeoIP: []string{"private"},
|
|
||||||
},
|
|
||||||
RuleAction: option.RuleAction{
|
|
||||||
Action: C.RuleActionTypeRoute,
|
|
||||||
RouteOptions: option.RouteActionOptions{
|
|
||||||
Outbound: directTag,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
options.Route.Rules = append(options.Route.Rules, option.Rule{
|
options.Route.Rules = append(options.Route.Rules, option.Rule{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
DefaultOptions: option.DefaultRule{
|
DefaultOptions: option.DefaultRule{
|
||||||
@ -93,7 +118,6 @@ func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) err
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
|
||||||
if !t.DisableClashMode {
|
if !t.DisableClashMode {
|
||||||
modeGlobal := t.ClashModeGlobal
|
modeGlobal := t.ClashModeGlobal
|
||||||
modeDirect := t.ClashModeDirect
|
modeDirect := t.ClashModeDirect
|
||||||
@ -131,26 +155,19 @@ func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) err
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
options.Route.Rules = append(options.Route.Rules, t.PreRules...)
|
if disableRuleAction {
|
||||||
if len(t.CustomRules) == 0 {
|
|
||||||
if !t.DisableTrafficBypass {
|
|
||||||
if t.DisableRuleSet || disable18Features {
|
|
||||||
options.Route.Rules = append(options.Route.Rules, option.Rule{
|
options.Route.Rules = append(options.Route.Rules, option.Rule{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
DefaultOptions: option.DefaultRule{
|
DefaultOptions: option.DefaultRule{
|
||||||
RawDefaultRule: option.RawDefaultRule{
|
|
||||||
GeoIP: []string{"cn"},
|
|
||||||
Geosite: []string{"geolocation-cn"},
|
|
||||||
},
|
|
||||||
RuleAction: option.RuleAction{
|
RuleAction: option.RuleAction{
|
||||||
Action: C.RuleActionTypeRoute,
|
Action: C.RuleActionTypeResolve,
|
||||||
RouteOptions: option.RouteActionOptions{
|
|
||||||
Outbound: directTag,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
}
|
||||||
|
options.Route.Rules = append(options.Route.Rules, t.PreRules...)
|
||||||
|
if len(t.CustomRules) == 0 {
|
||||||
|
if !t.DisableTrafficBypass {
|
||||||
options.Route.Rules = append(options.Route.Rules, option.Rule{
|
options.Route.Rules = append(options.Route.Rules, option.Rule{
|
||||||
Type: C.RuleTypeDefault,
|
Type: C.RuleTypeDefault,
|
||||||
DefaultOptions: option.DefaultRule{
|
DefaultOptions: option.DefaultRule{
|
||||||
@ -166,7 +183,6 @@ func (t *Template) renderRoute(metadata M.Metadata, options *option.Options) err
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
options.Route.Rules = append(options.Route.Rules, t.CustomRules...)
|
options.Route.Rules = append(options.Route.Rules, t.CustomRules...)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user