mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-09 02:30:15 +08:00
feat:添加回落和分流功能.
创建新子包netLayer, 将 proxy.Addr改为 netLayer.Addr 修订文档 RoutePolicy等分流机制也放到 netLayer 引入github.com/oschwald/maxminddb-golang 依赖,支持使用 GeoLite2-Country.mmdb 来进行ip分流 另外注意它默认的版本对于 golang.org/x/sys 包的依赖太老了,会导致go1.18中编译不通过,我在 go.mod 文件中新增了下面代码,就能通过编译了 ``` require ( golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect ) ``` verysimple的可执行文件的相同目录下,必须有该mmdb文件才能够开启ip分流功能 新配置方式:配置文件新加一行 "route":{ "mycountry":"CN" } mycountry指定的国家的ip会被直连发送,其他地址ip会被发送到代理. 新配置方式,回落,直接在 local 项的 url 的 query部分添加 fallback=:80, 或者 fallback=127.0.0.1:80 即可 回落到指定端口. 将tls_test重新挪动到tlsLayer包中 在main.go中添加了 logLevel变量,并且把关于配置文件的部分挪动到 config.go 出了上面的分流和回落以外,还新增支持了 #xxx 的尾缀,用于配置该url的tag. tag在未来会被用于精准分流 Makefile中新增了 PACK 参数用于编译出 打包版的发行包;可选 tag=embed_geoip 参数用于将mmdb.tgz文件内置到可执行程序里 同时,我开始直接使用go1.18编译本项目,期待性能提升,因为这是新发布的版本,看了介绍据说对 mac m1有20%的提升.
This commit is contained in:
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,8 +1,10 @@
|
||||
*.DS_Store
|
||||
v2ray_simple
|
||||
v2ray_simple_linux*
|
||||
v2ray_simple_win*
|
||||
verysimple_*
|
||||
*.exe
|
||||
client.json
|
||||
server.json
|
||||
v2ray_simple
|
||||
v2ray_simple_*
|
||||
verysimple
|
||||
verysimple_*
|
||||
*.exe
|
||||
*.DS_Store
|
||||
*.mmdb
|
||||
*.tgz
|
||||
|
50
Makefile
50
Makefile
@@ -8,33 +8,58 @@ macosAmd :=_macos_
|
||||
macosArm :=_macos_m1_
|
||||
windows :=_win10_
|
||||
|
||||
# for building with filename "verysimple" and pack into verysimple_xxx.tgz:
|
||||
# make PACK=1
|
||||
|
||||
#我们只支持64位
|
||||
|
||||
linuxAmdFn:=${prefix}${linuxAmd}${BUILD_VERSION}
|
||||
linuxArmFn:=${prefix}${linuxArm}${BUILD_VERSION}
|
||||
macFn :=${prefix}${macosAmd}${BUILD_VERSION}
|
||||
macM1Fn :=${prefix}${macosArm}${BUILD_VERSION}
|
||||
winFn :=${prefix}${windows}${BUILD_VERSION}
|
||||
|
||||
cmd:=go build -trimpath -ldflags "-X 'main.Version=${BUILD_VERSION}' -s -w -buildid=" -o
|
||||
# for embedding geoip file:
|
||||
# make tags="embed_geoip" macm1
|
||||
|
||||
all: win10 linux_amd64 linux_arm64 macos macos_arm64
|
||||
cmd:=go build -tags $(tags) -trimpath -ldflags "-X 'main.Version=${BUILD_VERSION}' -s -w -buildid=" -o
|
||||
|
||||
|
||||
ifdef PACK
|
||||
define compile
|
||||
GOOS=$(2) GOARCH=$(3) $(cmd) $(1)
|
||||
mv $(1) verysimple$(4)
|
||||
tar -czf $(1).tgz verysimple$(4)
|
||||
rm verysimple$(4)
|
||||
endef
|
||||
|
||||
else
|
||||
|
||||
define compile
|
||||
GOOS=$(2) GOARCH=$(3) $(cmd) $(1)$(4)
|
||||
endef
|
||||
endif
|
||||
|
||||
|
||||
all: win10 linux_amd64 linux_arm64 macos macm1
|
||||
|
||||
#注意调用参数时,逗号前后不能留空格
|
||||
|
||||
linux_amd64:
|
||||
GOARCH=amd64 GOOS=linux $(cmd) $(linuxAmdFn)
|
||||
$(call compile, $(linuxAmdFn),linux,amd64)
|
||||
|
||||
linux_arm64:
|
||||
GOARCH=arm64 GOOS=linux $(cmd) $(linuxArmFn)
|
||||
$(call compile, $(linuxArmFn),linux,arm64)
|
||||
|
||||
macos:
|
||||
GOARCH=amd64 GOOS=darwin $(cmd) $(macFn)
|
||||
$(call compile, $(macFn),darwin,amd64)
|
||||
|
||||
#我也提供mac 的apple silicon版本.
|
||||
macos_arm64:
|
||||
GOARCH=arm64 GOOS=darwin $(cmd) $(macM1Fn)
|
||||
#我也提供macos 的apple silicon版本.
|
||||
macm1:
|
||||
$(call compile, $(macM1Fn),darwin,arm64)
|
||||
|
||||
win10:
|
||||
GOARCH=amd64 GOOS=windows $(cmd) ${winFn}.exe
|
||||
$(call compile, $(winFn),windows,amd64,.exe)
|
||||
|
||||
|
||||
clean:
|
||||
@@ -43,3 +68,10 @@ clean:
|
||||
rm -f ${winFn}.exe
|
||||
rm -f $(macFn)
|
||||
rm -f $(macM1Fn)
|
||||
rm -f $(linuxAmdFn).tgz
|
||||
rm -f $(linuxArmFn).tgz
|
||||
rm -f ${winFn}.tgz
|
||||
rm -f $(macFn).tgz
|
||||
rm -f $(macM1Fn).tgz
|
||||
|
||||
|
||||
|
@@ -10,12 +10,15 @@
|
||||
|
||||
本项目独立发展,希望大家不要关注给其它项目贡献的问题,多关注给咱们verysimple项目贡献,不要本末倒置。
|
||||
|
||||
v2fly社区 的代码风气不好,竟然对 ’要写注释‘ 这个问题大加反驳,而且讨论时经常没有证据就指责他人,还讽刺人(从普通路人到真实开发者都是这个风气);内部开发者还有一个私聊群,从未公开,也就是说,Project V群,一个v2fly群,还有“私密开发者”群三群独立;对“群管理员”控制很差,v2ray“实控人”自己也承认,自己并不怎么看“社区群”的聊天,面向社区的群中的群管理同意的PR请求到了真正的开发者嘴上就又变得不同意,而且理由自己相互矛盾。整个社区充满“能不做就不做,代码不需要注释,开发者内部自己能看懂就行”的气氛,对创新以及代码质量的改善充满了抵触情绪,一副大爷的嘴脸。对于一些 “可能” “正在做的东西”,没有证据就说“正在做了”,毫无意义,给予虚假的期望。这种社区,没必要提PR的,自己没事找事,徒增烦恼。就算“实控人”的话语态度很和气,那也是老态龙钟,表面迎合,很难适应新变化,自己对自己的“管理员”们 表示下无奈而已,就像去餐馆吃饭,价格或者菜有问题,找他们经理一样,找来,明面说要教育自己员工,实际上回去还不是什么也不做, 笑面虎。
|
||||
v2fly社区 的代码风气不好,竟然对 ’要写注释‘ 这个问题大加反驳,而且讨论时经常没有证据就指责他人,还讽刺人(从普通路人到真实开发者都是这个风气);内部开发者还有一个私聊群,从未公开,也就是说,Project V群,一个v2fly群,还有“私密开发者”群三群独立;对“群管理员”控制很差,v2ray“实控人”自己也承认,自己并不怎么看“社区群”的聊天,面向社区的群中的群管理同意的PR请求到了真正的开发者嘴上就又变得不同意,而且理由自己相互矛盾。整个社区充满“能不做就不做,代码不需要注释,开发者内部自己能看懂就行”的气氛,对创新以及代码质量的改善充满了抵触情绪,一副大爷的嘴脸。对于一些 “可能” “正在做的东西”,没有证据就说“正在做了”,毫无意义,给予虚假的期望。这种社区,没必要提PR的,自己没事找事,徒增烦恼。就算“实控人”的话语态度很和气,那也是老态龙钟,表面迎合,很难适应新变化,自己对自己的“管理员”们 表示下无奈而已,就像去餐馆吃饭,价格或者菜有问题,找他们经理一样,找来,明面说要教育自己员工,实际上回去还不是什么也不做, 笑面虎.我成天热脸贴冷屁股,心态再好的人也崩了啊. 感觉他们就像被感染了一种僵尸病毒一样,腐朽,沿着自己固定的思路慢慢变老。
|
||||
|
||||
而xray更是漏洞百出、后继无人;
|
||||
|
||||
在中国的人,从小到大都在学习政治,各个管理员都是政治怪物。我已经向前看了,我在乎他们吗?他们那么垃圾,我巴不得跟他们划清界限。现在他们是把我逼上梁山了。
|
||||
|
||||
他们都已经是历史产物,我们要学会扬弃。只要我们做的足够好,就会有人来贡献的,不怕社区小,不怕没支持。我们向前看。
|
||||
|
||||
开源社区,就是自己动手,丰衣足食,风气不好、漏洞百出的东西我们 要扬弃,才能不断进化。否则的话,就会被时代所抛弃。
|
||||
|
||||
历史的车轮滚滚向前。
|
||||
|
||||
|
31
README.md
31
README.md
@@ -54,9 +54,9 @@ tls lazy encrypt 特性 运行时可以用 -lazy 参数打开(服务端客户
|
||||
|
||||
关于 splice,还可以参考我的文章 https://github.com/hahahrfool/xray_splice-
|
||||
|
||||
该特性不完全稳定,可能会导致一些网页访问有时出现异常
|
||||
该特性不完全稳定,可能会导致一些网页访问有时出现异常,有时出现bad mac alert;刷新页面可以解决
|
||||
|
||||
不是速度慢,是因为 目前的tls过滤方式有点问题, 对close_alert等情况没处理好。而且使用不同的浏览器,现象也会不同,似乎对safari支持好一些, chrome就差很多
|
||||
不是速度慢,是因为 目前的tls过滤方式有点问题, 对close_alert等情况没处理好。而且使用不同的浏览器,现象也会不同,似乎对safari支持好一些, chrome就差一些
|
||||
|
||||
在我的最新代码里,采用了独特的技术,已经规避了大部分不稳定性。总之比较适合看视频,毕竟双向splice,不是白给的!
|
||||
|
||||
@@ -107,8 +107,25 @@ cp client.example.json client.json
|
||||
cp server.example.json server.json
|
||||
```
|
||||
|
||||
详细优化的编译参数请参考Makefile文件
|
||||
|
||||
如果你是直接下载的可执行文件,则不需要 go build了,直接复制example.json文件即可
|
||||
|
||||
### 关于内嵌geoip 文件
|
||||
|
||||
默认的Makefile或者直接 go build 是不开启内嵌功能的,需要加载外部mmdb文件,就是说你要自己去下载mmdb文件,
|
||||
|
||||
可以从 https://github.com/P3TERX/GeoLite.mmdb 项目,https://github.com/Loyalsoldier/geoip 项目, 或者类似项目 进行下载
|
||||
|
||||
加载的外部文件 必须使用原始 mmdb格式。
|
||||
|
||||
若要内嵌编译,要用 `tar -czf GeoLite2-Country.mmdb.tgz GeoLite2-Country.mmdb` 来打包一下,将生成的tgz文件放到 netLayer文件夹中,然后再编译 ,用 `go build -tags embed_geoip` 编译
|
||||
|
||||
内嵌编译 所使用的 文件名 必须是 GeoLite2-Country.mmdb.tgz
|
||||
|
||||
|
||||
因为为了减小文件体积,所以才内嵌的gzip格式,而不是直接内嵌原始数据
|
||||
|
||||
## 使用方式
|
||||
|
||||
```sh
|
||||
@@ -137,7 +154,9 @@ v2ray_simple -c server.json
|
||||
|
||||
文档、注释尽量详细,且尽量完全使用中文,尽量符合golang的各种推荐标准。
|
||||
|
||||
根据golang的标准,注释就是文档本身(godoc的原理),所以一定要多写注释。
|
||||
根据golang的标准,注释就是文档本身(godoc的原理),所以一定要多写注释。不要以为解释重复了就不要写,因为要生成godoc文档,在 pkg.go.dev 上 给用户看的时候它们首先看到的是注释内容,而不是代码内容
|
||||
|
||||
本项目所生成的文档在 https://pkg.go.dev/github.com/hahahrfool/v2ray_simple
|
||||
|
||||
再次重复,文档越多越好,尽量降低开发者入门的门槛。
|
||||
|
||||
@@ -206,7 +225,7 @@ openssl req -new -x509 -days 7305 -key cert.key -out cert.pem
|
||||
测试环境:ubuntu虚拟机, 使用开源测试工具
|
||||
https://github.com/librespeed/speedtest-go
|
||||
|
||||
编译后运行,会监听8989
|
||||
编译后运行,会监听8989。注意要先按speedtest-go的要求,把web/asset文件夹 和一个toml配置文件 放到 可执行文件的文件夹中,我们直接在项目文件夹里编译的,所以直接移动到项目文件夹根部即可
|
||||
|
||||
然后内网搭建nginx 前置,加自签名证书,配置添加反代:
|
||||
`proxy_pass http://127.0.0.1:8989;`
|
||||
@@ -256,7 +275,9 @@ https://github.com/librespeed/speedtest-go
|
||||
https://t.me/shadowrocket_unofficial
|
||||
|
||||
|
||||
# 免责声明
|
||||
# 免责声明与鸣谢
|
||||
|
||||
## 免责
|
||||
|
||||
MIT协议!我不负任何责任。本项目只是个代理项目,适合内网测试使用,以及适合阅读代码了解原理。
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"local": "socks5://127.0.0.1:10800",
|
||||
"remote": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&version=0"
|
||||
"local": "socks5://127.0.0.1:10800#myvlesss1",
|
||||
"remote": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&version=0",
|
||||
"route":{ "mycountry":"CN" ,"comment":"route这一项是可选的,如果没给出的话,就不分流;写了mycountry后, 向该国家的ip发送的请求就会直连, 然后其他的过代理;本comment项你可以自行删掉, 注意要删的话, 连着前面的逗号一起删"}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"local": "socks5://0.0.0.0:10800",
|
||||
"remote": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&version=0"
|
||||
"remote": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&version=0",
|
||||
"xroute":{ "mycountry":"CN" }
|
||||
}
|
||||
|
44
config.go
Normal file
44
config.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/common"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server_ThatListenPort_Url string `json:"local"`
|
||||
Client_ThatDialRemote_Url string `json:"remote"`
|
||||
Route *RouteStruct `json:"route"`
|
||||
}
|
||||
|
||||
type RouteStruct struct {
|
||||
MyCountryISO_3166 string `json:"mycountry"` //加了mycountry后,就会自动按照geoip分流
|
||||
}
|
||||
|
||||
func loadConfig(fileName string) (*Config, error) {
|
||||
path := common.GetFilePath(fileName)
|
||||
if len(path) > 0 {
|
||||
if cf, err := os.Open(path); err == nil {
|
||||
defer cf.Close()
|
||||
bs, _ := ioutil.ReadAll(cf)
|
||||
config := &Config{}
|
||||
if err = json.Unmarshal(bs, config); err != nil {
|
||||
return nil, fmt.Errorf("can not parse config file %v, %v", fileName, err)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("can not load config file %v", fileName)
|
||||
}
|
||||
|
||||
func loadConfigFromStr(str string) (*Config, error) {
|
||||
config := &Config{}
|
||||
if err := json.Unmarshal([]byte(str), config); err != nil {
|
||||
return nil, common.NewErr("can not parse config ", err)
|
||||
}
|
||||
return config, nil
|
||||
}
|
37
config_test.go
Normal file
37
config_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNormalClientConfig(t *testing.T) {
|
||||
confstr1 := `{
|
||||
"local": "socks5://0.0.0.0:10800#taglocal",
|
||||
"remote": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&version=0#tag1",
|
||||
"route":{ "mycountry":"CN" }
|
||||
}`
|
||||
|
||||
mc, err := loadConfigFromStr(confstr1)
|
||||
if err != nil {
|
||||
t.Log("loadConfigFromStr err", err)
|
||||
t.FailNow()
|
||||
}
|
||||
t.Log(mc.Client_ThatDialRemote_Url)
|
||||
u, e := url.Parse(mc.Client_ThatDialRemote_Url)
|
||||
if e != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
t.Log(u.Fragment)
|
||||
|
||||
u, e = url.Parse(mc.Server_ThatListenPort_Url)
|
||||
if e != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
t.Log(u.Fragment)
|
||||
t.Log(mc.Server_ThatListenPort_Url)
|
||||
t.Log(mc.Route.MyCountryISO_3166)
|
||||
if mc.Route.MyCountryISO_3166 != "CN" {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
12
go.mod
12
go.mod
@@ -1,5 +1,13 @@
|
||||
module github.com/hahahrfool/v2ray_simple
|
||||
|
||||
go 1.14
|
||||
go 1.17
|
||||
|
||||
require github.com/yl2chen/cidranger v1.0.2
|
||||
require (
|
||||
github.com/oschwald/maxminddb-golang v1.8.0
|
||||
github.com/yl2chen/cidranger v1.0.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
|
||||
)
|
||||
|
21
go.sum
21
go.sum
@@ -1,26 +1,19 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
|
||||
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yl2chen/cidranger v1.0.0 h1:9tdo0orHQJvXsX6mf+1Goou/R4kq21AfpbYeTcpXs2Q=
|
||||
github.com/yl2chen/cidranger v1.0.0/go.mod h1:L7Msw4X7EQK7zMVjOtv7o8xMyjv1rJcNlYlMgGwP7ko=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
|
@@ -1,3 +1,6 @@
|
||||
/*
|
||||
Package httpLayer 提供http层的一些方法和定义
|
||||
*/
|
||||
package httpLayer
|
||||
|
||||
const (
|
||||
|
@@ -25,7 +25,7 @@ func HasFallbackType(ftype, b byte) bool {
|
||||
type Fallback interface {
|
||||
GetFallback(ftype byte, param string) *netLayer.Addr
|
||||
SupportType() byte //参考Fallback_开头的常量。如果支持多个,则返回它们 按位与 的结果
|
||||
FirstBuffer() *bytes.Buffer
|
||||
FirstBuffer() *bytes.Buffer //因为能确认fallback一定是读取过数据的,所以需要给出之前所读的数据,fallback时要用到,要重新传输给目标服务器
|
||||
}
|
||||
|
||||
type SingleFallback struct {
|
||||
@@ -45,7 +45,7 @@ func (ef *SingleFallback) FirstBuffer() *bytes.Buffer {
|
||||
return ef.First
|
||||
}
|
||||
|
||||
//实现 Fallback
|
||||
//实现 Fallback,支持 path,alpn, sni 分流
|
||||
type ClassicFallback struct {
|
||||
First *bytes.Buffer
|
||||
Default *netLayer.Addr
|
||||
|
76
main.go
76
main.go
@@ -1,11 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
@@ -25,22 +23,36 @@ import (
|
||||
"github.com/hahahrfool/v2ray_simple/proxy"
|
||||
)
|
||||
|
||||
const (
|
||||
log_debug = iota
|
||||
log_info
|
||||
log_warning
|
||||
log_error
|
||||
)
|
||||
|
||||
var (
|
||||
desc = "v2ray_simple, a very simple implementation of V2Ray, 并且在某些地方试图走在v2ray前面"
|
||||
|
||||
configFileName string
|
||||
|
||||
uniqueTestDomain string //有时需要测试到单一网站的流量,此时为了避免其它干扰,需要在这里声明 一下 该域名,然后程序里会进行过滤
|
||||
logLevel int //值越小越唠叨, 废话越多,值越大打印的越少,见log_开头的常量
|
||||
|
||||
//另外,本作暂时不考虑引入外界log包。依赖越少越好。
|
||||
|
||||
conf *Config
|
||||
//directClient proxy.Client
|
||||
directClient proxy.Client
|
||||
|
||||
tls_lazy_encrypt bool
|
||||
tls_lazy_secure bool
|
||||
|
||||
routePolicy *netLayer.RoutePolicy
|
||||
)
|
||||
|
||||
func init() {
|
||||
//directClient, _ = proxy.ClientFromURL("direct://")
|
||||
directClient, _ = proxy.ClientFromURL("direct://")
|
||||
|
||||
flag.IntVar(&logLevel, "ll", log_warning, "log level,0=debug, 1=info, 2=warning, 3=error")
|
||||
|
||||
flag.BoolVar(&tls_lazy_encrypt, "lazy", false, "tls lazy encrypt (splice)")
|
||||
flag.BoolVar(&tls_lazy_secure, "ls", false, "tls lazy secure, use special techs to ensure the tls lazy encrypt data can't be detected. Only valid at client end.")
|
||||
@@ -59,28 +71,6 @@ func printDesc() {
|
||||
fmt.Printf("=============== 所有协议均可套tls ================\n")
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Server_ThatListenPort_Url string `json:"local"`
|
||||
//RouteMethod string `json:"route"`
|
||||
Client_ThatDialRemote_Url string `json:"remote"`
|
||||
}
|
||||
|
||||
func loadConfig(fileName string) (*Config, error) {
|
||||
path := common.GetFilePath(fileName)
|
||||
if len(path) > 0 {
|
||||
if cf, err := os.Open(path); err == nil {
|
||||
defer cf.Close()
|
||||
bytes, _ := ioutil.ReadAll(cf)
|
||||
config := &Config{}
|
||||
if err = json.Unmarshal(bytes, config); err != nil {
|
||||
return nil, fmt.Errorf("can not parse config file %v, %v", fileName, err)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("can not load config file %v", fileName)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
printDesc()
|
||||
@@ -102,6 +92,18 @@ func main() {
|
||||
}
|
||||
defer localServer.Stop()
|
||||
|
||||
if !localServer.CantRoute() && conf.Route != nil {
|
||||
|
||||
netLayer.LoadMaxmindGeoipFile("")
|
||||
|
||||
//目前只支持通过 mycountry进行 geoip分流 这一种情况
|
||||
routePolicy = netLayer.NewRoutePolicy()
|
||||
if conf.Route.MyCountryISO_3166 != "" {
|
||||
routePolicy.AddRouteSet(netLayer.NewRouteSetForMyCountry(conf.Route.MyCountryISO_3166))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
remoteClient, err := proxy.ClientFromURL(conf.Client_ThatDialRemote_Url)
|
||||
if err != nil {
|
||||
log.Println("can not create remote client: ", err)
|
||||
@@ -217,9 +219,27 @@ func handleNewIncomeConnection(localServer proxy.Server, remoteClient proxy.Clie
|
||||
|
||||
afterLocalServerHandshake:
|
||||
|
||||
var client proxy.Client
|
||||
var client proxy.Client = remoteClient
|
||||
|
||||
client = remoteClient //如果加了白名单等过滤方式,则client可能会等于direct等,再说
|
||||
//如果可以route
|
||||
if !localServer.CantRoute() && routePolicy != nil {
|
||||
|
||||
if logLevel <= log_info {
|
||||
log.Println("enabling routing feature")
|
||||
}
|
||||
|
||||
//目前只支持一个 localServer/remoteClient, 所以目前根据tag分流是没有意义的,以后再说
|
||||
// 现在就用addr分流就行
|
||||
outtag := routePolicy.GetOutTag(&netLayer.TargetDescription{
|
||||
Addr: targetAddr,
|
||||
})
|
||||
if outtag == "direct" {
|
||||
client = directClient
|
||||
if logLevel <= log_info {
|
||||
log.Println("routed to direct", targetAddr.UrlString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 我们在客户端 lazy_encrypt 探测时,读取socks5 传来的信息,因为这个和要发送到tls的信息是一模一样的,所以就不需要等包上vless、tls后再判断了, 直接解包 socks5进行判断
|
||||
//
|
||||
|
75
netLayer/geoip.go
Normal file
75
netLayer/geoip.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
)
|
||||
|
||||
var (
|
||||
the_geoipdb *maxminddb.Reader
|
||||
embedGeoip bool
|
||||
|
||||
GeoipFileName string //若运行程序指定了 geoip 参数,则该值为给定值;否则默认会被init为 GeoLite2-Country.mmdb
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&GeoipFileName, "geoip", "GeoLite2-Country.mmdb", "geoip maxmind file name")
|
||||
|
||||
}
|
||||
|
||||
func HasEmbedGeoip() bool {
|
||||
return embedGeoip
|
||||
}
|
||||
|
||||
func loadMaxmindGeoipBytes(bs []byte) {
|
||||
db, err := maxminddb.FromBytes(bs)
|
||||
if err != nil {
|
||||
log.Fatalln("loadMaxmindGeoipBytes", err)
|
||||
}
|
||||
the_geoipdb = db
|
||||
}
|
||||
|
||||
//将一个外部的文件加载为我们默认的 geoip文件;若fn=="",则会自动使用 GeoipFileName 的值
|
||||
func LoadMaxmindGeoipFile(fn string) {
|
||||
if fn == "" {
|
||||
fn = GeoipFileName
|
||||
}
|
||||
if fn == "" { //因为 GeoipFileName 是共有变量,所以可能会被设成"", 不排除脑残
|
||||
return
|
||||
}
|
||||
bs, e := os.ReadFile(fn)
|
||||
if e != nil {
|
||||
log.Fatalln("loadMaxmindGeoipBytes", e)
|
||||
}
|
||||
loadMaxmindGeoipBytes(bs)
|
||||
|
||||
}
|
||||
|
||||
//使用默认的 geoip文件,会调用 GetIP_ISO_byReader
|
||||
func GetIP_ISO(ip net.IP) string {
|
||||
if the_geoipdb == nil {
|
||||
return ""
|
||||
}
|
||||
return GetIP_ISO_byReader(the_geoipdb, ip)
|
||||
}
|
||||
|
||||
//返回 iso 3166 字符串, 见 https://dev.maxmind.com/geoip/legacy/codes?lang=en ,大写,两字节
|
||||
func GetIP_ISO_byReader(db *maxminddb.Reader, ip net.IP) string {
|
||||
|
||||
var record struct {
|
||||
Country struct {
|
||||
ISOCode string `maxminddb:"iso_code"`
|
||||
} `maxminddb:"country"`
|
||||
}
|
||||
|
||||
err := db.Lookup(ip, &record)
|
||||
if err != nil {
|
||||
log.Fatal(err) //不应该发生
|
||||
}
|
||||
return record.Country.ISOCode
|
||||
}
|
71
netLayer/geoip_embed.go
Normal file
71
netLayer/geoip_embed.go
Normal file
@@ -0,0 +1,71 @@
|
||||
//go:build embed_geoip
|
||||
// +build embed_geoip
|
||||
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
)
|
||||
|
||||
//使用 go build -tags embed_geoip 来将文件 编译进 可执行文件
|
||||
|
||||
//文件来自 https://dev.maxmind.com/geoip/geolite2-free-geolocation-data?lang=en
|
||||
// 需要自行下载,或者到其他提供该文件的github项目下载,然后放入我们的 netLayer 文件夹中
|
||||
|
||||
//go:embed GeoLite2-Country.mmdb.tgz
|
||||
var geoLite2Country_embed_tgz []byte
|
||||
|
||||
//注意,如果使用了embed版,然后又提供命令参数加载外部文件,就会内嵌版被覆盖
|
||||
|
||||
//将 tgz文件解压成maxmind的mmdb文件
|
||||
func init() {
|
||||
embedGeoip = true
|
||||
|
||||
outBuf := &bytes.Buffer{}
|
||||
|
||||
gzf, err := gzip.NewReader(bytes.NewBuffer(geoLite2Country_embed_tgz))
|
||||
if err != nil {
|
||||
fmt.Println("load geoLite2Country_embed_tgz err,", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
tarReader := tar.NewReader(gzf)
|
||||
|
||||
header, err := tarReader.Next()
|
||||
|
||||
if err != nil {
|
||||
|
||||
if err == io.EOF {
|
||||
fmt.Println("load geoLite2Country_embed_tgz err,", io.EOF)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("load geoLite2Country_embed_tgz err,", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch header.Typeflag {
|
||||
|
||||
case tar.TypeReg:
|
||||
io.Copy(outBuf, tarReader)
|
||||
|
||||
default:
|
||||
log.Fatal("load geoLite2Country_embed_tgz, not a simple file??", header.Name)
|
||||
}
|
||||
|
||||
//这个函数应该直接接管了 我们的bytes,所以不能用 common.PutBuf 放回
|
||||
db, err := maxminddb.FromBytes(outBuf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatal("maxminddb.FromBytes", err)
|
||||
}
|
||||
the_geoipdb = db
|
||||
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/yl2chen/cidranger"
|
||||
)
|
||||
|
||||
type TargetDescription struct {
|
||||
}
|
||||
|
||||
//任意一个参数匹配后,都将发往相同的方向,具体房向并不是 SameGroup 所关心的
|
||||
// SameGroup只负责把一些属性相同的 “网络层特征” 放到一起
|
||||
type SameGroup struct {
|
||||
NetRanger cidranger.Ranger
|
||||
IPMap map[string]net.IP
|
||||
DomainMap map[string]string
|
||||
TagMap map[string]bool
|
||||
}
|
138
netLayer/route.go
Normal file
138
netLayer/route.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package netLayer
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/yl2chen/cidranger"
|
||||
)
|
||||
|
||||
// TargetDescription 可以完整地描述一个网络层/传输层上的一个特定目标,
|
||||
// 一般来说,一个具体的监听配置就会分配一个tag
|
||||
type TargetDescription struct {
|
||||
Addr *Addr
|
||||
Tag string
|
||||
}
|
||||
|
||||
// Set 是 “集合” 的意思, 是一组相同类型的数据放到一起。
|
||||
// 这里的相同点,就是它们同属于 将发往一个方向, 即同属一个路由策略
|
||||
// 任意一个参数匹配后,都将发往相同的方向,由该方向OutTag 指定
|
||||
// RouteSet 只负责把一些属性相同的 “网络层/传输层 特征” 放到一起
|
||||
type RouteSet struct {
|
||||
//网络层
|
||||
NetRanger cidranger.Ranger //一个范围
|
||||
IPs map[string]net.IP //一个确定值
|
||||
Domains, InTags, Countries map[string]bool // Countries 使用 ISO 3166 字符串 作为key
|
||||
|
||||
//传输层
|
||||
NotAllowTCP bool
|
||||
NotAllowUDP bool
|
||||
|
||||
OutTag string
|
||||
}
|
||||
|
||||
func NewRouteSetForMyCountry(iso string) *RouteSet {
|
||||
if len(iso) != 2 {
|
||||
return nil
|
||||
}
|
||||
rs := &RouteSet{
|
||||
Countries: make(map[string]bool),
|
||||
OutTag: "direct",
|
||||
}
|
||||
rs.Countries[iso] = true
|
||||
return rs
|
||||
}
|
||||
|
||||
func NewFullRouteSet() *RouteSet {
|
||||
return &RouteSet{
|
||||
NetRanger: cidranger.NewPCTrieRanger(),
|
||||
IPs: make(map[string]net.IP),
|
||||
Domains: make(map[string]bool),
|
||||
InTags: make(map[string]bool),
|
||||
Countries: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (sg *RouteSet) IsIn(td *TargetDescription) bool {
|
||||
if td.Tag != "" {
|
||||
if _, found := sg.InTags[td.Tag]; found {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return sg.IsAddrIn(td.Addr)
|
||||
|
||||
}
|
||||
|
||||
func (sg *RouteSet) IsAddrIn(a *Addr) bool {
|
||||
//我们先过滤传输层,再过滤网络层,因为传输层没那么多判断,只有tcp/udp这个判断而已
|
||||
if a.IsUDP && sg.NotAllowTCP || !a.IsUDP && sg.NotAllowUDP {
|
||||
return false
|
||||
|
||||
} else if sg.NotAllowTCP != sg.NotAllowUDP && sg.NetRanger == nil && sg.IPs == nil && sg.Domains == nil && sg.InTags == nil && sg.Countries == nil {
|
||||
//如果仅限制了一个传输层协议,且本集合里没有任何其它内容,那就直接通过
|
||||
return true
|
||||
}
|
||||
//开始网络层判断
|
||||
if a.IP != nil {
|
||||
if sg.NetRanger != nil {
|
||||
if has, _ := sg.NetRanger.Contains(a.IP); has {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if sg.Countries != nil {
|
||||
|
||||
if isoStr := GetIP_ISO(a.IP); isoStr != "" {
|
||||
if _, found := sg.Countries[isoStr]; found {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if sg.IPs != nil {
|
||||
if _, found := sg.IPs[a.IP.To16().String()]; found {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if a.Name != "" && sg.Domains != nil {
|
||||
tokens := strings.Split(a.Name, ".")
|
||||
if len(tokens) > 1 {
|
||||
suffix := tokens[len(tokens)-1]
|
||||
for i := len(tokens) - 2; i >= 0; i-- {
|
||||
suffix = tokens[i] + "." + suffix
|
||||
if _, found := sg.Domains[suffix]; found {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//一个完整的 所有RouteSet的列表,进行路由时,直接遍历即可
|
||||
// 所谓的路由实际上就是分流。
|
||||
type RoutePolicy struct {
|
||||
List []*RouteSet
|
||||
}
|
||||
|
||||
func NewRoutePolicy() *RoutePolicy {
|
||||
return &RoutePolicy{
|
||||
List: make([]*RouteSet, 0, 2),
|
||||
}
|
||||
}
|
||||
|
||||
func (rp *RoutePolicy) AddRouteSet(rs *RouteSet) {
|
||||
rp.List = append(rp.List, rs)
|
||||
}
|
||||
|
||||
// 返回一个 proxy.Client 的 tag
|
||||
// 默认情况下,始终具有direct这个tag以及 proxy这个tag,无需用户额外在配置文件中指定
|
||||
// 默认如果不匹配任何值的话,就会流向 "proxy" tag,也就是客户设置的 remoteClient的值
|
||||
func (rp *RoutePolicy) GetOutTag(td *TargetDescription) string {
|
||||
for _, s := range rp.List {
|
||||
if s.IsIn(td) {
|
||||
return s.OutTag
|
||||
}
|
||||
}
|
||||
return "proxy"
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
// Package direct provies direct proxy support for proxy.Client
|
||||
package direct
|
||||
|
||||
import (
|
||||
|
138
proxy/proxy.go
138
proxy/proxy.go
@@ -1,49 +1,49 @@
|
||||
/*
|
||||
package proxy 定义了代理转发所需的必备组件
|
||||
Package proxy 定义了代理转发所需的必备组件
|
||||
|
||||
# 层级讨论
|
||||
目前认为,一个传输过程由四个部分组成,基础连接(udp/tcp),TLS(可选),中间层(ws、grpc、http等,可选),具体协议(socks5,vless,trojan等)
|
||||
Layer Definition 层级讨论
|
||||
|
||||
其中,ws和grpc被认为是 高级应用层,http(伪装)属于低级应用层。
|
||||
目前认为,一个传输过程由四个部分组成,基础连接(udp/tcp),TLS(可选),中间层(ws、grpc、http等,可选),具体协议(socks5,vless,trojan等).
|
||||
|
||||
TLS:Transport Layer Security 顾名思义TLS作用于传输层,第四层,但是我们tcp也是第四层,所以在本项目中,认为不需要“会话层”,单独加一个专用于tls的层比较稳妥
|
||||
其中,ws和grpc被认为是 高级应用层,http(伪装)属于低级应用层.
|
||||
|
||||
正常OSI是7层,我们在这里规定一个 第八层和第九层,第八层就是 vless协议所在位置,第九层就是我们实际传输的承载数据;
|
||||
TLS - Transport Layer Security 顾名思义TLS作用于传输层,第四层,但是我们tcp也是第四层,所以在本项目中,认为不需要“会话层”,单独加一个专用于tls的层比较稳妥.
|
||||
|
||||
正常OSI是7层,我们在这里规定一个 第八层和第九层,第八层就是 vless协议所在位置,第九层就是我们实际传输的承载数据.
|
||||
|
||||
|
||||
## 新的VSI 模型
|
||||
New Model - VSI 新的VSI 模型
|
||||
|
||||
那么我们提出一个 verysimple Interconnection Model, 简称vsi模型。1到4层与OSI相同(物理、链路、网络、传输)
|
||||
那么我们提出一个 verysimple Interconnection Model, 简称vsi模型。1到4层与OSI相同(物理、链路、网络、传输).
|
||||
|
||||
把第五层替换成“加密层”,把TLS放进去;把第六层改为低级应用层,http属于这一层
|
||||
把第五层替换成“加密层”,把TLS放进去;把第六层改为低级应用层,http属于这一层.
|
||||
|
||||
第七层 改为高级应用层,ws/grpc 属于这一层, 简称高级层;第八层定为 代理层,vless/trojan 在这层,
|
||||
第七层 改为高级应用层,ws/grpc 属于这一层, 简称高级层;第八层定为 代理层,vless/trojan 在这层.
|
||||
|
||||
第九层为 承载数据层,承载的为 另一大串 第四层的数据。
|
||||
第九层为 承载数据层,承载的为 另一大串 第四层的数据.
|
||||
|
||||
那么我们verysimple实际上就是 基于 “层” 的架构,或称 可分层结构.
|
||||
|
||||
那么我们verysimple实际上就是 基于 “层” 的架构,或称 可分层结构。
|
||||
|
||||
```
|
||||
9 | tcp data
|
||||
--------------------
|
||||
--------------------
|
||||
8 | vless/trojan/socks5
|
||||
--------------------
|
||||
--------------------
|
||||
7 | ws/grpc
|
||||
--------------------
|
||||
--------------------
|
||||
6 | http
|
||||
--------------------
|
||||
--------------------
|
||||
5 | tls
|
||||
--------------------
|
||||
--------------------
|
||||
4 | tcp
|
||||
--------------------
|
||||
```
|
||||
--------------------
|
||||
|
||||
基本上5-8层都是可控的
|
||||
|
||||
对应的理想配置文件应该如下
|
||||
基本上5-8层都是可控的.
|
||||
|
||||
```json
|
||||
{
|
||||
对应的理想配置文件应该如下.
|
||||
|
||||
{
|
||||
"layer5_settings": { //或者叫 tls_settings,
|
||||
"tls":{"insecure": true},
|
||||
"utls":{}
|
||||
@@ -58,35 +58,42 @@ TLS:Transport Layer Security 顾名思义TLS作用于传输层,第四层,
|
||||
"vless":{}
|
||||
"trojan":{}
|
||||
},
|
||||
}
|
||||
```
|
||||
}
|
||||
|
||||
我们项目的文件夹,proxy文件夹代表第8层,tlsLayer文件夹代表第5层; 未来还计划在根目录下添加http文件夹(第六层),
|
||||
ws和grpc文件夹(第七层)
|
||||
ws和grpc文件夹(第七层).
|
||||
|
||||
|
||||
同级的ws和grpc是独占的,可以都放到一个layer里,然后比如第八层配置了一个vless一个trojan,那么排列组合就是4种,vless+ws, vless+ grpc, trojan+ws, trojan+grpc
|
||||
同级的ws和grpc是独占的,可以都放到一个layer里,然后比如第八层配置了一个vless一个trojan,那么排列组合就是4种,vless+ws, vless+ grpc, trojan+ws, trojan+grpc.
|
||||
|
||||
|
||||
这就大大减轻了各种”一键脚本“的 使用需求,咱们只要选择自己喜欢的各个层,程序自动就为我们生成所有配置;
|
||||
这就大大减轻了各种”一键脚本“的 使用需求,咱们只要选择自己喜欢的各个层,程序自动就为我们生成所有配置.
|
||||
|
||||
运行时,如果所有配置都要有,那么就需要多种端口;共用端口的话可以用nginx
|
||||
运行时,如果所有配置都要有,那么就需要多种端口;共用端口的话可以用nginx.
|
||||
|
||||
也可以程序指定一种 特定的情况,比如开始运行程序时,冒出交互界面,自己按项选择好后,就自动运行,然后自动生成客户端分享url。
|
||||
也可以程序指定一种 特定的情况,比如开始运行程序时,冒出交互界面,自己按项选择好后,就自动运行,然后自动生成客户端分享url.
|
||||
|
||||
可以在脑海里想象 “穿鞋带” 的画面,有很多洞可以经过,都穿好了,鞋带就系好了。或者手机手势解锁的情况。
|
||||
可以在脑海里想象 “穿鞋带” 的画面,有很多洞可以经过,都穿好了,鞋带就系好了。或者手机手势解锁的情况.
|
||||
|
||||
这种好处是,每次运行都可以采用不同的配置,不同的uuid,手机一扫码就能连上
|
||||
这种好处是,每次运行都可以采用不同的配置,不同的uuid,手机一扫码就能连上.
|
||||
|
||||
# proxy 文件夹内容
|
||||
Contents of proxy package - proxy包内容
|
||||
|
||||
接口 ProxyCommon 和 结构 ProxyCommonStruct 给 这个架构定义了标准
|
||||
接口 ProxyCommon 和 结构 ProxyCommonStruct 给 这个架构定义了标准.
|
||||
|
||||
而 Client 和 Server 接口 是 具体利用该架构的 客户端 和 服务端,都位于VSI中的第八层
|
||||
而 Client 和 Server 接口 是 具体利用该架构的 客户端 和 服务端,都位于VSI中的第八层.
|
||||
|
||||
使用 RegisterClient 和 RegisterServer 来注册新的实现
|
||||
使用 RegisterClient 和 RegisterServer 来注册新的实现.
|
||||
|
||||
还定义了关于udp 转发到机制,该部分直接参考 各个UDP开头的 部分即可
|
||||
还定义了关于udp 转发到机制,该部分直接参考 各个UDP开头的 部分即可.
|
||||
|
||||
Server and Client
|
||||
|
||||
我们服务端和 客户端的程序,都是有至少一个入口和一个出口的。入口我们叫做localServer,出口我们叫做remoteClient.
|
||||
|
||||
在localServer中,我们负责监听未知连接;在remoteClient中,我们负责拨号特定目标服务器.
|
||||
|
||||
这里的local和remote指的是 数据传输的目的地。
|
||||
*/
|
||||
package proxy
|
||||
|
||||
@@ -102,10 +109,12 @@ import (
|
||||
"github.com/hahahrfool/v2ray_simple/tlsLayer"
|
||||
)
|
||||
|
||||
// 给一个节点 提供 VSI中 第 5-7层 的支持
|
||||
// 给一个节点 提供 VSI中 第 5-7层 的支持, server和 client通用. 个别方法只能用于某一端
|
||||
type ProxyCommon interface {
|
||||
AddrStr() string //地址,在server就是监听地址,在client就是拨号地址
|
||||
SetAddrStr(string)
|
||||
CantRoute() bool //for localServer,
|
||||
GetTag() string
|
||||
|
||||
SetUseTLS()
|
||||
IsUseTLS() bool
|
||||
@@ -117,8 +126,12 @@ type ProxyCommon interface {
|
||||
|
||||
GetTLS_Server() *tlsLayer.Server
|
||||
GetTLS_Client() *tlsLayer.Client
|
||||
|
||||
setCantRoute(bool)
|
||||
setTag(string)
|
||||
}
|
||||
|
||||
//给 ProxyCommon 的tls做一些配置上的准备,从url读取配置
|
||||
func PrepareTLS_forProxyCommon(u *url.URL, isclient bool, com ProxyCommon) error {
|
||||
insecureStr := u.Query().Get("insecure")
|
||||
insecure := false
|
||||
@@ -149,9 +162,28 @@ func PrepareTLS_forProxyCommon(u *url.URL, isclient bool, com ProxyCommon) error
|
||||
type ProxyCommonStruct struct {
|
||||
Addr string
|
||||
TLS bool
|
||||
Tag string //可用于路由, 见 netLayer.route.go
|
||||
|
||||
tls_s *tlsLayer.Server
|
||||
tls_c *tlsLayer.Client
|
||||
|
||||
cantRoute bool //for localServer, 若为true,则 localServer 读得的数据 不会经过分流,一定会通过用户指定的remoteclient发出
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) CantRoute() bool {
|
||||
return pcs.cantRoute
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) GetTag() string {
|
||||
return pcs.Tag
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) setTag(tag string) {
|
||||
pcs.Tag = tag
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) setCantRoute(cr bool) {
|
||||
pcs.cantRoute = cr
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) HasAdvancedApplicationLayer() bool {
|
||||
@@ -228,7 +260,7 @@ func ClientFromURL(s string) (Client, error) {
|
||||
return creatorFunc(u)
|
||||
} else {
|
||||
|
||||
//尝试判断是否套tls
|
||||
//尝试判断是否套tls, 比如vlesss实际上是vless+tls,https实际上是http+tls
|
||||
|
||||
realScheme := strings.TrimSuffix(schemeName, "s")
|
||||
creatorFunc, ok = clientMap[realScheme]
|
||||
@@ -291,6 +323,8 @@ func PrintAllClientNames() {
|
||||
|
||||
// ServerFromURL calls the registered creator to create proxy servers
|
||||
// dialer is the default upstream dialer so cannot be nil, we can use Default when calling this function
|
||||
// 所有的server都可有 "norule"参数,标明无需路由或者此server不可使用路由,在监听多个ip时是有用的;
|
||||
// 路由配置可以在json的其他配置里面设置,如 mycountry项
|
||||
func ServerFromURL(s string) (Server, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
@@ -300,22 +334,40 @@ func ServerFromURL(s string) (Server, error) {
|
||||
schemeName := strings.ToLower(u.Scheme)
|
||||
creatorFunc, ok := serverMap[schemeName]
|
||||
if ok {
|
||||
return creatorFunc(u)
|
||||
ser, err := creatorFunc(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configCommonForServer(ser, u)
|
||||
|
||||
return ser, nil
|
||||
} else {
|
||||
realScheme := strings.TrimSuffix(schemeName, "s")
|
||||
creatorFunc, ok = serverMap[realScheme]
|
||||
if ok {
|
||||
server, err := creatorFunc(u)
|
||||
if err != nil {
|
||||
return server, err
|
||||
return nil, err
|
||||
}
|
||||
configCommonForServer(server, u)
|
||||
|
||||
server.SetUseTLS()
|
||||
PrepareTLS_forProxyCommon(u, false, server)
|
||||
return server, err
|
||||
return server, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil, common.NewDataErr("unknown server scheme '", nil, u.Scheme)
|
||||
}
|
||||
|
||||
func configCommonForServer(ser ProxyCommon, u *url.URL) {
|
||||
nr := false
|
||||
q := u.Query()
|
||||
if q.Get("norule") != "" {
|
||||
nr = true
|
||||
}
|
||||
ser.setCantRoute(nr)
|
||||
ser.setTag(u.Fragment)
|
||||
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// Package socks5 provies socks5 proxy support for proxy.Client and proxy.Server
|
||||
package socks5
|
||||
|
||||
//总体而言,vless和vmess协议借鉴了socks5,所以有类似的地方。trojan协议也是一样。
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// Package vless provies vless proxy support for proxy.Client and proxy.Server
|
||||
package vless
|
||||
|
||||
import (
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"local": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@0.0.0.0:4433?cert=cert.pem&key=cert.key&version=0&fallback=:80",
|
||||
"local": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@0.0.0.0:4433?cert=cert.pem&key=cert.key&version=0&fallback=:80#myvlesss1",
|
||||
"remote": "direct://"
|
||||
}
|
||||
|
@@ -14,9 +14,7 @@ type faketlsconn struct {
|
||||
}
|
||||
|
||||
// 本包会用到这个Conn,比如server和client的 Handshake,
|
||||
|
||||
//唯一特性就是它可以返回tls连接的底层tcp连接,见 GetRaw
|
||||
// 开启了回落功能的话,这里还会用到 http.sniff
|
||||
// 唯一特性就是它可以返回tls连接的底层tcp连接,见 GetRaw
|
||||
type Conn struct {
|
||||
*tls.Conn
|
||||
}
|
||||
@@ -47,3 +45,10 @@ func (c *Conn) GetTeeConn() *TeeConn {
|
||||
return rc.conn.(*TeeConn)
|
||||
|
||||
}
|
||||
|
||||
//return c.Conn.ConnectionState().NegotiatedProtocol
|
||||
func (c *Conn) GetAlpn() string {
|
||||
|
||||
return c.Conn.ConnectionState().NegotiatedProtocol
|
||||
|
||||
}
|
||||
|
@@ -1,3 +1,6 @@
|
||||
/*
|
||||
Package tlsLayer 提供tls流量的基本探测功能
|
||||
*/
|
||||
package tlsLayer
|
||||
|
||||
var etStrMap map[int]string
|
||||
|
@@ -34,7 +34,6 @@ func (s *Server) Handshake(underlay net.Conn) (tlsConn *Conn, err error) {
|
||||
rawTlsConn := tls.Server(underlay, s.tlsConfig)
|
||||
err = rawTlsConn.Handshake()
|
||||
if err != nil {
|
||||
//return tlsConn,
|
||||
err = common.NewErr("tlsLayer: tls握手失败", err)
|
||||
|
||||
return
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package tlsLayer_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -10,26 +10,23 @@ import (
|
||||
"github.com/hahahrfool/v2ray_simple/common"
|
||||
"github.com/hahahrfool/v2ray_simple/netLayer"
|
||||
"github.com/hahahrfool/v2ray_simple/proxy"
|
||||
_ "github.com/hahahrfool/v2ray_simple/proxy/vless"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
}
|
||||
|
||||
func TestVlesss(t *testing.T) {
|
||||
testTls("vlesss", "9507", t)
|
||||
}
|
||||
|
||||
func testTls(protocol string, port string, t *testing.T) {
|
||||
if !common.FileExist("cert.pem") {
|
||||
ioutil.WriteFile("cert.pem", []byte(sampleCertStr), 0777)
|
||||
if !common.FileExist("../cert.pem") {
|
||||
ioutil.WriteFile("../cert.pem", []byte(sampleCertStr), 0777)
|
||||
}
|
||||
|
||||
if !common.FileExist("cert.key") {
|
||||
ioutil.WriteFile("cert.key", []byte(sampleKeyStr), 0777)
|
||||
if !common.FileExist("../cert.key") {
|
||||
ioutil.WriteFile("../cert.key", []byte(sampleKeyStr), 0777)
|
||||
}
|
||||
|
||||
url := protocol + "://a684455c-b14f-11ea-bf0d-42010aaa0003@localhost:" + port + "?alterID=4&cert=cert.pem&key=cert.key&insecure=1"
|
||||
url := protocol + "://a684455c-b14f-11ea-bf0d-42010aaa0003@localhost:" + port + "?alterID=4&cert=../cert.pem&key=../cert.key&insecure=1"
|
||||
server, err := proxy.ServerFromURL(url)
|
||||
if err != nil {
|
||||
t.Log("fail1", err)
|
@@ -3,11 +3,16 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/netLayer"
|
||||
)
|
||||
|
||||
var Version string //版本号由Makefile指定
|
||||
|
||||
func printVersion() {
|
||||
fmt.Printf("===============================\nverysimple %v (%v), %v %v %v\n", Version, desc, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
if netLayer.HasEmbedGeoip() {
|
||||
fmt.Println("Contains embeded Geoip file")
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user