rule_set.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. package option
  2. import (
  3. "reflect"
  4. C "github.com/sagernet/sing-box/constant"
  5. "github.com/sagernet/sing/common"
  6. "github.com/sagernet/sing/common/domain"
  7. E "github.com/sagernet/sing/common/exceptions"
  8. F "github.com/sagernet/sing/common/format"
  9. "github.com/sagernet/sing/common/json"
  10. "go4.org/netipx"
  11. )
  12. type _RuleSet struct {
  13. Type string `json:"type,omitempty"`
  14. Tag string `json:"tag"`
  15. Format string `json:"format,omitempty"`
  16. InlineOptions PlainRuleSet `json:"-"`
  17. LocalOptions LocalRuleSet `json:"-"`
  18. RemoteOptions RemoteRuleSet `json:"-"`
  19. }
  20. type RuleSet _RuleSet
  21. func (r RuleSet) MarshalJSON() ([]byte, error) {
  22. var v any
  23. switch r.Type {
  24. case "", C.RuleSetTypeInline:
  25. r.Type = ""
  26. v = r.InlineOptions
  27. case C.RuleSetTypeLocal:
  28. v = r.LocalOptions
  29. case C.RuleSetTypeRemote:
  30. v = r.RemoteOptions
  31. default:
  32. return nil, E.New("unknown rule-set type: " + r.Type)
  33. }
  34. return MarshallObjects((_RuleSet)(r), v)
  35. }
  36. func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
  37. err := json.Unmarshal(bytes, (*_RuleSet)(r))
  38. if err != nil {
  39. return err
  40. }
  41. if r.Tag == "" {
  42. return E.New("missing tag")
  43. }
  44. if r.Type != C.RuleSetTypeInline {
  45. switch r.Format {
  46. case "":
  47. return E.New("missing format")
  48. case C.RuleSetFormatSource, C.RuleSetFormatBinary:
  49. default:
  50. return E.New("unknown rule-set format: " + r.Format)
  51. }
  52. } else {
  53. r.Format = ""
  54. }
  55. var v any
  56. switch r.Type {
  57. case "", C.RuleSetTypeInline:
  58. r.Type = C.RuleSetTypeInline
  59. v = &r.InlineOptions
  60. case C.RuleSetTypeLocal:
  61. v = &r.LocalOptions
  62. case C.RuleSetTypeRemote:
  63. v = &r.RemoteOptions
  64. default:
  65. return E.New("unknown rule-set type: " + r.Type)
  66. }
  67. err = UnmarshallExcluded(bytes, (*_RuleSet)(r), v)
  68. if err != nil {
  69. return err
  70. }
  71. return nil
  72. }
  73. type LocalRuleSet struct {
  74. Path string `json:"path,omitempty"`
  75. }
  76. type RemoteRuleSet struct {
  77. URL string `json:"url"`
  78. DownloadDetour string `json:"download_detour,omitempty"`
  79. UpdateInterval Duration `json:"update_interval,omitempty"`
  80. }
  81. type _HeadlessRule struct {
  82. Type string `json:"type,omitempty"`
  83. DefaultOptions DefaultHeadlessRule `json:"-"`
  84. LogicalOptions LogicalHeadlessRule `json:"-"`
  85. }
  86. type HeadlessRule _HeadlessRule
  87. func (r HeadlessRule) MarshalJSON() ([]byte, error) {
  88. var v any
  89. switch r.Type {
  90. case C.RuleTypeDefault:
  91. r.Type = ""
  92. v = r.DefaultOptions
  93. case C.RuleTypeLogical:
  94. v = r.LogicalOptions
  95. default:
  96. return nil, E.New("unknown rule type: " + r.Type)
  97. }
  98. return MarshallObjects((_HeadlessRule)(r), v)
  99. }
  100. func (r *HeadlessRule) UnmarshalJSON(bytes []byte) error {
  101. err := json.Unmarshal(bytes, (*_HeadlessRule)(r))
  102. if err != nil {
  103. return err
  104. }
  105. var v any
  106. switch r.Type {
  107. case "", C.RuleTypeDefault:
  108. r.Type = C.RuleTypeDefault
  109. v = &r.DefaultOptions
  110. case C.RuleTypeLogical:
  111. v = &r.LogicalOptions
  112. default:
  113. return E.New("unknown rule type: " + r.Type)
  114. }
  115. err = UnmarshallExcluded(bytes, (*_HeadlessRule)(r), v)
  116. if err != nil {
  117. return err
  118. }
  119. return nil
  120. }
  121. func (r HeadlessRule) IsValid() bool {
  122. switch r.Type {
  123. case C.RuleTypeDefault, "":
  124. return r.DefaultOptions.IsValid()
  125. case C.RuleTypeLogical:
  126. return r.LogicalOptions.IsValid()
  127. default:
  128. panic("unknown rule type: " + r.Type)
  129. }
  130. }
  131. type DefaultHeadlessRule struct {
  132. QueryType Listable[DNSQueryType] `json:"query_type,omitempty"`
  133. Network Listable[string] `json:"network,omitempty"`
  134. Domain Listable[string] `json:"domain,omitempty"`
  135. DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
  136. DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
  137. DomainRegex Listable[string] `json:"domain_regex,omitempty"`
  138. SourceIPCIDR Listable[string] `json:"source_ip_cidr,omitempty"`
  139. IPCIDR Listable[string] `json:"ip_cidr,omitempty"`
  140. SourcePort Listable[uint16] `json:"source_port,omitempty"`
  141. SourcePortRange Listable[string] `json:"source_port_range,omitempty"`
  142. Port Listable[uint16] `json:"port,omitempty"`
  143. PortRange Listable[string] `json:"port_range,omitempty"`
  144. ProcessName Listable[string] `json:"process_name,omitempty"`
  145. ProcessPath Listable[string] `json:"process_path,omitempty"`
  146. PackageName Listable[string] `json:"package_name,omitempty"`
  147. WIFISSID Listable[string] `json:"wifi_ssid,omitempty"`
  148. WIFIBSSID Listable[string] `json:"wifi_bssid,omitempty"`
  149. Invert bool `json:"invert,omitempty"`
  150. DomainMatcher *domain.Matcher `json:"-"`
  151. SourceIPSet *netipx.IPSet `json:"-"`
  152. IPSet *netipx.IPSet `json:"-"`
  153. }
  154. func (r DefaultHeadlessRule) IsValid() bool {
  155. var defaultValue DefaultHeadlessRule
  156. defaultValue.Invert = r.Invert
  157. return !reflect.DeepEqual(r, defaultValue)
  158. }
  159. type LogicalHeadlessRule struct {
  160. Mode string `json:"mode"`
  161. Rules []HeadlessRule `json:"rules,omitempty"`
  162. Invert bool `json:"invert,omitempty"`
  163. }
  164. func (r LogicalHeadlessRule) IsValid() bool {
  165. return len(r.Rules) > 0 && common.All(r.Rules, HeadlessRule.IsValid)
  166. }
  167. type _PlainRuleSetCompat struct {
  168. Version int `json:"version"`
  169. Options PlainRuleSet `json:"-"`
  170. }
  171. type PlainRuleSetCompat _PlainRuleSetCompat
  172. func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
  173. var v any
  174. switch r.Version {
  175. case C.RuleSetVersion1, C.RuleSetVersion2:
  176. v = r.Options
  177. default:
  178. return nil, E.New("unknown rule-set version: ", r.Version)
  179. }
  180. return MarshallObjects((_PlainRuleSetCompat)(r), v)
  181. }
  182. func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
  183. err := json.Unmarshal(bytes, (*_PlainRuleSetCompat)(r))
  184. if err != nil {
  185. return err
  186. }
  187. var v any
  188. switch r.Version {
  189. case C.RuleSetVersion1, C.RuleSetVersion2:
  190. v = &r.Options
  191. case 0:
  192. return E.New("missing rule-set version")
  193. default:
  194. return E.New("unknown rule-set version: ", r.Version)
  195. }
  196. err = UnmarshallExcluded(bytes, (*_PlainRuleSetCompat)(r), v)
  197. if err != nil {
  198. return err
  199. }
  200. return nil
  201. }
  202. func (r PlainRuleSetCompat) Upgrade() (PlainRuleSet, error) {
  203. switch r.Version {
  204. case C.RuleSetVersion1, C.RuleSetVersion2:
  205. default:
  206. return PlainRuleSet{}, E.New("unknown rule-set version: " + F.ToString(r.Version))
  207. }
  208. return r.Options, nil
  209. }
  210. type PlainRuleSet struct {
  211. Rules []HeadlessRule `json:"rules,omitempty"`
  212. }