mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 08:31:30 +00:00
Add clash mode support
This commit is contained in:
parent
80cfc9a25b
commit
5297273937
|
@ -9,18 +9,15 @@ import (
|
|||
|
||||
type ClashServer interface {
|
||||
Service
|
||||
TrafficController
|
||||
Mode() string
|
||||
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
|
||||
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
|
||||
}
|
||||
|
||||
type Tracker interface {
|
||||
Leave()
|
||||
}
|
||||
|
||||
type TrafficController interface {
|
||||
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
|
||||
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
|
||||
}
|
||||
|
||||
type OutboundGroup interface {
|
||||
Now() string
|
||||
All() []string
|
||||
|
|
|
@ -39,7 +39,9 @@ type Router interface {
|
|||
InterfaceMonitor() tun.DefaultInterfaceMonitor
|
||||
PackageManager() tun.PackageManager
|
||||
Rules() []Rule
|
||||
SetTrafficController(controller TrafficController)
|
||||
|
||||
ClashServer() ClashServer
|
||||
SetClashServer(controller ClashServer)
|
||||
}
|
||||
|
||||
type Rule interface {
|
||||
|
|
2
box.go
2
box.go
|
@ -154,7 +154,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
|
|||
if err != nil {
|
||||
return nil, E.Cause(err, "create clash api server")
|
||||
}
|
||||
router.SetTrafficController(clashServer)
|
||||
router.SetClashServer(clashServer)
|
||||
}
|
||||
return &Box{
|
||||
router: router,
|
||||
|
|
|
@ -2,6 +2,7 @@ package clashapi
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/log"
|
||||
|
||||
|
@ -9,11 +10,11 @@ import (
|
|||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func configRouter(logFactory log.Factory) http.Handler {
|
||||
func configRouter(server *Server, logFactory log.Factory, logger log.Logger) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Get("/", getConfigs(logFactory))
|
||||
r.Get("/", getConfigs(server, logFactory))
|
||||
r.Put("/", updateConfigs)
|
||||
r.Patch("/", patchConfigs)
|
||||
r.Patch("/", patchConfigs(server, logger))
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -31,7 +32,7 @@ type configSchema struct {
|
|||
Tun map[string]any `json:"tun"`
|
||||
}
|
||||
|
||||
func getConfigs(logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
|
||||
func getConfigs(server *Server, logFactory log.Factory) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
logLevel := logFactory.Level()
|
||||
if logLevel == log.LevelTrace {
|
||||
|
@ -40,15 +41,31 @@ func getConfigs(logFactory log.Factory) func(w http.ResponseWriter, r *http.Requ
|
|||
logLevel = log.LevelError
|
||||
}
|
||||
render.JSON(w, r, &configSchema{
|
||||
Mode: "rule",
|
||||
Mode: server.mode,
|
||||
BindAddress: "*",
|
||||
LogLevel: log.FormatLevel(logLevel),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func patchConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
func patchConfigs(server *Server, logger log.Logger) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var newConfig configSchema
|
||||
err := render.DecodeJSON(r.Body, &newConfig)
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
render.JSON(w, r, ErrBadRequest)
|
||||
return
|
||||
}
|
||||
if newConfig.Mode != "" {
|
||||
mode := strings.ToLower(newConfig.Mode)
|
||||
if server.mode != mode {
|
||||
server.mode = mode
|
||||
logger.Info("updated mode: ", mode)
|
||||
}
|
||||
}
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func updateConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -37,6 +37,7 @@ type Server struct {
|
|||
trafficManager *trafficontrol.Manager
|
||||
urlTestHistory *urltest.HistoryStorage
|
||||
tcpListener net.Listener
|
||||
mode string
|
||||
}
|
||||
|
||||
func NewServer(router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) *Server {
|
||||
|
@ -51,6 +52,10 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
|||
},
|
||||
trafficManager: trafficManager,
|
||||
urlTestHistory: urltest.NewHistoryStorage(),
|
||||
mode: strings.ToLower(options.DefaultMode),
|
||||
}
|
||||
if server.mode == "" {
|
||||
server.mode = "rule"
|
||||
}
|
||||
cors := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"},
|
||||
|
@ -65,7 +70,7 @@ func NewServer(router adapter.Router, logFactory log.ObservableFactory, options
|
|||
r.Get("/logs", getLogs(logFactory))
|
||||
r.Get("/traffic", traffic(trafficManager))
|
||||
r.Get("/version", version)
|
||||
r.Mount("/configs", configRouter(logFactory))
|
||||
r.Mount("/configs", configRouter(server, logFactory, server.logger))
|
||||
r.Mount("/proxies", proxyRouter(server, router))
|
||||
r.Mount("/rules", ruleRouter(router))
|
||||
r.Mount("/connections", connectionRouter(trafficManager))
|
||||
|
@ -121,6 +126,10 @@ func (s *Server) RoutedPacketConnection(ctx context.Context, conn N.PacketConn,
|
|||
return tracker, tracker
|
||||
}
|
||||
|
||||
func (s *Server) Mode() string {
|
||||
return s.mode
|
||||
}
|
||||
|
||||
func castMetadata(metadata adapter.InboundContext) trafficontrol.Metadata {
|
||||
var inbound string
|
||||
if metadata.Inbound != "" {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package option
|
||||
|
||||
type ClashAPIOptions struct {
|
||||
DefaultMode string `json:"default_mode,omitempty"`
|
||||
ExternalController string `json:"external_controller,omitempty"`
|
||||
ExternalUI string `json:"external_ui,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
|
|
|
@ -99,6 +99,7 @@ type DefaultDNSRule struct {
|
|||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
Outbound Listable[string] `json:"outbound,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
DisableCache bool `json:"disable_cache,omitempty"`
|
||||
|
|
|
@ -101,6 +101,7 @@ type DefaultRule struct {
|
|||
PackageName Listable[string] `json:"package_name,omitempty"`
|
||||
User Listable[string] `json:"user,omitempty"`
|
||||
UserID Listable[int32] `json:"user_id,omitempty"`
|
||||
ClashMode string `json:"clash_mode,omitempty"`
|
||||
Invert bool `json:"invert,omitempty"`
|
||||
Outbound string `json:"outbound,omitempty"`
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ type Router struct {
|
|||
networkMonitor tun.NetworkUpdateMonitor
|
||||
interfaceMonitor tun.DefaultInterfaceMonitor
|
||||
packageManager tun.PackageManager
|
||||
trafficController adapter.TrafficController
|
||||
clashServer adapter.ClashServer
|
||||
processSearcher process.Searcher
|
||||
}
|
||||
|
||||
|
@ -588,8 +588,8 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||
conn.Close()
|
||||
return E.New("missing supported outbound, closing connection")
|
||||
}
|
||||
if r.trafficController != nil {
|
||||
trackerConn, tracker := r.trafficController.RoutedConnection(ctx, conn, metadata, matchedRule)
|
||||
if r.clashServer != nil {
|
||||
trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, matchedRule)
|
||||
defer tracker.Leave()
|
||||
conn = trackerConn
|
||||
}
|
||||
|
@ -661,8 +661,8 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||
conn.Close()
|
||||
return E.New("missing supported outbound, closing packet connection")
|
||||
}
|
||||
if r.trafficController != nil {
|
||||
trackerConn, tracker := r.trafficController.RoutedPacketConnection(ctx, conn, metadata, matchedRule)
|
||||
if r.clashServer != nil {
|
||||
trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, matchedRule)
|
||||
defer tracker.Leave()
|
||||
conn = trackerConn
|
||||
}
|
||||
|
@ -746,8 +746,12 @@ func (r *Router) PackageManager() tun.PackageManager {
|
|||
return r.packageManager
|
||||
}
|
||||
|
||||
func (r *Router) SetTrafficController(controller adapter.TrafficController) {
|
||||
r.trafficController = controller
|
||||
func (r *Router) ClashServer() adapter.ClashServer {
|
||||
return r.clashServer
|
||||
}
|
||||
|
||||
func (r *Router) SetClashServer(controller adapter.ClashServer) {
|
||||
r.clashServer = controller
|
||||
}
|
||||
|
||||
func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool {
|
||||
|
|
|
@ -192,6 +192,11 @@ func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options opt
|
|||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.ClashMode != "" {
|
||||
item := NewClashModeItem(router, options.ClashMode)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
|
|
33
route/rule_clash_mode.go
Normal file
33
route/rule_clash_mode.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package route
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
)
|
||||
|
||||
var _ RuleItem = (*ClashModeItem)(nil)
|
||||
|
||||
type ClashModeItem struct {
|
||||
router adapter.Router
|
||||
mode string
|
||||
}
|
||||
|
||||
func NewClashModeItem(router adapter.Router, mode string) *ClashModeItem {
|
||||
return &ClashModeItem{
|
||||
router: router,
|
||||
mode: strings.ToLower(mode),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ClashModeItem) Match(metadata *adapter.InboundContext) bool {
|
||||
clashServer := r.router.ClashServer()
|
||||
if clashServer == nil {
|
||||
return false
|
||||
}
|
||||
return clashServer.Mode() == r.mode
|
||||
}
|
||||
|
||||
func (r *ClashModeItem) String() string {
|
||||
return "clash_mode=" + r.mode
|
||||
}
|
|
@ -180,6 +180,11 @@ func NewDefaultDNSRule(router adapter.Router, logger log.ContextLogger, options
|
|||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
if options.ClashMode != "" {
|
||||
item := NewClashModeItem(router, options.ClashMode)
|
||||
rule.items = append(rule.items, item)
|
||||
rule.allItems = append(rule.allItems, item)
|
||||
}
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue