mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-12-24 13:27:56 +08:00
修订代码,示例;修复ws的headers配置有些情况下匹配错误的bug
之前发现ws的header只设置了request,没设置response部分,这次都添加上了 且添加了ws的服务端 对于 额外header的验证
This commit is contained in:
@@ -51,9 +51,9 @@ type Conf struct {
|
||||
Host string
|
||||
Addr netLayer.Addr
|
||||
Path string
|
||||
Headers map[string][]string //http headers
|
||||
IsEarly bool //is 0-rtt or not; for quic and ws.
|
||||
Extra map[string]any //quic: useHysteria, hysteria_manual, maxbyte; grpc: multiMode
|
||||
Headers *httpLayer.HeaderPreset
|
||||
IsEarly bool //is 0-rtt or not; for quic and ws.
|
||||
Extra map[string]any //quic: useHysteria, hysteria_manual, maxbyte; grpc: multiMode
|
||||
}
|
||||
|
||||
type Common interface {
|
||||
|
||||
@@ -67,23 +67,27 @@ func (Creator) IsMux() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func getTunPath(sn string) string {
|
||||
func getServiceNameFromConf(conf *advLayer.Conf) (serviceName string) {
|
||||
if conf.Path != "" {
|
||||
serviceName = conf.Path
|
||||
} else {
|
||||
serviceName = "GunService"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getTunPath(serviceName string) string {
|
||||
var sb strings.Builder
|
||||
sb.Grow(1 + len(sn) + 4)
|
||||
sb.Grow(1 + len(serviceName) + 4)
|
||||
sb.WriteString("/")
|
||||
sb.WriteString(sn)
|
||||
sb.WriteString(serviceName)
|
||||
sb.WriteString("/Tun")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) {
|
||||
|
||||
var serviceName string
|
||||
if conf.Path != "" {
|
||||
serviceName = conf.Path
|
||||
} else {
|
||||
serviceName = "GunService"
|
||||
}
|
||||
serviceName := getServiceNameFromConf(conf)
|
||||
|
||||
c := &Client{
|
||||
Config: Config{
|
||||
@@ -105,19 +109,30 @@ func (Creator) NewClientFromConf(conf *advLayer.Conf) (advLayer.Client, error) {
|
||||
Proto: "HTTP/2",
|
||||
ProtoMajor: 2,
|
||||
ProtoMinor: 0,
|
||||
Header: defaultClientHeader,
|
||||
}
|
||||
|
||||
if conf.Headers != nil {
|
||||
h := c.theRequest.Header.Clone()
|
||||
for k, vs := range defaultClientHeader {
|
||||
h.Add(k, vs[0])
|
||||
}
|
||||
c.theRequest.Header = h
|
||||
} else {
|
||||
c.theRequest.Header = defaultClientHeader
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (Creator) NewServerFromConf(conf *advLayer.Conf) (advLayer.Server, error) {
|
||||
serviceName := getServiceNameFromConf(conf)
|
||||
s := &Server{
|
||||
Config: Config{
|
||||
ServiceName: conf.Path,
|
||||
ServiceName: serviceName,
|
||||
Host: conf.Host,
|
||||
},
|
||||
path: getTunPath(conf.Path),
|
||||
path: getTunPath(serviceName),
|
||||
Headers: conf.Headers,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
||||
@@ -20,6 +20,8 @@ type Server struct {
|
||||
|
||||
Config
|
||||
|
||||
Headers *httpLayer.HeaderPreset
|
||||
|
||||
http2.Server
|
||||
|
||||
path string
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/e1732a364fed/v2ray_simple/advLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/httpLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/netLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||
"github.com/gobwas/ws"
|
||||
@@ -23,11 +24,11 @@ type Client struct {
|
||||
path string
|
||||
UseEarlyData bool
|
||||
|
||||
headers map[string][]string
|
||||
headers *httpLayer.HeaderPreset
|
||||
}
|
||||
|
||||
// 这里默认,传入的path必须 以 "/" 为前缀. 本函数 不对此进行任何检查
|
||||
func NewClient(hostAddr, path string, headers map[string][]string, isEarly bool) (*Client, error) {
|
||||
func NewClient(hostAddr, path string, headers *httpLayer.HeaderPreset, isEarly bool) (*Client, error) {
|
||||
u, err := url.Parse("http://" + hostAddr + path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -82,8 +83,11 @@ func (c *Client) Handshake(underlay net.Conn, ed []byte) (net.Conn, error) {
|
||||
// 默认不给出Protocols的话, gobwas就不会发送这个header, 另一端也收不到此header
|
||||
|
||||
}
|
||||
if len(c.headers) > 0 {
|
||||
d.Header = ws.HandshakeHeaderHTTP(c.headers)
|
||||
|
||||
if c.headers != nil && c.headers.Request != nil && len(c.headers.Request.Headers) > 0 {
|
||||
d.Header = ws.HandshakeHeaderHTTP(c.headers.Request.Headers)
|
||||
|
||||
//实测Header里的Connection会被用到。
|
||||
}
|
||||
|
||||
br, _, err := d.Upgrade(underlay, c.requestURL)
|
||||
|
||||
@@ -25,11 +25,11 @@ type Server struct {
|
||||
UseEarlyData bool
|
||||
Thepath string
|
||||
|
||||
headers map[string][]string
|
||||
headers *httpLayer.HeaderPreset
|
||||
}
|
||||
|
||||
// 这里默认: 传入的path必须 以 "/" 为前缀. 本函数 不对此进行任何检查.
|
||||
func NewServer(path string, headers map[string][]string, UseEarlyData bool) *Server {
|
||||
func NewServer(path string, headers *httpLayer.HeaderPreset, UseEarlyData bool) *Server {
|
||||
|
||||
return &Server{
|
||||
//upgrader: upgrader,
|
||||
@@ -85,6 +85,28 @@ func (s *Server) Handshake(underlay net.Conn) (net.Conn, error) {
|
||||
theWrongPath := ""
|
||||
var thePotentialEarlyData []byte
|
||||
|
||||
var requestHeaderNotGivenCount int
|
||||
|
||||
noNeedToCheckRequestHeaders := s.headers == nil || s.headers.Request == nil || len(s.headers.Request.Headers) == 0
|
||||
|
||||
if !noNeedToCheckRequestHeaders {
|
||||
requestHeaderNotGivenCount = len(s.headers.Request.Headers)
|
||||
|
||||
for k := range s.headers.Request.Headers {
|
||||
switch k {
|
||||
case "Host", "Connection", "Upgrade", "Sec-WebSocket-Version", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol", "Sec-WebSocket-Accept", "Sec-WebSocket-Extensions":
|
||||
requestHeaderNotGivenCount -= 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var responseHeader ws.HandshakeHeader
|
||||
|
||||
if s.headers != nil && s.headers.Response != nil && len(s.headers.Response.Headers) > 0 {
|
||||
responseHeader = ws.HandshakeHeaderHTTP(s.headers.Response.Headers)
|
||||
}
|
||||
|
||||
var theUpgrader *ws.Upgrader = &ws.Upgrader{
|
||||
|
||||
//因为我们vs的架构,先统一监听tcp;然后再调用Handshake函数
|
||||
@@ -118,10 +140,32 @@ func (s *Server) Handshake(underlay net.Conn) (net.Conn, error) {
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
OnHeader: func(key, value []byte) error {
|
||||
if noNeedToCheckRequestHeaders {
|
||||
return nil
|
||||
}
|
||||
vs := s.headers.Request.Headers[string(key)]
|
||||
if len(vs) > 0 {
|
||||
for _, v := range vs {
|
||||
if v == (string(value)) {
|
||||
requestHeaderNotGivenCount -= 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.headers) > 0 {
|
||||
theUpgrader.Header = ws.HandshakeHeaderHTTP(s.headers)
|
||||
return nil
|
||||
},
|
||||
Header: responseHeader,
|
||||
OnBeforeUpgrade: func() (header ws.HandshakeHeader, err error) {
|
||||
if requestHeaderNotGivenCount > 0 {
|
||||
if ce := utils.CanLogWarn("ws headers not match"); ce != nil {
|
||||
ce.Write(zap.Int("requestHeaderNotGivenCount", requestHeaderNotGivenCount))
|
||||
}
|
||||
return nil, ws.RejectConnectionError(ws.RejectionStatus(http.StatusBadRequest))
|
||||
}
|
||||
return responseHeader, nil
|
||||
},
|
||||
}
|
||||
|
||||
if s.UseEarlyData {
|
||||
|
||||
@@ -12,16 +12,32 @@ port = 4434
|
||||
version = 0
|
||||
insecure = true
|
||||
utls = true
|
||||
# advancedLayer = "ws" # ws也可应用我们的http header
|
||||
advancedLayer = "ws" # ws也可应用我们的http header
|
||||
|
||||
path = "/very"
|
||||
|
||||
[dial.header.request]
|
||||
version = "1.1"
|
||||
method = "GET"
|
||||
path = ["/very","/simple"] # 每次请求随机选择一个值。
|
||||
version = "1.1" #如果你用ws, 则这一项不会被用到
|
||||
method = "GET" #如果你用ws, 则这一项不会被用到
|
||||
path = ["/very","/simple"] # 每次请求随机选择一个值。如果你用ws, 则这一项不会被用到
|
||||
|
||||
# 如果使用 ws, 则 Connection头必须指明为 Upgrade
|
||||
#headers.Connection = [ "Upgrade" ]
|
||||
|
||||
#[dial.header.response]
|
||||
# 如果使用 ws, 则 Connection头必须指明为 Upgrade, response 也一样
|
||||
# 如果使用 ws, 则 dial的 Connection头必须指明为 Upgrade, 这是因为, headers配置的默认行为是,如果你不给出header, 我们就会配置一个默认的header给你,而默认的header的 Connection 是 keep-alive, 这个对于ws的握手是无效的。
|
||||
|
||||
headers.Connection = [ "Upgrade" ]
|
||||
|
||||
headers.mycustom1 = ["verysimple"]
|
||||
|
||||
|
||||
headers.Host = ["www.baidu.com","www.bing.com"]
|
||||
headers.User-Agent = [ "Mozilla/5.0 (my fake system)"]
|
||||
headers.Accept-Encoding = [ "gzip, deflate" ]
|
||||
headers.Pragma = ["no-cache"]
|
||||
|
||||
|
||||
|
||||
|
||||
[dial.header.response]
|
||||
# 如果使用 ws, 则 Connection头不用给出,因为必须是Upgrade; 你给出也是可以的,但是会被无视。
|
||||
#headers.Connection = [ "Upgrade" ]
|
||||
headers.mycustom2 = ["verysimple_is_so_good"]
|
||||
|
||||
@@ -9,8 +9,8 @@ fallback = ":80"
|
||||
cert = "cert.pem"
|
||||
key = "cert.key"
|
||||
|
||||
# advancedLayer = "ws"
|
||||
|
||||
advancedLayer = "ws"
|
||||
path = "/very"
|
||||
|
||||
# 下面是 http伪装头的 配置
|
||||
# 完全兼容v2ray, 可参考 https://www.v2fly.org/config/transport/tcp.html#httprequestobject
|
||||
@@ -24,22 +24,28 @@ path = ["/very","/simple"] # 每次请求随机选择一个值。
|
||||
|
||||
# headers里的数组, 每次请求随机选择一个值。
|
||||
|
||||
# 如果使用 ws, 则 Connection头必须指明为 Upgrade
|
||||
#headers.Connection = [ "Upgrade" ]
|
||||
|
||||
|
||||
#headers.Host = ["www.baidu.com","www.bing.com"]
|
||||
#headers.User-Agent = [ "Mozilla/5.0 (my fake system)"]
|
||||
#headers.Accept-Encoding = [ "gzip, deflate" ]
|
||||
#headers.Connection = [ "keep-alive" ]
|
||||
#headers.Pragma = ["no-cache"]
|
||||
|
||||
# 如果使用 ws, 则 listen的 Connection头 不用给出,因为必须是Upgrade; 你给出也是可以的,但是会被无视。
|
||||
|
||||
# 如果你给出的Header的首字母小写的,则首字母自动被转换成大写.
|
||||
headers.mycustom1 = ["verysimple"]
|
||||
|
||||
|
||||
headers.Host = ["www.baidu.com","www.bing.com"]
|
||||
headers.User-Agent = [ "Mozilla/5.0 (my fake system)"]
|
||||
headers.Accept-Encoding = [ "gzip, deflate" ]
|
||||
headers.Pragma = ["no-cache"]
|
||||
|
||||
|
||||
|
||||
# 还可以配置 response, 道理一样。这里直接省略了,就是使用默认值, 参考 v2ray文档。
|
||||
|
||||
#[listen.header.response]
|
||||
# 如果使用 ws, 则 Connection头必须指明为 Upgrade, response 也一样
|
||||
#headers.Connection = [ "Upgrade" ]
|
||||
[listen.header.response]
|
||||
# 如果使用 ws, 则 listen.header.response 的 Connection头必须指明为 Upgrade
|
||||
headers.Connection = [ "Upgrade" ]
|
||||
|
||||
headers.mycustom2 = ["verysimple_is_so_good"]
|
||||
|
||||
[[dial]]
|
||||
protocol = "direct"
|
||||
|
||||
@@ -41,6 +41,34 @@ type HeaderPreset struct {
|
||||
Response *ResponseHeader `toml:"response"`
|
||||
}
|
||||
|
||||
// 将Header改为首字母大写
|
||||
func (hh *HeaderPreset) Prepare() {
|
||||
if hh.Request != nil && len(hh.Request.Headers) > 0 {
|
||||
|
||||
var realHeaders http.Header = make(http.Header)
|
||||
for k, vs := range hh.Request.Headers {
|
||||
for _, v := range vs {
|
||||
realHeaders.Add(k, v)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
hh.Request.Headers = realHeaders
|
||||
}
|
||||
if hh.Response != nil && len(hh.Response.Headers) > 0 {
|
||||
|
||||
var realHeaders http.Header = make(http.Header)
|
||||
for k, vs := range hh.Response.Headers {
|
||||
for _, v := range vs {
|
||||
realHeaders.Add(k, v)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
hh.Response.Headers = realHeaders
|
||||
}
|
||||
}
|
||||
|
||||
//默认值保持与v2ray的配置相同
|
||||
func (hh *HeaderPreset) AssignDefaultValue() {
|
||||
if hh.Request == nil {
|
||||
@@ -89,6 +117,8 @@ func (hh *HeaderPreset) AssignDefaultValue() {
|
||||
"Pragma": {"no-cache"},
|
||||
}
|
||||
}
|
||||
|
||||
hh.Prepare()
|
||||
}
|
||||
|
||||
func (h *HeaderPreset) ReadRequest(underlay net.Conn) (err error, leftBuf *bytes.Buffer) {
|
||||
|
||||
174
iics.go
Normal file
174
iics.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package v2ray_simple
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/e1732a364fed/v2ray_simple/httpLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/netLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/proxy"
|
||||
"github.com/e1732a364fed/v2ray_simple/tlsLayer"
|
||||
"github.com/e1732a364fed/v2ray_simple/utils"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
//一个贯穿转发流程的关键结构
|
||||
type incomingInserverConnState struct {
|
||||
|
||||
// 在多路复用的情况下, 可能产生多个 IncomingInserverConnState,
|
||||
// 共用一个 baseLocalConn, 但是 wrappedConn 各不相同。
|
||||
|
||||
baseLocalConn net.Conn // baseLocalConn 是来自客户端的原始传输层链接
|
||||
wrappedConn net.Conn // wrappedConn 是层层握手后,代理层握手前 包装的链接,一般为tls层/高级层;
|
||||
inServer proxy.Server //可为 nil
|
||||
defaultClient proxy.Client
|
||||
|
||||
cachedRemoteAddr string
|
||||
theRequestPath string
|
||||
|
||||
inServerTlsConn *tlsLayer.Conn
|
||||
inServerTlsRawReadRecorder *tlsLayer.Recorder
|
||||
|
||||
isFallbackH2 bool
|
||||
fallbackH2Request *http.Request
|
||||
theFallbackFirstBuffer *bytes.Buffer
|
||||
|
||||
fallbackXver int
|
||||
|
||||
isTlsLazyServerEnd bool
|
||||
|
||||
shouldCloseInSerBaseConnWhenFinish bool
|
||||
|
||||
routedToDirect bool
|
||||
|
||||
RoutingEnv *proxy.RoutingEnv //used in passToOutClient
|
||||
}
|
||||
|
||||
// 在调用 passToOutClient前遇到err时调用, 若找出了buf,设置iics,并返回true
|
||||
func (iics *incomingInserverConnState) extractFirstBufFromErr(err error) bool {
|
||||
if ce := utils.CanLogWarn("failed in inServer proxy handshake"); ce != nil {
|
||||
ce.Write(
|
||||
zap.String("handler", iics.inServer.AddrStr()),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
|
||||
if !iics.inServer.CanFallback() {
|
||||
iics.wrappedConn.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
//通过err找出 并赋值给 iics.theFallbackFirstBuffer
|
||||
{
|
||||
|
||||
fe, ok := err.(*utils.ErrBuffer)
|
||||
if !ok {
|
||||
// 能fallback 但是返回的 err却不是fallback err,证明遇到了更大问题,可能是底层read问题,所以也不用继续fallback了
|
||||
iics.wrappedConn.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
if firstbuffer := fe.Buf; firstbuffer == nil {
|
||||
//不应该,至少能读到1字节的。
|
||||
|
||||
panic("No FirstBuffer")
|
||||
|
||||
} else {
|
||||
iics.theFallbackFirstBuffer = firstbuffer
|
||||
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//查看当前配置 是否支持fallback, 并获得回落地址。
|
||||
// 被 passToOutClient 调用. 若 无fallback则 result < 0, 否则返回所使用的 PROXY protocol 版本, 0 表示 回落但是不用 PROXY protocol.
|
||||
func checkfallback(iics incomingInserverConnState) (targetAddr netLayer.Addr, result int) {
|
||||
//先检查 mainFallback,如果mainFallback中各项都不满足 or根本没有 mainFallback 再检查 defaultFallback
|
||||
|
||||
//一般情况下 iics.RoutingEnv 都会给出,但是 如果是 热加载、tproxy、go test、单独自定义 调用 ListenSer 不给出env 等情况的话, iics.RoutingEnv 都是空值
|
||||
if iics.RoutingEnv != nil {
|
||||
|
||||
if mf := iics.RoutingEnv.MainFallback; mf != nil {
|
||||
|
||||
var thisFallbackType byte
|
||||
|
||||
theRequestPath := iics.theRequestPath
|
||||
|
||||
if iics.theFallbackFirstBuffer != nil && theRequestPath == "" {
|
||||
var failreason int
|
||||
|
||||
_, _, theRequestPath, failreason = httpLayer.GetRequestMethod_and_PATH_from_Bytes(iics.theFallbackFirstBuffer.Bytes(), false)
|
||||
|
||||
if failreason != 0 {
|
||||
theRequestPath = ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fallback_params := make([]string, 0, 4)
|
||||
|
||||
if theRequestPath != "" {
|
||||
fallback_params = append(fallback_params, theRequestPath)
|
||||
thisFallbackType |= httpLayer.Fallback_path
|
||||
}
|
||||
|
||||
if inServerTlsConn := iics.inServerTlsConn; inServerTlsConn != nil {
|
||||
//默认似乎默认tls不会给出alpn和sni项?获得的是空值,也许是因为我用了自签名+insecure,所以导致server并不会设置连接好后所协商的ServerName
|
||||
// 而alpn则也是正常的, 不设置肯定就是空值
|
||||
alpn := inServerTlsConn.GetAlpn()
|
||||
|
||||
if alpn != "" {
|
||||
fallback_params = append(fallback_params, alpn)
|
||||
thisFallbackType |= httpLayer.Fallback_alpn
|
||||
|
||||
}
|
||||
|
||||
sni := inServerTlsConn.GetSni()
|
||||
if sni != "" {
|
||||
fallback_params = append(fallback_params, sni)
|
||||
thisFallbackType |= httpLayer.Fallback_sni
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
fromTag := iics.inServer.GetTag()
|
||||
|
||||
fbResult := mf.GetFallback(fromTag, thisFallbackType, fallback_params...)
|
||||
if fbResult == nil {
|
||||
fbResult = mf.GetFallback("", thisFallbackType, fallback_params...)
|
||||
}
|
||||
|
||||
if ce := utils.CanLogDebug("Fallback check"); ce != nil {
|
||||
if fbResult != nil {
|
||||
ce.Write(
|
||||
zap.String("matched", fbResult.Addr.String()),
|
||||
)
|
||||
} else {
|
||||
ce.Write(
|
||||
zap.String("no match", ""),
|
||||
)
|
||||
}
|
||||
}
|
||||
if fbResult != nil {
|
||||
targetAddr = fbResult.Addr
|
||||
result = fbResult.Xver
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//默认回落, 每个listen配置 都可 有一个自己独享的默认回落
|
||||
|
||||
if defaultFallbackAddr := iics.inServer.GetFallback(); defaultFallbackAddr != nil {
|
||||
|
||||
targetAddr = *defaultFallbackAddr
|
||||
result = 0
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
239
main.go
239
main.go
@@ -4,11 +4,9 @@ import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -44,25 +42,28 @@ var (
|
||||
|
||||
var (
|
||||
DirectClient, _, _ = proxy.ClientFromURL(proxy.DirectName + "://")
|
||||
|
||||
Tls_lazy_encrypt bool
|
||||
Tls_lazy_secure bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
//用于回落到h2c
|
||||
var (
|
||||
h2c_transport = &http2.Transport{
|
||||
DialTLS: func(n, a string, cfg *tls.Config) (net.Conn, error) {
|
||||
return net.Dial(n, a)
|
||||
},
|
||||
AllowHTTP: true,
|
||||
}
|
||||
|
||||
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.")
|
||||
|
||||
}
|
||||
h2c_PROXYprotocolAddrMap = make(map[string]*http2.Transport)
|
||||
h2c_PROXYprotocolAddrMap_mutex sync.RWMutex
|
||||
)
|
||||
|
||||
// ListenSer 函数 是本包 最重要的函数。可以 直接使用 本函数 来手动开启新的 自定义的 转发流程。
|
||||
// 监听 inServer, 然后试图转发到一个 proxy.Client。如果env没给出,则会转发到 defaultOutClient。
|
||||
// 若 env 不为 nil, 则会 进行分流或回落。具有env的情况下,可能会转发到 非 defaultOutClient 的其他 proxy.Client.
|
||||
//
|
||||
// 使用方式可以参考 tcp_test.go, udp_test.go or cmd/verysimple.
|
||||
// Use cases: refer to tcp_test.go, udp_test.go or cmd/verysimple.
|
||||
//
|
||||
// 非阻塞. 返回的closer 用于 停止监听,若为 nil则表示监听失败。
|
||||
// non-blocking. closer used to stop listening. It means listening failed if closer == nil,
|
||||
func ListenSer(inServer proxy.Server, defaultOutClient proxy.Client, env *proxy.RoutingEnv) (closer io.Closer) {
|
||||
|
||||
var handleHere bool
|
||||
@@ -157,74 +158,6 @@ func ListenSer(inServer proxy.Server, defaultOutClient proxy.Client, env *proxy.
|
||||
return
|
||||
}
|
||||
|
||||
type incomingInserverConnState struct {
|
||||
|
||||
// 在多路复用的情况下, 可能产生多个 IncomingInserverConnState,
|
||||
// 共用一个 baseLocalConn, 但是 wrappedConn 各不相同。
|
||||
|
||||
baseLocalConn net.Conn // baseLocalConn 是来自客户端的原始网络层链接
|
||||
wrappedConn net.Conn // wrappedConn 是层层握手后,代理层握手前 包装的链接,一般为tls层or高级层;
|
||||
inServer proxy.Server //可为 nil
|
||||
defaultClient proxy.Client
|
||||
|
||||
cachedRemoteAddr string
|
||||
theRequestPath string
|
||||
|
||||
inServerTlsConn *tlsLayer.Conn
|
||||
inServerTlsRawReadRecorder *tlsLayer.Recorder
|
||||
|
||||
isFallbackH2 bool
|
||||
fallbackH2Request *http.Request
|
||||
theFallbackFirstBuffer *bytes.Buffer
|
||||
|
||||
fallbackXver int
|
||||
|
||||
isTlsLazyServerEnd bool
|
||||
|
||||
shouldCloseInSerBaseConnWhenFinish bool
|
||||
|
||||
routedToDirect bool
|
||||
|
||||
RoutingEnv *proxy.RoutingEnv //used in passToOutClient
|
||||
}
|
||||
|
||||
// 在调用 passToOutClient前遇到err时调用, 若找出了buf,设置iics,并返回true
|
||||
func (iics *incomingInserverConnState) extractFirstBufFromErr(err error) bool {
|
||||
if ce := utils.CanLogWarn("failed in inServer proxy handshake"); ce != nil {
|
||||
ce.Write(
|
||||
zap.String("handler", iics.inServer.AddrStr()),
|
||||
zap.Error(err),
|
||||
)
|
||||
}
|
||||
|
||||
if !iics.inServer.CanFallback() {
|
||||
iics.wrappedConn.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
//通过err找出 并赋值给 iics.theFallbackFirstBuffer
|
||||
{
|
||||
|
||||
fe, ok := err.(*utils.ErrBuffer)
|
||||
if !ok {
|
||||
// 能fallback 但是返回的 err却不是fallback err,证明遇到了更大问题,可能是底层read问题,所以也不用继续fallback了
|
||||
iics.wrappedConn.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
if firstbuffer := fe.Buf; firstbuffer == nil {
|
||||
//不应该,至少能读到1字节的。
|
||||
|
||||
panic("No FirstBuffer")
|
||||
|
||||
} else {
|
||||
iics.theFallbackFirstBuffer = firstbuffer
|
||||
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// handleNewIncomeConnection 会处理 网络层至高级层的数据,
|
||||
// 然后将代理层的处理发往 handshakeInserver_and_passToOutClient 函数。
|
||||
//
|
||||
@@ -572,111 +505,8 @@ func handshakeInserver_and_passToOutClient(iics incomingInserverConnState) {
|
||||
|
||||
}
|
||||
|
||||
//查看当前配置 是否支持fallback, 并获得回落地址。
|
||||
// 被 passToOutClient 调用. 若 无fallback则 result < 0, 否则返回所使用的 PROXY protocol 版本, 0 表示 回落但是不用 PROXY protocol.
|
||||
func checkfallback(iics incomingInserverConnState) (targetAddr netLayer.Addr, result int) {
|
||||
//先检查 mainFallback,如果mainFallback中各项都不满足 or根本没有 mainFallback 再检查 defaultFallback
|
||||
|
||||
//一般情况下 iics.RoutingEnv 都会给出,但是 如果是 热加载、tproxy、go test、单独自定义 调用 ListenSer 不给出env 等情况的话, iics.RoutingEnv 都是空值
|
||||
if iics.RoutingEnv != nil {
|
||||
|
||||
if mf := iics.RoutingEnv.MainFallback; mf != nil {
|
||||
|
||||
var thisFallbackType byte
|
||||
|
||||
theRequestPath := iics.theRequestPath
|
||||
|
||||
if iics.theFallbackFirstBuffer != nil && theRequestPath == "" {
|
||||
var failreason int
|
||||
|
||||
_, _, theRequestPath, failreason = httpLayer.GetRequestMethod_and_PATH_from_Bytes(iics.theFallbackFirstBuffer.Bytes(), false)
|
||||
|
||||
if failreason != 0 {
|
||||
theRequestPath = ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fallback_params := make([]string, 0, 4)
|
||||
|
||||
if theRequestPath != "" {
|
||||
fallback_params = append(fallback_params, theRequestPath)
|
||||
thisFallbackType |= httpLayer.Fallback_path
|
||||
}
|
||||
|
||||
if inServerTlsConn := iics.inServerTlsConn; inServerTlsConn != nil {
|
||||
//默认似乎默认tls不会给出alpn和sni项?获得的是空值,也许是因为我用了自签名+insecure,所以导致server并不会设置连接好后所协商的ServerName
|
||||
// 而alpn则也是正常的, 不设置肯定就是空值
|
||||
alpn := inServerTlsConn.GetAlpn()
|
||||
|
||||
if alpn != "" {
|
||||
fallback_params = append(fallback_params, alpn)
|
||||
thisFallbackType |= httpLayer.Fallback_alpn
|
||||
|
||||
}
|
||||
|
||||
sni := inServerTlsConn.GetSni()
|
||||
if sni != "" {
|
||||
fallback_params = append(fallback_params, sni)
|
||||
thisFallbackType |= httpLayer.Fallback_sni
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
fromTag := iics.inServer.GetTag()
|
||||
|
||||
fbResult := mf.GetFallback(fromTag, thisFallbackType, fallback_params...)
|
||||
if fbResult == nil {
|
||||
fbResult = mf.GetFallback("", thisFallbackType, fallback_params...)
|
||||
}
|
||||
|
||||
if ce := utils.CanLogDebug("Fallback check"); ce != nil {
|
||||
if fbResult != nil {
|
||||
ce.Write(
|
||||
zap.String("matched", fbResult.Addr.String()),
|
||||
)
|
||||
} else {
|
||||
ce.Write(
|
||||
zap.String("no match", ""),
|
||||
)
|
||||
}
|
||||
}
|
||||
if fbResult != nil {
|
||||
targetAddr = fbResult.Addr
|
||||
result = fbResult.Xver
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//默认回落, 每个listen配置 都可 有一个自己独享的默认回落
|
||||
|
||||
if defaultFallbackAddr := iics.inServer.GetFallback(); defaultFallbackAddr != nil {
|
||||
|
||||
targetAddr = *defaultFallbackAddr
|
||||
result = 0
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
h2c_transport = &http2.Transport{
|
||||
DialTLS: func(n, a string, cfg *tls.Config) (net.Conn, error) {
|
||||
return net.Dial(n, a)
|
||||
},
|
||||
AllowHTTP: true,
|
||||
}
|
||||
|
||||
h2c_PROXYprotocolAddrMap = make(map[string]*http2.Transport)
|
||||
h2c_PROXYprotocolAddrMap_mutex sync.RWMutex
|
||||
)
|
||||
|
||||
//被 handshakeInserver_and_passToOutClient 和 handshakeInserver 的innerMux部分 调用。
|
||||
// 会调用 dialClient_andRelay。若isfallback为true,传入的 wlc 和 udp_wlc 必须为nil,targetAddr必须为空值。
|
||||
// 会调用 dialClient_andRelay. 若isfallback为true,传入的 wlc 和 udp_wlc 必须为nil,targetAddr必须为空值。
|
||||
func passToOutClient(iics incomingInserverConnState, isfallback bool, wlc net.Conn, udp_wlc netLayer.MsgConn, targetAddr netLayer.Addr) {
|
||||
|
||||
////////////////////////////// 回落阶段 /////////////////////////////////////
|
||||
@@ -698,8 +528,12 @@ func passToOutClient(iics incomingInserverConnState, isfallback bool, wlc net.Co
|
||||
url, _ := url.Parse(urlStr)
|
||||
rq.URL = url
|
||||
|
||||
transport := h2c_transport
|
||||
if fbResult > 0 {
|
||||
var transport *http2.Transport
|
||||
|
||||
if fbResult == 0 {
|
||||
transport = h2c_transport
|
||||
|
||||
} else if fbResult > 0 {
|
||||
|
||||
h2c_PROXYprotocolAddrMap_mutex.RLock()
|
||||
transport = h2c_PROXYprotocolAddrMap[targetAddrStr]
|
||||
@@ -915,13 +749,11 @@ func passToOutClient(iics incomingInserverConnState, isfallback bool, wlc net.Co
|
||||
}
|
||||
|
||||
//dialClient 对实际client进行拨号,处理传输层, tls层, 高级层等所有层级后,进行代理层握手。
|
||||
// result = 0 表示拨号成功, result = -1 表示 拨号失败, result = 1 表示 拨号成功 并 已经自行处理了转发阶段(用于lazy和 innerMux ); -10 标识因为client为reject而关闭了连接。
|
||||
// 在 dialClient_andRelay 中被调用。在udp为multi channel时也有用到
|
||||
func dialClient(targetAddr netLayer.Addr,
|
||||
client proxy.Client, xver int, fallbackFirstBuf *bytes.Buffer,
|
||||
baseLocalConn,
|
||||
// result = 0 表示拨号成功, result = -1 表示 拨号失败, result = 1 表示 拨号成功 并 已经自行处理了转发阶段(用于lazy和 innerMux ); -10 标识 因为 client为reject 而关闭了连接。
|
||||
// 在 dialClient_andRelay 中被调用。在udp为multi channel时也有用到.
|
||||
func dialClient(iics incomingInserverConnState, targetAddr netLayer.Addr,
|
||||
client proxy.Client,
|
||||
wlc net.Conn,
|
||||
cachedRemoteAddr string,
|
||||
isTlsLazy_clientEnd bool) (
|
||||
|
||||
//return values:
|
||||
@@ -979,7 +811,7 @@ func dialClient(targetAddr netLayer.Addr,
|
||||
if ce := utils.CanLogInfo("Request"); ce != nil {
|
||||
|
||||
ce.Write(
|
||||
zap.String("from", cachedRemoteAddr),
|
||||
zap.String("from", iics.cachedRemoteAddr),
|
||||
zap.String("target", targetAddr.UrlString()),
|
||||
zap.String("through", proxy.GetVSI_url(client)),
|
||||
)
|
||||
@@ -1074,7 +906,7 @@ func dialClient(targetAddr netLayer.Addr,
|
||||
|
||||
}
|
||||
|
||||
if xver > 0 && xver < 3 {
|
||||
if xver := iics.fallbackXver; xver > 0 && xver < 3 {
|
||||
utils.Debug("Trying to write proxy protocol head")
|
||||
|
||||
netLayer.WritePROXYprotocol(xver, wlc, clientConn)
|
||||
@@ -1184,8 +1016,8 @@ advLayerHandshakeStep:
|
||||
// rpc error: code = Unavailable desc = connection error: desc = "transport: failed to write client preface: tls: use of closed connection"
|
||||
|
||||
}
|
||||
if baseLocalConn != nil {
|
||||
baseLocalConn.Close()
|
||||
if iics.baseLocalConn != nil {
|
||||
iics.baseLocalConn.Close()
|
||||
}
|
||||
result = -1
|
||||
return
|
||||
@@ -1266,9 +1098,9 @@ advLayerHandshakeStep:
|
||||
if !hasInnerMux {
|
||||
//如果有内层mux,要在dialInnerProxy函数里再读, 而不是在这里读
|
||||
|
||||
if fallbackFirstBuf != nil {
|
||||
if iics.theFallbackFirstBuffer != nil {
|
||||
|
||||
firstPayload = fallbackFirstBuf.Bytes()
|
||||
firstPayload = iics.theFallbackFirstBuffer.Bytes()
|
||||
|
||||
} else {
|
||||
firstPayload = utils.GetMTU()
|
||||
@@ -1468,7 +1300,7 @@ func dialClient_andRelay(iics incomingInserverConnState, targetAddr netLayer.Add
|
||||
}
|
||||
}
|
||||
|
||||
wrc, udp_wrc, realTargetAddr, clientEndRemoteClientTlsRawReadRecorder, result := dialClient(targetAddr, client, iics.fallbackXver, iics.theFallbackFirstBuffer, iics.baseLocalConn, wlc, iics.cachedRemoteAddr, isTlsLazy_clientEnd)
|
||||
wrc, udp_wrc, realTargetAddr, clientEndRemoteClientTlsRawReadRecorder, result := dialClient(iics, targetAddr, client, wlc, isTlsLazy_clientEnd)
|
||||
if result != 0 {
|
||||
return
|
||||
}
|
||||
@@ -1514,11 +1346,8 @@ func dialClient_andRelay(iics incomingInserverConnState, targetAddr netLayer.Add
|
||||
|
||||
} else {
|
||||
|
||||
if iics.theFallbackFirstBuffer != nil {
|
||||
|
||||
udp_wrc.WriteMsgTo(iics.theFallbackFirstBuffer.Bytes(), targetAddr)
|
||||
utils.PutBytes(iics.theFallbackFirstBuffer.Bytes())
|
||||
|
||||
if ffb := iics.theFallbackFirstBuffer; ffb != nil {
|
||||
udp_wrc.WriteMsgTo(ffb.Bytes(), targetAddr)
|
||||
}
|
||||
|
||||
atomic.AddInt32(&ActiveConnectionCount, 1)
|
||||
@@ -1529,7 +1358,7 @@ func dialClient_andRelay(iics incomingInserverConnState, targetAddr netLayer.Add
|
||||
netLayer.RelayUDP_separate(udp_wrc, udp_wlc, &targetAddr, &AllDownloadBytesSinceStart, &AllUploadBytesSinceStart, func(raddr netLayer.Addr) netLayer.MsgConn {
|
||||
utils.Debug("Relaying UDP with MultiChannel,dialfunc called")
|
||||
|
||||
_, udp_wrc, _, _, result := dialClient(raddr, client, -1, iics.theFallbackFirstBuffer, iics.baseLocalConn, nil, "", false)
|
||||
_, udp_wrc, _, _, result := dialClient(iics, raddr, client, nil, false)
|
||||
|
||||
if ce := utils.CanLogDebug("Relaying UDP with MultiChannel, dialfunc call returned"); ce != nil {
|
||||
ce.Write(zap.Int("result", result))
|
||||
|
||||
@@ -38,7 +38,7 @@ type DirectClient struct {
|
||||
|
||||
func (*DirectClient) Name() string { return DirectName }
|
||||
|
||||
//若 underlay 为nil,则会对target进行拨号, 否则直接返回underlay。
|
||||
//若 underlay 为nil,则会对target进行拨号, 否则返回underlay本身
|
||||
func (d *DirectClient) Handshake(underlay net.Conn, firstPayload []byte, target netLayer.Addr) (result io.ReadWriteCloser, err error) {
|
||||
|
||||
if underlay == nil {
|
||||
|
||||
@@ -318,11 +318,9 @@ func (s *ProxyCommonStruct) InitAdvLayer() {
|
||||
|
||||
if dc := s.dialConf; dc != nil {
|
||||
|
||||
var Headers map[string][]string
|
||||
if dc.HttpHeader != nil {
|
||||
if dc.HttpHeader.Request != nil {
|
||||
Headers = dc.HttpHeader.Request.Headers
|
||||
}
|
||||
var Headers *httpLayer.HeaderPreset
|
||||
if creator.CanHandleHeaders() {
|
||||
Headers = dc.HttpHeader
|
||||
}
|
||||
|
||||
advClient, err := creator.NewClientFromConf(&advLayer.Conf{
|
||||
@@ -355,11 +353,10 @@ func (s *ProxyCommonStruct) InitAdvLayer() {
|
||||
|
||||
if lc := s.listenConf; lc != nil {
|
||||
|
||||
var Headers map[string][]string
|
||||
if lc.HttpHeader != nil {
|
||||
if lc.HttpHeader.Request != nil {
|
||||
Headers = lc.HttpHeader.Response.Headers
|
||||
}
|
||||
var Headers *httpLayer.HeaderPreset
|
||||
|
||||
if creator.CanHandleHeaders() {
|
||||
Headers = lc.HttpHeader
|
||||
}
|
||||
|
||||
var certArray []tls.Certificate
|
||||
|
||||
13
tls_lazy.go
13
tls_lazy.go
@@ -1,6 +1,7 @@
|
||||
package v2ray_simple
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
@@ -15,8 +16,20 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
Tls_lazy_encrypt bool
|
||||
Tls_lazy_secure bool
|
||||
)
|
||||
|
||||
const tlslazy_willuseSystemCall = runtime.GOOS == "linux" || runtime.GOOS == "darwin"
|
||||
|
||||
func init() {
|
||||
|
||||
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.")
|
||||
|
||||
}
|
||||
|
||||
//grpc 这种多路复用的链接是绝对无法开启 lazy的, ws 理论上也只有服务端发向客户端的链接 内嵌tls时可以lazy,但暂不考虑
|
||||
func canLazyEncryptServer(inServer proxy.Server) bool {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user