Files
v2ray_simple/configAdapter/thirdPartyApps.go
e1732a364fed 40082026e8 修订代码,完善gui配置代理的功能;url打印出path;其他:
修复quic关闭时闪退的bug;
url打印时若未配置network,去掉首部的加号

Uuid -> UUID
2022-12-28 21:01:20 +08:00

568 lines
12 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package configAdapter
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"strconv"
"strings"
"github.com/e1732a364fed/v2ray_simple/proxy"
"github.com/e1732a364fed/v2ray_simple/utils"
)
/*
To Quantumult X [server_local] string
quantumult X 只支持 vmess,trojan,shadowsocks,http 这四种协议.
See https://github.com/crossutility/Quantumult-X/blob/master/sample.conf
圈叉的配置,每一个协议的格式都略有不同,我们只能照着示例分情况处理。
同时,我们不支持里面的 fast-open 等选项。
*/
func ToQX(dc *proxy.DialConf) string {
var sb strings.Builder
sb.WriteString(dc.Protocol)
sb.WriteByte('=')
sb.WriteString(dc.GetAddrStrForListenOrDial())
var trojan_or_http_tlsFunc = func() {
if dc.TLS {
sb.WriteString(", over-tls=true")
if dc.Host != "" {
sb.WriteString(", tls-host=")
sb.WriteString(dc.Host)
}
sb.WriteString(", tls-verification=")
if dc.Insecure {
sb.WriteString("false")
} else {
sb.WriteString("true")
}
}
}
switch dc.Protocol {
case "shadowsocks":
//vs 中ss的 加密方法可以在两个地方指定一个是uuid中的method部分一个是 EncryptAlgo
//其中uuid的method部分是必须要给出的
ok, m, p := utils.CommonSplit(dc.UUID, "method", "pass")
if !ok {
return "parsing error when split uuid to get method and pass"
}
ea := m
if dc.EncryptAlgo != "" {
ea = dc.EncryptAlgo
}
sb.WriteString(", method=")
sb.WriteString(strings.ToLower(ea))
sb.WriteString(", password=")
sb.WriteString(p)
var hasObfs bool = true
var onlyTls bool
if dc.AdvancedLayer == "ws" {
if dc.TLS {
sb.WriteString(", obfs=wss")
} else {
sb.WriteString(", obfs=ws")
}
} else if dc.TLS {
sb.WriteString(", obfs=tls")
onlyTls = true
} else if dc.HttpHeader != nil {
sb.WriteString(", obfs=http")
} else {
hasObfs = false
}
if hasObfs {
if dc.Host != "" {
sb.WriteString(", obfs-host=")
sb.WriteString(dc.Host)
}
if !onlyTls {
if dc.Path != "" {
sb.WriteString(", obfs-uri=")
sb.WriteString(dc.Path)
} else if dc.HttpHeader != nil {
if len(dc.HttpHeader.Request.Path) > 0 {
sb.WriteString(", obfs-uri=")
sb.WriteString(dc.HttpHeader.Request.Path[0])
}
}
}
}
case "vmess":
sb.WriteString(", method=")
ea := dc.EncryptAlgo
if ea == "" {
ea = "none"
}
sb.WriteString(strings.ToLower(ea))
sb.WriteString(", password=")
sb.WriteString(dc.UUID)
var hasObfs bool
if dc.AdvancedLayer == "ws" {
if dc.TLS {
sb.WriteString(", obfs=wss")
} else {
sb.WriteString(", obfs=ws")
}
hasObfs = true
} else if dc.TLS {
sb.WriteString(", obfs=over-tls")
hasObfs = true
}
if hasObfs {
if dc.Host != "" {
sb.WriteString(", obfs-host=")
sb.WriteString(dc.Host)
}
if dc.AdvancedLayer == "ws" {
if dc.Path != "" {
sb.WriteString(", obfs-uri=")
sb.WriteString(dc.Path)
}
}
}
case "http":
if dc.UUID != "" {
ok, u, p := utils.CommonSplit(dc.UUID, "user", "pass")
if !ok {
return "parsing error when split uuid to get user and pass"
}
sb.WriteString(", username=")
sb.WriteString(u)
sb.WriteString(", password=")
sb.WriteString(p)
}
trojan_or_http_tlsFunc()
case "trojan":
sb.WriteString(", password=")
sb.WriteString(dc.UUID)
trojan_or_http_tlsFunc()
} //switch
if dc.Tag != "" {
sb.WriteString(", tag=")
sb.WriteString(dc.Tag)
}
return sb.String()
}
func FromQX(str string) (dc proxy.DialConf) {
//qx 的配置应该是基本的逗号分隔值形式
str = utils.StandardizeSpaces(str)
strs := strings.Split(str, ",")
for i, p := range strs {
ss := strings.Split(p, "=")
n := ss[0]
v := ss[1]
n = strings.TrimSpace(n)
v = strings.TrimSpace(v)
if i == 0 {
dc.Protocol = n
hostport := v
host, port, err := net.SplitHostPort(hostport)
if err != nil {
fmt.Printf("FromQX: net.SplitHostPort err, %s\n", err.Error())
} else {
ip := net.ParseIP(host)
if ip != nil {
dc.IP = host
} else {
dc.Host = host
}
np, _ := strconv.Atoi(port)
dc.Port = np
}
} else {
switch n {
case "method":
dc.EncryptAlgo = v
case "password":
dc.UUID = v
case "tag":
dc.Tag = v
case "obfs-uri":
dc.Path = v
case "tls-host":
fallthrough
case "obfs-host":
dc.Host = v
// case "udp-relay": //vs暂无对应配置
// if v == "false" {
// dc.Network = "tcp"
// }
case "obfs":
switch v {
case "ws":
dc.AdvancedLayer = "ws"
case "wss":
dc.AdvancedLayer = "ws"
dc.TLS = true
case "tls", "over-tls":
dc.TLS = true
}
case "over-tls":
dc.TLS = true
case "tls-verification":
if v == "false" {
dc.Insecure = true
}
case "tls13":
dc.Extra = map[string]any{
"tls_minVersion": "1.3",
}
}
}
}
if dc.Protocol == "shadowsocks" {
if dc.UUID != "" && dc.EncryptAlgo != "" {
dc.UUID = "method:" + dc.EncryptAlgo + "\n" + "pass:" + dc.UUID
}
}
if dc.Extra == nil {
dc.Extra = map[string]any{
"tls_minVersion": "1.2",
}
}
return
}
/*
clash使用yaml作为配置格式。本函数中不导出整个yaml配置文件而只导出
对应的proxies项下的子项比如
- name: "ss1"
type: ss
server: server
port: 443
cipher: chacha20-ietf-poly1305
password: "password"
See https://github.com/Dreamacro/clash/wiki/Configuration
clash的配置对于不同的协议来说格式也有不同clash基本上尊重了每一个协议的约定, 但也不完全一致
*/
func ToClash(dc *proxy.DialConf) string {
//这里我们不使用外部yaml包可以减少依赖.
// 开发提示: 边照着实际yaml边 编写; 随时运行 go test以查看效果
var sb strings.Builder
sb.WriteString(" - name: ")
if dc.Tag == "" {
sb.WriteString(dc.Protocol)
} else {
sb.WriteString(dc.Tag)
}
sb.WriteString("\n type: ")
if dc.Protocol == "shadowsocks" {
sb.WriteString("ss")
} else {
sb.WriteString(dc.Protocol)
}
sb.WriteString("\n server: ")
if dc.IP != "" {
sb.WriteString(dc.IP)
} else {
sb.WriteString(dc.Host)
}
sb.WriteString("\n port: ")
sb.WriteString(strconv.Itoa(dc.Port))
var writeHeaders = func() {
if dc.Host != "" {
sb.WriteString("\n host: ")
sb.WriteString(dc.Host)
}
if dc.Path != "" {
sb.WriteString("\n path: ")
sb.WriteString(dc.Path)
}
if dc.HttpHeader != nil {
if dc.HttpHeader.Request != nil {
if len(dc.HttpHeader.Request.Headers) > 0 {
sb.WriteString("\n headers: ")
for h, v := range dc.HttpHeader.Request.Headers {
if len(v) > 0 {
sb.WriteString("\n ")
sb.WriteString(h)
sb.WriteString(": ")
sb.WriteString(v[0])
}
}
}
}
}
}
var ss_http_or_tls = func(mode string) {
sb.WriteString("\n plugin: obfs")
sb.WriteString("\n plugin-opts:")
sb.WriteString("\n mode: ")
sb.WriteString(mode)
if dc.Host != "" {
sb.WriteString("\n host: ")
sb.WriteString(dc.Host)
}
}
switch dc.Protocol {
case "shadowsocks":
ok, m, p := utils.CommonSplit(dc.UUID, "method", "pass")
if !ok {
return "parsing error when split uuid to get method and pass"
}
sb.WriteString("\n cipher: ")
sb.WriteString(m)
sb.WriteString("\n password: ")
sb.WriteString(p)
if dc.AdvancedLayer == "ws" {
sb.WriteString("\n plugin: v2ray-plugin")
sb.WriteString("\n plugin-opts:")
sb.WriteString("\n mode: websocket")
if dc.TLS {
sb.WriteString("\n tls: true")
if dc.Insecure {
sb.WriteString("\n skip-cert-verify: true")
}
}
writeHeaders()
} else if dc.TLS {
ss_http_or_tls("tls")
} else if dc.HttpHeader != nil {
ss_http_or_tls("http")
}
case "trojan":
fallthrough
case "vmess":
if dc.Protocol == "vmess" {
sb.WriteString("\n uuid: ")
sb.WriteString(dc.UUID)
sb.WriteString("\n alterId: 0")
sb.WriteString("\n cipher: ")
if dc.EncryptAlgo != "" {
sb.WriteString(dc.EncryptAlgo)
} else {
sb.WriteString("auto")
}
} else {
sb.WriteString("\n password: ")
sb.WriteString(dc.UUID)
}
if dc.TLS {
if dc.Protocol == "vmess" {
sb.WriteString("\n tls: true")
}
if dc.Insecure {
sb.WriteString("\n skip-cert-verify: true")
}
}
if dc.Host != "" {
if dc.Protocol == "vmess" {
sb.WriteString("\n servername: ")
} else {
sb.WriteString("\n sni: ")
}
sb.WriteString(dc.Host)
}
if dc.HttpHeader != nil {
sb.WriteString("\n network: http")
if r := dc.HttpHeader.Request; r != nil {
sb.WriteString("\n http-opts:")
if r.Method != "" {
sb.WriteString("\n method: ")
sb.WriteString(r.Method)
}
writeHeaders()
}
} else if dc.AdvancedLayer != "" {
sb.WriteString("\n network: ")
sb.WriteString(dc.AdvancedLayer)
switch dc.AdvancedLayer {
case "ws":
sb.WriteString("\n ws-opts:")
writeHeaders()
if dc.IsEarly {
sb.WriteString("\n max-early-data: 2048")
sb.WriteString("\n early-data-header-name: Sec-WebSocket-Protocol")
}
case "grpc":
sb.WriteString("\n grpc-opts:")
sb.WriteString("\n grpc-service-name: ")
sb.WriteString(dc.Path)
}
}
case "http":
fallthrough
case "socks5":
ok, u, p := utils.CommonSplit(dc.UUID, "user", "pass")
if !ok {
return "parsing error when split uuid to get user and pass"
}
sb.WriteString("\n username: ")
sb.WriteString(u)
sb.WriteString("\n password: ")
sb.WriteString(p)
if dc.TLS {
sb.WriteString("\n tls: true")
if dc.Insecure {
sb.WriteString("\n skip-cert-verify: true")
}
}
} //switch
return sb.String()
}
type V2rayNConfig struct {
V string `json:"v"` //配置文件版本号,主要用来识别当前配置
PS string `json:"ps"` //备注或别名
Add string `json:"add"` //地址IP或域名
Port string `json:"port"`
ID string `json:"id"`
Security string `json:"scy"`
Net string `json:"net"` //(tcp\kcp\ws\h2\quic)
Type string `json:"type"` //(none\http\srtp\utp\wechat-video) *tcp or kcp or QUIC
Host string `json:"host"`
Path string `json:"path"`
Tls string `json:"tls"`
Sni string `json:"sni"`
}
// See https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
func ToV2rayN(dc *proxy.DialConf) string {
if dc.Protocol != "vmess" {
return "ToV2rayN doesn't support any protocol other than vmess, you give " + dc.Protocol
}
vc := V2rayNConfig{
V: "2",
PS: dc.Tag,
Add: dc.IP,
Port: strconv.Itoa(dc.Port),
ID: dc.UUID,
Security: dc.EncryptAlgo,
Host: dc.Host,
Sni: dc.Host,
Path: dc.Path,
}
if vc.Add == "" {
vc.Add = dc.Host
}
if dc.TLS {
vc.Tls = "tls"
}
if dc.AdvancedLayer != "" {
vc.Net = dc.AdvancedLayer
} else {
vc.Net = "tcp"
}
if dc.AdvancedLayer == "" && dc.HttpHeader != nil {
vc.Type = "http"
}
bs, err := json.Marshal(vc)
if err != nil {
return err.Error()
}
return "vmess://" + base64.URLEncoding.EncodeToString(bs)
}
func ExtractQxRemoteServers(configContentStr string) {
lines := strings.Split(configContentStr, "\n")
for i, l := range lines {
if strings.Contains(l, "[server_remote]") {
fmt.Printf("got [server_remote] field\n")
l = lines[i+1]
strs := strings.SplitN(l, ",", 2)
l = strs[0]
fmt.Printf("downloading %s\n", l)
resp, err := http.DefaultClient.Get(l)
if err != nil {
fmt.Printf("Download failed %s\n", err.Error())
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Printf("Download got bad status: %s\n", resp.Status)
return
}
fmt.Printf("download ok, reading...\n")
buf := utils.GetBuf()
counter := &utils.DownloadPrintCounter{}
fmt.Printf("\nread ok\n")
io.Copy(buf, io.TeeReader(resp.Body, counter))
ls := bytes.Split(buf.Bytes(), []byte("\n"))
for _, v := range ls {
fmt.Println(string(v))
}
return
}
}
}