github.com/kelleygo/clashcore@v1.0.2/hub/route/configs.go (about)

     1  package route
     2  
     3  import (
     4  	"net/http"
     5  	"net/netip"
     6  	"path/filepath"
     7  	"sync"
     8  
     9  	"github.com/kelleygo/clashcore/adapter/inbound"
    10  	"github.com/kelleygo/clashcore/component/dialer"
    11  	"github.com/kelleygo/clashcore/component/resolver"
    12  	"github.com/kelleygo/clashcore/config"
    13  	C "github.com/kelleygo/clashcore/constant"
    14  	"github.com/kelleygo/clashcore/hub/executor"
    15  	P "github.com/kelleygo/clashcore/listener"
    16  	LC "github.com/kelleygo/clashcore/listener/config"
    17  	"github.com/kelleygo/clashcore/log"
    18  	"github.com/kelleygo/clashcore/tunnel"
    19  
    20  	"github.com/go-chi/chi/v5"
    21  	"github.com/go-chi/render"
    22  )
    23  
    24  var (
    25  	updateGeoMux sync.Mutex
    26  	updatingGeo  = false
    27  )
    28  
    29  func configRouter() http.Handler {
    30  	r := chi.NewRouter()
    31  	r.Get("/", getConfigs)
    32  	r.Put("/", updateConfigs)
    33  	r.Post("/geo", updateGeoDatabases)
    34  	r.Patch("/", patchConfigs)
    35  	return r
    36  }
    37  
    38  type configSchema struct {
    39  	Port              *int               `json:"port"`
    40  	SocksPort         *int               `json:"socks-port"`
    41  	RedirPort         *int               `json:"redir-port"`
    42  	TProxyPort        *int               `json:"tproxy-port"`
    43  	MixedPort         *int               `json:"mixed-port"`
    44  	Tun               *tunSchema         `json:"tun"`
    45  	TuicServer        *tuicServerSchema  `json:"tuic-server"`
    46  	ShadowSocksConfig *string            `json:"ss-config"`
    47  	VmessConfig       *string            `json:"vmess-config"`
    48  	TcptunConfig      *string            `json:"tcptun-config"`
    49  	UdptunConfig      *string            `json:"udptun-config"`
    50  	AllowLan          *bool              `json:"allow-lan"`
    51  	SkipAuthPrefixes  *[]netip.Prefix    `json:"skip-auth-prefixes"`
    52  	LanAllowedIPs     *[]netip.Prefix    `json:"lan-allowed-ips"`
    53  	LanDisAllowedIPs  *[]netip.Prefix    `json:"lan-disallowed-ips"`
    54  	BindAddress       *string            `json:"bind-address"`
    55  	Mode              *tunnel.TunnelMode `json:"mode"`
    56  	LogLevel          *log.LogLevel      `json:"log-level"`
    57  	IPv6              *bool              `json:"ipv6"`
    58  	Sniffing          *bool              `json:"sniffing"`
    59  	TcpConcurrent     *bool              `json:"tcp-concurrent"`
    60  	InterfaceName     *string            `json:"interface-name"`
    61  }
    62  
    63  type tunSchema struct {
    64  	Enable              bool        `yaml:"enable" json:"enable"`
    65  	Device              *string     `yaml:"device" json:"device"`
    66  	Stack               *C.TUNStack `yaml:"stack" json:"stack"`
    67  	DNSHijack           *[]string   `yaml:"dns-hijack" json:"dns-hijack"`
    68  	AutoRoute           *bool       `yaml:"auto-route" json:"auto-route"`
    69  	AutoDetectInterface *bool       `yaml:"auto-detect-interface" json:"auto-detect-interface"`
    70  	//RedirectToTun       []string   		  `yaml:"-" json:"-"`
    71  
    72  	MTU        *uint32 `yaml:"mtu" json:"mtu,omitempty"`
    73  	GSO        *bool   `yaml:"gso" json:"gso,omitempty"`
    74  	GSOMaxSize *uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
    75  	//Inet4Address           *[]netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"`
    76  	Inet6Address             *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"`
    77  	StrictRoute              *bool           `yaml:"strict-route" json:"strict-route,omitempty"`
    78  	Inet4RouteAddress        *[]netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"`
    79  	Inet6RouteAddress        *[]netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"`
    80  	Inet4RouteExcludeAddress *[]netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"`
    81  	Inet6RouteExcludeAddress *[]netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"`
    82  	IncludeInterface         *[]string       `yaml:"include-interface" json:"include-interface,omitempty"`
    83  	ExcludeInterface         *[]string       `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
    84  	IncludeUID               *[]uint32       `yaml:"include-uid" json:"include-uid,omitempty"`
    85  	IncludeUIDRange          *[]string       `yaml:"include-uid-range" json:"include-uid-range,omitempty"`
    86  	ExcludeUID               *[]uint32       `yaml:"exclude-uid" json:"exclude-uid,omitempty"`
    87  	ExcludeUIDRange          *[]string       `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"`
    88  	IncludeAndroidUser       *[]int          `yaml:"include-android-user" json:"include-android-user,omitempty"`
    89  	IncludePackage           *[]string       `yaml:"include-package" json:"include-package,omitempty"`
    90  	ExcludePackage           *[]string       `yaml:"exclude-package" json:"exclude-package,omitempty"`
    91  	EndpointIndependentNat   *bool           `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"`
    92  	UDPTimeout               *int64          `yaml:"udp-timeout" json:"udp-timeout,omitempty"`
    93  	FileDescriptor           *int            `yaml:"file-descriptor" json:"file-descriptor"`
    94  	TableIndex               *int            `yaml:"table-index" json:"table-index"`
    95  }
    96  
    97  type tuicServerSchema struct {
    98  	Enable                bool               `yaml:"enable" json:"enable"`
    99  	Listen                *string            `yaml:"listen" json:"listen"`
   100  	Token                 *[]string          `yaml:"token" json:"token"`
   101  	Users                 *map[string]string `yaml:"users" json:"users,omitempty"`
   102  	Certificate           *string            `yaml:"certificate" json:"certificate"`
   103  	PrivateKey            *string            `yaml:"private-key" json:"private-key"`
   104  	CongestionController  *string            `yaml:"congestion-controller" json:"congestion-controller,omitempty"`
   105  	MaxIdleTime           *int               `yaml:"max-idle-time" json:"max-idle-time,omitempty"`
   106  	AuthenticationTimeout *int               `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"`
   107  	ALPN                  *[]string          `yaml:"alpn" json:"alpn,omitempty"`
   108  	MaxUdpRelayPacketSize *int               `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"`
   109  	CWND                  *int               `yaml:"cwnd" json:"cwnd,omitempty"`
   110  }
   111  
   112  func getConfigs(w http.ResponseWriter, r *http.Request) {
   113  	general := executor.GetGeneral()
   114  	render.JSON(w, r, general)
   115  }
   116  
   117  func pointerOrDefault(p *int, def int) int {
   118  	if p != nil {
   119  		return *p
   120  	}
   121  	return def
   122  }
   123  
   124  func pointerOrDefaultString(p *string, def string) string {
   125  	if p != nil {
   126  		return *p
   127  	}
   128  
   129  	return def
   130  }
   131  
   132  func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun {
   133  	if p != nil {
   134  		def.Enable = p.Enable
   135  		if p.Device != nil {
   136  			def.Device = *p.Device
   137  		}
   138  		if p.Stack != nil {
   139  			def.Stack = *p.Stack
   140  		}
   141  		if p.DNSHijack != nil {
   142  			def.DNSHijack = *p.DNSHijack
   143  		}
   144  		if p.AutoRoute != nil {
   145  			def.AutoRoute = *p.AutoRoute
   146  		}
   147  		if p.AutoDetectInterface != nil {
   148  			def.AutoDetectInterface = *p.AutoDetectInterface
   149  		}
   150  		if p.MTU != nil {
   151  			def.MTU = *p.MTU
   152  		}
   153  		if p.GSO != nil {
   154  			def.GSO = *p.GSO
   155  		}
   156  		if p.GSOMaxSize != nil {
   157  			def.GSOMaxSize = *p.GSOMaxSize
   158  		}
   159  		//if p.Inet4Address != nil {
   160  		//	def.Inet4Address = *p.Inet4Address
   161  		//}
   162  		if p.Inet6Address != nil {
   163  			def.Inet6Address = *p.Inet6Address
   164  		}
   165  		if p.Inet4RouteAddress != nil {
   166  			def.Inet4RouteAddress = *p.Inet4RouteAddress
   167  		}
   168  		if p.Inet6RouteAddress != nil {
   169  			def.Inet6RouteAddress = *p.Inet6RouteAddress
   170  		}
   171  		if p.Inet4RouteExcludeAddress != nil {
   172  			def.Inet4RouteExcludeAddress = *p.Inet4RouteExcludeAddress
   173  		}
   174  		if p.Inet6RouteExcludeAddress != nil {
   175  			def.Inet6RouteExcludeAddress = *p.Inet6RouteExcludeAddress
   176  		}
   177  		if p.IncludeInterface != nil {
   178  			def.IncludeInterface = *p.IncludeInterface
   179  		}
   180  		if p.ExcludeInterface != nil {
   181  			def.ExcludeInterface = *p.ExcludeInterface
   182  		}
   183  		if p.IncludeUID != nil {
   184  			def.IncludeUID = *p.IncludeUID
   185  		}
   186  		if p.IncludeUIDRange != nil {
   187  			def.IncludeUIDRange = *p.IncludeUIDRange
   188  		}
   189  		if p.ExcludeUID != nil {
   190  			def.ExcludeUID = *p.ExcludeUID
   191  		}
   192  		if p.ExcludeUIDRange != nil {
   193  			def.ExcludeUIDRange = *p.ExcludeUIDRange
   194  		}
   195  		if p.IncludeAndroidUser != nil {
   196  			def.IncludeAndroidUser = *p.IncludeAndroidUser
   197  		}
   198  		if p.IncludePackage != nil {
   199  			def.IncludePackage = *p.IncludePackage
   200  		}
   201  		if p.ExcludePackage != nil {
   202  			def.ExcludePackage = *p.ExcludePackage
   203  		}
   204  		if p.EndpointIndependentNat != nil {
   205  			def.EndpointIndependentNat = *p.EndpointIndependentNat
   206  		}
   207  		if p.UDPTimeout != nil {
   208  			def.UDPTimeout = *p.UDPTimeout
   209  		}
   210  		if p.FileDescriptor != nil {
   211  			def.FileDescriptor = *p.FileDescriptor
   212  		}
   213  		if p.TableIndex != nil {
   214  			def.TableIndex = *p.TableIndex
   215  		}
   216  	}
   217  	return def
   218  }
   219  
   220  func pointerOrDefaultTuicServer(p *tuicServerSchema, def LC.TuicServer) LC.TuicServer {
   221  	if p != nil {
   222  		def.Enable = p.Enable
   223  		if p.Listen != nil {
   224  			def.Listen = *p.Listen
   225  		}
   226  		if p.Token != nil {
   227  			def.Token = *p.Token
   228  		}
   229  		if p.Users != nil {
   230  			def.Users = *p.Users
   231  		}
   232  		if p.Certificate != nil {
   233  			def.Certificate = *p.Certificate
   234  		}
   235  		if p.PrivateKey != nil {
   236  			def.PrivateKey = *p.PrivateKey
   237  		}
   238  		if p.CongestionController != nil {
   239  			def.CongestionController = *p.CongestionController
   240  		}
   241  		if p.MaxIdleTime != nil {
   242  			def.MaxIdleTime = *p.MaxIdleTime
   243  		}
   244  		if p.AuthenticationTimeout != nil {
   245  			def.AuthenticationTimeout = *p.AuthenticationTimeout
   246  		}
   247  		if p.ALPN != nil {
   248  			def.ALPN = *p.ALPN
   249  		}
   250  		if p.MaxUdpRelayPacketSize != nil {
   251  			def.MaxUdpRelayPacketSize = *p.MaxUdpRelayPacketSize
   252  		}
   253  		if p.CWND != nil {
   254  			def.CWND = *p.CWND
   255  		}
   256  	}
   257  	return def
   258  }
   259  
   260  func patchConfigs(w http.ResponseWriter, r *http.Request) {
   261  	general := &configSchema{}
   262  	if err := render.DecodeJSON(r.Body, &general); err != nil {
   263  		render.Status(r, http.StatusBadRequest)
   264  		render.JSON(w, r, ErrBadRequest)
   265  		return
   266  	}
   267  
   268  	if general.AllowLan != nil {
   269  		P.SetAllowLan(*general.AllowLan)
   270  	}
   271  
   272  	if general.SkipAuthPrefixes != nil {
   273  		inbound.SetSkipAuthPrefixes(*general.SkipAuthPrefixes)
   274  	}
   275  
   276  	if general.LanAllowedIPs != nil {
   277  		inbound.SetAllowedIPs(*general.LanAllowedIPs)
   278  	}
   279  
   280  	if general.LanDisAllowedIPs != nil {
   281  		inbound.SetDisAllowedIPs(*general.LanDisAllowedIPs)
   282  	}
   283  
   284  	if general.BindAddress != nil {
   285  		P.SetBindAddress(*general.BindAddress)
   286  	}
   287  
   288  	if general.Sniffing != nil {
   289  		tunnel.SetSniffing(*general.Sniffing)
   290  	}
   291  
   292  	if general.TcpConcurrent != nil {
   293  		dialer.SetTcpConcurrent(*general.TcpConcurrent)
   294  	}
   295  
   296  	if general.InterfaceName != nil {
   297  		dialer.DefaultInterface.Store(*general.InterfaceName)
   298  	}
   299  
   300  	ports := P.GetPorts()
   301  
   302  	P.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port), tunnel.Tunnel)
   303  	P.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort), tunnel.Tunnel)
   304  	P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort), tunnel.Tunnel)
   305  	P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tunnel.Tunnel)
   306  	P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tunnel.Tunnel)
   307  	P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tunnel.Tunnel)
   308  	P.ReCreateShadowSocks(pointerOrDefaultString(general.ShadowSocksConfig, ports.ShadowSocksConfig), tunnel.Tunnel)
   309  	P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tunnel.Tunnel)
   310  	P.ReCreateTuic(pointerOrDefaultTuicServer(general.TuicServer, P.LastTuicConf), tunnel.Tunnel)
   311  
   312  	if general.Mode != nil {
   313  		tunnel.SetMode(*general.Mode)
   314  	}
   315  
   316  	if general.LogLevel != nil {
   317  		log.SetLevel(*general.LogLevel)
   318  	}
   319  
   320  	if general.IPv6 != nil {
   321  		resolver.DisableIPv6 = !*general.IPv6
   322  	}
   323  
   324  	render.NoContent(w, r)
   325  }
   326  
   327  func updateConfigs(w http.ResponseWriter, r *http.Request) {
   328  	req := struct {
   329  		Path    string `json:"path"`
   330  		Payload string `json:"payload"`
   331  	}{}
   332  	if err := render.DecodeJSON(r.Body, &req); err != nil {
   333  		render.Status(r, http.StatusBadRequest)
   334  		render.JSON(w, r, ErrBadRequest)
   335  		return
   336  	}
   337  
   338  	force := r.URL.Query().Get("force") == "true"
   339  	var cfg *config.Config
   340  	var err error
   341  
   342  	if req.Payload != "" {
   343  		cfg, err = executor.ParseWithBytes([]byte(req.Payload))
   344  		if err != nil {
   345  			render.Status(r, http.StatusBadRequest)
   346  			render.JSON(w, r, newError(err.Error()))
   347  			return
   348  		}
   349  	} else {
   350  		if req.Path == "" {
   351  			req.Path = C.Path.Config()
   352  		}
   353  		if !filepath.IsAbs(req.Path) {
   354  			render.Status(r, http.StatusBadRequest)
   355  			render.JSON(w, r, newError("path is not a absolute path"))
   356  			return
   357  		}
   358  
   359  		cfg, err = executor.ParseWithPath(req.Path)
   360  		if err != nil {
   361  			render.Status(r, http.StatusBadRequest)
   362  			render.JSON(w, r, newError(err.Error()))
   363  			return
   364  		}
   365  	}
   366  
   367  	executor.ApplyConfig(cfg, force)
   368  	render.NoContent(w, r)
   369  }
   370  
   371  func updateGeoDatabases(w http.ResponseWriter, r *http.Request) {
   372  	updateGeoMux.Lock()
   373  
   374  	if updatingGeo {
   375  		updateGeoMux.Unlock()
   376  		render.Status(r, http.StatusBadRequest)
   377  		render.JSON(w, r, newError("updating..."))
   378  		return
   379  	}
   380  
   381  	updatingGeo = true
   382  	updateGeoMux.Unlock()
   383  
   384  	go func() {
   385  		defer func() {
   386  			updatingGeo = false
   387  		}()
   388  
   389  		log.Warnln("[REST-API] updating GEO databases...")
   390  
   391  		if err := config.UpdateGeoDatabases(); err != nil {
   392  			log.Errorln("[REST-API] update GEO databases failed: %v", err)
   393  			return
   394  		}
   395  
   396  		cfg, err := executor.ParseWithPath(C.Path.Config())
   397  		if err != nil {
   398  			log.Errorln("[REST-API] update GEO databases failed: %v", err)
   399  			return
   400  		}
   401  
   402  		log.Warnln("[REST-API] update GEO databases successful, apply config...")
   403  
   404  		executor.ApplyConfig(cfg, false)
   405  	}()
   406  
   407  	render.NoContent(w, r)
   408  }