Support core status check and force restart Optimize proxies page and access page Update flutter and pub dependencies Optimize more details
281 lines
7.2 KiB
Go
281 lines
7.2 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/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"
|
|
"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 = parseWithPath(filepath.Join(constant.Path.HomeDir(), "config.json"))
|
|
if err != nil {
|
|
currentConfig, _ = config.ParseRawConfig(config.DefaultRawConfig())
|
|
}
|
|
hub.ApplyConfig(currentConfig)
|
|
patchSelectGroup(params.SelectedMap)
|
|
updateListeners()
|
|
return err
|
|
}
|
|
|
|
func UnmarshalJson(data []byte, v any) error {
|
|
decoder := json.NewDecoder(b.NewReader(data))
|
|
decoder.UseNumber()
|
|
err := decoder.Decode(v)
|
|
return err
|
|
}
|