修订代码,注释,示例

This commit is contained in:
hahahrfool
2022-03-29 21:58:23 +08:00
parent b63b90400a
commit 01d681f14f
7 changed files with 99 additions and 80 deletions

View File

@@ -25,10 +25,10 @@ host = "127.0.0.1" # 必填, 可填ip或域名如果 network是unix的话
port = 10800 # 必填
[[dial]]
tag = "my_vlesss1" # 可选, 但不可与其他tag重复
tag = "my_vlesss1" # 同listen对应配置, 可选, 但不可与其他tag重复
protocol = "vlesss" # vless 的 尾缀s 表示 使用tls
uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # 这个只是一个示例uuid,请自己生成一个新的.
host = "127.0.0.1"
host = "127.0.0.1" # 同listen对应配置, 可填ip或域名如果 network是unix的话要填一个文件名(不需要已存在,可以是完整路径).
#network = "udp"
# network 目前支持 tcp,udp 和unix;如果不给出则默认为 tcp; dial和listen都可配置此项
@@ -47,7 +47,7 @@ host = "127.0.0.1"
# 除了在 protocol 字段使用 s尾缀 之外还可以明示使用tls.
# 这两种方法不可重复使用.我们首选前者, 更简约, 当然如果你使用时需要频繁开关tls那么可以单独列出来 便于配置
port = 4433
port = 4433 # 必填
version = 0 # 协议版本, 可省略, 省略则默认为最老版本
insecure = true # 我们示例使用自签名证书,所以要开启 insecure. 实际场合请使用真证书并关闭 insecure
utls = true #是否使用 utls 来应用 chrome指纹进行伪装

View File

