rule_action.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. package rule
  2. import (
  3. "context"
  4. "errors"
  5. "net/netip"
  6. "strings"
  7. "sync"
  8. "syscall"
  9. "time"
  10. "github.com/sagernet/sing-box/adapter"
  11. "github.com/sagernet/sing-box/common/dialer"
  12. "github.com/sagernet/sing-box/common/sniff"
  13. C "github.com/sagernet/sing-box/constant"
  14. "github.com/sagernet/sing-box/option"
  15. "github.com/sagernet/sing-dns"
  16. "github.com/sagernet/sing-tun"
  17. "github.com/sagernet/sing/common"
  18. E "github.com/sagernet/sing/common/exceptions"
  19. F "github.com/sagernet/sing/common/format"
  20. "github.com/sagernet/sing/common/logger"
  21. M "github.com/sagernet/sing/common/metadata"
  22. N "github.com/sagernet/sing/common/network"
  23. )
  24. func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action option.RuleAction) (adapter.RuleAction, error) {
  25. switch action.Action {
  26. case "":
  27. return nil, nil
  28. case C.RuleActionTypeRoute:
  29. return &RuleActionRoute{
  30. Outbound: action.RouteOptions.Outbound,
  31. RuleActionRouteOptions: RuleActionRouteOptions{
  32. OverrideAddress: M.ParseSocksaddrHostPort(action.RouteOptions.OverrideAddress, 0),
  33. OverridePort: action.RouteOptions.OverridePort,
  34. NetworkStrategy: (*C.NetworkStrategy)(action.RouteOptions.NetworkStrategy),
  35. FallbackDelay: time.Duration(action.RouteOptions.FallbackDelay),
  36. UDPDisableDomainUnmapping: action.RouteOptions.UDPDisableDomainUnmapping,
  37. UDPConnect: action.RouteOptions.UDPConnect,
  38. },
  39. }, nil
  40. case C.RuleActionTypeRouteOptions:
  41. return &RuleActionRouteOptions{
  42. OverrideAddress: M.ParseSocksaddrHostPort(action.RouteOptionsOptions.OverrideAddress, 0),
  43. OverridePort: action.RouteOptionsOptions.OverridePort,
  44. NetworkStrategy: (*C.NetworkStrategy)(action.RouteOptionsOptions.NetworkStrategy),
  45. FallbackDelay: time.Duration(action.RouteOptionsOptions.FallbackDelay),
  46. UDPDisableDomainUnmapping: action.RouteOptionsOptions.UDPDisableDomainUnmapping,
  47. UDPConnect: action.RouteOptionsOptions.UDPConnect,
  48. UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout),
  49. }, nil
  50. case C.RuleActionTypeDirect:
  51. directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions))
  52. if err != nil {
  53. return nil, err
  54. }
  55. var description string
  56. descriptions := action.DirectOptions.Descriptions()
  57. switch len(descriptions) {
  58. case 0:
  59. case 1:
  60. description = F.ToString("(", descriptions[0], ")")
  61. case 2:
  62. description = F.ToString("(", descriptions[0], ",", descriptions[1], ")")
  63. default:
  64. description = F.ToString("(", descriptions[0], ",", descriptions[1], ",...)")
  65. }
  66. return &RuleActionDirect{
  67. Dialer: directDialer,
  68. description: description,
  69. }, nil
  70. case C.RuleActionTypeReject:
  71. return &RuleActionReject{
  72. Method: action.RejectOptions.Method,
  73. NoDrop: action.RejectOptions.NoDrop,
  74. logger: logger,
  75. }, nil
  76. case C.RuleActionTypeHijackDNS:
  77. return &RuleActionHijackDNS{}, nil
  78. case C.RuleActionTypeSniff:
  79. sniffAction := &RuleActionSniff{
  80. snifferNames: action.SniffOptions.Sniffer,
  81. Timeout: time.Duration(action.SniffOptions.Timeout),
  82. }
  83. return sniffAction, sniffAction.build()
  84. case C.RuleActionTypeResolve:
  85. return &RuleActionResolve{
  86. Strategy: dns.DomainStrategy(action.ResolveOptions.Strategy),
  87. Server: action.ResolveOptions.Server,
  88. }, nil
  89. default:
  90. panic(F.ToString("unknown rule action: ", action.Action))
  91. }
  92. }
  93. func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction) adapter.RuleAction {
  94. switch action.Action {
  95. case "":
  96. return nil
  97. case C.RuleActionTypeRoute:
  98. return &RuleActionDNSRoute{
  99. Server: action.RouteOptions.Server,
  100. RuleActionDNSRouteOptions: RuleActionDNSRouteOptions{
  101. DisableCache: action.RouteOptions.DisableCache,
  102. RewriteTTL: action.RouteOptions.RewriteTTL,
  103. ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptions.ClientSubnet)),
  104. },
  105. }
  106. case C.RuleActionTypeRouteOptions:
  107. return &RuleActionDNSRouteOptions{
  108. DisableCache: action.RouteOptionsOptions.DisableCache,
  109. RewriteTTL: action.RouteOptionsOptions.RewriteTTL,
  110. ClientSubnet: netip.Prefix(common.PtrValueOrDefault(action.RouteOptionsOptions.ClientSubnet)),
  111. }
  112. case C.RuleActionTypeReject:
  113. return &RuleActionReject{
  114. Method: action.RejectOptions.Method,
  115. NoDrop: action.RejectOptions.NoDrop,
  116. logger: logger,
  117. }
  118. default:
  119. panic(F.ToString("unknown rule action: ", action.Action))
  120. }
  121. }
  122. type RuleActionRoute struct {
  123. Outbound string
  124. RuleActionRouteOptions
  125. }
  126. func (r *RuleActionRoute) Type() string {
  127. return C.RuleActionTypeRoute
  128. }
  129. func (r *RuleActionRoute) String() string {
  130. var descriptions []string
  131. descriptions = append(descriptions, r.Outbound)
  132. if r.UDPDisableDomainUnmapping {
  133. descriptions = append(descriptions, "udp-disable-domain-unmapping")
  134. }
  135. if r.UDPConnect {
  136. descriptions = append(descriptions, "udp-connect")
  137. }
  138. return F.ToString("route(", strings.Join(descriptions, ","), ")")
  139. }
  140. type RuleActionRouteOptions struct {
  141. OverrideAddress M.Socksaddr
  142. OverridePort uint16
  143. NetworkStrategy *C.NetworkStrategy
  144. NetworkType []C.InterfaceType
  145. FallbackNetworkType []C.InterfaceType
  146. FallbackDelay time.Duration
  147. UDPDisableDomainUnmapping bool
  148. UDPConnect bool
  149. UDPTimeout time.Duration
  150. }
  151. func (r *RuleActionRouteOptions) Type() string {
  152. return C.RuleActionTypeRouteOptions
  153. }
  154. func (r *RuleActionRouteOptions) String() string {
  155. var descriptions []string
  156. if r.OverrideAddress.IsValid() {
  157. descriptions = append(descriptions, F.ToString("override-address=", r.OverrideAddress.AddrString()))
  158. }
  159. if r.OverridePort > 0 {
  160. descriptions = append(descriptions, F.ToString("override-port=", r.OverridePort))
  161. }
  162. if r.NetworkStrategy != nil {
  163. descriptions = append(descriptions, F.ToString("network-strategy=", r.NetworkStrategy))
  164. }
  165. if r.NetworkType != nil {
  166. descriptions = append(descriptions, F.ToString("network-type=", strings.Join(common.Map(r.NetworkType, C.InterfaceType.String), ",")))
  167. }
  168. if r.FallbackNetworkType != nil {
  169. descriptions = append(descriptions, F.ToString("fallback-network-type="+strings.Join(common.Map(r.NetworkType, C.InterfaceType.String), ",")))
  170. }
  171. if r.FallbackDelay > 0 {
  172. descriptions = append(descriptions, F.ToString("fallback-delay=", r.FallbackDelay.String()))
  173. }
  174. if r.UDPDisableDomainUnmapping {
  175. descriptions = append(descriptions, "udp-disable-domain-unmapping")
  176. }
  177. if r.UDPConnect {
  178. descriptions = append(descriptions, "udp-connect")
  179. }
  180. return F.ToString("route-options(", strings.Join(descriptions, ","), ")")
  181. }
  182. type RuleActionDNSRoute struct {
  183. Server string
  184. RuleActionDNSRouteOptions
  185. }
  186. func (r *RuleActionDNSRoute) Type() string {
  187. return C.RuleActionTypeRoute
  188. }
  189. func (r *RuleActionDNSRoute) String() string {
  190. var descriptions []string
  191. descriptions = append(descriptions, r.Server)
  192. if r.DisableCache {
  193. descriptions = append(descriptions, "disable-cache")
  194. }
  195. if r.RewriteTTL != nil {
  196. descriptions = append(descriptions, F.ToString("rewrite-ttl=", *r.RewriteTTL))
  197. }
  198. if r.ClientSubnet.IsValid() {
  199. descriptions = append(descriptions, F.ToString("client-subnet=", r.ClientSubnet))
  200. }
  201. return F.ToString("route(", strings.Join(descriptions, ","), ")")
  202. }
  203. type RuleActionDNSRouteOptions struct {
  204. DisableCache bool
  205. RewriteTTL *uint32
  206. ClientSubnet netip.Prefix
  207. }
  208. func (r *RuleActionDNSRouteOptions) Type() string {
  209. return C.RuleActionTypeRouteOptions
  210. }
  211. func (r *RuleActionDNSRouteOptions) String() string {
  212. var descriptions []string
  213. if r.DisableCache {
  214. descriptions = append(descriptions, "disable-cache")
  215. }
  216. if r.RewriteTTL != nil {
  217. descriptions = append(descriptions, F.ToString("rewrite-ttl=", *r.RewriteTTL))
  218. }
  219. if r.ClientSubnet.IsValid() {
  220. descriptions = append(descriptions, F.ToString("client-subnet=", r.ClientSubnet))
  221. }
  222. return F.ToString("route-options(", strings.Join(descriptions, ","), ")")
  223. }
  224. type RuleActionDirect struct {
  225. Dialer N.Dialer
  226. description string
  227. }
  228. func (r *RuleActionDirect) Type() string {
  229. return C.RuleActionTypeDirect
  230. }
  231. func (r *RuleActionDirect) String() string {
  232. return "direct" + r.description
  233. }
  234. type RejectedError struct {
  235. Cause error
  236. }
  237. func (r *RejectedError) Error() string {
  238. return "rejected"
  239. }
  240. func (r *RejectedError) Unwrap() error {
  241. return r.Cause
  242. }
  243. func IsRejected(err error) bool {
  244. var rejected *RejectedError
  245. return errors.As(err, &rejected)
  246. }
  247. type RuleActionReject struct {
  248. Method string
  249. NoDrop bool
  250. logger logger.ContextLogger
  251. dropAccess sync.Mutex
  252. dropCounter []time.Time
  253. }
  254. func (r *RuleActionReject) Type() string {
  255. return C.RuleActionTypeReject
  256. }
  257. func (r *RuleActionReject) String() string {
  258. if r.Method == C.RuleActionRejectMethodDefault {
  259. return "reject"
  260. }
  261. return F.ToString("reject(", r.Method, ")")
  262. }
  263. func (r *RuleActionReject) Error(ctx context.Context) error {
  264. var returnErr error
  265. switch r.Method {
  266. case C.RuleActionRejectMethodDefault:
  267. returnErr = &RejectedError{syscall.ECONNREFUSED}
  268. case C.RuleActionRejectMethodDrop:
  269. return &RejectedError{tun.ErrDrop}
  270. default:
  271. panic(F.ToString("unknown reject method: ", r.Method))
  272. }
  273. if r.NoDrop {
  274. return returnErr
  275. }
  276. r.dropAccess.Lock()
  277. defer r.dropAccess.Unlock()
  278. timeNow := time.Now()
  279. r.dropCounter = common.Filter(r.dropCounter, func(t time.Time) bool {
  280. return timeNow.Sub(t) <= 30*time.Second
  281. })
  282. r.dropCounter = append(r.dropCounter, timeNow)
  283. if len(r.dropCounter) > 50 {
  284. if ctx != nil {
  285. r.logger.DebugContext(ctx, "dropped due to flooding")
  286. }
  287. return &RejectedError{tun.ErrDrop}
  288. }
  289. return returnErr
  290. }
  291. type RuleActionHijackDNS struct{}
  292. func (r *RuleActionHijackDNS) Type() string {
  293. return C.RuleActionTypeHijackDNS
  294. }
  295. func (r *RuleActionHijackDNS) String() string {
  296. return "hijack-dns"
  297. }
  298. type RuleActionSniff struct {
  299. snifferNames []string
  300. StreamSniffers []sniff.StreamSniffer
  301. PacketSniffers []sniff.PacketSniffer
  302. Timeout time.Duration
  303. // Deprecated
  304. OverrideDestination bool
  305. }
  306. func (r *RuleActionSniff) Type() string {
  307. return C.RuleActionTypeSniff
  308. }
  309. func (r *RuleActionSniff) build() error {
  310. for _, name := range r.snifferNames {
  311. switch name {
  312. case C.ProtocolTLS:
  313. r.StreamSniffers = append(r.StreamSniffers, sniff.TLSClientHello)
  314. case C.ProtocolHTTP:
  315. r.StreamSniffers = append(r.StreamSniffers, sniff.HTTPHost)
  316. case C.ProtocolQUIC:
  317. r.PacketSniffers = append(r.PacketSniffers, sniff.QUICClientHello)
  318. case C.ProtocolDNS:
  319. r.StreamSniffers = append(r.StreamSniffers, sniff.StreamDomainNameQuery)
  320. r.PacketSniffers = append(r.PacketSniffers, sniff.DomainNameQuery)
  321. case C.ProtocolSTUN:
  322. r.PacketSniffers = append(r.PacketSniffers, sniff.STUNMessage)
  323. case C.ProtocolBitTorrent:
  324. r.StreamSniffers = append(r.StreamSniffers, sniff.BitTorrent)
  325. r.PacketSniffers = append(r.PacketSniffers, sniff.UTP)
  326. r.PacketSniffers = append(r.PacketSniffers, sniff.UDPTracker)
  327. case C.ProtocolDTLS:
  328. r.PacketSniffers = append(r.PacketSniffers, sniff.DTLSRecord)
  329. case C.ProtocolSSH:
  330. r.StreamSniffers = append(r.StreamSniffers, sniff.SSH)
  331. case C.ProtocolRDP:
  332. r.StreamSniffers = append(r.StreamSniffers, sniff.RDP)
  333. default:
  334. return E.New("unknown sniffer: ", name)
  335. }
  336. }
  337. return nil
  338. }
  339. func (r *RuleActionSniff) String() string {
  340. if len(r.snifferNames) == 0 && r.Timeout == 0 {
  341. return "sniff"
  342. } else if len(r.snifferNames) > 0 && r.Timeout == 0 {
  343. return F.ToString("sniff(", strings.Join(r.snifferNames, ","), ")")
  344. } else if len(r.snifferNames) == 0 && r.Timeout > 0 {
  345. return F.ToString("sniff(", r.Timeout.String(), ")")
  346. } else {
  347. return F.ToString("sniff(", strings.Join(r.snifferNames, ","), ",", r.Timeout.String(), ")")
  348. }
  349. }
  350. type RuleActionResolve struct {
  351. Strategy dns.DomainStrategy
  352. Server string
  353. }
  354. func (r *RuleActionResolve) Type() string {
  355. return C.RuleActionTypeResolve
  356. }
  357. func (r *RuleActionResolve) String() string {
  358. if r.Strategy == dns.DomainStrategyAsIS && r.Server == "" {
  359. return F.ToString("resolve")
  360. } else if r.Strategy != dns.DomainStrategyAsIS && r.Server == "" {
  361. return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ")")
  362. } else if r.Strategy == dns.DomainStrategyAsIS && r.Server != "" {
  363. return F.ToString("resolve(", r.Server, ")")
  364. } else {
  365. return F.ToString("resolve(", option.DomainStrategy(r.Strategy).String(), ",", r.Server, ")")
  366. }
  367. }