github.com/yaling888/clash@v1.53.0/adapter/provider/provider.go (about) 1 package provider 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "os" 10 "runtime" 11 "sync" 12 "time" 13 14 regexp "github.com/dlclark/regexp2" 15 "github.com/phuslu/log" 16 "github.com/samber/lo" 17 "gopkg.in/yaml.v3" 18 19 "github.com/yaling888/clash/adapter" 20 "github.com/yaling888/clash/adapter/outbound" 21 "github.com/yaling888/clash/common/convert" 22 "github.com/yaling888/clash/common/singledo" 23 "github.com/yaling888/clash/common/structure" 24 "github.com/yaling888/clash/component/resolver" 25 C "github.com/yaling888/clash/constant" 26 types "github.com/yaling888/clash/constant/provider" 27 "github.com/yaling888/clash/tunnel/statistic" 28 ) 29 30 var ( 31 group = &singledo.Group[[]C.Proxy]{} 32 reject = adapter.NewProxy(outbound.NewReject()) 33 ) 34 35 const ( 36 ReservedName = "default" 37 ) 38 39 type ProxySchema struct { 40 Proxies []C.RawProxy `yaml:"proxies"` 41 } 42 43 var _ types.ProxyProvider = (*ProxySetProvider)(nil) 44 45 type ProxySetProvider struct { 46 healthCheck *HealthCheck 47 proxies []C.Proxy 48 groupNames []string 49 globalFCV bool 50 tmCheck *time.Timer 51 52 mux sync.Mutex // guards following fields 53 hash [16]byte // config file hash 54 *fetcher[[]C.Proxy] 55 } 56 57 func (pp *ProxySetProvider) MarshalJSON() ([]byte, error) { 58 return json.Marshal(map[string]any{ 59 "name": pp.Name(), 60 "type": pp.Type().String(), 61 "vehicleType": pp.VehicleType().String(), 62 "proxies": pp.Proxies(), 63 "subscription": pp.subscription(), 64 "updatedAt": pp.updatedAt, 65 }) 66 } 67 68 func (pp *ProxySetProvider) Name() string { 69 return pp.name 70 } 71 72 func (pp *ProxySetProvider) HealthCheck() { 73 pp.healthCheck.checkAll() 74 } 75 76 func (pp *ProxySetProvider) Update() error { 77 defer runtime.GC() 78 79 pp.mux.Lock() 80 81 buf, err := os.ReadFile(C.Path.Config()) 82 if err != nil { 83 pp.mux.Unlock() 84 return err 85 } 86 87 hash := md5.Sum(buf) 88 if !bytes.Equal(pp.hash[:], hash[:]) { 89 pp.mux.Unlock() 90 91 rawCfg := struct { 92 ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` 93 }{} 94 95 if err = yaml.Unmarshal(buf, &rawCfg); err != nil { 96 return err 97 } 98 99 decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true}) 100 101 schema := &proxyProviderSchema{ForceCertVerify: pp.globalFCV} 102 103 for name, mapping := range rawCfg.ProxyProvider { 104 if name == pp.name { 105 if err := decoder.Decode(mapping, schema); err != nil { 106 return err 107 } 108 break 109 } 110 } 111 112 vehicle, err := newVehicle(schema) 113 if err != nil { 114 return err 115 } 116 117 option := adapter.ProxyOption{ 118 ForceCertVerify: schema.ForceCertVerify, 119 ForceUDP: schema.UDP, 120 DisableUDP: schema.DisableUDP, 121 DisableDNS: schema.DisableDNS, 122 RandomHost: schema.RandomHost, 123 PrefixName: schema.PrefixName, 124 AutoCipher: true, 125 } 126 127 pp.mux.Lock() 128 129 _, err = newOrUpdateFetcher(pp.name, schema.Interval, schema.Filter, vehicle, nil, pp.globalFCV, option, pp) 130 if err != nil { 131 pp.mux.Unlock() 132 return err 133 } 134 135 pp.hash = hash 136 } 137 pp.mux.Unlock() 138 139 elm, same, err := pp.fetcher.Update() 140 if err != nil { 141 return err 142 } 143 if same { 144 log.Info().Str("name", pp.Name()).Msg("[Provider] proxies doesn't change") 145 return nil 146 } 147 148 pp.onUpdate(elm) 149 return nil 150 } 151 152 func (pp *ProxySetProvider) Initial() error { 153 proxies, err := pp.fetcher.Initial() 154 if err != nil { 155 return err 156 } 157 158 if proxies == nil { 159 name := pp.name + "-" + "Reject" 160 proxies = append(proxies, adapter.NewProxy(outbound.NewRejectByName(name))) 161 } 162 pp.onUpdate(proxies) 163 return nil 164 } 165 166 func (pp *ProxySetProvider) Type() types.ProviderType { 167 return types.Proxy 168 } 169 170 func (pp *ProxySetProvider) Proxies() []C.Proxy { 171 return pp.proxies 172 } 173 174 func (pp *ProxySetProvider) Touch() { 175 pp.healthCheck.touch() 176 } 177 178 func (pp *ProxySetProvider) Finalize() { 179 if pp.tmCheck != nil { 180 pp.tmCheck.Stop() 181 pp.tmCheck = nil 182 } 183 pp.healthCheck.close() 184 _ = pp.fetcher.Destroy() 185 } 186 187 func (pp *ProxySetProvider) setProxies(proxies []C.Proxy) { 188 old := pp.proxies 189 pp.proxies = proxies 190 pp.healthCheck.setProxy(proxies) 191 192 for _, name := range pp.groupNames { 193 group.Forget(name) 194 } 195 196 if len(old) != 0 { 197 names := lo.Map(old, func(item C.Proxy, _ int) string { 198 p := item.(C.ProxyAdapter) 199 name := p.Name() 200 go func() { 201 p.Cleanup() 202 resolver.RemoveCache(name) 203 }() 204 return name 205 }) 206 statistic.DefaultManager.KickOut(names...) 207 go pp.healthCheck.checkAll() 208 } else { 209 pp.tmCheck = time.AfterFunc(45*time.Second, func() { 210 pp.healthCheck.checkAll() 211 pp.tmCheck = nil 212 }) 213 } 214 } 215 216 func (pp *ProxySetProvider) addGroupName(name string) { 217 pp.groupNames = append(pp.groupNames, name) 218 } 219 220 func (pp *ProxySetProvider) subscription() *Subscription { 221 if s, ok := pp.vehicle.(interface{ Subscription() *Subscription }); ok { 222 return s.Subscription() 223 } 224 return nil 225 } 226 227 func NewProxySetProvider( 228 name string, 229 interval time.Duration, 230 filter string, 231 vehicle types.Vehicle, 232 hc *HealthCheck, 233 globalForceCertVerify bool, 234 option adapter.ProxyOption, 235 ) (*ProxySetProvider, error) { 236 return newOrUpdateFetcher(name, interval, filter, vehicle, hc, globalForceCertVerify, option, nil) 237 } 238 239 func newOrUpdateFetcher( 240 name string, 241 interval time.Duration, 242 filter string, 243 vehicle types.Vehicle, 244 hc *HealthCheck, 245 globalForceCertVerify bool, 246 option adapter.ProxyOption, 247 pd *ProxySetProvider, 248 ) (*ProxySetProvider, error) { 249 var filterReg *regexp.Regexp 250 if filter != "" { 251 f, err := regexp.Compile(filter, 0) 252 if err != nil { 253 return nil, fmt.Errorf("invalid filter regex: %w", err) 254 } 255 filterReg = f 256 } 257 258 if pd == nil { 259 if hc.auto() { 260 go hc.process() 261 } 262 263 pd = &ProxySetProvider{ 264 proxies: []C.Proxy{}, 265 healthCheck: hc, 266 globalFCV: globalForceCertVerify, 267 } 268 } else { 269 _ = pd.fetcher.Destroy() 270 } 271 272 pd.fetcher = newFetcher[[]C.Proxy]( 273 name, 274 interval, 275 vehicle, 276 proxiesParseAndFilter(filterReg, option), 277 proxiesOnUpdate(pd), 278 ) 279 280 return pd, nil 281 } 282 283 var _ types.ProxyProvider = (*CompatibleProvider)(nil) 284 285 type CompatibleProvider struct { 286 name string 287 proxies []C.Proxy 288 providers []types.ProxyProvider 289 healthCheck *HealthCheck 290 filterRegx *regexp.Regexp 291 tmCheck *time.Timer 292 293 hasProxy bool 294 hasProvider bool 295 } 296 297 func (cp *CompatibleProvider) MarshalJSON() ([]byte, error) { 298 return json.Marshal(map[string]any{ 299 "name": cp.Name(), 300 "type": cp.Type().String(), 301 "vehicleType": cp.VehicleType().String(), 302 "proxies": cp.Proxies(), 303 }) 304 } 305 306 func (cp *CompatibleProvider) Name() string { 307 return cp.name 308 } 309 310 func (cp *CompatibleProvider) HealthCheck() { 311 cp.healthCheck.checkAll() 312 } 313 314 func (cp *CompatibleProvider) Update() error { 315 return nil 316 } 317 318 func (cp *CompatibleProvider) Initial() error { 319 cp.Forget() 320 if cp.hasProxy && !cp.hasProvider { 321 cp.healthCheckWait() 322 } else if len(cp.Proxies()) == 0 { 323 return errors.New("provider need one proxy at least") 324 } 325 return nil 326 } 327 328 func (cp *CompatibleProvider) VehicleType() types.VehicleType { 329 return types.Compatible 330 } 331 332 func (cp *CompatibleProvider) Type() types.ProviderType { 333 return types.Proxy 334 } 335 336 func (cp *CompatibleProvider) Proxies() []C.Proxy { 337 if !cp.hasProvider { 338 return cp.proxies 339 } 340 341 proxies, _, hitCache := group.Do(cp.name, func() ([]C.Proxy, error) { 342 var proxies []C.Proxy 343 if cp.filterRegx != nil { 344 proxies = lo.FlatMap( 345 cp.providers, 346 func(provider types.ProxyProvider, _ int) []C.Proxy { 347 return lo.Filter( 348 provider.Proxies(), 349 func(proxy C.Proxy, _ int) bool { 350 rs, _ := cp.filterRegx.MatchString(proxy.Name()) 351 return rs 352 }) 353 }) 354 355 if cp.hasProxy { 356 if len(proxies) == 0 { 357 return cp.proxies, nil 358 } 359 proxies = append(cp.proxies, proxies...) 360 } else if len(proxies) == 0 { 361 proxies = append(proxies, reject) 362 } 363 } else { 364 proxies = lo.FlatMap( 365 cp.providers, 366 func(pd types.ProxyProvider, _ int) []C.Proxy { 367 return pd.Proxies() 368 }) 369 370 if cp.hasProxy { 371 proxies = append(cp.proxies, proxies...) 372 } 373 } 374 375 return proxies, nil 376 }) 377 378 if !hitCache { 379 cp.healthCheckWait() 380 } 381 382 return proxies 383 } 384 385 func (cp *CompatibleProvider) Touch() { 386 cp.healthCheck.touch() 387 } 388 389 func (cp *CompatibleProvider) SetProxies(proxies []C.Proxy) { 390 cp.proxies = proxies 391 cp.hasProxy = len(cp.proxies) != 0 392 } 393 394 func (cp *CompatibleProvider) SetProviders(providers []types.ProxyProvider) { 395 for _, elem := range providers { 396 if e, ok := elem.(*ProxySetProvider); ok { 397 e.addGroupName(cp.Name()) 398 } 399 } 400 cp.providers = providers 401 cp.hasProvider = len(cp.providers) != 0 402 } 403 404 func (cp *CompatibleProvider) Forget() { 405 group.Forget(cp.name) 406 } 407 408 func (cp *CompatibleProvider) Finalize() { 409 if cp.tmCheck != nil { 410 cp.tmCheck.Stop() 411 cp.tmCheck = nil 412 } 413 cp.healthCheck.close() 414 cp.providers = nil 415 cp.Forget() 416 } 417 418 func (cp *CompatibleProvider) healthCheckWait() { 419 if cp.tmCheck != nil || !cp.healthCheck.auto() { 420 return 421 } 422 cp.tmCheck = time.AfterFunc(30*time.Second, func() { 423 if cp.healthCheck.auto() { 424 cp.healthCheck.checkAll() 425 } 426 cp.tmCheck = nil 427 }) 428 } 429 430 func NewCompatibleProvider(name string, hc *HealthCheck, filterRegx *regexp.Regexp) (*CompatibleProvider, error) { 431 if hc.auto() { 432 go hc.process() 433 } 434 435 pd := &CompatibleProvider{ 436 name: name, 437 healthCheck: hc, 438 filterRegx: filterRegx, 439 } 440 441 hc.setProxyFn(func() []C.Proxy { 442 return pd.Proxies() 443 }) 444 445 return pd, nil 446 } 447 448 func proxiesOnUpdate(pd *ProxySetProvider) func([]C.Proxy) { 449 return func(elm []C.Proxy) { 450 pd.setProxies(elm) 451 } 452 } 453 454 func proxiesParseAndFilter(filterReg *regexp.Regexp, option adapter.ProxyOption) parser[[]C.Proxy] { 455 return func(buf []byte) ([]C.Proxy, error) { 456 schema := &ProxySchema{} 457 458 if err := yaml.Unmarshal(buf, schema); err != nil { 459 proxies, err1 := convert.ConvertsV2Ray(buf) 460 if err1 != nil { 461 proxies, err1 = convert.ConvertsWireGuard(buf) 462 } 463 if err1 != nil { 464 return nil, errors.New("parse proxy provider failure, invalid data format") 465 } 466 schema.Proxies = lo.Map(proxies, func(m map[string]any, _ int) C.RawProxy { 467 return C.RawProxy{M: m} 468 }) 469 } 470 471 if len(schema.Proxies) == 0 { 472 return nil, errors.New("file must have a `proxies` field") 473 } 474 475 proxies := make([]C.Proxy, 0) 476 for idx, ps := range schema.Proxies { 477 ps.Init() 478 mapping := ps.M 479 name, ok := mapping["name"].(string) 480 if ok && filterReg != nil { 481 matched, err := filterReg.MatchString(name) 482 if err != nil { 483 return nil, fmt.Errorf("match filter regex failed: %w", err) 484 } 485 if !matched { 486 continue 487 } 488 } 489 490 if option.PrefixName != "" { 491 mapping["name"] = option.PrefixName + name 492 } 493 494 proxy, err := adapter.ParseProxy(mapping, option) 495 if err != nil { 496 return nil, fmt.Errorf("proxy %s[index: %d] error: %w", name, idx, err) 497 } 498 proxies = append(proxies, proxy) 499 } 500 501 if len(proxies) == 0 { 502 if filterReg != nil { 503 return nil, errors.New("doesn't match any proxy, please check your filter") 504 } 505 return nil, errors.New("file doesn't have any proxy") 506 } 507 508 return proxies, nil 509 } 510 }