rule_item_preferred_by.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package rule
  2. import (
  3. "context"
  4. "strings"
  5. "github.com/sagernet/sing-box/adapter"
  6. E "github.com/sagernet/sing/common/exceptions"
  7. F "github.com/sagernet/sing/common/format"
  8. "github.com/sagernet/sing/service"
  9. )
  10. var _ RuleItem = (*PreferredByItem)(nil)
  11. type PreferredByItem struct {
  12. ctx context.Context
  13. outboundTags []string
  14. outbounds []adapter.OutboundWithPreferredRoutes
  15. }
  16. func NewPreferredByItem(ctx context.Context, outboundTags []string) *PreferredByItem {
  17. return &PreferredByItem{
  18. ctx: ctx,
  19. outboundTags: outboundTags,
  20. }
  21. }
  22. func (r *PreferredByItem) Start() error {
  23. outboundManager := service.FromContext[adapter.OutboundManager](r.ctx)
  24. for _, outboundTag := range r.outboundTags {
  25. rawOutbound, loaded := outboundManager.Outbound(outboundTag)
  26. if !loaded {
  27. return E.New("outbound not found: ", outboundTag)
  28. }
  29. outboundWithPreferredRoutes, withRoutes := rawOutbound.(adapter.OutboundWithPreferredRoutes)
  30. if !withRoutes {
  31. return E.New("outbound type does not support preferred routes: ", rawOutbound.Type())
  32. }
  33. r.outbounds = append(r.outbounds, outboundWithPreferredRoutes)
  34. }
  35. return nil
  36. }
  37. func (r *PreferredByItem) Match(metadata *adapter.InboundContext) bool {
  38. var domainHost string
  39. if metadata.Domain != "" {
  40. domainHost = metadata.Domain
  41. } else {
  42. domainHost = metadata.Destination.Fqdn
  43. }
  44. if domainHost != "" {
  45. for _, outbound := range r.outbounds {
  46. if outbound.PreferredDomain(domainHost) {
  47. return true
  48. }
  49. }
  50. }
  51. if metadata.Destination.IsIP() {
  52. for _, outbound := range r.outbounds {
  53. if outbound.PreferredAddress(metadata.Destination.Addr) {
  54. return true
  55. }
  56. }
  57. }
  58. if len(metadata.DestinationAddresses) > 0 {
  59. for _, address := range metadata.DestinationAddresses {
  60. for _, outbound := range r.outbounds {
  61. if outbound.PreferredAddress(address) {
  62. return true
  63. }
  64. }
  65. }
  66. }
  67. return false
  68. }
  69. func (r *PreferredByItem) String() string {
  70. description := "preferred_by="
  71. pLen := len(r.outboundTags)
  72. if pLen == 1 {
  73. description += F.ToString(r.outboundTags[0])
  74. } else {
  75. description += "[" + strings.Join(F.MapToString(r.outboundTags), " ") + "]"
  76. }
  77. return description
  78. }