mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 08:31:30 +00:00
Invalid config check
This commit is contained in:
parent
6eae8e361f
commit
30444057bd
|
@ -8,10 +8,14 @@ import (
|
|||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
func New(ctx context.Context, router adapter.Router, logger log.Logger, index int, options option.Inbound) (adapter.Inbound, error) {
|
||||
if common.IsEmptyByEquals(options) {
|
||||
return nil, E.New("empty inbound config")
|
||||
}
|
||||
var tag string
|
||||
if options.Tag != "" {
|
||||
tag = options.Tag
|
||||
|
@ -21,16 +25,16 @@ func New(ctx context.Context, router adapter.Router, logger log.Logger, index in
|
|||
inboundLogger := logger.WithPrefix(F.ToString("inbound/", options.Type, "[", tag, "]: "))
|
||||
switch options.Type {
|
||||
case C.TypeDirect:
|
||||
return NewDirect(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.DirectOptions)), nil
|
||||
return NewDirect(ctx, router, inboundLogger, options.Tag, options.DirectOptions), nil
|
||||
case C.TypeSocks:
|
||||
return NewSocks(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.SocksOptions)), nil
|
||||
return NewSocks(ctx, router, inboundLogger, options.Tag, options.SocksOptions), nil
|
||||
case C.TypeHTTP:
|
||||
return NewHTTP(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.HTTPOptions)), nil
|
||||
return NewHTTP(ctx, router, inboundLogger, options.Tag, options.HTTPOptions), nil
|
||||
case C.TypeMixed:
|
||||
return NewMixed(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.MixedOptions)), nil
|
||||
return NewMixed(ctx, router, inboundLogger, options.Tag, options.MixedOptions), nil
|
||||
case C.TypeShadowsocks:
|
||||
return NewShadowsocks(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.ShadowsocksOptions))
|
||||
return NewShadowsocks(ctx, router, inboundLogger, options.Tag, options.ShadowsocksOptions)
|
||||
default:
|
||||
panic(F.ToString("unknown inbound type: ", options.Type))
|
||||
return nil, E.New("unknown inbound type: ", options.Type)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,14 @@ import (
|
|||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
)
|
||||
|
||||
func New(router adapter.Router, logger log.Logger, index int, options option.Outbound) (adapter.Outbound, error) {
|
||||
if common.IsEmpty(options) {
|
||||
return nil, E.New("empty outbound config")
|
||||
}
|
||||
var tag string
|
||||
if options.Tag != "" {
|
||||
tag = options.Tag
|
||||
|
@ -19,10 +23,10 @@ func New(router adapter.Router, logger log.Logger, index int, options option.Out
|
|||
outboundLogger := logger.WithPrefix(F.ToString("outbound/", options.Type, "[", tag, "]: "))
|
||||
switch options.Type {
|
||||
case C.TypeDirect:
|
||||
return NewDirect(router, outboundLogger, options.Tag, common.PtrValueOrDefault(options.DirectOptions)), nil
|
||||
return NewDirect(router, outboundLogger, options.Tag, options.DirectOptions), nil
|
||||
case C.TypeShadowsocks:
|
||||
return NewShadowsocks(router, outboundLogger, options.Tag, common.PtrValueOrDefault(options.ShadowsocksOptions))
|
||||
return NewShadowsocks(router, outboundLogger, options.Tag, options.ShadowsocksOptions)
|
||||
default:
|
||||
panic(F.ToString("unknown outbound type: ", options.Type))
|
||||
return nil, E.New("unknown outbound type: ", options.Type)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,25 @@ import (
|
|||
)
|
||||
|
||||
func NewRule(router adapter.Router, logger log.Logger, 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() {
|
||||
return nil, E.New("missing conditions")
|
||||
}
|
||||
if options.DefaultOptions.Outbound == "" {
|
||||
return nil, E.New("missing outbound field")
|
||||
}
|
||||
return NewDefaultRule(router, logger, common.PtrValueOrDefault(options.DefaultOptions))
|
||||
case C.RuleTypeLogical:
|
||||
if !options.LogicalOptions.IsValid() {
|
||||
return nil, E.New("missing conditions")
|
||||
}
|
||||
if options.LogicalOptions.Outbound == "" {
|
||||
return nil, E.New("missing outbound field")
|
||||
}
|
||||
return NewLogicalRule(router, logger, common.PtrValueOrDefault(options.LogicalOptions))
|
||||
default:
|
||||
return nil, E.New("unknown rule type: ", options.Type)
|
||||
|
|
|
@ -18,7 +18,10 @@ func init() {
|
|||
logrus.StandardLogger().Formatter.(*logrus.TextFormatter).ForceColors = true
|
||||
}
|
||||
|
||||
var configPath string
|
||||
var (
|
||||
configPath string
|
||||
workingDir string
|
||||
)
|
||||
|
||||
func main() {
|
||||
command := &cobra.Command{
|
||||
|
@ -26,12 +29,19 @@ func main() {
|
|||
Run: run,
|
||||
}
|
||||
command.Flags().StringVarP(&configPath, "config", "c", "config.json", "set configuration file path")
|
||||
command.Flags().StringVarP(&workingDir, "directory", "D", "", "set working directory")
|
||||
if err := command.Execute(); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) {
|
||||
if workingDir != "" {
|
||||
if err := os.Chdir(workingDir); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
configContent, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
logrus.Fatal("read config: ", err)
|
||||
|
|
2
go.mod
2
go.mod
|
@ -6,7 +6,7 @@ require (
|
|||
github.com/database64128/tfo-go v1.0.4
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/oschwald/geoip2-golang v1.7.0
|
||||
github.com/sagernet/sing v0.0.0-20220702141141-b3923d54845b
|
||||
github.com/sagernet/sing v0.0.0-20220702174608-cb5bb5132de4
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.5.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -18,8 +18,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-20220702141141-b3923d54845b h1:oK5RglZ0s4oXNSrIsLJkBiHbYoAUMOGLN3a0JgDNzVM=
|
||||
github.com/sagernet/sing v0.0.0-20220702141141-b3923d54845b/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
||||
github.com/sagernet/sing v0.0.0-20220702174608-cb5bb5132de4 h1:Ce6nW9gV6g2hq/1K/nMtlVGiTtxh86EWs0/jOzMuNa4=
|
||||
github.com/sagernet/sing v0.0.0-20220702174608-cb5bb5132de4/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
|
||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
)
|
||||
|
||||
type Logger interface {
|
||||
Start() error
|
||||
Close() error
|
||||
Trace(args ...interface{})
|
||||
Debug(args ...interface{})
|
||||
Info(args ...interface{})
|
||||
|
@ -18,7 +20,6 @@ type Logger interface {
|
|||
Panic(args ...interface{})
|
||||
WithContext(ctx context.Context) Logger
|
||||
WithPrefix(prefix string) Logger
|
||||
Close() error
|
||||
}
|
||||
|
||||
func NewLogger(options option.LogOption) (Logger, error) {
|
||||
|
|
|
@ -15,6 +15,7 @@ var _ Logger = (*logrusLogger)(nil)
|
|||
|
||||
type logrusLogger struct {
|
||||
abstractLogrusLogger
|
||||
outputPath string
|
||||
output *os.File
|
||||
}
|
||||
|
||||
|
@ -28,7 +29,6 @@ func NewLogrusLogger(options option.LogOption) (*logrusLogger, error) {
|
|||
logger.SetLevel(logrus.TraceLevel)
|
||||
logger.Formatter.(*logrus.TextFormatter).ForceColors = true
|
||||
logger.AddHook(new(logrusHook))
|
||||
var output *os.File
|
||||
var err error
|
||||
if options.Level != "" {
|
||||
logger.Level, err = logrus.ParseLevel(options.Level)
|
||||
|
@ -36,18 +36,26 @@ func NewLogrusLogger(options option.LogOption) (*logrusLogger, error) {
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
if options.Output != "" {
|
||||
output, err = os.OpenFile(options.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
return &logrusLogger{logger, options.Output, nil}, nil
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Start() error {
|
||||
if l.outputPath != "" {
|
||||
output, err := os.OpenFile(l.outputPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return nil, E.Extend(err, "open log output")
|
||||
return E.Cause(err, "open log output")
|
||||
}
|
||||
logger.SetOutput(output)
|
||||
l.abstractLogrusLogger.(*logrus.Logger).SetOutput(output)
|
||||
}
|
||||
return &logrusLogger{logger, output}, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Close() error {
|
||||
return common.Close(common.PtrOrNil(l.output))
|
||||
}
|
||||
|
||||
func (l *logrusLogger) WithContext(ctx context.Context) Logger {
|
||||
return &logrusLogger{l.abstractLogrusLogger.WithContext(ctx), nil}
|
||||
return &logrusLogger{abstractLogrusLogger: l.abstractLogrusLogger.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *logrusLogger) WithPrefix(prefix string) Logger {
|
||||
|
@ -57,9 +65,5 @@ func (l *logrusLogger) WithPrefix(prefix string) Logger {
|
|||
prefix = F.ToString(loadedPrefix, prefix)
|
||||
}
|
||||
}
|
||||
return &logrusLogger{l.WithField("prefix", prefix), nil}
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Close() error {
|
||||
return common.Close(common.PtrOrNil(l.output))
|
||||
return &logrusLogger{abstractLogrusLogger: l.WithField("prefix", prefix)}
|
||||
}
|
||||
|
|
12
log/nop.go
12
log/nop.go
|
@ -10,6 +10,14 @@ func NewNopLogger() Logger {
|
|||
return (*nopLogger)(nil)
|
||||
}
|
||||
|
||||
func (l *nopLogger) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *nopLogger) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *nopLogger) Trace(args ...interface{}) {
|
||||
}
|
||||
|
||||
|
@ -44,7 +52,3 @@ func (l *nopLogger) WithContext(ctx context.Context) Logger {
|
|||
func (l *nopLogger) WithPrefix(prefix string) Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *nopLogger) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package option
|
||||
|
||||
import "github.com/sagernet/sing/common"
|
||||
|
||||
type Options struct {
|
||||
Log *LogOption `json:"log"`
|
||||
Inbounds []Inbound `json:"inbounds,omitempty"`
|
||||
|
@ -7,6 +9,13 @@ type Options struct {
|
|||
Route *RouteOptions `json:"route,omitempty"`
|
||||
}
|
||||
|
||||
func (o Options) Equals(other Options) bool {
|
||||
return common.ComparablePtrEquals(o.Log, other.Log) &&
|
||||
common.SliceEquals(o.Inbounds, other.Inbounds) &&
|
||||
common.ComparableSliceEquals(o.Outbounds, other.Outbounds) &&
|
||||
common.PtrEquals(o.Route, other.Route)
|
||||
}
|
||||
|
||||
type LogOption struct {
|
||||
Disabled bool `json:"disabled,omitempty"`
|
||||
Level string `json:"level,omitempty"`
|
||||
|
|
|
@ -3,52 +3,50 @@ package option
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
var ErrUnknownInboundType = E.New("unknown inbound type")
|
||||
|
||||
type _Inbound struct {
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
DirectOptions *DirectInboundOptions `json:"directOptions,omitempty"`
|
||||
SocksOptions *SimpleInboundOptions `json:"socksOptions,omitempty"`
|
||||
HTTPOptions *SimpleInboundOptions `json:"httpOptions,omitempty"`
|
||||
MixedOptions *SimpleInboundOptions `json:"mixedOptions,omitempty"`
|
||||
ShadowsocksOptions *ShadowsocksInboundOptions `json:"shadowsocksOptions,omitempty"`
|
||||
Type string `json:"type"`
|
||||
DirectOptions DirectInboundOptions `json:"-"`
|
||||
SocksOptions SimpleInboundOptions `json:"-"`
|
||||
HTTPOptions SimpleInboundOptions `json:"-"`
|
||||
MixedOptions SimpleInboundOptions `json:"-"`
|
||||
ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
|
||||
}
|
||||
|
||||
type Inbound _Inbound
|
||||
|
||||
func (i Inbound) Equals(other Inbound) bool {
|
||||
return i.Type == other.Type &&
|
||||
i.Tag == other.Tag &&
|
||||
common.Equals(i.DirectOptions, other.DirectOptions) &&
|
||||
common.Equals(i.SocksOptions, other.SocksOptions) &&
|
||||
common.Equals(i.HTTPOptions, other.HTTPOptions) &&
|
||||
common.Equals(i.MixedOptions, other.MixedOptions) &&
|
||||
common.Equals(i.ShadowsocksOptions, other.ShadowsocksOptions)
|
||||
}
|
||||
|
||||
func (i *Inbound) MarshalJSON() ([]byte, error) {
|
||||
var options []byte
|
||||
var err error
|
||||
var v any
|
||||
switch i.Type {
|
||||
case "direct":
|
||||
options, err = json.Marshal(i.DirectOptions)
|
||||
v = i.DirectOptions
|
||||
case "socks":
|
||||
options, err = json.Marshal(i.SocksOptions)
|
||||
v = i.SocksOptions
|
||||
case "http":
|
||||
options, err = json.Marshal(i.HTTPOptions)
|
||||
v = i.HTTPOptions
|
||||
case "mixed":
|
||||
options, err = json.Marshal(i.MixedOptions)
|
||||
v = i.MixedOptions
|
||||
case "shadowsocks":
|
||||
options, err = json.Marshal(i.ShadowsocksOptions)
|
||||
v = i.ShadowsocksOptions
|
||||
default:
|
||||
return nil, E.Extend(ErrUnknownInboundType, i.Type)
|
||||
return nil, E.New("unknown inbound type: ", i.Type)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var content map[string]any
|
||||
err = json.Unmarshal(options, &content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content["tag"] = i.Tag
|
||||
content["type"] = i.Type
|
||||
return json.Marshal(content)
|
||||
return MarshallObjects(i, v)
|
||||
}
|
||||
|
||||
func (i *Inbound) UnmarshalJSON(bytes []byte) error {
|
||||
|
@ -56,43 +54,29 @@ func (i *Inbound) UnmarshalJSON(bytes []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var v any
|
||||
switch i.Type {
|
||||
case "direct":
|
||||
if i.DirectOptions != nil {
|
||||
break
|
||||
}
|
||||
err = json.Unmarshal(bytes, &i.DirectOptions)
|
||||
v = &i.DirectOptions
|
||||
case "socks":
|
||||
if i.SocksOptions != nil {
|
||||
break
|
||||
}
|
||||
err = json.Unmarshal(bytes, &i.SocksOptions)
|
||||
v = &i.SocksOptions
|
||||
case "http":
|
||||
if i.HTTPOptions != nil {
|
||||
break
|
||||
}
|
||||
err = json.Unmarshal(bytes, &i.HTTPOptions)
|
||||
v = &i.HTTPOptions
|
||||
case "mixed":
|
||||
if i.MixedOptions != nil {
|
||||
break
|
||||
}
|
||||
err = json.Unmarshal(bytes, &i.MixedOptions)
|
||||
v = &i.MixedOptions
|
||||
case "shadowsocks":
|
||||
if i.ShadowsocksOptions != nil {
|
||||
break
|
||||
}
|
||||
err = json.Unmarshal(bytes, &i.ShadowsocksOptions)
|
||||
v = &i.ShadowsocksOptions
|
||||
default:
|
||||
return E.Extend(ErrUnknownInboundType, i.Type)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
return json.Unmarshal(bytes, v)
|
||||
}
|
||||
|
||||
type ListenOptions struct {
|
||||
Listen ListenAddress `json:"listen"`
|
||||
Port uint16 `json:"listen_port"`
|
||||
TCPFastOpen bool `json:"tcpFastOpen,omitempty"`
|
||||
UDPTimeout int64 `json:"udpTimeout,omitempty"`
|
||||
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||
}
|
||||
|
||||
type SimpleInboundOptions struct {
|
||||
|
@ -100,11 +84,23 @@ type SimpleInboundOptions struct {
|
|||
Users []auth.User `json:"users,omitempty"`
|
||||
}
|
||||
|
||||
func (o SimpleInboundOptions) Equals(other SimpleInboundOptions) bool {
|
||||
return o.ListenOptions == other.ListenOptions &&
|
||||
common.ComparableSliceEquals(o.Users, other.Users)
|
||||
}
|
||||
|
||||
type DirectInboundOptions struct {
|
||||
ListenOptions
|
||||
Network NetworkList `json:"network,omitempty"`
|
||||
OverrideAddress string `json:"overrideAddress,omitempty"`
|
||||
OverridePort uint16 `json:"overridePort,omitempty"`
|
||||
OverrideAddress string `json:"override_address,omitempty"`
|
||||
OverridePort uint16 `json:"override_port,omitempty"`
|
||||
}
|
||||
|
||||
func (o DirectInboundOptions) Equals(other DirectInboundOptions) bool {
|
||||
return o.ListenOptions == other.ListenOptions &&
|
||||
common.ComparableSliceEquals(o.Network, other.Network) &&
|
||||
o.OverrideAddress == other.OverrideAddress &&
|
||||
o.OverridePort == other.OverridePort
|
||||
}
|
||||
|
||||
type ShadowsocksInboundOptions struct {
|
||||
|
@ -113,3 +109,10 @@ type ShadowsocksInboundOptions struct {
|
|||
Method string `json:"method"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func (o ShadowsocksInboundOptions) Equals(other ShadowsocksInboundOptions) bool {
|
||||
return o.ListenOptions == other.ListenOptions &&
|
||||
common.ComparableSliceEquals(o.Network, other.Network) &&
|
||||
o.Method == other.Method &&
|
||||
o.Password == other.Password
|
||||
}
|
||||
|
|
40
option/json.go
Normal file
40
option/json.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package option
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func ToMap(v any) (map[string]any, error) {
|
||||
bytes, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var content map[string]any
|
||||
err = json.Unmarshal(bytes, &content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func MergeObjects(objects ...any) (map[string]any, error) {
|
||||
content := make(map[string]any)
|
||||
for _, object := range objects {
|
||||
objectMap, err := ToMap(object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range objectMap {
|
||||
content[k] = v
|
||||
}
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func MarshallObjects(objects ...any) ([]byte, error) {
|
||||
content, err := MergeObjects(objects...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(content)
|
||||
}
|
|
@ -2,7 +2,7 @@ package option
|
|||
|
||||
import "encoding/json"
|
||||
|
||||
type Listable[T any] []T
|
||||
type Listable[T comparable] []T
|
||||
|
||||
func (l *Listable[T]) MarshalJSON() ([]byte, error) {
|
||||
arrayList := []T(*l)
|
||||
|
|
|
@ -7,65 +7,44 @@ import (
|
|||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
var ErrUnknownOutboundType = E.New("unknown outbound type")
|
||||
|
||||
type _Outbound struct {
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
DirectOptions *DirectOutboundOptions `json:"directOptions,omitempty"`
|
||||
ShadowsocksOptions *ShadowsocksOutboundOptions `json:"shadowsocksOptions,omitempty"`
|
||||
DirectOptions DirectOutboundOptions `json:"-"`
|
||||
ShadowsocksOptions ShadowsocksOutboundOptions `json:"-"`
|
||||
}
|
||||
|
||||
type Outbound _Outbound
|
||||
|
||||
func (i *Outbound) MarshalJSON() ([]byte, error) {
|
||||
var options []byte
|
||||
var err error
|
||||
var v any
|
||||
switch i.Type {
|
||||
case "direct":
|
||||
options, err = json.Marshal(i.DirectOptions)
|
||||
v = i.DirectOptions
|
||||
case "shadowsocks":
|
||||
options, err = json.Marshal(i.ShadowsocksOptions)
|
||||
v = i.ShadowsocksOptions
|
||||
default:
|
||||
return nil, E.Extend(ErrUnknownOutboundType, i.Type)
|
||||
return nil, E.New("unknown outbound type: ", i.Type)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var content map[string]any
|
||||
err = json.Unmarshal(options, &content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content["tag"] = i.Tag
|
||||
content["type"] = i.Type
|
||||
return json.Marshal(content)
|
||||
return MarshallObjects(i, v)
|
||||
}
|
||||
|
||||
func (i *Outbound) UnmarshalJSON(bytes []byte) error {
|
||||
if err := json.Unmarshal(bytes, (*_Outbound)(i)); err != nil {
|
||||
err := json.Unmarshal(bytes, (*_Outbound)(i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var v any
|
||||
switch i.Type {
|
||||
case "direct":
|
||||
if i.DirectOptions != nil {
|
||||
break
|
||||
}
|
||||
if err := json.Unmarshal(bytes, &i.DirectOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
v = &i.DirectOptions
|
||||
case "shadowsocks":
|
||||
if i.ShadowsocksOptions != nil {
|
||||
break
|
||||
}
|
||||
if err := json.Unmarshal(bytes, &i.ShadowsocksOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
v = &i.ShadowsocksOptions
|
||||
default:
|
||||
return E.Extend(ErrUnknownOutboundType, i.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(bytes, v)
|
||||
}
|
||||
|
||||
type DialerOptions struct {
|
||||
Detour string `json:"detour,omitempty"`
|
||||
|
|
|
@ -4,16 +4,20 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
var ErrUnknownRuleType = E.New("unknown rule type")
|
||||
|
||||
type RouteOptions struct {
|
||||
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
}
|
||||
|
||||
func (o RouteOptions) Equals(other RouteOptions) bool {
|
||||
return common.ComparablePtrEquals(o.GeoIP, other.GeoIP) &&
|
||||
common.SliceEquals(o.Rules, other.Rules)
|
||||
}
|
||||
|
||||
type GeoIPOptions struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
DownloadURL string `json:"download_url,omitempty"`
|
||||
|
@ -28,25 +32,23 @@ type _Rule struct {
|
|||
|
||||
type Rule _Rule
|
||||
|
||||
func (r Rule) Equals(other Rule) bool {
|
||||
return r.Type == other.Type &&
|
||||
common.PtrEquals(r.DefaultOptions, other.DefaultOptions) &&
|
||||
common.PtrEquals(r.LogicalOptions, other.LogicalOptions)
|
||||
}
|
||||
|
||||
func (r *Rule) MarshalJSON() ([]byte, error) {
|
||||
var content map[string]any
|
||||
var v any
|
||||
switch r.Type {
|
||||
case "", C.RuleTypeDefault:
|
||||
return json.Marshal(r.DefaultOptions)
|
||||
case C.RuleTypeDefault:
|
||||
v = r.DefaultOptions
|
||||
case C.RuleTypeLogical:
|
||||
options, err := json.Marshal(r.LogicalOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(options, &content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content["type"] = r.Type
|
||||
return json.Marshal(content)
|
||||
v = r.LogicalOptions
|
||||
default:
|
||||
return nil, E.Extend(ErrUnknownRuleType, r.Type)
|
||||
return nil, E.New("unknown rule type: " + r.Type)
|
||||
}
|
||||
return MarshallObjects(r, v)
|
||||
}
|
||||
|
||||
func (r *Rule) UnmarshalJSON(bytes []byte) error {
|
||||
|
@ -54,21 +56,19 @@ func (r *Rule) UnmarshalJSON(bytes []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Type == "" {
|
||||
r.Type = C.RuleTypeDefault
|
||||
}
|
||||
var v any
|
||||
switch r.Type {
|
||||
case "", C.RuleTypeDefault:
|
||||
if r.DefaultOptions == nil {
|
||||
break
|
||||
}
|
||||
err = json.Unmarshal(bytes, r.DefaultOptions)
|
||||
case C.RuleTypeDefault:
|
||||
v = &r.DefaultOptions
|
||||
case C.RuleTypeLogical:
|
||||
if r.LogicalOptions == nil {
|
||||
break
|
||||
}
|
||||
err = json.Unmarshal(bytes, r.LogicalOptions)
|
||||
v = &r.LogicalOptions
|
||||
default:
|
||||
err = E.Extend(ErrUnknownRuleType, r.Type)
|
||||
return E.New("unknown rule type: " + r.Type)
|
||||
}
|
||||
return err
|
||||
return json.Unmarshal(bytes, v)
|
||||
}
|
||||
|
||||
type DefaultRule struct {
|
||||
|
@ -90,8 +90,41 @@ type DefaultRule struct {
|
|||
Outbound string `json:"outbound,omitempty"`
|
||||
}
|
||||
|
||||
func (r DefaultRule) IsValid() bool {
|
||||
var defaultValue DefaultRule
|
||||
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.Protocol, other.Protocol) &&
|
||||
common.ComparableSliceEquals(r.Domain, other.Domain) &&
|
||||
common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) &&
|
||||
common.ComparableSliceEquals(r.DomainKeyword, other.DomainKeyword) &&
|
||||
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.Port, other.Port) &&
|
||||
r.Outbound == other.Outbound
|
||||
}
|
||||
|
||||
type LogicalRule struct {
|
||||
Mode string `json:"mode"`
|
||||
Rules []DefaultRule `json:"rules,omitempty"`
|
||||
Outbound string `json:"outbound,omitempty"`
|
||||
}
|
||||
|
||||
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.Outbound == other.Outbound
|
||||
}
|
||||
|
|
26
service.go
26
service.go
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
var _ adapter.Service = (*Service)(nil)
|
||||
|
@ -24,11 +25,11 @@ type Service struct {
|
|||
func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
||||
logger, err := log.NewLogger(common.PtrValueOrDefault(options.Log))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, E.Cause(err, "parse log options")
|
||||
}
|
||||
router, err := route.NewRouter(ctx, logger, common.PtrValueOrDefault(options.Route))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, E.Cause(err, "parse route options")
|
||||
}
|
||||
inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
|
||||
outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
|
||||
|
@ -36,7 +37,7 @@ func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
|||
var inboundService adapter.Inbound
|
||||
inboundService, err = inbound.New(ctx, router, logger, i, inboundOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||
}
|
||||
inbounds = append(inbounds, inboundService)
|
||||
}
|
||||
|
@ -44,7 +45,7 @@ func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
|||
var outboundService adapter.Outbound
|
||||
outboundService, err = outbound.New(router, logger, i, outboundOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, E.Cause(err, "parse outbound[", i, "]")
|
||||
}
|
||||
outbounds = append(outbounds, outboundService)
|
||||
}
|
||||
|
@ -61,13 +62,19 @@ func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
|||
}
|
||||
|
||||
func (s *Service) Start() error {
|
||||
err := s.logger.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, in := range s.inbounds {
|
||||
err := in.Start()
|
||||
err = in.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return common.AnyError(
|
||||
s.router.Start(),
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Service) Close() error {
|
||||
|
@ -77,7 +84,8 @@ func (s *Service) Close() error {
|
|||
for _, out := range s.outbounds {
|
||||
common.Close(out)
|
||||
}
|
||||
s.logger.Close()
|
||||
s.router.Close()
|
||||
return nil
|
||||
return common.Close(
|
||||
s.router,
|
||||
s.logger,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue