Fix windows backup recovery issues Optimize overwrite handle Optimize access control page Optimize some details
284 lines
7.3 KiB
Go
284 lines
7.3 KiB
Go
package main
|
|
|
|
import (
|
|
b "bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"github.com/metacubex/mihomo/adapter"
|
|
"github.com/metacubex/mihomo/adapter/inbound"
|
|
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
|
"github.com/metacubex/mihomo/adapter/provider"
|
|
"github.com/metacubex/mihomo/common/batch"
|
|
"github.com/metacubex/mihomo/component/dialer"
|
|
"github.com/metacubex/mihomo/component/resolver"
|
|
"github.com/metacubex/mihomo/config"
|
|
"github.com/metacubex/mihomo/constant"
|
|
"github.com/metacubex/mihomo/constant/features"
|
|
cp "github.com/metacubex/mihomo/constant/provider"
|
|
"github.com/metacubex/mihomo/hub"
|
|
"github.com/metacubex/mihomo/hub/executor"
|
|
"github.com/metacubex/mihomo/hub/route"
|
|
"github.com/metacubex/mihomo/listener"
|
|
"github.com/metacubex/mihomo/log"
|
|
rp "github.com/metacubex/mihomo/rules/provider"
|
|
"github.com/metacubex/mihomo/tunnel"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
currentConfig *config.Config
|
|
version = 0
|
|
isRunning = false
|
|
runLock sync.Mutex
|
|
mBatch, _ = batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](50))
|
|
)
|
|
|
|
type ExternalProviders []ExternalProvider
|
|
|
|
func (a ExternalProviders) Len() int { return len(a) }
|
|
func (a ExternalProviders) Less(i, j int) bool { return a[i].Name < a[j].Name }
|
|
func (a ExternalProviders) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
func getExternalProvidersRaw() map[string]cp.Provider {
|
|
eps := make(map[string]cp.Provider)
|
|
for n, p := range tunnel.Providers() {
|
|
if p.VehicleType() != cp.Compatible {
|
|
eps[n] = p
|
|
}
|
|
}
|
|
for n, p := range tunnel.RuleProviders() {
|
|
if p.VehicleType() != cp.Compatible {
|
|
eps[n] = p
|
|
}
|
|
}
|
|
return eps
|
|
}
|
|
|
|
func toExternalProvider(p cp.Provider) (*ExternalProvider, error) {
|
|
switch p.(type) {
|
|
case *provider.ProxySetProvider:
|
|
psp := p.(*provider.ProxySetProvider)
|
|
return &ExternalProvider{
|
|
Name: psp.Name(),
|
|
Type: psp.Type().String(),
|
|
VehicleType: psp.VehicleType().String(),
|
|
Count: psp.Count(),
|
|
UpdateAt: psp.UpdatedAt(),
|
|
Path: psp.Vehicle().Path(),
|
|
SubscriptionInfo: psp.GetSubscriptionInfo(),
|
|
}, nil
|
|
case *rp.RuleSetProvider:
|
|
rsp := p.(*rp.RuleSetProvider)
|
|
return &ExternalProvider{
|
|
Name: rsp.Name(),
|
|
Type: rsp.Type().String(),
|
|
VehicleType: rsp.VehicleType().String(),
|
|
Count: rsp.Count(),
|
|
UpdateAt: rsp.UpdatedAt(),
|
|
Path: rsp.Vehicle().Path(),
|
|
}, nil
|
|
default:
|
|
return nil, errors.New("not external provider")
|
|
}
|
|
}
|
|
|
|
func sideUpdateExternalProvider(p cp.Provider, bytes []byte) error {
|
|
switch p.(type) {
|
|
case *provider.ProxySetProvider:
|
|
psp := p.(*provider.ProxySetProvider)
|
|
_, _, err := psp.SideUpdate(bytes)
|
|
if err == nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case rp.RuleSetProvider:
|
|
rsp := p.(*rp.RuleSetProvider)
|
|
_, _, err := rsp.SideUpdate(bytes)
|
|
if err == nil {
|
|
return err
|
|
}
|
|
return nil
|
|
default:
|
|
return errors.New("not external provider")
|
|
}
|
|
}
|
|
|
|
func updateListeners() {
|
|
if !isRunning {
|
|
return
|
|
}
|
|
if currentConfig == nil {
|
|
return
|
|
}
|
|
listeners := currentConfig.Listeners
|
|
general := currentConfig.General
|
|
listener.PatchInboundListeners(listeners, tunnel.Tunnel, true)
|
|
|
|
allowLan := general.AllowLan
|
|
listener.SetAllowLan(allowLan)
|
|
inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)
|
|
inbound.SetAllowedIPs(general.LanAllowedIPs)
|
|
inbound.SetDisAllowedIPs(general.LanDisAllowedIPs)
|
|
|
|
bindAddress := general.BindAddress
|
|
listener.SetBindAddress(bindAddress)
|
|
listener.ReCreateHTTP(general.Port, tunnel.Tunnel)
|
|
listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)
|
|
listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)
|
|
listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)
|
|
listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)
|
|
listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)
|
|
listener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel)
|
|
listener.ReCreateTuic(general.TuicServer, tunnel.Tunnel)
|
|
if !features.Android {
|
|
listener.ReCreateTun(general.Tun, tunnel.Tunnel)
|
|
}
|
|
}
|
|
|
|
func stopListeners() {
|
|
listener.StopListener()
|
|
}
|
|
|
|
func patchSelectGroup(mapping map[string]string) {
|
|
for name, proxy := range tunnel.ProxiesWithProviders() {
|
|
outbound, ok := proxy.(*adapter.Proxy)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
selector, ok := outbound.ProxyAdapter.(outboundgroup.SelectAble)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
selected, exist := mapping[name]
|
|
if !exist {
|
|
continue
|
|
}
|
|
|
|
selector.ForceSet(selected)
|
|
}
|
|
}
|
|
|
|
func defaultSetupParams() *SetupParams {
|
|
return &SetupParams{
|
|
TestURL: "https://www.gstatic.com/generate_204",
|
|
SelectedMap: map[string]string{},
|
|
}
|
|
}
|
|
|
|
func readFile(path string) ([]byte, error) {
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
return nil, err
|
|
}
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return data, err
|
|
}
|
|
|
|
func updateConfig(params *UpdateParams) {
|
|
runLock.Lock()
|
|
defer runLock.Unlock()
|
|
general := currentConfig.General
|
|
if params.MixedPort != nil {
|
|
general.MixedPort = *params.MixedPort
|
|
}
|
|
if params.Sniffing != nil {
|
|
general.Sniffing = *params.Sniffing
|
|
tunnel.SetSniffing(general.Sniffing)
|
|
}
|
|
if params.FindProcessMode != nil {
|
|
general.FindProcessMode = *params.FindProcessMode
|
|
tunnel.SetFindProcessMode(general.FindProcessMode)
|
|
}
|
|
if params.TCPConcurrent != nil {
|
|
general.TCPConcurrent = *params.TCPConcurrent
|
|
dialer.SetTcpConcurrent(general.TCPConcurrent)
|
|
}
|
|
if params.Interface != nil {
|
|
general.Interface = *params.Interface
|
|
dialer.DefaultInterface.Store(general.Interface)
|
|
}
|
|
if params.UnifiedDelay != nil {
|
|
general.UnifiedDelay = *params.UnifiedDelay
|
|
adapter.UnifiedDelay.Store(general.UnifiedDelay)
|
|
}
|
|
if params.Mode != nil {
|
|
general.Mode = *params.Mode
|
|
tunnel.SetMode(general.Mode)
|
|
}
|
|
if params.LogLevel != nil {
|
|
general.LogLevel = *params.LogLevel
|
|
log.SetLevel(general.LogLevel)
|
|
}
|
|
if params.IPv6 != nil {
|
|
general.IPv6 = *params.IPv6
|
|
resolver.DisableIPv6 = !general.IPv6
|
|
}
|
|
if params.ExternalController != nil {
|
|
currentConfig.Controller.ExternalController = *params.ExternalController
|
|
route.ReCreateServer(&route.Config{
|
|
Addr: currentConfig.Controller.ExternalController,
|
|
})
|
|
}
|
|
|
|
if params.Tun != nil {
|
|
general.Tun.Enable = params.Tun.Enable
|
|
general.Tun.AutoRoute = *params.Tun.AutoRoute
|
|
general.Tun.Device = *params.Tun.Device
|
|
general.Tun.RouteAddress = *params.Tun.RouteAddress
|
|
general.Tun.DNSHijack = *params.Tun.DNSHijack
|
|
general.Tun.Stack = *params.Tun.Stack
|
|
}
|
|
|
|
updateListeners()
|
|
}
|
|
|
|
func parseWithPath(path string) (*config.Config, error) {
|
|
buf, err := readFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rawConfig := config.DefaultRawConfig()
|
|
err = UnmarshalJson(buf, rawConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
parseRawConfig, err := config.ParseRawConfig(rawConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return parseRawConfig, nil
|
|
}
|
|
|
|
func setupConfig(params *SetupParams) error {
|
|
runLock.Lock()
|
|
defer runLock.Unlock()
|
|
var err error
|
|
constant.DefaultTestURL = params.TestURL
|
|
currentConfig, err = executor.ParseWithPath(filepath.Join(constant.Path.HomeDir(), "config.yaml"))
|
|
if err != nil {
|
|
currentConfig, _ = config.ParseRawConfig(config.DefaultRawConfig())
|
|
}
|
|
hub.ApplyConfig(currentConfig)
|
|
patchSelectGroup(params.SelectedMap)
|
|
updateListeners()
|
|
runtime.GC()
|
|
return err
|
|
}
|
|
|
|
func UnmarshalJson(data []byte, v any) error {
|
|
decoder := json.NewDecoder(b.NewReader(data))
|
|
decoder.UseNumber()
|
|
err := decoder.Decode(v)
|
|
return err
|
|
}
|