@@ -3,7 +3,7 @@ package grpc
import (
"context"
"net"
sync "sync"
"sync"
"time"
"github.com/hahahrfool/v2ray_simple/netLayer"
@@ -21,11 +21,11 @@ var (
type ClientConn *grpc.ClientConn
/*
调用过程
建立新客户端连接的调用过程
先用 GetEstablishedConnFor 看看有没有已存在的 clientConn
没有已存在的自己先拨号tcp然后拨号tls然后把tls连接 传递给 ClientHandshake, 生成一个 clientConn
没有 已存在的自己先拨号tcp然后拨号tls然后把tls连接 传递给 ClientHandshake, 生成一个 clientConn
然后把获取到的 clientConn传递给 DialNewSubConn, 获取可用的一条 grpc 连接
@@ -34,16 +34,18 @@ type ClientConn *grpc.ClientConn
//获取与 某grpc服务器的 已存在的grpc连接
func GetEstablishedConnFor(addr *netLayer.Addr) ClientConn {
clientconnMutex.RLock()
clieintconn := clientconnMap[addr.GetHashable()]
clientconn := clientconnMap[addr.GetHashable()]
clientconnMutex.RUnlock()
if clieintconn == nil {
if clientconn == nil {
return nil
}
if (*grpc.ClientConn)(clieintconn).GetState() != connectivity.Shutdown {
return clieintconn
if (*grpc.ClientConn)(clientconn).GetState() != connectivity.Shutdown {
return clientconn
}
//如果state是shutdown的话我们也不用特地清除map因为下一次申请就会覆盖map中的该项.
//如果底层tcp被关闭state就会为 Shutdown
return nil
}
@@ -51,10 +53,11 @@ func GetEstablishedConnFor(addr *netLayer.Addr) ClientConn {
//该 underlay一般为 tls连接。 addr为实际的远程地址我们不从 underlay里获取addr,避免转换.
func ClientHandshake(underlay net.Conn, addr *netLayer.Addr) (ClientConn, error) {
//v2ray的实现中用到了一个 globalDialerMap[dest], 可以利用现有连接,
// 用到了一个 clientconnMap , 可以利用现有连接,
// 只有map里没有与对应目标远程地址的连接的时候才会拨号;
// 这 应该就是一种mux的实现
// 如果有之前过的client的话直接利用现有client进行 NewStream, 然后服务端的话, 实际上会获得到第二条连接;
// 这就是一种mux的实现
// 如果有之前拨号过的client的话直接利用现有client进行 NewStream,
// 然后服务端的话, 实际上会获得到同一条tcp链接上的第二条子连接;
//也就是说,底层连接在客户端-服务端只用了一条,但是 服务端处理时却会抽象出 多条Stream连接进行处理
@@ -84,7 +87,8 @@ func ClientHandshake(underlay net.Conn, addr *netLayer.Addr) (ClientConn, error)
//在一个已存在的grpc连接中 进行新的子连接申请
func DialNewSubConn(path string, clientconn ClientConn, addr *netLayer.Addr) (net.Conn, error) {
//不像服务端需要自己写一个实现StreamServer接口的结构, 我们Client端直接可以调用函数生成 StreamClient
// 帮助理解:
// 不像服务端需要自己写一个实现StreamServer接口的结构, 我们Client端直接可以调用函数生成 StreamClient
// 这也是grpc的特点, 客户端只负责 “调用“ ”service“而具体的service的实现 是在服务端.
streamClient := NewStreamClient((*grpc.ClientConn)(clientconn)).(StreamClient_withName)

View File

@@ -9,8 +9,8 @@ import (
)
// google.golang.org/grpc.(*Server).handleRawConn
//go:linkname HandleRawConn google.golang.org/grpc.(*Server).handleRawConn
func HandleRawConn(c *grpc.Server, lisAddr string, rawConn net.Conn)
//go:linkname handle_grpcRawConn google.golang.org/grpc.(*Server).handleRawConn
func handle_grpcRawConn(c *grpc.Server, lisAddr string, rawConn net.Conn)
//Server实现 grpc生成的 StreamServer 接口,用于不断处理一个客户端传来的新需求
type Server struct {
@@ -25,15 +25,17 @@ type Server struct {
}
// StartHandle方法 被用于 手动给 grpc提供新连接.
// 在本作中 我们不使用 grpc的listen的方法。
//非阻塞,
// 在本作中 我们不使用 grpc的listen的方法。这样更加灵活.
//非阻塞.
func (s *Server) StartHandle(conn net.Conn) {
//非阻塞,因为 grpc.(*Server).handleRawConn 是非阻塞的里面用了新的goroutine
HandleRawConn(s.gs, "", conn)
handle_grpcRawConn(s.gs, "", conn)
}
// 该 Tun方法会被 grpc包调用, stream_TunServer就是获取到的新连接;
// 实际上就是在 handle_grpcRawConn 后, 每一条客户端发来的子连接 都会调用一次 s.Tun .
//
// 我们把该 stream_TunServer 包装成 net.Conn 并传入 NewConnChan
// 该方法是自动调用的, 我们不用管.
func (s *Server) Tun(stream_TunServer Stream_TunServer) error {
@@ -66,7 +68,7 @@ func NewServer(serviceName string) *Server {
newConnChan := make(chan net.Conn, 10)
s := &Server{
NewConnChan: newConnChan,
NewConnChan: newConnChan, //该NewConnChan目前是永远不会被关闭的, 因为我们始终在监听新连接
gs: gs,
serviceName: serviceName,
}

74
main.go
View File

@@ -399,9 +399,8 @@ func handleNewIncomeConnection(inServer proxy.Server, defaultClientForThis proxy
for {
newGConn, ok := <-grpcs.NewConnChan
if !ok {
if utils.CanLogErr() {
log.Println("upgrade grpc not ok")
if utils.CanLogWarn() {
log.Println("grpc getNewSubConn not ok")
}
iics.baseLocalConn.Close()
@@ -706,35 +705,34 @@ afterLocalServerHandshake:
// server也一样会在特定的场合给 CRUMFURS 传值这个机制是与main函数无关的
// 而且 wrappedConn 会被 inServer 保存起来,用于后面的 unknownRemoteAddrMsgWriter
return
} else {
//如果不是CRUMFURS命令那就是普通的针对某udp地址的连接见下文 uniExtractor 的使用
//defer wrappedConn.Close()
iics.shouldCloseBaseConnAfterCopyComplete = true
}
case "socks5":
// UDP Associate
//
// 因为socks5的 UDP Associate 办法是较为特殊的不使用现有tcp而是新建立udp所以此时该tcp连接已经没用了
// 另外,此时 targetAddr.IsUDP 只是用于告知此链接是udp Associate并不包含实际地址信息
//但是根据socks5标准这个tcp链接同样是 keep alive的否则客户端就会认为服务端挂掉了.
default:
//defer wrappedConn.Close()
iics.shouldCloseBaseConnAfterCopyComplete = true
}
} else {
if !(iics.isTlsLazyServerEnd) {
//lazy_encrypt情况比较特殊
// 如果不是lazy的情况的话转发结束后要自动关闭
//defer wrappedConn.Close()
iics.shouldCloseBaseConnAfterCopyComplete = true
//lazy_encrypt情况比较特殊基础连接何时被关闭会在tlslazy相关代码中处理。
// 如果不是lazy的情况的话转发结束后要自动关闭
if !iics.isTlsLazyServerEnd {
//实测 grpc.Conn 被调用了Close 也不会实际关闭连接而是会卡住阻塞直到底层tcp连接被关闭后才会返回
// 但是我们还是 直接避免这种情况
if !inServer.IsMux() {
iics.shouldCloseBaseConnAfterCopyComplete = true
}
}
@@ -742,7 +740,7 @@ afterLocalServerHandshake:
// 下面一段代码 单独处理 udp承载数据的特殊转发。
//
// 这里只处理 vless v1 的CRUMFURS 转发到direct的情况 以及 socks5 的udp associate 转发到vless v1的情况;
// 这里只处理 vless v1 的CRUMFURS 转发到direct的情况 以及 socks5 的udp associate 转发到vless 的情况;
// 如果条件不符合则会跳过而进入下一阶段
if targetAddr.IsUDP() {
@@ -791,11 +789,11 @@ afterLocalServerHandshake:
// 将 outClient 视为 UDP_Putter 就可以转发udp信息了
//direct 和 vless 都实现了 UDP_Putter.
//direct 和 vless 的Client 都实现了 UDP_Putter.
// direct 实现了 UDP_Putter (通过 UDP_Pipe和 RelayUDP_to_Direct函数), 所以目前 socks5直接转发udp到direct 的功能 已经实现。
// direct 通过 UDP_Pipe和 RelayUDP_to_Direct函数 实现了 UDP_Putter
// 我在 vless 的client 的 UserConn 中实现了 UDP_Putter, 新连接的Handshake过程会在 dialFunc 被调用 时发生
// vless 的client 实现了 UDP_Putter, 新连接的Handshake过程会在 dialFunc 被调用 时发生
if putter := client.(netLayer.UDP_Putter); putter != nil {
@@ -818,6 +816,9 @@ afterLocalServerHandshake:
}
////////////////////////////// 拨号阶段 /////////////////////////////////////
//log.Println("will dial", iics.shouldCloseBaseConnAfterCopyComplete)
dialClient(iics, targetAddr, client, isTlsLazy_clientEnd, wlc, false)
}
@@ -830,11 +831,19 @@ afterLocalServerHandshake:
func dialClient(iics incomingInserverConnState, targetAddr *netLayer.Addr, client proxy.Client, isTlsLazy_clientEnd bool, wlc io.ReadWriter, noCopy bool) (io.ReadWriter, error) {
if iics.shouldCloseBaseConnAfterCopyComplete && !noCopy {
/*
log.Println("iics.wrappedConn will close after", reflect.TypeOf(iics.wrappedConn))
defer func() {
log.Println("iics.wrappedConn called")
iics.wrappedConn.Close()
log.Println("iics.wrappedConn closed")
}()
*/
defer iics.wrappedConn.Close()
}
var err error
////////////////////////////// 拨号阶段 /////////////////////////////////////
//先确认拨号地址
@@ -847,7 +856,7 @@ func dialClient(iics incomingInserverConnState, targetAddr *netLayer.Addr, clien
log.Println("request isn't the appointed domain", targetAddr, uniqueTestDomain)
}
return nil, &utils.NumErr{N: 1, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 1, Prefix: "dialClient err, "}
}
if utils.CanLogInfo() {
@@ -890,7 +899,7 @@ func dialClient(iics incomingInserverConnState, targetAddr *netLayer.Addr, clien
log.Println("failed in dial", realTargetAddr.String(), ", Reason: ", err)
}
return nil, &utils.NumErr{N: 2, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 2, Prefix: "dialClient err, "}
}
//log.Println("dial real addr ok", realTargetAddr)
@@ -912,7 +921,7 @@ func dialClient(iics incomingInserverConnState, targetAddr *netLayer.Addr, clien
}
return nil, &utils.NumErr{N: 3, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 3, Prefix: "dialClient err, "}
} else {
clientEndRemoteClientTlsRawReadRecorder = tlsLayer.NewRecorder()
@@ -925,7 +934,7 @@ func dialClient(iics incomingInserverConnState, targetAddr *netLayer.Addr, clien
tlsConn, err := client.GetTLS_Client().Handshake(clientConn)
if err != nil {
log.Println("failed in handshake outClient tls", targetAddr.String(), ", Reason: ", err)
return nil, &utils.NumErr{N: 4, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 4, Prefix: "dialClient err, "}
}
clientConn = tlsConn
@@ -950,7 +959,7 @@ advLayerStep:
iics.baseLocalConn.Close()
}
return nil, &utils.NumErr{N: 5, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 5, Prefix: "dialClient err, "}
}
}
@@ -960,11 +969,14 @@ advLayerStep:
if utils.CanLogErr() {
log.Println("grpc.DialNewSubConn failed,", err)
//如果底层tcp连接被关闭了的话错误会是
// rpc error: code = Unavailable desc = connection error: desc = "transport: failed to write client preface: tls: use of closed connection"
}
if iics.baseLocalConn != nil {
iics.baseLocalConn.Close()
}
return nil, &utils.NumErr{N: 6, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 6, Prefix: "dialClient err, "}
}
case "ws":
@@ -981,7 +993,7 @@ advLayerStep:
if utils.CanLogErr() {
log.Println("err when reading ws early data", e)
}
return nil, &utils.NumErr{N: 7, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 7, Prefix: "dialClient err, "}
}
ed = edBuf[:n]
//log.Println("will send early data", n, ed)
@@ -1007,7 +1019,7 @@ advLayerStep:
log.Println("failed in handshake ws to", targetAddr.String(), ", Reason: ", err)
}
return nil, &utils.NumErr{N: 8, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 8, Prefix: "dialClient err, "}
}
clientConn = wc
@@ -1022,7 +1034,7 @@ advLayerStep:
log.Println("failed in handshake to", targetAddr.String(), ", Reason: ", err)
}
return nil, &utils.NumErr{N: 9, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 9, Prefix: "dialClient err, "}
}
//log.Println("all handshake finished")
@@ -1041,7 +1053,7 @@ advLayerStep:
//必须是 UserClient
if userClient := client.(proxy.UserClient); userClient != nil {
tryTlsLazyRawCopy(false, userClient, nil, nil, wrc, wlc, iics.baseLocalConn, true, clientEndRemoteClientTlsRawReadRecorder)
return nil, &utils.NumErr{N: 11, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 11, Prefix: "dialClient err, "}
}
}
@@ -1052,7 +1064,7 @@ advLayerStep:
if userServer, ok := iics.inServer.(proxy.UserServer); ok {
tryTlsLazyRawCopy(false, nil, userServer, nil, wrc, wlc, iics.baseLocalConn, false, iics.inServerTlsRawReadRecorder)
return nil, &utils.NumErr{N: 12, Prefix: "dialClient err, "}
return nil, utils.NumErr{N: 12, Prefix: "dialClient err, "}
}
}

