Files
FlClash/core/hub.go
T

581 lines
12 KiB
Go
Raw Normal View History

2024-04-30 23:38:49 +08:00
package main
2024-12-03 21:47:12 +08:00
import (
2025-12-16 11:23:09 +08:00
"cmp"
2024-12-03 21:47:12 +08:00
"context"
"encoding/json"
"github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/adapter/outboundgroup"
"github.com/metacubex/mihomo/common/observable"
"github.com/metacubex/mihomo/common/utils"
2024-12-09 01:40:39 +08:00
"github.com/metacubex/mihomo/component/mmdb"
2025-05-02 02:24:12 +08:00
"github.com/metacubex/mihomo/component/resolver"
2024-12-03 21:47:12 +08:00
"github.com/metacubex/mihomo/component/updater"
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant"
2025-09-27 23:39:20 +08:00
"github.com/metacubex/mihomo/constant/features"
2024-12-03 21:47:12 +08:00
cp "github.com/metacubex/mihomo/constant/provider"
"github.com/metacubex/mihomo/hub/executor"
"github.com/metacubex/mihomo/listener"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/tunnel"
"github.com/metacubex/mihomo/tunnel/statistic"
2025-12-16 11:23:09 +08:00
"golang.org/x/exp/slices"
2024-12-09 01:40:39 +08:00
"net"
2025-07-31 17:09:18 +08:00
"os"
2024-12-03 21:47:12 +08:00
"runtime"
2025-09-27 23:39:20 +08:00
"runtime/debug"
2024-12-09 01:40:39 +08:00
"strconv"
2024-12-03 21:47:12 +08:00
"time"
)
var (
2025-02-09 18:39:38 +08:00
isInit = false
2024-12-03 21:47:12 +08:00
externalProviders = map[string]cp.Provider{}
logSubscriber observable.Subscription[log.Event]
)
2025-04-09 16:46:14 +08:00
func handleInitClash(paramsString string) bool {
2025-09-27 23:39:20 +08:00
runLock.Lock()
defer runLock.Unlock()
2025-04-09 16:46:14 +08:00
var params = InitParams{}
err := json.Unmarshal([]byte(paramsString), &params)
if err != nil {
return false
}
version = params.Version
2025-12-16 11:23:09 +08:00
constant.SetHomeDir(params.HomeDir)
isInit = true
2024-12-03 21:47:12 +08:00
return isInit
}
func handleStartListener() bool {
runLock.Lock()
defer runLock.Unlock()
isRunning = true
2025-05-02 02:24:12 +08:00
updateListeners()
resolver.ResetConnection()
2024-12-03 21:47:12 +08:00
return true
}
func handleStopListener() bool {
runLock.Lock()
defer runLock.Unlock()
isRunning = false
listener.StopListener()
2025-07-31 17:09:18 +08:00
resolver.ResetConnection()
2024-12-03 21:47:12 +08:00
return true
}
func handleGetIsInit() bool {
return isInit
}
2025-07-31 17:09:18 +08:00
func handleForceGC() {
2025-09-27 23:39:20 +08:00
log.Infoln("[APP] request force GC")
runtime.GC()
if features.Android {
debug.FreeOSMemory()
}
2024-12-03 21:47:12 +08:00
}
func handleShutdown() bool {
stopListeners()
executor.Shutdown()
2025-09-27 23:39:20 +08:00
handleForceGC()
2024-12-03 21:47:12 +08:00
isInit = false
return true
}
2025-09-23 21:02:47 +08:00
func handleValidateConfig(path string) string {
buf, err := readFile(path)
_, err = config.UnmarshalRawConfig(buf)
2024-12-03 21:47:12 +08:00
if err != nil {
return err.Error()
}
return ""
}
2025-12-16 11:23:09 +08:00
func handleGetProxies() ProxiesData {
2024-12-03 21:47:12 +08:00
runLock.Lock()
defer runLock.Unlock()
2025-12-16 11:23:09 +08:00
nameList := config.GetProxyNameList()
proxies := make(map[string]constant.Proxy)
for name, proxy := range tunnel.Proxies() {
proxies[name] = proxy
}
for _, p := range tunnel.Providers() {
for _, proxy := range p.Proxies() {
proxies[proxy.Name()] = proxy
}
}
hasGlobal := false
allNames := make([]string, 0, len(nameList)+1)
for _, name := range nameList {
if name == "GLOBAL" {
hasGlobal = true
}
p, ok := proxies[name]
if !ok || p == nil {
continue
}
switch p.Type() {
case constant.Selector, constant.URLTest, constant.Fallback, constant.Relay, constant.LoadBalance:
allNames = append(allNames, name)
default:
}
}
if !hasGlobal {
if p, ok := proxies["GLOBAL"]; ok && p != nil {
allNames = append([]string{"GLOBAL"}, allNames...)
}
}
return ProxiesData{
All: allNames,
Proxies: proxies,
}
2024-12-03 21:47:12 +08:00
}
func handleChangeProxy(data string, fn func(string string)) {
runLock.Lock()
go func() {
defer runLock.Unlock()
var params = &ChangeProxyParams{}
err := json.Unmarshal([]byte(data), params)
if err != nil {
fn(err.Error())
return
}
groupName := *params.GroupName
proxyName := *params.ProxyName
proxies := tunnel.ProxiesWithProviders()
group, ok := proxies[groupName]
if !ok {
fn("Not found group")
return
}
adapterProxy := group.(*adapter.Proxy)
selector, ok := adapterProxy.ProxyAdapter.(outboundgroup.SelectAble)
if !ok {
fn("Group is not selectable")
return
}
if proxyName == "" {
selector.ForceSet(proxyName)
} else {
err = selector.Set(proxyName)
}
if err != nil {
fn(err.Error())
return
}
fn("")
return
}()
}
2025-07-31 17:09:18 +08:00
func handleGetTraffic(onlyStatisticsProxy bool) string {
2025-12-16 11:23:09 +08:00
up, down := statistic.DefaultManager.NowTraffic(onlyStatisticsProxy)
2024-12-03 21:47:12 +08:00
traffic := map[string]int64{
"up": up,
"down": down,
}
data, err := json.Marshal(traffic)
if err != nil {
2025-07-31 17:09:18 +08:00
log.Errorln("Error: %s", err)
2024-12-03 21:47:12 +08:00
return ""
}
return string(data)
}
2025-07-31 17:09:18 +08:00
func handleGetTotalTraffic(onlyStatisticsProxy bool) string {
2025-12-16 11:23:09 +08:00
up, down := statistic.DefaultManager.TotalTraffic(onlyStatisticsProxy)
2024-12-03 21:47:12 +08:00
traffic := map[string]int64{
"up": up,
"down": down,
}
data, err := json.Marshal(traffic)
if err != nil {
2025-07-31 17:09:18 +08:00
log.Errorln("Error: %s", err)
2024-12-03 21:47:12 +08:00
return ""
}
return string(data)
}
func handleResetTraffic() {
statistic.DefaultManager.ResetStatistic()
}
func handleAsyncTestDelay(paramsString string, fn func(string)) {
2025-05-02 02:24:12 +08:00
mBatch.Go(paramsString, func() (bool, error) {
2024-12-03 21:47:12 +08:00
var params = &TestDelayParams{}
err := json.Unmarshal([]byte(paramsString), params)
if err != nil {
fn("")
return false, nil
}
expectedStatus, err := utils.NewUnsignedRanges[uint16]("")
if err != nil {
fn("")
return false, nil
}
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(params.Timeout))
defer cancel()
proxies := tunnel.ProxiesWithProviders()
proxy := proxies[params.ProxyName]
delayData := &Delay{
Name: params.ProxyName,
}
if proxy == nil {
delayData.Value = -1
data, _ := json.Marshal(delayData)
fn(string(data))
return false, nil
}
2025-01-13 19:08:17 +08:00
testUrl := constant.DefaultTestURL
if params.TestUrl != "" {
testUrl = params.TestUrl
}
2025-02-09 18:39:38 +08:00
delayData.Url = testUrl
2025-01-13 19:08:17 +08:00
delay, err := proxy.URLTest(ctx, testUrl, expectedStatus)
2024-12-03 21:47:12 +08:00
if err != nil || delay == 0 {
delayData.Value = -1
data, _ := json.Marshal(delayData)
fn(string(data))
return false, nil
}
delayData.Value = int32(delay)
data, _ := json.Marshal(delayData)
fn(string(data))
return false, nil
})
}
func handleGetConnections() string {
runLock.Lock()
defer runLock.Unlock()
snapshot := statistic.DefaultManager.Snapshot()
data, err := json.Marshal(snapshot)
if err != nil {
2025-07-31 17:09:18 +08:00
log.Errorln("Error: %s", err)
2024-12-03 21:47:12 +08:00
return ""
}
return string(data)
}
func handleCloseConnections() bool {
runLock.Lock()
defer runLock.Unlock()
2025-05-02 02:24:12 +08:00
closeConnections()
return true
}
func closeConnections() {
2024-12-03 21:47:12 +08:00
statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
err := c.Close()
if err != nil {
return false
}
return true
})
2025-05-02 02:24:12 +08:00
}
func handleResetConnections() bool {
runLock.Lock()
defer runLock.Unlock()
resolver.ResetConnection()
2024-12-03 21:47:12 +08:00
return true
}
func handleCloseConnection(connectionId string) bool {
runLock.Lock()
defer runLock.Unlock()
c := statistic.DefaultManager.Get(connectionId)
if c == nil {
return false
}
_ = c.Close()
return true
}
func handleGetExternalProviders() string {
runLock.Lock()
defer runLock.Unlock()
externalProviders = getExternalProvidersRaw()
eps := make([]ExternalProvider, 0)
for _, p := range externalProviders {
externalProvider, err := toExternalProvider(p)
if err != nil {
continue
}
eps = append(eps, *externalProvider)
}
2025-12-16 11:23:09 +08:00
slices.SortFunc(eps, func(a, b ExternalProvider) int {
return cmp.Compare(a.Name, b.Name)
})
2024-12-03 21:47:12 +08:00
data, err := json.Marshal(eps)
if err != nil {
return ""
}
return string(data)
}
func handleGetExternalProvider(externalProviderName string) string {
runLock.Lock()
defer runLock.Unlock()
externalProvider, exist := externalProviders[externalProviderName]
if !exist {
return ""
}
e, err := toExternalProvider(externalProvider)
if err != nil {
return ""
}
data, err := json.Marshal(e)
if err != nil {
return ""
}
return string(data)
}
func handleUpdateGeoData(geoType string, geoName string, fn func(value string)) {
go func() {
path := constant.Path.Resolve(geoName)
switch geoType {
case "MMDB":
err := updater.UpdateMMDBWithPath(path)
if err != nil {
fn(err.Error())
return
}
case "ASN":
err := updater.UpdateASNWithPath(path)
if err != nil {
fn(err.Error())
return
}
2025-07-31 17:09:18 +08:00
case "GEOIP":
2024-12-03 21:47:12 +08:00
err := updater.UpdateGeoIpWithPath(path)
if err != nil {
fn(err.Error())
return
}
2025-07-31 17:09:18 +08:00
case "GEOSITE":
2024-12-03 21:47:12 +08:00
err := updater.UpdateGeoSiteWithPath(path)
if err != nil {
fn(err.Error())
return
}
}
fn("")
}()
}
func handleUpdateExternalProvider(providerName string, fn func(value string)) {
go func() {
externalProvider, exist := externalProviders[providerName]
if !exist {
fn("external provider is not exist")
return
}
err := externalProvider.Update()
if err != nil {
fn(err.Error())
return
}
fn("")
}()
}
func handleSideLoadExternalProvider(providerName string, data []byte, fn func(value string)) {
go func() {
runLock.Lock()
defer runLock.Unlock()
externalProvider, exist := externalProviders[providerName]
if !exist {
fn("external provider is not exist")
return
}
err := sideUpdateExternalProvider(externalProvider, data)
if err != nil {
fn(err.Error())
return
}
fn("")
}()
}
2025-07-31 17:09:18 +08:00
func handleSuspend(suspended bool) bool {
if suspended {
tunnel.OnSuspend()
} else {
tunnel.OnRunning()
}
return true
}
2024-12-03 21:47:12 +08:00
func handleStartLog() {
if logSubscriber != nil {
log.UnSubscribe(logSubscriber)
logSubscriber = nil
}
logSubscriber = log.Subscribe()
go func() {
for logData := range logSubscriber {
if logData.LogLevel < log.Level() {
continue
}
message := &Message{
Type: LogMessage,
Data: logData,
}
2025-01-13 19:08:17 +08:00
sendMessage(*message)
2024-12-03 21:47:12 +08:00
}
}()
}
func handleStopLog() {
if logSubscriber != nil {
log.UnSubscribe(logSubscriber)
logSubscriber = nil
}
}
2024-12-09 01:40:39 +08:00
func handleGetCountryCode(ip string, fn func(value string)) {
go func() {
runLock.Lock()
defer runLock.Unlock()
codes := mmdb.IPInstance().LookupCode(net.ParseIP(ip))
if len(codes) == 0 {
fn("")
return
}
fn(codes[0])
}()
}
func handleGetMemory(fn func(value string)) {
go func() {
fn(strconv.FormatUint(statistic.DefaultManager.Memory(), 10))
}()
}
2025-05-02 02:24:12 +08:00
func handleGetConfig(path string) (*config.RawConfig, error) {
bytes, err := readFile(path)
if err != nil {
return nil, err
}
prof, err := config.UnmarshalRawConfig(bytes)
if err != nil {
return nil, err
}
return prof, nil
}
func handleCrash() {
panic("handle invoke crash")
}
2025-05-02 02:24:12 +08:00
func handleUpdateConfig(bytes []byte) string {
var params = &UpdateParams{}
err := json.Unmarshal(bytes, params)
if err != nil {
return err.Error()
}
updateConfig(params)
return ""
}
2025-07-31 17:09:18 +08:00
func handleDelFile(path string, result ActionResult) {
go func() {
fileInfo, err := os.Stat(path)
if err != nil {
if !os.IsNotExist(err) {
result.success(err.Error())
}
result.success("")
return
}
if fileInfo.IsDir() {
err = os.RemoveAll(path)
if err != nil {
result.success(err.Error())
return
}
} else {
err = os.Remove(path)
if err != nil {
result.success(err.Error())
return
}
}
result.success("")
}()
}
2025-05-02 02:24:12 +08:00
func handleSetupConfig(bytes []byte) string {
2025-12-16 11:23:09 +08:00
if !isInit {
return "not initialized"
}
2025-05-02 02:24:12 +08:00
var params = defaultSetupParams()
err := UnmarshalJson(bytes, params)
if err != nil {
log.Errorln("unmarshalRawConfig error %v", err)
2025-12-16 11:23:09 +08:00
_ = applyConfig(defaultSetupParams())
2025-05-02 02:24:12 +08:00
return err.Error()
}
2025-12-16 11:23:09 +08:00
err = applyConfig(params)
2025-05-02 02:24:12 +08:00
if err != nil {
return err.Error()
}
return ""
}
2024-12-03 21:47:12 +08:00
func init() {
2025-01-13 19:08:17 +08:00
adapter.UrlTestHook = func(url string, name string, delay uint16) {
2024-12-03 21:47:12 +08:00
delayData := &Delay{
2025-01-13 19:08:17 +08:00
Url: url,
2024-12-03 21:47:12 +08:00
Name: name,
}
if delay == 0 {
delayData.Value = -1
} else {
delayData.Value = int32(delay)
}
2025-01-13 19:08:17 +08:00
sendMessage(Message{
2024-12-03 21:47:12 +08:00
Type: DelayMessage,
Data: delayData,
})
}
statistic.DefaultRequestNotify = func(c statistic.Tracker) {
2025-01-13 19:08:17 +08:00
sendMessage(Message{
2024-12-03 21:47:12 +08:00
Type: RequestMessage,
Data: c,
})
}
executor.DefaultProviderLoadedHook = func(providerName string) {
2025-01-13 19:08:17 +08:00
sendMessage(Message{
2024-12-03 21:47:12 +08:00
Type: LoadedMessage,
Data: providerName,
})
}
}