shadowsocks.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. ErrorNotSSLink = errors.New("not a correct ss link")
  16. )
  17. type Shadowsocks struct {
  18. Base
  19. Password string `yaml:"password" json:"password"`
  20. Cipher string `yaml:"cipher" json:"cipher"`
  21. Plugin string `yaml:"plugin,omitempty" json:"plugin,omitempty"`
  22. PluginOpts map[string]interface{} `yaml:"plugin-opts,omitempty" json:"plugin-opts,omitempty"`
  23. }
  24. func (ss Shadowsocks) Identifier() string {
  25. return net.JoinHostPort(ss.Server, strconv.Itoa(ss.Port)) + ss.Password
  26. }
  27. func (ss Shadowsocks) String() string {
  28. data, err := json.Marshal(ss)
  29. if err != nil {
  30. return ""
  31. }
  32. return string(data)
  33. }
  34. func (ss Shadowsocks) ToClash() string {
  35. data, err := json.Marshal(ss)
  36. if err != nil {
  37. return ""
  38. }
  39. return "- " + string(data)
  40. }
  41. func (ss Shadowsocks) ToSurge() string {
  42. // node1 = ss, server, port, encrypt-method=, password=, obfs=, obfs-host=, udp-relay=false
  43. if ss.Plugin == "obfs" {
  44. text := fmt.Sprintf("%s = ss, %s, %d, encrypt-method=%s, password=%s, obfs=%s, udp-relay=false",
  45. ss.Name, ss.Server, ss.Port, ss.Cipher, ss.Password, ss.PluginOpts["mode"])
  46. if ss.PluginOpts["host"].(string) != "" {
  47. text += ", obfs-host=" + ss.PluginOpts["host"].(string)
  48. }
  49. return text
  50. } else {
  51. return fmt.Sprintf("%s = ss, %s, %d, encrypt-method=%s, password=%s, udp-relay=false",
  52. ss.Name, ss.Server, ss.Port, ss.Cipher, ss.Password)
  53. }
  54. }
  55. func (ss Shadowsocks) Clone() Proxy {
  56. return &ss
  57. }
  58. // https://shadowsocks.org/en/config/quick-guide.html
  59. func (ss Shadowsocks) Link() (link string) {
  60. payload := fmt.Sprintf("%s:%s@%s:%d", ss.Cipher, ss.Password, ss.Server, ss.Port)
  61. payload = tool.Base64EncodeString(payload, false)
  62. return fmt.Sprintf("ss://%s#%s", payload, ss.Name)
  63. }
  64. func ParseSSLink(link string) (*Shadowsocks, error) {
  65. if !strings.HasPrefix(link, "ss://") {
  66. return nil, ErrorNotSSRLink
  67. }
  68. uri, err := url.Parse(link)
  69. if err != nil {
  70. return nil, ErrorNotSSLink
  71. }
  72. cipher := ""
  73. password := ""
  74. if uri.User.String() == "" {
  75. // base64的情况
  76. infos, err := tool.Base64DecodeString(uri.Hostname())
  77. if err != nil {
  78. return nil, err
  79. }
  80. uri, err = url.Parse("ss://" + infos)
  81. if err != nil {
  82. return nil, err
  83. }
  84. cipher = uri.User.Username()
  85. password, _ = uri.User.Password()
  86. } else {
  87. cipherInfoString, err := tool.Base64DecodeString(uri.User.Username())
  88. if err != nil {
  89. return nil, ErrorPasswordParseFail
  90. }
  91. cipherInfo := strings.SplitN(cipherInfoString, ":", 2)
  92. if len(cipherInfo) < 2 {
  93. return nil, ErrorPasswordParseFail
  94. }
  95. cipher = strings.ToLower(cipherInfo[0])
  96. password = cipherInfo[1]
  97. }
  98. server := uri.Hostname()
  99. port, _ := strconv.Atoi(uri.Port())
  100. moreInfos := uri.Query()
  101. pluginString := moreInfos.Get("plugin")
  102. plugin := ""
  103. pluginOpts := make(map[string]interface{})
  104. if strings.Contains(pluginString, ";") {
  105. pluginInfos, err := url.ParseQuery(pluginString)
  106. if err == nil {
  107. if strings.Contains(pluginString, "obfs") {
  108. plugin = "obfs"
  109. pluginOpts["mode"] = pluginInfos.Get("obfs")
  110. pluginOpts["host"] = pluginInfos.Get("obfs-host")
  111. } else if strings.Contains(pluginString, "v2ray") {
  112. plugin = "v2ray-plugin"
  113. pluginOpts["mode"] = pluginInfos.Get("mode")
  114. pluginOpts["host"] = pluginInfos.Get("host")
  115. pluginOpts["tls"] = strings.Contains(pluginString, "tls")
  116. }
  117. }
  118. }
  119. if port == 0 || cipher == "" {
  120. return nil, ErrorNotSSLink
  121. }
  122. return &Shadowsocks{
  123. Base: Base{
  124. Name: strconv.Itoa(rand.Int()),
  125. Server: server,
  126. Port: port,
  127. Type: "ss",
  128. },
  129. Password: password,
  130. Cipher: cipher,
  131. Plugin: plugin,
  132. PluginOpts: pluginOpts,
  133. }, nil
  134. }
  135. var (
  136. ssPlainRe = regexp.MustCompile("ss://([A-Za-z0-9+/_&?=@:%.-])+")
  137. )
  138. func GrepSSLinkFromString(text string) []string {
  139. results := make([]string, 0)
  140. texts := strings.Split(text, "ss://")
  141. for _, text := range texts {
  142. results = append(results, ssPlainRe.FindAllString("ss://"+text, -1)...)
  143. }
  144. return results
  145. }