rule_action.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. package option
  2. import (
  3. "fmt"
  4. "time"
  5. C "github.com/sagernet/sing-box/constant"
  6. dns "github.com/sagernet/sing-dns"
  7. E "github.com/sagernet/sing/common/exceptions"
  8. "github.com/sagernet/sing/common/json"
  9. "github.com/sagernet/sing/common/json/badjson"
  10. )
  11. type _RuleAction struct {
  12. Action string `json:"action,omitempty"`
  13. RouteOptions RouteActionOptions `json:"-"`
  14. RouteOptionsOptions RouteOptionsActionOptions `json:"-"`
  15. DirectOptions DirectActionOptions `json:"-"`
  16. RejectOptions RejectActionOptions `json:"-"`
  17. SniffOptions RouteActionSniff `json:"-"`
  18. ResolveOptions RouteActionResolve `json:"-"`
  19. }
  20. type RuleAction _RuleAction
  21. func (r RuleAction) MarshalJSON() ([]byte, error) {
  22. if r.Action == "" {
  23. return json.Marshal(struct{}{})
  24. }
  25. var v any
  26. switch r.Action {
  27. case C.RuleActionTypeRoute:
  28. r.Action = ""
  29. v = r.RouteOptions
  30. case C.RuleActionTypeRouteOptions:
  31. v = r.RouteOptionsOptions
  32. case C.RuleActionTypeDirect:
  33. v = r.DirectOptions
  34. case C.RuleActionTypeReject:
  35. v = r.RejectOptions
  36. case C.RuleActionTypeHijackDNS:
  37. v = nil
  38. case C.RuleActionTypeSniff:
  39. v = r.SniffOptions
  40. case C.RuleActionTypeResolve:
  41. v = r.ResolveOptions
  42. default:
  43. return nil, E.New("unknown rule action: " + r.Action)
  44. }
  45. if v == nil {
  46. return badjson.MarshallObjects((_RuleAction)(r))
  47. }
  48. return badjson.MarshallObjects((_RuleAction)(r), v)
  49. }
  50. func (r *RuleAction) UnmarshalJSON(data []byte) error {
  51. err := json.Unmarshal(data, (*_RuleAction)(r))
  52. if err != nil {
  53. return err
  54. }
  55. var v any
  56. switch r.Action {
  57. case "", C.RuleActionTypeRoute:
  58. r.Action = C.RuleActionTypeRoute
  59. v = &r.RouteOptions
  60. case C.RuleActionTypeRouteOptions:
  61. v = &r.RouteOptionsOptions
  62. case C.RuleActionTypeDirect:
  63. v = &r.DirectOptions
  64. case C.RuleActionTypeReject:
  65. v = &r.RejectOptions
  66. case C.RuleActionTypeHijackDNS:
  67. v = nil
  68. case C.RuleActionTypeSniff:
  69. v = &r.SniffOptions
  70. case C.RuleActionTypeResolve:
  71. v = &r.ResolveOptions
  72. default:
  73. return E.New("unknown rule action: " + r.Action)
  74. }
  75. if v == nil {
  76. // check unknown fields
  77. return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{})
  78. }
  79. return badjson.UnmarshallExcluded(data, (*_RuleAction)(r), v)
  80. }
  81. type _DNSRuleAction struct {
  82. Action string `json:"action,omitempty"`
  83. RouteOptions DNSRouteActionOptions `json:"-"`
  84. RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"`
  85. RejectOptions RejectActionOptions `json:"-"`
  86. }
  87. type DNSRuleAction _DNSRuleAction
  88. func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
  89. if r.Action == "" {
  90. return json.Marshal(struct{}{})
  91. }
  92. var v any
  93. switch r.Action {
  94. case C.RuleActionTypeRoute:
  95. r.Action = ""
  96. v = r.RouteOptions
  97. case C.RuleActionTypeRouteOptions:
  98. v = r.RouteOptionsOptions
  99. case C.RuleActionTypeReject:
  100. v = r.RejectOptions
  101. default:
  102. return nil, E.New("unknown DNS rule action: " + r.Action)
  103. }
  104. return badjson.MarshallObjects((_DNSRuleAction)(r), v)
  105. }
  106. func (r *DNSRuleAction) UnmarshalJSON(data []byte) error {
  107. err := json.Unmarshal(data, (*_DNSRuleAction)(r))
  108. if err != nil {
  109. return err
  110. }
  111. var v any
  112. switch r.Action {
  113. case "", C.RuleActionTypeRoute:
  114. r.Action = C.RuleActionTypeRoute
  115. v = &r.RouteOptions
  116. case C.RuleActionTypeRouteOptions:
  117. v = &r.RouteOptionsOptions
  118. case C.RuleActionTypeReject:
  119. v = &r.RejectOptions
  120. default:
  121. return E.New("unknown DNS rule action: " + r.Action)
  122. }
  123. if v == nil {
  124. // check unknown fields
  125. return json.UnmarshalDisallowUnknownFields(data, &_DNSRuleAction{})
  126. }
  127. return badjson.UnmarshallExcluded(data, (*_DNSRuleAction)(r), v)
  128. }
  129. type _RouteActionOptions struct {
  130. Outbound string `json:"outbound,omitempty"`
  131. }
  132. type RouteActionOptions _RouteActionOptions
  133. func (r *RouteActionOptions) UnmarshalJSON(data []byte) error {
  134. err := json.Unmarshal(data, (*_RouteActionOptions)(r))
  135. if err != nil {
  136. return err
  137. }
  138. if r.Outbound == "" {
  139. return E.New("missing outbound")
  140. }
  141. return nil
  142. }
  143. type _RouteOptionsActionOptions struct {
  144. UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
  145. UDPConnect bool `json:"udp_connect,omitempty"`
  146. }
  147. type RouteOptionsActionOptions _RouteOptionsActionOptions
  148. func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
  149. err := json.Unmarshal(data, (*_RouteOptionsActionOptions)(r))
  150. if err != nil {
  151. return err
  152. }
  153. if *r == (RouteOptionsActionOptions{}) {
  154. return E.New("empty route option action")
  155. }
  156. return nil
  157. }
  158. type _DNSRouteActionOptions struct {
  159. Server string `json:"server,omitempty"`
  160. // Deprecated: Use DNSRouteOptionsActionOptions instead.
  161. DisableCache bool `json:"disable_cache,omitempty"`
  162. // Deprecated: Use DNSRouteOptionsActionOptions instead.
  163. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  164. // Deprecated: Use DNSRouteOptionsActionOptions instead.
  165. ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
  166. }
  167. type DNSRouteActionOptions _DNSRouteActionOptions
  168. func (r *DNSRouteActionOptions) UnmarshalJSON(data []byte) error {
  169. err := json.Unmarshal(data, (*_DNSRouteActionOptions)(r))
  170. if err != nil {
  171. return err
  172. }
  173. if r.Server == "" {
  174. return E.New("missing server")
  175. }
  176. return nil
  177. }
  178. type _DNSRouteOptionsActionOptions struct {
  179. DisableCache bool `json:"disable_cache,omitempty"`
  180. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  181. ClientSubnet *AddrPrefix `json:"client_subnet,omitempty"`
  182. }
  183. type DNSRouteOptionsActionOptions _DNSRouteOptionsActionOptions
  184. func (r *DNSRouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
  185. err := json.Unmarshal(data, (*_DNSRouteOptionsActionOptions)(r))
  186. if err != nil {
  187. return err
  188. }
  189. if *r == (DNSRouteOptionsActionOptions{}) {
  190. return E.New("empty DNS route option action")
  191. }
  192. return nil
  193. }
  194. type _DirectActionOptions DialerOptions
  195. type DirectActionOptions _DirectActionOptions
  196. func (d DirectActionOptions) Descriptions() []string {
  197. var descriptions []string
  198. if d.BindInterface != "" {
  199. descriptions = append(descriptions, "bind_interface="+d.BindInterface)
  200. }
  201. if d.Inet4BindAddress != nil {
  202. descriptions = append(descriptions, "inet4_bind_address="+d.Inet4BindAddress.Build().String())
  203. }
  204. if d.Inet6BindAddress != nil {
  205. descriptions = append(descriptions, "inet6_bind_address="+d.Inet6BindAddress.Build().String())
  206. }
  207. if d.RoutingMark != 0 {
  208. descriptions = append(descriptions, "routing_mark="+fmt.Sprintf("0x%x", d.RoutingMark))
  209. }
  210. if d.ReuseAddr {
  211. descriptions = append(descriptions, "reuse_addr")
  212. }
  213. if d.ConnectTimeout != 0 {
  214. descriptions = append(descriptions, "connect_timeout="+time.Duration(d.ConnectTimeout).String())
  215. }
  216. if d.TCPFastOpen {
  217. descriptions = append(descriptions, "tcp_fast_open")
  218. }
  219. if d.TCPMultiPath {
  220. descriptions = append(descriptions, "tcp_multi_path")
  221. }
  222. if d.UDPFragment != nil {
  223. descriptions = append(descriptions, "udp_fragment="+fmt.Sprint(*d.UDPFragment))
  224. }
  225. if d.DomainStrategy != DomainStrategy(dns.DomainStrategyAsIS) {
  226. descriptions = append(descriptions, "domain_strategy="+d.DomainStrategy.String())
  227. }
  228. if d.FallbackDelay != 0 {
  229. descriptions = append(descriptions, "fallback_delay="+time.Duration(d.FallbackDelay).String())
  230. }
  231. return descriptions
  232. }
  233. func (d *DirectActionOptions) UnmarshalJSON(data []byte) error {
  234. err := json.Unmarshal(data, (*_DirectActionOptions)(d))
  235. if err != nil {
  236. return err
  237. }
  238. if d.Detour != "" {
  239. return E.New("detour is not available in the current context")
  240. }
  241. return nil
  242. }
  243. type _RejectActionOptions struct {
  244. Method string `json:"method,omitempty"`
  245. NoDrop bool `json:"no_drop,omitempty"`
  246. }
  247. type RejectActionOptions _RejectActionOptions
  248. func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
  249. err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
  250. if err != nil {
  251. return err
  252. }
  253. switch r.Method {
  254. case "", C.RuleActionRejectMethodDefault:
  255. r.Method = C.RuleActionRejectMethodDefault
  256. case C.RuleActionRejectMethodDrop:
  257. default:
  258. return E.New("unknown reject method: " + r.Method)
  259. }
  260. if r.Method == C.RuleActionRejectMethodDrop && r.NoDrop {
  261. return E.New("no_drop is not available in current context")
  262. }
  263. return nil
  264. }
  265. type RouteActionSniff struct {
  266. Sniffer Listable[string] `json:"sniffer,omitempty"`
  267. Timeout Duration `json:"timeout,omitempty"`
  268. }
  269. type RouteActionResolve struct {
  270. Strategy DomainStrategy `json:"strategy,omitempty"`
  271. Server string `json:"server,omitempty"`
  272. }