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