mitm_surge_urlrewrite.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. package option
  2. import (
  3. "encoding/base64"
  4. "net/http"
  5. "net/url"
  6. "regexp"
  7. "strconv"
  8. "strings"
  9. "unicode"
  10. "github.com/sagernet/sing/common"
  11. E "github.com/sagernet/sing/common/exceptions"
  12. F "github.com/sagernet/sing/common/format"
  13. "github.com/sagernet/sing/common/json"
  14. )
  15. type SurgeURLRewriteLine struct {
  16. Pattern *regexp.Regexp
  17. Destination *url.URL
  18. Redirect bool
  19. Reject bool
  20. }
  21. func (l SurgeURLRewriteLine) String() string {
  22. var fields []string
  23. fields = append(fields, l.Pattern.String())
  24. if l.Reject {
  25. fields = append(fields, "_")
  26. } else {
  27. fields = append(fields, l.Destination.String())
  28. }
  29. switch {
  30. case l.Redirect:
  31. fields = append(fields, "302")
  32. case l.Reject:
  33. fields = append(fields, "reject")
  34. default:
  35. fields = append(fields, "header")
  36. }
  37. return encodeSurgeKeys(fields)
  38. }
  39. func (l SurgeURLRewriteLine) MarshalJSON() ([]byte, error) {
  40. return json.Marshal(l.String())
  41. }
  42. func (l *SurgeURLRewriteLine) UnmarshalJSON(bytes []byte) error {
  43. var stringValue string
  44. err := json.Unmarshal(bytes, &stringValue)
  45. if err != nil {
  46. return err
  47. }
  48. fields, err := surgeFields(stringValue)
  49. if err != nil {
  50. return E.Cause(err, "invalid surge_url_rewrite line: ", stringValue)
  51. } else if len(fields) < 2 || len(fields) > 3 {
  52. return E.New("invalid surge_url_rewrite line: ", stringValue)
  53. }
  54. pattern, err := regexp.Compile(fields[0].Key)
  55. if err != nil {
  56. return E.Cause(err, "invalid surge_url_rewrite line: invalid pattern: ", stringValue)
  57. }
  58. l.Pattern = pattern
  59. l.Destination, err = url.Parse(fields[1].Key)
  60. if err != nil {
  61. return E.Cause(err, "invalid surge_url_rewrite line: invalid destination: ", stringValue)
  62. }
  63. if len(fields) == 3 {
  64. switch fields[2].Key {
  65. case "header":
  66. case "302":
  67. l.Redirect = true
  68. case "reject":
  69. l.Reject = true
  70. default:
  71. return E.New("invalid surge_url_rewrite line: invalid action: ", stringValue)
  72. }
  73. }
  74. return nil
  75. }
  76. type SurgeHeaderRewriteLine struct {
  77. Response bool
  78. Pattern *regexp.Regexp
  79. Add bool
  80. Delete bool
  81. Replace bool
  82. ReplaceRegex bool
  83. Key string
  84. Match *regexp.Regexp
  85. Value string
  86. }
  87. func (l SurgeHeaderRewriteLine) String() string {
  88. var fields []string
  89. if !l.Response {
  90. fields = append(fields, "http-request")
  91. } else {
  92. fields = append(fields, "http-response")
  93. }
  94. fields = append(fields, l.Pattern.String())
  95. if l.Add {
  96. fields = append(fields, "header-add")
  97. } else if l.Delete {
  98. fields = append(fields, "header-del")
  99. } else if l.Replace {
  100. fields = append(fields, "header-replace")
  101. } else if l.ReplaceRegex {
  102. fields = append(fields, "header-replace-regex")
  103. }
  104. fields = append(fields, l.Key)
  105. if l.Add || l.Replace {
  106. fields = append(fields, l.Value)
  107. } else if l.ReplaceRegex {
  108. fields = append(fields, l.Match.String(), l.Value)
  109. }
  110. return encodeSurgeKeys(fields)
  111. }
  112. func (l SurgeHeaderRewriteLine) MarshalJSON() ([]byte, error) {
  113. return json.Marshal(l.String())
  114. }
  115. func (l *SurgeHeaderRewriteLine) UnmarshalJSON(bytes []byte) error {
  116. var stringValue string
  117. err := json.Unmarshal(bytes, &stringValue)
  118. if err != nil {
  119. return err
  120. }
  121. fields, err := surgeFields(stringValue)
  122. if err != nil {
  123. return E.Cause(err, "invalid surge_header_rewrite line: ", stringValue)
  124. } else if len(fields) < 4 {
  125. return E.New("invalid surge_header_rewrite line: ", stringValue)
  126. }
  127. switch fields[0].Key {
  128. case "http-request":
  129. case "http-response":
  130. l.Response = true
  131. default:
  132. return E.New("invalid surge_header_rewrite line: invalid type: ", stringValue)
  133. }
  134. l.Pattern, err = regexp.Compile(fields[1].Key)
  135. if err != nil {
  136. return E.Cause(err, "invalid surge_header_rewrite line: invalid pattern: ", stringValue)
  137. }
  138. switch fields[2].Key {
  139. case "header-add":
  140. l.Add = true
  141. if len(fields) != 5 {
  142. return E.New("invalid surge_header_rewrite line: " + stringValue)
  143. }
  144. l.Key = fields[3].Key
  145. l.Value = fields[4].Key
  146. case "header-del":
  147. l.Delete = true
  148. l.Key = fields[3].Key
  149. case "header-replace":
  150. l.Replace = true
  151. if len(fields) != 5 {
  152. return E.New("invalid surge_header_rewrite line: " + stringValue)
  153. }
  154. l.Key = fields[3].Key
  155. l.Value = fields[4].Key
  156. case "header-replace-regex":
  157. l.ReplaceRegex = true
  158. if len(fields) != 6 {
  159. return E.New("invalid surge_header_rewrite line: " + stringValue)
  160. }
  161. l.Key = fields[3].Key
  162. l.Match, err = regexp.Compile(fields[4].Key)
  163. if err != nil {
  164. return E.Cause(err, "invalid surge_header_rewrite line: invalid match: ", stringValue)
  165. }
  166. l.Value = fields[5].Key
  167. default:
  168. return E.New("invalid surge_header_rewrite line: invalid action: ", stringValue)
  169. }
  170. return nil
  171. }
  172. type SurgeBodyRewriteLine struct {
  173. Response bool
  174. Pattern *regexp.Regexp
  175. Match []*regexp.Regexp
  176. Replace []string
  177. }
  178. func (l SurgeBodyRewriteLine) String() string {
  179. var fields []string
  180. if !l.Response {
  181. fields = append(fields, "http-request")
  182. } else {
  183. fields = append(fields, "http-response")
  184. }
  185. for i := 0; i < len(l.Match); i += 2 {
  186. fields = append(fields, l.Match[i].String(), l.Replace[i])
  187. }
  188. return strings.Join(fields, " ")
  189. }
  190. func (l SurgeBodyRewriteLine) MarshalJSON() ([]byte, error) {
  191. return json.Marshal(l.String())
  192. }
  193. func (l *SurgeBodyRewriteLine) UnmarshalJSON(bytes []byte) error {
  194. var stringValue string
  195. err := json.Unmarshal(bytes, &stringValue)
  196. if err != nil {
  197. return err
  198. }
  199. fields, err := surgeFields(stringValue)
  200. if err != nil {
  201. return E.Cause(err, "invalid surge_body_rewrite line: ", stringValue)
  202. } else if len(fields) < 4 {
  203. return E.New("invalid surge_body_rewrite line: ", stringValue)
  204. } else if len(fields)%2 != 0 {
  205. return E.New("invalid surge_body_rewrite line: ", stringValue)
  206. }
  207. switch fields[0].Key {
  208. case "http-request":
  209. case "http-response":
  210. l.Response = true
  211. default:
  212. return E.New("invalid surge_body_rewrite line: invalid type: ", stringValue)
  213. }
  214. l.Pattern, err = regexp.Compile(fields[1].Key)
  215. for i := 2; i < len(fields); i += 2 {
  216. var match *regexp.Regexp
  217. match, err = regexp.Compile(fields[i].Key)
  218. if err != nil {
  219. return E.Cause(err, "invalid surge_body_rewrite line: invalid match: ", stringValue)
  220. }
  221. l.Match = append(l.Match, match)
  222. l.Replace = append(l.Replace, fields[i+1].Key)
  223. }
  224. return nil
  225. }
  226. type SurgeMapLocalLine struct {
  227. Pattern *regexp.Regexp
  228. StatusCode int
  229. File bool
  230. Text bool
  231. TinyGif bool
  232. Base64 bool
  233. Data string
  234. Base64Data []byte
  235. Headers http.Header
  236. }
  237. func (l SurgeMapLocalLine) String() string {
  238. var fields []surgeField
  239. fields = append(fields, surgeField{Key: l.Pattern.String()})
  240. if l.File {
  241. fields = append(fields, surgeField{Key: "data-type", Value: "file"})
  242. fields = append(fields, surgeField{Key: "data", Value: l.Data})
  243. } else if l.Text {
  244. fields = append(fields, surgeField{Key: "data-type", Value: "text"})
  245. fields = append(fields, surgeField{Key: "data", Value: l.Data})
  246. } else if l.TinyGif {
  247. fields = append(fields, surgeField{Key: "data-type", Value: "tiny-gif"})
  248. } else if l.Base64 {
  249. fields = append(fields, surgeField{Key: "data-type", Value: "base64"})
  250. fields = append(fields, surgeField{Key: "data-type", Value: base64.StdEncoding.EncodeToString(l.Base64Data)})
  251. }
  252. if l.StatusCode != 0 {
  253. fields = append(fields, surgeField{Key: "status-code", Value: F.ToString(l.StatusCode), ValueSet: true})
  254. }
  255. if len(l.Headers) > 0 {
  256. var headers []string
  257. for key, values := range l.Headers {
  258. for _, value := range values {
  259. headers = append(headers, key+":"+value)
  260. }
  261. }
  262. fields = append(fields, surgeField{Key: "headers", Value: strings.Join(headers, "|")})
  263. }
  264. return encodeSurgeFields(fields)
  265. }
  266. func (l SurgeMapLocalLine) MarshalJSON() ([]byte, error) {
  267. return json.Marshal(l.String())
  268. }
  269. func (l *SurgeMapLocalLine) UnmarshalJSON(bytes []byte) error {
  270. var stringValue string
  271. err := json.Unmarshal(bytes, &stringValue)
  272. if err != nil {
  273. return err
  274. }
  275. fields, err := surgeFields(stringValue)
  276. if err != nil {
  277. return E.Cause(err, "invalid surge_map_local line: ", stringValue)
  278. } else if len(fields) < 1 {
  279. return E.New("invalid surge_map_local line: ", stringValue)
  280. }
  281. l.Pattern, err = regexp.Compile(fields[0].Key)
  282. if err != nil {
  283. return E.Cause(err, "invalid surge_map_local line: invalid pattern: ", stringValue)
  284. }
  285. dataTypeField := common.Find(fields, func(it surgeField) bool {
  286. return it.Key == "data-type"
  287. })
  288. if !dataTypeField.ValueSet {
  289. return E.New("invalid surge_map_local line: missing data-type: ", stringValue)
  290. }
  291. switch dataTypeField.Value {
  292. case "file":
  293. l.File = true
  294. case "text":
  295. l.Text = true
  296. case "tiny-gif":
  297. l.TinyGif = true
  298. case "base64":
  299. l.Base64 = true
  300. default:
  301. return E.New("unsupported data-type ", dataTypeField.Value)
  302. }
  303. for i := 1; i < len(fields); i++ {
  304. switch fields[i].Key {
  305. case "data-type":
  306. continue
  307. case "data":
  308. if l.File {
  309. l.Data = fields[i].Value
  310. } else if l.Text {
  311. l.Data = fields[i].Value
  312. } else if l.Base64 {
  313. l.Base64Data, err = base64.StdEncoding.DecodeString(fields[i].Value)
  314. if err != nil {
  315. return E.New("invalid surge_map_local line: invalid base64 data: ", stringValue)
  316. }
  317. }
  318. case "status-code":
  319. statusCode, err := strconv.ParseInt(fields[i].Value, 10, 16)
  320. if err != nil {
  321. return E.New("invalid surge_map_local line: invalid status code: ", stringValue)
  322. }
  323. l.StatusCode = int(statusCode)
  324. case "header":
  325. headers := make(http.Header)
  326. for _, headerLine := range strings.Split(fields[i].Value, "|") {
  327. if !strings.Contains(headerLine, ":") {
  328. return E.New("invalid surge_map_local line: headers: missing `:` in item: ", stringValue, ": ", headerLine)
  329. }
  330. headers.Add(common.SubstringBefore(headerLine, ":"), common.SubstringAfter(headerLine, ":"))
  331. }
  332. l.Headers = headers
  333. default:
  334. return E.New("invalid surge_map_local line: unknown options: ", fields[i].Key)
  335. }
  336. }
  337. return nil
  338. }
  339. type surgeField struct {
  340. Key string
  341. Value string
  342. ValueSet bool
  343. }
  344. func encodeSurgeKeys(keys []string) string {
  345. keys = common.Map(keys, func(it string) string {
  346. if strings.ContainsFunc(it, unicode.IsSpace) {
  347. return "\"" + it + "\""
  348. } else {
  349. return it
  350. }
  351. })
  352. return strings.Join(keys, " ")
  353. }
  354. func encodeSurgeFields(fields []surgeField) string {
  355. return strings.Join(common.Map(fields, func(it surgeField) string {
  356. if !it.ValueSet {
  357. if strings.ContainsFunc(it.Key, unicode.IsSpace) {
  358. return "\"" + it.Key + "\""
  359. } else {
  360. return it.Key
  361. }
  362. } else {
  363. if strings.ContainsFunc(it.Value, unicode.IsSpace) {
  364. return it.Key + "=\"" + it.Value + "\""
  365. } else {
  366. return it.Key + "=" + it.Value
  367. }
  368. }
  369. }), " ")
  370. }
  371. func surgeFields(s string) ([]surgeField, error) {
  372. var (
  373. fields []surgeField
  374. currentField *surgeField
  375. )
  376. for _, field := range strings.Fields(s) {
  377. if currentField != nil {
  378. field = " " + field
  379. if strings.HasSuffix(field, "\"") {
  380. field = field[:len(field)-1]
  381. if !currentField.ValueSet {
  382. currentField.Key += field
  383. } else {
  384. currentField.Value += field
  385. }
  386. fields = append(fields, *currentField)
  387. currentField = nil
  388. } else {
  389. if !currentField.ValueSet {
  390. currentField.Key += field
  391. } else {
  392. currentField.Value += field
  393. }
  394. }
  395. continue
  396. }
  397. if !strings.Contains(field, "=") {
  398. if strings.HasPrefix(field, "\"") {
  399. field = field[1:]
  400. if strings.HasSuffix(field, "\"") {
  401. field = field[:len(field)-1]
  402. } else {
  403. currentField = &surgeField{Key: field}
  404. continue
  405. }
  406. }
  407. fields = append(fields, surgeField{Key: field})
  408. } else {
  409. key := common.SubstringBefore(field, "=")
  410. value := common.SubstringAfter(field, "=")
  411. if strings.HasPrefix(value, "\"") {
  412. value = value[1:]
  413. if strings.HasSuffix(field, "\"") {
  414. value = value[:len(value)-1]
  415. } else {
  416. currentField = &surgeField{Key: key, Value: value, ValueSet: true}
  417. continue
  418. }
  419. }
  420. fields = append(fields, surgeField{Key: key, Value: value, ValueSet: true})
  421. }
  422. }
  423. if currentField != nil {
  424. return nil, E.New("invalid surge fields line: ", s)
  425. }
  426. return fields, nil
  427. }