shadowsocksr.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package proxy
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "math/rand"
  7. "net"
  8. "net/url"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. "github.com/zu1k/proxypool/pkg/tool"
  13. )
  14. var (
  15. ErrorNotSSRLink = errors.New("not a correct ssr link")
  16. ErrorPasswordParseFail = errors.New("password parse failed")
  17. ErrorPathNotComplete = errors.New("path not complete")
  18. ErrorMissingQuery = errors.New("link missing query")
  19. ErrorProtocolParamParseFail = errors.New("protocol param parse failed")
  20. ErrorObfsParamParseFail = errors.New("obfs param parse failed")
  21. )
  22. type ShadowsocksR struct {
  23. Base
  24. Password string `yaml:"password" json:"password"`
  25. Cipher string `yaml:"cipher" json:"cipher"`
  26. Protocol string `yaml:"protocol" json:"protocol"`
  27. ProtocolParam string `yaml:"protocol-param,omitempty" json:"protocol_param,omitempty"`
  28. Obfs string `yaml:"obfs" json:"obfs"`
  29. ObfsParam string `yaml:"obfs-param,omitempty" json:"obfs_param,omitempty"`
  30. Group string `yaml:"group,omitempty" json:"group,omitempty"`
  31. }
  32. func (ssr ShadowsocksR) Identifier() string {
  33. return net.JoinHostPort(ssr.Server, strconv.Itoa(ssr.Port)) + ssr.Password + ssr.ProtocolParam
  34. }
  35. func (ssr ShadowsocksR) String() string {
  36. data, err := json.Marshal(ssr)
  37. if err != nil {
  38. return ""
  39. }
  40. return string(data)
  41. }
  42. func (ssr ShadowsocksR) ToClash() string {
  43. data, err := json.Marshal(ssr)
  44. if err != nil {
  45. return ""
  46. }
  47. return "- " + string(data)
  48. }
  49. func (ssr ShadowsocksR) ToSurge() string {
  50. return ""
  51. }
  52. func (ssr ShadowsocksR) Clone() Proxy {
  53. return &ssr
  54. }
  55. // https://github.com/HMBSbige/ShadowsocksR-Windows/wiki/SSR-QRcode-scheme
  56. func (ssr ShadowsocksR) Link() (link string) {
  57. payload := fmt.Sprintf("%s:%d:%s:%s:%s:%s",
  58. ssr.Server, ssr.Port, ssr.Protocol, ssr.Cipher, ssr.Obfs, tool.Base64EncodeString(ssr.Password, true))
  59. query := url.Values{}
  60. query.Add("obfsparam", tool.Base64EncodeString(ssr.ObfsParam, true))
  61. query.Add("protoparam", tool.Base64EncodeString(ssr.ProtocolParam, true))
  62. query.Add("remarks", tool.Base64EncodeString(ssr.Name, true))
  63. query.Add("group", tool.Base64EncodeString("proxy.tgbot.co", true))
  64. payload = tool.Base64EncodeString(fmt.Sprintf("%s/?%s", payload, query.Encode()), true)
  65. return fmt.Sprintf("ssr://%s", payload)
  66. }
  67. func ParseSSRLink(link string) (*ShadowsocksR, error) {
  68. if !strings.HasPrefix(link, "ssr") {
  69. return nil, ErrorNotSSRLink
  70. }
  71. ssrmix := strings.SplitN(link, "://", 2)
  72. if len(ssrmix) < 2 {
  73. return nil, ErrorNotSSRLink
  74. }
  75. linkPayloadBase64 := ssrmix[1]
  76. payload, err := tool.Base64DecodeString(linkPayloadBase64)
  77. if err != nil {
  78. return nil, ErrorMissingQuery
  79. }
  80. infoPayload := strings.SplitN(payload, "/?", 2)
  81. if len(infoPayload) < 2 {
  82. return nil, ErrorNotSSRLink
  83. }
  84. ssrpath := strings.Split(infoPayload[0], ":")
  85. if len(ssrpath) < 6 {
  86. return nil, ErrorPathNotComplete
  87. }
  88. // base info
  89. server := strings.ToLower(ssrpath[0])
  90. port, _ := strconv.Atoi(ssrpath[1])
  91. protocol := strings.ToLower(ssrpath[2])
  92. cipher := strings.ToLower(ssrpath[3])
  93. obfs := strings.ToLower(ssrpath[4])
  94. password, err := tool.Base64DecodeString(ssrpath[5])
  95. if err != nil {
  96. return nil, ErrorPasswordParseFail
  97. }
  98. moreInfo, _ := url.ParseQuery(infoPayload[1])
  99. // remarks
  100. remarks := moreInfo.Get("remarks")
  101. remarks, err = tool.Base64DecodeString(remarks)
  102. if err != nil {
  103. remarks = ""
  104. err = nil
  105. }
  106. if strings.ContainsAny(remarks, "\t\r\n ") {
  107. remarks = strings.ReplaceAll(remarks, "\t", "")
  108. remarks = strings.ReplaceAll(remarks, "\r", "")
  109. remarks = strings.ReplaceAll(remarks, "\n", "")
  110. remarks = strings.ReplaceAll(remarks, " ", "")
  111. }
  112. // protocol param
  113. protocolParam, err := tool.Base64DecodeString(moreInfo.Get("protoparam"))
  114. if err != nil {
  115. return nil, ErrorProtocolParamParseFail
  116. }
  117. if tool.ContainChineseChar(protocolParam) {
  118. protocolParam = ""
  119. }
  120. if strings.HasSuffix(protocol, "_compatible") {
  121. protocol = strings.ReplaceAll(protocol, "_compatible", "")
  122. }
  123. // obfs param
  124. obfsParam, err := tool.Base64DecodeString(moreInfo.Get("obfsparam"))
  125. if err != nil {
  126. return nil, ErrorObfsParamParseFail
  127. }
  128. if tool.ContainChineseChar(obfsParam) {
  129. obfsParam = ""
  130. }
  131. if strings.HasSuffix(obfs, "_compatible") {
  132. obfs = strings.ReplaceAll(obfs, "_compatible", "")
  133. }
  134. //group, err := tool.Base64DecodeString(moreInfo.Get("group"))
  135. //if err != nil {
  136. // group = ""
  137. //}
  138. group := ""
  139. return &ShadowsocksR{
  140. Base: Base{
  141. Name: remarks + "_" + strconv.Itoa(rand.Int()),
  142. Server: server,
  143. Port: port,
  144. Type: "ssr",
  145. },
  146. Password: password,
  147. Cipher: cipher,
  148. Protocol: protocol,
  149. ProtocolParam: protocolParam,
  150. Obfs: obfs,
  151. ObfsParam: obfsParam,
  152. Group: group,
  153. }, nil
  154. }
  155. var (
  156. ssrPlainRe = regexp.MustCompile("ssr://([A-Za-z0-9+/_-])+")
  157. )
  158. func GrepSSRLinkFromString(text string) []string {
  159. results := make([]string, 0)
  160. texts := strings.Split(text, "ssr://")
  161. for _, text := range texts {
  162. results = append(results, ssrPlainRe.FindAllString("ssr://"+text, -1)...)
  163. }
  164. return results
  165. }