mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Add RuleSet ClashAPI (Rule Provider)
This commit is contained in:
parent
bdfe6ec626
commit
ac283332e3
@ -4,10 +4,11 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/common/geoip"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing-tun"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
tun "github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
@ -32,6 +33,7 @@ type Router interface {
|
||||
LoadGeosite(code string) (Rule, error)
|
||||
|
||||
RuleSet(tag string) (RuleSet, bool)
|
||||
RuleSets() []RuleSet
|
||||
|
||||
Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
|
||||
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
|
||||
@ -87,6 +89,8 @@ type DNSRule interface {
|
||||
}
|
||||
|
||||
type RuleSet interface {
|
||||
Tag() string
|
||||
Type() string
|
||||
StartContext(ctx context.Context, startContext RuleSetStartContext) error
|
||||
PostStart() error
|
||||
Metadata() RuleSetMetadata
|
||||
@ -97,6 +101,9 @@ type RuleSet interface {
|
||||
type RuleSetMetadata struct {
|
||||
ContainsProcessRule bool
|
||||
ContainsWIFIRule bool
|
||||
RuleNum int
|
||||
LastUpdated time.Time
|
||||
Format string
|
||||
}
|
||||
|
||||
type RuleSetStartContext interface {
|
||||
|
@ -1,34 +1,49 @@
|
||||
package clashapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common/json/badjson"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func ruleProviderRouter() http.Handler {
|
||||
func ruleProviderRouter(router adapter.Router) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getRuleProviders)
|
||||
r.Get("/", getRuleProviders(router))
|
||||
|
||||
r.Route("/{name}", func(r chi.Router) {
|
||||
r.Use(parseProviderName, findRuleProviderByName)
|
||||
r.Use(parseProviderName, findRuleProviderByName(router))
|
||||
r.Get("/", getRuleProvider)
|
||||
r.Put("/", updateRuleProvider)
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func getRuleProviders(w http.ResponseWriter, r *http.Request) {
|
||||
func getRuleProviders(router adapter.Router) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ruleSets := router.RuleSets()
|
||||
if len(ruleSets) == 0 {
|
||||
render.JSON(w, r, render.M{
|
||||
"providers": []string{},
|
||||
})
|
||||
}
|
||||
m := render.M{}
|
||||
for _, ruleSet := range ruleSets {
|
||||
m[ruleSet.Tag()] = ruleProviderInfo(ruleSet)
|
||||
}
|
||||
render.JSON(w, r, render.M{
|
||||
"providers": m,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getRuleProvider(w http.ResponseWriter, r *http.Request) {
|
||||
// provider := r.Context().Value(CtxKeyProvider).(provider.RuleProvider)
|
||||
// render.JSON(w, r, provider)
|
||||
render.NoContent(w, r)
|
||||
ruleSet := r.Context().Value(CtxKeyProvider).(adapter.RuleSet)
|
||||
render.JSON(w, r, ruleProviderInfo(ruleSet))
|
||||
}
|
||||
|
||||
func updateRuleProvider(w http.ResponseWriter, r *http.Request) {
|
||||
@ -41,18 +56,36 @@ func updateRuleProvider(w http.ResponseWriter, r *http.Request) {
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
func findRuleProviderByName(next http.Handler) http.Handler {
|
||||
func findRuleProviderByName(router adapter.Router) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
/*name := r.Context().Value(CtxKeyProviderName).(string)
|
||||
providers := tunnel.RuleProviders()
|
||||
provider, exist := providers[name]
|
||||
if !exist {*/
|
||||
name := r.Context().Value(CtxKeyProviderName).(string)
|
||||
ruleSet, exist := router.RuleSet(name)
|
||||
if !exist {
|
||||
render.Status(r, http.StatusNotFound)
|
||||
render.JSON(w, r, ErrNotFound)
|
||||
//return
|
||||
//}
|
||||
return
|
||||
}
|
||||
|
||||
// ctx := context.WithValue(r.Context(), CtxKeyProvider, provider)
|
||||
// next.ServeHTTP(w, r.WithContext(ctx))
|
||||
ctx := context.WithValue(r.Context(), CtxKeyProvider, ruleSet)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ruleProviderInfo(ruleSet adapter.RuleSet) *badjson.JSONObject {
|
||||
var info badjson.JSONObject
|
||||
info.Put("name", ruleSet.Tag())
|
||||
info.Put("type", "Rule")
|
||||
if ruleSet.Type() == "remote" {
|
||||
info.Put("vehicleType", "HTTP")
|
||||
} else {
|
||||
info.Put("vehicleType", "File")
|
||||
}
|
||||
metadata := ruleSet.Metadata()
|
||||
info.Put("format", metadata.Format)
|
||||
info.Put("behavior", "sing")
|
||||
info.Put("ruleCount", metadata.RuleNum)
|
||||
info.Put("updatedAt", metadata.LastUpdated)
|
||||
return &info
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||
r.Mount("/rules", ruleRouter(router))
|
||||
r.Mount("/connections", connectionRouter(router, trafficManager))
|
||||
r.Mount("/providers/proxies", proxyProviderRouter())
|
||||
r.Mount("/providers/rules", ruleProviderRouter())
|
||||
r.Mount("/providers/rules", ruleProviderRouter(router))
|
||||
r.Mount("/script", scriptRouter())
|
||||
r.Mount("/profile", profileRouter())
|
||||
r.Mount("/cache", cacheRouter(ctx))
|
||||
|
@ -26,10 +26,10 @@ import (
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing-box/outbound"
|
||||
"github.com/sagernet/sing-box/transport/fakeip"
|
||||
"github.com/sagernet/sing-dns"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
mux "github.com/sagernet/sing-mux"
|
||||
"github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing-vmess"
|
||||
tun "github.com/sagernet/sing-tun"
|
||||
vmess "github.com/sagernet/sing-vmess"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
@ -713,6 +713,10 @@ func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
|
||||
return ruleSet, loaded
|
||||
}
|
||||
|
||||
func (r *Router) RuleSets() []adapter.RuleSet {
|
||||
return r.ruleSets
|
||||
}
|
||||
|
||||
func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||
if r.pauseManager.IsDevicePaused() {
|
||||
return E.New("reject connection to ", metadata.Destination, " while device paused")
|
||||
|
@ -3,6 +3,7 @@ package route
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/srs"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
var _ adapter.RuleSet = (*LocalRuleSet)(nil)
|
||||
|
||||
type LocalRuleSet struct {
|
||||
tag string
|
||||
rules []adapter.HeadlessRule
|
||||
metadata adapter.RuleSetMetadata
|
||||
}
|
||||
@ -53,7 +55,18 @@ func NewLocalRuleSet(router adapter.Router, options option.RuleSet) (*LocalRuleS
|
||||
var metadata adapter.RuleSetMetadata
|
||||
metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
||||
metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
||||
return &LocalRuleSet{rules, metadata}, nil
|
||||
metadata.RuleNum = len(rules)
|
||||
metadata.LastUpdated = time.Now()
|
||||
metadata.Format = options.Format
|
||||
return &LocalRuleSet{options.Tag, rules, metadata}, nil
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) Tag() string {
|
||||
return s.tag
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) Type() string {
|
||||
return "local"
|
||||
}
|
||||
|
||||
func (s *LocalRuleSet) Match(metadata *adapter.InboundContext) bool {
|
||||
|
@ -59,6 +59,14 @@ func NewRemoteRuleSet(ctx context.Context, router adapter.Router, logger logger.
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RemoteRuleSet) Tag() string {
|
||||
return s.options.Tag
|
||||
}
|
||||
|
||||
func (s *RemoteRuleSet) Type() string {
|
||||
return "remote"
|
||||
}
|
||||
|
||||
func (s *RemoteRuleSet) Match(metadata *adapter.InboundContext) bool {
|
||||
for _, rule := range s.rules {
|
||||
if rule.Match(metadata) {
|
||||
@ -117,7 +125,10 @@ func (s *RemoteRuleSet) PostStart() error {
|
||||
}
|
||||
|
||||
func (s *RemoteRuleSet) Metadata() adapter.RuleSetMetadata {
|
||||
return s.metadata
|
||||
metadata := s.metadata
|
||||
metadata.LastUpdated = s.lastUpdated
|
||||
metadata.Format = s.options.Format
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (s *RemoteRuleSet) loadBytes(content []byte) error {
|
||||
@ -152,6 +163,8 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
|
||||
}
|
||||
s.metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
||||
s.metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
||||
s.metadata.RuleNum = len(rules)
|
||||
s.lastUpdated = time.Now()
|
||||
s.rules = rules
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user