vmess.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. ErrorNotVmessLink = errors.New("not a correct vmess link")
  16. ErrorVmessPayloadParseFail = errors.New("vmess link payload parse failed")
  17. )
  18. type Vmess struct {
  19. Base
  20. UUID string `yaml:"uuid" json:"uuid"`
  21. AlterID int `yaml:"alterId" json:"alterId"`
  22. Cipher string `yaml:"cipher" json:"cipher"`
  23. TLS bool `yaml:"tls,omitempty" json:"tls,omitempty"`
  24. Network string `yaml:"network,omitempty" json:"network,omitempty"`
  25. HTTPOpts HTTPOptions `yaml:"http-opts,omitempty" json:"http-opts,omitempty"`
  26. WSPath string `yaml:"ws-path,omitempty" json:"ws-path,omitempty"`
  27. WSHeaders map[string]string `yaml:"ws-headers,omitempty" json:"ws-headers,omitempty"`
  28. SkipCertVerify bool `yaml:"skip-cert-verify,omitempty" json:"skip-cert-verify,omitempty"`
  29. ServerName string `yaml:"servername,omitempty" json:"servername,omitempty"`
  30. }
  31. type HTTPOptions struct {
  32. Method string `yaml:"method,omitempty" json:"method,omitempty"`
  33. Path []string `yaml:"path,omitempty" json:"path,omitempty"`
  34. Headers map[string][]string `yaml:"headers,omitempty" json:"headers,omitempty"`
  35. }
  36. func (v Vmess) Identifier() string {
  37. return net.JoinHostPort(v.Server, strconv.Itoa(v.Port)) + v.Cipher + v.UUID
  38. }
  39. func (v Vmess) String() string {
  40. data, err := json.Marshal(v)
  41. if err != nil {
  42. return ""
  43. }
  44. return string(data)
  45. }
  46. func (v Vmess) ToClash() string {
  47. data, err := json.Marshal(v)
  48. if err != nil {
  49. return ""
  50. }
  51. return "- " + string(data)
  52. }
  53. func (v Vmess) ToSurge() string {
  54. // node2 = vmess, server, port, username=, ws=true, ws-path=, ws-headers=
  55. if v.Network == "ws" {
  56. wsHeasers := ""
  57. for k, v := range v.WSHeaders {
  58. if wsHeasers == "" {
  59. wsHeasers = k + ":" + v
  60. } else {
  61. wsHeasers += "|" + k + ":" + v
  62. }
  63. }
  64. text := fmt.Sprintf("%s = vmess, %s, %d, username=%s, ws=true, tls=%t, ws-path=%s",
  65. v.Name, v.Server, v.Port, v.UUID, v.TLS, v.WSPath)
  66. if wsHeasers != "" {
  67. text += ", ws-headers=" + wsHeasers
  68. }
  69. return text
  70. } else {
  71. return fmt.Sprintf("%s = vmess, %s, %d, username=%s, tls=%t",
  72. v.Name, v.Server, v.Port, v.UUID, v.TLS)
  73. }
  74. }
  75. func (v Vmess) Clone() Proxy {
  76. return &v
  77. }
  78. func (v Vmess) Link() (link string) {
  79. vjv, err := json.Marshal(v.toLinkJson())
  80. if err != nil {
  81. return
  82. }
  83. return fmt.Sprintf("vmess://%s", tool.Base64EncodeBytes(vjv))
  84. }
  85. type vmessLinkJson struct {
  86. Add string `json:"add"`
  87. V string `json:"v"`
  88. Ps string `json:"ps"`
  89. Port interface{} `json:"port"`
  90. Id string `json:"id"`
  91. Aid string `json:"aid"`
  92. Net string `json:"net"`
  93. Type string `json:"type"`
  94. Host string `json:"host"`
  95. Path string `json:"path"`
  96. Tls string `json:"tls"`
  97. }
  98. func (v Vmess) toLinkJson() vmessLinkJson {
  99. vj := vmessLinkJson{
  100. Add: v.Server,
  101. Ps: v.Name,
  102. Port: v.Port,
  103. Id: v.UUID,
  104. Aid: strconv.Itoa(v.AlterID),
  105. Net: v.Network,
  106. Path: v.WSPath,
  107. Host: v.ServerName,
  108. V: "2",
  109. }
  110. if v.TLS {
  111. vj.Tls = "tls"
  112. }
  113. if host, ok := v.WSHeaders["HOST"]; ok && host != "" {
  114. vj.Host = host
  115. }
  116. return vj
  117. }
  118. func ParseVmessLink(link string) (*Vmess, error) {
  119. if !strings.HasPrefix(link, "vmess") {
  120. return nil, ErrorNotVmessLink
  121. }
  122. vmessmix := strings.SplitN(link, "://", 2)
  123. if len(vmessmix) < 2 {
  124. return nil, ErrorNotVmessLink
  125. }
  126. linkPayload := vmessmix[1]
  127. if strings.Contains(linkPayload, "?") {
  128. // 使用第二种解析方法
  129. var infoPayloads []string
  130. if strings.Contains(linkPayload, "/?") {
  131. infoPayloads = strings.SplitN(linkPayload, "/?", 2)
  132. } else {
  133. infoPayloads = strings.SplitN(linkPayload, "?", 2)
  134. }
  135. if len(infoPayloads) < 2 {
  136. return nil, ErrorNotVmessLink
  137. }
  138. baseInfo, err := tool.Base64DecodeString(infoPayloads[0])
  139. if err != nil {
  140. return nil, ErrorVmessPayloadParseFail
  141. }
  142. baseInfoPath := strings.Split(baseInfo, ":")
  143. if len(baseInfoPath) < 3 {
  144. return nil, ErrorPathNotComplete
  145. }
  146. // base info
  147. cipher := baseInfoPath[0]
  148. mixInfo := strings.SplitN(baseInfoPath[1], "@", 2)
  149. if len(mixInfo) < 2 {
  150. return nil, ErrorVmessPayloadParseFail
  151. }
  152. uuid := mixInfo[0]
  153. server := mixInfo[1]
  154. portStr := baseInfoPath[2]
  155. port, err := strconv.Atoi(portStr)
  156. if err != nil {
  157. return nil, ErrorVmessPayloadParseFail
  158. }
  159. moreInfo, _ := url.ParseQuery(infoPayloads[1])
  160. remarks := moreInfo.Get("remarks")
  161. obfs := moreInfo.Get("obfs")
  162. network := "tcp"
  163. if obfs == "websocket" {
  164. network = "ws"
  165. }
  166. //obfsParam := moreInfo.Get("obfsParam")
  167. path := moreInfo.Get("path")
  168. if path == "" {
  169. path = "/"
  170. }
  171. tls := moreInfo.Get("tls") == "1"
  172. wsHeaders := make(map[string]string)
  173. return &Vmess{
  174. Base: Base{
  175. Name: remarks + "_" + strconv.Itoa(rand.Int()),
  176. Server: server,
  177. Port: port,
  178. Type: "vmess",
  179. UDP: false,
  180. },
  181. UUID: uuid,
  182. AlterID: 0,
  183. Cipher: cipher,
  184. TLS: tls,
  185. Network: network,
  186. HTTPOpts: HTTPOptions{},
  187. WSPath: path,
  188. WSHeaders: wsHeaders,
  189. SkipCertVerify: true,
  190. ServerName: server,
  191. }, nil
  192. } else {
  193. payload, err := tool.Base64DecodeString(linkPayload)
  194. if err != nil {
  195. return nil, ErrorVmessPayloadParseFail
  196. }
  197. vmessJson := vmessLinkJson{}
  198. err = json.Unmarshal([]byte(payload), &vmessJson)
  199. if err != nil {
  200. return nil, err
  201. }
  202. port := 443
  203. portInterface := vmessJson.Port
  204. switch portInterface.(type) {
  205. case int:
  206. port = portInterface.(int)
  207. case string:
  208. port, _ = strconv.Atoi(portInterface.(string))
  209. }
  210. alterId, err := strconv.Atoi(vmessJson.Aid)
  211. if err != nil {
  212. alterId = 0
  213. }
  214. tls := vmessJson.Tls == "tls"
  215. wsHeaders := make(map[string]string)
  216. if vmessJson.Host != "" {
  217. wsHeaders["HOST"] = vmessJson.Host
  218. }
  219. if vmessJson.Path == "" {
  220. vmessJson.Path = "/"
  221. }
  222. return &Vmess{
  223. Base: Base{
  224. Name: vmessJson.Ps + "_" + strconv.Itoa(rand.Int()),
  225. Server: vmessJson.Add,
  226. Port: port,
  227. Type: "vmess",
  228. UDP: false,
  229. },
  230. UUID: vmessJson.Id,
  231. AlterID: alterId,
  232. Cipher: "auto",
  233. TLS: tls,
  234. Network: vmessJson.Net,
  235. HTTPOpts: HTTPOptions{},
  236. WSPath: vmessJson.Path,
  237. WSHeaders: wsHeaders,
  238. SkipCertVerify: true,
  239. ServerName: vmessJson.Host,
  240. }, nil
  241. }
  242. }
  243. var (
  244. vmessPlainRe = regexp.MustCompile("vmess://([A-Za-z0-9+/_?&=-])+")
  245. )
  246. func GrepVmessLinkFromString(text string) []string {
  247. results := make([]string, 0)
  248. texts := strings.Split(text, "vmess://")
  249. for _, text := range texts {
  250. results = append(results, vmessPlainRe.FindAllString("vmess://"+text, -1)...)
  251. }
  252. return results
  253. }