instance.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package daemon
  2. import (
  3. "bytes"
  4. "context"
  5. "github.com/sagernet/sing-box"
  6. "github.com/sagernet/sing-box/adapter"
  7. "github.com/sagernet/sing-box/common/urltest"
  8. C "github.com/sagernet/sing-box/constant"
  9. "github.com/sagernet/sing-box/dns"
  10. "github.com/sagernet/sing-box/experimental/deprecated"
  11. "github.com/sagernet/sing-box/include"
  12. "github.com/sagernet/sing-box/option"
  13. E "github.com/sagernet/sing/common/exceptions"
  14. "github.com/sagernet/sing/common/json"
  15. "github.com/sagernet/sing/service"
  16. "github.com/sagernet/sing/service/filemanager"
  17. "github.com/sagernet/sing/service/pause"
  18. )
  19. type Instance struct {
  20. ctx context.Context
  21. cancel context.CancelFunc
  22. instance *box.Box
  23. clashServer adapter.ClashServer
  24. cacheFile adapter.CacheFile
  25. pauseManager pause.Manager
  26. urlTestHistoryStorage *urltest.HistoryStorage
  27. }
  28. func (s *StartedService) baseContext() context.Context {
  29. dnsRegistry := include.DNSTransportRegistry()
  30. if s.platform != nil && s.platform.UsePlatformLocalDNSTransport() {
  31. dns.RegisterTransport[option.LocalDNSServerOptions](dnsRegistry, C.DNSTypeLocal, s.platform.LocalDNSTransport())
  32. }
  33. ctx := box.Context(s.ctx, include.InboundRegistry(), include.OutboundRegistry(), include.EndpointRegistry(), dnsRegistry, include.ServiceRegistry())
  34. ctx = filemanager.WithDefault(ctx, s.workingDirectory, s.tempDirectory, s.userID, s.groupID)
  35. return ctx
  36. }
  37. func (s *StartedService) CheckConfig(configContent string) error {
  38. ctx := s.baseContext()
  39. options, err := parseConfig(ctx, configContent)
  40. if err != nil {
  41. return err
  42. }
  43. ctx, cancel := context.WithCancel(ctx)
  44. defer cancel()
  45. instance, err := box.New(box.Options{
  46. Context: ctx,
  47. Options: options,
  48. })
  49. if err == nil {
  50. instance.Close()
  51. }
  52. return err
  53. }
  54. func (s *StartedService) FormatConfig(configContent string) (string, error) {
  55. options, err := parseConfig(s.baseContext(), configContent)
  56. if err != nil {
  57. return "", err
  58. }
  59. var buffer bytes.Buffer
  60. encoder := json.NewEncoder(&buffer)
  61. encoder.SetIndent("", " ")
  62. err = encoder.Encode(options)
  63. if err != nil {
  64. return "", err
  65. }
  66. return buffer.String(), nil
  67. }
  68. type OverrideOptions struct {
  69. AutoRedirect bool
  70. IncludePackage []string
  71. ExcludePackage []string
  72. }
  73. func (s *StartedService) newInstance(profileContent string, overrideOptions *OverrideOptions) (*Instance, error) {
  74. ctx := s.baseContext()
  75. service.MustRegister[deprecated.Manager](ctx, new(deprecatedManager))
  76. ctx, cancel := context.WithCancel(include.Context(ctx))
  77. options, err := parseConfig(ctx, profileContent)
  78. if err != nil {
  79. cancel()
  80. return nil, err
  81. }
  82. if overrideOptions != nil {
  83. for _, inbound := range options.Inbounds {
  84. if tunInboundOptions, isTUN := inbound.Options.(*option.TunInboundOptions); isTUN {
  85. tunInboundOptions.AutoRedirect = overrideOptions.AutoRedirect
  86. tunInboundOptions.IncludePackage = append(tunInboundOptions.IncludePackage, overrideOptions.IncludePackage...)
  87. tunInboundOptions.ExcludePackage = append(tunInboundOptions.ExcludePackage, overrideOptions.ExcludePackage...)
  88. break
  89. }
  90. }
  91. }
  92. urlTestHistoryStorage := urltest.NewHistoryStorage()
  93. ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
  94. i := &Instance{
  95. ctx: ctx,
  96. cancel: cancel,
  97. urlTestHistoryStorage: urlTestHistoryStorage,
  98. }
  99. boxInstance, err := box.New(box.Options{
  100. Context: ctx,
  101. Options: options,
  102. PlatformLogWriter: s,
  103. })
  104. if err != nil {
  105. cancel()
  106. return nil, err
  107. }
  108. i.instance = boxInstance
  109. i.clashServer = service.FromContext[adapter.ClashServer](ctx)
  110. i.pauseManager = service.FromContext[pause.Manager](ctx)
  111. i.cacheFile = service.FromContext[adapter.CacheFile](ctx)
  112. return i, nil
  113. }
  114. func (i *Instance) Start() error {
  115. return i.instance.Start()
  116. }
  117. func (i *Instance) Close() error {
  118. i.cancel()
  119. i.urlTestHistoryStorage.Close()
  120. return i.instance.Close()
  121. }
  122. func (i *Instance) Box() *box.Box {
  123. return i.instance
  124. }
  125. func (i *Instance) PauseManager() pause.Manager {
  126. return i.pauseManager
  127. }
  128. func parseConfig(ctx context.Context, configContent string) (option.Options, error) {
  129. options, err := json.UnmarshalExtendedContext[option.Options](ctx, []byte(configContent))
  130. if err != nil {
  131. return option.Options{}, E.Cause(err, "decode config")
  132. }
  133. return options, nil
  134. }