完成url标准格式,见docs/url.md; 修订代码

This commit is contained in:
e1732a364fed
2000-01-01 00:00:00 +00:00
parent a5b9455b88
commit 6e39f4f2f2
23 changed files with 276 additions and 152 deletions

View File

@@ -135,7 +135,7 @@ verysimple -c server.json
verysimple 继承 v2simple的一个优点就是服务端的配置也可以用url做到。谁规定url只能用于分享客户端配置了一条url肯定比json更容易配置不容易出错。
关于url格式 https://github.com/e1732a364fed/v2ray_simple/discussions/163
关于url格式 [url标准定义](docs/url.md)
### 命令行模式

View File

@@ -42,6 +42,7 @@ package grpc
import (
"github.com/e1732a364fed/v2ray_simple/advLayer"
"github.com/e1732a364fed/v2ray_simple/utils"
)
func init() {
@@ -79,10 +80,10 @@ func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) {
grpc_multi := false
if extra := conf.Extra; len(extra) > 0 {
if thing := extra["grpc_multi"]; thing != nil {
if use_multi, ok := thing.(bool); ok {
grpc_multi = use_multi
}
if v, ok := utils.AnyToBool(thing); ok {
grpc_multi = v
}
}
}

View File

@@ -191,7 +191,7 @@ func getExtra(extra map[string]any) (useHysteria, hysteria_manual bool,
maxStreamsInOneConn int64) {
if thing := extra["maxStreamsInOneConn"]; thing != nil {
if count, ok := thing.(int64); ok && count > 0 {
if count, ok := utils.AnyToInt64(thing); ok && count > 0 {
if ce := utils.CanLogInfo("Quic max Streams In One Conn"); ce != nil {
ce.Write(zap.Int("count,", int(count)))
} else {
@@ -209,7 +209,7 @@ func getExtra(extra map[string]any) (useHysteria, hysteria_manual bool,
useHysteria = true
if thing := extra["mbps"]; thing != nil {
if mbps, ok := thing.(int64); ok && mbps > 1 {
if mbps, ok := utils.AnyToInt64(thing); ok && mbps > 1 {
maxbyteCount = int(mbps) * 1024 * 1024 / 8
}
} else {
@@ -224,7 +224,7 @@ func getExtra(extra map[string]any) (useHysteria, hysteria_manual bool,
}
if thing := extra["hy_manual"]; thing != nil {
if ismanual, ok := thing.(bool); ok {
if ismanual, ok := utils.AnyToBool(thing); ok {
hysteria_manual = ismanual
if ismanual {
@@ -235,7 +235,7 @@ func getExtra(extra map[string]any) (useHysteria, hysteria_manual bool,
}
if thing := extra["hy_manual_initial_rate"]; thing != nil {
if initRate, ok := thing.(float64); ok {
if initRate, ok := utils.AnyToFloat64(thing); ok {
if rateOk(initRate) == 0 {
TheCustomRate = initRate

View File

@@ -372,8 +372,8 @@ func interactively_hotLoadUrlConfig() {
Select := promptui.Select{
Label: "请选择你的url的格式类型",
Items: []string{
"协议官方url格式(视代理协议不同而不同)",
"vs标准url格式",
"协议官方url格式(视代理协议不同而不同)",
},
}
i, result, err := Select.Run()
@@ -386,16 +386,16 @@ func interactively_hotLoadUrlConfig() {
fmt.Printf("你选择了 %s\n", result)
switch i {
case 0:
case 1:
fmt.Printf("目前暂不支持")
return
case 1:
case 0:
Select := promptui.Select{
Label: "请选择该url是用于服务端还是客户端",
Label: "请选择该url是用于dial还是listen",
Items: []string{
"客户端",
"服务端",
"dial",
"listen",
},
}
i, result, err := Select.Run()
@@ -418,7 +418,7 @@ func interactively_hotLoadUrlConfig() {
}
if i == 0 {
u, sn, _, okTls, err := proxy.GetRealProtocolFromClientUrl(theUrlStr)
u, sn, creator, okTls, err := proxy.GetRealProtocolFromClientUrl(theUrlStr)
if err != nil {
fmt.Printf("parse url failed %v\n", err)
return
@@ -432,12 +432,17 @@ func interactively_hotLoadUrlConfig() {
fmt.Printf("parse url failed %v\n", err)
return
}
dc, err = creator.URLToDialConf(u, dc, proxy.UrlStandardFormat)
if err != nil {
fmt.Printf("parse url step 2 failed %v\n", err)
return
}
hotLoadDialConfForRuntime("", []*proxy.DialConf{dc})
} else {
u, sn, _, okTls, err := proxy.GetRealProtocolFromServerUrl(theUrlStr)
u, sn, creator, okTls, err := proxy.GetRealProtocolFromServerUrl(theUrlStr)
if err != nil {
fmt.Printf("parse url failed %v\n", err)
return
@@ -453,7 +458,11 @@ func interactively_hotLoadUrlConfig() {
fmt.Printf("parse url failed %v\n", err)
return
}
lc, err = creator.URLToListenConf(u, lc, proxy.UrlStandardFormat)
if err != nil {
fmt.Printf("parse url step 2 failed %v\n", err)
return
}
hotLoadListenConfForRuntime([]*proxy.ListenConf{lc})
}
return

View File

@@ -5,6 +5,8 @@ package configAdapter provides methods to convert proxy.ListenConf and proxy.Dia
参考 https://github.com/e1732a364fed/v2ray_simple/discussions/163
以及 docs/url.md
本包内的函数不支持 vs约定的 末尾+s即表示使用tls的用法, 所以调用函数之前要预处理一下。
本包依然秉持KISS原则用最笨的代码、最少的依赖实现最小的可执行文件大小以及最快的执行速度。

View File

@@ -1,8 +1,6 @@
# vs的通用url格式 ( verysimple Standard URL Format )
虽然verysimple通用url格式从一开始就存在但是却没有文档详细描述阅读proxy/creator_url.go 可以了解毕竟golang的理念是代码就是文档。。那么在这里就作为文档进行介绍。
vs的通用url格式并不遵循现存市面上的其他格式而是针对vs的架构逻辑所设计的新格式。如果你是一个vs老手则使用vs的通用url格式你会感觉得心应手。
注意要想读懂本文档需要了解url的基础知识。
@@ -20,8 +18,19 @@ authority = [userinfo "@"] host [":" port]
```
举例
`vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&v=0&utls=true#myvless1`
## 举例
```
vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&v=0&utls=true#my_vless1
vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?adv=quic&v=0&extra.maxStreamsInOneConn=6&extra.congestion_control=hy&extra.mbps=1024#my_vless_quic
dokodemo://?target.ip=1.1.1.1&target.port=80#my_doko
shadowsocks://aes-128-gcm:mypasswordxxxxx@127.0.0.1:8000#my_ss
socks5://myusername:mypassword@127.0.0.1:1080#my_socks5
```
## 基础部分
@@ -37,7 +46,7 @@ authority = [userinfo "@"] host [":" port]
熟悉ssh的同学会看出差别ssh中userinfo 是不包含密码的也就是说ssh 需要 额外参数 或 步骤 来输入密码 或 等价信息 才能进行鉴权而我们这里直接将鉴权的信息也包含在userinfo中来。
在vless/vmess中 就是 uuid在socks5/http中 就是 “用户名+密码”在ss/trojan中就是 密码。
在vless/vmess中 就是 uuid, 在socks5/http中 使用 “用户名:密码”的形式, 在shadowsocks中, 使用 method:password 的形式, 在trojan 中就是 密码。
### host:port
@@ -51,7 +60,7 @@ authority = [userinfo "@"] host [":" port]
## query
v=0, 控制该proxy协议的 版本。
v, 控制该proxy协议的 版本。
fallback=:80 设置回落的地址。
@@ -59,11 +68,46 @@ network 设置 使用的传输层,如 network="udp", 如不给出则默认n
fullcone=true设置 是否需要udp的fullcone功能。
security=aes-128-gcm 设置 vmess/ss等存在多种加密方式等proxy的 具体加密方式
security=aes-128-gcm 设置 vmess/ss等存在多种加密方式等proxy的 具体加密方式
adv=ws 设置使用的高级层,如不给出则没有高级层,如给出,可选 ws, grpc, quic
mux=true 是否使用内层smux
path=/mypath 设置 http头存在时或者用 ws的 路径 或 grpc的 service name。
### http 头相关
http=true 设置是否使用http头
http.method=GET 设置 http的method
http.version=1.1 设置http的版本
以 "header." 开头的字段开头字母大写后放到Header里。
比如,如果 vmess://uuid?http=true&header.host=myhost.com&header.Accept-Encoding=gzip,deflate
则会设置http头的header 的 Host 字段为 myhost.com设置 Accept-Encoding 为 gzip,deflate, 如果有空格等情况的话,需要进行 百分号转译。
转译可以使用这个工具测试: https://www.url-encode-decode.com/
### extra 相关
在标准toml配置中有时会填充extra项来配置额外参数。在本标准url格式中该类配置也是相当简单的。
就是用 extra.yyy=zzz 的格式
举几个例子,比如 reject 类型的proxy 需要一个 type 的extra参数 来指示 使用什么类型的拒绝响应;
此时在url中这么写 extra.type=nginx
再举几个例子
```
#quic
extra.maxStreamsInOneConn=6&extra.congestion_control=hy&extra.mbps=1024
#grpc (非grpcSimple的情况)
extra.grpc_multi=true
```
### tls相关 proxy/tlsConfig.go)
@@ -75,8 +119,8 @@ cert=cert.pem&key=cert.key
用于设置证书名称。
### 其他
dokodemo的目标这么写 `target.ip=1.1.1.1&target.port=80`

View File

@@ -19,7 +19,7 @@ const (
var (
// Url格式 设置以何种方式解析 命令行模式/极简模式 中出现的url配置
//
//关于url格式的详细https://github.com/e1732a364fed/v2ray_simple/discussions/163
//关于url格式的详细docs/url.md
UrlFormat = UrlStandardFormat
)
@@ -175,6 +175,8 @@ func URLToCommonConf(u *url.URL, conf *CommonConf) error {
conf.Network = q.Get("network")
conf.Uuid = u.User.Username()
conf.Fullcone = utils.QueryPositive(q, "fullcone")
conf.Tag = u.Fragment
conf.Path = u.Path
@@ -281,5 +283,19 @@ func URLToListenConf(u *url.URL, conf *ListenConf) error {
conf.TLSKey = keyFile
}
targetStr := q.Get("target.ip")
if targetStr != "" {
target_portStr := q.Get("target.port")
if target_portStr != "" {
if conf.Network == "" {
conf.Network = "tcp"
}
taStr := conf.Network + "://" + targetStr + ":" + target_portStr
conf.TargetAddr = taStr
}
}
return e
}

View File

@@ -53,7 +53,6 @@ func init() {
type ServerCreator struct{}
// 用如下参数形式: network=tcp&target=127.0.0.1&target_port=80
func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) {
if format != proxy.StandardMode {
return lc, utils.ErrUnImplemented
@@ -62,20 +61,6 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format
return nil, utils.ErrNilParameter
}
targetStr := url.Query().Get("target")
if targetStr == "" {
return nil, utils.ErrNilParameter
}
target_portStr := url.Query().Get("target_port")
if target_portStr == "" {
return nil, utils.ErrNilParameter
}
if lc.Network == "" {
lc.Network = "tcp"
}
taStr := lc.Network + "://" + targetStr + ":" + target_portStr
lc.TargetAddr = taStr
return lc, nil
}

View File

@@ -57,13 +57,16 @@ func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format in
lc = &proxy.ListenConf{}
}
user := u.Query().Get("user")
pass := u.Query().Get("pass")
if p, set := u.User.Password(); set {
user := u.User.Username()
pass := p
lc.Users = append(lc.Users, utils.UserConf{
User: user,
Pass: pass,
})
}
lc.Users = append(lc.Users, utils.UserConf{
User: user,
Pass: pass,
})
return lc, nil
}

View File

@@ -66,29 +66,11 @@ func (RejectCreator) NewClient(dc *DialConf) (Client, error) {
return r, nil
}
func (RejectCreator) initCommonConfByURL(url *url.URL, lc *CommonConf, format int) {
if format != UrlStandardFormat {
return
}
nStr := url.Query().Get("type")
if nStr != "" {
lc.Extra = make(map[string]any)
lc.Extra["type"] = nStr
}
}
func (rc RejectCreator) URLToDialConf(url *url.URL, iv *DialConf, format int) (*DialConf, error) {
if iv == nil {
iv = &DialConf{}
}
rc.initCommonConfByURL(url, &iv.CommonConf, format)
return iv, nil
}
@@ -97,7 +79,6 @@ func (rc RejectCreator) URLToListenConf(url *url.URL, iv *ListenConf, format int
if iv == nil {
iv = &ListenConf{}
}
rc.initCommonConfByURL(url, &iv.CommonConf, format)
return iv, nil
}

View File

@@ -25,10 +25,11 @@ func (ClientCreator) URLToDialConf(u *url.URL, dc *proxy.DialConf, format int) (
dc = &proxy.DialConf{}
}
m := u.Query().Get("method")
p := u.Query().Get("pass")
if p, set := u.User.Password(); set {
dc.Uuid = "method:" + u.User.Username() + "\npass:" + p
}
dc.Uuid = "method:" + m + "\npass:" + p
return dc, nil
}

View File

@@ -38,10 +38,10 @@ func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format in
lc = &proxy.ListenConf{}
}
m := u.Query().Get("method")
p := u.Query().Get("pass")
if p, set := u.User.Password(); set {
lc.Uuid = "method:" + u.User.Username() + "\npass:" + p
}
lc.Uuid = "method:" + m + "\npass:" + p
return lc, nil
}

View File

@@ -25,10 +25,10 @@ func (ClientCreator) URLToDialConf(u *url.URL, dc *proxy.DialConf, format int) (
dc = &proxy.DialConf{}
}
user := u.Query().Get("user")
pass := u.Query().Get("pass")
if p, set := u.User.Password(); set {
dc.Uuid = "user:" + u.User.Username() + "\npass:" + p
}
dc.Uuid = "user:" + user + "\npass:" + pass
return dc, nil
}

View File

@@ -50,13 +50,15 @@ func (ServerCreator) URLToListenConf(u *url.URL, lc *proxy.ListenConf, format in
lc = &proxy.ListenConf{}
}
user := u.Query().Get("user")
pass := u.Query().Get("pass")
if p, set := u.User.Password(); set {
user := u.User.Username()
pass := p
lc.Users = append(lc.Users, utils.UserConf{
User: user,
Pass: pass,
})
}
lc.Users = append(lc.Users, utils.UserConf{
User: user,
Pass: pass,
})
return lc, nil
}

View File

@@ -25,9 +25,10 @@ func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int)
case proxy.UrlStandardFormat:
if dc == nil {
dc = &proxy.DialConf{}
uuidStr := url.User.Username()
dc.Uuid = uuidStr
}
uuidStr := url.User.Username()
dc.Uuid = uuidStr
return dc, nil
default:
return nil, utils.ErrUnImplemented

View File

@@ -35,11 +35,10 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format
case proxy.UrlStandardFormat:
if lc == nil {
lc = &proxy.ListenConf{}
uuidStr := url.User.Username()
lc.Uuid = uuidStr
}
uuidStr := url.User.Username()
lc.Uuid = uuidStr
return lc, nil
default:
return nil, utils.ErrUnImplemented

View File

@@ -59,27 +59,8 @@ func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int)
case proxy.UrlStandardFormat:
if dc == nil {
dc = &proxy.DialConf{}
}
uuidStr := url.User.Username()
dc.Uuid = uuidStr
vStr := url.Query().Get("v")
if vStr != "" {
v, e := strconv.Atoi(vStr)
if e != nil {
return nil, e
}
dc.Version = v
if v == 1 {
vless1_udp_multiStr := url.Query().Get("vless1_udp_multi")
if vless1_udp_multiStr == "true" || vless1_udp_multiStr == "1" {
if dc.Extra == nil {
dc.Extra = make(map[string]any)
}
dc.Extra["vless1_udp_multi"] = true
}
}
uuidStr := url.User.Username()
dc.Uuid = uuidStr
}

View File

@@ -6,7 +6,6 @@ import (
"io"
"net"
"net/url"
"strconv"
"unsafe"
"github.com/e1732a364fed/v2ray_simple/netLayer"
@@ -54,18 +53,8 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format
if lc == nil {
lc = &proxy.ListenConf{}
}
uuidStr := url.User.Username()
lc.Uuid = uuidStr
vStr := url.Query().Get("v")
if vStr != "" {
v, e := strconv.Atoi(vStr)
if e != nil {
return nil, e
}
lc.Version = v
uuidStr := url.User.Username()
lc.Uuid = uuidStr
}
return lc, nil

View File

@@ -56,23 +56,12 @@ func (ClientCreator) URLToDialConf(url *url.URL, dc *proxy.DialConf, format int)
if dc == nil {
dc = &proxy.DialConf{}
}
uuidStr := url.User.Username()
uuidStr := url.User.Username()
dc.Uuid = uuidStr
query := url.Query()
security := query.Get("security")
if security != "" {
if dc.Extra == nil {
dc.Extra = make(map[string]any)
}
dc.Extra[Security_confStr] = security
dc.Uuid = uuidStr
}
return dc, nil
}

View File

@@ -76,11 +76,10 @@ func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format
if lc == nil {
lc = &proxy.ListenConf{}
uuidStr := url.User.Username()
lc.Uuid = uuidStr
}
uuidStr := url.User.Username()
lc.Uuid = uuidStr
return lc, nil
default:
return lc, utils.ErrUnImplemented

133
utils/cast.go Normal file
View File

@@ -0,0 +1,133 @@
package utils
import (
"net/url"
"strconv"
"strings"
)
func StrPositive(value string) bool {
value = strings.ToLower(value)
return value == "1" || value == "on" || value == "true"
}
func StrNegative(value string) bool {
value = strings.ToLower(value)
return value == "" || value == "0" || value == "off" || value == "false"
}
func QueryPositive(query url.Values, key string) bool {
nStr := query.Get(key)
return StrPositive(nStr)
}
func QueryNegative(query url.Values, key string) bool {
nStr := query.Get(key)
return StrNegative(nStr)
}
func AnyToBool(a any) (v bool, ok bool) {
ok = true
switch value := a.(type) {
case bool:
v = value
case int:
if value == 0 {
v = false
} else {
v = true
}
case int64:
if value == 0 {
v = false
} else {
v = true
}
case string:
if StrNegative(value) {
v = false
} else if StrPositive(value) {
v = true
}
default:
var i64 int64
i64, ok = AnyToInt64(a)
if ok {
v, ok = AnyToBool(i64)
return
}
ok = false
}
return
}
func AnyToInt64(a any) (int64, bool) {
switch value := a.(type) {
case int64:
return value, true
case int:
return int64(value), true
case uint:
return int64(value), true
case int32:
return int64(value), true
case int16:
return int64(value), true
case int8:
return int64(value), true
case uint64:
return int64(value), true
case uint32:
return int64(value), true
case uint16:
return int64(value), true
case uint8:
return int64(value), true
case float64:
return int64(value), true
case float32:
return int64(value), true
case string:
v, err := strconv.Atoi(value)
if err == nil {
return int64(v), true
}
}
return 0, false
}
func AnyToFloat64(a any) (float64, bool) {
switch value := a.(type) {
case float64:
return value, true
case float32:
return float64(value), true
case int64:
return float64(value), true
case int:
return float64(value), true
case uint:
return float64(value), true
case int32:
return float64(value), true
case int16:
return float64(value), true
case int8:
return float64(value), true
case uint32:
return float64(value), true
case uint16:
return float64(value), true
case uint8:
return float64(value), true
case string:
v, err := strconv.ParseFloat(value, 64)
if err == nil {
return v, true
}
}
return 0, false
}

View File

@@ -3,7 +3,6 @@ package utils
import (
"flag"
"net/url"
"os"
"strings"
@@ -86,13 +85,3 @@ 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"
}

View File

@@ -67,7 +67,7 @@ GenerateUUID_v4 生成符合v4标准的uuid.
version: https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.3
*/
func GenerateUUID_v4() (r [UUID_BytesLen]byte) {
func GenerateUUID_v4() (r UUID) {
rand.Reader.Read(r[:])
r[6] = (r[6] & 0x0f) | 0x40 // Version 4
r[8] = (r[8] & 0x3f) | 0x80