github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/outbound/selector.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  
     7  	"github.com/inazumav/sing-box/adapter"
     8  	C "github.com/inazumav/sing-box/constant"
     9  	"github.com/inazumav/sing-box/log"
    10  	"github.com/inazumav/sing-box/option"
    11  	E "github.com/sagernet/sing/common/exceptions"
    12  	M "github.com/sagernet/sing/common/metadata"
    13  	N "github.com/sagernet/sing/common/network"
    14  )
    15  
    16  var (
    17  	_ adapter.Outbound      = (*Selector)(nil)
    18  	_ adapter.OutboundGroup = (*Selector)(nil)
    19  )
    20  
    21  type Selector struct {
    22  	myOutboundAdapter
    23  	tags       []string
    24  	defaultTag string
    25  	outbounds  map[string]adapter.Outbound
    26  	selected   adapter.Outbound
    27  }
    28  
    29  func NewSelector(router adapter.Router, logger log.ContextLogger, tag string, options option.SelectorOutboundOptions) (*Selector, error) {
    30  	outbound := &Selector{
    31  		myOutboundAdapter: myOutboundAdapter{
    32  			protocol:     C.TypeSelector,
    33  			router:       router,
    34  			logger:       logger,
    35  			tag:          tag,
    36  			dependencies: options.Outbounds,
    37  		},
    38  		tags:       options.Outbounds,
    39  		defaultTag: options.Default,
    40  		outbounds:  make(map[string]adapter.Outbound),
    41  	}
    42  	if len(outbound.tags) == 0 {
    43  		return nil, E.New("missing tags")
    44  	}
    45  	return outbound, nil
    46  }
    47  
    48  func (s *Selector) Network() []string {
    49  	if s.selected == nil {
    50  		return []string{N.NetworkTCP, N.NetworkUDP}
    51  	}
    52  	return s.selected.Network()
    53  }
    54  
    55  func (s *Selector) Start() error {
    56  	for i, tag := range s.tags {
    57  		detour, loaded := s.router.Outbound(tag)
    58  		if !loaded {
    59  			return E.New("outbound ", i, " not found: ", tag)
    60  		}
    61  		s.outbounds[tag] = detour
    62  	}
    63  
    64  	if s.tag != "" {
    65  		if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
    66  			selected := clashServer.CacheFile().LoadSelected(s.tag)
    67  			if selected != "" {
    68  				detour, loaded := s.outbounds[selected]
    69  				if loaded {
    70  					s.selected = detour
    71  					return nil
    72  				}
    73  			}
    74  		}
    75  	}
    76  
    77  	if s.defaultTag != "" {
    78  		detour, loaded := s.outbounds[s.defaultTag]
    79  		if !loaded {
    80  			return E.New("default outbound not found: ", s.defaultTag)
    81  		}
    82  		s.selected = detour
    83  		return nil
    84  	}
    85  
    86  	s.selected = s.outbounds[s.tags[0]]
    87  	return nil
    88  }
    89  
    90  func (s *Selector) Now() string {
    91  	return s.selected.Tag()
    92  }
    93  
    94  func (s *Selector) All() []string {
    95  	return s.tags
    96  }
    97  
    98  func (s *Selector) SelectOutbound(tag string) bool {
    99  	detour, loaded := s.outbounds[tag]
   100  	if !loaded {
   101  		return false
   102  	}
   103  	s.selected = detour
   104  	if s.tag != "" {
   105  		if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
   106  			err := clashServer.CacheFile().StoreSelected(s.tag, tag)
   107  			if err != nil {
   108  				s.logger.Error("store selected: ", err)
   109  			}
   110  		}
   111  	}
   112  	return true
   113  }
   114  
   115  func (s *Selector) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
   116  	return s.selected.DialContext(ctx, network, destination)
   117  }
   118  
   119  func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
   120  	return s.selected.ListenPacket(ctx, destination)
   121  }
   122  
   123  func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
   124  	return s.selected.NewConnection(ctx, conn, metadata)
   125  }
   126  
   127  func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
   128  	return s.selected.NewPacketConnection(ctx, conn, metadata)
   129  }
   130  
   131  func RealTag(detour adapter.Outbound) string {
   132  	if group, isGroup := detour.(adapter.OutboundGroup); isGroup {
   133  		return group.Now()
   134  	}
   135  	return detour.Tag()
   136  }