添加utls支持,直接用chrome指纹;修订代码

This commit is contained in:
hahahrfool
2022-03-17 18:40:54 +08:00
parent af8aadb370
commit 42236d5cd8
9 changed files with 132 additions and 46 deletions

View File

@@ -4,7 +4,7 @@ verysimple 实际上 谐音来自 V2ray Simple (显然只适用于汉语母
verysimple项目大大简化了 转发机制,能提高运行速度。本项目 转发流量时关键代码直接放在main.go里非常直白易懂 verysimple项目大大简化了 转发机制,能提高运行速度。本项目 转发流量时关键代码直接放在main.go里非常直白易懂
只有项目名称是v2ray_simple其它所有场合全 使用 verysimple 这个名称,可简称 "vs"。 只有项目名称是v2ray_simple其它所有场合 全使用 verysimple 这个名称,可简称 "vs"。
规定,编译出的文件名必须以 verysimple 开头. 规定,编译出的文件名必须以 verysimple 开头.
@@ -130,22 +130,57 @@ cp server.example.json server.json
```sh ```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就知道了很简单的。 关于 vlesss 的配置,查看 server.example.json和 client.example.json就知道了很简单的。
目前配置文件最短情况一共就4行其中两行还是花括号这要是还要我解释我就踢你的屁股。 目前配置文件最短情况一共就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 参数 对于功能的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 协议是兼容的,可以直接使用现有其它客户端** 不过显然url无法配置大量复杂的内容而且有些玩家也喜欢一份配置可以搞定多种内核所以未来 verysimple 会推出兼容 v2ray的json配置 的模块。**只是兼容配置格式不是兼容所有协议目前只有vless v0 协议是兼容的,可以直接使用现有其它客户端**
目前的json格式被称作“极简模式”只需给定url就可以完整确认一个协议的配置.
其它开发计划请参考 其它开发计划请参考
https://github.com/hahahrfool/v2ray_simple/discussions/3 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虚拟机, 使用开源测试工具 测试环境ubuntu虚拟机, 使用开源测试工具

View File

@@ -1,5 +1,5 @@
{ {
"listen": "socks5://127.0.0.1:10800#myvlesss1", "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项你可以自行删掉, 注意要删的话, 连着前面的逗号一起删"} "route":{ "mycountry":"CN" ,"comment":"route这一项是可选的,如果没给出的话,就不分流;写了mycountry后, 向该国家的ip发送的请求就会直连, 然后其他的过代理;本comment项你可以自行删掉, 注意要删的话, 连着前面的逗号一起删"}
} }

3
go.mod
View File

@@ -7,7 +7,10 @@ require (
github.com/yl2chen/cidranger v1.0.2 github.com/yl2chen/cidranger v1.0.2
) )
require golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
require ( require (
github.com/refraction-networking/utls v1.0.0
github.com/stretchr/testify v1.7.0 // indirect github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
) )

4
go.sum
View File

@@ -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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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/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 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= 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-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 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

16
main.go
View File

