From d7af8bd589ba71430868ed458e307ea9d874d7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sat, 6 Jul 2024 08:09:19 +0800 Subject: [PATCH] Add support for inline rule-sets --- cmd/serenity/cmd_run.go | 2 +- go.mod | 21 +- go.sum | 55 ++-- template/filter/filter.go | 10 +- template/filter/filter_1100.go | 238 ++++++++++++++---- template/filter/filter_170.go | 92 ------- template/filter/filter_180.go | 58 ----- template/filter/filter_190.go | 17 +- .../filter/filter_null_group_reference.go | 3 +- template/filter/filter_test.go | 81 ++++++ template/manager.go | 2 +- template/render_experimental.go | 2 +- template/render_inbounds.go | 4 +- template/template.go | 5 +- 14 files changed, 337 insertions(+), 253 deletions(-) delete mode 100644 template/filter/filter_170.go delete mode 100644 template/filter/filter_180.go create mode 100644 template/filter/filter_test.go diff --git a/cmd/serenity/cmd_run.go b/cmd/serenity/cmd_run.go index bd69202..f234ce9 100644 --- a/cmd/serenity/cmd_run.go +++ b/cmd/serenity/cmd_run.go @@ -110,7 +110,7 @@ func readConfigAndMerge() (option.Options, error) { } var mergedMessage json.RawMessage for _, options := range optionsList { - mergedMessage, err = badjson.MergeJSON(options.options.RawMessage, mergedMessage) + mergedMessage, err = badjson.MergeJSON(options.options.RawMessage, mergedMessage, false) if err != nil { return option.Options{}, E.Cause(err, "merge config at ", options.path) } diff --git a/go.mod b/go.mod index 9427638..5f1c6d5 100644 --- a/go.mod +++ b/go.mod @@ -7,12 +7,13 @@ require ( github.com/go-chi/chi/v5 v5.0.12 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 - github.com/miekg/dns v1.1.59 + github.com/miekg/dns v1.1.61 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a - github.com/sagernet/sing v0.5.0-alpha.10.0.20240615025518-3de31664ce39 - github.com/sagernet/sing-box v1.10.0-alpha.13 - github.com/sagernet/sing-dns v0.3.0-beta.5 + github.com/sagernet/sing v0.5.0-alpha.12 + github.com/sagernet/sing-box v1.10.0-alpha.19 + github.com/sagernet/sing-dns v0.3.0-beta.10 github.com/spf13/cobra v1.8.0 + github.com/stretchr/testify v1.9.0 golang.org/x/mod v0.18.0 golang.org/x/net v0.26.0 ) @@ -25,6 +26,7 @@ require ( github.com/caddyserver/certmagic v0.20.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/cretz/bine v0.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gaukas/godicttls v0.0.4 // indirect @@ -55,21 +57,22 @@ require ( github.com/oschwald/geoip2-golang v1.9.0 // indirect github.com/oschwald/maxminddb-golang v1.12.0 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect + github.com/sagernet/fswatch v0.1.1 // indirect github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect - github.com/sagernet/quic-go v0.45.0-beta.2 // indirect + github.com/sagernet/quic-go v0.45.1-beta.2 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/sing-mux v0.2.0 // indirect - github.com/sagernet/sing-quic v0.2.0-beta.9 // indirect - github.com/sagernet/sing-shadowsocks v0.2.6 // indirect + github.com/sagernet/sing-quic v0.2.0-beta.12 // indirect github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect - github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240616073727-85f5f2dd587d // indirect - github.com/sagernet/sing-vmess v0.1.8 // indirect + github.com/sagernet/sing-tun v0.4.0-beta.13.0.20240703164908-1f043289199d // indirect + github.com/sagernet/sing-vmess v0.1.11 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect github.com/sagernet/utls v1.5.4 // indirect diff --git a/go.sum b/go.sum index 1d0eb5c..c34e315 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054= @@ -82,8 +84,10 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= +github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= +github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= @@ -107,49 +111,37 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY= github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k= +github.com/sagernet/fswatch v0.1.1 h1:YqID+93B7VRfqIH3PArW/XpJv5H4OLEVWDfProGoRQs= +github.com/sagernet/fswatch v0.1.1/go.mod h1:nz85laH0mkQqJfaOrqPpkwtU1znMFNVTpT/5oRsVz/o= github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I= github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= -github.com/sagernet/quic-go v0.43.1-beta.1 h1:alizUjpvWYcz08dBCQsULOd+1xu0o7UtlyYf6SLbRNg= -github.com/sagernet/quic-go v0.43.1-beta.1/go.mod h1:BkrQYeop7Jx3hN3TW8/76CXcdhYiNPyYEBL/BVJ1ifc= -github.com/sagernet/quic-go v0.45.0-beta.2 h1:nWq9KJTR+cGU8UU4E20XNjdM6QgbLkBgpq+NCExg5RY= -github.com/sagernet/quic-go v0.45.0-beta.2/go.mod h1:rs3XCo3SQ2sB96NtaKnEyq+ZkyaKWL51BvIW3veaiWw= +github.com/sagernet/quic-go v0.45.1-beta.2 h1:zkEeCbhdFFkrxKcuIRBtXNKci/1t2J/39QSG/sPvlmc= +github.com/sagernet/quic-go v0.45.1-beta.2/go.mod h1:+N3FqM9DAzOWfe64uxXuBejVJwX7DeW7BslzLO6N/xI= 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/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing v0.5.0-alpha.10 h1:kuHl10gpjbKQAdQfyogQU3u0CVnpqC3wrAHe/+BFaXc= -github.com/sagernet/sing v0.5.0-alpha.10/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing v0.5.0-alpha.10.0.20240615025518-3de31664ce39 h1:Q1Hlje8FURQLHyDq8V2myk/YG+0RPdcnTRZ7Of9/iv8= -github.com/sagernet/sing v0.5.0-alpha.10.0.20240615025518-3de31664ce39/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= -github.com/sagernet/sing-box v1.10.0-alpha.7 h1:J3osMCs6ODluRnLfDmoFut9rCpJe/32K9ChpDjG99WI= -github.com/sagernet/sing-box v1.10.0-alpha.7/go.mod h1:fccWloAcFMMFqwYitclTwgUFXJSkC9fNmtbBL6AUhoc= -github.com/sagernet/sing-box v1.10.0-alpha.13 h1:Pmw0U9vB0BYqg5J41KjEzoW93GzpyFdFHp1hg+FxNTE= -github.com/sagernet/sing-box v1.10.0-alpha.13/go.mod h1:xrhprxb5867wn85fGfXcTbrFs2dcatQv/WUvgIudMQQ= -github.com/sagernet/sing-dns v0.3.0-beta.1 h1:ZARhK79eWYckWcGtYBLIT68LnwfbdE2EojkpIpoPGlc= -github.com/sagernet/sing-dns v0.3.0-beta.1/go.mod h1:k/dmFcQpg6+m08gC1yQBy+13+QkuLqpKr4bIreq4U24= -github.com/sagernet/sing-dns v0.3.0-beta.5 h1:lX+wfnBVaOlSd7+GBgb431Tt/gmYwJXSHvS1HutfnD4= -github.com/sagernet/sing-dns v0.3.0-beta.5/go.mod h1:qeO/lOUK/c3Zczp5a1VO13fbmolaM8xGKCUXtaX0/NQ= +github.com/sagernet/sing v0.5.0-alpha.12 h1:pjffG3SUpuF9PLDCqPO2fOAUozXItIBmnMVTKQ/QMhM= +github.com/sagernet/sing v0.5.0-alpha.12/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing-box v1.10.0-alpha.19 h1:nsl5T5MAemZdHpsOd10WyTArMsg7QGrNiY1wEsj/ZBM= +github.com/sagernet/sing-box v1.10.0-alpha.19/go.mod h1:LjkcUc0igCyGrX3Ac1JPq2UzUYFQyfCr69f0e0oTOa8= +github.com/sagernet/sing-dns v0.3.0-beta.10 h1:Js61EjQXVpcu2VDegWEQTH1isCcVwJju8WEHYgG4tQ0= +github.com/sagernet/sing-dns v0.3.0-beta.10/go.mod h1:nXE6EYMXahB5DV3AcXYbFfuorqF7tbQ86kxweSxRKM4= github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo= github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= -github.com/sagernet/sing-quic v0.2.0-beta.5 h1:ceKFLd1iS5AtM+pScKmcDp5k7R6WgYIe8vl6nB0aVsE= -github.com/sagernet/sing-quic v0.2.0-beta.5/go.mod h1:lfad61lScAZhAxZ0DHZWvEIcAaT38O6zPTR4vLsHeP0= -github.com/sagernet/sing-quic v0.2.0-beta.9 h1:gfqUwVgKA6APwFOPhwge9VrPZ0XQtmuXF8hbbIVZML8= -github.com/sagernet/sing-quic v0.2.0-beta.9/go.mod h1:hb6RwYy9Js0gZi80zlTBWABMOIvJDv46K5yak/pbZ4w= -github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= -github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM= +github.com/sagernet/sing-quic v0.2.0-beta.12 h1:BhvA5mmrDFEyDUQB5eeu+9UhF+ieyuNJ5Rsb0dAG3QY= +github.com/sagernet/sing-quic v0.2.0-beta.12/go.mod h1:YVpLfVi8BvYM7NMrjmnvcRm3E8iMETf1gFQmTQDN9jI= 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-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240616071054-61dfac626b74 h1:GcPy5uRjdIdjMvAlx4ZESq2K+9vZNnQpTSPRMXdsHfY= -github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240616071054-61dfac626b74/go.mod h1:YgaSM4cm+YIn6erBZN/eF+sW7I27BfBt91EWGo53MME= -github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240616073727-85f5f2dd587d h1:ppBKR8H4x64nbtselcGdGOrIMJUIXQL135s5tNUcQMQ= -github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240616073727-85f5f2dd587d/go.mod h1:YgaSM4cm+YIn6erBZN/eF+sW7I27BfBt91EWGo53MME= -github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= -github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= +github.com/sagernet/sing-tun v0.4.0-beta.13.0.20240703164908-1f043289199d h1:2nBM9W9fOCM45hjlu1Fh9qyzBCgKEkq+SOuRCbCCs7c= +github.com/sagernet/sing-tun v0.4.0-beta.13.0.20240703164908-1f043289199d/go.mod h1:81JwnnYw8X9W9XvmZetSTTiPgIE3SbAbnc+EHKwPJ5U= +github.com/sagernet/sing-vmess v0.1.11 h1:Kq20MJOBrZzxyHko+/fPHuTszYxe41ClbiNt0TTaqP4= +github.com/sagernet/sing-vmess v0.1.11/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/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI= @@ -244,8 +236,9 @@ google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/template/filter/filter.go b/template/filter/filter.go index 7664365..04a35ba 100644 --- a/template/filter/filter.go +++ b/template/filter/filter.go @@ -5,12 +5,16 @@ import ( boxOption "github.com/sagernet/sing-box/option" ) -type OptionsFilter func(metadata metadata.Metadata, options *boxOption.Options) +type OptionsFilter func(metadata metadata.Metadata, options *boxOption.Options) error var filters []OptionsFilter -func Filter(metadata metadata.Metadata, options *boxOption.Options) { +func Filter(metadata metadata.Metadata, options *boxOption.Options) error { for _, filter := range filters { - filter(metadata, options) + err := filter(metadata, options) + if err != nil { + return err + } } + return nil } diff --git a/template/filter/filter_1100.go b/template/filter/filter_1100.go index 7f6efc2..17e571e 100644 --- a/template/filter/filter_1100.go +++ b/template/filter/filter_1100.go @@ -8,57 +8,207 @@ import ( C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" ) func init() { filters = append(filters, filter1100) } -func filter1100(metadata metadata.Metadata, options *option.Options) { - if metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.10.0-alpha.13")) { - return +func filter1100(metadata metadata.Metadata, options *option.Options) error { + if metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.10.0-alpha.19")) { + return nil } - newInbounds := make([]option.Inbound, 0, len(options.Inbounds)) - for _, inbound := range options.Inbounds { - if inbound.Type == C.TypeTun { - inbound.TunOptions.AutoRedirect = false - inbound.TunOptions.RouteAddressSet = nil - inbound.TunOptions.RouteExcludeAddressSet = nil - //nolint:staticcheck - //goland:noinspection GoDeprecation - if len(inbound.TunOptions.Address) > 0 { - inbound.TunOptions.Inet4Address = append(inbound.TunOptions.Inet4Address, common.Filter(inbound.TunOptions.Address, func(it netip.Prefix) bool { - return it.Addr().Is4() - })...) - inbound.TunOptions.Inet6Address = append(inbound.TunOptions.Inet6Address, common.Filter(inbound.TunOptions.Address, func(it netip.Prefix) bool { - return it.Addr().Is6() - })...) - inbound.TunOptions.Address = nil - } - //nolint:staticcheck - //goland:noinspection GoDeprecation - if len(inbound.TunOptions.RouteAddress) > 0 { - inbound.TunOptions.Inet4RouteAddress = append(inbound.TunOptions.Inet4RouteAddress, common.Filter(inbound.TunOptions.RouteAddress, func(it netip.Prefix) bool { - return it.Addr().Is4() - })...) - inbound.TunOptions.Inet6RouteAddress = append(inbound.TunOptions.Inet6RouteAddress, common.Filter(inbound.TunOptions.RouteAddress, func(it netip.Prefix) bool { - return it.Addr().Is6() - })...) - inbound.TunOptions.RouteAddress = nil - } - //nolint:staticcheck - //goland:noinspection GoDeprecation - if len(inbound.TunOptions.RouteExcludeAddress) > 0 { - inbound.TunOptions.Inet4RouteExcludeAddress = append(inbound.TunOptions.Inet4RouteExcludeAddress, common.Filter(inbound.TunOptions.RouteExcludeAddress, func(it netip.Prefix) bool { - return it.Addr().Is4() - })...) - inbound.TunOptions.Inet6RouteExcludeAddress = append(inbound.TunOptions.Inet6RouteExcludeAddress, common.Filter(inbound.TunOptions.RouteExcludeAddress, func(it netip.Prefix) bool { - return it.Addr().Is6() - })...) - inbound.TunOptions.RouteExcludeAddress = nil - } + var newRuleSets []option.RuleSet + var inlineRuleSets []option.RuleSet + for _, ruleSet := range options.Route.RuleSet { + if ruleSet.Type == C.RuleSetTypeInline { + inlineRuleSets = append(inlineRuleSets, ruleSet) + } else { + newRuleSets = append(newRuleSets, ruleSet) } - newInbounds = append(newInbounds, inbound) } - options.Inbounds = newInbounds + options.Route.RuleSet = newRuleSets + if len(inlineRuleSets) > 0 { + var ( + currentRules []option.Rule + newRules []option.Rule + ) + currentRules = options.Route.Rules + for _, inlineRuleSet := range inlineRuleSets { + for i, rule := range currentRules { + newRuleItems, err := expandInlineRule(inlineRuleSet, rule) + if err != nil { + return E.Cause(err, "expand rule[", i, "]") + } + newRules = append(newRules, newRuleItems...) + } + currentRules = newRules + newRules = newRules[:0] + } + options.Route.Rules = currentRules + + var ( + currentDNSRules []option.DNSRule + newDNSRules []option.DNSRule + ) + currentDNSRules = options.DNS.Rules + for _, inlineRuleSet := range inlineRuleSets { + for i, rule := range currentDNSRules { + newRuleItems, err := expandInlineDNSRule(inlineRuleSet, rule) + if err != nil { + return E.Cause(err, "expand dns rule[", i, "]") + } + newDNSRules = append(newDNSRules, newRuleItems...) + } + currentDNSRules = newDNSRules + newDNSRules = newDNSRules[:0] + } + options.DNS.Rules = currentDNSRules + } + options.Route.Rules = common.Filter(options.Route.Rules, filter1100Rule) + options.DNS.Rules = common.Filter(options.DNS.Rules, filter1100DNSRule) + if metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.10.0-alpha.13")) { + return nil + } + if len(options.Inbounds) > 0 { + newInbounds := make([]option.Inbound, 0, len(options.Inbounds)) + for _, inbound := range options.Inbounds { + if inbound.Type == C.TypeTun { + inbound.TunOptions.AutoRedirect = false + inbound.TunOptions.RouteAddressSet = nil + inbound.TunOptions.RouteExcludeAddressSet = nil + //nolint:staticcheck + //goland:noinspection GoDeprecation + if len(inbound.TunOptions.Address) > 0 { + inbound.TunOptions.Inet4Address = append(inbound.TunOptions.Inet4Address, common.Filter(inbound.TunOptions.Address, func(it netip.Prefix) bool { + return it.Addr().Is4() + })...) + inbound.TunOptions.Inet6Address = append(inbound.TunOptions.Inet6Address, common.Filter(inbound.TunOptions.Address, func(it netip.Prefix) bool { + return it.Addr().Is6() + })...) + inbound.TunOptions.Address = nil + } + //nolint:staticcheck + //goland:noinspection GoDeprecation + if len(inbound.TunOptions.RouteAddress) > 0 { + inbound.TunOptions.Inet4RouteAddress = append(inbound.TunOptions.Inet4RouteAddress, common.Filter(inbound.TunOptions.RouteAddress, func(it netip.Prefix) bool { + return it.Addr().Is4() + })...) + inbound.TunOptions.Inet6RouteAddress = append(inbound.TunOptions.Inet6RouteAddress, common.Filter(inbound.TunOptions.RouteAddress, func(it netip.Prefix) bool { + return it.Addr().Is6() + })...) + inbound.TunOptions.RouteAddress = nil + } + //nolint:staticcheck + //goland:noinspection GoDeprecation + if len(inbound.TunOptions.RouteExcludeAddress) > 0 { + inbound.TunOptions.Inet4RouteExcludeAddress = append(inbound.TunOptions.Inet4RouteExcludeAddress, common.Filter(inbound.TunOptions.RouteExcludeAddress, func(it netip.Prefix) bool { + return it.Addr().Is4() + })...) + inbound.TunOptions.Inet6RouteExcludeAddress = append(inbound.TunOptions.Inet6RouteExcludeAddress, common.Filter(inbound.TunOptions.RouteExcludeAddress, func(it netip.Prefix) bool { + return it.Addr().Is6() + })...) + inbound.TunOptions.RouteExcludeAddress = nil + } + } + newInbounds = append(newInbounds, inbound) + } + options.Inbounds = newInbounds + } + return nil +} + +func expandInlineRule(ruleSet option.RuleSet, rule option.Rule) ([]option.Rule, error) { + var ( + newRules []option.Rule + err error + ) + if rule.Type == C.RuleTypeLogical { + for i := range rule.LogicalOptions.Rules { + newRules, err = expandInlineRule(ruleSet, rule.LogicalOptions.Rules[i]) + if err != nil { + return nil, E.Cause(err, "[", i, "]") + } + newRules = append(newRules, newRules...) + } + rule.LogicalOptions.Rules = newRules + return []option.Rule{rule}, nil + } + if !common.Contains(rule.DefaultOptions.RuleSet, ruleSet.Tag) { + return []option.Rule{rule}, nil + } + rule.DefaultOptions.RuleSet = common.Filter(rule.DefaultOptions.RuleSet, func(it string) bool { + return it != ruleSet.Tag + }) + for i, hRule := range ruleSet.InlineOptions.Rules { + var ( + rawRule json.RawMessage + newRule option.Rule + ) + rawRule, err = json.Marshal(hRule) + if err != nil { + return nil, E.Cause(err, "marshal inline rule ", ruleSet.Tag, "[", i, "]") + } + newRule, err = badjson.MergeFromSource(rawRule, rule, false) + if err != nil { + return nil, E.Cause(err, "merge inline rule ", ruleSet.Tag, "[", i, "]") + } + newRules = append(newRules, newRule) + } + return newRules, nil +} + +func expandInlineDNSRule(ruleSet option.RuleSet, rule option.DNSRule) ([]option.DNSRule, error) { + var ( + newRules []option.DNSRule + err error + ) + if rule.Type == C.RuleTypeLogical { + for i := range rule.LogicalOptions.Rules { + newRules, err = expandInlineDNSRule(ruleSet, rule.LogicalOptions.Rules[i]) + if err != nil { + return nil, E.Cause(err, "[", i, "]") + } + newRules = append(newRules, newRules...) + } + rule.LogicalOptions.Rules = newRules + return []option.DNSRule{rule}, nil + } + if !common.Contains(rule.DefaultOptions.RuleSet, ruleSet.Tag) { + return []option.DNSRule{rule}, nil + } + rule.DefaultOptions.RuleSet = common.Filter(rule.DefaultOptions.RuleSet, func(it string) bool { + return it != ruleSet.Tag + }) + for i, hRule := range ruleSet.InlineOptions.Rules { + var ( + rawRule json.RawMessage + newRule option.DNSRule + ) + rawRule, err = json.Marshal(hRule) + if err != nil { + return nil, E.Cause(err, "marshal inline rule ", ruleSet.Tag, "[", i, "]") + } + newRule, err = badjson.MergeFromSource(rawRule, rule, false) + if err != nil { + return nil, E.Cause(err, "merge inline rule ", ruleSet.Tag, "[", i, "]") + } + newRules = append(newRules, newRule) + } + return newRules, nil +} + +func filter1100Rule(it option.Rule) bool { + return !hasRule([]option.Rule{it}, func(it option.DefaultRule) bool { + return it.RuleSetIPCIDRMatchSource + }) +} + +func filter1100DNSRule(it option.DNSRule) bool { + return !hasDNSRule([]option.DNSRule{it}, func(it option.DefaultDNSRule) bool { + return it.RuleSetIPCIDRMatchSource || it.RuleSetIPCIDRAcceptEmpty + }) } diff --git a/template/filter/filter_170.go b/template/filter/filter_170.go deleted file mode 100644 index 46fdd07..0000000 --- a/template/filter/filter_170.go +++ /dev/null @@ -1,92 +0,0 @@ -package filter - -import ( - "github.com/sagernet/serenity/common/metadata" - "github.com/sagernet/serenity/common/semver" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing/common" -) - -func init() { - filters = append(filters, filter170) -} - -func filter170(metadata metadata.Metadata, options *option.Options) { - if metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.Version{Major: 1, Minor: 7}) { - return - } - newInbounds := make([]option.Inbound, 0, len(options.Inbounds)) - for _, inbound := range options.Inbounds { - switch inbound.Type { - case C.TypeTun: - inbound.TunOptions = filter170Tun(inbound.TunOptions) - inbound.TunOptions.InboundOptions = filter170InboundOptions(inbound.TunOptions.InboundOptions) - case C.TypeRedirect: - inbound.RedirectOptions.InboundOptions = filter170InboundOptions(inbound.RedirectOptions.InboundOptions) - case C.TypeTProxy: - inbound.TProxyOptions.InboundOptions = filter170InboundOptions(inbound.TProxyOptions.InboundOptions) - case C.TypeDirect: - inbound.DirectOptions.InboundOptions = filter170InboundOptions(inbound.DirectOptions.InboundOptions) - case C.TypeSOCKS: - inbound.SocksOptions.InboundOptions = filter170InboundOptions(inbound.SocksOptions.InboundOptions) - case C.TypeHTTP: - inbound.HTTPOptions.InboundOptions = filter170InboundOptions(inbound.HTTPOptions.InboundOptions) - case C.TypeMixed: - inbound.MixedOptions.InboundOptions = filter170InboundOptions(inbound.MixedOptions.InboundOptions) - case C.TypeShadowsocks: - inbound.ShadowsocksOptions.InboundOptions = filter170InboundOptions(inbound.ShadowsocksOptions.InboundOptions) - inbound.ShadowsocksOptions.Multiplex = nil - case C.TypeVMess: - inbound.VMessOptions.InboundOptions = filter170InboundOptions(inbound.VMessOptions.InboundOptions) - inbound.VMessOptions.Multiplex = nil - case C.TypeTrojan: - inbound.TrojanOptions.InboundOptions = filter170InboundOptions(inbound.TrojanOptions.InboundOptions) - inbound.TrojanOptions.Multiplex = nil - case C.TypeNaive: - inbound.NaiveOptions.InboundOptions = filter170InboundOptions(inbound.NaiveOptions.InboundOptions) - case C.TypeHysteria: - inbound.HysteriaOptions.InboundOptions = filter170InboundOptions(inbound.HysteriaOptions.InboundOptions) - case C.TypeShadowTLS: - inbound.ShadowTLSOptions.InboundOptions = filter170InboundOptions(inbound.ShadowTLSOptions.InboundOptions) - case C.TypeVLESS: - inbound.VLESSOptions.InboundOptions = filter170InboundOptions(inbound.VLESSOptions.InboundOptions) - inbound.VLESSOptions.Multiplex = nil - case C.TypeTUIC: - inbound.TUICOptions.InboundOptions = filter170InboundOptions(inbound.TUICOptions.InboundOptions) - case C.TypeHysteria2: - inbound.Hysteria2Options.InboundOptions = filter170InboundOptions(inbound.Hysteria2Options.InboundOptions) - default: - continue - } - newInbounds = append(newInbounds, inbound) - } - options.Inbounds = newInbounds - if options.Route != nil { - options.Route.Rules = common.Filter(options.Route.Rules, filter170Rule) - } - if options.DNS != nil { - options.DNS.Rules = common.Filter(options.DNS.Rules, filter170DNSRule) - } -} - -//nolint:staticcheck -//goland:noinspection GoDeprecation -func filter170Tun(options option.TunInboundOptions) option.TunInboundOptions { - options.Inet4RouteExcludeAddress = nil - options.Inet6RouteExcludeAddress = nil - return options -} - -func filter170InboundOptions(options option.InboundOptions) option.InboundOptions { - options.UDPDisableDomainUnmapping = false - return options -} - -func filter170Rule(it option.Rule) bool { - return !hasRule([]option.Rule{it}, isWIFIRule) -} - -func filter170DNSRule(it option.DNSRule) bool { - return !hasDNSRule([]option.DNSRule{it}, isWIFIDNSRule) -} diff --git a/template/filter/filter_180.go b/template/filter/filter_180.go deleted file mode 100644 index 9954f06..0000000 --- a/template/filter/filter_180.go +++ /dev/null @@ -1,58 +0,0 @@ -package filter - -import ( - "github.com/sagernet/serenity/common/metadata" - "github.com/sagernet/serenity/common/semver" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing/common" -) - -func init() { - filters = append(filters, filter180) -} - -func filter180(metadata metadata.Metadata, options *option.Options) { - if metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.8.0-alpha.10")) { - return - } - for index, outbound := range options.Outbounds { - switch outbound.Type { - case C.TypeURLTest: - options.Outbounds[index].URLTestOptions = filter180a10URLTest(outbound.URLTestOptions) - } - } - if metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.8.0-alpha.5")) { - return - } - options.Route.RuleSet = nil - if options.Route != nil { - options.Route.Rules = common.Filter(options.Route.Rules, filter180Rule) - } - if options.DNS != nil { - options.DNS.Rules = common.Filter(options.DNS.Rules, filter180DNSRule) - } - if metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.8.0-alpha.1")) { - return - } - if options.Route != nil { - options.Route.Rules = common.Filter(options.Route.Rules, filter180a5Rule) - } -} - -func filter180Rule(it option.Rule) bool { - return !hasRule([]option.Rule{it}, isRuleSetRule) -} - -func filter180DNSRule(it option.DNSRule) bool { - return !hasDNSRule([]option.DNSRule{it}, isRuleSetDNSRule) -} - -func filter180a5Rule(it option.Rule) bool { - return !hasRule([]option.Rule{it}, isIPIsPrivateRule) -} - -func filter180a10URLTest(options option.URLTestOutboundOptions) option.URLTestOutboundOptions { - options.IdleTimeout = 0 - return options -} diff --git a/template/filter/filter_190.go b/template/filter/filter_190.go index a8050e1..e3fb2df 100644 --- a/template/filter/filter_190.go +++ b/template/filter/filter_190.go @@ -12,16 +12,16 @@ func init() { filters = append(filters, filter190) } -func filter190(metadata metadata.Metadata, options *option.Options) { +func filter190(metadata metadata.Metadata, options *option.Options) error { if metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.9.0-alpha.1")) { - return + return nil } if options.DNS == nil || len(options.DNS.Rules) == 0 { - return + return nil } options.DNS.Rules = common.Filter(options.DNS.Rules, filter190DNSRule) if metadata.Version == nil || metadata.Version.GreaterThanOrEqual(semver.ParseVersion("1.9.0-alpha.10")) { - return + return nil } for _, inbound := range options.Inbounds { switch inbound.Type { @@ -36,12 +36,11 @@ func filter190(metadata metadata.Metadata, options *option.Options) { } } } + return nil } func filter190DNSRule(it option.DNSRule) bool { - return !hasDNSRule([]option.DNSRule{it}, isAddressFilterRule) -} - -func isAddressFilterRule(it option.DefaultDNSRule) bool { - return len(it.GeoIP) > 0 || len(it.IPCIDR) > 0 || it.IPIsPrivate + return !hasDNSRule([]option.DNSRule{it}, func(it option.DefaultDNSRule) bool { + return len(it.GeoIP) > 0 || len(it.IPCIDR) > 0 || it.IPIsPrivate + }) } diff --git a/template/filter/filter_null_group_reference.go b/template/filter/filter_null_group_reference.go index bcfc38c..72092ed 100644 --- a/template/filter/filter_null_group_reference.go +++ b/template/filter/filter_null_group_reference.go @@ -11,7 +11,7 @@ func init() { filters = append(filters, filterNullGroupReference) } -func filterNullGroupReference(metadata M.Metadata, options *option.Options) { +func filterNullGroupReference(metadata M.Metadata, options *option.Options) error { outboundTags := common.Map(options.Outbounds, func(it option.Outbound) string { return it.Tag }) @@ -30,4 +30,5 @@ func filterNullGroupReference(metadata M.Metadata, options *option.Options) { } options.Outbounds[i] = outbound } + return nil } diff --git a/template/filter/filter_test.go b/template/filter/filter_test.go new file mode 100644 index 0000000..199004c --- /dev/null +++ b/template/filter/filter_test.go @@ -0,0 +1,81 @@ +package filter + +import ( + "testing" + + "github.com/sagernet/serenity/common/metadata" + "github.com/sagernet/serenity/common/semver" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/option" + + "github.com/stretchr/testify/require" +) + +func TestFilter1100(t *testing.T) { + options := &option.Options{ + DNS: &option.DNSOptions{ + Rules: []option.DNSRule{ + { + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultDNSRule{ + RuleSet: []string{"test"}, + Server: "test", + }, + }, + }, + }, + Route: &option.RouteOptions{ + Rules: []option.Rule{ + { + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultRule{ + RuleSet: []string{"test"}, + Outbound: "test", + }, + }, + }, + RuleSet: []option.RuleSet{ + { + Type: C.RuleSetTypeInline, + Tag: "test", + InlineOptions: option.PlainRuleSet{ + Rules: []option.HeadlessRule{ + { + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultHeadlessRule{ + Domain: []string{"example.com"}, + }, + }, + }, + }, + }, + }, + }, + } + err := filter1100(metadata.Metadata{Version: &semver.Version{Major: 1, Minor: 9, Patch: 3}}, options) + require.NoError(t, err) + require.Equal(t, options, &option.Options{ + DNS: &option.DNSOptions{ + Rules: []option.DNSRule{ + { + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultDNSRule{ + Domain: []string{"example.com"}, + Server: "test", + }, + }, + }, + }, + Route: &option.RouteOptions{ + Rules: []option.Rule{ + { + Type: C.RuleTypeDefault, + DefaultOptions: option.DefaultRule{ + Domain: []string{"example.com"}, + Outbound: "test", + }, + }, + }, + }, + }) +} diff --git a/template/manager.go b/template/manager.go index 416f2d5..83508cc 100644 --- a/template/manager.go +++ b/template/manager.go @@ -41,7 +41,7 @@ func extendTemplate(rawTemplates []option.Template, root, current option.Templat } next = newNext } - newRawTemplate, err := badjson.MergeJSON(next.RawMessage, current.RawMessage) + newRawTemplate, err := badjson.MergeJSON(next.RawMessage, current.RawMessage, true) if err != nil { return option.Template{}, E.Cause(err, "initialize template[", current.Name, "]: merge extended template: ", current.Extend) } diff --git a/template/render_experimental.go b/template/render_experimental.go index fc0ebd8..3b48644 100644 --- a/template/render_experimental.go +++ b/template/render_experimental.go @@ -37,7 +37,7 @@ func (t *Template) renderExperimental(metadata M.Metadata, options *option.Optio } if t.CustomClashAPI != nil { - newClashOptions, err := badjson.MergeFromDestination(options.Experimental.ClashAPI, t.CustomClashAPI.Message) + newClashOptions, err := badjson.MergeFromDestination(options.Experimental.ClashAPI, t.CustomClashAPI.Message, true) if err != nil { return err } diff --git a/template/render_inbounds.go b/template/render_inbounds.go index 7933b5b..136fab7 100644 --- a/template/render_inbounds.go +++ b/template/render_inbounds.go @@ -77,7 +77,7 @@ func (t *Template) renderInbounds(metadata M.Metadata, options *option.Options) } } if t.CustomTUN != nil { - newTUNOptions, err := badjson.MergeFromDestination(tunInbound.TunOptions, t.CustomTUN.Message) + newTUNOptions, err := badjson.MergeFromDestination(tunInbound.TunOptions, t.CustomTUN.Message, true) if err != nil { return E.Cause(err, "merge custom tun options") } @@ -101,7 +101,7 @@ func (t *Template) renderInbounds(metadata M.Metadata, options *option.Options) }, } if t.CustomMixed != nil { - newMixedOptions, err := badjson.MergeFromDestination(mixedInbound.MixedOptions, t.CustomMixed.Message) + newMixedOptions, err := badjson.MergeFromDestination(mixedInbound.MixedOptions, t.CustomMixed.Message, true) if err != nil { return E.Cause(err, "merge custom mixed options") } diff --git a/template/template.go b/template/template.go index 2582d3a..d707714 100644 --- a/template/template.go +++ b/template/template.go @@ -62,6 +62,9 @@ func (t *Template) Render(metadata M.Metadata, profileName string, outbounds [][ if err != nil { return nil, E.Cause(err, "render experimental") } - filter.Filter(metadata, &options) + err = filter.Filter(metadata, &options) + if err != nil { + return nil, E.Cause(err, "filter options") + } return &options, nil }