box.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. package box
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "os"
  7. "runtime/debug"
  8. "time"
  9. "github.com/sagernet/sing-box/adapter"
  10. "github.com/sagernet/sing-box/adapter/inbound"
  11. "github.com/sagernet/sing-box/adapter/outbound"
  12. "github.com/sagernet/sing-box/common/dialer"
  13. "github.com/sagernet/sing-box/common/taskmonitor"
  14. C "github.com/sagernet/sing-box/constant"
  15. "github.com/sagernet/sing-box/experimental"
  16. "github.com/sagernet/sing-box/experimental/cachefile"
  17. "github.com/sagernet/sing-box/experimental/libbox/platform"
  18. "github.com/sagernet/sing-box/log"
  19. "github.com/sagernet/sing-box/option"
  20. "github.com/sagernet/sing-box/protocol/direct"
  21. "github.com/sagernet/sing-box/route"
  22. "github.com/sagernet/sing/common"
  23. E "github.com/sagernet/sing/common/exceptions"
  24. F "github.com/sagernet/sing/common/format"
  25. "github.com/sagernet/sing/common/ntp"
  26. "github.com/sagernet/sing/service"
  27. "github.com/sagernet/sing/service/pause"
  28. )
  29. var _ adapter.Service = (*Box)(nil)
  30. type Box struct {
  31. createdAt time.Time
  32. logFactory log.Factory
  33. logger log.ContextLogger
  34. network *route.NetworkManager
  35. inbound *inbound.Manager
  36. outbound *outbound.Manager
  37. connection *route.ConnectionManager
  38. router *route.Router
  39. services []adapter.LifecycleService
  40. done chan struct{}
  41. }
  42. type Options struct {
  43. option.Options
  44. Context context.Context
  45. PlatformLogWriter log.PlatformWriter
  46. }
  47. func Context(
  48. ctx context.Context,
  49. inboundRegistry adapter.InboundRegistry,
  50. outboundRegistry adapter.OutboundRegistry,
  51. ) context.Context {
  52. if service.FromContext[option.InboundOptionsRegistry](ctx) == nil ||
  53. service.FromContext[adapter.InboundRegistry](ctx) == nil {
  54. ctx = service.ContextWith[option.InboundOptionsRegistry](ctx, inboundRegistry)
  55. ctx = service.ContextWith[adapter.InboundRegistry](ctx, inboundRegistry)
  56. }
  57. if service.FromContext[option.OutboundOptionsRegistry](ctx) == nil ||
  58. service.FromContext[adapter.OutboundRegistry](ctx) == nil {
  59. ctx = service.ContextWith[option.OutboundOptionsRegistry](ctx, outboundRegistry)
  60. ctx = service.ContextWith[adapter.OutboundRegistry](ctx, outboundRegistry)
  61. }
  62. return ctx
  63. }
  64. func New(options Options) (*Box, error) {
  65. createdAt := time.Now()
  66. ctx := options.Context
  67. if ctx == nil {
  68. ctx = context.Background()
  69. }
  70. ctx = service.ContextWithDefaultRegistry(ctx)
  71. inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx)
  72. if inboundRegistry == nil {
  73. return nil, E.New("missing inbound registry in context")
  74. }
  75. outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx)
  76. if outboundRegistry == nil {
  77. return nil, E.New("missing outbound registry in context")
  78. }
  79. ctx = pause.WithDefaultManager(ctx)
  80. experimentalOptions := common.PtrValueOrDefault(options.Experimental)
  81. applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
  82. var needCacheFile bool
  83. var needClashAPI bool
  84. var needV2RayAPI bool
  85. if experimentalOptions.CacheFile != nil && experimentalOptions.CacheFile.Enabled || options.PlatformLogWriter != nil {
  86. needCacheFile = true
  87. }
  88. if experimentalOptions.ClashAPI != nil || options.PlatformLogWriter != nil {
  89. needClashAPI = true
  90. }
  91. if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
  92. needV2RayAPI = true
  93. }
  94. platformInterface := service.FromContext[platform.Interface](ctx)
  95. var defaultLogWriter io.Writer
  96. if platformInterface != nil {
  97. defaultLogWriter = io.Discard
  98. }
  99. logFactory, err := log.New(log.Options{
  100. Context: ctx,
  101. Options: common.PtrValueOrDefault(options.Log),
  102. Observable: needClashAPI,
  103. DefaultWriter: defaultLogWriter,
  104. BaseTime: createdAt,
  105. PlatformWriter: options.PlatformLogWriter,
  106. })
  107. if err != nil {
  108. return nil, E.Cause(err, "create log factory")
  109. }
  110. routeOptions := common.PtrValueOrDefault(options.Route)
  111. inboundManager := inbound.NewManager(logFactory.NewLogger("inbound"), inboundRegistry)
  112. outboundManager := outbound.NewManager(logFactory.NewLogger("outbound"), outboundRegistry, routeOptions.Final)
  113. service.MustRegister[adapter.InboundManager](ctx, inboundManager)
  114. service.MustRegister[adapter.OutboundManager](ctx, outboundManager)
  115. networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions)
  116. if err != nil {
  117. return nil, E.Cause(err, "initialize network manager")
  118. }
  119. service.MustRegister[adapter.NetworkManager](ctx, networkManager)
  120. connectionManager := route.NewConnectionManager(logFactory.NewLogger("connection"))
  121. service.MustRegister[adapter.ConnectionManager](ctx, connectionManager)
  122. router, err := route.NewRouter(ctx, logFactory, routeOptions, common.PtrValueOrDefault(options.DNS))
  123. if err != nil {
  124. return nil, E.Cause(err, "initialize router")
  125. }
  126. for i, inboundOptions := range options.Inbounds {
  127. var tag string
  128. if inboundOptions.Tag != "" {
  129. tag = inboundOptions.Tag
  130. } else {
  131. tag = F.ToString(i)
  132. }
  133. err = inboundManager.Create(ctx,
  134. router,
  135. logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
  136. tag,
  137. inboundOptions.Type,
  138. inboundOptions.Options,
  139. )
  140. if err != nil {
  141. return nil, E.Cause(err, "initialize inbound[", i, "]")
  142. }
  143. }
  144. for i, outboundOptions := range options.Outbounds {
  145. var tag string
  146. if outboundOptions.Tag != "" {
  147. tag = outboundOptions.Tag
  148. } else {
  149. tag = F.ToString(i)
  150. }
  151. outboundCtx := ctx
  152. if tag != "" {
  153. // TODO: remove this
  154. outboundCtx = adapter.WithContext(outboundCtx, &adapter.InboundContext{
  155. Outbound: tag,
  156. })
  157. }
  158. err = outboundManager.Create(
  159. outboundCtx,
  160. router,
  161. logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")),
  162. tag,
  163. outboundOptions.Type,
  164. outboundOptions.Options,
  165. )
  166. if err != nil {
  167. return nil, E.Cause(err, "initialize outbound[", i, "]")
  168. }
  169. }
  170. outboundManager.Initialize(common.Must1(
  171. direct.NewOutbound(
  172. ctx,
  173. router,
  174. logFactory.NewLogger("outbound/direct"),
  175. "direct",
  176. option.DirectOutboundOptions{},
  177. ),
  178. ))
  179. if platformInterface != nil {
  180. err = platformInterface.Initialize(networkManager)
  181. if err != nil {
  182. return nil, E.Cause(err, "initialize platform interface")
  183. }
  184. }
  185. var services []adapter.LifecycleService
  186. if needCacheFile {
  187. cacheFile := cachefile.New(ctx, common.PtrValueOrDefault(experimentalOptions.CacheFile))
  188. service.MustRegister[adapter.CacheFile](ctx, cacheFile)
  189. services = append(services, cacheFile)
  190. }
  191. if needClashAPI {
  192. clashAPIOptions := common.PtrValueOrDefault(experimentalOptions.ClashAPI)
  193. clashAPIOptions.ModeList = experimental.CalculateClashModeList(options.Options)
  194. clashServer, err := experimental.NewClashServer(ctx, logFactory.(log.ObservableFactory), clashAPIOptions)
  195. if err != nil {
  196. return nil, E.Cause(err, "create clash-server")
  197. }
  198. router.SetTracker(clashServer)
  199. service.MustRegister[adapter.ClashServer](ctx, clashServer)
  200. services = append(services, clashServer)
  201. }
  202. if needV2RayAPI {
  203. v2rayServer, err := experimental.NewV2RayServer(logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(experimentalOptions.V2RayAPI))
  204. if err != nil {
  205. return nil, E.Cause(err, "create v2ray-server")
  206. }
  207. if v2rayServer.StatsService() != nil {
  208. router.SetTracker(v2rayServer.StatsService())
  209. services = append(services, v2rayServer)
  210. service.MustRegister[adapter.V2RayServer](ctx, v2rayServer)
  211. }
  212. }
  213. ntpOptions := common.PtrValueOrDefault(options.NTP)
  214. if ntpOptions.Enabled {
  215. ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions)
  216. if err != nil {
  217. return nil, E.Cause(err, "create NTP service")
  218. }
  219. timeService := ntp.NewService(ntp.Options{
  220. Context: ctx,
  221. Dialer: ntpDialer,
  222. Logger: logFactory.NewLogger("ntp"),
  223. Server: ntpOptions.ServerOptions.Build(),
  224. Interval: time.Duration(ntpOptions.Interval),
  225. WriteToSystem: ntpOptions.WriteToSystem,
  226. })
  227. service.MustRegister[ntp.TimeService](ctx, timeService)
  228. services = append(services, adapter.NewLifecycleService(timeService, "ntp service"))
  229. }
  230. return &Box{
  231. network: networkManager,
  232. inbound: inboundManager,
  233. outbound: outboundManager,
  234. connection: connectionManager,
  235. router: router,
  236. createdAt: createdAt,
  237. logFactory: logFactory,
  238. logger: logFactory.Logger(),
  239. services: services,
  240. done: make(chan struct{}),
  241. }, nil
  242. }
  243. func (s *Box) PreStart() error {
  244. err := s.preStart()
  245. if err != nil {
  246. // TODO: remove catch error
  247. defer func() {
  248. v := recover()
  249. if v != nil {
  250. println(err.Error())
  251. debug.PrintStack()
  252. panic("panic on early close: " + fmt.Sprint(v))
  253. }
  254. }()
  255. s.Close()
  256. return err
  257. }
  258. s.logger.Info("sing-box pre-started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
  259. return nil
  260. }
  261. func (s *Box) Start() error {
  262. err := s.start()
  263. if err != nil {
  264. // TODO: remove catch error
  265. defer func() {
  266. v := recover()
  267. if v != nil {
  268. println(err.Error())
  269. debug.PrintStack()
  270. println("panic on early start: " + fmt.Sprint(v))
  271. }
  272. }()
  273. s.Close()
  274. return err
  275. }
  276. s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
  277. return nil
  278. }
  279. func (s *Box) preStart() error {
  280. monitor := taskmonitor.New(s.logger, C.StartTimeout)
  281. monitor.Start("start logger")
  282. err := s.logFactory.Start()
  283. monitor.Finish()
  284. if err != nil {
  285. return E.Cause(err, "start logger")
  286. }
  287. err = adapter.StartNamed(adapter.StartStateInitialize, s.services) // cache-file clash-api v2ray-api
  288. if err != nil {
  289. return err
  290. }
  291. err = adapter.Start(adapter.StartStateInitialize, s.network, s.connection, s.router, s.outbound, s.inbound)
  292. if err != nil {
  293. return err
  294. }
  295. err = adapter.Start(adapter.StartStateStart, s.outbound, s.network, s.connection, s.router)
  296. if err != nil {
  297. return err
  298. }
  299. return nil
  300. }
  301. func (s *Box) start() error {
  302. err := s.preStart()
  303. if err != nil {
  304. return err
  305. }
  306. err = adapter.StartNamed(adapter.StartStateStart, s.services)
  307. if err != nil {
  308. return err
  309. }
  310. err = s.inbound.Start(adapter.StartStateStart)
  311. if err != nil {
  312. return err
  313. }
  314. err = adapter.Start(adapter.StartStatePostStart, s.outbound, s.network, s.connection, s.router, s.inbound)
  315. if err != nil {
  316. return err
  317. }
  318. err = adapter.StartNamed(adapter.StartStatePostStart, s.services)
  319. if err != nil {
  320. return err
  321. }
  322. err = adapter.Start(adapter.StartStateStarted, s.network, s.connection, s.router, s.outbound, s.inbound)
  323. if err != nil {
  324. return err
  325. }
  326. err = adapter.StartNamed(adapter.StartStateStarted, s.services)
  327. if err != nil {
  328. return err
  329. }
  330. return nil
  331. }
  332. func (s *Box) Close() error {
  333. select {
  334. case <-s.done:
  335. return os.ErrClosed
  336. default:
  337. close(s.done)
  338. }
  339. err := common.Close(
  340. s.inbound, s.outbound, s.router, s.connection, s.network,
  341. )
  342. for _, lifecycleService := range s.services {
  343. err = E.Append(err, lifecycleService.Close(), func(err error) error {
  344. return E.Cause(err, "close ", lifecycleService.Name())
  345. })
  346. }
  347. err = E.Append(err, s.logFactory.Close(), func(err error) error {
  348. return E.Cause(err, "close logger")
  349. })
  350. return err
  351. }
  352. func (s *Box) Network() adapter.NetworkManager {
  353. return s.network
  354. }
  355. func (s *Box) Router() adapter.Router {
  356. return s.router
  357. }
  358. func (s *Box) Inbound() adapter.InboundManager {
  359. return s.inbound
  360. }
  361. func (s *Box) Outbound() adapter.OutboundManager {
  362. return s.outbound
  363. }