瀏覽代碼

Improve config struct

世界 3 年之前
父節點
當前提交
cc78f0347d
共有 22 個文件被更改,包括 187 次插入362 次删除
  1. 1 1
      go.mod
  2. 2 2
      go.sum
  3. 2 3
      inbound/builder.go
  4. 19 0
      option/clash.go
  5. 0 10
      option/config.go
  6. 14 0
      option/direct.go
  7. 5 47
      option/dns.go
  8. 0 6
      option/experimental.go
  9. 0 116
      option/inbound.go
  10. 0 85
      option/outbound.go
  11. 10 0
      option/redir.go
  12. 4 52
      option/route.go
  13. 29 0
      option/shadowsocks.go
  14. 32 0
      option/simple.go
  15. 0 28
      option/tls.go
  16. 10 0
      option/tun.go
  17. 54 0
      option/vmess.go
  18. 2 3
      outbound/builder.go
  19. 0 3
      route/rule.go
  20. 0 3
      route/rule_dns.go
  21. 1 1
      test/go.mod
  22. 2 2
      test/go.sum

+ 1 - 1
go.mod

@@ -12,7 +12,7 @@ require (
 	github.com/gorilla/websocket v1.5.0
 	github.com/logrusorgru/aurora v2.0.3+incompatible
 	github.com/oschwald/maxminddb-golang v1.9.0
-	github.com/sagernet/sing v0.0.0-20220722081142-8311d6e9709c
+	github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3
 	github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175
 	github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f
 	github.com/sagernet/sing-tun v0.0.0-20220720051454-d35c334b46c9

+ 2 - 2
go.sum

@@ -37,8 +37,8 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sagernet/sing v0.0.0-20220722081142-8311d6e9709c h1:Y+4UoqqksclZfp7qVenaw10bQ/O5XH9JFiB7Xhfrip4=
-github.com/sagernet/sing v0.0.0-20220722081142-8311d6e9709c/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
+github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3 h1:Qm57CtqzaZ6Cq0ZDz1dX4vSNUoTYKn7qfXnpleKE0z0=
+github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
 github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175 h1:YpacS9+rDFcLG8lSkmwU21capz2gewk4LQfCE/x73U0=
 github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175/go.mod h1:2A34p89do4H4E9Ke046cJCMTdVqmvsXGWXzRwgeO2TQ=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc=

+ 2 - 3
inbound/builder.go

@@ -7,13 +7,12 @@ import (
 	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"
 )
 
 func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.Inbound) (adapter.Inbound, error) {
-	if common.IsEmptyByEquals(options) {
-		return nil, E.New("empty inbound config")
+	if options.Type == "" {
+		return nil, E.New("missing inbound type")
 	}
 	switch options.Type {
 	case C.TypeTun:

+ 19 - 0
option/clash.go

@@ -0,0 +1,19 @@
+package option
+
+type ClashAPIOptions struct {
+	ExternalController string `json:"external_controller,omitempty"`
+	ExternalUI         string `json:"external_ui,omitempty"`
+	Secret             string `json:"secret,omitempty"`
+}
+
+type SelectorOutboundOptions struct {
+	Outbounds []string `json:"outbounds"`
+	Default   string   `json:"default,omitempty"`
+}
+
+type URLTestOutboundOptions struct {
+	Outbounds []string `json:"outbounds"`
+	URL       string   `json:"url,omitempty"`
+	Interval  Duration `json:"interval,omitempty"`
+	Tolerance uint16   `json:"tolerance,omitempty"`
+}

+ 0 - 10
option/config.go

@@ -5,7 +5,6 @@ import (
 	"strings"
 
 	"github.com/sagernet/sing-box/common/json"
-	"github.com/sagernet/sing/common"
 	E "github.com/sagernet/sing/common/exceptions"
 )
 
@@ -36,15 +35,6 @@ func (o *Options) UnmarshalJSON(content []byte) error {
 	return err
 }
 
-func (o Options) Equals(other Options) bool {
-	return common.ComparablePtrEquals(o.Log, other.Log) &&
-		common.PtrEquals(o.DNS, other.DNS) &&
-		common.SliceEquals(o.Inbounds, other.Inbounds) &&
-		common.SliceEquals(o.Outbounds, other.Outbounds) &&
-		common.PtrEquals(o.Route, other.Route) &&
-		common.ComparablePtrEquals(o.Experimental, other.Experimental)
-}
-
 type LogOptions struct {
 	Disabled     bool   `json:"disabled,omitempty"`
 	Level        string `json:"level,omitempty"`

+ 14 - 0
option/direct.go

@@ -0,0 +1,14 @@
+package option
+
+type DirectInboundOptions struct {
+	ListenOptions
+	Network         NetworkList `json:"network,omitempty"`
+	OverrideAddress string      `json:"override_address,omitempty"`
+	OverridePort    uint16      `json:"override_port,omitempty"`
+}
+
+type DirectOutboundOptions struct {
+	OutboundDialerOptions
+	OverrideAddress string `json:"override_address,omitempty"`
+	OverridePort    uint16 `json:"override_port,omitempty"`
+}

+ 5 - 47
option/dns.go

@@ -1,6 +1,8 @@
 package option
 
 import (
+	"reflect"
+
 	"github.com/sagernet/sing-box/common/json"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing/common"
@@ -14,13 +16,6 @@ type DNSOptions struct {
 	DNSClientOptions
 }
 
-func (o DNSOptions) Equals(other DNSOptions) bool {
-	return common.ComparableSliceEquals(o.Servers, other.Servers) &&
-		common.SliceEquals(o.Rules, other.Rules) &&
-		o.Final == other.Final &&
-		o.DNSClientOptions == other.DNSClientOptions
-}
-
 type DNSClientOptions struct {
 	Strategy      DomainStrategy `json:"strategy,omitempty"`
 	DisableCache  bool           `json:"disable_cache,omitempty"`
@@ -44,12 +39,6 @@ type _DNSRule struct {
 
 type DNSRule _DNSRule
 
-func (r DNSRule) Equals(other DNSRule) bool {
-	return r.Type == other.Type &&
-		r.DefaultOptions.Equals(other.DefaultOptions) &&
-		r.LogicalOptions.Equals(other.LogicalOptions)
-}
-
 func (r DNSRule) MarshalJSON() ([]byte, error) {
 	var v any
 	switch r.Type {
@@ -116,33 +105,10 @@ type DefaultDNSRule struct {
 
 func (r DefaultDNSRule) IsValid() bool {
 	var defaultValue DefaultDNSRule
+	defaultValue.Invert = r.Invert
 	defaultValue.Server = r.Server
-	return !r.Equals(defaultValue)
-}
-
-func (r DefaultDNSRule) Equals(other DefaultDNSRule) bool {
-	return common.ComparableSliceEquals(r.Inbound, other.Inbound) &&
-		r.Network == other.Network &&
-		common.ComparableSliceEquals(r.User, other.User) &&
-		common.ComparableSliceEquals(r.Protocol, other.Protocol) &&
-		common.ComparableSliceEquals(r.Domain, other.Domain) &&
-		common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) &&
-		common.ComparableSliceEquals(r.DomainKeyword, other.DomainKeyword) &&
-		common.ComparableSliceEquals(r.DomainRegex, other.DomainRegex) &&
-		common.ComparableSliceEquals(r.Geosite, other.Geosite) &&
-		common.ComparableSliceEquals(r.SourceGeoIP, other.SourceGeoIP) &&
-		common.ComparableSliceEquals(r.SourceIPCIDR, other.SourceIPCIDR) &&
-		common.ComparableSliceEquals(r.SourcePort, other.SourcePort) &&
-		common.ComparableSliceEquals(r.SourcePortRange, other.SourcePortRange) &&
-		common.ComparableSliceEquals(r.Port, other.Port) &&
-		common.ComparableSliceEquals(r.PortRange, other.PortRange) &&
-		common.ComparableSliceEquals(r.ProcessName, other.ProcessName) &&
-		common.ComparableSliceEquals(r.UserID, other.UserID) &&
-		common.ComparableSliceEquals(r.PackageName, other.PackageName) &&
-		common.ComparableSliceEquals(r.Outbound, other.Outbound) &&
-		r.Invert == other.Invert &&
-		r.Server == other.Server &&
-		r.DisableCache == other.DisableCache
+	defaultValue.DisableCache = r.DisableCache
+	return !reflect.DeepEqual(r, defaultValue)
 }
 
 type LogicalDNSRule struct {
@@ -156,11 +122,3 @@ type LogicalDNSRule struct {
 func (r LogicalDNSRule) IsValid() bool {
 	return len(r.Rules) > 0 && common.All(r.Rules, DefaultDNSRule.IsValid)
 }
-
-func (r LogicalDNSRule) Equals(other LogicalDNSRule) bool {
-	return r.Mode == other.Mode &&
-		common.SliceEquals(r.Rules, other.Rules) &&
-		r.Invert == other.Invert &&
-		r.Server == other.Server &&
-		r.DisableCache == other.DisableCache
-}

+ 0 - 6
option/experimental.go

@@ -3,9 +3,3 @@ package option
 type ExperimentalOptions struct {
 	ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"`
 }
-
-type ClashAPIOptions struct {
-	ExternalController string `json:"external_controller,omitempty"`
-	ExternalUI         string `json:"external_ui,omitempty"`
-	Secret             string `json:"secret,omitempty"`
-}

+ 0 - 116
option/inbound.go

@@ -3,8 +3,6 @@ package option
 import (
 	"github.com/sagernet/sing-box/common/json"
 	C "github.com/sagernet/sing-box/constant"
-	"github.com/sagernet/sing/common"
-	"github.com/sagernet/sing/common/auth"
 	E "github.com/sagernet/sing/common/exceptions"
 )
 
@@ -24,20 +22,6 @@ type _Inbound struct {
 
 type Inbound _Inbound
 
-func (h Inbound) Equals(other Inbound) bool {
-	return h.Type == other.Type &&
-		h.Tag == other.Tag &&
-		h.TunOptions == other.TunOptions &&
-		h.RedirectOptions == other.RedirectOptions &&
-		h.TProxyOptions == other.TProxyOptions &&
-		h.DirectOptions == other.DirectOptions &&
-		h.SocksOptions.Equals(other.SocksOptions) &&
-		h.HTTPOptions.Equals(other.HTTPOptions) &&
-		h.MixedOptions.Equals(other.MixedOptions) &&
-		h.ShadowsocksOptions.Equals(other.ShadowsocksOptions) &&
-		h.VMessOptions.Equals(other.VMessOptions)
-}
-
 func (h Inbound) MarshalJSON() ([]byte, error) {
 	var v any
 	switch h.Type {
@@ -113,103 +97,3 @@ type ListenOptions struct {
 	UDPTimeout  int64         `json:"udp_timeout,omitempty"`
 	InboundOptions
 }
-
-type SocksInboundOptions struct {
-	ListenOptions
-	Users []auth.User `json:"users,omitempty"`
-}
-
-func (o SocksInboundOptions) Equals(other SocksInboundOptions) bool {
-	return o.ListenOptions == other.ListenOptions &&
-		common.ComparableSliceEquals(o.Users, other.Users)
-}
-
-type HTTPMixedInboundOptions struct {
-	ListenOptions
-	Users          []auth.User        `json:"users,omitempty"`
-	SetSystemProxy bool               `json:"set_system_proxy,omitempty"`
-	TLS            *InboundTLSOptions `json:"tls,omitempty"`
-}
-
-func (o HTTPMixedInboundOptions) Equals(other HTTPMixedInboundOptions) bool {
-	return o.ListenOptions == other.ListenOptions &&
-		common.ComparableSliceEquals(o.Users, other.Users) &&
-		o.SetSystemProxy == other.SetSystemProxy &&
-		common.PtrEquals(o.TLS, other.TLS)
-}
-
-type DirectInboundOptions struct {
-	ListenOptions
-	Network         NetworkList `json:"network,omitempty"`
-	OverrideAddress string      `json:"override_address,omitempty"`
-	OverridePort    uint16      `json:"override_port,omitempty"`
-}
-
-type ShadowsocksInboundOptions struct {
-	ListenOptions
-	Network      NetworkList              `json:"network,omitempty"`
-	Method       string                   `json:"method"`
-	Password     string                   `json:"password"`
-	Users        []ShadowsocksUser        `json:"users,omitempty"`
-	Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
-}
-
-func (o ShadowsocksInboundOptions) Equals(other ShadowsocksInboundOptions) bool {
-	return o.ListenOptions == other.ListenOptions &&
-		o.Network == other.Network &&
-		o.Method == other.Method &&
-		o.Password == other.Password &&
-		common.ComparableSliceEquals(o.Users, other.Users) &&
-		common.ComparableSliceEquals(o.Destinations, other.Destinations)
-}
-
-type ShadowsocksUser struct {
-	Name     string `json:"name"`
-	Password string `json:"password"`
-}
-
-type ShadowsocksDestination struct {
-	Name     string `json:"name"`
-	Password string `json:"password"`
-	ServerOptions
-}
-
-type VMessInboundOptions struct {
-	ListenOptions
-	Users []VMessUser        `json:"users,omitempty"`
-	TLS   *InboundTLSOptions `json:"tls,omitempty"`
-}
-
-func (o VMessInboundOptions) Equals(other VMessInboundOptions) bool {
-	return o.ListenOptions == other.ListenOptions &&
-		common.ComparableSliceEquals(o.Users, other.Users) &&
-		common.PtrEquals(o.TLS, other.TLS)
-}
-
-type VMessUser struct {
-	Name string `json:"name"`
-	UUID string `json:"uuid"`
-}
-
-type TunInboundOptions struct {
-	InterfaceName string        `json:"interface_name,omitempty"`
-	MTU           uint32        `json:"mtu,omitempty"`
-	Inet4Address  *ListenPrefix `json:"inet4_address,omitempty"`
-	Inet6Address  *ListenPrefix `json:"inet6_address,omitempty"`
-	AutoRoute     bool          `json:"auto_route,omitempty"`
-	InboundOptions
-}
-
-type RedirectInboundOptions struct {
-	ListenOptions
-}
-
-type TProxyInboundOptions struct {
-	ListenOptions
-	Network NetworkList `json:"network,omitempty"`
-}
-
-type DNSInboundOptions struct {
-	ListenOptions
-	Network NetworkList `json:"network,omitempty"`
-}

+ 0 - 85
option/outbound.go

@@ -3,7 +3,6 @@ package option
 import (
 	"github.com/sagernet/sing-box/common/json"
 	C "github.com/sagernet/sing-box/constant"
-	"github.com/sagernet/sing/common"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 )
@@ -22,18 +21,6 @@ type _Outbound struct {
 
 type Outbound _Outbound
 
-func (h Outbound) Equals(other Outbound) bool {
-	return h.Type == other.Type &&
-		h.Tag == other.Tag &&
-		h.DirectOptions == other.DirectOptions &&
-		h.SocksOptions == other.SocksOptions &&
-		h.HTTPOptions == other.HTTPOptions &&
-		h.ShadowsocksOptions == other.ShadowsocksOptions &&
-		h.VMessOptions == other.VMessOptions &&
-		common.Equals(h.SelectorOptions, other.SelectorOptions) &&
-		common.Equals(h.URLTestOptions, other.URLTestOptions)
-}
-
 func (h Outbound) MarshalJSON() ([]byte, error) {
 	var v any
 	switch h.Type {
@@ -116,75 +103,3 @@ type ServerOptions struct {
 func (o ServerOptions) Build() M.Socksaddr {
 	return M.ParseSocksaddrHostPort(o.Server, o.ServerPort)
 }
-
-type DirectOutboundOptions struct {
-	OutboundDialerOptions
-	OverrideAddress string `json:"override_address,omitempty"`
-	OverridePort    uint16 `json:"override_port,omitempty"`
-}
-
-type SocksOutboundOptions struct {
-	OutboundDialerOptions
-	ServerOptions
-	Version  string      `json:"version,omitempty"`
-	Username string      `json:"username,omitempty"`
-	Password string      `json:"password,omitempty"`
-	Network  NetworkList `json:"network,omitempty"`
-}
-
-type HTTPOutboundOptions struct {
-	OutboundDialerOptions
-	ServerOptions
-	Username   string              `json:"username,omitempty"`
-	Password   string              `json:"password,omitempty"`
-	TLSOptions *OutboundTLSOptions `json:"tls,omitempty"`
-}
-
-type ShadowsocksOutboundOptions struct {
-	OutboundDialerOptions
-	ServerOptions
-	Method   string      `json:"method"`
-	Password string      `json:"password"`
-	Network  NetworkList `json:"network,omitempty"`
-}
-
-type VMessOutboundOptions struct {
-	OutboundDialerOptions
-	ServerOptions
-	UUID                string                 `json:"uuid"`
-	Security            string                 `json:"security"`
-	AlterId             int                    `json:"alter_id,omitempty"`
-	GlobalPadding       bool                   `json:"global_padding,omitempty"`
-	AuthenticatedLength bool                   `json:"authenticated_length,omitempty"`
-	Network             NetworkList            `json:"network,omitempty"`
-	TLSOptions          *OutboundTLSOptions    `json:"tls,omitempty"`
-	TransportOptions    *VMessTransportOptions `json:"transport,omitempty"`
-}
-
-type VMessTransportOptions struct {
-	Network string `json:"network,omitempty"`
-}
-
-type SelectorOutboundOptions struct {
-	Outbounds []string `json:"outbounds"`
-	Default   string   `json:"default,omitempty"`
-}
-
-func (o SelectorOutboundOptions) Equals(other SelectorOutboundOptions) bool {
-	return common.ComparableSliceEquals(o.Outbounds, other.Outbounds) &&
-		o.Default == other.Default
-}
-
-type URLTestOutboundOptions struct {
-	Outbounds []string `json:"outbounds"`
-	URL       string   `json:"url,omitempty"`
-	Interval  Duration `json:"interval,omitempty"`
-	Tolerance uint16   `json:"tolerance,omitempty"`
-}
-
-func (o URLTestOutboundOptions) Equals(other URLTestOutboundOptions) bool {
-	return common.ComparableSliceEquals(o.Outbounds, other.Outbounds) &&
-		o.URL == other.URL &&
-		o.Interval == other.Interval &&
-		o.Tolerance == other.Tolerance
-}

+ 10 - 0
option/redir.go

@@ -0,0 +1,10 @@
+package option
+
+type RedirectInboundOptions struct {
+	ListenOptions
+}
+
+type TProxyInboundOptions struct {
+	ListenOptions
+	Network NetworkList `json:"network,omitempty"`
+}

+ 4 - 52
option/route.go

@@ -1,6 +1,8 @@
 package option
 
 import (
+	"reflect"
+
 	"github.com/sagernet/sing-box/common/json"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing/common"
@@ -18,17 +20,6 @@ type RouteOptions struct {
 	DefaultMark         int             `json:"default_mark,omitempty"`
 }
 
-func (o RouteOptions) Equals(other RouteOptions) bool {
-	return common.ComparablePtrEquals(o.GeoIP, other.GeoIP) &&
-		common.ComparablePtrEquals(o.Geosite, other.Geosite) &&
-		common.SliceEquals(o.Rules, other.Rules) &&
-		o.Final == other.Final &&
-		o.FindProcess == other.FindProcess &&
-		o.AutoDetectInterface == other.AutoDetectInterface &&
-		o.DefaultInterface == other.DefaultInterface &&
-		o.DefaultMark == other.DefaultMark
-}
-
 type GeoIPOptions struct {
 	Path           string `json:"path,omitempty"`
 	DownloadURL    string `json:"download_url,omitempty"`
@@ -49,12 +40,6 @@ type _Rule struct {
 
 type Rule _Rule
 
-func (r Rule) Equals(other Rule) bool {
-	return r.Type == other.Type &&
-		r.DefaultOptions.Equals(other.DefaultOptions) &&
-		r.LogicalOptions.Equals(other.LogicalOptions)
-}
-
 func (r Rule) MarshalJSON() ([]byte, error) {
 	var v any
 	switch r.Type {
@@ -120,35 +105,9 @@ type DefaultRule struct {
 
 func (r DefaultRule) IsValid() bool {
 	var defaultValue DefaultRule
+	defaultValue.Invert = r.Invert
 	defaultValue.Outbound = r.Outbound
-	return !r.Equals(defaultValue)
-}
-
-func (r DefaultRule) Equals(other DefaultRule) bool {
-	return common.ComparableSliceEquals(r.Inbound, other.Inbound) &&
-		r.IPVersion == other.IPVersion &&
-		r.Network == other.Network &&
-		common.ComparableSliceEquals(r.User, other.User) &&
-		common.ComparableSliceEquals(r.Protocol, other.Protocol) &&
-		common.ComparableSliceEquals(r.Domain, other.Domain) &&
-		common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) &&
-		common.ComparableSliceEquals(r.DomainKeyword, other.DomainKeyword) &&
-		common.ComparableSliceEquals(r.DomainRegex, other.DomainRegex) &&
-		common.ComparableSliceEquals(r.Geosite, other.Geosite) &&
-		common.ComparableSliceEquals(r.SourceGeoIP, other.SourceGeoIP) &&
-		common.ComparableSliceEquals(r.GeoIP, other.GeoIP) &&
-		common.ComparableSliceEquals(r.SourceIPCIDR, other.SourceIPCIDR) &&
-		common.ComparableSliceEquals(r.IPCIDR, other.IPCIDR) &&
-		common.ComparableSliceEquals(r.SourcePort, other.SourcePort) &&
-		common.ComparableSliceEquals(r.SourcePortRange, other.SourcePortRange) &&
-		common.ComparableSliceEquals(r.Port, other.Port) &&
-		common.ComparableSliceEquals(r.PortRange, other.PortRange) &&
-		common.ComparableSliceEquals(r.ProcessName, other.ProcessName) &&
-		common.ComparableSliceEquals(r.PackageName, other.PackageName) &&
-		common.ComparableSliceEquals(r.User, other.User) &&
-		common.ComparableSliceEquals(r.UserID, other.UserID) &&
-		r.Invert == other.Invert &&
-		r.Outbound == other.Outbound
+	return !reflect.DeepEqual(r, defaultValue)
 }
 
 type LogicalRule struct {
@@ -161,10 +120,3 @@ type LogicalRule struct {
 func (r LogicalRule) IsValid() bool {
 	return len(r.Rules) > 0 && common.All(r.Rules, DefaultRule.IsValid)
 }
-
-func (r LogicalRule) Equals(other LogicalRule) bool {
-	return r.Mode == other.Mode &&
-		common.SliceEquals(r.Rules, other.Rules) &&
-		r.Invert == other.Invert &&
-		r.Outbound == other.Outbound
-}

+ 29 - 0
option/shadowsocks.go

@@ -0,0 +1,29 @@
+package option
+
+type ShadowsocksInboundOptions struct {
+	ListenOptions
+	Network      NetworkList              `json:"network,omitempty"`
+	Method       string                   `json:"method"`
+	Password     string                   `json:"password"`
+	Users        []ShadowsocksUser        `json:"users,omitempty"`
+	Destinations []ShadowsocksDestination `json:"destinations,omitempty"`
+}
+
+type ShadowsocksUser struct {
+	Name     string `json:"name"`
+	Password string `json:"password"`
+}
+
+type ShadowsocksDestination struct {
+	Name     string `json:"name"`
+	Password string `json:"password"`
+	ServerOptions
+}
+
+type ShadowsocksOutboundOptions struct {
+	OutboundDialerOptions
+	ServerOptions
+	Method   string      `json:"method"`
+	Password string      `json:"password"`
+	Network  NetworkList `json:"network,omitempty"`
+}

+ 32 - 0
option/simple.go

@@ -0,0 +1,32 @@
+package option
+
+import "github.com/sagernet/sing/common/auth"
+
+type SocksInboundOptions struct {
+	ListenOptions
+	Users []auth.User `json:"users,omitempty"`
+}
+
+type HTTPMixedInboundOptions struct {
+	ListenOptions
+	Users          []auth.User        `json:"users,omitempty"`
+	SetSystemProxy bool               `json:"set_system_proxy,omitempty"`
+	TLS            *InboundTLSOptions `json:"tls,omitempty"`
+}
+
+type SocksOutboundOptions struct {
+	OutboundDialerOptions
+	ServerOptions
+	Version  string      `json:"version,omitempty"`
+	Username string      `json:"username,omitempty"`
+	Password string      `json:"password,omitempty"`
+	Network  NetworkList `json:"network,omitempty"`
+}
+
+type HTTPOutboundOptions struct {
+	OutboundDialerOptions
+	ServerOptions
+	Username   string              `json:"username,omitempty"`
+	Password   string              `json:"password,omitempty"`
+	TLSOptions *OutboundTLSOptions `json:"tls,omitempty"`
+}

+ 0 - 28
option/tls.go

@@ -3,7 +3,6 @@ package option
 import (
 	"crypto/tls"
 
-	"github.com/sagernet/sing/common"
 	E "github.com/sagernet/sing/common/exceptions"
 )
 
@@ -20,19 +19,6 @@ type InboundTLSOptions struct {
 	KeyPath         string   `json:"key_path,omitempty"`
 }
 
-func (o InboundTLSOptions) Equals(other InboundTLSOptions) bool {
-	return o.Enabled == other.Enabled &&
-		o.ServerName == other.ServerName &&
-		common.ComparableSliceEquals(o.ALPN, other.ALPN) &&
-		o.MinVersion == other.MinVersion &&
-		o.MaxVersion == other.MaxVersion &&
-		common.ComparableSliceEquals(o.CipherSuites, other.CipherSuites) &&
-		o.Certificate == other.Certificate &&
-		o.CertificatePath == other.CertificatePath &&
-		o.Key == other.Key &&
-		o.KeyPath == other.KeyPath
-}
-
 type OutboundTLSOptions struct {
 	Enabled           bool     `json:"enabled,omitempty"`
 	DisableSNI        bool     `json:"disable_sni,omitempty"`
@@ -47,20 +33,6 @@ type OutboundTLSOptions struct {
 	CertificatePath   string   `json:"certificate_path,omitempty"`
 }
 
-func (o OutboundTLSOptions) Equals(other OutboundTLSOptions) bool {
-	return o.Enabled == other.Enabled &&
-		o.DisableSNI == other.DisableSNI &&
-		o.ServerName == other.ServerName &&
-		o.Insecure == other.Insecure &&
-		common.ComparableSliceEquals(o.ALPN, other.ALPN) &&
-		o.MinVersion == other.MinVersion &&
-		o.MaxVersion == other.MaxVersion &&
-		common.ComparableSliceEquals(o.CipherSuites, other.CipherSuites) &&
-		o.DisableSystemRoot == other.DisableSystemRoot &&
-		o.Certificate == other.Certificate &&
-		o.CertificatePath == other.CertificatePath
-}
-
 func ParseTLSVersion(version string) (uint16, error) {
 	switch version {
 	case "1.0":

+ 10 - 0
option/tun.go

@@ -0,0 +1,10 @@
+package option
+
+type TunInboundOptions struct {
+	InterfaceName string        `json:"interface_name,omitempty"`
+	MTU           uint32        `json:"mtu,omitempty"`
+	Inet4Address  *ListenPrefix `json:"inet4_address,omitempty"`
+	Inet6Address  *ListenPrefix `json:"inet6_address,omitempty"`
+	AutoRoute     bool          `json:"auto_route,omitempty"`
+	InboundOptions
+}

+ 54 - 0
option/vmess.go

@@ -0,0 +1,54 @@
+package option
+
+import (
+	E "github.com/sagernet/sing/common/exceptions"
+)
+
+type VMessInboundOptions struct {
+	ListenOptions
+	Users []VMessUser        `json:"users,omitempty"`
+	TLS   *InboundTLSOptions `json:"tls,omitempty"`
+}
+
+type VMessUser struct {
+	Name string `json:"name"`
+	UUID string `json:"uuid"`
+}
+
+type VMessOutboundOptions struct {
+	OutboundDialerOptions
+	ServerOptions
+	UUID                string                         `json:"uuid"`
+	Security            string                         `json:"security"`
+	AlterId             int                            `json:"alter_id,omitempty"`
+	GlobalPadding       bool                           `json:"global_padding,omitempty"`
+	AuthenticatedLength bool                           `json:"authenticated_length,omitempty"`
+	Network             NetworkList                    `json:"network,omitempty"`
+	TLSOptions          *OutboundTLSOptions            `json:"tls,omitempty"`
+	TransportOptions    *VMessOutboundTransportOptions `json:"transport,omitempty"`
+}
+
+type _VMessOutboundTransportOptions struct {
+	Type        string                    `json:"network,omitempty"`
+	HTTPOptions *VMessOutboundHTTPOptions `json:"-"`
+}
+
+type VMessOutboundTransportOptions _VMessOutboundTransportOptions
+
+func (o VMessOutboundTransportOptions) MarshalJSON() ([]byte, error) {
+	var v any
+	switch o.Type {
+	case "http":
+		v = o.HTTPOptions
+	default:
+		return nil, E.New("unknown transport type: ", o.Type)
+	}
+	return MarshallObjects(_VMessOutboundTransportOptions(o), v)
+}
+
+type VMessOutboundHTTPOptions struct {
+	Method  string            `json:"method,omitempty"`
+	Host    string            `json:"host,omitempty"`
+	Path    []string          `proxy:"path,omitempty"`
+	Headers map[string]string `proxy:"headers,omitempty"`
+}

+ 2 - 3
outbound/builder.go

@@ -5,13 +5,12 @@ import (
 	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"
 )
 
 func New(router adapter.Router, logger log.ContextLogger, options option.Outbound) (adapter.Outbound, error) {
-	if common.IsEmptyByEquals(options) {
-		return nil, E.New("empty outbound config")
+	if options.Type == "" {
+		return nil, E.New("missing outbound type")
 	}
 	switch options.Type {
 	case C.TypeDirect:

+ 0 - 3
route/rule.go

@@ -13,9 +13,6 @@ import (
 )
 
 func NewRule(router adapter.Router, logger log.ContextLogger, options option.Rule) (adapter.Rule, error) {
-	if common.IsEmptyByEquals(options) {
-		return nil, E.New("empty rule config")
-	}
 	switch options.Type {
 	case "", C.RuleTypeDefault:
 		if !options.DefaultOptions.IsValid() {

+ 0 - 3
route/rule_dns.go

@@ -13,9 +13,6 @@ import (
 )
 
 func NewDNSRule(router adapter.Router, logger log.ContextLogger, options option.DNSRule) (adapter.DNSRule, error) {
-	if common.IsEmptyByEquals(options) {
-		return nil, E.New("empty rule config")
-	}
 	switch options.Type {
 	case "", C.RuleTypeDefault:
 		if !options.DefaultOptions.IsValid() {

+ 1 - 1
test/go.mod

@@ -10,7 +10,7 @@ require (
 	github.com/docker/docker v20.10.17+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/gofrs/uuid v4.2.0+incompatible
-	github.com/sagernet/sing v0.0.0-20220722081142-8311d6e9709c
+	github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3
 	github.com/spyzhov/ajson v0.7.1
 	github.com/stretchr/testify v1.8.0
 	golang.org/x/net v0.0.0-20220722155237-a158d28d115b

+ 2 - 2
test/go.sum

@@ -64,8 +64,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/sagernet/sing v0.0.0-20220722081142-8311d6e9709c h1:Y+4UoqqksclZfp7qVenaw10bQ/O5XH9JFiB7Xhfrip4=
-github.com/sagernet/sing v0.0.0-20220722081142-8311d6e9709c/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
+github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3 h1:Qm57CtqzaZ6Cq0ZDz1dX4vSNUoTYKn7qfXnpleKE0z0=
+github.com/sagernet/sing v0.0.0-20220725031620-096223b346f3/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
 github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175 h1:YpacS9+rDFcLG8lSkmwU21capz2gewk4LQfCE/x73U0=
 github.com/sagernet/sing-dns v0.0.0-20220724053927-eb8d0d542175/go.mod h1:2A34p89do4H4E9Ke046cJCMTdVqmvsXGWXzRwgeO2TQ=
 github.com/sagernet/sing-shadowsocks v0.0.0-20220717063942-45a2ad9cd41f h1:F6yiuKbBoXgWiuoP7R0YA14pDEl3emxA1mL7M16Q7gc=