View File

@@ -29,7 +29,7 @@ func Client_EstablishUDPAssociate(conn net.Conn) (port int, err error) {
return
}
if n != 2 || ba[0] != Version5 || ba[1] != 0 {
return 0, &utils.NumErr{Prefix: "EstablishUDPAssociate,protocol err", N: 1}
return 0, utils.NumErr{Prefix: "EstablishUDPAssociate,protocol err", N: 1}
}
//请求udp associate 阶段
@@ -45,6 +45,8 @@ func Client_EstablishUDPAssociate(conn net.Conn) (port int, err error) {
ba[8] = 0 //port
ba[9] = 0 //port
// 按理说要告诉服务端我们要用到的ip和端口但是我们不知道所以全填零
// 在内网中的话我们是可以知道的但是因为内网很安全所以无所谓在NAT中我们肯定是不知道的。
// 如果是在纯外网中则是可以知道的但是为啥非要socks5这么不安全的协议呢所以还是不予考虑。
_, err = conn.Write(ba[:10])
if err != nil {
@@ -56,7 +58,7 @@ func Client_EstablishUDPAssociate(conn net.Conn) (port int, err error) {
return
}
if n != 10 || ba[0] != Version5 || ba[1] != 0 || ba[2] != 0 || ba[3] != 1 || ba[4] != 0 || ba[5] != 0 || ba[6] != 0 || ba[7] != 0 {
return 0, &utils.NumErr{Prefix: "EstablishUDPAssociate,protocol err", N: 2}
return 0, utils.NumErr{Prefix: "EstablishUDPAssociate,protocol err", N: 2}
}
port = int(ba[8])<<8 | int(ba[9])
@@ -126,7 +128,7 @@ func Client_ReadUDPResponse(udpConn *net.UDPConn, supposedServerAddr *net.UDPAdd
return
}
if buf[0] != 0 || buf[1] != 0 || buf[2] != 0 {
e = &utils.NumErr{Prefix: "EstablishUDPAssociate,protocol err", N: 1}
e = utils.NumErr{Prefix: "EstablishUDPAssociate,protocol err", N: 1}
return
}
atype := buf[3]
@@ -149,7 +151,7 @@ func Client_ReadUDPResponse(udpConn *net.UDPConn, supposedServerAddr *net.UDPAdd
target.Name = string(nameBuf)
default:
e = &utils.NumErr{Prefix: "EstablishUDPAssociate,protocol err", N: 2}
e = utils.NumErr{Prefix: "EstablishUDPAssociate,protocol err", N: 2}
return
}

View File

@@ -164,7 +164,7 @@ func (s *Server) Handshake(underlay net.Conn) (io.ReadWriter, *netLayer.Addr, er
Network: "udp",
}
//这里为了解析域名, 就用了 netLayer.Addr 作为中介的方式
//这里为了解析域名, 就用了 netLayer.Addr 作为中介的方式
uc := &UDPConn{
clientSupposedAddr: clientFutureAddr.ToUDPAddr(),
UDPConn: udpRC,
@@ -243,6 +243,7 @@ func (u *UDPConn) StartPushResponse(udpPutter netLayer.UDP_Putter) {
// 监听 与客户端的udp连接 (u.UDPConn);循环查看客户端发来的请求信息;
// 然后将该请求 用 udpPutter.WriteUDPRequest 发送给 udpPutter
// 至于fullcone与否它是不管的。
// 如果客户端一开始没有指明自己连接本服务端的ip和端口, 则将第一个发来的正确的socks5请求视为该客户端,并记录。
func (u *UDPConn) StartReadRequest(udpPutter netLayer.UDP_Putter, dialFunc func(targetAddr *netLayer.Addr) (io.ReadWriter, error)) {
var clientSupposedAddrIsNothing bool
@@ -269,11 +270,14 @@ func (u *UDPConn) StartReadRequest(udpPutter netLayer.UDP_Putter, dialFunc func(
continue
}
if !clientSupposedAddrIsNothing && (!addr.IP.Equal(u.clientSupposedAddr.IP) || addr.Port != u.clientSupposedAddr.Port) {
if !clientSupposedAddrIsNothing {
//just random attack message.
continue
if !addr.IP.Equal(u.clientSupposedAddr.IP) || addr.Port != u.clientSupposedAddr.Port {
//just random attack message.
continue
}
}
atyp := bs[3]
@@ -322,7 +326,8 @@ func (u *UDPConn) StartReadRequest(udpPutter netLayer.UDP_Putter, dialFunc func(
newStart := off + l
thisaddr := &netLayer.Addr{
//为了解析域名, 我们用 netLayer.Addr 作为中介.
requestAddr := &netLayer.Addr{
IP: theIP,
Name: theName,
Port: thePort,
@@ -336,7 +341,7 @@ func (u *UDPConn) StartReadRequest(udpPutter netLayer.UDP_Putter, dialFunc func(
//log.Println("socks5 server,StartReadRequest, got msg", thisaddr, string(bs[newStart:n]))
udpPutter.WriteUDPRequest(thisaddr.ToUDPAddr(), bs[newStart:n], dialFunc)
udpPutter.WriteUDPRequest(requestAddr.ToUDPAddr(), bs[newStart:n], dialFunc)
}
}

View File

@@ -12,7 +12,7 @@ type NumErr struct {
Prefix string
}
func (ne *NumErr) Error() string {
func (ne NumErr) Error() string {
return ne.Prefix + strconv.Itoa(ne.N)
}
@@ -23,57 +23,51 @@ type ErrFirstBuffer struct {
First *bytes.Buffer
}
func (ef *ErrFirstBuffer) Unwarp() error {
func (ef ErrFirstBuffer) Unwarp() error {
return ef.Err
}
func (ef *ErrFirstBuffer) Error() string {
func (ef ErrFirstBuffer) Error() string {
return ef.Err.Error()
}
func NewErr(desc string, e error) *ErrInErr {
return &ErrInErr{
// 返回结构体,而不是指针, 这样可以避免内存逃逸到堆
func NewErr(desc string, e error) ErrInErr {
return ErrInErr{
ErrDesc: desc,
ErrDetail: e,
}
}
func NewDataErr(desc string, e error, data interface{}) *ErrInErr {
return &ErrInErr{
// 返回结构体,而不是指针, 这样可以避免内存逃逸到堆
func NewDataErr(desc string, e error, data interface{}) ErrInErr {
return ErrInErr{
ErrDesc: desc,
ErrDetail: e,
Data: data,
}
}
// ErrInErr 很适合一个err包含另一个err并且提供附带数据的情况
// ErrInErr 很适合一个err包含另一个err并且提供附带数据的情况.
type ErrInErr struct {
ErrDesc string
ErrDetail error
Data any
cachedStr string
}
func (e *ErrInErr) Error() string {
func (e ErrInErr) Error() string {
return e.String()
}
func (e *ErrInErr) Unwarp() error {
func (e ErrInErr) Unwarp() error {
return e.ErrDetail
}
func (e *ErrInErr) String() string {
if e.cachedStr == "" {
e.cachedStr = e.string()
}
return e.cachedStr
}
func (e ErrInErr) String() string {
func (e *ErrInErr) string() string {
if e.Data != nil {
if e.ErrDetail != nil {