Support proxies search Support svg display Optimize config persistence Add some scenes auto close connections Update core Optimize more details
259 lines
6.8 KiB
Go
259 lines
6.8 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"
|
|
"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)
|
|
listener.SetAllowLan(general.AllowLan)
|
|
inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)
|
|
inbound.SetAllowedIPs(general.LanAllowedIPs)
|
|
inbound.SetDisAllowedIPs(general.LanDisAllowedIPs)
|
|
listener.SetBindAddress(general.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{
|
|
Config: config.DefaultRawConfig(),
|
|
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 setupConfig(params *SetupParams) error {
|
|
runLock.Lock()
|
|
defer runLock.Unlock()
|
|
var err error
|
|
constant.DefaultTestURL = params.TestURL
|
|
currentConfig, err = config.ParseRawConfig(params.Config)
|
|
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
|
|
}
|