Browse Source

Add H2 path support for fallback

风扇滑翔翼 1 năm trước cách đây
mục cha
commit
4bec9ab845
2 tập tin đã thay đổi với 95 bổ sung10 xóa
  1. 47 5
      proxy/trojan/server.go
  2. 48 5
      proxy/vless/inbound/inbound.go

+ 47 - 5
proxy/trojan/server.go

@@ -1,7 +1,10 @@
 package trojan
 
 import (
+	"bufio"
 	"context"
+	"golang.org/x/net/http2"
+	"golang.org/x/net/http2/hpack"
 	"io"
 	"strconv"
 	"strings"
@@ -346,14 +349,14 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S
 		cs := tlsConn.ConnectionState()
 		name = cs.ServerName
 		alpn = cs.NegotiatedProtocol
-		errors.LogInfo(ctx, "realName = " + name)
-		errors.LogInfo(ctx, "realAlpn = " + alpn)
+		errors.LogInfo(ctx, "realName = "+name)
+		errors.LogInfo(ctx, "realAlpn = "+alpn)
 	} else if realityConn, ok := iConn.(*reality.Conn); ok {
 		cs := realityConn.ConnectionState()
 		name = cs.ServerName
 		alpn = cs.NegotiatedProtocol
-		errors.LogInfo(ctx, "realName = " + name)
-		errors.LogInfo(ctx, "realAlpn = " + alpn)
+		errors.LogInfo(ctx, "realName = "+name)
+		errors.LogInfo(ctx, "realAlpn = "+alpn)
 	}
 	name = strings.ToLower(name)
 	alpn = strings.ToLower(alpn)
@@ -403,7 +406,7 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S
 						}
 						if k == '?' || k == ' ' {
 							path = string(firstBytes[i:j])
-							errors.LogInfo(ctx, "realPath = " + path)
+							errors.LogInfo(ctx, "realPath = "+path)
 							if pfb[path] == nil {
 								path = ""
 							}
@@ -413,6 +416,11 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S
 					break
 				}
 			}
+		} else if firstLen >= 18 && first.Byte(4) == '*' { // process h2c
+			h2path := extractPathFromH2Request(connection)
+			if h2path != "" {
+				path = h2path
+			}
 		}
 	}
 	fb := pfb[path]
@@ -520,3 +528,37 @@ func (s *Server) fallback(ctx context.Context, err error, sessionPolicy policy.S
 
 	return nil
 }
+
+// Get path form http2
+func extractPathFromH2Request(conn stat.Connection) string {
+	reader := bufio.NewReader(conn)
+	framer := http2.NewFramer(conn, reader)
+
+	for {
+		frame, err := framer.ReadFrame()
+		if err != nil {
+			return ""
+		}
+
+		// find headers frame
+		if f, ok := frame.(*http2.HeadersFrame); ok {
+			decoder := hpack.NewDecoder(4096, func(hf hpack.HeaderField) {})
+			headerBlock := f.HeaderBlockFragment()
+
+			hf, err := decoder.DecodeFull(headerBlock)
+			if err != nil {
+				return ""
+			}
+			path := func(headers []hpack.HeaderField) string {
+				for _, header := range headers {
+					if header.Name == ":path" {
+						return header.Value
+					}
+				}
+				return ""
+			}
+
+			return path(hf)
+		}
+	}
+}

+ 48 - 5
proxy/vless/inbound/inbound.go

@@ -3,6 +3,7 @@ package inbound
 //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
 
 import (
+	"bufio"
 	"bytes"
 	"context"
 	gotls "crypto/tls"
@@ -13,6 +14,9 @@ import (
 	"time"
 	"unsafe"
 
+	"golang.org/x/net/http2"
+	"golang.org/x/net/http2/hpack"
+
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/buf"
 	"github.com/xtls/xray-core/common/errors"
@@ -223,14 +227,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
 				cs := tlsConn.ConnectionState()
 				name = cs.ServerName
 				alpn = cs.NegotiatedProtocol
-				errors.LogInfo(ctx, "realName = " + name)
-				errors.LogInfo(ctx, "realAlpn = " + alpn)
+				errors.LogInfo(ctx, "realName = "+name)
+				errors.LogInfo(ctx, "realAlpn = "+alpn)
 			} else if realityConn, ok := iConn.(*reality.Conn); ok {
 				cs := realityConn.ConnectionState()
 				name = cs.ServerName
 				alpn = cs.NegotiatedProtocol
-				errors.LogInfo(ctx, "realName = " + name)
-				errors.LogInfo(ctx, "realAlpn = " + alpn)
+				errors.LogInfo(ctx, "realName = "+name)
+				errors.LogInfo(ctx, "realAlpn = "+alpn)
 			}
 			name = strings.ToLower(name)
 			alpn = strings.ToLower(alpn)
@@ -295,7 +299,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
 								}
 								if k == '?' || k == ' ' {
 									path = string(firstBytes[i:j])
-									errors.LogInfo(ctx, "realPath = " + path)
+									errors.LogInfo(ctx, "realPath = "+path)
 									if pfb[path] == nil {
 										path = ""
 									}
@@ -305,6 +309,11 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
 							break
 						}
 					}
+				} else if firstLen >= 18 && first.Byte(4) == '*' { // process h2c
+					h2path := extractPathFromH2Request(connection)
+					if h2path != "" {
+						path = h2path
+					}
 				}
 			}
 			fb := pfb[path]
@@ -583,3 +592,37 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
 
 	return nil
 }
+
+// Get path form http2
+func extractPathFromH2Request(conn stat.Connection) string {
+	reader := bufio.NewReader(conn)
+	framer := http2.NewFramer(conn, reader)
+
+	for {
+		frame, err := framer.ReadFrame()
+		if err != nil {
+			return ""
+		}
+
+		// find headers frame
+		if f, ok := frame.(*http2.HeadersFrame); ok {
+			decoder := hpack.NewDecoder(4096, func(hf hpack.HeaderField) {})
+			headerBlock := f.HeaderBlockFragment()
+
+			hf, err := decoder.DecodeFull(headerBlock)
+			if err != nil {
+				return ""
+			}
+			path := func(headers []hpack.HeaderField) string {
+				for _, header := range headers {
+					if header.Name == ":path" {
+						return header.Value
+					}
+				}
+				return ""
+			}
+
+			return path(hf)
+		}
+	}
+}