common.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. package main
  2. import "C"
  3. import (
  4. "context"
  5. "errors"
  6. "math"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "runtime"
  11. "strings"
  12. "sync"
  13. "syscall"
  14. "time"
  15. "github.com/metacubex/mihomo/adapter"
  16. "github.com/metacubex/mihomo/adapter/inbound"
  17. "github.com/metacubex/mihomo/adapter/outboundgroup"
  18. "github.com/metacubex/mihomo/adapter/provider"
  19. "github.com/metacubex/mihomo/common/batch"
  20. "github.com/metacubex/mihomo/component/dialer"
  21. "github.com/metacubex/mihomo/component/resolver"
  22. "github.com/metacubex/mihomo/component/sniffer"
  23. "github.com/metacubex/mihomo/config"
  24. "github.com/metacubex/mihomo/constant"
  25. cp "github.com/metacubex/mihomo/constant/provider"
  26. "github.com/metacubex/mihomo/hub"
  27. "github.com/metacubex/mihomo/hub/executor"
  28. "github.com/metacubex/mihomo/hub/route"
  29. "github.com/metacubex/mihomo/listener"
  30. "github.com/metacubex/mihomo/log"
  31. rp "github.com/metacubex/mihomo/rules/provider"
  32. "github.com/metacubex/mihomo/tunnel"
  33. )
  34. type ConfigExtendedParams struct {
  35. IsPatch bool `json:"is-patch"`
  36. IsCompatible bool `json:"is-compatible"`
  37. SelectedMap map[string]string `json:"selected-map"`
  38. TestURL *string `json:"test-url"`
  39. }
  40. type GenerateConfigParams struct {
  41. ProfileId string `json:"profile-id"`
  42. Config config.RawConfig `json:"config" `
  43. Params ConfigExtendedParams `json:"params"`
  44. }
  45. type ChangeProxyParams struct {
  46. GroupName *string `json:"group-name"`
  47. ProxyName *string `json:"proxy-name"`
  48. }
  49. type TestDelayParams struct {
  50. ProxyName string `json:"proxy-name"`
  51. Timeout int64 `json:"timeout"`
  52. }
  53. type ProcessMapItem struct {
  54. Id int64 `json:"id"`
  55. Value string `json:"value"`
  56. }
  57. type ExternalProvider struct {
  58. Name string `json:"name"`
  59. Type string `json:"type"`
  60. VehicleType string `json:"vehicle-type"`
  61. Count int `json:"count"`
  62. Path string `json:"path"`
  63. UpdateAt time.Time `json:"update-at"`
  64. }
  65. type ExternalProviders []ExternalProvider
  66. func (a ExternalProviders) Len() int { return len(a) }
  67. func (a ExternalProviders) Less(i, j int) bool { return a[i].Name < a[j].Name }
  68. func (a ExternalProviders) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  69. var b, _ = batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](50))
  70. func restartExecutable(execPath string) {
  71. var err error
  72. executor.Shutdown()
  73. if runtime.GOOS == "windows" {
  74. cmd := exec.Command(execPath, os.Args[1:]...)
  75. log.Infoln("restarting: %q %q", execPath, os.Args[1:])
  76. cmd.Stdin = os.Stdin
  77. cmd.Stdout = os.Stdout
  78. cmd.Stderr = os.Stderr
  79. err = cmd.Start()
  80. if err != nil {
  81. log.Fatalln("restarting: %s", err)
  82. }
  83. os.Exit(0)
  84. }
  85. log.Infoln("restarting: %q %q", execPath, os.Args[1:])
  86. err = syscall.Exec(execPath, os.Args, os.Environ())
  87. if err != nil {
  88. log.Fatalln("restarting: %s", err)
  89. }
  90. }
  91. func readFile(path string) ([]byte, error) {
  92. if _, err := os.Stat(path); os.IsNotExist(err) {
  93. return nil, err
  94. }
  95. data, err := os.ReadFile(path)
  96. if err != nil {
  97. return nil, err
  98. }
  99. return data, err
  100. }
  101. func removeFile(path string) error {
  102. absPath, err := filepath.Abs(path)
  103. if err != nil {
  104. return err
  105. }
  106. err = os.Remove(absPath)
  107. if err != nil {
  108. return err
  109. }
  110. return nil
  111. }
  112. func getProfilePath(id string) string {
  113. return filepath.Join(constant.Path.HomeDir(), "profiles", id+".yaml")
  114. }
  115. func getProfileProvidersPath(id string) string {
  116. return filepath.Join(constant.Path.HomeDir(), "providers", id)
  117. }
  118. func getRawConfigWithId(id string) *config.RawConfig {
  119. path := getProfilePath(id)
  120. bytes, err := readFile(path)
  121. if err != nil {
  122. log.Errorln("profile is not exist")
  123. return config.DefaultRawConfig()
  124. }
  125. prof, err := config.UnmarshalRawConfig(bytes)
  126. if err != nil {
  127. log.Errorln("unmarshalRawConfig error %v", err)
  128. return config.DefaultRawConfig()
  129. }
  130. for _, mapping := range prof.ProxyProvider {
  131. value, exist := mapping["path"].(string)
  132. if !exist {
  133. continue
  134. }
  135. mapping["path"] = filepath.Join(getProfileProvidersPath(id), value)
  136. }
  137. for _, mapping := range prof.RuleProvider {
  138. value, exist := mapping["path"].(string)
  139. if !exist {
  140. continue
  141. }
  142. mapping["path"] = filepath.Join(getProfileProvidersPath(id), value)
  143. }
  144. return prof
  145. }
  146. func getExternalProvidersRaw() map[string]cp.Provider {
  147. eps := make(map[string]cp.Provider)
  148. for n, p := range tunnel.Providers() {
  149. if p.VehicleType() != cp.Compatible {
  150. eps[n] = p
  151. }
  152. }
  153. for n, p := range tunnel.RuleProviders() {
  154. if p.VehicleType() != cp.Compatible {
  155. eps[n] = p
  156. }
  157. }
  158. return eps
  159. }
  160. func toExternalProvider(p cp.Provider) (*ExternalProvider, error) {
  161. switch p.(type) {
  162. case *provider.ProxySetProvider:
  163. psp := p.(*provider.ProxySetProvider)
  164. return &ExternalProvider{
  165. Name: psp.Name(),
  166. Type: psp.Type().String(),
  167. VehicleType: psp.VehicleType().String(),
  168. Count: psp.Count(),
  169. Path: psp.Vehicle().Path(),
  170. UpdateAt: psp.UpdatedAt,
  171. }, nil
  172. case *rp.RuleSetProvider:
  173. rsp := p.(*rp.RuleSetProvider)
  174. return &ExternalProvider{
  175. Name: rsp.Name(),
  176. Type: rsp.Type().String(),
  177. VehicleType: rsp.VehicleType().String(),
  178. Count: rsp.Count(),
  179. Path: rsp.Vehicle().Path(),
  180. UpdateAt: rsp.UpdatedAt,
  181. }, nil
  182. default:
  183. return nil, errors.New("not external provider")
  184. }
  185. }
  186. func sideUpdateExternalProvider(p cp.Provider, bytes []byte) error {
  187. switch p.(type) {
  188. case *provider.ProxySetProvider:
  189. psp := p.(*provider.ProxySetProvider)
  190. elm, same, err := psp.SideUpdate(bytes)
  191. if err == nil && !same {
  192. psp.OnUpdate(elm)
  193. }
  194. return nil
  195. case rp.RuleSetProvider:
  196. rsp := p.(*rp.RuleSetProvider)
  197. elm, same, err := rsp.SideUpdate(bytes)
  198. if err == nil && !same {
  199. rsp.OnUpdate(elm)
  200. }
  201. return nil
  202. default:
  203. return errors.New("not external provider")
  204. }
  205. }
  206. func decorationConfig(profileId string, cfg config.RawConfig) *config.RawConfig {
  207. prof := getRawConfigWithId(profileId)
  208. overwriteConfig(prof, cfg)
  209. return prof
  210. }
  211. func Reduce[T any, U any](s []T, initVal U, f func(U, T) U) U {
  212. for _, v := range s {
  213. initVal = f(initVal, v)
  214. }
  215. return initVal
  216. }
  217. func Map[T, U any](slice []T, fn func(T) U) []U {
  218. result := make([]U, len(slice))
  219. for i, v := range slice {
  220. result[i] = fn(v)
  221. }
  222. return result
  223. }
  224. func replaceFromMap(s string, m map[string]string) string {
  225. for k, v := range m {
  226. s = strings.ReplaceAll(s, k, v)
  227. }
  228. return s
  229. }
  230. func removeDuplicateFromSlice[T any](slice []T) []T {
  231. result := make([]T, 0)
  232. seen := make(map[any]struct{})
  233. for _, value := range slice {
  234. if _, ok := seen[value]; !ok {
  235. result = append(result, value)
  236. seen[value] = struct{}{}
  237. }
  238. }
  239. return result
  240. }
  241. func generateProxyGroupAndRule(proxyGroup *[]map[string]any, rule *[]string) {
  242. var replacements = map[string]string{}
  243. var selectArr []map[string]any
  244. var urlTestArr []map[string]any
  245. var fallbackArr []map[string]any
  246. for _, group := range *proxyGroup {
  247. switch group["type"] {
  248. case "select":
  249. selectArr = append(selectArr, group)
  250. replacements[group["name"].(string)] = "Proxy"
  251. break
  252. case "url-test":
  253. urlTestArr = append(urlTestArr, group)
  254. replacements[group["name"].(string)] = "Auto"
  255. break
  256. case "fallback":
  257. fallbackArr = append(fallbackArr, group)
  258. replacements[group["name"].(string)] = "Fallback"
  259. break
  260. default:
  261. break
  262. }
  263. }
  264. ProxyProxies := Reduce(selectArr, []string{}, func(res []string, cur map[string]any) []string {
  265. if cur["proxies"] == nil {
  266. return res
  267. }
  268. for _, proxyName := range cur["proxies"].([]interface{}) {
  269. if str, ok := proxyName.(string); ok {
  270. str = replaceFromMap(str, replacements)
  271. if str != "Proxy" {
  272. res = append(res, str)
  273. }
  274. }
  275. }
  276. return res
  277. })
  278. ProxyProxies = removeDuplicateFromSlice(ProxyProxies)
  279. AutoProxies := Reduce(urlTestArr, []string{}, func(res []string, cur map[string]any) []string {
  280. if cur["proxies"] == nil {
  281. return res
  282. }
  283. for _, proxyName := range cur["proxies"].([]interface{}) {
  284. if str, ok := proxyName.(string); ok {
  285. str = replaceFromMap(str, replacements)
  286. if str != "Auto" {
  287. res = append(res, str)
  288. }
  289. }
  290. }
  291. return res
  292. })
  293. AutoProxies = removeDuplicateFromSlice(AutoProxies)
  294. FallbackProxies := Reduce(fallbackArr, []string{}, func(res []string, cur map[string]any) []string {
  295. if cur["proxies"] == nil {
  296. return res
  297. }
  298. for _, proxyName := range cur["proxies"].([]interface{}) {
  299. if str, ok := proxyName.(string); ok {
  300. str = replaceFromMap(str, replacements)
  301. if str != "Fallback" {
  302. res = append(res, str)
  303. }
  304. }
  305. }
  306. return res
  307. })
  308. FallbackProxies = removeDuplicateFromSlice(FallbackProxies)
  309. var computedProxyGroup []map[string]any
  310. if len(ProxyProxies) > 0 {
  311. computedProxyGroup = append(computedProxyGroup,
  312. map[string]any{
  313. "name": "Proxy",
  314. "type": "select",
  315. "proxies": ProxyProxies,
  316. })
  317. }
  318. if len(AutoProxies) > 0 {
  319. computedProxyGroup = append(computedProxyGroup,
  320. map[string]any{
  321. "name": "Auto",
  322. "type": "url-test",
  323. "proxies": AutoProxies,
  324. })
  325. }
  326. if len(FallbackProxies) > 0 {
  327. computedProxyGroup = append(computedProxyGroup,
  328. map[string]any{
  329. "name": "Fallback",
  330. "type": "fallback",
  331. "proxies": FallbackProxies,
  332. })
  333. }
  334. computedRule := Map(*rule, func(value string) string {
  335. return replaceFromMap(value, replacements)
  336. })
  337. *proxyGroup = computedProxyGroup
  338. *rule = computedRule
  339. }
  340. func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig) {
  341. targetConfig.ExternalController = patchConfig.ExternalController
  342. targetConfig.ExternalUI = ""
  343. targetConfig.Interface = ""
  344. targetConfig.ExternalUIURL = ""
  345. targetConfig.TCPConcurrent = patchConfig.TCPConcurrent
  346. targetConfig.UnifiedDelay = patchConfig.UnifiedDelay
  347. //targetConfig.GeodataMode = false
  348. targetConfig.IPv6 = patchConfig.IPv6
  349. targetConfig.LogLevel = patchConfig.LogLevel
  350. targetConfig.Port = 0
  351. targetConfig.SocksPort = 0
  352. targetConfig.KeepAliveInterval = patchConfig.KeepAliveInterval
  353. targetConfig.MixedPort = patchConfig.MixedPort
  354. targetConfig.FindProcessMode = patchConfig.FindProcessMode
  355. targetConfig.AllowLan = patchConfig.AllowLan
  356. targetConfig.Mode = patchConfig.Mode
  357. targetConfig.Tun.Enable = patchConfig.Tun.Enable
  358. targetConfig.Tun.Device = patchConfig.Tun.Device
  359. targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack
  360. targetConfig.Tun.Stack = patchConfig.Tun.Stack
  361. targetConfig.GeodataLoader = patchConfig.GeodataLoader
  362. targetConfig.Profile.StoreSelected = false
  363. targetConfig.GeoXUrl = patchConfig.GeoXUrl
  364. targetConfig.GlobalUA = patchConfig.GlobalUA
  365. if targetConfig.DNS.Enable == false {
  366. targetConfig.DNS = patchConfig.DNS
  367. }
  368. //if runtime.GOOS == "android" {
  369. // targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, "dhcp://"+dns.SystemDNSPlaceholder)
  370. //} else if runtime.GOOS == "windows" {
  371. // targetConfig.DNS.NameServer = append(targetConfig.DNS.NameServer, dns.SystemDNSPlaceholder)
  372. //}
  373. if configParams.IsCompatible == false {
  374. targetConfig.ProxyProvider = make(map[string]map[string]any)
  375. targetConfig.RuleProvider = make(map[string]map[string]any)
  376. generateProxyGroupAndRule(&targetConfig.ProxyGroup, &targetConfig.Rule)
  377. }
  378. }
  379. func patchConfig(general *config.General) {
  380. log.Infoln("[Apply] patch")
  381. route.ReStartServer(general.ExternalController)
  382. if sniffer.Dispatcher != nil {
  383. tunnel.SetSniffing(general.Sniffing)
  384. }
  385. tunnel.SetFindProcessMode(general.FindProcessMode)
  386. dialer.SetTcpConcurrent(general.TCPConcurrent)
  387. dialer.DefaultInterface.Store(general.Interface)
  388. adapter.UnifiedDelay.Store(general.UnifiedDelay)
  389. tunnel.SetMode(general.Mode)
  390. log.SetLevel(general.LogLevel)
  391. resolver.DisableIPv6 = !general.IPv6
  392. }
  393. var isRunning = false
  394. var runLock sync.Mutex
  395. func updateListeners(general *config.General, listeners map[string]constant.InboundListener) {
  396. listener.PatchInboundListeners(listeners, tunnel.Tunnel, true)
  397. listener.SetAllowLan(general.AllowLan)
  398. inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)
  399. inbound.SetAllowedIPs(general.LanAllowedIPs)
  400. inbound.SetDisAllowedIPs(general.LanDisAllowedIPs)
  401. listener.SetBindAddress(general.BindAddress)
  402. listener.ReCreateHTTP(general.Port, tunnel.Tunnel)
  403. listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)
  404. listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)
  405. listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel)
  406. listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)
  407. listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)
  408. listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)
  409. listener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel)
  410. listener.ReCreateTuic(general.TuicServer, tunnel.Tunnel)
  411. listener.ReCreateTun(general.Tun, tunnel.Tunnel)
  412. listener.ReCreateRedirToTun(general.EBpf.RedirectToTun)
  413. }
  414. func stopListeners() {
  415. listener.StopListener()
  416. }
  417. func hcCompatibleProvider(proxyProviders map[string]cp.ProxyProvider) {
  418. wg := sync.WaitGroup{}
  419. ch := make(chan struct{}, math.MaxInt)
  420. for _, proxyProvider := range proxyProviders {
  421. proxyProvider := proxyProvider
  422. if proxyProvider.VehicleType() == cp.Compatible {
  423. log.Infoln("Start initial Compatible provider %s", proxyProvider.Name())
  424. wg.Add(1)
  425. ch <- struct{}{}
  426. go func() {
  427. defer func() { <-ch; wg.Done() }()
  428. if err := proxyProvider.Initial(); err != nil {
  429. log.Errorln("initial Compatible provider %s error: %v", proxyProvider.Name(), err)
  430. }
  431. }()
  432. }
  433. }
  434. }
  435. func patchSelectGroup() {
  436. mapping := configParams.SelectedMap
  437. if mapping == nil {
  438. return
  439. }
  440. for name, proxy := range tunnel.ProxiesWithProviders() {
  441. outbound, ok := proxy.(*adapter.Proxy)
  442. if !ok {
  443. continue
  444. }
  445. selector, ok := outbound.ProxyAdapter.(outboundgroup.SelectAble)
  446. if !ok {
  447. continue
  448. }
  449. selected, exist := mapping[name]
  450. if !exist {
  451. continue
  452. }
  453. selector.ForceSet(selected)
  454. }
  455. }
  456. func applyConfig() error {
  457. cfg, err := config.ParseRawConfig(currentRawConfig)
  458. if err != nil {
  459. cfg, _ = config.ParseRawConfig(config.DefaultRawConfig())
  460. }
  461. if configParams.TestURL != nil {
  462. constant.DefaultTestURL = *configParams.TestURL
  463. }
  464. if configParams.IsPatch {
  465. patchConfig(cfg.General)
  466. } else {
  467. closeConnections()
  468. runtime.GC()
  469. hub.UltraApplyConfig(cfg)
  470. patchSelectGroup()
  471. }
  472. if isRunning {
  473. updateListeners(cfg.General, cfg.Listeners)
  474. hcCompatibleProvider(cfg.Providers)
  475. }
  476. externalProviders = getExternalProvidersRaw()
  477. return err
  478. }