box.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package box
  2. import (
  3. "context"
  4. "io"
  5. "os"
  6. "time"
  7. "github.com/sagernet/sing-box/adapter"
  8. "github.com/sagernet/sing-box/inbound"
  9. "github.com/sagernet/sing-box/log"
  10. "github.com/sagernet/sing-box/option"
  11. "github.com/sagernet/sing-box/outbound"
  12. "github.com/sagernet/sing-box/route"
  13. "github.com/sagernet/sing/common"
  14. E "github.com/sagernet/sing/common/exceptions"
  15. F "github.com/sagernet/sing/common/format"
  16. )
  17. var _ adapter.Service = (*Box)(nil)
  18. type Box struct {
  19. createdAt time.Time
  20. router adapter.Router
  21. inbounds []adapter.Inbound
  22. outbounds []adapter.Outbound
  23. logFactory log.Factory
  24. logger log.ContextLogger
  25. logFile *os.File
  26. }
  27. func New(ctx context.Context, options option.Options) (*Box, error) {
  28. createdAt := time.Now()
  29. logOptions := common.PtrValueOrDefault(options.Log)
  30. var logFactory log.Factory
  31. var logFile *os.File
  32. if logOptions.Disabled {
  33. logFactory = log.NewNOPFactory()
  34. } else {
  35. var logWriter io.Writer
  36. switch logOptions.Output {
  37. case "", "stderr":
  38. logWriter = os.Stderr
  39. case "stdout":
  40. logWriter = os.Stdout
  41. default:
  42. var err error
  43. logFile, err = os.OpenFile(logOptions.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
  44. if err != nil {
  45. return nil, err
  46. }
  47. }
  48. logFormatter := log.Formatter{
  49. BaseTime: createdAt,
  50. DisableColors: logOptions.DisableColor || logFile != nil,
  51. DisableTimestamp: !logOptions.Timestamp && logFile != nil,
  52. FullTimestamp: logOptions.Timestamp,
  53. TimestampFormat: "-0700 2006-01-02 15:04:05",
  54. }
  55. logFactory = log.NewFactory(logFormatter, logWriter)
  56. if logOptions.Level != "" {
  57. logLevel, err := log.ParseLevel(logOptions.Level)
  58. if err != nil {
  59. return nil, E.Cause(err, "parse log level")
  60. }
  61. logFactory.SetLevel(logLevel)
  62. } else {
  63. logFactory.SetLevel(log.LevelTrace)
  64. }
  65. }
  66. router, err := route.NewRouter(
  67. ctx,
  68. logFactory.NewLogger("router"),
  69. logFactory.NewLogger("dns"),
  70. common.PtrValueOrDefault(options.Route),
  71. common.PtrValueOrDefault(options.DNS),
  72. )
  73. if err != nil {
  74. return nil, E.Cause(err, "parse route options")
  75. }
  76. inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
  77. outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
  78. for i, inboundOptions := range options.Inbounds {
  79. var in adapter.Inbound
  80. var tag string
  81. if inboundOptions.Tag != "" {
  82. tag = inboundOptions.Tag
  83. } else {
  84. tag = F.ToString(i)
  85. }
  86. in, err = inbound.New(
  87. ctx,
  88. router,
  89. logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
  90. inboundOptions,
  91. )
  92. if err != nil {
  93. return nil, E.Cause(err, "parse inbound[", i, "]")
  94. }
  95. inbounds = append(inbounds, in)
  96. }
  97. for i, outboundOptions := range options.Outbounds {
  98. var out adapter.Outbound
  99. var tag string
  100. if outboundOptions.Tag != "" {
  101. tag = outboundOptions.Tag
  102. } else {
  103. tag = F.ToString(i)
  104. }
  105. out, err = outbound.New(
  106. router,
  107. logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")),
  108. outboundOptions)
  109. if err != nil {
  110. return nil, E.Cause(err, "parse outbound[", i, "]")
  111. }
  112. outbounds = append(outbounds, out)
  113. }
  114. err = router.Initialize(outbounds, func() adapter.Outbound {
  115. out, oErr := outbound.New(router, logFactory.NewLogger("outbound/direct"), option.Outbound{Type: "direct", Tag: "default"})
  116. common.Must(oErr)
  117. outbounds = append(outbounds, out)
  118. return out
  119. })
  120. if err != nil {
  121. return nil, err
  122. }
  123. return &Box{
  124. router: router,
  125. inbounds: inbounds,
  126. outbounds: outbounds,
  127. createdAt: createdAt,
  128. logFactory: logFactory,
  129. logger: logFactory.NewLogger(""),
  130. logFile: logFile,
  131. }, nil
  132. }
  133. func (s *Box) Start() error {
  134. err := s.router.Start()
  135. if err != nil {
  136. return err
  137. }
  138. for _, in := range s.inbounds {
  139. err = in.Start()
  140. if err != nil {
  141. return err
  142. }
  143. }
  144. s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
  145. return nil
  146. }
  147. func (s *Box) Close() error {
  148. for _, in := range s.inbounds {
  149. in.Close()
  150. }
  151. for _, out := range s.outbounds {
  152. common.Close(out)
  153. }
  154. return common.Close(
  155. s.router,
  156. common.PtrOrNil(s.logFile),
  157. )
  158. }