mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-26 10:10:38 +08:00
添加utls支持,直接用chrome指纹;修订代码
This commit is contained in:
57
README.md
57
README.md
@@ -130,22 +130,57 @@ cp server.example.json server.json
|
||||
|
||||
```sh
|
||||
#客户端
|
||||
v2ray_simple -c client.json
|
||||
verysimple -c client.json
|
||||
|
||||
#服务端
|
||||
v2ray_simple -c server.json
|
||||
verysimple -c server.json
|
||||
```
|
||||
|
||||
如果你不是放在path里的,则要 `./v2ray_simple`, 前面要加一个点和一个斜杠。windows没这个要求。
|
||||
如果你不是放在path里的,则要 `./verysimple`, 前面要加一个点和一个斜杠。windows没这个要求。
|
||||
|
||||
注意,如果你是自己直接 go build 编译的,则可执行文件与项目名称一致,为 v2ray_simple;
|
||||
|
||||
如果用的下载的官方编译版本,则可执行文件叫做 verysimple. 可以通过文件名称判断是自己编译的还是下载的。
|
||||
|
||||
官方发布版统一叫做verysimple是为了与 v2ray区别开。
|
||||
|
||||
关于 vlesss 的配置,查看 server.example.json和 client.example.json就知道了,很简单的。
|
||||
|
||||
目前配置文件最短情况一共就4行,其中两行还是花括号,这要是还要我解释我就踢你的屁股。
|
||||
|
||||
如果学会了配置后,如果你使用v1.0.5以及更新版本,还可以用如下命令来运行,无需配置文件
|
||||
|
||||
```sh
|
||||
#客户端
|
||||
verysimple -L=socks5://127.0.0.1:10800 -D=vlesss://你的uuid@你的服务器ip:443?insecure=true
|
||||
|
||||
#服务端
|
||||
verysimple -L=vlesss://你的uuid@你的服务器ip:443?cert=cert.pem&key=cert.key&version=0&fallback=:80
|
||||
```
|
||||
|
||||
## 验证方式
|
||||
|
||||
对于功能的golang test,请使用 `go test ./...` 命令。如果要详细的打印出test的过程,可以添加 -v 参数
|
||||
|
||||
## 关于证书
|
||||
|
||||
不要在实际场合使用我提供的证书!自己生成!而且最好是用 自己真实拥有的域名,使用acme.sh等脚本申请免费证书,特别是建站等情况。
|
||||
|
||||
而且用了真证书后,别忘了把配置文件中的 `insecure=true` 给删掉.
|
||||
|
||||
使用自签名证书是会被中间人攻击的,再次特地提醒。如果被中间人攻击,就能直接获取你的uuid,然后你的服务器 攻击者就也能用了。
|
||||
|
||||
要想申请真实证书,仅有ip是不够的,要拥有一个域名。本项目提供的自签名证书仅供快速测试使用,切勿用于实际场合。
|
||||
|
||||
### 生成自签名证书
|
||||
|
||||
注意运行第二行命令时会要求你输入一些信息。确保至少有一行不是空白即可,比如打个1
|
||||
```sh
|
||||
openssl ecparam -genkey -name prime256v1 -out cert.key
|
||||
openssl req -new -x509 -days 7305 -key cert.key -out cert.pem
|
||||
```
|
||||
|
||||
此命令会生成ecc证书,这个证书比rsa证书 速度更快, 有利于网速加速(加速tls握手)。
|
||||
|
||||
## 开发标准以及理念
|
||||
|
||||
@@ -206,24 +241,12 @@ verysimple 继承 v2simple的一个优点,就是服务端的配置也可以用
|
||||
|
||||
不过,显然url无法配置大量复杂的内容,而且有些玩家也喜欢一份配置可以搞定多种内核,所以未来 verysimple 会推出兼容 v2ray的json配置 的模块。**只是兼容配置格式,不是兼容所有协议!目前只有vless v0 协议是兼容的,可以直接使用现有其它客户端**
|
||||
|
||||
目前的json格式被称作“极简模式”,只需给定url就可以完整确认一个协议的配置.
|
||||
|
||||
其它开发计划请参考
|
||||
https://github.com/hahahrfool/v2ray_simple/discussions/3
|
||||
|
||||
## 生成自签名证书
|
||||
|
||||
注意运行第二行命令时会要求你输入一些信息。确保至少有一行不是空白即可,比如打个1
|
||||
```sh
|
||||
openssl ecparam -genkey -name prime256v1 -out cert.key
|
||||
openssl req -new -x509 -days 7305 -key cert.key -out cert.pem
|
||||
```
|
||||
|
||||
我给出的命令会生成ecc证书,这个证书速度更快, 有利于网速加速(加速tls握手)。
|
||||
|
||||
不要在实际场合使用我提供的证书!自己生成!而且最好是用 自己真实拥有的域名,使用acme.sh等脚本申请免费证书,特别是建站等情况。
|
||||
|
||||
使用自签名证书是会被中间人攻击的,再次特地提醒。如果被中间人攻击,就能直接获取你的uuid,然后你的服务器 攻击者就也能用了。
|
||||
|
||||
仅有ip是不够的,要结合域名。本项目提供的自签名证书仅供快速测试使用,切勿用于实际场合。
|
||||
## 测速
|
||||
|
||||
测试环境:ubuntu虚拟机, 使用开源测试工具
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"listen": "socks5://127.0.0.1:10800#myvlesss1",
|
||||
"dial": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&version=0",
|
||||
"dial": "vlesss://a684455c-b14f-11ea-bf0d-42010aaa0003@127.0.0.1:4433?insecure=true&version=0&utls=true",
|
||||
"route":{ "mycountry":"CN" ,"comment":"route这一项是可选的,如果没给出的话,就不分流;写了mycountry后, 向该国家的ip发送的请求就会直连, 然后其他的过代理;本comment项你可以自行删掉, 注意要删的话, 连着前面的逗号一起删"}
|
||||
}
|
||||
|
||||
3
go.mod
3
go.mod
@@ -7,7 +7,10 @@ require (
|
||||
github.com/yl2chen/cidranger v1.0.2
|
||||
)
|
||||
|
||||
require golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
|
||||
require (
|
||||
github.com/refraction-networking/utls v1.0.0
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -4,6 +4,8 @@ github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph
|
||||
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/refraction-networking/utls v1.0.0 h1:6XQHSjDmeBCF9sPq8p2zMVGq7Ud3rTD2q88Fw8Tz1tA=
|
||||
github.com/refraction-networking/utls v1.0.0/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -11,6 +13,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
|
||||
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-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
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=
|
||||
|
||||
16
main.go
16
main.go
@@ -161,7 +161,7 @@ func handleNewIncomeConnection(inServer proxy.Server, outClient proxy.Client, th
|
||||
//此时,baseLocalConn里面 正常情况下, 服务端看到的是 客户端的golang的tls 拨号发出的 tls数据
|
||||
// 客户端看到的是 socks5的数据, 我们首先就是要看看socks5里的数据是不是tls,而socks5自然 IsUseTLS 是false
|
||||
|
||||
// 如果是服务端的话,那就是 localServer.IsUseTLS == true, 此时,我们正常握手,然后我们需要判断的是它承载的数据
|
||||
// 如果是服务端的话,那就是 inServer.IsUseTLS == true, 此时,我们正常握手,然后我们需要判断的是它承载的数据
|
||||
|
||||
// 每次tls试图从 原始连接 读取内容时,都会附带把原始数据写入到 这个 Recorder中
|
||||
var serverEndLocalServerTlsRawReadRecorder *tlsLayer.Recorder
|
||||
@@ -181,7 +181,7 @@ func handleNewIncomeConnection(inServer proxy.Server, outClient proxy.Client, th
|
||||
if err != nil {
|
||||
|
||||
if utils.CanLogErr() {
|
||||
log.Println("failed in handshake localServer tls", inServer.AddrStr(), err)
|
||||
log.Println("failed in handshake inServer tls", inServer.AddrStr(), err)
|
||||
|
||||
}
|
||||
thisLocalConnectionInstance.Close()
|
||||
@@ -237,7 +237,7 @@ afterLocalServerHandshake:
|
||||
log.Println("trying routing feature")
|
||||
}
|
||||
|
||||
//目前只支持一个 localServer/remoteClient, 所以目前根据tag分流是没有意义的,以后再说
|
||||
//目前只支持一个 inServer/outClient, 所以目前根据tag分流是没有意义的,以后再说
|
||||
// 现在就用addr分流就行
|
||||
outtag := routePolicy.GetOutTag(&netLayer.TargetDescription{
|
||||
Addr: targetAddr,
|
||||
@@ -254,7 +254,7 @@ afterLocalServerHandshake:
|
||||
// 我们在客户端 lazy_encrypt 探测时,读取socks5 传来的信息,因为这个和要发送到tls的信息是一模一样的,所以就不需要等包上vless、tls后再判断了, 直接解包 socks5进行判断
|
||||
//
|
||||
// 而在服务端探测时,因为 客户端传来的连接 包了 tls,所以要在tls解包后, vless 解包后,再进行判断;
|
||||
// 所以总之都是要在 localServer判断 wlc; 总之,含义就是,去检索“用户承载数据”的来源
|
||||
// 所以总之都是要在 inServer 判断 wlc; 总之,含义就是,去检索“用户承载数据”的来源
|
||||
|
||||
if tls_lazy_encrypt {
|
||||
|
||||
@@ -276,7 +276,7 @@ afterLocalServerHandshake:
|
||||
// 而且也不在这里处理监听事件,client自己会在额外的 goroutine里处理
|
||||
// server也一样,会在特定的场合给 CRUMFURS 传值,这个机制是与main函数无关的
|
||||
|
||||
// 而且 thisLocalConnectionInstance 会被 localServer 保存起来,用于后面的 unknownRemoteAddrMsgWriter
|
||||
// 而且 thisLocalConnectionInstance 会被 inServer 保存起来,用于后面的 unknownRemoteAddrMsgWriter
|
||||
|
||||
return
|
||||
|
||||
@@ -297,7 +297,7 @@ afterLocalServerHandshake:
|
||||
// 此时socks5包已经帮我们dial好了一个udp连接,即wlc,但是还未读取到客户端想要访问的东西
|
||||
udpConn := wlc.(*socks5.UDPConn)
|
||||
|
||||
// 将 remoteClient 视为 UDP_Putter ,就可以转发udp信息了
|
||||
// 将 outClient 视为 UDP_Putter ,就可以转发udp信息了
|
||||
// direct 也实现了 UDP_Putter (通过 UDP_Pipe和 RelayUDP_to_Direct函数), 所以目前 socks5直接转发udp到direct 的功能 已经实现。
|
||||
|
||||
// 我在 vless v1 的client 的 UserConn 中实现了 UDP_Putter, vless 的 client的 新连接的Handshake过程会在 UDP_Putter.WriteUDPRequest 被调用 时发生
|
||||
@@ -428,7 +428,7 @@ afterLocalServerHandshake:
|
||||
|
||||
tlsConn, err := client.GetTLS_Client().Handshake(clientConn)
|
||||
if err != nil {
|
||||
log.Println("failed in handshake remoteClient tls", targetAddr.String(), ", Reason: ", err)
|
||||
log.Println("failed in handshake outClient tls", targetAddr.String(), ", Reason: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -601,7 +601,7 @@ func tryRawCopy(useSecureMethod bool, proxy_client proxy.UserClient, proxy_serve
|
||||
tlsConn, err := proxy_client.GetTLS_Client().Handshake(teeConn)
|
||||
if err != nil {
|
||||
if utils.CanLogErr() {
|
||||
log.Println("failed in handshake remoteClient tls", ", Reason: ", err)
|
||||
log.Println("failed in handshake outClient tls", ", Reason: ", err)
|
||||
|
||||
}
|
||||
return
|
||||
|
||||
@@ -91,6 +91,8 @@ Server and Client
|
||||
|
||||
我们服务端和 客户端的程序,都是有至少一个入口和一个出口的。入口我们叫做 inServer ,出口我们叫做 outClient.
|
||||
|
||||
这两个词的含义和 v2ray的 inbound 和 outbound 是等价的.
|
||||
|
||||
在 inServer 中,我们负责监听未知连接;在 outClient 中,我们负责拨号特定目标服务器.
|
||||
|
||||
*/
|
||||
@@ -109,10 +111,11 @@ import (
|
||||
)
|
||||
|
||||
// 给一个节点 提供 VSI中 第 5-7层 的支持, server和 client通用. 个别方法只能用于某一端
|
||||
// 一个 ProxyCommon 会内嵌proxy以及上面各层的所有信息
|
||||
type ProxyCommon interface {
|
||||
AddrStr() string //地址,在server就是监听地址,在client就是拨号地址
|
||||
SetAddrStr(string)
|
||||
CantRoute() bool //for localServer,
|
||||
CantRoute() bool //for inServer, 分流属于netLayer的指责
|
||||
GetTag() string
|
||||
|
||||
SetUseTLS()
|
||||
@@ -120,6 +123,7 @@ type ProxyCommon interface {
|
||||
|
||||
HasAdvancedApplicationLayer() bool //如果使用了ws或者grpc,这个要返回true
|
||||
|
||||
//tls属于tlsLayer的指责
|
||||
SetTLS_Server(*tlsLayer.Server)
|
||||
SetTLS_Client(*tlsLayer.Client)
|
||||
|
||||
@@ -139,7 +143,9 @@ func PrepareTLS_forProxyCommon(u *url.URL, isclient bool, com ProxyCommon) error
|
||||
}
|
||||
|
||||
if isclient {
|
||||
com.SetTLS_Client(tlsLayer.NewTlsClient(u.Host, insecure))
|
||||
utlsStr := u.Query().Get("utls")
|
||||
useUtls := utlsStr != "" && utlsStr != "false" && utlsStr != "0"
|
||||
com.SetTLS_Client(tlsLayer.NewTlsClient(u.Host, insecure, useUtls))
|
||||
|
||||
} else {
|
||||
certFile := u.Query().Get("cert")
|
||||
@@ -166,7 +172,7 @@ type ProxyCommonStruct struct {
|
||||
tls_s *tlsLayer.Server
|
||||
tls_c *tlsLayer.Client
|
||||
|
||||
cantRoute bool //for localServer, 若为true,则 localServer 读得的数据 不会经过分流,一定会通过用户指定的remoteclient发出
|
||||
cantRoute bool //for inServer, 若为true,则 inServer 读得的数据 不会经过分流,一定会通过用户指定的remoteclient发出
|
||||
}
|
||||
|
||||
func (pcs *ProxyCommonStruct) CantRoute() bool {
|
||||
|
||||
@@ -208,7 +208,7 @@ func (c *Client) getBufWithCmd(cmd byte) *bytes.Buffer {
|
||||
return buf
|
||||
}
|
||||
|
||||
// 把在 CRUMFURS 信道中 获取到的 未知流量 转发到 UDPResponseWriter (本v2simple中就是 转发到localServer中, 而且 只有 socks5 这一种localServer实现了该方法, 见 main.go)
|
||||
// 把在 CRUMFURS 信道中 获取到的 未知流量 转发到 UDPResponseWriter (本作中就是 转发到 inServer 中, 而且 只有 socks5 这一种 inServer 实现了该方法, 见 main.go)
|
||||
func (c *Client) handle_CRUMFURS(UMFURS_conn net.Conn) {
|
||||
|
||||
if c.udpResponseChan == nil {
|
||||
|
||||
@@ -2,36 +2,68 @@ package tlsLayer
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"net"
|
||||
"unsafe"
|
||||
|
||||
"github.com/hahahrfool/v2ray_simple/utils"
|
||||
utls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
tlsConfig *tls.Config
|
||||
useTls bool
|
||||
}
|
||||
|
||||
func NewTlsClient(host string, insecure bool) *Client {
|
||||
func NewTlsClient(host string, insecure bool, useTls bool) *Client {
|
||||
|
||||
c := &Client{
|
||||
tlsConfig: &tls.Config{
|
||||
InsecureSkipVerify: insecure,
|
||||
ServerName: host,
|
||||
},
|
||||
useTls: useTls,
|
||||
}
|
||||
|
||||
if useTls && utils.CanLogInfo() {
|
||||
log.Println("using utls and Chrome fingerprint")
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) Handshake(underlay net.Conn) (tlsConn *Conn, err error) {
|
||||
rawTlsConn := tls.Client(underlay, c.tlsConfig)
|
||||
err = rawTlsConn.Handshake()
|
||||
|
||||
if c.useTls {
|
||||
utlsConn := utls.UClient(underlay, &utls.Config{
|
||||
InsecureSkipVerify: c.tlsConfig.InsecureSkipVerify,
|
||||
ServerName: c.tlsConfig.ServerName,
|
||||
}, utls.HelloChrome_Auto)
|
||||
|
||||
err = utlsConn.Handshake()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tlsConn = &Conn{
|
||||
Conn: utlsConn,
|
||||
ptr: unsafe.Pointer(utlsConn.Conn),
|
||||
tlsPackageType: utlsPackage,
|
||||
}
|
||||
|
||||
} else {
|
||||
officialConn := tls.Client(underlay, c.tlsConfig)
|
||||
err = officialConn.Handshake()
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tlsConn = &Conn{
|
||||
Conn: rawTlsConn,
|
||||
Conn: officialConn,
|
||||
ptr: unsafe.Pointer(officialConn),
|
||||
tlsPackageType: official,
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,23 +4,34 @@ import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"unsafe"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
const (
|
||||
official = iota
|
||||
utlsPackage
|
||||
)
|
||||
|
||||
//参考 crypt/tls 的 conn.go, 注意,如果上游代码的底层结构发生了改变,则这里也要跟着修改,保持头部结构一致
|
||||
type faketlsconn struct {
|
||||
// constant
|
||||
conn net.Conn
|
||||
isClient bool
|
||||
}
|
||||
|
||||
// 本包会用到这个Conn,比如server和client的 Handshake,
|
||||
// 唯一特性就是它可以返回tls连接的底层tcp连接,见 GetRaw
|
||||
|
||||
type Conn struct {
|
||||
*tls.Conn
|
||||
//*tls.Conn
|
||||
net.Conn
|
||||
ptr unsafe.Pointer
|
||||
tlsPackageType byte // 0 means crypto/tls, 1 means utls
|
||||
}
|
||||
|
||||
func (c *Conn) GetRaw(tls_lazy_encrypt bool) *net.TCPConn {
|
||||
rc := (*faketlsconn)(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn))))
|
||||
|
||||
rc := (*faketlsconn)(c.ptr)
|
||||
if rc != nil {
|
||||
if rc.conn != nil {
|
||||
//log.Println("成功获取到 *net.TCPConn!", rc.conn.(*net.TCPConn)) //经测试,是毫无问题的,完全能提取出来并正常使用
|
||||
@@ -40,7 +51,7 @@ func (c *Conn) GetRaw(tls_lazy_encrypt bool) *net.TCPConn {
|
||||
|
||||
// 直接获取TeeConn,仅用于已经确定肯定能获取到的情况
|
||||
func (c *Conn) GetTeeConn() *TeeConn {
|
||||
rc := (*faketlsconn)(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn))))
|
||||
rc := (*faketlsconn)(c.ptr)
|
||||
|
||||
return rc.conn.(*TeeConn)
|
||||
|
||||
@@ -49,6 +60,13 @@ func (c *Conn) GetTeeConn() *TeeConn {
|
||||
//return c.Conn.ConnectionState().NegotiatedProtocol
|
||||
func (c *Conn) GetAlpn() string {
|
||||
|
||||
return c.Conn.ConnectionState().NegotiatedProtocol
|
||||
if c.tlsPackageType == utlsPackage {
|
||||
cc := (*utls.Conn)(c.ptr)
|
||||
return cc.ConnectionState().NegotiatedProtocol
|
||||
|
||||
} else {
|
||||
cc := (*tls.Conn)(c.ptr)
|
||||
return cc.ConnectionState().NegotiatedProtocol
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user