provider.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. package provider
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "net/http"
  8. "reflect"
  9. "runtime"
  10. "strings"
  11. "time"
  12. "github.com/metacubex/mihomo/adapter"
  13. "github.com/metacubex/mihomo/common/convert"
  14. "github.com/metacubex/mihomo/common/utils"
  15. mihomoHttp "github.com/metacubex/mihomo/component/http"
  16. "github.com/metacubex/mihomo/component/resource"
  17. C "github.com/metacubex/mihomo/constant"
  18. types "github.com/metacubex/mihomo/constant/provider"
  19. "github.com/metacubex/mihomo/log"
  20. "github.com/metacubex/mihomo/tunnel/statistic"
  21. "github.com/dlclark/regexp2"
  22. "gopkg.in/yaml.v3"
  23. )
  24. const (
  25. ReservedName = "default"
  26. )
  27. type ProxySchema struct {
  28. Proxies []map[string]any `yaml:"proxies"`
  29. }
  30. // ProxySetProvider for auto gc
  31. type ProxySetProvider struct {
  32. *proxySetProvider
  33. }
  34. type proxySetProvider struct {
  35. *resource.Fetcher[[]C.Proxy]
  36. proxies []C.Proxy
  37. healthCheck *HealthCheck
  38. version uint32
  39. subscriptionInfo *SubscriptionInfo
  40. }
  41. func (pp *proxySetProvider) MarshalJSON() ([]byte, error) {
  42. return json.Marshal(map[string]any{
  43. "name": pp.Name(),
  44. "type": pp.Type().String(),
  45. "vehicleType": pp.VehicleType().String(),
  46. "proxies": pp.Proxies(),
  47. "testUrl": pp.healthCheck.url,
  48. "expectedStatus": pp.healthCheck.expectedStatus.String(),
  49. "updatedAt": pp.UpdatedAt,
  50. "subscriptionInfo": pp.subscriptionInfo,
  51. })
  52. }
  53. func (pp *proxySetProvider) Version() uint32 {
  54. return pp.version
  55. }
  56. func (pp *proxySetProvider) Name() string {
  57. return pp.Fetcher.Name()
  58. }
  59. func (pp *proxySetProvider) HealthCheck() {
  60. pp.healthCheck.check()
  61. }
  62. func (pp *proxySetProvider) Update() error {
  63. elm, same, err := pp.Fetcher.Update()
  64. if err == nil && !same {
  65. pp.OnUpdate(elm)
  66. }
  67. return err
  68. }
  69. func (pp *proxySetProvider) Initial() error {
  70. elm, err := pp.Fetcher.Initial()
  71. if err != nil {
  72. return err
  73. }
  74. pp.OnUpdate(elm)
  75. pp.getSubscriptionInfo()
  76. pp.closeAllConnections()
  77. return nil
  78. }
  79. func (pp *proxySetProvider) Type() types.ProviderType {
  80. return types.Proxy
  81. }
  82. func (pp *proxySetProvider) Proxies() []C.Proxy {
  83. return pp.proxies
  84. }
  85. func (pp *proxySetProvider) Touch() {
  86. pp.healthCheck.touch()
  87. }
  88. func (pp *proxySetProvider) HealthCheckURL() string {
  89. return pp.healthCheck.url
  90. }
  91. func (pp *proxySetProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {
  92. pp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval)
  93. }
  94. func (pp *proxySetProvider) setProxies(proxies []C.Proxy) {
  95. pp.proxies = proxies
  96. pp.healthCheck.setProxy(proxies)
  97. if pp.healthCheck.auto() {
  98. go pp.healthCheck.check()
  99. }
  100. }
  101. func (pp *proxySetProvider) getSubscriptionInfo() {
  102. if pp.VehicleType() != types.HTTP {
  103. return
  104. }
  105. go func() {
  106. ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
  107. defer cancel()
  108. resp, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(),
  109. http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil, pp.Vehicle().Proxy())
  110. if err != nil {
  111. return
  112. }
  113. defer resp.Body.Close()
  114. userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo"))
  115. if userInfoStr == "" {
  116. resp2, err := mihomoHttp.HttpRequestWithProxy(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(),
  117. http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil, pp.Vehicle().Proxy())
  118. if err != nil {
  119. return
  120. }
  121. defer resp2.Body.Close()
  122. userInfoStr = strings.TrimSpace(resp2.Header.Get("subscription-userinfo"))
  123. if userInfoStr == "" {
  124. return
  125. }
  126. }
  127. pp.subscriptionInfo, err = NewSubscriptionInfo(userInfoStr)
  128. if err != nil {
  129. log.Warnln("[Provider] get subscription-userinfo: %e", err)
  130. }
  131. }()
  132. }
  133. func (pp *proxySetProvider) closeAllConnections() {
  134. statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
  135. for _, chain := range c.Chains() {
  136. if chain == pp.Name() {
  137. _ = c.Close()
  138. break
  139. }
  140. }
  141. return true
  142. })
  143. }
  144. func stopProxyProvider(pd *ProxySetProvider) {
  145. pd.healthCheck.close()
  146. _ = pd.Fetcher.Destroy()
  147. }
  148. func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {
  149. excludeFilterReg, err := regexp2.Compile(excludeFilter, regexp2.None)
  150. if err != nil {
  151. return nil, fmt.Errorf("invalid excludeFilter regex: %w", err)
  152. }
  153. var excludeTypeArray []string
  154. if excludeType != "" {
  155. excludeTypeArray = strings.Split(excludeType, "|")
  156. }
  157. var filterRegs []*regexp2.Regexp
  158. for _, filter := range strings.Split(filter, "`") {
  159. filterReg, err := regexp2.Compile(filter, regexp2.None)
  160. if err != nil {
  161. return nil, fmt.Errorf("invalid filter regex: %w", err)
  162. }
  163. filterRegs = append(filterRegs, filterReg)
  164. }
  165. if hc.auto() {
  166. go hc.process()
  167. }
  168. pd := &proxySetProvider{
  169. proxies: []C.Proxy{},
  170. healthCheck: hc,
  171. }
  172. fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd))
  173. pd.Fetcher = fetcher
  174. wrapper := &ProxySetProvider{pd}
  175. runtime.SetFinalizer(wrapper, stopProxyProvider)
  176. return wrapper, nil
  177. }
  178. // CompatibleProvider for auto gc
  179. type CompatibleProvider struct {
  180. *compatibleProvider
  181. }
  182. type compatibleProvider struct {
  183. name string
  184. healthCheck *HealthCheck
  185. proxies []C.Proxy
  186. version uint32
  187. }
  188. func (cp *compatibleProvider) MarshalJSON() ([]byte, error) {
  189. return json.Marshal(map[string]any{
  190. "name": cp.Name(),
  191. "type": cp.Type().String(),
  192. "vehicleType": cp.VehicleType().String(),
  193. "proxies": cp.Proxies(),
  194. "testUrl": cp.healthCheck.url,
  195. "expectedStatus": cp.healthCheck.expectedStatus.String(),
  196. })
  197. }
  198. func (cp *compatibleProvider) Version() uint32 {
  199. return cp.version
  200. }
  201. func (cp *compatibleProvider) Name() string {
  202. return cp.name
  203. }
  204. func (cp *compatibleProvider) HealthCheck() {
  205. cp.healthCheck.check()
  206. }
  207. func (cp *compatibleProvider) Update() error {
  208. return nil
  209. }
  210. func (cp *compatibleProvider) Initial() error {
  211. if cp.healthCheck.interval != 0 && cp.healthCheck.url != "" {
  212. cp.HealthCheck()
  213. }
  214. return nil
  215. }
  216. func (cp *compatibleProvider) VehicleType() types.VehicleType {
  217. return types.Compatible
  218. }
  219. func (cp *compatibleProvider) Type() types.ProviderType {
  220. return types.Proxy
  221. }
  222. func (cp *compatibleProvider) Proxies() []C.Proxy {
  223. return cp.proxies
  224. }
  225. func (cp *compatibleProvider) Touch() {
  226. cp.healthCheck.touch()
  227. }
  228. func (cp *compatibleProvider) HealthCheckURL() string {
  229. return cp.healthCheck.url
  230. }
  231. func (cp *compatibleProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {
  232. cp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval)
  233. }
  234. func stopCompatibleProvider(pd *CompatibleProvider) {
  235. pd.healthCheck.close()
  236. }
  237. func NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) {
  238. if len(proxies) == 0 {
  239. return nil, errors.New("provider need one proxy at least")
  240. }
  241. if hc.auto() {
  242. go hc.process()
  243. }
  244. pd := &compatibleProvider{
  245. name: name,
  246. proxies: proxies,
  247. healthCheck: hc,
  248. }
  249. wrapper := &CompatibleProvider{pd}
  250. runtime.SetFinalizer(wrapper, stopCompatibleProvider)
  251. return wrapper, nil
  252. }
  253. func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) {
  254. return func(elm []C.Proxy) {
  255. pd.setProxies(elm)
  256. pd.version += 1
  257. pd.getSubscriptionInfo()
  258. }
  259. }
  260. func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string, override OverrideSchema) resource.Parser[[]C.Proxy] {
  261. return func(buf []byte) ([]C.Proxy, error) {
  262. schema := &ProxySchema{}
  263. if err := yaml.Unmarshal(buf, schema); err != nil {
  264. proxies, err1 := convert.ConvertsV2Ray(buf)
  265. if err1 != nil {
  266. return nil, fmt.Errorf("%w, %w", err, err1)
  267. }
  268. schema.Proxies = proxies
  269. }
  270. if schema.Proxies == nil {
  271. return nil, errors.New("file must have a `proxies` field")
  272. }
  273. proxies := []C.Proxy{}
  274. proxiesSet := map[string]struct{}{}
  275. for _, filterReg := range filterRegs {
  276. for idx, mapping := range schema.Proxies {
  277. if nil != excludeTypeArray && len(excludeTypeArray) > 0 {
  278. mType, ok := mapping["type"]
  279. if !ok {
  280. continue
  281. }
  282. pType, ok := mType.(string)
  283. if !ok {
  284. continue
  285. }
  286. flag := false
  287. for i := range excludeTypeArray {
  288. if strings.EqualFold(pType, excludeTypeArray[i]) {
  289. flag = true
  290. break
  291. }
  292. }
  293. if flag {
  294. continue
  295. }
  296. }
  297. mName, ok := mapping["name"]
  298. if !ok {
  299. continue
  300. }
  301. name, ok := mName.(string)
  302. if !ok {
  303. continue
  304. }
  305. if len(excludeFilter) > 0 {
  306. if mat, _ := excludeFilterReg.MatchString(name); mat {
  307. continue
  308. }
  309. }
  310. if len(filter) > 0 {
  311. if mat, _ := filterReg.MatchString(name); !mat {
  312. continue
  313. }
  314. }
  315. if _, ok := proxiesSet[name]; ok {
  316. continue
  317. }
  318. if len(dialerProxy) > 0 {
  319. mapping["dialer-proxy"] = dialerProxy
  320. }
  321. val := reflect.ValueOf(override)
  322. for i := 0; i < val.NumField(); i++ {
  323. field := val.Field(i)
  324. if field.IsNil() {
  325. continue
  326. }
  327. fieldName := strings.Split(val.Type().Field(i).Tag.Get("provider"), ",")[0]
  328. switch fieldName {
  329. case "additional-prefix":
  330. name := mapping["name"].(string)
  331. mapping["name"] = *field.Interface().(*string) + name
  332. case "additional-suffix":
  333. name := mapping["name"].(string)
  334. mapping["name"] = name + *field.Interface().(*string)
  335. default:
  336. mapping[fieldName] = field.Elem().Interface()
  337. }
  338. }
  339. proxy, err := adapter.ParseProxy(mapping)
  340. if err != nil {
  341. return nil, fmt.Errorf("proxy %d error: %w", idx, err)
  342. }
  343. proxiesSet[name] = struct{}{}
  344. proxies = append(proxies, proxy)
  345. }
  346. }
  347. if len(proxies) == 0 {
  348. if len(filter) > 0 {
  349. return nil, errors.New("doesn't match any proxy, please check your filter")
  350. }
  351. return nil, errors.New("file doesn't have any proxy")
  352. }
  353. return proxies, nil
  354. }
  355. }