@@ -161,7 +161,7 @@ func handleNewIncomeConnection(inServer proxy.Server, outClient proxy.Client, th
//此时baseLocalConn里面 正常情况下, 服务端看到的是 客户端的golang的tls 拨号发出的 tls数据 //此时baseLocalConn里面 正常情况下, 服务端看到的是 客户端的golang的tls 拨号发出的 tls数据
// 客户端看到的是 socks5的数据 我们首先就是要看看socks5里的数据是不是tls而socks5自然 IsUseTLS 是false // 客户端看到的是 socks5的数据 我们首先就是要看看socks5里的数据是不是tls而socks5自然 IsUseTLS 是false
// 如果是服务端的话,那就是 localServer.IsUseTLS == true, 此时,我们正常握手,然后我们需要判断的是它承载的数据 // 如果是服务端的话,那就是 inServer.IsUseTLS == true, 此时,我们正常握手,然后我们需要判断的是它承载的数据
// 每次tls试图从 原始连接 读取内容时,都会附带把原始数据写入到 这个 Recorder中 // 每次tls试图从 原始连接 读取内容时,都会附带把原始数据写入到 这个 Recorder中
var serverEndLocalServerTlsRawReadRecorder *tlsLayer.Recorder var serverEndLocalServerTlsRawReadRecorder *tlsLayer.Recorder
@@ -181,7 +181,7 @@ func handleNewIncomeConnection(inServer proxy.Server, outClient proxy.Client, th
if err != nil { if err != nil {
if utils.CanLogErr() { 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() thisLocalConnectionInstance.Close()
@@ -237,7 +237,7 @@ afterLocalServerHandshake:
log.Println("trying routing feature") log.Println("trying routing feature")
} }
//目前只支持一个 localServer/remoteClient, 所以目前根据tag分流是没有意义的以后再说 //目前只支持一个 inServer/outClient, 所以目前根据tag分流是没有意义的以后再说
// 现在就用addr分流就行 // 现在就用addr分流就行
outtag := routePolicy.GetOutTag(&netLayer.TargetDescription{ outtag := routePolicy.GetOutTag(&netLayer.TargetDescription{
Addr: targetAddr, Addr: targetAddr,
@@ -254,7 +254,7 @@ afterLocalServerHandshake:
// 我们在客户端 lazy_encrypt 探测时读取socks5 传来的信息因为这个和要发送到tls的信息是一模一样的所以就不需要等包上vless、tls后再判断了, 直接解包 socks5进行判断 // 我们在客户端 lazy_encrypt 探测时读取socks5 传来的信息因为这个和要发送到tls的信息是一模一样的所以就不需要等包上vless、tls后再判断了, 直接解包 socks5进行判断
// //
// 而在服务端探测时,因为 客户端传来的连接 包了 tls所以要在tls解包后, vless 解包后,再进行判断; // 而在服务端探测时,因为 客户端传来的连接 包了 tls所以要在tls解包后, vless 解包后,再进行判断;
// 所以总之都是要在 localServer判断 wlc; 总之,含义就是,去检索“用户承载数据”的来源 // 所以总之都是要在 inServer 判断 wlc; 总之,含义就是,去检索“用户承载数据”的来源
if tls_lazy_encrypt { if tls_lazy_encrypt {
@@ -276,7 +276,7 @@ afterLocalServerHandshake:
// 而且也不在这里处理监听事件client自己会在额外的 goroutine里处理 // 而且也不在这里处理监听事件client自己会在额外的 goroutine里处理
// server也一样会在特定的场合给 CRUMFURS 传值这个机制是与main函数无关的 // server也一样会在特定的场合给 CRUMFURS 传值这个机制是与main函数无关的
// 而且 thisLocalConnectionInstance 会被 localServer 保存起来,用于后面的 unknownRemoteAddrMsgWriter // 而且 thisLocalConnectionInstance 会被 inServer 保存起来,用于后面的 unknownRemoteAddrMsgWriter
return return
@@ -297,7 +297,7 @@ afterLocalServerHandshake:
// 此时socks5包已经帮我们dial好了一个udp连接即wlc但是还未读取到客户端想要访问的东西 // 此时socks5包已经帮我们dial好了一个udp连接即wlc但是还未读取到客户端想要访问的东西
udpConn := wlc.(*socks5.UDPConn) udpConn := wlc.(*socks5.UDPConn)
// 将 remoteClient 视为 UDP_Putter 就可以转发udp信息了 // 将 outClient 视为 UDP_Putter 就可以转发udp信息了
// direct 也实现了 UDP_Putter (通过 UDP_Pipe和 RelayUDP_to_Direct函数), 所以目前 socks5直接转发udp到direct 的功能 已经实现。 // direct 也实现了 UDP_Putter (通过 UDP_Pipe和 RelayUDP_to_Direct函数), 所以目前 socks5直接转发udp到direct 的功能 已经实现。
// 我在 vless v1 的client 的 UserConn 中实现了 UDP_Putter, vless 的 client的 新连接的Handshake过程会在 UDP_Putter.WriteUDPRequest 被调用 时发生 // 我在 vless v1 的client 的 UserConn 中实现了 UDP_Putter, vless 的 client的 新连接的Handshake过程会在 UDP_Putter.WriteUDPRequest 被调用 时发生
@@ -428,7 +428,7 @@ afterLocalServerHandshake:
tlsConn, err := client.GetTLS_Client().Handshake(clientConn) tlsConn, err := client.GetTLS_Client().Handshake(clientConn)
if err != nil { 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 return
} }
@@ -601,7 +601,7 @@ func tryRawCopy(useSecureMethod bool, proxy_client proxy.UserClient, proxy_serve
tlsConn, err := proxy_client.GetTLS_Client().Handshake(teeConn) tlsConn, err := proxy_client.GetTLS_Client().Handshake(teeConn)
if err != nil { if err != nil {
if utils.CanLogErr() { if utils.CanLogErr() {
log.Println("failed in handshake remoteClient tls", ", Reason: ", err) log.Println("failed in handshake outClient tls", ", Reason: ", err)
} }
return return

View File

@@ -91,6 +91,8 @@ Server and Client
我们服务端和 客户端的程序,都是有至少一个入口和一个出口的。入口我们叫做 inServer ,出口我们叫做 outClient. 我们服务端和 客户端的程序,都是有至少一个入口和一个出口的。入口我们叫做 inServer ,出口我们叫做 outClient.
这两个词的含义和 v2ray的 inbound 和 outbound 是等价的.
在 inServer 中,我们负责监听未知连接;在 outClient 中,我们负责拨号特定目标服务器. 在 inServer 中,我们负责监听未知连接;在 outClient 中,我们负责拨号特定目标服务器.
*/ */
@@ -109,10 +111,11 @@ import (
) )
// 给一个节点 提供 VSI中 第 5-7层 的支持, server和 client通用. 个别方法只能用于某一端 // 给一个节点 提供 VSI中 第 5-7层 的支持, server和 client通用. 个别方法只能用于某一端
// 一个 ProxyCommon 会内嵌proxy以及上面各层的所有信息
type ProxyCommon interface { type ProxyCommon interface {
AddrStr() string //地址在server就是监听地址在client就是拨号地址 AddrStr() string //地址在server就是监听地址在client就是拨号地址
SetAddrStr(string) SetAddrStr(string)
CantRoute() bool //for localServer, CantRoute() bool //for inServer, 分流属于netLayer的指责
GetTag() string GetTag() string
SetUseTLS() SetUseTLS()
@@ -120,6 +123,7 @@ type ProxyCommon interface {
HasAdvancedApplicationLayer() bool //如果使用了ws或者grpc这个要返回true HasAdvancedApplicationLayer() bool //如果使用了ws或者grpc这个要返回true
//tls属于tlsLayer的指责
SetTLS_Server(*tlsLayer.Server) SetTLS_Server(*tlsLayer.Server)
SetTLS_Client(*tlsLayer.Client) SetTLS_Client(*tlsLayer.Client)
@@ -139,7 +143,9 @@ func PrepareTLS_forProxyCommon(u *url.URL, isclient bool, com ProxyCommon) error
} }
if isclient { 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 { } else {
certFile := u.Query().Get("cert") certFile := u.Query().Get("cert")
@@ -166,7 +172,7 @@ type ProxyCommonStruct struct {
tls_s *tlsLayer.Server tls_s *tlsLayer.Server
tls_c *tlsLayer.Client tls_c *tlsLayer.Client
cantRoute bool //for localServer, 若为truelocalServer 读得的数据 不会经过分流一定会通过用户指定的remoteclient发出 cantRoute bool //for inServer, 若为trueinServer 读得的数据 不会经过分流一定会通过用户指定的remoteclient发出
} }
func (pcs *ProxyCommonStruct) CantRoute() bool { func (pcs *ProxyCommonStruct) CantRoute() bool {

View File

@@ -208,7 +208,7 @@ func (c *Client) getBufWithCmd(cmd byte) *bytes.Buffer {
return buf 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) { func (c *Client) handle_CRUMFURS(UMFURS_conn net.Conn) {
if c.udpResponseChan == nil { if c.udpResponseChan == nil {

View File

@@ -2,36 +2,68 @@ package tlsLayer
import ( import (
"crypto/tls" "crypto/tls"
"log"
"net" "net"
"unsafe"
"github.com/hahahrfool/v2ray_simple/utils"
utls "github.com/refraction-networking/utls"
) )
type Client struct { type Client struct {
tlsConfig *tls.Config tlsConfig *tls.Config
useTls bool
} }
func NewTlsClient(host string, insecure bool) *Client { func NewTlsClient(host string, insecure bool, useTls bool) *Client {
c := &Client{ c := &Client{
tlsConfig: &tls.Config{ tlsConfig: &tls.Config{
InsecureSkipVerify: insecure, InsecureSkipVerify: insecure,
ServerName: host, ServerName: host,
}, },
useTls: useTls,
}
if useTls && utils.CanLogInfo() {
log.Println("using utls and Chrome fingerprint")
} }
return c return c
} }
func (c *Client) Handshake(underlay net.Conn) (tlsConn *Conn, err error) { func (c *Client) Handshake(underlay net.Conn) (tlsConn *Conn, err error) {
rawTlsConn := tls.Client(underlay, c.tlsConfig)
err = rawTlsConn.Handshake()
if err != nil { if c.useTls {
return 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: officialConn,
ptr: unsafe.Pointer(officialConn),
tlsPackageType: official,
}
} }
tlsConn = &Conn{
Conn: rawTlsConn,
}
return return
} }

View File

@@ -4,23 +4,34 @@ import (
"crypto/tls" "crypto/tls"
"net" "net"
"unsafe" "unsafe"
utls "github.com/refraction-networking/utls"
)
const (
official = iota
utlsPackage
) )
//参考 crypt/tls 的 conn.go 注意,如果上游代码的底层结构发生了改变,则这里也要跟着修改,保持头部结构一致 //参考 crypt/tls 的 conn.go 注意,如果上游代码的底层结构发生了改变,则这里也要跟着修改,保持头部结构一致
type faketlsconn struct { type faketlsconn struct {
// constant
conn net.Conn conn net.Conn
isClient bool isClient bool
} }
// 本包会用到这个Conn比如server和client的 Handshake // 本包会用到这个Conn比如server和client的 Handshake
// 唯一特性就是它可以返回tls连接的底层tcp连接见 GetRaw // 唯一特性就是它可以返回tls连接的底层tcp连接见 GetRaw
type Conn struct { 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 { 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 != nil {
if rc.conn != nil { if rc.conn != nil {
//log.Println("成功获取到 *net.TCPConn", rc.conn.(*net.TCPConn)) //经测试,是毫无问题的,完全能提取出来并正常使用 //log.Println("成功获取到 *net.TCPConn", rc.conn.(*net.TCPConn)) //经测试,是毫无问题的,完全能提取出来并正常使用
@@ -40,7 +51,7 @@ func (c *Conn) GetRaw(tls_lazy_encrypt bool) *net.TCPConn {
// 直接获取TeeConn仅用于已经确定肯定能获取到的情况 // 直接获取TeeConn仅用于已经确定肯定能获取到的情况
func (c *Conn) GetTeeConn() *TeeConn { func (c *Conn) GetTeeConn() *TeeConn {
rc := (*faketlsconn)(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)))) rc := (*faketlsconn)(c.ptr)
return rc.conn.(*TeeConn) return rc.conn.(*TeeConn)
@@ -49,6 +60,13 @@ func (c *Conn) GetTeeConn() *TeeConn {
//return c.Conn.ConnectionState().NegotiatedProtocol //return c.Conn.ConnectionState().NegotiatedProtocol
func (c *Conn) GetAlpn() string { 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
}
} }