github.com/sagernet/sing-box@v1.2.7/outbound/selector.go (about)

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