box.go 12 KB

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