2024-04-30 23:38:49 +08:00
|
|
|
package main
|
2024-12-03 21:47:12 +08:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/metacubex/mihomo/adapter"
|
|
|
|
|
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
|
|
|
|
"github.com/metacubex/mihomo/common/observable"
|
|
|
|
|
"github.com/metacubex/mihomo/common/utils"
|
|
|
|
|
"github.com/metacubex/mihomo/component/updater"
|
|
|
|
|
"github.com/metacubex/mihomo/config"
|
|
|
|
|
"github.com/metacubex/mihomo/constant"
|
|
|
|
|
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"
|
|
|
|
|
"runtime"
|
|
|
|
|
"sort"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
isInit = false
|
|
|
|
|
configParams = ConfigExtendedParams{}
|
|
|
|
|
externalProviders = map[string]cp.Provider{}
|
|
|
|
|
logSubscriber observable.Subscription[log.Event]
|
|
|
|
|
currentConfig *config.Config
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func handleInitClash(homeDirStr string) bool {
|
|
|
|
|
if !isInit {
|
|
|
|
|
constant.SetHomeDir(homeDirStr)
|
|
|
|
|
isInit = true
|
|
|
|
|
}
|
|
|
|
|
return isInit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleStartListener() bool {
|
|
|
|
|
runLock.Lock()
|
|
|
|
|
defer runLock.Unlock()
|
|
|
|
|
isRunning = true
|
|
|
|
|
updateListeners(true)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleStopListener() bool {
|
|
|
|
|
runLock.Lock()
|
|
|
|
|
defer runLock.Unlock()
|
|
|
|
|
isRunning = false
|
|
|
|
|
listener.StopListener()
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleGetIsInit() bool {
|
|
|
|
|
return isInit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleForceGc() {
|
|
|
|
|
go func() {
|
|
|
|
|
log.Infoln("[APP] request force GC")
|
|
|
|
|
runtime.GC()
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleShutdown() bool {
|
|
|
|
|
stopListeners()
|
|
|
|
|
executor.Shutdown()
|
|
|
|
|
runtime.GC()
|
|
|
|
|
isInit = false
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleValidateConfig(bytes []byte) string {
|
|
|
|
|
_, err := config.UnmarshalRawConfig(bytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err.Error()
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleUpdateConfig(bytes []byte) string {
|
|
|
|
|
var params = &GenerateConfigParams{}
|
|
|
|
|
err := json.Unmarshal(bytes, params)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err.Error()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
configParams = params.Params
|
|
|
|
|
prof := decorationConfig(params.ProfileId, params.Config)
|
|
|
|
|
err = applyConfig(prof)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err.Error()
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleGetProxies() string {
|
|
|
|
|
runLock.Lock()
|
|
|
|
|
defer runLock.Unlock()
|
|
|
|
|
data, err := json.Marshal(tunnel.ProxiesWithProviders())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return string(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleGetTraffic(onlyProxy bool) string {
|
|
|
|
|
up, down := statistic.DefaultManager.Current(onlyProxy)
|
|
|
|
|
traffic := map[string]int64{
|
|
|
|
|
"up": up,
|
|
|
|
|
"down": down,
|
|
|
|
|
}
|
|
|
|
|
data, err := json.Marshal(traffic)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("Error:", err)
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return string(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleGetTotalTraffic(onlyProxy bool) string {
|
|
|
|
|
up, down := statistic.DefaultManager.Total(onlyProxy)
|
|
|
|
|
traffic := map[string]int64{
|
|
|
|
|
"up": up,
|
|
|
|
|
"down": down,
|
|
|
|
|
}
|
|
|
|
|
data, err := json.Marshal(traffic)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("Error:", err)
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return string(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleResetTraffic() {
|
|
|
|
|
statistic.DefaultManager.ResetStatistic()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleAsyncTestDelay(paramsString string, fn func(string)) {
|
|
|
|
|
b.Go(paramsString, func() (bool, error) {
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus)
|
|
|
|
|
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 {
|
|
|
|
|
fmt.Println("Error:", err)
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return string(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleCloseConnectionsUnLock() bool {
|
|
|
|
|
statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
|
|
|
|
|
err := c.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleCloseConnections() bool {
|
|
|
|
|
runLock.Lock()
|
|
|
|
|
defer runLock.Unlock()
|
|
|
|
|
statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
|
|
|
|
|
err := c.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
sort.Sort(ExternalProviders(eps))
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
case "GeoIp":
|
|
|
|
|
err := updater.UpdateGeoIpWithPath(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fn(err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
case "GeoSite":
|
|
|
|
|
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("")
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
}
|
|
|
|
|
SendMessage(*message)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleStopLog() {
|
|
|
|
|
if logSubscriber != nil {
|
|
|
|
|
log.UnSubscribe(logSubscriber)
|
|
|
|
|
logSubscriber = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
adapter.UrlTestHook = func(name string, delay uint16) {
|
|
|
|
|
delayData := &Delay{
|
|
|
|
|
Name: name,
|
|
|
|
|
}
|
|
|
|
|
if delay == 0 {
|
|
|
|
|
delayData.Value = -1
|
|
|
|
|
} else {
|
|
|
|
|
delayData.Value = int32(delay)
|
|
|
|
|
}
|
|
|
|
|
SendMessage(Message{
|
|
|
|
|
Type: DelayMessage,
|
|
|
|
|
Data: delayData,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
statistic.DefaultRequestNotify = func(c statistic.Tracker) {
|
|
|
|
|
SendMessage(Message{
|
|
|
|
|
Type: RequestMessage,
|
|
|
|
|
Data: c,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
executor.DefaultProviderLoadedHook = func(providerName string) {
|
|
|
|
|
SendMessage(Message{
|
|
|
|
|
Type: LoadedMessage,
|
|
|
|
|
Data: providerName,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|