2022-06-30 21:27:56 +08:00
package box
import (
"context"
2022-08-24 19:03:00 +08:00
"fmt"
2022-07-12 15:17:29 +08:00
"io"
"os"
2022-08-24 19:03:00 +08:00
"runtime/debug"
2022-07-04 19:34:45 +08:00
"time"
2022-06-30 21:27:56 +08:00
"github.com/sagernet/sing-box/adapter"
2026-03-23 20:04:36 +08:00
boxCertificate "github.com/sagernet/sing-box/adapter/certificate"
2024-11-21 18:10:41 +08:00
"github.com/sagernet/sing-box/adapter/endpoint"
2024-11-09 21:16:11 +08:00
"github.com/sagernet/sing-box/adapter/inbound"
"github.com/sagernet/sing-box/adapter/outbound"
2025-03-29 17:24:34 +08:00
boxService "github.com/sagernet/sing-box/adapter/service"
2025-01-08 10:34:45 +08:00
"github.com/sagernet/sing-box/common/certificate"
2024-11-10 16:46:59 +08:00
"github.com/sagernet/sing-box/common/dialer"
2026-04-14 22:59:46 +08:00
"github.com/sagernet/sing-box/common/httpclient"
2023-12-04 11:47:25 +08:00
"github.com/sagernet/sing-box/common/taskmonitor"
2024-12-14 22:41:58 +08:00
"github.com/sagernet/sing-box/common/tls"
2023-12-04 11:47:25 +08:00
C "github.com/sagernet/sing-box/constant"
2025-03-16 14:50:44 +08:00
"github.com/sagernet/sing-box/dns"
2022-07-20 07:12:40 +08:00
"github.com/sagernet/sing-box/experimental"
2023-11-28 12:00:28 +08:00
"github.com/sagernet/sing-box/experimental/cachefile"
2026-04-14 22:59:46 +08:00
"github.com/sagernet/sing-box/experimental/deprecated"
2022-06-30 21:27:56 +08:00
"github.com/sagernet/sing-box/log"
2022-07-02 14:07:50 +08:00
"github.com/sagernet/sing-box/option"
2024-11-02 00:39:02 +08:00
"github.com/sagernet/sing-box/protocol/direct"
2022-07-03 20:59:25 +08:00
"github.com/sagernet/sing-box/route"
2022-07-08 23:03:57 +08:00
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
2024-11-10 16:46:59 +08:00
"github.com/sagernet/sing/common/ntp"
2023-08-29 13:43:42 +08:00
"github.com/sagernet/sing/service"
2023-08-07 17:46:51 +08:00
"github.com/sagernet/sing/service/pause"
2022-06-30 21:27:56 +08:00
)
2025-03-29 17:24:34 +08:00
var _ adapter . SimpleLifecycle = ( * Box )( nil )
2022-06-30 21:27:56 +08:00
2022-07-07 21:47:21 +08:00
type Box struct {
2026-03-23 20:04:36 +08:00
createdAt time . Time
logFactory log . Factory
logger log . ContextLogger
network * route . NetworkManager
endpoint * endpoint . Manager
inbound * inbound . Manager
outbound * outbound . Manager
service * boxService . Manager
certificateProvider * boxCertificate . Manager
dnsTransport * dns . TransportManager
dnsRouter * dns . Router
connection * route . ConnectionManager
router * route . Router
2026-04-14 22:59:46 +08:00
httpClientService adapter . LifecycleService
2026-03-23 20:04:36 +08:00
internalService [] adapter . LifecycleService
done chan struct {}
2022-06-30 21:27:56 +08:00
}
2023-04-03 18:24:20 +08:00
type Options struct {
option . Options
Context context . Context
2023-11-15 13:05:33 +08:00
PlatformLogWriter log . PlatformWriter
2023-04-03 18:24:20 +08:00
}
2023-03-17 14:51:09 +08:00
2024-11-10 16:46:59 +08:00
func Context (
ctx context . Context ,
inboundRegistry adapter . InboundRegistry ,
outboundRegistry adapter . OutboundRegistry ,
2024-11-21 18:10:41 +08:00
endpointRegistry adapter . EndpointRegistry ,
2025-03-16 14:50:44 +08:00
dnsTransportRegistry adapter . DNSTransportRegistry ,
2025-03-29 17:24:34 +08:00
serviceRegistry adapter . ServiceRegistry ,
2026-03-23 20:04:36 +08:00
certificateProviderRegistry adapter . CertificateProviderRegistry ,
2024-11-10 16:46:59 +08:00
) context . Context {
2024-11-02 00:39:02 +08:00
if service . FromContext [ option . InboundOptionsRegistry ]( ctx ) == nil ||
service . FromContext [ adapter . InboundRegistry ]( ctx ) == nil {
ctx = service . ContextWith [ option . InboundOptionsRegistry ]( ctx , inboundRegistry )
ctx = service . ContextWith [ adapter . InboundRegistry ]( ctx , inboundRegistry )
}
if service . FromContext [ option . OutboundOptionsRegistry ]( ctx ) == nil ||
service . FromContext [ adapter . OutboundRegistry ]( ctx ) == nil {
ctx = service . ContextWith [ option . OutboundOptionsRegistry ]( ctx , outboundRegistry )
ctx = service . ContextWith [ adapter . OutboundRegistry ]( ctx , outboundRegistry )
}
2024-11-21 18:10:41 +08:00
if service . FromContext [ option . EndpointOptionsRegistry ]( ctx ) == nil ||
service . FromContext [ adapter . EndpointRegistry ]( ctx ) == nil {
ctx = service . ContextWith [ option . EndpointOptionsRegistry ]( ctx , endpointRegistry )
ctx = service . ContextWith [ adapter . EndpointRegistry ]( ctx , endpointRegistry )
}
2025-03-16 14:50:44 +08:00
if service . FromContext [ adapter . DNSTransportRegistry ]( ctx ) == nil {
ctx = service . ContextWith [ option . DNSTransportOptionsRegistry ]( ctx , dnsTransportRegistry )
ctx = service . ContextWith [ adapter . DNSTransportRegistry ]( ctx , dnsTransportRegistry )
}
2025-03-29 17:24:34 +08:00
if service . FromContext [ adapter . ServiceRegistry ]( ctx ) == nil {
ctx = service . ContextWith [ option . ServiceOptionsRegistry ]( ctx , serviceRegistry )
ctx = service . ContextWith [ adapter . ServiceRegistry ]( ctx , serviceRegistry )
}
2026-03-23 20:04:36 +08:00
if service . FromContext [ adapter . CertificateProviderRegistry ]( ctx ) == nil {
ctx = service . ContextWith [ option . CertificateProviderOptionsRegistry ]( ctx , certificateProviderRegistry )
ctx = service . ContextWith [ adapter . CertificateProviderRegistry ]( ctx , certificateProviderRegistry )
}
2024-11-02 00:39:02 +08:00
return ctx
}
2023-04-03 18:24:20 +08:00
func New ( options Options ) ( * Box , error ) {
2023-11-28 12:00:28 +08:00
createdAt := time . Now ()
2023-04-03 18:24:20 +08:00
ctx := options . Context
if ctx == nil {
ctx = context . Background ()
}
2024-11-09 21:16:11 +08:00
ctx = service . ContextWithDefaultRegistry ( ctx )
2024-11-10 16:46:59 +08:00
2024-11-21 18:10:41 +08:00
endpointRegistry := service . FromContext [ adapter . EndpointRegistry ]( ctx )
2024-11-02 00:39:02 +08:00
inboundRegistry := service . FromContext [ adapter . InboundRegistry ]( ctx )
2024-11-21 18:10:41 +08:00
outboundRegistry := service . FromContext [ adapter . OutboundRegistry ]( ctx )
2025-03-16 14:50:44 +08:00
dnsTransportRegistry := service . FromContext [ adapter . DNSTransportRegistry ]( ctx )
2025-03-29 17:24:34 +08:00
serviceRegistry := service . FromContext [ adapter . ServiceRegistry ]( ctx )
2026-03-23 20:04:36 +08:00
certificateProviderRegistry := service . FromContext [ adapter . CertificateProviderRegistry ]( ctx )
2024-11-21 18:10:41 +08:00
if endpointRegistry == nil {
return nil , E . New ( "missing endpoint registry in context" )
}
2024-11-02 00:39:02 +08:00
if inboundRegistry == nil {
return nil , E . New ( "missing inbound registry in context" )
}
if outboundRegistry == nil {
return nil , E . New ( "missing outbound registry in context" )
}
2025-03-29 17:24:34 +08:00
if dnsTransportRegistry == nil {
return nil , E . New ( "missing DNS transport registry in context" )
}
if serviceRegistry == nil {
return nil , E . New ( "missing service registry in context" )
}
2026-03-23 20:04:36 +08:00
if certificateProviderRegistry == nil {
return nil , E . New ( "missing certificate provider registry in context" )
}
2024-11-10 16:46:59 +08:00
2023-12-16 15:40:14 +08:00
ctx = pause . WithDefaultManager ( ctx )
2023-03-17 14:51:09 +08:00
experimentalOptions := common . PtrValueOrDefault ( options . Experimental )
2026-02-26 12:54:00 +08:00
err := applyDebugOptions ( common . PtrValueOrDefault ( experimentalOptions . Debug ))
if err != nil {
return nil , err
}
2023-11-28 12:00:28 +08:00
var needCacheFile bool
2022-07-19 22:16:49 +08:00
var needClashAPI bool
2022-09-26 19:37:06 +08:00
var needV2RayAPI bool
2023-11-28 12:00:28 +08:00
if experimentalOptions . CacheFile != nil && experimentalOptions . CacheFile . Enabled || options . PlatformLogWriter != nil {
needCacheFile = true
}
2023-11-20 23:41:10 +08:00
if experimentalOptions . ClashAPI != nil || options . PlatformLogWriter != nil {
2023-03-17 14:51:09 +08:00
needClashAPI = true
}
if experimentalOptions . V2RayAPI != nil && experimentalOptions . V2RayAPI . Listen != "" {
needV2RayAPI = true
2022-07-19 22:16:49 +08:00
}
2025-10-07 15:40:11 +08:00
platformInterface := service . FromContext [ adapter . PlatformInterface ]( ctx )
2023-04-03 18:24:20 +08:00
var defaultLogWriter io . Writer
2024-11-02 00:39:02 +08:00
if platformInterface != nil {
2023-04-03 18:24:20 +08:00
defaultLogWriter = io . Discard
}
logFactory , err := log . New ( log . Options {
2023-04-21 17:29:00 +08:00
Context : ctx ,
2023-04-03 18:24:20 +08:00
Options : common . PtrValueOrDefault ( options . Log ),
Observable : needClashAPI ,
DefaultWriter : defaultLogWriter ,
BaseTime : createdAt ,
2023-11-15 13:05:33 +08:00
PlatformWriter : options . PlatformLogWriter ,
2023-04-03 18:24:20 +08:00
})
if err != nil {
return nil , E . Cause ( err , "create log factory" )
2022-06-30 21:27:56 +08:00
}
2024-11-10 16:46:59 +08:00
2025-03-29 17:24:34 +08:00
var internalServices [] adapter . LifecycleService
2026-04-14 22:59:46 +08:00
routeOptions := common . PtrValueOrDefault ( options . Route )
2025-01-08 10:34:45 +08:00
certificateOptions := common . PtrValueOrDefault ( options . Certificate )
2026-04-09 19:34:54 +08:00
if C . IsAndroid || certificateOptions . Store != "" && certificateOptions . Store != C . CertificateStoreSystem ||
len ( certificateOptions . Certificate ) > 0 ||
len ( certificateOptions . CertificatePath ) > 0 ||
len ( certificateOptions . CertificateDirectoryPath ) > 0 {
2025-01-08 10:34:45 +08:00
certificateStore , err := certificate . NewStore ( ctx , logFactory . NewLogger ( "certificate" ), certificateOptions )
if err != nil {
return nil , err
}
service . MustRegister [ adapter . CertificateStore ]( ctx , certificateStore )
2025-03-29 17:24:34 +08:00
internalServices = append ( internalServices , certificateStore )
2025-01-08 10:34:45 +08:00
}
2025-03-16 14:50:44 +08:00
dnsOptions := common . PtrValueOrDefault ( options . DNS )
2024-11-21 18:10:41 +08:00
endpointManager := endpoint . NewManager ( logFactory . NewLogger ( "endpoint" ), endpointRegistry )
inboundManager := inbound . NewManager ( logFactory . NewLogger ( "inbound" ), inboundRegistry , endpointManager )
outboundManager := outbound . NewManager ( logFactory . NewLogger ( "outbound" ), outboundRegistry , endpointManager , routeOptions . Final )
2025-03-16 14:50:44 +08:00
dnsTransportManager := dns . NewTransportManager ( logFactory . NewLogger ( "dns/transport" ), dnsTransportRegistry , outboundManager , dnsOptions . Final )
2025-03-29 17:24:34 +08:00
serviceManager := boxService . NewManager ( logFactory . NewLogger ( "service" ), serviceRegistry )
2026-03-23 20:04:36 +08:00
certificateProviderManager := boxCertificate . NewManager ( logFactory . NewLogger ( "certificate-provider" ), certificateProviderRegistry )
2024-11-21 18:10:41 +08:00
service . MustRegister [ adapter . EndpointManager ]( ctx , endpointManager )
2024-11-10 16:46:59 +08:00
service . MustRegister [ adapter . InboundManager ]( ctx , inboundManager )
service . MustRegister [ adapter . OutboundManager ]( ctx , outboundManager )
2025-03-16 14:50:44 +08:00
service . MustRegister [ adapter . DNSTransportManager ]( ctx , dnsTransportManager )
2025-03-29 17:24:34 +08:00
service . MustRegister [ adapter . ServiceManager ]( ctx , serviceManager )
2026-03-23 20:04:36 +08:00
service . MustRegister [ adapter . CertificateProviderManager ]( ctx , certificateProviderManager )
2026-04-11 12:10:52 +08:00
dnsRouter , err := dns . NewRouter ( ctx , logFactory , dnsOptions )
if err != nil {
return nil , E . Cause ( err , "initialize DNS router" )
}
2025-03-16 14:50:44 +08:00
service . MustRegister [ adapter . DNSRouter ]( ctx , dnsRouter )
2026-04-07 20:02:32 +08:00
service . MustRegister [ adapter . DNSRuleSetUpdateValidator ]( ctx , dnsRouter )
2025-12-07 11:05:43 +08:00
networkManager , err := route . NewNetworkManager ( ctx , logFactory . NewLogger ( "network" ), routeOptions , dnsOptions )
2022-07-02 14:07:50 +08:00
if err != nil {
2024-11-10 12:11:21 +08:00
return nil , E . Cause ( err , "initialize network manager" )
}
2024-11-10 16:46:59 +08:00
service . MustRegister [ adapter . NetworkManager ]( ctx , networkManager )
2024-11-20 11:32:02 +08:00
connectionManager := route . NewConnectionManager ( logFactory . NewLogger ( "connection" ))
service . MustRegister [ adapter . ConnectionManager ]( ctx , connectionManager )
2026-04-14 22:59:46 +08:00
// Must register after ConnectionManager: the Apple HTTP engine's proxy bridge reads it from the context when Manager.Start resolves the default client.
httpClientManager := httpclient . NewManager ( ctx , logFactory . NewLogger ( "httpclient" ), options . HTTPClients , routeOptions . DefaultHTTPClient )
service . MustRegister [ adapter . HTTPClientManager ]( ctx , httpClientManager )
httpClientService := adapter . LifecycleService ( httpClientManager )
2025-03-16 14:50:44 +08:00
router := route . NewRouter ( ctx , logFactory , routeOptions , dnsOptions )
service . MustRegister [ adapter . Router ]( ctx , router )
err = router . Initialize ( routeOptions . Rules , routeOptions . RuleSet )
2024-11-10 12:11:21 +08:00
if err != nil {
return nil , E . Cause ( err , "initialize router" )
2022-06-30 21:27:56 +08:00
}
2024-12-14 22:41:58 +08:00
ntpOptions := common . PtrValueOrDefault ( options . NTP )
var timeService * tls . TimeServiceWrapper
if ntpOptions . Enabled {
timeService = new ( tls . TimeServiceWrapper )
service . MustRegister [ ntp . TimeService ]( ctx , timeService )
}
2025-03-16 14:50:44 +08:00
for i , transportOptions := range dnsOptions . Servers {
var tag string
if transportOptions . Tag != "" {
tag = transportOptions . Tag
} else {
tag = F . ToString ( i )
}
err = dnsTransportManager . Create (
ctx ,
logFactory . NewLogger ( F . ToString ( "dns/" , transportOptions . Type , "[" , tag , "]" )),
tag ,
transportOptions . Type ,
transportOptions . Options ,
)
if err != nil {
2025-01-12 12:45:27 +08:00
return nil , E . Cause ( err , "initialize DNS server[" , i , "]" )
2025-03-16 14:50:44 +08:00
}
}
err = dnsRouter . Initialize ( dnsOptions . Rules )
if err != nil {
return nil , E . Cause ( err , "initialize dns router" )
}
2024-11-21 18:10:41 +08:00
for i , endpointOptions := range options . Endpoints {
var tag string
if endpointOptions . Tag != "" {
tag = endpointOptions . Tag
} else {
tag = F . ToString ( i )
}
2025-03-11 12:01:31 +08:00
endpointCtx := ctx
if tag != "" {
// TODO: remove this
endpointCtx = adapter . WithContext ( endpointCtx , & adapter . InboundContext {
Outbound : tag ,
})
}
err = endpointManager . Create (
endpointCtx ,
2024-11-21 18:10:41 +08:00
router ,
logFactory . NewLogger ( F . ToString ( "endpoint/" , endpointOptions . Type , "[" , tag , "]" )),
tag ,
endpointOptions . Type ,
endpointOptions . Options ,
)
if err != nil {
2025-01-12 12:45:27 +08:00
return nil , E . Cause ( err , "initialize endpoint[" , i , "]" )
2024-11-21 18:10:41 +08:00
}
}
2022-07-02 22:55:10 +08:00
for i , inboundOptions := range options . Inbounds {
2022-07-12 15:17:29 +08:00
var tag string
if inboundOptions . Tag != "" {
tag = inboundOptions . Tag
} else {
tag = F . ToString ( i )
}
2025-03-11 12:01:31 +08:00
err = inboundManager . Create (
ctx ,
2022-07-12 15:17:29 +08:00
router ,
logFactory . NewLogger ( F . ToString ( "inbound/" , inboundOptions . Type , "[" , tag , "]" )),
2024-06-11 21:16:33 +08:00
tag ,
2024-11-02 00:39:02 +08:00
inboundOptions . Type ,
inboundOptions . Options ,
2022-07-12 15:17:29 +08:00
)
2022-07-02 22:55:10 +08:00
if err != nil {
2024-11-09 21:16:11 +08:00
return nil , E . Cause ( err , "initialize inbound[" , i , "]" )
2022-06-30 21:27:56 +08:00
}
}
2026-03-23 20:04:36 +08:00
for i , serviceOptions := range options . Services {
var tag string
if serviceOptions . Tag != "" {
tag = serviceOptions . Tag
} else {
tag = F . ToString ( i )
}
err = serviceManager . Create (
ctx ,
logFactory . NewLogger ( F . ToString ( "service/" , serviceOptions . Type , "[" , tag , "]" )),
tag ,
serviceOptions . Type ,
serviceOptions . Options ,
)
if err != nil {
return nil , E . Cause ( err , "initialize service[" , i , "]" )
}
}
2022-06-30 21:27:56 +08:00
for i , outboundOptions := range options . Outbounds {
2022-07-12 15:17:29 +08:00
var tag string
if outboundOptions . Tag != "" {
tag = outboundOptions . Tag
} else {
tag = F . ToString ( i )
}
2024-11-02 00:39:02 +08:00
outboundCtx := ctx
if tag != "" {
// TODO: remove this
outboundCtx = adapter . WithContext ( outboundCtx , & adapter . InboundContext {
Outbound : tag ,
})
}
2024-11-09 21:16:11 +08:00
err = outboundManager . Create (
2024-11-02 00:39:02 +08:00
outboundCtx ,
2022-07-12 15:17:29 +08:00
router ,
logFactory . NewLogger ( F . ToString ( "outbound/" , outboundOptions . Type , "[" , tag , "]" )),
2023-04-08 08:58:01 +08:00
tag ,
2024-11-02 00:39:02 +08:00
outboundOptions . Type ,
outboundOptions . Options ,
)
2022-06-30 21:27:56 +08:00
if err != nil {
2024-11-09 21:16:11 +08:00
return nil , E . Cause ( err , "initialize outbound[" , i , "]" )
2022-06-30 21:27:56 +08:00
}
2022-07-02 14:07:50 +08:00
}
2026-03-23 20:04:36 +08:00
for i , certificateProviderOptions := range options . CertificateProviders {
2025-03-29 17:24:34 +08:00
var tag string
2026-03-23 20:04:36 +08:00
if certificateProviderOptions . Tag != "" {
tag = certificateProviderOptions . Tag
2025-03-29 17:24:34 +08:00
} else {
tag = F . ToString ( i )
}
2026-03-23 20:04:36 +08:00
err = certificateProviderManager . Create (
2025-03-29 17:24:34 +08:00
ctx ,
2026-03-23 20:04:36 +08:00
logFactory . NewLogger ( F . ToString ( "certificate-provider/" , certificateProviderOptions . Type , "[" , tag , "]" )),
2025-03-29 17:24:34 +08:00
tag ,
2026-03-23 20:04:36 +08:00
certificateProviderOptions . Type ,
certificateProviderOptions . Options ,
2025-03-29 17:24:34 +08:00
)
if err != nil {
2026-03-23 20:04:36 +08:00
return nil , E . Cause ( err , "initialize certificate provider[" , i , "]" )
2025-03-29 17:24:34 +08:00
}
}
2025-08-15 23:29:55 +08:00
outboundManager . Initialize ( func () ( adapter . Outbound , error ) {
return direct . NewOutbound (
2024-11-09 21:16:11 +08:00
ctx ,
router ,
logFactory . NewLogger ( "outbound/direct" ),
"direct" ,
option . DirectOutboundOptions {},
2025-08-15 23:29:55 +08:00
)
})
2025-08-18 14:19:49 +08:00
dnsTransportManager . Initialize ( func () ( adapter . DNSTransport , error ) {
2026-04-09 20:54:32 +08:00
return dnsTransportRegistry . CreateDNSTransport (
2025-03-16 14:50:44 +08:00
ctx ,
logFactory . NewLogger ( "dns/local" ),
"local" ,
2026-04-09 20:54:32 +08:00
C . DNSTypeLocal ,
& option . LocalDNSServerOptions {},
2025-08-18 14:19:49 +08:00
)
})
2026-04-14 22:59:46 +08:00
httpClientManager . Initialize ( func () ( * httpclient . ManagedTransport , error ) {
deprecated . Report ( ctx , deprecated . OptionImplicitDefaultHTTPClient )
var httpClientOptions option . HTTPClientOptions
httpClientOptions . DefaultOutbound = true
return httpclient . NewTransport ( ctx , logFactory . NewLogger ( "httpclient" ), "" , httpClientOptions )
})
2024-11-02 00:39:02 +08:00
if platformInterface != nil {
2024-11-10 12:11:21 +08:00
err = platformInterface . Initialize ( networkManager )
2023-04-18 14:04:09 +08:00
if err != nil {
return nil , E . Cause ( err , "initialize platform interface" )
}
}
2023-11-28 12:00:28 +08:00
if needCacheFile {
2026-04-11 12:10:52 +08:00
cacheFile := cachefile . New ( ctx , logFactory . NewLogger ( "cache-file" ), common . PtrValueOrDefault ( experimentalOptions . CacheFile ))
2024-11-10 16:46:59 +08:00
service . MustRegister [ adapter . CacheFile ]( ctx , cacheFile )
2025-03-29 17:24:34 +08:00
internalServices = append ( internalServices , cacheFile )
2023-11-28 12:00:28 +08:00
}
2022-07-19 22:16:49 +08:00
if needClashAPI {
2023-08-24 21:52:38 +08:00
clashAPIOptions := common . PtrValueOrDefault ( experimentalOptions . ClashAPI )
clashAPIOptions . ModeList = experimental . CalculateClashModeList ( options . Options )
2024-11-09 21:16:11 +08:00
clashServer , err := experimental . NewClashServer ( ctx , logFactory .( log . ObservableFactory ), clashAPIOptions )
2022-07-20 07:12:40 +08:00
if err != nil {
2024-11-10 16:46:59 +08:00
return nil , E . Cause ( err , "create clash-server" )
2022-07-20 07:12:40 +08:00
}
2025-04-03 20:01:08 +08:00
router . AppendTracker ( clashServer )
2024-11-10 16:46:59 +08:00
service . MustRegister [ adapter . ClashServer ]( ctx , clashServer )
2025-03-29 17:24:34 +08:00
internalServices = append ( internalServices , clashServer )
2022-07-19 22:16:49 +08:00
}
2022-09-26 19:37:06 +08:00
if needV2RayAPI {
2023-08-09 09:55:40 +08:00
v2rayServer , err := experimental . NewV2RayServer ( logFactory . NewLogger ( "v2ray-api" ), common . PtrValueOrDefault ( experimentalOptions . V2RayAPI ))
2022-09-26 19:37:06 +08:00
if err != nil {
2024-11-10 16:46:59 +08:00
return nil , E . Cause ( err , "create v2ray-server" )
}
if v2rayServer . StatsService () != nil {
2025-04-03 20:01:08 +08:00
router . AppendTracker ( v2rayServer . StatsService ())
2025-03-29 17:24:34 +08:00
internalServices = append ( internalServices , v2rayServer )
2024-11-10 16:46:59 +08:00
service . MustRegister [ adapter . V2RayServer ]( ctx , v2rayServer )
}
}
if ntpOptions . Enabled {
2025-01-12 12:45:27 +08:00
ntpDialer , err := dialer . New ( ctx , ntpOptions . DialerOptions , ntpOptions . ServerIsDomain ())
2024-11-10 16:46:59 +08:00
if err != nil {
return nil , E . Cause ( err , "create NTP service" )
2022-09-26 19:37:06 +08:00
}
2024-12-14 22:41:58 +08:00
ntpService := ntp . NewService ( ntp . Options {
2024-11-10 16:46:59 +08:00
Context : ctx ,
Dialer : ntpDialer ,
Logger : logFactory . NewLogger ( "ntp" ),
Server : ntpOptions . ServerOptions . Build (),
Interval : time . Duration ( ntpOptions . Interval ),
WriteToSystem : ntpOptions . WriteToSystem ,
})
2024-12-14 22:41:58 +08:00
timeService . TimeService = ntpService
2025-03-29 17:24:34 +08:00
internalServices = append ( internalServices , adapter . NewLifecycleService ( ntpService , "ntp service" ))
2022-09-26 19:37:06 +08:00
}
2022-07-07 21:47:21 +08:00
return & Box {
2026-03-23 20:04:36 +08:00
network : networkManager ,
endpoint : endpointManager ,
inbound : inboundManager ,
outbound : outboundManager ,
dnsTransport : dnsTransportManager ,
service : serviceManager ,
certificateProvider : certificateProviderManager ,
dnsRouter : dnsRouter ,
connection : connectionManager ,
router : router ,
2026-04-14 22:59:46 +08:00
httpClientService : httpClientService ,
2026-03-23 20:04:36 +08:00
createdAt : createdAt ,
logFactory : logFactory ,
logger : logFactory . Logger (),
internalService : internalServices ,
done : make ( chan struct {}),
2022-07-02 14:07:50 +08:00
}, nil
2022-06-30 21:27:56 +08:00
}
2023-03-18 20:26:58 +08:00
func ( s * Box ) PreStart () error {
err := s . preStart ()
if err != nil {
// TODO: remove catch error
defer func () {
v := recover ()
if v != nil {
2024-06-25 13:10:25 +08:00
println ( err . Error ())
2023-03-18 20:26:58 +08:00
debug . PrintStack ()
panic ( "panic on early close: " + fmt . Sprint ( v ))
}
}()
s . Close ()
return err
}
s . logger . Info ( "sing-box pre-started (" , F . Seconds ( time . Since ( s . createdAt ). Seconds ()), "s)" )
return nil
}
2022-07-07 21:47:21 +08:00
func ( s * Box ) Start () error {
2022-08-22 12:43:21 +08:00
err := s . start ()
if err != nil {
2022-08-24 19:03:00 +08:00
// TODO: remove catch error
defer func () {
v := recover ()
if v != nil {
2024-06-25 13:10:25 +08:00
println ( err . Error ())
2022-08-24 19:03:00 +08:00
debug . PrintStack ()
2024-06-25 13:10:25 +08:00
println ( "panic on early start: " + fmt . Sprint ( v ))
2022-08-24 19:03:00 +08:00
}
}()
2022-08-22 12:43:21 +08:00
s . Close ()
2023-03-18 20:26:58 +08:00
return err
2022-08-22 12:43:21 +08:00
}
2023-03-18 20:26:58 +08:00
s . logger . Info ( "sing-box started (" , F . Seconds ( time . Since ( s . createdAt ). Seconds ()), "s)" )
return nil
2022-08-22 12:43:21 +08:00
}
2023-03-18 20:26:58 +08:00
func ( s * Box ) preStart () error {
2024-04-02 23:07:26 +08:00
monitor := taskmonitor . New ( s . logger , C . StartTimeout )
2023-12-04 11:47:25 +08:00
monitor . Start ( "start logger" )
err := s . logFactory . Start ()
monitor . Finish ()
if err != nil {
return E . Cause ( err , "start logger" )
}
2025-12-20 18:47:50 +08:00
err = adapter . StartNamed ( s . logger , adapter . StartStateInitialize , s . internalService ) // cache-file clash-api v2ray-api
2023-12-20 20:00:00 +08:00
if err != nil {
2024-11-10 16:46:59 +08:00
return err
2023-12-20 20:00:00 +08:00
}
2026-03-23 20:04:36 +08:00
err = adapter . Start ( s . logger , adapter . StartStateInitialize , s . network , s . dnsTransport , s . dnsRouter , s . connection , s . router , s . outbound , s . inbound , s . endpoint , s . service , s . certificateProvider )
2023-06-13 22:38:05 +08:00
if err != nil {
return err
2022-06-30 21:27:56 +08:00
}
2026-04-14 22:59:46 +08:00
err = adapter . Start ( s . logger , adapter . StartStateStart , s . outbound , s . dnsTransport , s . network , s . connection )
if err != nil {
return err
}
err = adapter . StartNamed ( s . logger , adapter . StartStateStart , [] adapter . LifecycleService { s . httpClientService })
if err != nil {
return err
}
err = adapter . Start ( s . logger , adapter . StartStateStart , s . router , s . dnsRouter )
2024-11-10 12:11:21 +08:00
if err != nil {
return err
}
2024-11-10 16:46:59 +08:00
return nil
2023-03-18 20:26:58 +08:00
}
func ( s * Box ) start () error {
err := s . preStart ()
2022-08-20 09:13:00 +08:00
if err != nil {
return err
}
2025-12-20 18:47:50 +08:00
err = adapter . StartNamed ( s . logger , adapter . StartStateStart , s . internalService )
2024-06-07 15:55:21 +08:00
if err != nil {
return err
}
2026-03-23 20:04:36 +08:00
err = adapter . Start ( s . logger , adapter . StartStateStart , s . endpoint )
2024-11-21 18:10:41 +08:00
if err != nil {
return err
}
2026-03-23 20:04:36 +08:00
err = adapter . Start ( s . logger , adapter . StartStateStart , s . certificateProvider )
if err != nil {
return err
}
err = adapter . Start ( s . logger , adapter . StartStateStart , s . inbound , s . service )
if err != nil {
return err
}
err = adapter . Start ( s . logger , adapter . StartStatePostStart , s . outbound , s . network , s . dnsTransport , s . dnsRouter , s . connection , s . router , s . endpoint , s . certificateProvider , s . inbound , s . service )
2024-11-10 12:11:21 +08:00
if err != nil {
return err
}
2025-12-20 18:47:50 +08:00
err = adapter . StartNamed ( s . logger , adapter . StartStatePostStart , s . internalService )
2024-11-09 21:16:11 +08:00
if err != nil {
return err
}
2026-03-23 20:04:36 +08:00
err = adapter . Start ( s . logger , adapter . StartStateStarted , s . network , s . dnsTransport , s . dnsRouter , s . connection , s . router , s . outbound , s . endpoint , s . certificateProvider , s . inbound , s . service )
2024-11-09 21:16:11 +08:00
if err != nil {
return err
}
2025-12-20 18:47:50 +08:00
err = adapter . StartNamed ( s . logger , adapter . StartStateStarted , s . internalService )
2024-11-09 21:16:11 +08:00
if err != nil {
return err
2024-06-07 15:55:21 +08:00
}
return nil
2022-06-30 21:27:56 +08:00
}
2022-07-07 21:47:21 +08:00
func ( s * Box ) Close () error {
2022-08-12 12:13:57 +08:00
select {
case <- s . done :
return os . ErrClosed
default :
close ( s . done )
}
2025-12-20 18:47:50 +08:00
var err error
for _ , closeItem := range [] struct {
name string
service adapter . Lifecycle
}{
{ "service" , s . service },
{ "inbound" , s . inbound },
2026-03-23 20:04:36 +08:00
{ "certificate-provider" , s . certificateProvider },
{ "endpoint" , s . endpoint },
2025-12-20 18:47:50 +08:00
{ "outbound" , s . outbound },
{ "router" , s . router },
{ "connection" , s . connection },
{ "dns-router" , s . dnsRouter },
{ "dns-transport" , s . dnsTransport },
{ "network" , s . network },
} {
2026-04-21 17:15:16 +08:00
done := adapter . LogElapsed ( s . logger , "close " , closeItem . name )
2025-12-20 18:47:50 +08:00
err = E . Append ( err , closeItem . service . Close (), func ( err error ) error {
return E . Cause ( err , "close " , closeItem . name )
})
2026-04-21 17:15:16 +08:00
done ()
2025-12-20 18:47:50 +08:00
}
2026-04-14 22:59:46 +08:00
if s . httpClientService != nil {
s . logger . Trace ( "close " , s . httpClientService . Name ())
startTime := time . Now ()
err = E . Append ( err , s . httpClientService . Close (), func ( err error ) error {
return E . Cause ( err , "close " , s . httpClientService . Name ())
})
s . logger . Trace ( "close " , s . httpClientService . Name (), " completed (" , F . Seconds ( time . Since ( startTime ). Seconds ()), "s)" )
}
2025-03-29 17:24:34 +08:00
for _ , lifecycleService := range s . internalService {
2026-04-21 17:15:16 +08:00
done := adapter . LogElapsed ( s . logger , "close " , lifecycleService . Name ())
2024-11-10 16:46:59 +08:00
err = E . Append ( err , lifecycleService . Close (), func ( err error ) error {
return E . Cause ( err , "close " , lifecycleService . Name ())
2022-10-25 12:55:00 +08:00
})
2026-04-21 17:15:16 +08:00
done ()
2022-10-25 12:55:00 +08:00
}
2026-04-21 17:15:16 +08:00
done := adapter . LogElapsed ( s . logger , "close logger" )
2024-11-10 16:46:59 +08:00
err = E . Append ( err , s . logFactory . Close (), func ( err error ) error {
return E . Cause ( err , "close logger" )
})
2026-04-21 17:15:16 +08:00
done ()
2024-11-10 16:46:59 +08:00
return err
2024-11-09 21:16:11 +08:00
}
2024-11-10 12:11:21 +08:00
func ( s * Box ) Network () adapter . NetworkManager {
return s . network
}
2022-09-26 19:37:06 +08:00
func ( s * Box ) Router () adapter . Router {
return s . router
}
2024-11-10 16:46:59 +08:00
func ( s * Box ) Inbound () adapter . InboundManager {
return s . inbound
}
func ( s * Box ) Outbound () adapter . OutboundManager {
return s . outbound
}
2025-10-07 15:40:11 +08:00
2026-04-09 20:54:32 +08:00
func ( s * Box ) Endpoint () adapter . EndpointManager {
return s . endpoint
}
2025-10-07 15:40:11 +08:00
func ( s * Box ) LogFactory () log . Factory {
return s . logFactory
}