From 07b0a53a126fca61a9b42eacd8a7ee68fd0676d9 Mon Sep 17 00:00:00 2001 From: e1732a364fed <75717694+e1732a364fed@users.noreply.github.com> Date: Sat, 1 Jan 2000 00:00:00 +0000 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20-cvqxtvs=20=E5=92=8C=20-eq?= =?UTF-8?q?xrs=20=E5=91=BD=E4=BB=A4=EF=BC=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 分别意思是 convert QX to verysimple 和 extract QX remote servers 将圈叉的配置文件转换为verysimple格式,这样就可以更快地使用自己的vs客户端 --- cmd/verysimple/cmd.go | 20 +++++- cmd/verysimple/cmd_convert.go | 63 ++++++++++++++++++ configAdapter/thirdPartyApps.go | 112 +++++++++++++++++++++++++++++--- netLayer/interface_darwin.go | 2 +- utils/download.go | 8 +-- 5 files changed, 189 insertions(+), 16 deletions(-) create mode 100644 cmd/verysimple/cmd_convert.go diff --git a/cmd/verysimple/cmd.go b/cmd/verysimple/cmd.go index f1c7942..f32a908 100644 --- a/cmd/verysimple/cmd.go +++ b/cmd/verysimple/cmd.go @@ -26,6 +26,9 @@ var ( cmdPrintVer bool cmdGenerateUUid bool + cmdConvertQxToVs string + cmdExtractQX_remoteServer string + download bool defaultApiServerConf machine.ApiServerConf @@ -34,10 +37,13 @@ var ( ) func init() { + flag.BoolVar(&download, "d", false, " automatically download required mmdb file") + flag.BoolVar(&cmdPrintSupportedProtocols, "sp", false, "print supported protocols") flag.BoolVar(&cmdPrintVer, "v", false, "print the version string then exit") - flag.BoolVar(&download, "d", false, " automatically download required mmdb file") flag.BoolVar(&cmdGenerateUUid, "gu", false, " automatically generate a uuid for you") + flag.StringVar(&cmdConvertQxToVs, "cvqxtvs", "", "if given, convert qx server config string to vs toml config") + flag.StringVar(&cmdExtractQX_remoteServer, "eqxrs", "", "if given, automatically extract remote servers from quantumultX config for you") //apiServer stuff @@ -67,6 +73,18 @@ func runExitCommands() (atLeastOneCalled bool) { generateAndPrintUUID() } + + if cmdConvertQxToVs != "" { + atLeastOneCalled = true + + convertQxToVs() + } + + if cmdExtractQX_remoteServer != "" { + atLeastOneCalled = true + + extractQxRemoteServers() + } return } diff --git a/cmd/verysimple/cmd_convert.go b/cmd/verysimple/cmd_convert.go new file mode 100644 index 0000000..b2e1ebd --- /dev/null +++ b/cmd/verysimple/cmd_convert.go @@ -0,0 +1,63 @@ +package main + +import ( + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/e1732a364fed/v2ray_simple/configAdapter" + "github.com/e1732a364fed/v2ray_simple/proxy" + "github.com/e1732a364fed/v2ray_simple/utils" +) + +func convertQxToVs() { + dc := configAdapter.FromQX(cmdConvertQxToVs) + fmt.Println(utils.GetPurgedTomlStr(proxy.StandardConf{ + Dial: []*proxy.DialConf{&dc}, + })) + +} + +func extractQxRemoteServers() { + var bs []byte + var readE error + if strings.HasPrefix(cmdExtractQX_remoteServer, "http") { + + fmt.Printf("downloading %s\n", cmdExtractQX_remoteServer) + + resp, err := http.DefaultClient.Get(cmdExtractQX_remoteServer) + + if err != nil { + fmt.Printf("Download failed %s\n", err.Error()) + return + } + defer resp.Body.Close() + + counter := &utils.DownloadPrintCounter{} + + bs, readE = io.ReadAll(io.TeeReader(resp.Body, counter)) + fmt.Printf("\n") + } else { + if utils.FileExist(cmdExtractQX_remoteServer) { + path := utils.GetFilePath(cmdExtractQX_remoteServer) + f, e := os.Open(path) + if e != nil { + fmt.Printf("Download failed %s\n", e.Error()) + return + } + bs, readE = io.ReadAll(f) + } else { + fmt.Printf("file not exist %s\n", cmdExtractQX_remoteServer) + return + + } + } + + if readE != nil { + fmt.Printf("read failed %s\n", readE.Error()) + return + } + configAdapter.ExtractQxRemoteServers(string(bs)) +} diff --git a/configAdapter/thirdPartyApps.go b/configAdapter/thirdPartyApps.go index 13ba326..60ac9ca 100644 --- a/configAdapter/thirdPartyApps.go +++ b/configAdapter/thirdPartyApps.go @@ -1,8 +1,13 @@ package configAdapter import ( + "bytes" "encoding/base64" "encoding/json" + "fmt" + "io" + "net" + "net/http" "strconv" "strings" @@ -177,21 +182,64 @@ func ToQX(dc *proxy.DialConf) string { return sb.String() } -/* clash使用yaml作为配置格式。本函数中不导出整个yaml配置文件,而只导出 +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 { + 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 + } + } + } + + if dc.Protocol == "shadowsocks" { + if dc.Uuid != "" && dc.EncryptAlgo != "" { + dc.Uuid = "method:" + dc.EncryptAlgo + "\n" + "pass:" + dc.Uuid + } + } + return +} + +/* + clash使用yaml作为配置格式。本函数中不导出整个yaml配置文件,而只导出 + 对应的proxies项下的子项,比如 - - - name: "ss1" - type: ss - server: server - port: 443 - cipher: chacha20-ietf-poly1305 - password: "password" + - 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包,可以减少依赖. @@ -391,7 +439,7 @@ type V2rayNConfig struct { 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) +// 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 @@ -430,3 +478,47 @@ func ToV2rayN(dc *proxy.DialConf) string { 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 + } + } + +} diff --git a/netLayer/interface_darwin.go b/netLayer/interface_darwin.go index 45aea09..2d1bb88 100644 --- a/netLayer/interface_darwin.go +++ b/netLayer/interface_darwin.go @@ -127,7 +127,7 @@ func GetGatewayDeviceName() (string, error) { // helper func, call GetGatewayDeviceName and utils.GetDarwinNetAdapterNameByInterfaceName. // -// hardwarePort 和 interface的名称不同,hardwareport是 Wi-Fi, 而interface.Name 是 eth0 +// hardwarePort 和 interface的名称不同,hardwareport是 Wi-Fi, 而interface.Name 是 en0 func GetDefaultHardwarePort() (string, error) { n, e := GetGatewayDeviceName() if e != nil { diff --git a/utils/download.go b/utils/download.go index 1a7b0d6..9e39f49 100644 --- a/utils/download.go +++ b/utils/download.go @@ -86,18 +86,18 @@ func SimpleDownloadFile(fname, downloadLink string) (ok bool) { } // https://golangcode.com/download-a-file-with-progress/ -type downloadPrintCounter struct { +type DownloadPrintCounter struct { Total uint64 } -func (wc *downloadPrintCounter) Write(p []byte) (int, error) { +func (wc *DownloadPrintCounter) Write(p []byte) (int, error) { n := len(p) wc.Total += uint64(n) wc.PrintProgress() return n, nil } -func (wc downloadPrintCounter) PrintProgress() { +func (wc DownloadPrintCounter) PrintProgress() { fmt.Printf("\r%s", strings.Repeat(" ", 35)) fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total)) @@ -121,7 +121,7 @@ func DownloadAndUnzip(fname, downloadLink, dst string) (ok bool) { return } buf := new(bytes.Buffer) - counter := &downloadPrintCounter{} + counter := &DownloadPrintCounter{} io.Copy(buf, io.TeeReader(resp.Body, counter)) out := bytes.NewReader(buf.Bytes())