mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-12-24 13:27:56 +08:00
feature:交互模式添加 热加载url配置功能;修订代码,文档;
This commit is contained in:
13
README.md
13
README.md
@@ -206,6 +206,8 @@ verysimple -c server.toml
|
||||
|
||||
## 关于证书
|
||||
|
||||
<details>
|
||||
|
||||
自己生成证书!而且最好是用 自己真实拥有的域名,使用acme.sh等脚本申请免费证书,特别是建站等情况。
|
||||
|
||||
而且用了真证书后,别忘了把配置文件中的 `insecure=true` 给删掉.
|
||||
@@ -249,6 +251,14 @@ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.crt -CAkey ca.key -se
|
||||
|
||||
在你的服务端下载好程序后,运行 `verysimple -i` 开启交互模式,然后按向下箭头 找到对应选项,按回车 来自动生成tls证书。
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
# 技术相关
|
||||
|
||||
<details>
|
||||
|
||||
<summary>技术相关</summary>
|
||||
|
||||
## 创新点
|
||||
|
||||
@@ -531,6 +541,9 @@ https://github.com/e1732a364fed/v2ray_simple/discussions/3
|
||||
./verysimple -c ../../examples/quic.server.toml -ll 0
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## 测速
|
||||
|
||||
测试环境:ubuntu虚拟机, 使用开源测试工具
|
||||
|
||||
@@ -10,110 +10,43 @@ import (
|
||||
vs "github.com/e1732a364fed/v2ray_simple"
|
||||
"github.com/e1732a364fed/v2ray_simple/netLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/proxy"
|
||||
"github.com/e1732a364fed/v2ray_simple/tlsLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||
"github.com/manifoldco/promptui"
|
||||
)
|
||||
|
||||
type CliCmd struct {
|
||||
Name string
|
||||
f func()
|
||||
}
|
||||
|
||||
func (cc CliCmd) String() string {
|
||||
return cc.Name
|
||||
}
|
||||
|
||||
func nlist(list []CliCmd) (result []string) {
|
||||
for _, v := range list {
|
||||
result = append(result, v.Name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func flist(list []CliCmd) (result []func()) {
|
||||
for _, v := range list {
|
||||
result = append(result, v.f)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var cliCmdList = []CliCmd{
|
||||
{
|
||||
"生成随机ssl证书", func() {
|
||||
const certFn = "cert.pem"
|
||||
const keyFn = "cert.key"
|
||||
if utils.FileExist(certFn) {
|
||||
utils.PrintStr(certFn)
|
||||
utils.PrintStr(" 已存在!\n")
|
||||
return
|
||||
}
|
||||
|
||||
if utils.FileExist(keyFn) {
|
||||
utils.PrintStr(keyFn)
|
||||
utils.PrintStr(" 已存在!\n")
|
||||
return
|
||||
}
|
||||
|
||||
err := tlsLayer.GenerateRandomCertKeyFiles(certFn, keyFn)
|
||||
if err == nil {
|
||||
utils.PrintStr("生成成功!请查看目录中的 ")
|
||||
utils.PrintStr(certFn)
|
||||
utils.PrintStr(" 和 ")
|
||||
utils.PrintStr(keyFn)
|
||||
utils.PrintStr("\n")
|
||||
|
||||
} else {
|
||||
|
||||
utils.PrintStr("生成失败,")
|
||||
utils.PrintStr(err.Error())
|
||||
utils.PrintStr("\n")
|
||||
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getStandardConfFromCurrentState() (sc proxy.StandardConf) {
|
||||
for _, c := range allClients {
|
||||
dc := c.GetBase().DialConf
|
||||
sc.Dial = append(sc.Dial, dc)
|
||||
|
||||
}
|
||||
for _, s := range allServers {
|
||||
lc := s.GetBase().ListenConf
|
||||
sc.Listen = append(sc.Listen, lc)
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
//cli.go 中添加的 CliCmd都是需进一步交互的命令
|
||||
|
||||
var getStandardConfFromCurrentState = func() (sc proxy.StandardConf) {
|
||||
for _, c := range allClients {
|
||||
dc := c.GetBase().DialConf
|
||||
sc.Dial = append(sc.Dial, dc)
|
||||
|
||||
}
|
||||
for _, s := range allServers {
|
||||
lc := s.GetBase().ListenConf
|
||||
sc.Listen = append(sc.Listen, lc)
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//cli.go 中定义的 CliCmd都是需进一步交互的命令
|
||||
cliCmdList = append(cliCmdList, CliCmd{
|
||||
"对当前引用的配置文件生成分享链接", func() {
|
||||
|
||||
"【生成分享链接】<-当前的配置", func() {
|
||||
sc := getStandardConfFromCurrentState()
|
||||
interactively_generate_share(&sc)
|
||||
},
|
||||
}, CliCmd{
|
||||
"交互生成配置,超级强大", func() {
|
||||
generateConfigFileInteractively()
|
||||
},
|
||||
"【交互生成配置】,超级强大", generateConfigFileInteractively,
|
||||
}, CliCmd{
|
||||
"热删除配置", func() {
|
||||
interactively_hotRemoveServerOrClient()
|
||||
},
|
||||
"热删除配置", interactively_hotRemoveServerOrClient,
|
||||
}, CliCmd{
|
||||
"热加载新配置文件", func() {
|
||||
interactively_hotLoadConfigFile()
|
||||
},
|
||||
"【热加载】新配置文件", interactively_hotLoadConfigFile,
|
||||
}, CliCmd{
|
||||
"调节日志等级", func() {
|
||||
interactively_adjust_loglevel()
|
||||
},
|
||||
"【热加载】新配置url", interactively_hotLoadUrlConfig,
|
||||
}, CliCmd{
|
||||
"调节日志等级", interactively_adjust_loglevel,
|
||||
})
|
||||
|
||||
}
|
||||
@@ -446,6 +379,100 @@ func interactively_hotRemoveServerOrClient() {
|
||||
printAllState(os.Stdout, true)
|
||||
}
|
||||
|
||||
func interactively_hotLoadUrlConfig() {
|
||||
utils.PrintStr("即将开始热添加url配置\n")
|
||||
Select := promptui.Select{
|
||||
Label: "请选择你的url的格式类型",
|
||||
Items: []string{
|
||||
"协议官方url格式(视代理协议不同而不同)",
|
||||
"vs标准url格式",
|
||||
},
|
||||
}
|
||||
i, result, err := Select.Run()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Prompt failed %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("你选择了 %s\n", result)
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
fmt.Printf("目前暂不支持")
|
||||
return
|
||||
|
||||
case 1:
|
||||
Select := promptui.Select{
|
||||
Label: "请选择该url是用于服务端还是客户端",
|
||||
Items: []string{
|
||||
"客户端",
|
||||
"服务端",
|
||||
},
|
||||
}
|
||||
i, result, err := Select.Run()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Prompt failed %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("你选择了 %s\n", result)
|
||||
|
||||
fmt.Printf("请输入你的配置url\n")
|
||||
|
||||
var theUrlStr string
|
||||
|
||||
fmt.Scanln(&theUrlStr)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Prompt failed %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
u, sn, _, okTls, err := proxy.GetRealProtocolFromClientUrl(theUrlStr)
|
||||
if err != nil {
|
||||
fmt.Printf("parse url failed %v\n", err)
|
||||
return
|
||||
}
|
||||
dc := &proxy.DialConf{}
|
||||
dc.Protocol = sn
|
||||
|
||||
dc.TLS = okTls
|
||||
err = proxy.URLToDialConf(u, dc)
|
||||
if err != nil {
|
||||
fmt.Printf("parse url failed %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
hotLoadDialConfForRuntime("", []*proxy.DialConf{dc})
|
||||
|
||||
} else {
|
||||
|
||||
u, sn, _, okTls, err := proxy.GetRealProtocolFromServerUrl(theUrlStr)
|
||||
if err != nil {
|
||||
fmt.Printf("parse url failed %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
lc := &proxy.ListenConf{}
|
||||
lc.Protocol = sn
|
||||
|
||||
lc.TLS = okTls
|
||||
|
||||
err = proxy.URLToListenConf(u, lc)
|
||||
if err != nil {
|
||||
fmt.Printf("parse url failed %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
hotLoadListenConfForRuntime([]*proxy.ListenConf{lc})
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//热添加配置文件
|
||||
func interactively_hotLoadConfigFile() {
|
||||
utils.PrintStr("即将开始热添加配置文件\n")
|
||||
|
||||
@@ -34,6 +34,15 @@ func interactively_generate_share(conf *proxy.StandardConf) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "vs标准toml",
|
||||
f: func() {
|
||||
fmt.Println("#vs_auto_generated:")
|
||||
|
||||
fmt.Println(utils.GetPurgedTomlStr(conf))
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "xray分享链接标准提案 (#716)",
|
||||
f: func() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
|
||||
httpProxy "github.com/e1732a364fed/v2ray_simple/proxy/http"
|
||||
"github.com/e1732a364fed/v2ray_simple/tlsLayer"
|
||||
|
||||
vs "github.com/e1732a364fed/v2ray_simple"
|
||||
"go.uber.org/zap"
|
||||
@@ -29,36 +30,55 @@ var (
|
||||
download bool
|
||||
)
|
||||
|
||||
type CliCmd struct {
|
||||
Name string
|
||||
f func()
|
||||
}
|
||||
|
||||
func (cc CliCmd) String() string {
|
||||
return cc.Name
|
||||
}
|
||||
|
||||
// func nlist(list []CliCmd) (result []string) {
|
||||
// for _, v := range list {
|
||||
// result = append(result, v.Name)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func flist(list []CliCmd) (result []func()) {
|
||||
for _, v := range list {
|
||||
result = append(result, v.f)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// cliCmdList 包含所有交互模式中可执行的命令;
|
||||
//本文件 中添加的 CliCmd都是直接返回运行结果的、无需进一步交互的命令
|
||||
var cliCmdList = []CliCmd{
|
||||
{
|
||||
"查询当前状态", func() {
|
||||
printAllState(os.Stdout, false)
|
||||
},
|
||||
}, {
|
||||
"打印当前版本所支持的所有协议", printSupportedProtocols,
|
||||
}, {
|
||||
"生成随机ssl证书", generateRandomSSlCert,
|
||||
}, {
|
||||
"生成一个随机的uuid供你参考", generateAndPrintUUID,
|
||||
}, {
|
||||
"下载geosite文件夹", tryDownloadGeositeSource,
|
||||
}, {
|
||||
"下载geoip文件(GeoLite2-Country.mmdb)", tryDownloadMMDB,
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&cmdPrintSupportedProtocols, "sp", false, "print supported protocols")
|
||||
flag.BoolVar(&cmdPrintVer, "v", false, "print the version string then exit")
|
||||
flag.BoolVar(&interactive_mode, "i", false, "enable interactive commandline mode")
|
||||
flag.BoolVar(&download, "d", false, " automatically download required mmdb file")
|
||||
|
||||
//本文件 中定义的 CliCmd都是直接返回运行结果的、无需进一步交互的命令
|
||||
|
||||
cliCmdList = append(cliCmdList, CliCmd{
|
||||
"生成一个随机的uuid供你参考", func() {
|
||||
generateAndPrintUUID()
|
||||
},
|
||||
}, CliCmd{
|
||||
"下载geosite文件夹", func() {
|
||||
tryDownloadGeositeSource()
|
||||
},
|
||||
}, CliCmd{
|
||||
"下载geoip文件(GeoLite2-Country.mmdb)", func() {
|
||||
tryDownloadMMDB()
|
||||
},
|
||||
}, CliCmd{
|
||||
"打印当前版本所支持的所有协议", func() {
|
||||
printSupportedProtocols()
|
||||
},
|
||||
}, CliCmd{
|
||||
"查询当前状态", func() {
|
||||
printAllState(os.Stdout, false)
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
//运行一些 执行后立即退出程序的 命令
|
||||
@@ -90,6 +110,38 @@ func generateAndPrintUUID() {
|
||||
fmt.Printf("New random uuid : %s\n", utils.GenerateUUIDStr())
|
||||
}
|
||||
|
||||
func generateRandomSSlCert() {
|
||||
const certFn = "cert.pem"
|
||||
const keyFn = "cert.key"
|
||||
if utils.FileExist(certFn) {
|
||||
utils.PrintStr(certFn)
|
||||
utils.PrintStr(" 已存在!\n")
|
||||
return
|
||||
}
|
||||
|
||||
if utils.FileExist(keyFn) {
|
||||
utils.PrintStr(keyFn)
|
||||
utils.PrintStr(" 已存在!\n")
|
||||
return
|
||||
}
|
||||
|
||||
err := tlsLayer.GenerateRandomCertKeyFiles(certFn, keyFn)
|
||||
if err == nil {
|
||||
utils.PrintStr("生成成功!请查看目录中的 ")
|
||||
utils.PrintStr(certFn)
|
||||
utils.PrintStr(" 和 ")
|
||||
utils.PrintStr(keyFn)
|
||||
utils.PrintStr("\n")
|
||||
|
||||
} else {
|
||||
|
||||
utils.PrintStr("生成失败,")
|
||||
utils.PrintStr(err.Error())
|
||||
utils.PrintStr("\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func printSupportedProtocols() {
|
||||
utils.PrintStr("Support tcp/udp/tproxy/unix domain socket/tls/uTls by default.\n")
|
||||
proxy.PrintAllServerNames()
|
||||
@@ -259,6 +311,10 @@ func hotLoadDialConfForRuntime(Default_uuid string, conf []*proxy.DialConf) {
|
||||
}
|
||||
func hotLoadListenConfForRuntime(conf []*proxy.ListenConf) {
|
||||
|
||||
if defaultOutClient == nil {
|
||||
defaultOutClient = vs.DirectClient
|
||||
}
|
||||
|
||||
for i, l := range conf {
|
||||
inServer, err := proxy.NewServer(l)
|
||||
if err != nil {
|
||||
|
||||
@@ -14,11 +14,12 @@ package configAdapter
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/e1732a364fed/v2ray_simple/proxy"
|
||||
)
|
||||
|
||||
//convert proxy.DialConf to verysimple's official URL format.
|
||||
//convert proxy.DialConf to verysimple Official URL format.
|
||||
// See https://github.com/e1732a364fed/v2ray_simple/discussions/163
|
||||
func ToVS(cc *proxy.CommonConf, dc *proxy.DialConf) string {
|
||||
var u url.URL
|
||||
@@ -71,15 +72,9 @@ func ToVS(cc *proxy.CommonConf, dc *proxy.DialConf) string {
|
||||
q.Add("http.version", r.Version)
|
||||
}
|
||||
|
||||
for k, v := range r.Headers {
|
||||
vstr := ""
|
||||
for i, v2 := range v {
|
||||
vstr += v2
|
||||
if i != len(v)-1 {
|
||||
vstr += ", "
|
||||
}
|
||||
}
|
||||
q.Add("header."+k, vstr)
|
||||
for k, headers := range r.Headers {
|
||||
|
||||
q.Add("header."+k, strings.Join(headers, ", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,12 @@ import (
|
||||
)
|
||||
|
||||
/*
|
||||
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 等选项。
|
||||
*/
|
||||
@@ -175,7 +177,7 @@ func ToQX(dc *proxy.DialConf) string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
/* clash使用yaml作为配置格式。我们该函数中不导出整个yaml配置文件,而只导出
|
||||
/* clash使用yaml作为配置格式。本函数中不导出整个yaml配置文件,而只导出
|
||||
对应的proxies项下的子项,比如
|
||||
|
||||
|
||||
@@ -374,7 +376,6 @@ func ToClash(dc *proxy.DialConf) string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
//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)
|
||||
type V2rayNConfig struct {
|
||||
V string `json:"v"` //配置文件版本号,主要用来识别当前配置
|
||||
PS string `json:"ps"` //备注或别名
|
||||
@@ -390,6 +391,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)
|
||||
func ToV2rayN(dc *proxy.DialConf) string {
|
||||
if dc.Protocol != "vmess" {
|
||||
return "ToV2rayN doesn't support any protocol other than vmess, you give " + dc.Protocol
|
||||
|
||||
268
proxy/config_url.go
Normal file
268
proxy/config_url.go
Normal file
@@ -0,0 +1,268 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/e1732a364fed/v2ray_simple/httpLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
UrlNativeFormat = iota //proxy对应的标准文档所定义的url模式,一般散布于对应github的文档上
|
||||
UrlStandardFormat //VS定义的 供 所有proxy 使用的 标准 url模式
|
||||
|
||||
)
|
||||
|
||||
var (
|
||||
// Url格式 设置以何种方式解析 命令行模式/极简模式 中出现的url配置
|
||||
//
|
||||
//关于url格式的详细, 见 https://github.com/e1732a364fed/v2ray_simple/discussions/163
|
||||
UrlFormat = UrlStandardFormat
|
||||
)
|
||||
|
||||
//try find from map, trim tail s if necessary
|
||||
func GetRealProtocolFromClientUrl(s string) (u *url.URL, schemeName string, creator ClientCreator, okTls bool, err error) {
|
||||
u, err = url.Parse(s)
|
||||
if err != nil {
|
||||
|
||||
err = utils.ErrInErr{ErrDesc: "Can't parse client url", ErrDetail: err, Data: s}
|
||||
return
|
||||
}
|
||||
|
||||
schemeName = strings.ToLower(u.Scheme)
|
||||
var ok bool
|
||||
creator, ok = clientCreatorMap[schemeName]
|
||||
|
||||
if !ok {
|
||||
schemeName = strings.TrimSuffix(schemeName, "s")
|
||||
creator, okTls = clientCreatorMap[schemeName]
|
||||
if okTls {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
err = utils.ErrInErr{ErrDesc: "Unknown client protocol ", Data: u.Scheme}
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
//try find from map, trim tail s if necessary
|
||||
func GetRealProtocolFromServerUrl(s string) (u *url.URL, schemeName string, creator ServerCreator, okTls bool, err error) {
|
||||
u, err = url.Parse(s)
|
||||
if err != nil {
|
||||
|
||||
err = utils.ErrInErr{ErrDesc: "Can't parse server url", ErrDetail: err, Data: s}
|
||||
return
|
||||
}
|
||||
|
||||
schemeName = strings.ToLower(u.Scheme)
|
||||
var ok bool
|
||||
creator, ok = serverCreatorMap[schemeName]
|
||||
|
||||
if !ok {
|
||||
schemeName = strings.TrimSuffix(schemeName, "s")
|
||||
creator, okTls = serverCreatorMap[schemeName]
|
||||
if okTls {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
err = utils.ErrInErr{ErrDesc: "Unknown server protocol ", Data: u.Scheme}
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// ClientFromURL calls the registered creator to create client. The returned bool is true if has err.
|
||||
func ClientFromURL(s string) (Client, error) {
|
||||
u, sn, creator, okTls, err := GetRealProtocolFromClientUrl(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dc *DialConf
|
||||
|
||||
if UrlFormat == UrlStandardFormat {
|
||||
dc = &DialConf{}
|
||||
dc.TLS = okTls
|
||||
dc.Protocol = sn
|
||||
e := URLToDialConf(u, dc)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
}
|
||||
var e error
|
||||
dc, e = creator.URLToDialConf(u, dc, UrlFormat)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
c, e := newClient(creator, dc, false)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// ServerFromURL calls the registered creator to create proxy servers.
|
||||
func ServerFromURL(s string) (Server, error) {
|
||||
u, sn, creator, okTls, err := GetRealProtocolFromServerUrl(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var lc *ListenConf
|
||||
|
||||
if UrlFormat == UrlStandardFormat {
|
||||
lc = &ListenConf{}
|
||||
lc.TLS = okTls
|
||||
lc.Protocol = sn
|
||||
|
||||
e := URLToListenConf(u, lc)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
}
|
||||
|
||||
lc, err = creator.URLToListenConf(u, lc, UrlFormat)
|
||||
if err != nil {
|
||||
return nil, utils.ErrInErr{
|
||||
ErrDesc: "URLToListenConf err ",
|
||||
ErrDetail: err,
|
||||
Data: s,
|
||||
}
|
||||
}
|
||||
|
||||
return newServer(creator, lc, false)
|
||||
|
||||
}
|
||||
|
||||
//setup conf with vs standard url format
|
||||
func URLToCommonConf(u *url.URL, conf *CommonConf) error {
|
||||
|
||||
if u.Scheme != DirectName {
|
||||
|
||||
hn := u.Hostname()
|
||||
|
||||
ip := net.ParseIP(hn)
|
||||
if ip != nil {
|
||||
conf.IP = hn
|
||||
} else {
|
||||
conf.Host = hn
|
||||
}
|
||||
|
||||
if hn != u.Host { //给出了port
|
||||
colon := strings.LastIndexByte(u.Host, ':')
|
||||
p, err := strconv.Atoi(u.Host[colon+1:])
|
||||
if err != nil {
|
||||
return err
|
||||
} else if p < 0 || p > 65535 {
|
||||
return utils.ErrInvalidData
|
||||
}
|
||||
conf.Port = p
|
||||
|
||||
}
|
||||
}
|
||||
q := u.Query()
|
||||
|
||||
conf.Network = q.Get("network")
|
||||
|
||||
conf.Fullcone = utils.QueryPositive(q, "fullcone")
|
||||
conf.Tag = u.Fragment
|
||||
conf.Path = u.Path
|
||||
conf.AdvancedLayer = q.Get("adv")
|
||||
conf.EncryptAlgo = q.Get("security")
|
||||
|
||||
if q.Get("v") != "" {
|
||||
v, e := strconv.Atoi(q.Get("v"))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
conf.Version = v
|
||||
}
|
||||
|
||||
if utils.QueryPositive(q, "http") {
|
||||
conf.HttpHeader = &httpLayer.HeaderPreset{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setHeaders(rawq, headers map[string][]string) {
|
||||
for k, list := range rawq {
|
||||
if strings.HasPrefix(k, "header.") && len(list) > 0 {
|
||||
k = strings.TrimPrefix(k, "header.")
|
||||
headers[k] = list
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//setup conf with vs standard URL format
|
||||
func URLToDialConf(u *url.URL, conf *DialConf) error {
|
||||
e := URLToCommonConf(u, &conf.CommonConf)
|
||||
|
||||
q := u.Query()
|
||||
|
||||
if conf.HttpHeader != nil {
|
||||
rh := &httpLayer.RequestHeader{
|
||||
Method: q.Get("http.method"),
|
||||
Version: q.Get("http.version"),
|
||||
Path: []string{conf.Path},
|
||||
Headers: map[string][]string{},
|
||||
}
|
||||
setHeaders(q, rh.Headers)
|
||||
conf.HttpHeader.Request = rh
|
||||
|
||||
}
|
||||
|
||||
if conf.TLS {
|
||||
conf.Insecure = utils.QueryPositive(q, "insecure")
|
||||
conf.Utls = utils.QueryPositive(q, "utls")
|
||||
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
//setup conf with vs standard URL format
|
||||
func URLToListenConf(u *url.URL, conf *ListenConf) error {
|
||||
e := URLToCommonConf(u, &conf.CommonConf)
|
||||
|
||||
q := u.Query()
|
||||
|
||||
conf.NoRoute = utils.QueryPositive(q, "noroute")
|
||||
|
||||
conf.Fallback = q.Get("fallback")
|
||||
|
||||
if conf.HttpHeader != nil {
|
||||
rh := &httpLayer.ResponseHeader{
|
||||
StatusCode: q.Get("http.status_code"),
|
||||
Version: q.Get("http.version"),
|
||||
Reason: q.Get("http.reason"),
|
||||
Headers: map[string][]string{},
|
||||
}
|
||||
setHeaders(q, rh.Headers)
|
||||
conf.HttpHeader.Response = rh
|
||||
|
||||
}
|
||||
|
||||
if conf.TLS {
|
||||
conf.Insecure = utils.QueryPositive(q, "insecure")
|
||||
|
||||
certFile := q.Get("cert")
|
||||
keyFile := q.Get("key")
|
||||
|
||||
conf.TLSCert = certFile
|
||||
conf.TLSKey = keyFile
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
@@ -40,6 +40,7 @@ type ClientCreator interface {
|
||||
//大部分通用内容都会被proxy包解析,方法只需要处理proxy包未知的内容
|
||||
NewClient(*DialConf) (Client, error) //标准配置
|
||||
|
||||
//URLToDialConf 执行proxy自定义的非标准代码;
|
||||
//iv: initial value, can be nil.
|
||||
URLToDialConf(url *url.URL, iv *DialConf, format int) (*DialConf, error)
|
||||
//DialConfToURL(url *DialConf, format int) (*url.URL, error)
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
UrlNativeFormat = iota //proxy对应的标准文档所定义的url模式,一般散布于对应github的文档上
|
||||
UrlStandardFormat //VS定义的 供 所有proxy 使用的 标准 url模式
|
||||
|
||||
)
|
||||
|
||||
var (
|
||||
// Url格式 设置以何种方式解析 命令行模式/极简模式 中出现的url配置
|
||||
//
|
||||
//关于url格式的详细, 见 https://github.com/e1732a364fed/v2ray_simple/discussions/163
|
||||
UrlFormat = UrlStandardFormat
|
||||
)
|
||||
|
||||
// ClientFromURL calls the registered creator to create client. The returned bool is true if has err.
|
||||
func ClientFromURL(s string) (Client, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
|
||||
return nil, utils.ErrInErr{ErrDesc: "Can't parse client url", ErrDetail: err, Data: s}
|
||||
}
|
||||
|
||||
schemeName := strings.ToLower(u.Scheme)
|
||||
|
||||
creator, ok := clientCreatorMap[schemeName]
|
||||
|
||||
var okTls bool
|
||||
|
||||
if !ok {
|
||||
realScheme := strings.TrimSuffix(schemeName, "s")
|
||||
creator, okTls = clientCreatorMap[realScheme]
|
||||
}
|
||||
//尝试判断是否套tls, 比如vlesss实际上是vless+tls,https实际上是http+tls
|
||||
|
||||
if okTls {
|
||||
ok = true
|
||||
}
|
||||
|
||||
if ok {
|
||||
var dc *DialConf
|
||||
|
||||
if UrlFormat == UrlStandardFormat {
|
||||
dc = &DialConf{}
|
||||
setConfByStandardURL(&dc.CommonConf, u)
|
||||
|
||||
if okTls {
|
||||
dc.TLS = true
|
||||
setTLS_forConf_withStandardURL(u, &dc.CommonConf, nil, dc)
|
||||
|
||||
}
|
||||
}
|
||||
var e error
|
||||
dc, e = creator.URLToDialConf(u, dc, UrlFormat)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
c, e := newClient(creator, dc, false)
|
||||
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
return nil, utils.ErrInErr{ErrDesc: "Unknown client protocol ", Data: u.Scheme}
|
||||
}
|
||||
|
||||
// ServerFromURL calls the registered creator to create proxy servers.
|
||||
func ServerFromURL(s string) (Server, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, utils.ErrInErr{
|
||||
ErrDesc: "Can't parse server url ",
|
||||
ErrDetail: err,
|
||||
Data: s,
|
||||
}
|
||||
}
|
||||
|
||||
schemeName := strings.ToLower(u.Scheme)
|
||||
creator, ok := serverCreatorMap[schemeName]
|
||||
|
||||
var okTls bool
|
||||
|
||||
if !ok {
|
||||
realScheme := strings.TrimSuffix(schemeName, "s")
|
||||
creator, okTls = serverCreatorMap[realScheme]
|
||||
|
||||
}
|
||||
|
||||
if okTls {
|
||||
ok = true
|
||||
}
|
||||
|
||||
if ok {
|
||||
|
||||
var sConf *ListenConf
|
||||
|
||||
if UrlFormat == UrlStandardFormat {
|
||||
sConf = &ListenConf{}
|
||||
|
||||
confQueryForServer(sConf, u)
|
||||
|
||||
if okTls {
|
||||
sConf.TLS = true
|
||||
|
||||
setTLS_forConf_withStandardURL(u, &sConf.CommonConf, sConf, nil)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sConf, err := creator.URLToListenConf(u, sConf, UrlFormat)
|
||||
if err != nil {
|
||||
return nil, utils.ErrInErr{
|
||||
ErrDesc: "URLToListenConf err ",
|
||||
ErrDetail: err,
|
||||
Data: s,
|
||||
}
|
||||
}
|
||||
|
||||
return newServer(creator, sConf, false)
|
||||
|
||||
}
|
||||
|
||||
return nil, utils.ErrInErr{ErrDesc: "Unknown server protocol ", Data: u.Scheme}
|
||||
}
|
||||
|
||||
func getFullconeFromUrl(url *url.URL) bool {
|
||||
nStr := url.Query().Get("fullcone")
|
||||
return nStr == "true" || nStr == "1"
|
||||
}
|
||||
|
||||
//SetAddrStr, setNetwork, Isfullcone
|
||||
func setConfByStandardURL(conf *CommonConf, u *url.URL) error {
|
||||
if u.Scheme != DirectName {
|
||||
|
||||
hn := u.Hostname()
|
||||
|
||||
ip := net.ParseIP(hn)
|
||||
if ip != nil {
|
||||
conf.IP = hn
|
||||
} else {
|
||||
conf.Host = hn
|
||||
}
|
||||
|
||||
if hn != u.Host { //给出了port
|
||||
colon := strings.LastIndexByte(u.Host, ':')
|
||||
p, err := strconv.Atoi(u.Host[colon+1:])
|
||||
if err != nil {
|
||||
return err
|
||||
} else if p < 0 || p > 65535 {
|
||||
return utils.ErrInvalidData
|
||||
}
|
||||
conf.Port = p
|
||||
|
||||
}
|
||||
}
|
||||
conf.Network = u.Query().Get("network")
|
||||
|
||||
conf.Fullcone = getFullconeFromUrl(u)
|
||||
conf.Tag = u.Fragment
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//set Tag, NoRoute,Fallback, call setConfByStandardURL
|
||||
func confQueryForServer(conf *ListenConf, u *url.URL) {
|
||||
nr := false
|
||||
q := u.Query()
|
||||
if q.Get("noroute") != "" {
|
||||
nr = true
|
||||
}
|
||||
setConfByStandardURL(&conf.CommonConf, u)
|
||||
|
||||
conf.NoRoute = nr
|
||||
|
||||
conf.Tag = u.Fragment
|
||||
|
||||
fallbackStr := q.Get("fallback")
|
||||
|
||||
conf.Fallback = fallbackStr
|
||||
}
|
||||
|
||||
//给 ProxyCommon 的tls做一些配置上的准备,从url读取配置
|
||||
func setTLS_forConf_withStandardURL(u *url.URL, com *CommonConf, lc *ListenConf, dc *DialConf) error {
|
||||
q := u.Query()
|
||||
insecureStr := q.Get("insecure")
|
||||
if insecureStr != "" && insecureStr != "false" && insecureStr != "0" {
|
||||
|
||||
com.Insecure = true
|
||||
}
|
||||
|
||||
if dc != nil {
|
||||
utlsStr := q.Get("utls")
|
||||
useUtls := utlsStr != "" && utlsStr != "false" && utlsStr != "0"
|
||||
|
||||
dc.Utls = useUtls
|
||||
|
||||
} else {
|
||||
certFile := q.Get("cert")
|
||||
keyFile := q.Get("key")
|
||||
|
||||
lc.TLSCert = certFile
|
||||
lc.TLSKey = keyFile
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package utils
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@@ -85,3 +86,13 @@ func WrapFuncForPromptUI(f func(string) bool) func(string) error {
|
||||
return ErrInvalidData
|
||||
}
|
||||
}
|
||||
|
||||
func QueryPositive(query url.Values, key string) bool {
|
||||
nStr := query.Get(key)
|
||||
return nStr == "true" || nStr == "1"
|
||||
}
|
||||
|
||||
func QueryNegative(query url.Values, key string) bool {
|
||||
nStr := query.Get(key)
|
||||
return nStr == "false" || nStr == "0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user