github.com/yaling888/clash@v1.53.0/common/convert/converter.go (about) 1 package convert 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/base64" 7 "encoding/json" 8 "fmt" 9 "net" 10 "net/netip" 11 "net/url" 12 "strconv" 13 "strings" 14 ) 15 16 var enc = base64.StdEncoding 17 18 func DecodeBase64(buf []byte) ([]byte, error) { 19 buff := bytes.TrimSpace(buf) 20 dBuf := make([]byte, enc.DecodedLen(len(buff))) 21 n, err := enc.Decode(dBuf, buff) 22 if err != nil { 23 return nil, err 24 } 25 26 return dBuf[:n], nil 27 } 28 29 func DecodeRawBase64(buf []byte) ([]byte, error) { 30 buff := bytes.TrimSpace(buf) 31 dBuf := make([]byte, base64.RawStdEncoding.DecodedLen(len(buff))) 32 n, err := base64.RawStdEncoding.Decode(dBuf, buff) 33 if err != nil { 34 return nil, err 35 } 36 37 return dBuf[:n], nil 38 } 39 40 // ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config 41 func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { 42 data, err := DecodeBase64(buf) 43 if err != nil { 44 data, err = DecodeRawBase64(buf) 45 if err != nil { 46 data = buf 47 } 48 } 49 50 arr := strings.Split(string(data), "\n") 51 52 proxies := make([]map[string]any, 0, len(arr)) 53 names := make(map[string]int, 200) 54 55 for _, line := range arr { 56 line = strings.TrimRight(line, " \r") 57 if line == "" { 58 continue 59 } 60 61 scheme, body, found := strings.Cut(line, "://") 62 if !found { 63 continue 64 } 65 66 scheme = strings.ToLower(scheme) 67 switch scheme { 68 case "trojan": 69 urlTrojan, err := url.Parse(line) 70 if err != nil { 71 continue 72 } 73 74 query := urlTrojan.Query() 75 76 name := uniqueName(names, urlTrojan.Fragment) 77 trojan := make(map[string]any, 20) 78 79 trojan["name"] = name 80 trojan["type"] = scheme 81 trojan["server"] = urlTrojan.Hostname() 82 trojan["port"] = urlTrojan.Port() 83 trojan["password"] = urlTrojan.User.Username() 84 trojan["udp"] = true 85 trojan["skip-cert-verify"] = false 86 87 sni := query.Get("sni") 88 if sni != "" { 89 trojan["sni"] = sni 90 } 91 92 network := strings.ToLower(query.Get("type")) 93 if network != "" { 94 trojan["network"] = network 95 } 96 97 if network == "ws" { 98 headers := make(map[string]any) 99 wsOpts := make(map[string]any) 100 101 headers["User-Agent"] = RandUserAgent() 102 103 wsOpts["path"] = query.Get("path") 104 wsOpts["headers"] = headers 105 106 trojan["ws-opts"] = wsOpts 107 } 108 109 proxies = append(proxies, trojan) 110 case "vmess": 111 dcBuf, err := enc.DecodeString(body) 112 if err != nil { 113 continue 114 } 115 116 jsonDc := json.NewDecoder(bytes.NewReader(dcBuf)) 117 values := make(map[string]any, 20) 118 119 if jsonDc.Decode(&values) != nil { 120 continue 121 } 122 123 name, ok := values["ps"].(string) 124 if !ok { 125 continue 126 } 127 128 name = uniqueName(names, name) 129 vmess := make(map[string]any, 20) 130 131 vmess["name"] = name 132 vmess["type"] = scheme 133 vmess["server"] = values["add"] 134 vmess["port"] = values["port"] 135 vmess["uuid"] = values["id"] 136 vmess["alterId"] = values["aid"] 137 vmess["cipher"] = "auto" 138 vmess["udp"] = true 139 vmess["skip-cert-verify"] = false 140 141 var ( 142 sni = values["sni"] 143 host = values["host"] 144 network = "tcp" 145 ) 146 if n, ok := values["net"].(string); ok { 147 network = strings.ToLower(n) 148 } 149 vmess["network"] = network 150 151 var ( 152 tls = "" 153 isTls = false 154 ) 155 if t, ok := values["tls"].(string); ok { 156 tls = strings.ToLower(t) 157 } 158 if tls != "" && tls != "0" && tls != "null" { 159 if sni != nil { 160 vmess["servername"] = sni 161 } 162 vmess["tls"] = true 163 isTls = true 164 } 165 166 if network == "ws" { 167 headers := make(map[string]any) 168 wsOpts := make(map[string]any) 169 170 if !isTls { 171 if _, ok = host.(string); ok { 172 headers["Host"] = host 173 } else { 174 headers["Host"] = RandHost() 175 } 176 } 177 178 headers["User-Agent"] = RandUserAgent() 179 180 if values["path"] != nil { 181 wsOpts["path"] = values["path"] 182 } 183 wsOpts["headers"] = headers 184 185 vmess["ws-opts"] = wsOpts 186 } else if network == "http" { 187 headers := make(map[string][]string) 188 httpOpts := make(map[string]any) 189 190 if !isTls { 191 if h, ok := host.(string); ok { 192 headers["Host"] = []string{h} 193 } else { 194 headers["Host"] = []string{RandHost()} 195 } 196 } 197 198 headers["User-Agent"] = []string{RandUserAgent()} 199 200 if values["path"] != nil { 201 httpOpts["path"] = values["path"] 202 } 203 httpOpts["Host"] = values["add"] 204 httpOpts["headers"] = headers 205 206 vmess["http-opts"] = httpOpts 207 } 208 209 proxies = append(proxies, vmess) 210 case "ss": 211 urlSS, err := url.Parse(line) 212 if err != nil { 213 continue 214 } 215 216 name := uniqueName(names, urlSS.Fragment) 217 port := urlSS.Port() 218 219 if port == "" { 220 dcBuf, err := enc.DecodeString(urlSS.Host) 221 if err != nil { 222 continue 223 } 224 225 urlSS, err = url.Parse("ss://" + string(dcBuf)) 226 if err != nil { 227 continue 228 } 229 } 230 231 var ( 232 cipher = urlSS.User.Username() 233 password string 234 ) 235 236 if password, found = urlSS.User.Password(); !found { 237 dcBuf, err := enc.DecodeString(cipher) 238 if err != nil { 239 continue 240 } 241 242 cipher, password, found = strings.Cut(string(dcBuf), ":") 243 if !found { 244 continue 245 } 246 } 247 248 ss := make(map[string]any, 20) 249 250 ss["name"] = name 251 ss["type"] = scheme 252 ss["server"] = urlSS.Hostname() 253 ss["port"] = urlSS.Port() 254 ss["cipher"] = cipher 255 ss["password"] = password 256 ss["udp"] = true 257 258 proxies = append(proxies, ss) 259 case "ssr": 260 dcBuf, err := enc.DecodeString(body) 261 if err != nil { 262 continue 263 } 264 265 // ssr://host:port:protocol:method:obfs:urlsafebase64pass/?obfsparam=urlsafebase64&protoparam=&remarks=urlsafebase64&group=urlsafebase64&udpport=0&uot=1 266 267 before, after, ok := strings.Cut(string(dcBuf), "/?") 268 if !ok { 269 continue 270 } 271 272 beforeArr := strings.Split(before, ":") 273 274 if len(beforeArr) != 6 { 275 continue 276 } 277 278 host := beforeArr[0] 279 port := beforeArr[1] 280 protocol := beforeArr[2] 281 method := beforeArr[3] 282 obfs := beforeArr[4] 283 password := decodeUrlSafe(urlSafe(beforeArr[5])) 284 285 query, err := url.ParseQuery(urlSafe(after)) 286 if err != nil { 287 continue 288 } 289 290 remarks := decodeUrlSafe(query.Get("remarks")) 291 name := uniqueName(names, remarks) 292 293 obfsParam := decodeUrlSafe(query.Get("obfsparam")) 294 protocolParam := query.Get("protoparam") 295 296 ssr := make(map[string]any, 20) 297 298 ssr["name"] = name 299 ssr["type"] = scheme 300 ssr["server"] = host 301 ssr["port"] = port 302 ssr["cipher"] = method 303 ssr["password"] = password 304 ssr["obfs"] = obfs 305 ssr["protocol"] = protocol 306 ssr["udp"] = true 307 308 if obfsParam != "" { 309 ssr["obfs-param"] = obfsParam 310 } 311 312 if protocolParam != "" { 313 ssr["protocol-param"] = protocolParam 314 } 315 316 proxies = append(proxies, ssr) 317 case "vless": 318 urlVless, err := url.Parse(line) 319 if err != nil { 320 continue 321 } 322 323 query := urlVless.Query() 324 325 name := uniqueName(names, urlVless.Fragment) 326 vless := make(map[string]any, 20) 327 328 vless["name"] = name 329 vless["type"] = scheme 330 vless["server"] = urlVless.Hostname() 331 vless["port"] = urlVless.Port() 332 vless["uuid"] = urlVless.User.Username() 333 vless["udp"] = true 334 vless["tls"] = true 335 vless["skip-cert-verify"] = false 336 337 sni := query.Get("sni") 338 if sni != "" { 339 vless["servername"] = sni 340 } 341 342 network := strings.ToLower(query.Get("type")) 343 if network != "" { 344 vless["network"] = network 345 } 346 347 if network == "ws" { 348 security := strings.ToLower(query.Get("security")) 349 vless["tls"] = security == "tls" 350 351 headers := make(map[string]any) 352 wsOpts := make(map[string]any) 353 354 headers["User-Agent"] = RandUserAgent() 355 356 wsOpts["path"] = query.Get("path") 357 wsOpts["headers"] = headers 358 359 vless["ws-opts"] = wsOpts 360 } 361 362 proxies = append(proxies, vless) 363 } 364 } 365 366 if len(proxies) == 0 { 367 return nil, fmt.Errorf("convert v2ray subscribe error: format invalid") 368 } 369 370 return proxies, nil 371 } 372 373 func urlSafe(data string) string { 374 return strings.ReplaceAll(strings.ReplaceAll(data, "+", "-"), "/", "_") 375 } 376 377 func decodeUrlSafe(data string) string { 378 dcBuf, err := base64.URLEncoding.DecodeString(data) 379 if err != nil { 380 return "" 381 } 382 return string(dcBuf) 383 } 384 385 func uniqueName(names map[string]int, name string) string { 386 if index, ok := names[name]; ok { 387 index++ 388 names[name] = index 389 name = fmt.Sprintf("%s-%02d", name, index) 390 } else { 391 index = 0 392 names[name] = index 393 } 394 return name 395 } 396 397 func ConvertsWireGuard(buf []byte) ([]map[string]any, error) { 398 var ( 399 proxies = make([]map[string]any, 0, 50) 400 wgMap map[string]any 401 scanner = bufio.NewScanner(bytes.NewReader(buf)) 402 ) 403 for scanner.Scan() { 404 line := scanner.Text() 405 if line == "" { 406 continue 407 } 408 409 key, value, ok := strings.Cut(line, "=") 410 if !ok { 411 line = strings.ToLower(strings.TrimSpace(line)) 412 if line == "[interface]" { 413 if wgMap != nil && wgMap["name"] == nil { 414 if pk, ok := wgMap["public-key"].(string); ok && len(pk) >= 8 { 415 wgMap["name"] = fmt.Sprintf("wg-%s", pk[:8]) 416 } 417 } 418 wgMap = make(map[string]any, 12) 419 wgMap["type"] = "wireguard" 420 wgMap["dns"] = make([]string, 0, 5) 421 wgMap["udp"] = true 422 proxies = append(proxies, wgMap) 423 } 424 continue 425 } 426 427 key = strings.ToLower(strings.TrimSpace(key)) 428 value = strings.TrimSpace(value) 429 430 switch key { 431 case "name": 432 wgMap["name"] = value 433 case "endpoint": 434 host, port, err := net.SplitHostPort(value) 435 if err != nil { 436 return nil, err 437 } 438 p, err := strconv.Atoi(port) 439 if err != nil { 440 return nil, err 441 } 442 wgMap["server"] = host 443 wgMap["port"] = p 444 case "address": 445 ips := strings.Split(value, ",") 446 for _, v := range ips { 447 e, _, _ := strings.Cut(v, "/") 448 ip, err := netip.ParseAddr(strings.TrimSpace(e)) 449 if err != nil { 450 return nil, err 451 } 452 if ip.Is4() { 453 wgMap["ip"] = ip.String() 454 } else { 455 wgMap["ipv6"] = ip.String() 456 } 457 } 458 case "privatekey": 459 wgMap["private-key"] = value 460 case "publickey": 461 wgMap["public-key"] = value 462 case "presharedkey": 463 wgMap["preshared-key"] = value 464 case "dns": 465 ips := strings.Split(value, ",") 466 for _, v := range ips { 467 v = strings.TrimSpace(v) 468 ip, err := netip.ParseAddr(v) 469 if err != nil { 470 return nil, err 471 } 472 dnses := wgMap["dns"].([]string) 473 wgMap["dns"] = append(dnses, ip.String()) 474 } 475 case "mtu": 476 v, err := strconv.Atoi(value) 477 if err != nil { 478 return nil, err 479 } 480 wgMap["mtu"] = v 481 } 482 } 483 484 if len(proxies) == 0 { 485 return nil, fmt.Errorf("convert WireGuard error: format invalid") 486 } 487 488 if pk, ok := wgMap["public-key"].(string); ok && wgMap["name"] == nil && len(pk) >= 8 { 489 wgMap["name"] = fmt.Sprintf("wg-%s", pk[:8]) 490 } 491 492 if err := scanner.Err(); err != nil { 493 return nil, err 494 } 495 return proxies, nil 496 }