diff --git a/adapter/outbound.go b/adapter/outbound.go index 312cdf3e..df11ed61 100644 --- a/adapter/outbound.go +++ b/adapter/outbound.go @@ -1,6 +1,10 @@ package adapter import ( + "context" + + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" N "github.com/sagernet/sing/common/network" ) @@ -13,3 +17,8 @@ type Outbound interface { Dependencies() []string N.Dialer } + +type OutboundRegistry interface { + option.OutboundOptionsRegistry + CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error) +} diff --git a/adapter/outbound/adapter.go b/adapter/outbound/adapter.go new file mode 100644 index 00000000..481bb619 --- /dev/null +++ b/adapter/outbound/adapter.go @@ -0,0 +1,45 @@ +package outbound + +import ( + "github.com/sagernet/sing-box/option" +) + +type Adapter struct { + protocol string + network []string + tag string + dependencies []string +} + +func NewAdapter(protocol string, network []string, tag string, dependencies []string) Adapter { + return Adapter{ + protocol: protocol, + network: network, + tag: tag, + dependencies: dependencies, + } +} + +func NewAdapterWithDialerOptions(protocol string, network []string, tag string, dialOptions option.DialerOptions) Adapter { + var dependencies []string + if dialOptions.Detour != "" { + dependencies = []string{dialOptions.Detour} + } + return NewAdapter(protocol, network, tag, dependencies) +} + +func (a *Adapter) Type() string { + return a.protocol +} + +func (a *Adapter) Tag() string { + return a.tag +} + +func (a *Adapter) Network() []string { + return a.network +} + +func (a *Adapter) Dependencies() []string { + return a.dependencies +} diff --git a/outbound/default.go b/adapter/outbound/default.go similarity index 87% rename from outbound/default.go rename to adapter/outbound/default.go index a34ac97a..78b9bfd8 100644 --- a/outbound/default.go +++ b/adapter/outbound/default.go @@ -9,8 +9,6 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" @@ -21,42 +19,6 @@ import ( N "github.com/sagernet/sing/common/network" ) -type myOutboundAdapter struct { - protocol string - network []string - router adapter.Router - logger log.ContextLogger - tag string - dependencies []string -} - -func (a *myOutboundAdapter) Type() string { - return a.protocol -} - -func (a *myOutboundAdapter) Tag() string { - return a.tag -} - -func (a *myOutboundAdapter) Network() []string { - return a.network -} - -func (a *myOutboundAdapter) Dependencies() []string { - return a.dependencies -} - -func (a *myOutboundAdapter) NewError(ctx context.Context, err error) { - NewError(a.logger, ctx, err) -} - -func withDialerDependency(options option.DialerOptions) []string { - if options.Detour != "" { - return []string{options.Detour} - } - return nil -} - func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error { ctx = adapter.WithContext(ctx, &metadata) var outConn net.Conn @@ -233,12 +195,3 @@ func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) erro } return bufio.CopyConn(ctx, conn, serverConn) } - -func NewError(logger log.ContextLogger, ctx context.Context, err error) { - common.Close(err) - if E.IsClosedOrCanceled(err) { - logger.DebugContext(ctx, "connection closed: ", err) - return - } - logger.ErrorContext(ctx, err) -} diff --git a/adapter/outbound/registry.go b/adapter/outbound/registry.go new file mode 100644 index 00000000..02cc649f --- /dev/null +++ b/adapter/outbound/registry.go @@ -0,0 +1,68 @@ +package outbound + +import ( + "context" + "sync" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing/common" + E "github.com/sagernet/sing/common/exceptions" +) + +type ConstructorFunc[T any] func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options T) (adapter.Outbound, error) + +func Register[Options any](registry *Registry, outboundType string, constructor ConstructorFunc[Options]) { + registry.register(outboundType, func() any { + return new(Options) + }, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options any) (adapter.Outbound, error) { + return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options.(*Options))) + }) +} + +var _ adapter.OutboundRegistry = (*Registry)(nil) + +type ( + optionsConstructorFunc func() any + constructorFunc func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options any) (adapter.Outbound, error) +) + +type Registry struct { + access sync.Mutex + optiosnType map[string]optionsConstructorFunc + constructors map[string]constructorFunc +} + +func NewRegistry() *Registry { + return &Registry{ + optiosnType: make(map[string]optionsConstructorFunc), + constructors: make(map[string]constructorFunc), + } +} + +func (r *Registry) CreateOptions(outboundType string) (any, bool) { + r.access.Lock() + defer r.access.Unlock() + optionsConstructor, loaded := r.optiosnType[outboundType] + if !loaded { + return nil, false + } + return optionsConstructor(), true +} + +func (r *Registry) CreateOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Outbound, error) { + r.access.Lock() + defer r.access.Unlock() + constructor, loaded := r.constructors[outboundType] + if !loaded { + return nil, E.New("outbound type not found: " + outboundType) + } + return constructor(ctx, router, logger, tag, options) +} + +func (r *Registry) register(outboundType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) { + r.access.Lock() + defer r.access.Unlock() + r.optiosnType[outboundType] = optionsConstructor + r.constructors[outboundType] = constructor +} diff --git a/box.go b/box.go index 716b1b09..15b3d6da 100644 --- a/box.go +++ b/box.go @@ -17,7 +17,7 @@ import ( "github.com/sagernet/sing-box/inbound" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-box/outbound" + "github.com/sagernet/sing-box/protocol/direct" "github.com/sagernet/sing-box/route" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" @@ -48,12 +48,26 @@ type Options struct { PlatformLogWriter log.PlatformWriter } +func Context(ctx context.Context, registry adapter.OutboundRegistry) context.Context { + if service.FromContext[option.OutboundOptionsRegistry](ctx) != nil && + service.FromContext[adapter.OutboundRegistry](ctx) != nil { + return ctx + } + ctx = service.ContextWith[option.OutboundOptionsRegistry](ctx, registry) + ctx = service.ContextWith[adapter.OutboundRegistry](ctx, registry) + return ctx +} + func New(options Options) (*Box, error) { createdAt := time.Now() ctx := options.Context if ctx == nil { ctx = context.Background() } + outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx) + if outboundRegistry == nil { + return nil, E.New("missing outbound registry in context") + } ctx = service.ContextWithDefaultRegistry(ctx) ctx = pause.WithDefaultManager(ctx) experimentalOptions := common.PtrValueOrDefault(options.Experimental) @@ -98,6 +112,16 @@ func New(options Options) (*Box, error) { return nil, E.Cause(err, "parse route options") } inbounds := make([]adapter.Inbound, 0, len(options.Inbounds)) + //nolint:staticcheck + if len(options.LegacyOutbounds) > 0 { + for _, legacyOutbound := range options.LegacyOutbounds { + options.Outbounds = append(options.Outbounds, option.Outbound{ + Type: legacyOutbound.Type, + Tag: legacyOutbound.Tag, + Options: common.Must1(legacyOutbound.RawOptions()), + }) + } + } outbounds := make([]adapter.Outbound, 0, len(options.Outbounds)) for i, inboundOptions := range options.Inbounds { var in adapter.Inbound @@ -121,29 +145,38 @@ func New(options Options) (*Box, error) { inbounds = append(inbounds, in) } for i, outboundOptions := range options.Outbounds { - var out adapter.Outbound + var currentOutbound adapter.Outbound var tag string if outboundOptions.Tag != "" { tag = outboundOptions.Tag } else { tag = F.ToString(i) } - out, err = outbound.New( - ctx, + outboundCtx := ctx + if tag != "" { + // TODO: remove this + outboundCtx = adapter.WithContext(outboundCtx, &adapter.InboundContext{ + Outbound: tag, + }) + } + currentOutbound, err = outboundRegistry.CreateOutbound( + outboundCtx, router, logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")), tag, - outboundOptions) + outboundOptions.Type, + outboundOptions.Options, + ) if err != nil { return nil, E.Cause(err, "parse outbound[", i, "]") } - outbounds = append(outbounds, out) + outbounds = append(outbounds, currentOutbound) } err = router.Initialize(inbounds, outbounds, func() adapter.Outbound { - out, oErr := outbound.New(ctx, router, logFactory.NewLogger("outbound/direct"), "direct", option.Outbound{Type: "direct", Tag: "default"}) - common.Must(oErr) - outbounds = append(outbounds, out) - return out + defaultOutbound, cErr := direct.NewOutbound(ctx, router, logFactory.NewLogger("outbound/direct"), "direct", option.DirectOutboundOptions{}) + common.Must(cErr) + outbounds = append(outbounds, defaultOutbound) + return defaultOutbound }) if err != nil { return nil, err diff --git a/cmd/sing-box/cmd.go b/cmd/sing-box/cmd.go index 9ae3a268..d25417c9 100644 --- a/cmd/sing-box/cmd.go +++ b/cmd/sing-box/cmd.go @@ -7,8 +7,9 @@ import ( "strconv" "time" + "github.com/sagernet/sing-box" "github.com/sagernet/sing-box/experimental/deprecated" - _ "github.com/sagernet/sing-box/include" + "github.com/sagernet/sing-box/include" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/filemanager" @@ -68,4 +69,5 @@ func preRun(cmd *cobra.Command, args []string) { configPaths = append(configPaths, "config.json") } globalCtx = service.ContextWith(globalCtx, deprecated.NewEnvManager(log.StdLogger())) + globalCtx = box.Context(globalCtx, include.OutboundRegistry()) } diff --git a/cmd/sing-box/cmd_format.go b/cmd/sing-box/cmd_format.go index fc47c5a8..9856c763 100644 --- a/cmd/sing-box/cmd_format.go +++ b/cmd/sing-box/cmd_format.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "os" "path/filepath" @@ -38,7 +39,7 @@ func format() error { return err } for _, optionsEntry := range optionsList { - optionsEntry.options, err = badjson.Omitempty(optionsEntry.options) + optionsEntry.options, err = badjson.Omitempty(context.TODO(), optionsEntry.options) if err != nil { return err } diff --git a/cmd/sing-box/cmd_merge.go b/cmd/sing-box/cmd_merge.go index 10dd38a1..a607dccb 100644 --- a/cmd/sing-box/cmd_merge.go +++ b/cmd/sing-box/cmd_merge.go @@ -78,19 +78,14 @@ func mergePathResources(options *option.Options) error { } options.Inbounds[index] = inbound } - for index, outbound := range options.Outbounds { - rawOptions, err := outbound.RawOptions() - if err != nil { - return err - } + for _, outbound := range options.Outbounds { switch outbound.Type { case C.TypeSSH: - outbound.SSHOptions = mergeSSHOutboundOptions(outbound.SSHOptions) + mergeSSHOutboundOptions(outbound.Options.(*option.SSHOutboundOptions)) } - if tlsOptions, containsTLSOptions := rawOptions.(option.OutboundTLSOptionsWrapper); containsTLSOptions { + if tlsOptions, containsTLSOptions := outbound.Options.(option.OutboundTLSOptionsWrapper); containsTLSOptions { tlsOptions.ReplaceOutboundTLSOptions(mergeTLSOutboundOptions(tlsOptions.TakeOutboundTLSOptions())) } - options.Outbounds[index] = outbound } return nil } @@ -138,13 +133,12 @@ func mergeTLSOutboundOptions(options *option.OutboundTLSOptions) *option.Outboun return options } -func mergeSSHOutboundOptions(options option.SSHOutboundOptions) option.SSHOutboundOptions { +func mergeSSHOutboundOptions(options *option.SSHOutboundOptions) { if options.PrivateKeyPath != "" { if content, err := os.ReadFile(os.ExpandEnv(options.PrivateKeyPath)); err == nil { options.PrivateKey = trimStringArray(strings.Split(string(content), "\n")) } } - return options } func trimStringArray(array []string) []string { diff --git a/cmd/sing-box/cmd_run.go b/cmd/sing-box/cmd_run.go index 6850cd19..f31db9dc 100644 --- a/cmd/sing-box/cmd_run.go +++ b/cmd/sing-box/cmd_run.go @@ -57,7 +57,7 @@ func readConfigAt(path string) (*OptionsEntry, error) { if err != nil { return nil, E.Cause(err, "read config at ", path) } - options, err := json.UnmarshalExtended[option.Options](configContent) + options, err := json.UnmarshalExtendedContext[option.Options](globalCtx, configContent) if err != nil { return nil, E.Cause(err, "decode config at ", path) } @@ -109,13 +109,13 @@ func readConfigAndMerge() (option.Options, error) { } var mergedMessage json.RawMessage for _, options := range optionsList { - mergedMessage, err = badjson.MergeJSON(options.options.RawMessage, mergedMessage, false) + mergedMessage, err = badjson.MergeJSON(globalCtx, options.options.RawMessage, mergedMessage, false) if err != nil { return option.Options{}, E.Cause(err, "merge config at ", options.path) } } var mergedOptions option.Options - err = mergedOptions.UnmarshalJSON(mergedMessage) + err = mergedOptions.UnmarshalJSONContext(globalCtx, mergedMessage) if err != nil { return option.Options{}, E.Cause(err, "unmarshal merged config") } diff --git a/common/dialer/default.go b/common/dialer/default.go index b4bca55e..b8a0d5f4 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -125,7 +125,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi setMultiPathTCP(&dialer4) } if options.IsWireGuardListener { - for _, controlFn := range wgControlFns { + for _, controlFn := range WgControlFns { listener.Control = control.Append(listener.Control, controlFn) } } diff --git a/common/dialer/wireguard.go b/common/dialer/wireguard.go index 195133c6..fbd323d8 100644 --- a/common/dialer/wireguard.go +++ b/common/dialer/wireguard.go @@ -2,8 +2,12 @@ package dialer import ( "net" + + "github.com/sagernet/sing/common/control" ) type WireGuardListener interface { ListenPacketCompat(network, address string) (net.PacketConn, error) } + +var WgControlFns []control.Func diff --git a/common/dialer/wireguard_control.go b/common/dialer/wireguard_control.go deleted file mode 100644 index def86411..00000000 --- a/common/dialer/wireguard_control.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build with_wireguard - -package dialer - -import ( - "github.com/sagernet/wireguard-go/conn" -) - -var _ WireGuardListener = (conn.Listener)(nil) - -var wgControlFns = conn.ControlFns diff --git a/common/dialer/wiregurad_stub.go b/common/dialer/wiregurad_stub.go deleted file mode 100644 index d30c223a..00000000 --- a/common/dialer/wiregurad_stub.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !with_wireguard - -package dialer - -import ( - "github.com/sagernet/sing/common/control" -) - -var wgControlFns []control.Func diff --git a/experimental/clashapi/api_meta_group.go b/experimental/clashapi/api_meta_group.go index 396dee7f..531311f4 100644 --- a/experimental/clashapi/api_meta_group.go +++ b/experimental/clashapi/api_meta_group.go @@ -10,7 +10,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/urltest" - "github.com/sagernet/sing-box/outbound" + "github.com/sagernet/sing-box/protocol/group" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/batch" "github.com/sagernet/sing/common/json/badjson" @@ -59,7 +59,7 @@ func getGroup(server *Server) func(w http.ResponseWriter, r *http.Request) { func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { proxy := r.Context().Value(CtxKeyProxy).(adapter.Outbound) - group, ok := proxy.(adapter.OutboundGroup) + outboundGroup, ok := proxy.(adapter.OutboundGroup) if !ok { render.Status(r, http.StatusNotFound) render.JSON(w, r, ErrNotFound) @@ -82,10 +82,10 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request) defer cancel() var result map[string]uint16 - if urlTestGroup, isURLTestGroup := group.(adapter.URLTestGroup); isURLTestGroup { + if urlTestGroup, isURLTestGroup := outboundGroup.(adapter.URLTestGroup); isURLTestGroup { result, err = urlTestGroup.URLTest(ctx) } else { - outbounds := common.FilterNotNil(common.Map(group.All(), func(it string) adapter.Outbound { + outbounds := common.FilterNotNil(common.Map(outboundGroup.All(), func(it string) adapter.Outbound { itOutbound, _ := server.router.Outbound(it) return itOutbound })) @@ -95,7 +95,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request) var resultAccess sync.Mutex for _, detour := range outbounds { tag := detour.Tag() - realTag := outbound.RealTag(detour) + realTag := group.RealTag(detour) if checked[realTag] { continue } diff --git a/experimental/clashapi/proxies.go b/experimental/clashapi/proxies.go index 7a807c1f..4a9564ee 100644 --- a/experimental/clashapi/proxies.go +++ b/experimental/clashapi/proxies.go @@ -11,7 +11,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/urltest" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/outbound" + "github.com/sagernet/sing-box/protocol/group" "github.com/sagernet/sing/common" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/json/badjson" @@ -168,7 +168,7 @@ func updateProxy(w http.ResponseWriter, r *http.Request) { } proxy := r.Context().Value(CtxKeyProxy).(adapter.Outbound) - selector, ok := proxy.(*outbound.Selector) + selector, ok := proxy.(*group.Selector) if !ok { render.Status(r, http.StatusBadRequest) render.JSON(w, r, newError("Must be a Selector")) @@ -204,7 +204,7 @@ func getProxyDelay(server *Server) func(w http.ResponseWriter, r *http.Request) delay, err := urltest.URLTest(ctx, url, proxy) defer func() { - realTag := outbound.RealTag(proxy) + realTag := group.RealTag(proxy) if err != nil { server.urlTestHistory.DeleteURLTestHistory(realTag) } else { diff --git a/experimental/libbox/command_group.go b/experimental/libbox/command_group.go index 3a8d2a07..0915f56b 100644 --- a/experimental/libbox/command_group.go +++ b/experimental/libbox/command_group.go @@ -9,7 +9,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/urltest" - "github.com/sagernet/sing-box/outbound" + "github.com/sagernet/sing-box/protocol/group" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/varbin" "github.com/sagernet/sing/service" @@ -118,14 +118,14 @@ func writeGroups(writer io.Writer, boxService *BoxService) error { } var groups []OutboundGroup for _, iGroup := range iGroups { - var group OutboundGroup - group.Tag = iGroup.Tag() - group.Type = iGroup.Type() - _, group.Selectable = iGroup.(*outbound.Selector) - group.Selected = iGroup.Now() + var outboundGroup OutboundGroup + outboundGroup.Tag = iGroup.Tag() + outboundGroup.Type = iGroup.Type() + _, outboundGroup.Selectable = iGroup.(*group.Selector) + outboundGroup.Selected = iGroup.Now() if cacheFile != nil { - if isExpand, loaded := cacheFile.LoadGroupExpand(group.Tag); loaded { - group.IsExpand = isExpand + if isExpand, loaded := cacheFile.LoadGroupExpand(outboundGroup.Tag); loaded { + outboundGroup.IsExpand = isExpand } } @@ -142,12 +142,12 @@ func writeGroups(writer io.Writer, boxService *BoxService) error { item.URLTestTime = history.Time.Unix() item.URLTestDelay = int32(history.Delay) } - group.ItemList = append(group.ItemList, &item) + outboundGroup.ItemList = append(outboundGroup.ItemList, &item) } - if len(group.ItemList) < 2 { + if len(outboundGroup.ItemList) < 2 { continue } - groups = append(groups, group) + groups = append(groups, outboundGroup) } return varbin.Write(writer, binary.BigEndian, groups) } diff --git a/experimental/libbox/command_select.go b/experimental/libbox/command_select.go index e1e67e60..f352005d 100644 --- a/experimental/libbox/command_select.go +++ b/experimental/libbox/command_select.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "net" - "github.com/sagernet/sing-box/outbound" + "github.com/sagernet/sing-box/protocol/group" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/varbin" ) @@ -47,7 +47,7 @@ func (s *CommandServer) handleSelectOutbound(conn net.Conn) error { if !isLoaded { return writeError(conn, E.New("selector not found: ", groupTag)) } - selector, isSelector := outboundGroup.(*outbound.Selector) + selector, isSelector := outboundGroup.(*group.Selector) if !isSelector { return writeError(conn, E.New("outbound is not a selector: ", groupTag)) } diff --git a/experimental/libbox/command_urltest.go b/experimental/libbox/command_urltest.go index 6feda3f8..c72ded8a 100644 --- a/experimental/libbox/command_urltest.go +++ b/experimental/libbox/command_urltest.go @@ -7,7 +7,7 @@ import ( "github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/common/urltest" - "github.com/sagernet/sing-box/outbound" + "github.com/sagernet/sing-box/protocol/group" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/batch" E "github.com/sagernet/sing/common/exceptions" @@ -49,7 +49,7 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error { if !isOutboundGroup { return writeError(conn, E.New("outbound is not a group: ", groupTag)) } - urlTest, isURLTest := abstractOutboundGroup.(*outbound.URLTest) + urlTest, isURLTest := abstractOutboundGroup.(*group.URLTest) if isURLTest { go urlTest.CheckOutbounds() } else { diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index 7e2e1414..baa192c3 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -17,6 +17,7 @@ import ( "github.com/sagernet/sing-box/experimental/deprecated" "github.com/sagernet/sing-box/experimental/libbox/internal/procfs" "github.com/sagernet/sing-box/experimental/libbox/platform" + "github.com/sagernet/sing-box/include" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-tun" @@ -47,6 +48,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box } runtimeDebug.FreeOSMemory() ctx, cancel := context.WithCancel(context.Background()) + ctx = box.Context(ctx, include.OutboundRegistry()) ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID) urlTestHistoryStorage := urltest.NewHistoryStorage() ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage) diff --git a/experimental/libbox/setup.go b/experimental/libbox/setup.go index ac67db38..64786aa8 100644 --- a/experimental/libbox/setup.go +++ b/experimental/libbox/setup.go @@ -9,7 +9,6 @@ import ( "github.com/sagernet/sing-box/common/humanize" C "github.com/sagernet/sing-box/constant" - _ "github.com/sagernet/sing-box/include" "github.com/sagernet/sing-box/log" ) diff --git a/go.mod b/go.mod index 40ad0e3c..24b75370 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/sagernet/sing-box go 1.20 require ( - berty.tech/go-libtor v1.0.385 github.com/caddyserver/certmagic v0.20.0 github.com/cloudflare/circl v1.3.7 github.com/cretz/bine v0.2.0 @@ -17,7 +16,6 @@ require ( github.com/metacubex/tfo-go v0.0.0-20241006021335-daedaf0ca7aa github.com/mholt/acmez v1.2.0 github.com/miekg/dns v1.1.62 - github.com/ooni/go-libtor v1.1.8 github.com/oschwald/maxminddb-golang v1.12.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 @@ -27,7 +25,7 @@ require ( github.com/sagernet/gvisor v0.0.0-20241021032506-a4324256e4a3 github.com/sagernet/quic-go v0.48.0-beta.1 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.5.0-rc.4.0.20241023053048-94f058276959 + github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369 github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec github.com/sagernet/sing-quic v0.3.0-rc.1 diff --git a/go.sum b/go.sum index 78be2963..6e29bdb4 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw= -berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= @@ -9,7 +7,6 @@ github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NY github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw= github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -81,8 +78,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= -github.com/ooni/go-libtor v1.1.8 h1:Wo3V3DVTxl5vZdxtQakqYP+DAHx7pPtAFSl1bnAa08w= -github.com/ooni/go-libtor v1.1.8/go.mod h1:q1YyLwRD9GeMyeerVvwc0vJ2YgwDLTp2bdVcrh/JXyI= github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= @@ -115,8 +110,8 @@ github.com/sagernet/quic-go v0.48.0-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= -github.com/sagernet/sing v0.5.0-rc.4.0.20241023053048-94f058276959 h1:8BzTt5cU8h6HK4CcRq1UQHKsgUi942GjO0by/ntFZIs= -github.com/sagernet/sing v0.5.0-rc.4.0.20241023053048-94f058276959/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369 h1:gfiUYWslwKM7OtvG37PV0iIDCWcacJSEUS2h29rpYac= +github.com/sagernet/sing v0.5.0-rc.4.0.20241101160402-8452992a6369/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a h1:jpAlbmZxc1LymZrmJacsvHI57Wito5xy8qASZJMWoOQ= github.com/sagernet/sing-dns v0.3.0-rc.2.0.20241023053951-feb6d5403f2a/go.mod h1:TqLIelI+FAbVEdiTRolhGLOwvhVjY7oT+wezlOJUQ7M= github.com/sagernet/sing-mux v0.2.1-0.20241020175909-fe6153f7a9ec h1:6Fd/VsEsw9qIjaGi1IBTZSb4b4v5JYtNcoiBtGsQC48= @@ -146,7 +141,6 @@ github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3k github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= @@ -168,7 +162,6 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= -golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= @@ -182,7 +175,6 @@ golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/include/outbound.go b/include/outbound.go new file mode 100644 index 00000000..06ff45e0 --- /dev/null +++ b/include/outbound.go @@ -0,0 +1,59 @@ +package include + +import ( + "context" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-box/protocol/block" + "github.com/sagernet/sing-box/protocol/direct" + "github.com/sagernet/sing-box/protocol/dns" + "github.com/sagernet/sing-box/protocol/group" + "github.com/sagernet/sing-box/protocol/http" + "github.com/sagernet/sing-box/protocol/shadowsocks" + "github.com/sagernet/sing-box/protocol/shadowtls" + "github.com/sagernet/sing-box/protocol/socks" + "github.com/sagernet/sing-box/protocol/ssh" + "github.com/sagernet/sing-box/protocol/tor" + "github.com/sagernet/sing-box/protocol/trojan" + "github.com/sagernet/sing-box/protocol/vless" + "github.com/sagernet/sing-box/protocol/vmess" + E "github.com/sagernet/sing/common/exceptions" +) + +func OutboundRegistry() *outbound.Registry { + registry := outbound.NewRegistry() + + direct.RegisterOutbound(registry) + + block.RegisterOutbound(registry) + dns.RegisterOutbound(registry) + + group.RegisterSelector(registry) + group.RegisterURLTest(registry) + + socks.RegisterOutbound(registry) + http.RegisterOutbound(registry) + shadowsocks.RegisterOutbound(registry) + vmess.RegisterOutbound(registry) + trojan.RegisterOutbound(registry) + tor.RegisterOutbound(registry) + ssh.RegisterOutbound(registry) + shadowtls.RegisterOutbound(registry) + vless.RegisterOutbound(registry) + + registerQUICOutbounds(registry) + registerWireGuardOutbound(registry) + registerStubForRemovedOutbounds(registry) + + return registry +} + +func registerStubForRemovedOutbounds(registry *outbound.Registry) { + outbound.Register[option.ShadowsocksROutboundOptions](registry, C.TypeShadowsocksR, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) { + return nil, E.New("ShadowsocksR is deprecated and removed in sing-box 1.6.0") + }) +} diff --git a/include/quic.go b/include/quic.go index 1bcc0fbc..a0a8d901 100644 --- a/include/quic.go +++ b/include/quic.go @@ -3,6 +3,16 @@ package include import ( + "github.com/sagernet/sing-box/adapter/outbound" + "github.com/sagernet/sing-box/protocol/hysteria" + "github.com/sagernet/sing-box/protocol/hysteria2" + "github.com/sagernet/sing-box/protocol/tuic" _ "github.com/sagernet/sing-box/transport/v2rayquic" _ "github.com/sagernet/sing-dns/quic" ) + +func registerQUICOutbounds(registry *outbound.Registry) { + hysteria.RegisterOutbound(registry) + tuic.RegisterOutbound(registry) + hysteria2.RegisterOutbound(registry) +} diff --git a/include/quic_stub.go b/include/quic_stub.go index 43aa58d9..f0c12f3a 100644 --- a/include/quic_stub.go +++ b/include/quic_stub.go @@ -6,8 +6,10 @@ import ( "context" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/transport/v2ray" "github.com/sagernet/sing-dns" @@ -29,3 +31,15 @@ func init() { }, ) } + +func registerQUICOutbounds(registry *outbound.Registry) { + outbound.Register[option.HysteriaOutboundOptions](registry, C.TypeHysteria, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (adapter.Outbound, error) { + return nil, C.ErrQUICNotIncluded + }) + outbound.Register[option.TUICOutboundOptions](registry, C.TypeTUIC, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICOutboundOptions) (adapter.Outbound, error) { + return nil, C.ErrQUICNotIncluded + }) + outbound.Register[option.Hysteria2OutboundOptions](registry, C.TypeHysteria2, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (adapter.Outbound, error) { + return nil, C.ErrQUICNotIncluded + }) +} diff --git a/include/wireguard.go b/include/wireguard.go new file mode 100644 index 00000000..dfc3a242 --- /dev/null +++ b/include/wireguard.go @@ -0,0 +1,12 @@ +//go:build with_wireguard + +package include + +import ( + "github.com/sagernet/sing-box/adapter/outbound" + "github.com/sagernet/sing-box/protocol/wireguard" +) + +func registerWireGuardOutbound(registry *outbound.Registry) { + wireguard.RegisterOutbound(registry) +} diff --git a/include/wireguard_stub.go b/include/wireguard_stub.go new file mode 100644 index 00000000..a9e84522 --- /dev/null +++ b/include/wireguard_stub.go @@ -0,0 +1,20 @@ +//go:build !with_wireguard + +package include + +import ( + "context" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + E "github.com/sagernet/sing/common/exceptions" +) + +func registerWireGuardOutbound(registry *outbound.Registry) { + outbound.Register[option.WireGuardOutboundOptions](registry, C.TypeWireGuard, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (adapter.Outbound, error) { + return nil, E.New(`WireGuard is not included in this build, rebuild with -tags with_wireguard`) + }) +} diff --git a/option/inbound.go b/option/inbound.go index d3879904..9346966e 100644 --- a/option/inbound.go +++ b/option/inbound.go @@ -6,6 +6,7 @@ import ( C "github.com/sagernet/sing-box/constant" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" ) type _Inbound struct { @@ -79,7 +80,7 @@ func (h Inbound) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return MarshallObjects((_Inbound)(h), rawOptions) + return badjson.MarshallObjects((_Inbound)(h), rawOptions) } func (h *Inbound) UnmarshalJSON(bytes []byte) error { @@ -91,7 +92,7 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error { if err != nil { return err } - err = UnmarshallExcluded(bytes, (*_Inbound)(h), rawOptions) + err = badjson.UnmarshallExcluded(bytes, (*_Inbound)(h), rawOptions) if err != nil { return err } diff --git a/option/json.go b/option/json.go deleted file mode 100644 index 775141d5..00000000 --- a/option/json.go +++ /dev/null @@ -1,71 +0,0 @@ -package option - -import ( - "github.com/sagernet/sing/common" - E "github.com/sagernet/sing/common/exceptions" - "github.com/sagernet/sing/common/json" - "github.com/sagernet/sing/common/json/badjson" -) - -func ToMap(v any) (*badjson.JSONObject, error) { - inputContent, err := json.Marshal(v) - if err != nil { - return nil, err - } - var content badjson.JSONObject - err = content.UnmarshalJSON(inputContent) - if err != nil { - return nil, err - } - return &content, nil -} - -func MergeObjects(objects ...any) (*badjson.JSONObject, error) { - var content badjson.JSONObject - for _, object := range objects { - objectMap, err := ToMap(object) - if err != nil { - return nil, err - } - content.PutAll(objectMap) - } - return &content, nil -} - -func MarshallObjects(objects ...any) ([]byte, error) { - objects = common.FilterNotNil(objects) - if len(objects) == 1 { - return json.Marshal(objects[0]) - } - content, err := MergeObjects(objects...) - if err != nil { - return nil, err - } - return content.MarshalJSON() -} - -func UnmarshallExcluded(inputContent []byte, parentObject any, object any) error { - parentContent, err := ToMap(parentObject) - if err != nil { - return err - } - var content badjson.JSONObject - err = content.UnmarshalJSON(inputContent) - if err != nil { - return err - } - for _, key := range parentContent.Keys() { - content.Remove(key) - } - if object == nil { - if content.IsEmpty() { - return nil - } - return E.New("unexpected key: ", content.Keys()[0]) - } - inputContent, err = content.MarshalJSON() - if err != nil { - return err - } - return json.UnmarshalDisallowUnknownFields(inputContent, object) -} diff --git a/option/config.go b/option/options.go similarity index 79% rename from option/config.go rename to option/options.go index 3f5d7602..92fccb38 100644 --- a/option/config.go +++ b/option/options.go @@ -2,6 +2,7 @@ package option import ( "bytes" + "context" "github.com/sagernet/sing/common/json" ) @@ -16,12 +17,15 @@ type _Options struct { Outbounds []Outbound `json:"outbounds,omitempty"` Route *RouteOptions `json:"route,omitempty"` Experimental *ExperimentalOptions `json:"experimental,omitempty"` + + // Deprecated: use Outbounds instead + LegacyOutbounds []LegacyOutbound `json:"_"` } type Options _Options -func (o *Options) UnmarshalJSON(content []byte) error { - decoder := json.NewDecoder(bytes.NewReader(content)) +func (o *Options) UnmarshalJSONContext(ctx context.Context, content []byte) error { + decoder := json.NewDecoderContext(ctx, bytes.NewReader(content)) decoder.DisallowUnknownFields() err := decoder.Decode((*_Options)(o)) if err != nil { @@ -38,3 +42,5 @@ type LogOptions struct { Timestamp bool `json:"timestamp,omitempty"` DisableColor bool `json:"-"` } + +type StubOptions struct{} diff --git a/option/outbound.go b/option/outbound.go index 6c943cd9..00a20aa5 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -1,104 +1,49 @@ package option import ( - C "github.com/sagernet/sing-box/constant" + "context" + E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" M "github.com/sagernet/sing/common/metadata" + "github.com/sagernet/sing/service" ) +type OutboundOptionsRegistry interface { + CreateOptions(outboundType string) (any, bool) +} + type _Outbound struct { - Type string `json:"type"` - Tag string `json:"tag,omitempty"` - DirectOptions DirectOutboundOptions `json:"-"` - SocksOptions SocksOutboundOptions `json:"-"` - HTTPOptions HTTPOutboundOptions `json:"-"` - ShadowsocksOptions ShadowsocksOutboundOptions `json:"-"` - VMessOptions VMessOutboundOptions `json:"-"` - TrojanOptions TrojanOutboundOptions `json:"-"` - WireGuardOptions WireGuardOutboundOptions `json:"-"` - HysteriaOptions HysteriaOutboundOptions `json:"-"` - TorOptions TorOutboundOptions `json:"-"` - SSHOptions SSHOutboundOptions `json:"-"` - ShadowTLSOptions ShadowTLSOutboundOptions `json:"-"` - ShadowsocksROptions ShadowsocksROutboundOptions `json:"-"` - VLESSOptions VLESSOutboundOptions `json:"-"` - TUICOptions TUICOutboundOptions `json:"-"` - Hysteria2Options Hysteria2OutboundOptions `json:"-"` - SelectorOptions SelectorOutboundOptions `json:"-"` - URLTestOptions URLTestOutboundOptions `json:"-"` + Type string `json:"type"` + Tag string `json:"tag,omitempty"` + Options any `json:"-"` } type Outbound _Outbound -func (h *Outbound) RawOptions() (any, error) { - var rawOptionsPtr any - switch h.Type { - case C.TypeDirect: - rawOptionsPtr = &h.DirectOptions - case C.TypeBlock, C.TypeDNS: - rawOptionsPtr = nil - case C.TypeSOCKS: - rawOptionsPtr = &h.SocksOptions - case C.TypeHTTP: - rawOptionsPtr = &h.HTTPOptions - case C.TypeShadowsocks: - rawOptionsPtr = &h.ShadowsocksOptions - case C.TypeVMess: - rawOptionsPtr = &h.VMessOptions - case C.TypeTrojan: - rawOptionsPtr = &h.TrojanOptions - case C.TypeWireGuard: - rawOptionsPtr = &h.WireGuardOptions - case C.TypeHysteria: - rawOptionsPtr = &h.HysteriaOptions - case C.TypeTor: - rawOptionsPtr = &h.TorOptions - case C.TypeSSH: - rawOptionsPtr = &h.SSHOptions - case C.TypeShadowTLS: - rawOptionsPtr = &h.ShadowTLSOptions - case C.TypeShadowsocksR: - rawOptionsPtr = &h.ShadowsocksROptions - case C.TypeVLESS: - rawOptionsPtr = &h.VLESSOptions - case C.TypeTUIC: - rawOptionsPtr = &h.TUICOptions - case C.TypeHysteria2: - rawOptionsPtr = &h.Hysteria2Options - case C.TypeSelector: - rawOptionsPtr = &h.SelectorOptions - case C.TypeURLTest: - rawOptionsPtr = &h.URLTestOptions - case "": - return nil, E.New("missing outbound type") - default: - return nil, E.New("unknown outbound type: ", h.Type) - } - return rawOptionsPtr, nil +func (h *Outbound) MarshalJSONContext(ctx context.Context) ([]byte, error) { + return badjson.MarshallObjectsContext(ctx, (*_Outbound)(h), h.Options) } -func (h *Outbound) MarshalJSON() ([]byte, error) { - rawOptions, err := h.RawOptions() - if err != nil { - return nil, err - } - return MarshallObjects((*_Outbound)(h), rawOptions) -} - -func (h *Outbound) UnmarshalJSON(bytes []byte) error { - err := json.Unmarshal(bytes, (*_Outbound)(h)) +func (h *Outbound) UnmarshalJSONContext(ctx context.Context, content []byte) error { + err := json.Unmarshal(content, (*_Outbound)(h)) if err != nil { return err } - rawOptions, err := h.RawOptions() - if err != nil { - return err - } - err = UnmarshallExcluded(bytes, (*_Outbound)(h), rawOptions) + registry := service.FromContext[OutboundOptionsRegistry](ctx) + if registry == nil { + return E.New("missing outbound options registry in context") + } + options, loaded := registry.CreateOptions(h.Type) + if !loaded { + return E.New("unknown outbound type: ", h.Type) + } + err = badjson.UnmarshallExcludedContext(ctx, content, (*_Outbound)(h), options) if err != nil { return err } + h.Options = options return nil } diff --git a/option/outbound_legacy.go b/option/outbound_legacy.go new file mode 100644 index 00000000..d840884b --- /dev/null +++ b/option/outbound_legacy.go @@ -0,0 +1,103 @@ +package option + +import ( + C "github.com/sagernet/sing-box/constant" + E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" +) + +type _LegacyOutbound struct { + Type string `json:"type"` + Tag string `json:"tag,omitempty"` + DirectOptions DirectOutboundOptions `json:"-"` + SocksOptions SOCKSOutboundOptions `json:"-"` + HTTPOptions HTTPOutboundOptions `json:"-"` + ShadowsocksOptions ShadowsocksOutboundOptions `json:"-"` + VMessOptions VMessOutboundOptions `json:"-"` + TrojanOptions TrojanOutboundOptions `json:"-"` + WireGuardOptions WireGuardOutboundOptions `json:"-"` + HysteriaOptions HysteriaOutboundOptions `json:"-"` + TorOptions TorOutboundOptions `json:"-"` + SSHOptions SSHOutboundOptions `json:"-"` + ShadowTLSOptions ShadowTLSOutboundOptions `json:"-"` + ShadowsocksROptions ShadowsocksROutboundOptions `json:"-"` + VLESSOptions VLESSOutboundOptions `json:"-"` + TUICOptions TUICOutboundOptions `json:"-"` + Hysteria2Options Hysteria2OutboundOptions `json:"-"` + SelectorOptions SelectorOutboundOptions `json:"-"` + URLTestOptions URLTestOutboundOptions `json:"-"` +} + +type LegacyOutbound _LegacyOutbound + +func (h *LegacyOutbound) RawOptions() (any, error) { + var rawOptionsPtr any + switch h.Type { + case C.TypeDirect: + rawOptionsPtr = &h.DirectOptions + case C.TypeBlock, C.TypeDNS: + rawOptionsPtr = new(StubOptions) + case C.TypeSOCKS: + rawOptionsPtr = &h.SocksOptions + case C.TypeHTTP: + rawOptionsPtr = &h.HTTPOptions + case C.TypeShadowsocks: + rawOptionsPtr = &h.ShadowsocksOptions + case C.TypeVMess: + rawOptionsPtr = &h.VMessOptions + case C.TypeTrojan: + rawOptionsPtr = &h.TrojanOptions + case C.TypeWireGuard: + rawOptionsPtr = &h.WireGuardOptions + case C.TypeHysteria: + rawOptionsPtr = &h.HysteriaOptions + case C.TypeTor: + rawOptionsPtr = &h.TorOptions + case C.TypeSSH: + rawOptionsPtr = &h.SSHOptions + case C.TypeShadowTLS: + rawOptionsPtr = &h.ShadowTLSOptions + case C.TypeShadowsocksR: + rawOptionsPtr = &h.ShadowsocksROptions + case C.TypeVLESS: + rawOptionsPtr = &h.VLESSOptions + case C.TypeTUIC: + rawOptionsPtr = &h.TUICOptions + case C.TypeHysteria2: + rawOptionsPtr = &h.Hysteria2Options + case C.TypeSelector: + rawOptionsPtr = &h.SelectorOptions + case C.TypeURLTest: + rawOptionsPtr = &h.URLTestOptions + case "": + return nil, E.New("missing outbound type") + default: + return nil, E.New("unknown outbound type: ", h.Type) + } + return rawOptionsPtr, nil +} + +func (h *LegacyOutbound) MarshalJSON() ([]byte, error) { + rawOptions, err := h.RawOptions() + if err != nil { + return nil, err + } + return badjson.MarshallObjects((*_LegacyOutbound)(h), rawOptions) +} + +func (h *LegacyOutbound) UnmarshalJSON(bytes []byte) error { + err := json.Unmarshal(bytes, (*_LegacyOutbound)(h)) + if err != nil { + return err + } + rawOptions, err := h.RawOptions() + if err != nil { + return err + } + err = badjson.UnmarshallExcluded(bytes, (*_LegacyOutbound)(h), rawOptions) + if err != nil { + return err + } + return nil +} diff --git a/option/rule.go b/option/rule.go index 0b11cbdd..07e6ddbe 100644 --- a/option/rule.go +++ b/option/rule.go @@ -7,6 +7,7 @@ import ( "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" ) type _Rule struct { @@ -28,7 +29,7 @@ func (r Rule) MarshalJSON() ([]byte, error) { default: return nil, E.New("unknown rule type: " + r.Type) } - return MarshallObjects((_Rule)(r), v) + return badjson.MarshallObjects((_Rule)(r), v) } func (r *Rule) UnmarshalJSON(bytes []byte) error { @@ -46,7 +47,7 @@ func (r *Rule) UnmarshalJSON(bytes []byte) error { default: return E.New("unknown rule type: " + r.Type) } - err = UnmarshallExcluded(bytes, (*_Rule)(r), v) + err = badjson.UnmarshallExcluded(bytes, (*_Rule)(r), v) if err != nil { return err } @@ -109,7 +110,7 @@ type DefaultRule struct { } func (r *DefaultRule) MarshalJSON() ([]byte, error) { - return MarshallObjects(r.RawDefaultRule, r.RuleAction) + return badjson.MarshallObjects(r.RawDefaultRule, r.RuleAction) } func (r *DefaultRule) UnmarshalJSON(data []byte) error { @@ -117,7 +118,7 @@ func (r *DefaultRule) UnmarshalJSON(data []byte) error { if err != nil { return err } - return UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction) + return badjson.UnmarshallExcluded(data, &r.RawDefaultRule, &r.RuleAction) } func (r *DefaultRule) IsValid() bool { @@ -139,7 +140,7 @@ type LogicalRule struct { } func (r *LogicalRule) MarshalJSON() ([]byte, error) { - return MarshallObjects(r._LogicalRule, r.RuleAction) + return badjson.MarshallObjects(r._LogicalRule, r.RuleAction) } func (r *LogicalRule) UnmarshalJSON(data []byte) error { @@ -147,7 +148,7 @@ func (r *LogicalRule) UnmarshalJSON(data []byte) error { if err != nil { return err } - return UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction) + return badjson.UnmarshallExcluded(data, &r._LogicalRule, &r.RuleAction) } func (r *LogicalRule) IsValid() bool { diff --git a/option/rule_action.go b/option/rule_action.go index f446d81d..e752a2be 100644 --- a/option/rule_action.go +++ b/option/rule_action.go @@ -4,6 +4,7 @@ import ( C "github.com/sagernet/sing-box/constant" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" ) type _RuleAction struct { @@ -36,9 +37,9 @@ func (r RuleAction) MarshalJSON() ([]byte, error) { return nil, E.New("unknown rule action: " + r.Action) } if v == nil { - return MarshallObjects((_RuleAction)(r)) + return badjson.MarshallObjects((_RuleAction)(r)) } - return MarshallObjects((_RuleAction)(r), v) + return badjson.MarshallObjects((_RuleAction)(r), v) } func (r *RuleAction) UnmarshalJSON(data []byte) error { @@ -68,7 +69,7 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error { // check unknown fields return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{}) } - return UnmarshallExcluded(data, (*_RuleAction)(r), v) + return badjson.UnmarshallExcluded(data, (*_RuleAction)(r), v) } type _DNSRuleAction struct { @@ -95,9 +96,9 @@ func (r DNSRuleAction) MarshalJSON() ([]byte, error) { return nil, E.New("unknown DNS rule action: " + r.Action) } if v == nil { - return MarshallObjects((_DNSRuleAction)(r)) + return badjson.MarshallObjects((_DNSRuleAction)(r)) } - return MarshallObjects((_DNSRuleAction)(r), v) + return badjson.MarshallObjects((_DNSRuleAction)(r), v) } func (r *DNSRuleAction) UnmarshalJSON(data []byte) error { @@ -121,7 +122,7 @@ func (r *DNSRuleAction) UnmarshalJSON(data []byte) error { // check unknown fields return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{}) } - return UnmarshallExcluded(data, (*_DNSRuleAction)(r), v) + return badjson.UnmarshallExcluded(data, (*_DNSRuleAction)(r), v) } type RouteActionOptions struct { diff --git a/option/rule_dns.go b/option/rule_dns.go index b328c45c..8c4b6ab8 100644 --- a/option/rule_dns.go +++ b/option/rule_dns.go @@ -7,6 +7,7 @@ import ( "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" ) type _DNSRule struct { @@ -28,7 +29,7 @@ func (r DNSRule) MarshalJSON() ([]byte, error) { default: return nil, E.New("unknown rule type: " + r.Type) } - return MarshallObjects((_DNSRule)(r), v) + return badjson.MarshallObjects((_DNSRule)(r), v) } func (r *DNSRule) UnmarshalJSON(bytes []byte) error { @@ -46,7 +47,7 @@ func (r *DNSRule) UnmarshalJSON(bytes []byte) error { default: return E.New("unknown rule type: " + r.Type) } - err = UnmarshallExcluded(bytes, (*_DNSRule)(r), v) + err = badjson.UnmarshallExcluded(bytes, (*_DNSRule)(r), v) if err != nil { return err } @@ -111,7 +112,7 @@ type DefaultDNSRule struct { } func (r *DefaultDNSRule) MarshalJSON() ([]byte, error) { - return MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction) + return badjson.MarshallObjects(r.RawDefaultDNSRule, r.DNSRuleAction) } func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error { @@ -119,7 +120,7 @@ func (r *DefaultDNSRule) UnmarshalJSON(data []byte) error { if err != nil { return err } - return UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction) + return badjson.UnmarshallExcluded(data, &r.RawDefaultDNSRule, &r.DNSRuleAction) } func (r *DefaultDNSRule) IsValid() bool { @@ -141,7 +142,7 @@ type LogicalDNSRule struct { } func (r *LogicalDNSRule) MarshalJSON() ([]byte, error) { - return MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction) + return badjson.MarshallObjects(r._LogicalDNSRule, r.DNSRuleAction) } func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error { @@ -149,7 +150,7 @@ func (r *LogicalDNSRule) UnmarshalJSON(data []byte) error { if err != nil { return err } - return UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction) + return badjson.UnmarshallExcluded(data, &r._LogicalDNSRule, &r.DNSRuleAction) } func (r *LogicalDNSRule) IsValid() bool { diff --git a/option/rule_set.go b/option/rule_set.go index d4368de3..358821de 100644 --- a/option/rule_set.go +++ b/option/rule_set.go @@ -9,6 +9,7 @@ import ( E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" "go4.org/netipx" ) @@ -37,7 +38,7 @@ func (r RuleSet) MarshalJSON() ([]byte, error) { default: return nil, E.New("unknown rule-set type: " + r.Type) } - return MarshallObjects((_RuleSet)(r), v) + return badjson.MarshallObjects((_RuleSet)(r), v) } func (r *RuleSet) UnmarshalJSON(bytes []byte) error { @@ -71,7 +72,7 @@ func (r *RuleSet) UnmarshalJSON(bytes []byte) error { } else { r.Format = "" } - err = UnmarshallExcluded(bytes, (*_RuleSet)(r), v) + err = badjson.UnmarshallExcluded(bytes, (*_RuleSet)(r), v) if err != nil { return err } @@ -107,7 +108,7 @@ func (r HeadlessRule) MarshalJSON() ([]byte, error) { default: return nil, E.New("unknown rule type: " + r.Type) } - return MarshallObjects((_HeadlessRule)(r), v) + return badjson.MarshallObjects((_HeadlessRule)(r), v) } func (r *HeadlessRule) UnmarshalJSON(bytes []byte) error { @@ -125,7 +126,7 @@ func (r *HeadlessRule) UnmarshalJSON(bytes []byte) error { default: return E.New("unknown rule type: " + r.Type) } - err = UnmarshallExcluded(bytes, (*_HeadlessRule)(r), v) + err = badjson.UnmarshallExcluded(bytes, (*_HeadlessRule)(r), v) if err != nil { return err } @@ -203,7 +204,7 @@ func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) { default: return nil, E.New("unknown rule-set version: ", r.Version) } - return MarshallObjects((_PlainRuleSetCompat)(r), v) + return badjson.MarshallObjects((_PlainRuleSetCompat)(r), v) } func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error { @@ -220,7 +221,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error { default: return E.New("unknown rule-set version: ", r.Version) } - err = UnmarshallExcluded(bytes, (*_PlainRuleSetCompat)(r), v) + err = badjson.UnmarshallExcluded(bytes, (*_PlainRuleSetCompat)(r), v) if err != nil { return err } diff --git a/option/simple.go b/option/simple.go index ba9d6bf1..78171ce4 100644 --- a/option/simple.go +++ b/option/simple.go @@ -14,7 +14,7 @@ type HTTPMixedInboundOptions struct { InboundTLSOptionsContainer } -type SocksOutboundOptions struct { +type SOCKSOutboundOptions struct { DialerOptions ServerOptions Version string `json:"version,omitempty"` diff --git a/option/tls_acme.go b/option/tls_acme.go index 17d515e2..9c2e081f 100644 --- a/option/tls_acme.go +++ b/option/tls_acme.go @@ -4,6 +4,7 @@ import ( C "github.com/sagernet/sing-box/constant" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" ) type InboundACMEOptions struct { @@ -45,7 +46,7 @@ func (o ACMEDNS01ChallengeOptions) MarshalJSON() ([]byte, error) { default: return nil, E.New("unknown provider type: " + o.Provider) } - return MarshallObjects((_ACMEDNS01ChallengeOptions)(o), v) + return badjson.MarshallObjects((_ACMEDNS01ChallengeOptions)(o), v) } func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error { @@ -62,7 +63,7 @@ func (o *ACMEDNS01ChallengeOptions) UnmarshalJSON(bytes []byte) error { default: return E.New("unknown provider type: " + o.Provider) } - err = UnmarshallExcluded(bytes, (*_ACMEDNS01ChallengeOptions)(o), v) + err = badjson.UnmarshallExcluded(bytes, (*_ACMEDNS01ChallengeOptions)(o), v) if err != nil { return err } diff --git a/option/v2ray_transport.go b/option/v2ray_transport.go index fcd81f94..f87b175d 100644 --- a/option/v2ray_transport.go +++ b/option/v2ray_transport.go @@ -4,6 +4,7 @@ import ( C "github.com/sagernet/sing-box/constant" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" + "github.com/sagernet/sing/common/json/badjson" ) type _V2RayTransportOptions struct { @@ -35,7 +36,7 @@ func (o V2RayTransportOptions) MarshalJSON() ([]byte, error) { default: return nil, E.New("unknown transport type: " + o.Type) } - return MarshallObjects((_V2RayTransportOptions)(o), v) + return badjson.MarshallObjects((_V2RayTransportOptions)(o), v) } func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error { @@ -58,7 +59,7 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error { default: return E.New("unknown transport type: " + o.Type) } - err = UnmarshallExcluded(bytes, (*_V2RayTransportOptions)(o), v) + err = badjson.UnmarshallExcluded(bytes, (*_V2RayTransportOptions)(o), v) if err != nil { return err } diff --git a/outbound/block.go b/outbound/block.go deleted file mode 100644 index b6ccefe2..00000000 --- a/outbound/block.go +++ /dev/null @@ -1,54 +0,0 @@ -package outbound - -import ( - "context" - "io" - "net" - - "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/log" - M "github.com/sagernet/sing/common/metadata" - N "github.com/sagernet/sing/common/network" -) - -var _ adapter.Outbound = (*Block)(nil) - -type Block struct { - myOutboundAdapter -} - -func NewBlock(logger log.ContextLogger, tag string) *Block { - return &Block{ - myOutboundAdapter{ - protocol: C.TypeBlock, - network: []string{N.NetworkTCP, N.NetworkUDP}, - logger: logger, - tag: tag, - }, - } -} - -func (h *Block) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - h.logger.InfoContext(ctx, "blocked connection to ", destination) - return nil, io.EOF -} - -func (h *Block) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - h.logger.InfoContext(ctx, "blocked packet connection to ", destination) - return nil, io.EOF -} - -// Deprecated -func (h *Block) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - conn.Close() - h.logger.InfoContext(ctx, "blocked connection to ", metadata.Destination) - return nil -} - -// Deprecated -func (h *Block) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - conn.Close() - h.logger.InfoContext(ctx, "blocked packet connection to ", metadata.Destination) - return nil -} diff --git a/outbound/builder.go b/outbound/builder.go deleted file mode 100644 index d895b56d..00000000 --- a/outbound/builder.go +++ /dev/null @@ -1,65 +0,0 @@ -package outbound - -import ( - "context" - - "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" - E "github.com/sagernet/sing/common/exceptions" -) - -func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Outbound) (adapter.Outbound, error) { - if tag != "" { - ctx = adapter.WithContext(ctx, &adapter.InboundContext{ - Outbound: tag, - }) - } - if options.Type == "" { - return nil, E.New("missing outbound type") - } - ctx = ContextWithTag(ctx, tag) - switch options.Type { - case C.TypeDirect: - return NewDirect(router, logger, tag, options.DirectOptions) - case C.TypeBlock: - return NewBlock(logger, tag), nil - case C.TypeDNS: - return NewDNS(router, tag), nil - case C.TypeSOCKS: - return NewSocks(router, logger, tag, options.SocksOptions) - case C.TypeHTTP: - return NewHTTP(ctx, router, logger, tag, options.HTTPOptions) - case C.TypeShadowsocks: - return NewShadowsocks(ctx, router, logger, tag, options.ShadowsocksOptions) - case C.TypeVMess: - return NewVMess(ctx, router, logger, tag, options.VMessOptions) - case C.TypeTrojan: - return NewTrojan(ctx, router, logger, tag, options.TrojanOptions) - case C.TypeWireGuard: - return NewWireGuard(ctx, router, logger, tag, options.WireGuardOptions) - case C.TypeHysteria: - return NewHysteria(ctx, router, logger, tag, options.HysteriaOptions) - case C.TypeTor: - return NewTor(ctx, router, logger, tag, options.TorOptions) - case C.TypeSSH: - return NewSSH(ctx, router, logger, tag, options.SSHOptions) - case C.TypeShadowTLS: - return NewShadowTLS(ctx, router, logger, tag, options.ShadowTLSOptions) - case C.TypeShadowsocksR: - return NewShadowsocksR(ctx, router, logger, tag, options.ShadowsocksROptions) - case C.TypeVLESS: - return NewVLESS(ctx, router, logger, tag, options.VLESSOptions) - case C.TypeTUIC: - return NewTUIC(ctx, router, logger, tag, options.TUICOptions) - case C.TypeHysteria2: - return NewHysteria2(ctx, router, logger, tag, options.Hysteria2Options) - case C.TypeSelector: - return NewSelector(ctx, router, logger, tag, options.SelectorOptions) - case C.TypeURLTest: - return NewURLTest(ctx, router, logger, tag, options.URLTestOptions) - default: - return nil, E.New("unknown outbound type: ", options.Type) - } -} diff --git a/outbound/hysteria_stub.go b/outbound/hysteria_stub.go deleted file mode 100644 index 84db5305..00000000 --- a/outbound/hysteria_stub.go +++ /dev/null @@ -1,20 +0,0 @@ -//go:build !with_quic - -package outbound - -import ( - "context" - - "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" -) - -func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (adapter.Outbound, error) { - return nil, C.ErrQUICNotIncluded -} - -func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (adapter.Outbound, error) { - return nil, C.ErrQUICNotIncluded -} diff --git a/outbound/lookback.go b/outbound/lookback.go deleted file mode 100644 index aeb7451d..00000000 --- a/outbound/lookback.go +++ /dev/null @@ -1,14 +0,0 @@ -package outbound - -import "context" - -type outboundTagKey struct{} - -func ContextWithTag(ctx context.Context, outboundTag string) context.Context { - return context.WithValue(ctx, outboundTagKey{}, outboundTag) -} - -func TagFromContext(ctx context.Context) (string, bool) { - value, loaded := ctx.Value(outboundTagKey{}).(string) - return value, loaded -} diff --git a/outbound/shadowsocksr.go b/outbound/shadowsocksr.go deleted file mode 100644 index 615a71e4..00000000 --- a/outbound/shadowsocksr.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build with_shadowsocksr - -package outbound - -import ( - "context" - "os" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" -) - -var _ int = "ShadowsocksR is deprecated and removed in sing-box 1.6.0" - -func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) { - return nil, os.ErrInvalid -} diff --git a/outbound/shadowsocksr_stub.go b/outbound/shadowsocksr_stub.go deleted file mode 100644 index 94971da0..00000000 --- a/outbound/shadowsocksr_stub.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build !with_shadowsocksr - -package outbound - -import ( - "context" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" - E "github.com/sagernet/sing/common/exceptions" -) - -func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) { - return nil, E.New("ShadowsocksR is deprecated and removed in sing-box 1.6.0") -} diff --git a/outbound/tor_embed.go b/outbound/tor_embed.go deleted file mode 100644 index d80b49ae..00000000 --- a/outbound/tor_embed.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build with_embedded_tor && !(android || ios) - -package outbound - -import ( - "berty.tech/go-libtor" - "github.com/cretz/bine/tor" -) - -func newConfig() tor.StartConf { - return tor.StartConf{ - ProcessCreator: libtor.Creator, - UseEmbeddedControlConn: true, - } -} diff --git a/outbound/tor_embed_mobile.go b/outbound/tor_embed_mobile.go deleted file mode 100644 index 0900d8c9..00000000 --- a/outbound/tor_embed_mobile.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build with_embedded_tor && (android || ios) - -package outbound - -import ( - "github.com/cretz/bine/tor" - "github.com/ooni/go-libtor" -) - -func newConfig() tor.StartConf { - return tor.StartConf{ - ProcessCreator: libtor.Creator, - UseEmbeddedControlConn: true, - } -} diff --git a/outbound/tor_external.go b/outbound/tor_external.go deleted file mode 100644 index 6bce95d1..00000000 --- a/outbound/tor_external.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !with_embedded_tor - -package outbound - -import "github.com/cretz/bine/tor" - -func newConfig() tor.StartConf { - return tor.StartConf{} -} diff --git a/outbound/tuic_stub.go b/outbound/tuic_stub.go deleted file mode 100644 index a6372c9e..00000000 --- a/outbound/tuic_stub.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build !with_quic - -package outbound - -import ( - "context" - - "github.com/sagernet/sing-box/adapter" - C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" -) - -func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICOutboundOptions) (adapter.Outbound, error) { - return nil, C.ErrQUICNotIncluded -} diff --git a/outbound/wireguard_stub.go b/outbound/wireguard_stub.go deleted file mode 100644 index 3a8b0e87..00000000 --- a/outbound/wireguard_stub.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build !with_wireguard - -package outbound - -import ( - "context" - - "github.com/sagernet/sing-box/adapter" - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" - E "github.com/sagernet/sing/common/exceptions" -) - -func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (adapter.Outbound, error) { - return nil, E.New(`WireGuard is not included in this build, rebuild with -tags with_wireguard`) -} diff --git a/protocol/block/outbound.go b/protocol/block/outbound.go new file mode 100644 index 00000000..75bc7797 --- /dev/null +++ b/protocol/block/outbound.go @@ -0,0 +1,42 @@ +package block + +import ( + "context" + "net" + "syscall" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" +) + +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.StubOptions](registry, C.TypeBlock, New) +} + +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger +} + +func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, _ option.StubOptions) (adapter.Outbound, error) { + return &Outbound{ + Adapter: outbound.NewAdapter(C.TypeBlock, []string{N.NetworkTCP, N.NetworkUDP}, tag, nil), + logger: logger, + }, nil +} + +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + h.logger.InfoContext(ctx, "blocked connection to ", destination) + return nil, syscall.EPERM +} + +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + h.logger.InfoContext(ctx, "blocked packet connection to ", destination) + return nil, syscall.EPERM +} diff --git a/outbound/direct_loopback_detect.go b/protocol/direct/loopback_detect.go similarity index 99% rename from outbound/direct_loopback_detect.go rename to protocol/direct/loopback_detect.go index 1469b9d0..5a184e69 100644 --- a/outbound/direct_loopback_detect.go +++ b/protocol/direct/loopback_detect.go @@ -1,4 +1,4 @@ -package outbound +package direct import ( "net" diff --git a/outbound/direct.go b/protocol/direct/outbound.go similarity index 76% rename from outbound/direct.go rename to protocol/direct/outbound.go index 415a72f3..32c1ed8f 100644 --- a/outbound/direct.go +++ b/protocol/direct/outbound.go @@ -1,4 +1,4 @@ -package outbound +package direct import ( "context" @@ -7,6 +7,7 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -14,17 +15,20 @@ import ( "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) -var ( - _ adapter.Outbound = (*Direct)(nil) - _ N.ParallelDialer = (*Direct)(nil) -) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.DirectOutboundOptions](registry, C.TypeDirect, NewOutbound) +} -type Direct struct { - myOutboundAdapter +var _ N.ParallelDialer = (*Outbound)(nil) + +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger dialer N.Dialer domainStrategy dns.DomainStrategy fallbackDelay time.Duration @@ -33,21 +37,15 @@ type Direct struct { // loopBack *loopBackDetector } -func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (adapter.Outbound, error) { options.UDPFragmentDefault = true outboundDialer, err := dialer.New(router, options.DialerOptions) if err != nil { return nil, err } - outbound := &Direct{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeDirect, - network: []string{N.NetworkTCP, N.NetworkUDP}, - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, []string{N.NetworkTCP, N.NetworkUDP}, tag, options.DialerOptions), + logger: logger, domainStrategy: dns.DomainStrategy(options.DomainStrategy), fallbackDelay: time.Duration(options.FallbackDelay), dialer: outboundDialer, @@ -69,9 +67,9 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti return outbound, nil } -func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination switch h.overrideOption { case 1: @@ -98,9 +96,9 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M. return h.dialer.DialContext(ctx, network, destination) } -func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) { +func (h *Outbound) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination switch h.overrideOption { case 1, 2: @@ -125,9 +123,9 @@ func (h *Direct) DialParallel(ctx context.Context, network string, destination M return N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay) } -func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination originDestination := destination switch h.overrideOption { @@ -156,14 +154,14 @@ func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net return conn, nil } -/*func (h *Direct) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { +/*func (h *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { if h.loopBack.CheckConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) { return E.New("reject loopback connection to ", metadata.Destination) } return NewConnection(ctx, h, conn, metadata) } -func (h *Direct) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { +func (h *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { if h.loopBack.CheckPacketConn(metadata.Source.AddrPort(), M.AddrPortFromNet(conn.LocalAddr())) { return E.New("reject loopback packet connection to ", metadata.Destination) } diff --git a/outbound/dns.go b/protocol/dns/handle.go similarity index 83% rename from outbound/dns.go rename to protocol/dns/handle.go index d9c92f19..2f8d93ea 100644 --- a/outbound/dns.go +++ b/protocol/dns/handle.go @@ -1,15 +1,13 @@ -package outbound +package dns import ( "context" "encoding/binary" "net" - "os" - "time" "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-dns" + dns "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" @@ -21,44 +19,6 @@ import ( mDNS "github.com/miekg/dns" ) -var _ adapter.Outbound = (*DNS)(nil) - -type DNS struct { - myOutboundAdapter -} - -func NewDNS(router adapter.Router, tag string) *DNS { - return &DNS{ - myOutboundAdapter{ - protocol: C.TypeDNS, - network: []string{N.NetworkTCP, N.NetworkUDP}, - router: router, - tag: tag, - }, - } -} - -func (d *DNS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - return nil, os.ErrInvalid -} - -func (d *DNS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return nil, os.ErrInvalid -} - -// Deprecated -func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - metadata.Destination = M.Socksaddr{} - defer conn.Close() - for { - conn.SetReadDeadline(time.Now().Add(C.DNSTimeout)) - err := HandleStreamDNSRequest(ctx, d.router, conn, metadata) - if err != nil { - return err - } - } -} - func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net.Conn, metadata adapter.InboundContext) error { var queryLength uint16 err := binary.Read(conn, binary.BigEndian, &queryLength) @@ -100,11 +60,6 @@ func HandleStreamDNSRequest(ctx context.Context, router adapter.Router, conn net return nil } -// Deprecated -func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewDNSPacketConnection(ctx, d.router, conn, nil, metadata) -} - func NewDNSPacketConnection(ctx context.Context, router adapter.Router, conn N.PacketConn, cachedPackets []*N.PacketBuffer, metadata adapter.InboundContext) error { metadata.Destination = M.Socksaddr{} var reader N.PacketReader = conn diff --git a/protocol/dns/outbound.go b/protocol/dns/outbound.go new file mode 100644 index 00000000..7ce9fde2 --- /dev/null +++ b/protocol/dns/outbound.go @@ -0,0 +1,61 @@ +package dns + +import ( + "context" + "net" + "os" + "time" + + "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" + C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common/logger" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" +) + +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.StubOptions](registry, C.TypeDNS, NewOutbound) +} + +type Outbound struct { + outbound.Adapter + router adapter.Router + logger logger.ContextLogger +} + +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.StubOptions) (adapter.Outbound, error) { + return &Outbound{ + Adapter: outbound.NewAdapter(C.TypeDNS, []string{N.NetworkTCP, N.NetworkUDP}, tag, nil), + router: router, + logger: logger, + }, nil +} + +func (d *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + return nil, os.ErrInvalid +} + +func (d *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + return nil, os.ErrInvalid +} + +// Deprecated +func (d *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + metadata.Destination = M.Socksaddr{} + defer conn.Close() + for { + conn.SetReadDeadline(time.Now().Add(C.DNSTimeout)) + err := HandleStreamDNSRequest(ctx, d.router, conn, metadata) + if err != nil { + return err + } + } +} + +// Deprecated +func (d *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { + return NewDNSPacketConnection(ctx, d.router, conn, nil, metadata) +} diff --git a/outbound/selector.go b/protocol/group/selector.go similarity index 82% rename from outbound/selector.go rename to protocol/group/selector.go index 59e940df..32ab8b2a 100644 --- a/outbound/selector.go +++ b/protocol/group/selector.go @@ -1,28 +1,33 @@ -package outbound +package group import ( "context" "net" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/interrupt" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/service" ) -var ( - _ adapter.Outbound = (*Selector)(nil) - _ adapter.OutboundGroup = (*Selector)(nil) -) +func RegisterSelector(registry *outbound.Registry) { + outbound.Register[option.SelectorOutboundOptions](registry, C.TypeSelector, NewSelector) +} + +var _ adapter.OutboundGroup = (*Selector)(nil) type Selector struct { - myOutboundAdapter + outbound.Adapter ctx context.Context + router adapter.Router + logger logger.ContextLogger tags []string defaultTag string outbounds map[string]adapter.Outbound @@ -31,16 +36,12 @@ type Selector struct { interruptExternalConnections bool } -func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SelectorOutboundOptions) (*Selector, error) { +func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SelectorOutboundOptions) (adapter.Outbound, error) { outbound := &Selector{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeSelector, - router: router, - logger: logger, - tag: tag, - dependencies: options.Outbounds, - }, + Adapter: outbound.NewAdapter(C.TypeSelector, nil, tag, options.Outbounds), ctx: ctx, + router: router, + logger: logger, tags: options.Outbounds, defaultTag: options.Default, outbounds: make(map[string]adapter.Outbound), @@ -69,10 +70,10 @@ func (s *Selector) Start() error { s.outbounds[tag] = detour } - if s.tag != "" { + if s.Tag() != "" { cacheFile := service.FromContext[adapter.CacheFile](s.ctx) if cacheFile != nil { - selected := cacheFile.LoadSelected(s.tag) + selected := cacheFile.LoadSelected(s.Tag()) if selected != "" { detour, loaded := s.outbounds[selected] if loaded { @@ -113,10 +114,10 @@ func (s *Selector) SelectOutbound(tag string) bool { return true } s.selected = detour - if s.tag != "" { + if s.Tag() != "" { cacheFile := service.FromContext[adapter.CacheFile](s.ctx) if cacheFile != nil { - err := cacheFile.StoreSelected(s.tag, tag) + err := cacheFile.StoreSelected(s.Tag(), tag) if err != nil { s.logger.Error("store selected: ", err) } @@ -149,7 +150,7 @@ func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata ad if legacyHandler, ok := s.selected.(adapter.ConnectionHandler); ok { return legacyHandler.NewConnection(ctx, conn, metadata) } else { - return NewConnection(ctx, s.selected, conn, metadata) + return outbound.NewConnection(ctx, s.selected, conn, metadata) } } @@ -160,7 +161,7 @@ func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, m if legacyHandler, ok := s.selected.(adapter.PacketConnectionHandler); ok { return legacyHandler.NewPacketConnection(ctx, conn, metadata) } else { - return NewPacketConnection(ctx, s.selected, conn, metadata) + return outbound.NewPacketConnection(ctx, s.selected, conn, metadata) } } diff --git a/outbound/urltest.go b/protocol/group/urltest.go similarity index 94% rename from outbound/urltest.go rename to protocol/group/urltest.go index 564a0ddc..ccdf809d 100644 --- a/outbound/urltest.go +++ b/protocol/group/urltest.go @@ -1,4 +1,4 @@ -package outbound +package group import ( "context" @@ -7,6 +7,7 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/interrupt" "github.com/sagernet/sing-box/common/urltest" C "github.com/sagernet/sing-box/constant" @@ -22,15 +23,20 @@ import ( "github.com/sagernet/sing/service/pause" ) +func RegisterURLTest(registry *outbound.Registry) { + outbound.Register[option.URLTestOutboundOptions](registry, C.TypeURLTest, NewURLTest) +} + var ( - _ adapter.Outbound = (*URLTest)(nil) _ adapter.OutboundGroup = (*URLTest)(nil) _ adapter.InterfaceUpdateListener = (*URLTest)(nil) ) type URLTest struct { - myOutboundAdapter + outbound.Adapter ctx context.Context + router adapter.Router + logger log.ContextLogger tags []string link string interval time.Duration @@ -40,17 +46,12 @@ type URLTest struct { interruptExternalConnections bool } -func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) { +func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (adapter.Outbound, error) { outbound := &URLTest{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeURLTest, - network: []string{N.NetworkTCP, N.NetworkUDP}, - router: router, - logger: logger, - tag: tag, - dependencies: options.Outbounds, - }, + Adapter: outbound.NewAdapter(C.TypeURLTest, []string{N.NetworkTCP, N.NetworkUDP}, tag, options.Outbounds), ctx: ctx, + router: router, + logger: logger, tags: options.Outbounds, link: options.URL, interval: time.Duration(options.Interval), @@ -171,14 +172,14 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne // Deprecated func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { ctx = interrupt.ContextWithIsExternalConnection(ctx) - return NewConnection(ctx, s, conn, metadata) + return outbound.NewConnection(ctx, s, conn, metadata) } // TODO // Deprecated func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { ctx = interrupt.ContextWithIsExternalConnection(ctx) - return NewPacketConnection(ctx, s, conn, metadata) + return outbound.NewPacketConnection(ctx, s, conn, metadata) } func (s *URLTest) InterfaceUpdated() { diff --git a/outbound/http.go b/protocol/http/outbound.go similarity index 56% rename from outbound/http.go rename to protocol/http/outbound.go index 6f15afb5..4c930591 100644 --- a/outbound/http.go +++ b/protocol/http/outbound.go @@ -1,4 +1,4 @@ -package outbound +package http import ( "context" @@ -6,25 +6,30 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" sHTTP "github.com/sagernet/sing/protocol/http" ) -var _ adapter.Outbound = (*HTTP)(nil) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.HTTPOutboundOptions](registry, C.TypeHTTP, NewOutbound) +} -type HTTP struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger client *sHTTP.Client } -func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (*HTTP, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (adapter.Outbound, error) { outboundDialer, err := dialer.New(router, options.DialerOptions) if err != nil { return nil, err @@ -33,16 +38,10 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge if err != nil { return nil, err } - return &HTTP{ - myOutboundAdapter{ - protocol: C.TypeHTTP, - network: []string{N.NetworkTCP}, - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, - sHTTP.NewClient(sHTTP.Options{ + return &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeHTTP, []string{N.NetworkTCP}, tag, options.DialerOptions), + logger: logger, + client: sHTTP.NewClient(sHTTP.Options{ Dialer: detour, Server: options.ServerOptions.Build(), Username: options.Username, @@ -53,14 +52,14 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge }, nil } -func (h *HTTP) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination h.logger.InfoContext(ctx, "outbound connection to ", destination) return h.client.DialContext(ctx, network, destination) } -func (h *HTTP) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } diff --git a/outbound/hysteria.go b/protocol/hysteria/outbound.go similarity index 76% rename from outbound/hysteria.go rename to protocol/hysteria/outbound.go index f513cf64..4722f4f0 100644 --- a/outbound/hysteria.go +++ b/protocol/hysteria/outbound.go @@ -1,6 +1,4 @@ -//go:build with_quic - -package outbound +package hysteria import ( "context" @@ -8,31 +6,39 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/humanize" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-box/protocol/tuic" "github.com/sagernet/sing-quic/hysteria" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.HysteriaOutboundOptions](registry, C.TypeHysteria, NewOutbound) +} + var ( - _ adapter.Outbound = (*TUIC)(nil) - _ adapter.InterfaceUpdateListener = (*TUIC)(nil) + _ adapter.Outbound = (*tuic.Outbound)(nil) + _ adapter.InterfaceUpdateListener = (*tuic.Outbound)(nil) ) -type Hysteria struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger client *hysteria.Client } -func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (*Hysteria, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (adapter.Outbound, error) { options.UDPFragmentDefault = true if options.TLS == nil || !options.TLS.Enabled { return nil, C.ErrTLSRequired @@ -88,20 +94,14 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - return &Hysteria{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeHysteria, - network: networkList, - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, - client: client, + return &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeHysteria, networkList, tag, options.DialerOptions), + logger: logger, + client: client, }, nil } -func (h *Hysteria) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { switch N.NetworkName(network) { case N.NetworkTCP: h.logger.InfoContext(ctx, "outbound connection to ", destination) @@ -117,15 +117,15 @@ func (h *Hysteria) DialContext(ctx context.Context, network string, destination } } -func (h *Hysteria) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { h.logger.InfoContext(ctx, "outbound packet connection to ", destination) return h.client.ListenPacket(ctx, destination) } -func (h *Hysteria) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated() { h.client.CloseWithError(E.New("network changed")) } -func (h *Hysteria) Close() error { +func (h *Outbound) Close() error { return h.client.CloseWithError(os.ErrClosed) } diff --git a/outbound/hysteria2.go b/protocol/hysteria2/outbound.go similarity index 69% rename from outbound/hysteria2.go rename to protocol/hysteria2/outbound.go index 5e46f6a8..5ebc6c91 100644 --- a/outbound/hysteria2.go +++ b/protocol/hysteria2/outbound.go @@ -1,6 +1,4 @@ -//go:build with_quic - -package outbound +package hysteria2 import ( "context" @@ -8,31 +6,39 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing-box/protocol/tuic" "github.com/sagernet/sing-quic/hysteria" "github.com/sagernet/sing-quic/hysteria2" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.Hysteria2OutboundOptions](registry, C.TypeHysteria2, NewOutbound) +} + var ( - _ adapter.Outbound = (*TUIC)(nil) - _ adapter.InterfaceUpdateListener = (*TUIC)(nil) + _ adapter.Outbound = (*tuic.Outbound)(nil) + _ adapter.InterfaceUpdateListener = (*tuic.Outbound)(nil) ) -type Hysteria2 struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger client *hysteria2.Client } -func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (*Hysteria2, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (adapter.Outbound, error) { options.UDPFragmentDefault = true if options.TLS == nil || !options.TLS.Enabled { return nil, C.ErrTLSRequired @@ -74,20 +80,14 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context if err != nil { return nil, err } - return &Hysteria2{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeHysteria2, - network: networkList, - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, - client: client, + return &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeHysteria2, networkList, tag, options.DialerOptions), + logger: logger, + client: client, }, nil } -func (h *Hysteria2) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { switch N.NetworkName(network) { case N.NetworkTCP: h.logger.InfoContext(ctx, "outbound connection to ", destination) @@ -103,15 +103,15 @@ func (h *Hysteria2) DialContext(ctx context.Context, network string, destination } } -func (h *Hysteria2) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { h.logger.InfoContext(ctx, "outbound packet connection to ", destination) return h.client.ListenPacket(ctx) } -func (h *Hysteria2) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated() { h.client.CloseWithError(E.New("network changed")) } -func (h *Hysteria2) Close() error { +func (h *Outbound) Close() error { return h.client.CloseWithError(os.ErrClosed) } diff --git a/outbound/shadowsocks.go b/protocol/shadowsocks/outbound.go similarity index 80% rename from outbound/shadowsocks.go rename to protocol/shadowsocks/outbound.go index 15354274..73b38385 100644 --- a/outbound/shadowsocks.go +++ b/protocol/shadowsocks/outbound.go @@ -1,10 +1,11 @@ -package outbound +package shadowsocks import ( "context" "net" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/mux" C "github.com/sagernet/sing-box/constant" @@ -15,15 +16,19 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/uot" ) -var _ adapter.Outbound = (*Shadowsocks)(nil) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.ShadowsocksOutboundOptions](registry, C.TypeShadowsocks, NewOutbound) +} -type Shadowsocks struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger dialer N.Dialer method shadowsocks.Method serverAddr M.Socksaddr @@ -32,7 +37,7 @@ type Shadowsocks struct { multiplexDialer *mux.Client } -func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (*Shadowsocks, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksOutboundOptions) (adapter.Outbound, error) { method, err := shadowsocks.CreateMethod(ctx, options.Method, shadowsocks.MethodOptions{ Password: options.Password, }) @@ -43,15 +48,9 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte if err != nil { return nil, err } - outbound := &Shadowsocks{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeShadowsocks, - network: options.Network.Build(), - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeShadowsocks, options.Network.Build(), tag, options.DialerOptions), + logger: logger, dialer: outboundDialer, method: method, serverAddr: options.ServerOptions.Build(), @@ -78,9 +77,9 @@ func NewShadowsocks(ctx context.Context, router adapter.Router, logger log.Conte return outbound, nil } -func (h *Shadowsocks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination if h.multiplexDialer == nil { switch N.NetworkName(network) { @@ -106,9 +105,9 @@ func (h *Shadowsocks) DialContext(ctx context.Context, network string, destinati } } -func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination if h.multiplexDialer == nil { if h.uotClient != nil { @@ -125,24 +124,24 @@ func (h *Shadowsocks) ListenPacket(ctx context.Context, destination M.Socksaddr) } } -func (h *Shadowsocks) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated() { if h.multiplexDialer != nil { h.multiplexDialer.Reset() } return } -func (h *Shadowsocks) Close() error { +func (h *Outbound) Close() error { return common.Close(common.PtrOrNil(h.multiplexDialer)) } var _ N.Dialer = (*shadowsocksDialer)(nil) -type shadowsocksDialer Shadowsocks +type shadowsocksDialer Outbound func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination switch N.NetworkName(network) { case N.NetworkTCP: @@ -170,7 +169,7 @@ func (h *shadowsocksDialer) DialContext(ctx context.Context, network string, des func (h *shadowsocksDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr) if err != nil { diff --git a/outbound/shadowtls.go b/protocol/shadowtls/outbound.go similarity index 74% rename from outbound/shadowtls.go rename to protocol/shadowtls/outbound.go index ff1b9d6c..7d46a8f6 100644 --- a/outbound/shadowtls.go +++ b/protocol/shadowtls/outbound.go @@ -1,4 +1,4 @@ -package outbound +package shadowtls import ( "context" @@ -6,6 +6,7 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" @@ -17,23 +18,18 @@ import ( N "github.com/sagernet/sing/common/network" ) -var _ adapter.Outbound = (*ShadowTLS)(nil) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.ShadowTLSOutboundOptions](registry, C.TypeShadowTLS, NewOutbound) +} -type ShadowTLS struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter client *shadowtls.Client } -func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (*ShadowTLS, error) { - outbound := &ShadowTLS{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeShadowTLS, - network: []string{N.NetworkTCP}, - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowTLSOutboundOptions) (adapter.Outbound, error) { + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeShadowTLS, []string{N.NetworkTCP}, tag, options.DialerOptions), } if options.TLS == nil || !options.TLS.Enabled { return nil, C.ErrTLSRequired @@ -91,9 +87,9 @@ func NewShadowTLS(ctx context.Context, router adapter.Router, logger log.Context return outbound, nil } -func (h *ShadowTLS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination switch N.NetworkName(network) { case N.NetworkTCP: @@ -103,6 +99,6 @@ func (h *ShadowTLS) DialContext(ctx context.Context, network string, destination } } -func (h *ShadowTLS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } diff --git a/outbound/socks.go b/protocol/socks/outbound.go similarity index 65% rename from outbound/socks.go rename to protocol/socks/outbound.go index 575d6eb3..0194800a 100644 --- a/outbound/socks.go +++ b/protocol/socks/outbound.go @@ -1,10 +1,11 @@ -package outbound +package socks import ( "context" "net" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -12,22 +13,29 @@ import ( "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/uot" "github.com/sagernet/sing/protocol/socks" ) -var _ adapter.Outbound = (*Socks)(nil) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.SOCKSOutboundOptions](registry, C.TypeSOCKS, NewOutbound) +} -type Socks struct { - myOutboundAdapter +var _ adapter.Outbound = (*Outbound)(nil) + +type Outbound struct { + outbound.Adapter + router adapter.Router + logger logger.ContextLogger client *socks.Client resolve bool uotClient *uot.Client } -func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SOCKSOutboundOptions) (adapter.Outbound, error) { var version socks.Version var err error if options.Version != "" { @@ -42,15 +50,10 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio if err != nil { return nil, err } - outbound := &Socks{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeSOCKS, - network: options.Network.Build(), - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeSOCKS, options.Network.Build(), tag, options.DialerOptions), + router: router, + logger: logger, client: socks.NewClient(outboundDialer, options.ServerOptions.Build(), version, options.Username, options.Password), resolve: version == socks.Version4, } @@ -64,9 +67,9 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio return outbound, nil } -func (h *Socks) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination switch N.NetworkName(network) { case N.NetworkTCP: @@ -90,9 +93,9 @@ func (h *Socks) DialContext(ctx context.Context, network string, destination M.S return h.client.DialContext(ctx, network, destination) } -func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination if h.uotClient != nil { h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination) @@ -115,20 +118,20 @@ func (h *Socks) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. // TODO // Deprecated -func (h *Socks) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { +func (h *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { if h.resolve { - return NewDirectConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4) + return outbound.NewDirectConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4) } else { - return NewConnection(ctx, h, conn, metadata) + return outbound.NewConnection(ctx, h, conn, metadata) } } // TODO // Deprecated -func (h *Socks) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { +func (h *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { if h.resolve { - return NewDirectPacketConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4) + return outbound.NewDirectPacketConnection(ctx, h.router, h, conn, metadata, dns.DomainStrategyUseIPv4) } else { - return NewPacketConnection(ctx, h, conn, metadata) + return outbound.NewPacketConnection(ctx, h, conn, metadata) } } diff --git a/outbound/ssh.go b/protocol/ssh/outbound.go similarity index 80% rename from outbound/ssh.go rename to protocol/ssh/outbound.go index 28abe9a5..62a2a8d9 100644 --- a/outbound/ssh.go +++ b/protocol/ssh/outbound.go @@ -1,4 +1,4 @@ -package outbound +package ssh import ( "bytes" @@ -12,26 +12,30 @@ import ( "sync" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "golang.org/x/crypto/ssh" ) -var ( - _ adapter.Outbound = (*SSH)(nil) - _ adapter.InterfaceUpdateListener = (*SSH)(nil) -) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.SSHOutboundOptions](registry, C.TypeSSH, NewOutbound) +} -type SSH struct { - myOutboundAdapter +var _ adapter.InterfaceUpdateListener = (*Outbound)(nil) + +type Outbound struct { + outbound.Adapter ctx context.Context + logger logger.ContextLogger dialer N.Dialer serverAddr M.Socksaddr user string @@ -44,21 +48,15 @@ type SSH struct { client *ssh.Client } -func NewSSH(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SSHOutboundOptions) (*SSH, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SSHOutboundOptions) (adapter.Outbound, error) { outboundDialer, err := dialer.New(router, options.DialerOptions) if err != nil { return nil, err } - outbound := &SSH{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeSSH, - network: []string{N.NetworkTCP}, - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeSSH, []string{N.NetworkTCP}, tag, options.DialerOptions), ctx: ctx, + logger: logger, dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), user: options.User, @@ -122,7 +120,7 @@ func randomVersion() string { return version } -func (s *SSH) connect() (*ssh.Client, error) { +func (s *Outbound) connect() (*ssh.Client, error) { if s.client != nil { return s.client, nil } @@ -179,16 +177,16 @@ func (s *SSH) connect() (*ssh.Client, error) { return client, nil } -func (s *SSH) InterfaceUpdated() { +func (s *Outbound) InterfaceUpdated() { common.Close(s.clientConn) return } -func (s *SSH) Close() error { +func (s *Outbound) Close() error { return common.Close(s.clientConn) } -func (s *SSH) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (s *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { client, err := s.connect() if err != nil { return nil, err @@ -196,6 +194,6 @@ func (s *SSH) DialContext(ctx context.Context, network string, destination M.Soc return client.Dial(network, destination.String()) } -func (s *SSH) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (s *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } diff --git a/outbound/tor.go b/protocol/tor/outbound.go similarity index 83% rename from outbound/tor.go rename to protocol/tor/outbound.go index ccc0c0cf..89a295b8 100644 --- a/outbound/tor.go +++ b/protocol/tor/outbound.go @@ -1,4 +1,4 @@ -package outbound +package tor import ( "context" @@ -8,6 +8,7 @@ import ( "strings" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -15,6 +16,7 @@ import ( "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/rw" @@ -24,11 +26,14 @@ import ( "github.com/cretz/bine/tor" ) -var _ adapter.Outbound = (*Tor)(nil) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.TorOutboundOptions](registry, C.TypeTor, NewOutbound) +} -type Tor struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter ctx context.Context + logger logger.ContextLogger proxy *ProxyListener startConf *tor.StartConf options map[string]string @@ -37,8 +42,8 @@ type Tor struct { socksClient *socks.Client } -func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TorOutboundOptions) (*Tor, error) { - startConf := newConfig() +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TorOutboundOptions) (adapter.Outbound, error) { + var startConf tor.StartConf startConf.DataDir = os.ExpandEnv(options.DataDirectory) startConf.TempDataDirBase = os.TempDir() startConf.ExtraArgs = options.ExtraArgs @@ -74,23 +79,17 @@ func NewTor(ctx context.Context, router adapter.Router, logger log.ContextLogger if err != nil { return nil, err } - return &Tor{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeTor, - network: []string{N.NetworkTCP}, - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + return &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeTor, []string{N.NetworkTCP}, tag, options.DialerOptions), ctx: ctx, + logger: logger, proxy: NewProxyListener(ctx, logger, outboundDialer), startConf: &startConf, options: options.Options, }, nil } -func (t *Tor) Start() error { +func (t *Outbound) Start() error { err := t.start() if err != nil { t.Close() @@ -106,7 +105,7 @@ var torLogEvents = []control.EventCode{ control.EventCodeLogWarn, } -func (t *Tor) start() error { +func (t *Outbound) start() error { torInstance, err := tor.Start(t.ctx, t.startConf) if err != nil { return E.New(strings.ToLower(err.Error())) @@ -168,7 +167,7 @@ func (t *Tor) start() error { return nil } -func (t *Tor) recvLoop() { +func (t *Outbound) recvLoop() { for rawEvent := range t.events { switch event := rawEvent.(type) { case *control.LogEvent: @@ -191,7 +190,7 @@ func (t *Tor) recvLoop() { } } -func (t *Tor) Close() error { +func (t *Outbound) Close() error { err := common.Close( common.PtrOrNil(t.proxy), common.PtrOrNil(t.instance), @@ -203,11 +202,11 @@ func (t *Tor) Close() error { return err } -func (t *Tor) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (t *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { t.logger.InfoContext(ctx, "outbound connection to ", destination) return t.socksClient.DialContext(ctx, network, destination) } -func (t *Tor) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (t *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { return nil, os.ErrInvalid } diff --git a/outbound/proxy.go b/protocol/tor/proxy.go similarity index 94% rename from outbound/proxy.go rename to protocol/tor/proxy.go index 38c18453..ef60bd1f 100644 --- a/outbound/proxy.go +++ b/protocol/tor/proxy.go @@ -1,4 +1,4 @@ -package outbound +package tor import ( "context" @@ -7,6 +7,7 @@ import ( "net" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/auth" @@ -106,7 +107,7 @@ func (l *ProxyListener) NewConnection(ctx context.Context, conn net.Conn, upstre metadata.Network = N.NetworkTCP metadata.Destination = upstreamMetadata.Destination l.logger.InfoContext(ctx, "proxy connection to ", metadata.Destination) - return NewConnection(ctx, l.dialer, conn, metadata) + return outbound.NewConnection(ctx, l.dialer, conn, metadata) } func (l *ProxyListener) NewPacketConnection(ctx context.Context, conn N.PacketConn, upstreamMetadata M.Metadata) error { @@ -114,5 +115,5 @@ func (l *ProxyListener) NewPacketConnection(ctx context.Context, conn N.PacketCo metadata.Network = N.NetworkUDP metadata.Destination = upstreamMetadata.Destination l.logger.InfoContext(ctx, "proxy packet connection to ", metadata.Destination) - return NewPacketConnection(ctx, l.dialer, conn, metadata) + return outbound.NewPacketConnection(ctx, l.dialer, conn, metadata) } diff --git a/outbound/trojan.go b/protocol/trojan/outbound.go similarity index 79% rename from outbound/trojan.go rename to protocol/trojan/outbound.go index ee0b2a4b..f64c48c3 100644 --- a/outbound/trojan.go +++ b/protocol/trojan/outbound.go @@ -1,10 +1,11 @@ -package outbound +package trojan import ( "context" "net" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/tls" @@ -16,14 +17,18 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) -var _ adapter.Outbound = (*Trojan)(nil) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.TrojanOutboundOptions](registry, C.TypeTrojan, NewOutbound) +} -type Trojan struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger dialer N.Dialer serverAddr M.Socksaddr key [56]byte @@ -32,20 +37,14 @@ type Trojan struct { transport adapter.V2RayClientTransport } -func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanOutboundOptions) (*Trojan, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanOutboundOptions) (adapter.Outbound, error) { outboundDialer, err := dialer.New(router, options.DialerOptions) if err != nil { return nil, err } - outbound := &Trojan{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeTrojan, - network: options.Network.Build(), - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeTrojan, options.Network.Build(), tag, options.DialerOptions), + logger: logger, dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), key: trojan.Key(options.Password), @@ -69,7 +68,7 @@ func NewTrojan(ctx context.Context, router adapter.Router, logger log.ContextLog return outbound, nil } -func (h *Trojan) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if h.multiplexDialer == nil { switch N.NetworkName(network) { case N.NetworkTCP: @@ -89,7 +88,7 @@ func (h *Trojan) DialContext(ctx context.Context, network string, destination M. } } -func (h *Trojan) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { if h.multiplexDialer == nil { h.logger.InfoContext(ctx, "outbound packet connection to ", destination) return (*trojanDialer)(h).ListenPacket(ctx, destination) @@ -99,7 +98,7 @@ func (h *Trojan) ListenPacket(ctx context.Context, destination M.Socksaddr) (net } } -func (h *Trojan) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated() { if h.transport != nil { h.transport.Close() } @@ -109,15 +108,15 @@ func (h *Trojan) InterfaceUpdated() { return } -func (h *Trojan) Close() error { +func (h *Outbound) Close() error { return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport) } -type trojanDialer Trojan +type trojanDialer Outbound func (h *trojanDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination var conn net.Conn var err error diff --git a/outbound/tuic.go b/protocol/tuic/outbound.go similarity index 76% rename from outbound/tuic.go rename to protocol/tuic/outbound.go index aaf998b1..691d1658 100644 --- a/outbound/tuic.go +++ b/protocol/tuic/outbound.go @@ -1,6 +1,4 @@ -//go:build with_quic - -package outbound +package tuic import ( "context" @@ -9,6 +7,7 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" @@ -18,6 +17,7 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/uot" @@ -25,18 +25,20 @@ import ( "github.com/gofrs/uuid/v5" ) -var ( - _ adapter.Outbound = (*TUIC)(nil) - _ adapter.InterfaceUpdateListener = (*TUIC)(nil) -) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.TUICOutboundOptions](registry, C.TypeTUIC, NewOutbound) +} -type TUIC struct { - myOutboundAdapter +var _ adapter.InterfaceUpdateListener = (*Outbound)(nil) + +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger client *tuic.Client udpStream bool } -func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICOutboundOptions) (*TUIC, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TUICOutboundOptions) (adapter.Outbound, error) { options.UDPFragmentDefault = true if options.TLS == nil || !options.TLS.Enabled { return nil, C.ErrTLSRequired @@ -77,21 +79,15 @@ func NewTUIC(ctx context.Context, router adapter.Router, logger log.ContextLogge if err != nil { return nil, err } - return &TUIC{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeTUIC, - network: options.Network.Build(), - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + return &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeTUIC, options.Network.Build(), tag, options.DialerOptions), + logger: logger, client: client, udpStream: options.UDPOverStream, }, nil } -func (h *TUIC) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { switch N.NetworkName(network) { case N.NetworkTCP: h.logger.InfoContext(ctx, "outbound connection to ", destination) @@ -119,7 +115,7 @@ func (h *TUIC) DialContext(ctx context.Context, network string, destination M.So } } -func (h *TUIC) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { if h.udpStream { h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination) streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version)) @@ -136,10 +132,10 @@ func (h *TUIC) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.P } } -func (h *TUIC) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated() { _ = h.client.CloseWithError(E.New("network changed")) } -func (h *TUIC) Close() error { +func (h *Outbound) Close() error { return h.client.CloseWithError(os.ErrClosed) } diff --git a/outbound/vless.go b/protocol/vless/outbound.go similarity index 84% rename from outbound/vless.go rename to protocol/vless/outbound.go index 536a1e8f..1074549e 100644 --- a/outbound/vless.go +++ b/protocol/vless/outbound.go @@ -1,10 +1,11 @@ -package outbound +package vless import ( "context" "net" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/tls" @@ -17,14 +18,18 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" ) -var _ adapter.Outbound = (*VLESS)(nil) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.VLESSOutboundOptions](registry, C.TypeVLESS, NewOutbound) +} -type VLESS struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger dialer N.Dialer client *vless.Client serverAddr M.Socksaddr @@ -35,20 +40,14 @@ type VLESS struct { xudp bool } -func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (*VLESS, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (adapter.Outbound, error) { outboundDialer, err := dialer.New(router, options.DialerOptions) if err != nil { return nil, err } - outbound := &VLESS{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeVLESS, - network: options.Network.Build(), - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeVLESS, options.Network.Build(), tag, options.DialerOptions), + logger: logger, dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), } @@ -88,7 +87,7 @@ func NewVLESS(ctx context.Context, router adapter.Router, logger log.ContextLogg return outbound, nil } -func (h *VLESS) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if h.multiplexDialer == nil { switch N.NetworkName(network) { case N.NetworkTCP: @@ -108,7 +107,7 @@ func (h *VLESS) DialContext(ctx context.Context, network string, destination M.S } } -func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { if h.multiplexDialer == nil { h.logger.InfoContext(ctx, "outbound packet connection to ", destination) return (*vlessDialer)(h).ListenPacket(ctx, destination) @@ -118,7 +117,7 @@ func (h *VLESS) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. } } -func (h *VLESS) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated() { if h.transport != nil { h.transport.Close() } @@ -128,15 +127,15 @@ func (h *VLESS) InterfaceUpdated() { return } -func (h *VLESS) Close() error { +func (h *Outbound) Close() error { return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport) } -type vlessDialer VLESS +type vlessDialer Outbound func (h *vlessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination var conn net.Conn var err error @@ -179,7 +178,7 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { h.logger.InfoContext(ctx, "outbound packet connection to ", destination) ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination var conn net.Conn var err error diff --git a/outbound/vmess.go b/protocol/vmess/vmess.go similarity index 84% rename from outbound/vmess.go rename to protocol/vmess/vmess.go index 126d2fd0..759ea8ba 100644 --- a/outbound/vmess.go +++ b/protocol/vmess/vmess.go @@ -1,10 +1,11 @@ -package outbound +package vmess import ( "context" "net" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" "github.com/sagernet/sing-box/common/mux" "github.com/sagernet/sing-box/common/tls" @@ -16,15 +17,19 @@ import ( "github.com/sagernet/sing-vmess/packetaddr" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/ntp" ) -var _ adapter.Outbound = (*VMess)(nil) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.VMessOutboundOptions](registry, C.TypeVMess, NewOutbound) +} -type VMess struct { - myOutboundAdapter +type Outbound struct { + outbound.Adapter + logger logger.ContextLogger dialer N.Dialer client *vmess.Client serverAddr M.Socksaddr @@ -35,20 +40,14 @@ type VMess struct { xudp bool } -func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (*VMess, error) { +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (adapter.Outbound, error) { outboundDialer, err := dialer.New(router, options.DialerOptions) if err != nil { return nil, err } - outbound := &VMess{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeVMess, - network: options.Network.Build(), - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeVMess, options.Network.Build(), tag, options.DialerOptions), + logger: logger, dialer: outboundDialer, serverAddr: options.ServerOptions.Build(), } @@ -102,7 +101,7 @@ func NewVMess(ctx context.Context, router adapter.Router, logger log.ContextLogg return outbound, nil } -func (h *VMess) InterfaceUpdated() { +func (h *Outbound) InterfaceUpdated() { if h.transport != nil { h.transport.Close() } @@ -112,11 +111,11 @@ func (h *VMess) InterfaceUpdated() { return } -func (h *VMess) Close() error { +func (h *Outbound) Close() error { return common.Close(common.PtrOrNil(h.multiplexDialer), h.transport) } -func (h *VMess) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if h.multiplexDialer == nil { switch N.NetworkName(network) { case N.NetworkTCP: @@ -136,7 +135,7 @@ func (h *VMess) DialContext(ctx context.Context, network string, destination M.S } } -func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { if h.multiplexDialer == nil { h.logger.InfoContext(ctx, "outbound packet connection to ", destination) return (*vmessDialer)(h).ListenPacket(ctx, destination) @@ -146,11 +145,11 @@ func (h *VMess) ListenPacket(ctx context.Context, destination M.Socksaddr) (net. } } -type vmessDialer VMess +type vmessDialer Outbound func (h *vmessDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination var conn net.Conn var err error @@ -178,7 +177,7 @@ func (h *vmessDialer) DialContext(ctx context.Context, network string, destinati func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { ctx, metadata := adapter.ExtendContext(ctx) - metadata.Outbound = h.tag + metadata.Outbound = h.Tag() metadata.Destination = destination var conn net.Conn var err error diff --git a/protocol/wireguard/init.go b/protocol/wireguard/init.go new file mode 100644 index 00000000..848c113b --- /dev/null +++ b/protocol/wireguard/init.go @@ -0,0 +1,10 @@ +package wireguard + +import ( + "github.com/sagernet/sing-box/common/dialer" + "github.com/sagernet/wireguard-go/conn" +) + +func init() { + dialer.WgControlFns = conn.ControlFns +} diff --git a/outbound/wireguard.go b/protocol/wireguard/outbound.go similarity index 74% rename from outbound/wireguard.go rename to protocol/wireguard/outbound.go index 8eb043f4..7251de9e 100644 --- a/outbound/wireguard.go +++ b/protocol/wireguard/outbound.go @@ -1,6 +1,4 @@ -//go:build with_wireguard - -package outbound +package wireguard import ( "context" @@ -12,6 +10,7 @@ import ( "strings" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" @@ -21,6 +20,7 @@ import ( "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/common/x/list" @@ -30,14 +30,17 @@ import ( "github.com/sagernet/wireguard-go/device" ) -var ( - _ adapter.Outbound = (*WireGuard)(nil) - _ adapter.InterfaceUpdateListener = (*WireGuard)(nil) -) +func RegisterOutbound(registry *outbound.Registry) { + outbound.Register[option.WireGuardOutboundOptions](registry, C.TypeWireGuard, NewOutbound) +} -type WireGuard struct { - myOutboundAdapter +var _ adapter.InterfaceUpdateListener = (*Outbound)(nil) + +type Outbound struct { + outbound.Adapter ctx context.Context + router adapter.Router + logger logger.ContextLogger workers int peers []wireguard.PeerConfig useStdNetBind bool @@ -51,17 +54,12 @@ type WireGuard struct { tunDevice wireguard.Device } -func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) { - outbound := &WireGuard{ - myOutboundAdapter: myOutboundAdapter{ - protocol: C.TypeWireGuard, - network: options.Network.Build(), - router: router, - logger: logger, - tag: tag, - dependencies: withDialerDependency(options.DialerOptions), - }, +func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (adapter.Outbound, error) { + outbound := &Outbound{ + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeWireGuard, options.Network.Build(), tag, options.DialerOptions), ctx: ctx, + router: router, + logger: logger, workers: options.Workers, pauseManager: service.FromContext[pause.Manager](ctx), } @@ -111,7 +109,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context return outbound, nil } -func (w *WireGuard) Start() error { +func (w *Outbound) Start() error { if common.Any(w.peers, func(peer wireguard.PeerConfig) bool { return !peer.Endpoint.IsValid() }) { @@ -121,7 +119,7 @@ func (w *WireGuard) Start() error { return w.start() } -func (w *WireGuard) PostStart() error { +func (w *Outbound) PostStart() error { if common.All(w.peers, func(peer wireguard.PeerConfig) bool { return peer.Endpoint.IsValid() }) { @@ -130,7 +128,7 @@ func (w *WireGuard) PostStart() error { return w.start() } -func (w *WireGuard) start() error { +func (w *Outbound) start() error { err := wireguard.ResolvePeers(w.ctx, w.router, w.peers) if err != nil { return err @@ -150,7 +148,7 @@ func (w *WireGuard) start() error { connectAddr = w.peers[0].Endpoint reserved = w.peers[0].Reserved } - bind = wireguard.NewClientBind(w.ctx, w, w.listener, isConnect, connectAddr, reserved) + bind = wireguard.NewClientBind(w.ctx, w.logger, w.listener, isConnect, connectAddr, reserved) } err = w.tunDevice.Start() if err != nil { @@ -177,7 +175,7 @@ func (w *WireGuard) start() error { return nil } -func (w *WireGuard) Close() error { +func (w *Outbound) Close() error { if w.device != nil { w.device.Close() } @@ -187,12 +185,12 @@ func (w *WireGuard) Close() error { return nil } -func (w *WireGuard) InterfaceUpdated() { +func (w *Outbound) InterfaceUpdated() { w.device.BindUpdate() return } -func (w *WireGuard) onPauseUpdated(event int) { +func (w *Outbound) onPauseUpdated(event int) { switch event { case pause.EventDevicePaused: w.device.Down() @@ -201,7 +199,7 @@ func (w *WireGuard) onPauseUpdated(event int) { } } -func (w *WireGuard) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { +func (w *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { switch network { case N.NetworkTCP: w.logger.InfoContext(ctx, "outbound connection to ", destination) @@ -218,7 +216,7 @@ func (w *WireGuard) DialContext(ctx context.Context, network string, destination return w.tunDevice.DialContext(ctx, network, destination) } -func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { +func (w *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { w.logger.InfoContext(ctx, "outbound packet connection to ", destination) if destination.IsFqdn() { destinationAddresses, err := w.router.LookupDefault(ctx, destination.Fqdn) @@ -236,12 +234,12 @@ func (w *WireGuard) ListenPacket(ctx context.Context, destination M.Socksaddr) ( // TODO // Deprecated -func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { - return NewDirectConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) +func (w *Outbound) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { + return outbound.NewDirectConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) } // TODO // Deprecated -func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { - return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) +func (w *Outbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error { + return outbound.NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS) } diff --git a/route/dns.go b/route/dns.go index 34299ebf..a0c376c2 100644 --- a/route/dns.go +++ b/route/dns.go @@ -7,7 +7,7 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/outbound" + dnsOutbound "github.com/sagernet/sing-box/protocol/dns" "github.com/sagernet/sing-dns" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" @@ -22,7 +22,7 @@ func (r *Router) hijackDNSStream(ctx context.Context, conn net.Conn, metadata ad metadata.Destination = M.Socksaddr{} for { conn.SetReadDeadline(time.Now().Add(C.DNSTimeout)) - err := outbound.HandleStreamDNSRequest(ctx, r, conn, metadata) + err := dnsOutbound.HandleStreamDNSRequest(ctx, r, conn, metadata) if err != nil { return err } @@ -46,7 +46,7 @@ func (r *Router) hijackDNSPacket(ctx context.Context, conn N.PacketConn, packetB }) return } - err := outbound.NewDNSPacketConnection(ctx, r, conn, packetBuffers, metadata) + err := dnsOutbound.NewDNSPacketConnection(ctx, r, conn, packetBuffers, metadata) if err != nil && !E.IsClosedOrCanceled(err) { r.dnsLogger.ErrorContext(ctx, E.Cause(err, "process packet connection")) } diff --git a/route/route.go b/route/route.go index ebffdddd..6c68cf79 100644 --- a/route/route.go +++ b/route/route.go @@ -12,12 +12,12 @@ import ( "time" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/adapter/outbound" "github.com/sagernet/sing-box/common/conntrack" "github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/sniff" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" - "github.com/sagernet/sing-box/outbound" "github.com/sagernet/sing-box/route/rule" "github.com/sagernet/sing-dns" "github.com/sagernet/sing-mux" diff --git a/test/brutal_test.go b/test/brutal_test.go index 18aae2e2..b7499e8c 100644 --- a/test/brutal_test.go +++ b/test/brutal_test.go @@ -46,7 +46,7 @@ func TestBrutalShadowsocks(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -138,7 +138,7 @@ func TestBrutalTrojan(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -227,7 +227,7 @@ func TestBrutalVMess(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -326,7 +326,7 @@ func TestBrutalVLESS(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/direct_test.go b/test/direct_test.go index 1dbf1de1..00eeefb9 100644 --- a/test/direct_test.go +++ b/test/direct_test.go @@ -33,7 +33,7 @@ func _TestProxyProtocol(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/domain_inbound_test.go b/test/domain_inbound_test.go index 1ca2121d..16649fc4 100644 --- a/test/domain_inbound_test.go +++ b/test/domain_inbound_test.go @@ -49,7 +49,7 @@ func TestTUICDomainUDP(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/ech_test.go b/test/ech_test.go index 90eae1f4..9a70f1a5 100644 --- a/test/ech_test.go +++ b/test/ech_test.go @@ -55,7 +55,7 @@ func TestECH(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -145,7 +145,7 @@ func TestECHQUIC(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -235,7 +235,7 @@ func TestECHHysteria2(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/http_test.go b/test/http_test.go index 88385c27..8f61c3fe 100644 --- a/test/http_test.go +++ b/test/http_test.go @@ -31,7 +31,7 @@ func TestHTTPSelf(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/hysteria2_test.go b/test/hysteria2_test.go index 9ca2f5d3..2e8babe1 100644 --- a/test/hysteria2_test.go +++ b/test/hysteria2_test.go @@ -63,7 +63,7 @@ func testHysteria2Self(t *testing.T, salamanderPassword string) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -178,7 +178,7 @@ func TestHysteria2Outbound(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeHysteria2, Hysteria2Options: option.Hysteria2OutboundOptions{ diff --git a/test/hysteria_test.go b/test/hysteria_test.go index bde1b9fa..51456f93 100644 --- a/test/hysteria_test.go +++ b/test/hysteria_test.go @@ -46,7 +46,7 @@ func TestHysteriaSelf(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -160,7 +160,7 @@ func TestHysteriaOutbound(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeHysteria, HysteriaOptions: option.HysteriaOutboundOptions{ diff --git a/test/inbound_detour_test.go b/test/inbound_detour_test.go index 9505c217..ecc90082 100644 --- a/test/inbound_detour_test.go +++ b/test/inbound_detour_test.go @@ -49,7 +49,7 @@ func TestChainedInbound(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/mux_cool_test.go b/test/mux_cool_test.go index 81130fad..f6be39bb 100644 --- a/test/mux_cool_test.go +++ b/test/mux_cool_test.go @@ -92,7 +92,7 @@ func TestMuxCoolClient(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeVMess, VMessOptions: option.VMessOutboundOptions{ @@ -139,7 +139,7 @@ func TestMuxCoolSelf(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/mux_test.go b/test/mux_test.go index 8d755185..8696502c 100644 --- a/test/mux_test.go +++ b/test/mux_test.go @@ -81,7 +81,7 @@ func testShadowsocksMux(t *testing.T, options option.OutboundMultiplexOptions) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -154,7 +154,7 @@ func testVMessMux(t *testing.T, options option.OutboundMultiplexOptions) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/shadowsocks_legacy_test.go b/test/shadowsocks_legacy_test.go index 8075a7df..d59cefed 100644 --- a/test/shadowsocks_legacy_test.go +++ b/test/shadowsocks_legacy_test.go @@ -35,7 +35,7 @@ func testShadowsocksLegacy(t *testing.T, method string) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeShadowsocks, ShadowsocksOptions: option.ShadowsocksOutboundOptions{ diff --git a/test/shadowsocks_test.go b/test/shadowsocks_test.go index 4ef1ee9d..28e5d455 100644 --- a/test/shadowsocks_test.go +++ b/test/shadowsocks_test.go @@ -135,7 +135,7 @@ func testShadowsocksOutboundWithShadowsocksRust(t *testing.T, method string, pas }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeShadowsocks, ShadowsocksOptions: option.ShadowsocksOutboundOptions{ @@ -177,7 +177,7 @@ func testShadowsocksSelf(t *testing.T, method string, password string) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -244,7 +244,7 @@ func TestShadowsocksUoT(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -317,7 +317,7 @@ func testShadowsocks2022EIH(t *testing.T, method string, password string) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/shadowtls_test.go b/test/shadowtls_test.go index 6f9ee1e5..cda18498 100644 --- a/test/shadowtls_test.go +++ b/test/shadowtls_test.go @@ -80,7 +80,7 @@ func testShadowTLS(t *testing.T, version int, password string, utlsEanbled bool) }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeShadowsocks, ShadowsocksOptions: option.ShadowsocksOutboundOptions{ @@ -232,7 +232,7 @@ func TestShadowTLSInbound(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -306,7 +306,7 @@ func TestShadowTLSOutbound(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeShadowsocks, ShadowsocksOptions: option.ShadowsocksOutboundOptions{ diff --git a/test/ss_plugin_test.go b/test/ss_plugin_test.go index 94606b70..361be466 100644 --- a/test/ss_plugin_test.go +++ b/test/ss_plugin_test.go @@ -44,7 +44,7 @@ func testShadowsocksPlugin(t *testing.T, name string, opts string, args string) }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeShadowsocks, ShadowsocksOptions: option.ShadowsocksOutboundOptions{ diff --git a/test/tfo_test.go b/test/tfo_test.go index 7bd34e2d..5cc73395 100644 --- a/test/tfo_test.go +++ b/test/tfo_test.go @@ -37,7 +37,7 @@ func TestTCPSlowOpen(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/tls_test.go b/test/tls_test.go index cfc6c1a5..11d2adb0 100644 --- a/test/tls_test.go +++ b/test/tls_test.go @@ -46,7 +46,7 @@ func TestUTLS(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/trojan_test.go b/test/trojan_test.go index f88ec885..e87fed4c 100644 --- a/test/trojan_test.go +++ b/test/trojan_test.go @@ -31,7 +31,7 @@ func TestTrojanOutbound(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeTrojan, TrojanOptions: option.TrojanOutboundOptions{ @@ -92,7 +92,7 @@ func TestTrojanSelf(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -167,7 +167,7 @@ func TestTrojanPlainSelf(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/tuic_test.go b/test/tuic_test.go index 5b838f22..80b00535 100644 --- a/test/tuic_test.go +++ b/test/tuic_test.go @@ -62,7 +62,7 @@ func testTUICSelf(t *testing.T, udpStream bool, zeroRTTHandshake bool) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -171,7 +171,7 @@ func TestTUICOutbound(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeTUIC, TUICOptions: option.TUICOutboundOptions{ diff --git a/test/v2ray_api_test.go b/test/v2ray_api_test.go index 1bea41a6..abcf0e6a 100644 --- a/test/v2ray_api_test.go +++ b/test/v2ray_api_test.go @@ -26,7 +26,7 @@ func TestV2RayAPI(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, Tag: "out", diff --git a/test/v2ray_grpc_test.go b/test/v2ray_grpc_test.go index fa43f753..7c6f7532 100644 --- a/test/v2ray_grpc_test.go +++ b/test/v2ray_grpc_test.go @@ -138,7 +138,7 @@ func testV2RayGRPCOutbound(t *testing.T, forceLite bool) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeVMess, Tag: "vmess-out", diff --git a/test/v2ray_transport_test.go b/test/v2ray_transport_test.go index c7362f34..3c21bee2 100644 --- a/test/v2ray_transport_test.go +++ b/test/v2ray_transport_test.go @@ -80,7 +80,7 @@ func testVMessTransportSelf(t *testing.T, server *option.V2RayTransportOptions, }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -169,7 +169,7 @@ func testTrojanTransportSelf(t *testing.T, server *option.V2RayTransportOptions, }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -260,7 +260,7 @@ func TestVMessQUICSelf(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, @@ -340,7 +340,7 @@ func testV2RayTransportNOTLSSelf(t *testing.T, transport *option.V2RayTransportO }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/v2ray_ws_test.go b/test/v2ray_ws_test.go index 0e238c28..ec36e394 100644 --- a/test/v2ray_ws_test.go +++ b/test/v2ray_ws_test.go @@ -170,7 +170,7 @@ func testV2RayWebsocketOutbound(t *testing.T, maxEarlyData uint32, earlyDataHead }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeVMess, Tag: "vmess-out", diff --git a/test/vmess_test.go b/test/vmess_test.go index fcf7bf8f..ab5e16bc 100644 --- a/test/vmess_test.go +++ b/test/vmess_test.go @@ -240,7 +240,7 @@ func testVMessOutboundWithV2Ray(t *testing.T, security string, globalPadding boo }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeVMess, VMessOptions: option.VMessOutboundOptions{ @@ -291,7 +291,7 @@ func testVMessSelf(t *testing.T, security string, alterId int, globalPadding boo }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, diff --git a/test/wireguard_test.go b/test/wireguard_test.go index 50e87ee0..3417dadc 100644 --- a/test/wireguard_test.go +++ b/test/wireguard_test.go @@ -32,7 +32,7 @@ func _TestWireGuard(t *testing.T) { }, }, }, - Outbounds: []option.Outbound{ + LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeWireGuard, WireGuardOptions: option.WireGuardOutboundOptions{ diff --git a/transport/wireguard/client_bind.go b/transport/wireguard/client_bind.go index 6c534532..20e7c079 100644 --- a/transport/wireguard/client_bind.go +++ b/transport/wireguard/client_bind.go @@ -10,6 +10,7 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" E "github.com/sagernet/sing/common/exceptions" + "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/service" @@ -21,10 +22,10 @@ var _ conn.Bind = (*ClientBind)(nil) type ClientBind struct { ctx context.Context + logger logger.Logger pauseManager pause.Manager bindCtx context.Context bindDone context.CancelFunc - errorHandler E.Handler dialer N.Dialer reservedForEndpoint map[netip.AddrPort][3]uint8 connAccess sync.Mutex @@ -35,11 +36,11 @@ type ClientBind struct { reserved [3]uint8 } -func NewClientBind(ctx context.Context, errorHandler E.Handler, dialer N.Dialer, isConnect bool, connectAddr netip.AddrPort, reserved [3]uint8) *ClientBind { +func NewClientBind(ctx context.Context, logger logger.Logger, dialer N.Dialer, isConnect bool, connectAddr netip.AddrPort, reserved [3]uint8) *ClientBind { return &ClientBind{ ctx: ctx, + logger: logger, pauseManager: service.FromContext[pause.Manager](ctx), - errorHandler: errorHandler, dialer: dialer, reservedForEndpoint: make(map[netip.AddrPort][3]uint8), done: make(chan struct{}), @@ -115,7 +116,7 @@ func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint) return default: } - c.errorHandler.NewError(context.Background(), E.Cause(err, "connect to server")) + c.logger.Error(E.Cause(err, "connect to server")) err = nil c.pauseManager.WaitActive() time.Sleep(time.Second) @@ -127,7 +128,7 @@ func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint) select { case <-c.done: default: - c.errorHandler.NewError(context.Background(), E.Cause(err, "read packet")) + c.logger.Error(context.Background(), E.Cause(err, "read packet")) err = nil } return