mitm_surge_urlrewrite.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  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. fields = append(fields, surgeField{Key: "status-code", Value: F.ToString(l.StatusCode), ValueSet: true})
  253. if len(l.Headers) > 0 {
  254. var headers []string
  255. for key, values := range l.Headers {
  256. for _, value := range values {
  257. headers = append(headers, key+":"+value)
  258. }
  259. }
  260. fields = append(fields, surgeField{Key: "headers", Value: strings.Join(headers, "|")})
  261. }
  262. return encodeSurgeFields(fields)
  263. }
  264. func (l SurgeMapLocalLine) MarshalJSON() ([]byte, error) {
  265. return json.Marshal(l.String())
  266. }
  267. func (l *SurgeMapLocalLine) UnmarshalJSON(bytes []byte) error {
  268. var stringValue string
  269. err := json.Unmarshal(bytes, &stringValue)
  270. if err != nil {
  271. return err
  272. }
  273. fields, err := surgeFields(stringValue)
  274. if err != nil {
  275. return E.Cause(err, "invalid surge_map_local line: ", stringValue)
  276. } else if len(fields) < 1 {
  277. return E.New("invalid surge_map_local line: ", stringValue)
  278. }
  279. l.Pattern, err = regexp.Compile(fields[0].Key)
  280. if err != nil {
  281. return E.Cause(err, "invalid surge_map_local line: invalid pattern: ", stringValue)
  282. }
  283. dataTypeField := common.Find(fields, func(it surgeField) bool {
  284. return it.Key == "data-type"
  285. })
  286. if !dataTypeField.ValueSet {
  287. return E.New("invalid surge_map_local line: missing data-type: ", stringValue)
  288. }
  289. switch dataTypeField.Value {
  290. case "file":
  291. l.File = true
  292. case "text":
  293. l.Text = true
  294. case "tiny-gif":
  295. l.TinyGif = true
  296. case "base64":
  297. l.Base64 = true
  298. }
  299. for i := 1; i < len(fields); i++ {
  300. switch fields[i].Key {
  301. case "data-type":
  302. continue
  303. case "data":
  304. if l.File {
  305. l.Data = fields[i].Value
  306. } else if l.Text {
  307. l.Data = fields[i].Value
  308. } else if l.Base64 {
  309. l.Base64Data, err = base64.StdEncoding.DecodeString(fields[i].Value)
  310. if err != nil {
  311. return E.New("invalid surge_map_local line: invalid base64 data: ", stringValue)
  312. }
  313. }
  314. case "status-code":
  315. statusCode, err := strconv.ParseInt(fields[i].Value, 10, 16)
  316. if err != nil {
  317. return E.New("invalid surge_map_local line: invalid status code: ", stringValue)
  318. }
  319. l.StatusCode = int(statusCode)
  320. case "headers":
  321. headers := make(http.Header)
  322. for _, headerLine := range strings.Split(fields[i].Value, "|") {
  323. if !strings.Contains(headerLine, ":") {
  324. return E.New("invalid surge_map_local line: headers: missing `:` in item: ", stringValue, ": ", headerLine)
  325. }
  326. headers.Add(common.SubstringBefore(headerLine, ":"), common.SubstringAfter(headerLine, ":"))
  327. }
  328. l.Headers = headers
  329. default:
  330. return E.New("invalid surge_map_local line: unknown options: ", stringValue)
  331. }
  332. }
  333. return nil
  334. }
  335. type surgeField struct {
  336. Key string
  337. Value string
  338. ValueSet bool
  339. }
  340. func encodeSurgeKeys(keys []string) string {
  341. keys = common.Map(keys, func(it string) string {
  342. if strings.ContainsFunc(it, unicode.IsSpace) {
  343. return "\"" + it + "\""
  344. } else {
  345. return it
  346. }
  347. })
  348. return strings.Join(keys, " ")
  349. }
  350. func encodeSurgeFields(fields []surgeField) string {
  351. return strings.Join(common.Map(fields, func(it surgeField) string {
  352. if !it.ValueSet {
  353. if strings.ContainsFunc(it.Key, unicode.IsSpace) {
  354. return "\"" + it.Key + "\""
  355. } else {
  356. return it.Key
  357. }
  358. } else {
  359. if strings.ContainsFunc(it.Value, unicode.IsSpace) {
  360. return it.Key + "=\"" + it.Value + "\""
  361. } else {
  362. return it.Key + "=" + it.Value
  363. }
  364. }
  365. }), " ")
  366. }
  367. func surgeFields(s string) ([]surgeField, error) {
  368. var (
  369. fields []surgeField
  370. currentField *surgeField
  371. )
  372. for _, field := range strings.Fields(s) {
  373. if currentField != nil {
  374. field = " " + field
  375. if strings.HasSuffix(field, "\"") {
  376. field = field[:len(field)-1]
  377. if !currentField.ValueSet {
  378. currentField.Key += field
  379. } else {
  380. currentField.Value += field
  381. }
  382. fields = append(fields, *currentField)
  383. currentField = nil
  384. } else {
  385. if !currentField.ValueSet {
  386. currentField.Key += field
  387. } else {
  388. currentField.Value += " " + field
  389. }
  390. }
  391. }
  392. if !strings.Contains(field, "=") {
  393. if strings.HasPrefix(field, "\"") {
  394. field = field[1:]
  395. if strings.HasSuffix(field, "\"") {
  396. field = field[:len(field)-1]
  397. } else {
  398. currentField = &surgeField{Key: field}
  399. continue
  400. }
  401. }
  402. fields = append(fields, surgeField{Key: field})
  403. } else {
  404. key := common.SubstringBefore(field, "=")
  405. value := common.SubstringAfter(field, "=")
  406. if strings.HasPrefix(value, "\"") {
  407. value = value[1:]
  408. if strings.HasSuffix(field, "\"") {
  409. value = value[:len(value)-1]
  410. } else {
  411. currentField = &surgeField{Key: key, Value: field, ValueSet: true}
  412. continue
  413. }
  414. }
  415. fields = append(fields, surgeField{Key: key, Value: value, ValueSet: true})
  416. }
  417. }
  418. if currentField != nil {
  419. return nil, E.New("invalid surge fields line: ", s)
  420. }
  421. return fields, nil
  422. }