mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-08 14:13:22 +00:00
XHTTP transport: New options for bypassing CDN's detection (#5414)
Usage: https://github.com/XTLS/Xray-core/pull/5414#issuecomment-3770071786 Closes https://github.com/XTLS/Xray-core/issues/4346 --------- Co-authored-by: 风扇滑翔翼 <Fangliding.fshxy@outlook.com>
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
@@ -20,6 +19,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/protocol/dns"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/utils"
|
||||
dns_feature "github.com/xtls/xray-core/features/dns"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
@@ -214,8 +214,7 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
|
||||
|
||||
req.Header.Add("Accept", "application/dns-message")
|
||||
req.Header.Add("Content-Type", "application/dns-message")
|
||||
|
||||
req.Header.Set("X-Padding", strings.Repeat("X", int(crypto.RandBetween(100, 1000))))
|
||||
req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000)))
|
||||
|
||||
hc := s.httpClient
|
||||
|
||||
|
||||
24
common/utils/padding.go
Normal file
24
common/utils/padding.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"math/rand/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
// 8 ÷ (397/62)
|
||||
h2packCorrectionFactor = 1.2493702770780857
|
||||
base62TotalCharsNum = 62
|
||||
base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
)
|
||||
|
||||
// H2Base62Pad generates a base62 padding string for HTTP/2 header
|
||||
// The total len will be slightly longer than the input to match the length after h2(h3 also) header huffman encoding
|
||||
func H2Base62Pad[T int32 | int64 | int](expectedLen T) string {
|
||||
actualLenFloat := float64(expectedLen) * h2packCorrectionFactor
|
||||
actualLen := int(actualLenFloat)
|
||||
result := make([]byte, actualLen)
|
||||
for i := range actualLen {
|
||||
result[i] = base62Chars[rand.N(base62TotalCharsNum)]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
@@ -228,6 +228,19 @@ type SplitHTTPConfig struct {
|
||||
Mode string `json:"mode"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
XPaddingBytes Int32Range `json:"xPaddingBytes"`
|
||||
XPaddingObfsMode bool `json:"xPaddingObfsMode"`
|
||||
XPaddingKey string `json:"xPaddingKey"`
|
||||
XPaddingHeader string `json:"xPaddingHeader"`
|
||||
XPaddingPlacement string `json:"xPaddingPlacement"`
|
||||
XPaddingMethod string `json:"xPaddingMethod"`
|
||||
UplinkHTTPMethod string `json:"uplinkHTTPMethod"`
|
||||
SessionPlacement string `json:"sessionPlacement"`
|
||||
SessionKey string `json:"sessionKey"`
|
||||
SeqPlacement string `json:"seqPlacement"`
|
||||
SeqKey string `json:"seqKey"`
|
||||
UplinkDataPlacement string `json:"uplinkDataPlacement"`
|
||||
UplinkDataKey string `json:"uplinkDataKey"`
|
||||
UplinkChunkSize uint32 `json:"uplinkChunkSize"`
|
||||
NoGRPCHeader bool `json:"noGRPCHeader"`
|
||||
NoSSEHeader bool `json:"noSSEHeader"`
|
||||
ScMaxEachPostBytes Int32Range `json:"scMaxEachPostBytes"`
|
||||
@@ -287,6 +300,107 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
return nil, errors.New("xPaddingBytes cannot be disabled")
|
||||
}
|
||||
|
||||
if c.XPaddingKey == "" {
|
||||
c.XPaddingKey = "x_padding"
|
||||
}
|
||||
|
||||
if c.XPaddingHeader == "" {
|
||||
c.XPaddingHeader = "X-Padding"
|
||||
}
|
||||
|
||||
switch c.XPaddingPlacement {
|
||||
case "":
|
||||
c.XPaddingPlacement = "queryInHeader"
|
||||
case "cookie", "header", "query", "queryInHeader":
|
||||
default:
|
||||
return nil, errors.New("unsupported padding placement: " + c.XPaddingPlacement)
|
||||
}
|
||||
|
||||
switch c.XPaddingMethod {
|
||||
case "":
|
||||
c.XPaddingMethod = "repeat-x"
|
||||
case "repeat-x", "tokenish":
|
||||
default:
|
||||
return nil, errors.New("unsupported padding method: " + c.XPaddingMethod)
|
||||
}
|
||||
|
||||
switch c.UplinkDataPlacement {
|
||||
case "":
|
||||
c.UplinkDataPlacement = "body"
|
||||
case "cookie", "header":
|
||||
if c.Mode != "packet-up" {
|
||||
return nil, errors.New("UplinkDataPlacement can be " + c.UplinkDataPlacement + " only in packet-up mode")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unsupported uplink data placement: " + c.UplinkDataPlacement)
|
||||
}
|
||||
|
||||
if c.UplinkHTTPMethod == "" {
|
||||
c.UplinkHTTPMethod = "POST"
|
||||
}
|
||||
c.UplinkHTTPMethod = strings.ToUpper(c.UplinkHTTPMethod)
|
||||
|
||||
if c.UplinkHTTPMethod == "GET" && c.Mode != "packet-up" {
|
||||
return nil, errors.New("uplinkHTTPMethod can be GET only in packet-up mode")
|
||||
}
|
||||
|
||||
switch c.SessionPlacement {
|
||||
case "":
|
||||
c.SessionPlacement = "path"
|
||||
case "cookie", "header", "query":
|
||||
default:
|
||||
return nil, errors.New("unsupported session placement: " + c.SessionPlacement)
|
||||
}
|
||||
|
||||
switch c.SeqPlacement {
|
||||
case "":
|
||||
c.SeqPlacement = "path"
|
||||
case "cookie", "header", "query":
|
||||
if c.SessionPlacement == "path" {
|
||||
return nil, errors.New("SeqPlacement must be path when SessionPlacement is path")
|
||||
}
|
||||
default:
|
||||
return nil, errors.New("unsupported seq placement: " + c.SeqPlacement)
|
||||
}
|
||||
|
||||
if c.SessionPlacement != "path" && c.SessionKey == "" {
|
||||
switch c.SessionPlacement {
|
||||
case "cookie", "query":
|
||||
c.SessionKey = "x_session"
|
||||
case "header":
|
||||
c.SessionKey = "X-Session"
|
||||
}
|
||||
}
|
||||
|
||||
if c.SeqPlacement != "path" && c.SeqKey == "" {
|
||||
switch c.SeqPlacement {
|
||||
case "cookie", "query":
|
||||
c.SeqKey = "x_seq"
|
||||
case "header":
|
||||
c.SeqKey = "X-Seq"
|
||||
}
|
||||
}
|
||||
|
||||
if c.UplinkDataPlacement != "body" && c.UplinkDataKey == "" {
|
||||
switch c.UplinkDataPlacement {
|
||||
case "cookie":
|
||||
c.UplinkDataKey = "x_data"
|
||||
case "header":
|
||||
c.UplinkDataKey = "X-Data"
|
||||
}
|
||||
}
|
||||
|
||||
if c.UplinkChunkSize == 0 {
|
||||
switch c.UplinkDataPlacement {
|
||||
case "cookie":
|
||||
c.UplinkChunkSize = 3 * 1024 // 3KB
|
||||
case "header":
|
||||
c.UplinkChunkSize = 4 * 1024 // 4KB
|
||||
}
|
||||
} else if c.UplinkChunkSize < 64 {
|
||||
c.UplinkChunkSize = 64
|
||||
}
|
||||
|
||||
if c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency.To > 0 {
|
||||
return nil, errors.New("maxConnections cannot be specified together with maxConcurrency")
|
||||
}
|
||||
@@ -305,6 +419,19 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
|
||||
Mode: c.Mode,
|
||||
Headers: c.Headers,
|
||||
XPaddingBytes: newRangeConfig(c.XPaddingBytes),
|
||||
XPaddingObfsMode: c.XPaddingObfsMode,
|
||||
XPaddingKey: c.XPaddingKey,
|
||||
XPaddingHeader: c.XPaddingHeader,
|
||||
XPaddingPlacement: c.XPaddingPlacement,
|
||||
XPaddingMethod: c.XPaddingMethod,
|
||||
UplinkHTTPMethod: c.UplinkHTTPMethod,
|
||||
SessionPlacement: c.SessionPlacement,
|
||||
SeqPlacement: c.SeqPlacement,
|
||||
SessionKey: c.SessionKey,
|
||||
SeqKey: c.SeqKey,
|
||||
UplinkDataPlacement: c.UplinkDataPlacement,
|
||||
UplinkDataKey: c.UplinkDataKey,
|
||||
UplinkChunkSize: c.UplinkChunkSize,
|
||||
NoGRPCHeader: c.NoGRPCHeader,
|
||||
NoSSEHeader: c.NoSSEHeader,
|
||||
ScMaxEachPostBytes: newRangeConfig(c.ScMaxEachPostBytes),
|
||||
|
||||
@@ -19,12 +19,35 @@ func (c *BrowserDialerClient) IsClosed() bool {
|
||||
panic("not implemented yet")
|
||||
}
|
||||
|
||||
func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (io.ReadCloser, net.Addr, net.Addr, error) {
|
||||
func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, _ string, body io.Reader, uploadOnly bool) (io.ReadCloser, net.Addr, net.Addr, error) {
|
||||
if body != nil {
|
||||
return nil, nil, nil, errors.New("bidirectional streaming for browser dialer not implemented yet")
|
||||
}
|
||||
|
||||
conn, err := browser_dialer.DialGet(url, c.transportConfig.GetRequestHeader(url))
|
||||
header := c.transportConfig.GetRequestHeader()
|
||||
length := int(c.transportConfig.GetNormalizedXPaddingBytes().rand())
|
||||
config := XPaddingConfig{Length: length}
|
||||
|
||||
if c.transportConfig.XPaddingObfsMode {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: c.transportConfig.XPaddingPlacement,
|
||||
Key: c.transportConfig.XPaddingKey,
|
||||
Header: c.transportConfig.XPaddingHeader,
|
||||
RawURL: url,
|
||||
}
|
||||
config.Method = PaddingMethod(c.transportConfig.XPaddingMethod)
|
||||
} else {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: PlacementQueryInHeader,
|
||||
Key: "x_padding",
|
||||
Header: "Referer",
|
||||
RawURL: url,
|
||||
}
|
||||
}
|
||||
|
||||
c.transportConfig.ApplyXPaddingToHeader(header, config)
|
||||
|
||||
conn, err := browser_dialer.DialGet(url, header)
|
||||
dummyAddr := &net.IPAddr{}
|
||||
if err != nil {
|
||||
return nil, dummyAddr, dummyAddr, err
|
||||
@@ -33,13 +56,36 @@ func (c *BrowserDialerClient) OpenStream(ctx context.Context, url string, body i
|
||||
return websocket.NewConnection(conn, dummyAddr, nil, 0), conn.RemoteAddr(), conn.LocalAddr(), nil
|
||||
}
|
||||
|
||||
func (c *BrowserDialerClient) PostPacket(ctx context.Context, url string, body io.Reader, contentLength int64) error {
|
||||
func (c *BrowserDialerClient) PostPacket(ctx context.Context, url string, _ string, _ string, body io.Reader, contentLength int64) error {
|
||||
bytes, err := io.ReadAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = browser_dialer.DialPost(url, c.transportConfig.GetRequestHeader(url), bytes)
|
||||
header := c.transportConfig.GetRequestHeader()
|
||||
length := int(c.transportConfig.GetNormalizedXPaddingBytes().rand())
|
||||
config := XPaddingConfig{Length: length}
|
||||
|
||||
if c.transportConfig.XPaddingObfsMode {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: c.transportConfig.XPaddingPlacement,
|
||||
Key: c.transportConfig.XPaddingKey,
|
||||
Header: c.transportConfig.XPaddingHeader,
|
||||
RawURL: url,
|
||||
}
|
||||
config.Method = PaddingMethod(c.transportConfig.XPaddingMethod)
|
||||
} else {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: PlacementQueryInHeader,
|
||||
Key: "x_padding",
|
||||
Header: "Referer",
|
||||
RawURL: url,
|
||||
}
|
||||
}
|
||||
|
||||
c.transportConfig.ApplyXPaddingToHeader(header, config)
|
||||
|
||||
err = browser_dialer.DialPost(url, header, bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package splithttp
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -19,11 +20,11 @@ import (
|
||||
type DialerClient interface {
|
||||
IsClosed() bool
|
||||
|
||||
// ctx, url, body, uploadOnly
|
||||
OpenStream(context.Context, string, io.Reader, bool) (io.ReadCloser, net.Addr, net.Addr, error)
|
||||
// ctx, url, sessionId, body, uploadOnly
|
||||
OpenStream(context.Context, string, string, io.Reader, bool) (io.ReadCloser, net.Addr, net.Addr, error)
|
||||
|
||||
// ctx, url, body, contentLength
|
||||
PostPacket(context.Context, string, io.Reader, int64) error
|
||||
// ctx, url, sessionId, seqStr, body, contentLength
|
||||
PostPacket(context.Context, string, string, string, io.Reader, int64) error
|
||||
}
|
||||
|
||||
// implements splithttp.DialerClient in terms of direct network connections
|
||||
@@ -41,7 +42,7 @@ func (c *DefaultDialerClient) IsClosed() bool {
|
||||
return c.closed
|
||||
}
|
||||
|
||||
func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body io.Reader, uploadOnly bool) (wrc io.ReadCloser, remoteAddr, localAddr net.Addr, err error) {
|
||||
func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, sessionId string, body io.Reader, uploadOnly bool) (wrc io.ReadCloser, remoteAddr, localAddr net.Addr, err error) {
|
||||
// this is done when the TCP/UDP connection to the server was established,
|
||||
// and we can unblock the Dial function and print correct net addresses in
|
||||
// logs
|
||||
@@ -56,11 +57,34 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body i
|
||||
|
||||
method := "GET" // stream-down
|
||||
if body != nil {
|
||||
method = "POST" // stream-up/one
|
||||
method = c.transportConfig.GetNormalizedUplinkHTTPMethod() // stream-up/one
|
||||
}
|
||||
req, _ := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, body)
|
||||
req.Header = c.transportConfig.GetRequestHeader(url)
|
||||
if method == "POST" && !c.transportConfig.NoGRPCHeader {
|
||||
req.Header = c.transportConfig.GetRequestHeader()
|
||||
length := int(c.transportConfig.GetNormalizedXPaddingBytes().rand())
|
||||
config := XPaddingConfig{Length: length}
|
||||
|
||||
if c.transportConfig.XPaddingObfsMode {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: c.transportConfig.XPaddingPlacement,
|
||||
Key: c.transportConfig.XPaddingKey,
|
||||
Header: c.transportConfig.XPaddingHeader,
|
||||
RawURL: url,
|
||||
}
|
||||
config.Method = PaddingMethod(c.transportConfig.XPaddingMethod)
|
||||
} else {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: PlacementQueryInHeader,
|
||||
Key: "x_padding",
|
||||
Header: "Referer",
|
||||
RawURL: url,
|
||||
}
|
||||
}
|
||||
|
||||
c.transportConfig.ApplyXPaddingToRequest(req, config)
|
||||
c.transportConfig.ApplyMetaToRequest(req, sessionId, "")
|
||||
|
||||
if method == c.transportConfig.GetNormalizedUplinkHTTPMethod() && !c.transportConfig.NoGRPCHeader {
|
||||
req.Header.Set("Content-Type", "application/grpc")
|
||||
}
|
||||
|
||||
@@ -92,13 +116,83 @@ func (c *DefaultDialerClient) OpenStream(ctx context.Context, url string, body i
|
||||
return
|
||||
}
|
||||
|
||||
func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, body io.Reader, contentLength int64) error {
|
||||
req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), "POST", url, body)
|
||||
func (c *DefaultDialerClient) PostPacket(ctx context.Context, url string, sessionId string, seqStr string, body io.Reader, contentLength int64) error {
|
||||
var encodedData string
|
||||
dataPlacement := c.transportConfig.GetNormalizedUplinkDataPlacement()
|
||||
|
||||
if dataPlacement != PlacementBody {
|
||||
data, err := io.ReadAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encodedData = base64.RawURLEncoding.EncodeToString(data)
|
||||
body = nil
|
||||
contentLength = 0
|
||||
}
|
||||
|
||||
method := c.transportConfig.GetNormalizedUplinkHTTPMethod()
|
||||
req, err := http.NewRequestWithContext(context.WithoutCancel(ctx), method, url, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.ContentLength = contentLength
|
||||
req.Header = c.transportConfig.GetRequestHeader(url)
|
||||
req.Header = c.transportConfig.GetRequestHeader()
|
||||
|
||||
if dataPlacement != PlacementBody {
|
||||
key := c.transportConfig.UplinkDataKey
|
||||
chunkSize := int(c.transportConfig.UplinkChunkSize)
|
||||
|
||||
switch dataPlacement {
|
||||
case PlacementHeader:
|
||||
for i := 0; i < len(encodedData); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
if end > len(encodedData) {
|
||||
end = len(encodedData)
|
||||
}
|
||||
chunk := encodedData[i:end]
|
||||
headerKey := fmt.Sprintf("%s-%d", key, i/chunkSize)
|
||||
req.Header.Set(headerKey, chunk)
|
||||
}
|
||||
|
||||
req.Header.Set(key+"-Length", fmt.Sprintf("%d", len(encodedData)))
|
||||
req.Header.Set(key+"-Upstream", "1")
|
||||
case PlacementCookie:
|
||||
for i := 0; i < len(encodedData); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
if end > len(encodedData) {
|
||||
end = len(encodedData)
|
||||
}
|
||||
chunk := encodedData[i:end]
|
||||
cookieName := fmt.Sprintf("%s_%d", key, i/chunkSize)
|
||||
req.AddCookie(&http.Cookie{Name: cookieName, Value: chunk})
|
||||
}
|
||||
|
||||
req.AddCookie(&http.Cookie{Name: key + "_upstream", Value: "1"})
|
||||
}
|
||||
}
|
||||
|
||||
length := int(c.transportConfig.GetNormalizedXPaddingBytes().rand())
|
||||
config := XPaddingConfig{Length: length}
|
||||
|
||||
if c.transportConfig.XPaddingObfsMode {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: c.transportConfig.XPaddingPlacement,
|
||||
Key: c.transportConfig.XPaddingKey,
|
||||
Header: c.transportConfig.XPaddingHeader,
|
||||
RawURL: url,
|
||||
}
|
||||
config.Method = PaddingMethod(c.transportConfig.XPaddingMethod)
|
||||
} else {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: PlacementQueryInHeader,
|
||||
Key: "x_padding",
|
||||
Header: "Referer",
|
||||
RawURL: url,
|
||||
}
|
||||
}
|
||||
|
||||
c.transportConfig.ApplyXPaddingToRequest(req, config)
|
||||
c.transportConfig.ApplyMetaToRequest(req, sessionId, seqStr)
|
||||
|
||||
if c.httpVersion != "1.1" {
|
||||
resp, err := c.client.Do(req)
|
||||
|
||||
10
transport/internet/splithttp/common.go
Normal file
10
transport/internet/splithttp/common.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package splithttp
|
||||
|
||||
const (
|
||||
PlacementQueryInHeader = "queryInHeader"
|
||||
PlacementCookie = "cookie"
|
||||
PlacementHeader = "header"
|
||||
PlacementQuery = "query"
|
||||
PlacementPath = "path"
|
||||
PlacementBody = "body"
|
||||
)
|
||||
@@ -2,7 +2,6 @@ package splithttp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
@@ -43,41 +42,27 @@ func (c *Config) GetNormalizedQuery() string {
|
||||
return query
|
||||
}
|
||||
|
||||
func (c *Config) GetRequestHeader(rawURL string) http.Header {
|
||||
func (c *Config) GetRequestHeader() http.Header {
|
||||
header := http.Header{}
|
||||
for k, v := range c.Headers {
|
||||
header.Add(k, v)
|
||||
}
|
||||
|
||||
u, _ := url.Parse(rawURL)
|
||||
// https://www.rfc-editor.org/rfc/rfc7541.html#appendix-B
|
||||
// h2's HPACK Header Compression feature employs a huffman encoding using a static table.
|
||||
// 'X' is assigned an 8 bit code, so HPACK compression won't change actual padding length on the wire.
|
||||
// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2-2
|
||||
// h3's similar QPACK feature uses the same huffman table.
|
||||
u.RawQuery = "x_padding=" + strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().rand()))
|
||||
header.Set("Referer", u.String())
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func (c *Config) WriteResponseHeader(writer http.ResponseWriter) {
|
||||
// CORS headers for the browser dialer
|
||||
writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
writer.Header().Set("Access-Control-Allow-Methods", "GET, POST")
|
||||
writer.Header().Set("Access-Control-Allow-Methods", "*")
|
||||
// writer.Header().Set("X-Version", core.Version())
|
||||
writer.Header().Set("X-Padding", strings.Repeat("X", int(c.GetNormalizedXPaddingBytes().rand())))
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedXPaddingBytes() RangeConfig {
|
||||
if c.XPaddingBytes == nil || c.XPaddingBytes.To == 0 {
|
||||
return RangeConfig{
|
||||
From: 100,
|
||||
To: 1000,
|
||||
}
|
||||
func (c *Config) GetNormalizedUplinkHTTPMethod() string {
|
||||
if c.UplinkHTTPMethod == "" {
|
||||
return "POST"
|
||||
}
|
||||
|
||||
return *c.XPaddingBytes
|
||||
return c.UplinkHTTPMethod
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedScMaxEachPostBytes() RangeConfig {
|
||||
@@ -121,6 +106,134 @@ func (c *Config) GetNormalizedScStreamUpServerSecs() RangeConfig {
|
||||
return *c.ScStreamUpServerSecs
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedSessionPlacement() string {
|
||||
if c.SessionPlacement == "" {
|
||||
return PlacementPath
|
||||
}
|
||||
return c.SessionPlacement
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedSeqPlacement() string {
|
||||
if c.SeqPlacement == "" {
|
||||
return PlacementPath
|
||||
}
|
||||
return c.SeqPlacement
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedUplinkDataPlacement() string {
|
||||
if c.UplinkDataPlacement == "" {
|
||||
return PlacementBody
|
||||
}
|
||||
return c.UplinkDataPlacement
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedSessionKey() string {
|
||||
if c.SessionKey != "" {
|
||||
return c.SessionKey
|
||||
}
|
||||
switch c.GetNormalizedSessionPlacement() {
|
||||
case PlacementHeader:
|
||||
return "X-Session"
|
||||
case PlacementCookie, PlacementQuery:
|
||||
return "x_session"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedSeqKey() string {
|
||||
if c.SeqKey != "" {
|
||||
return c.SeqKey
|
||||
}
|
||||
switch c.GetNormalizedSeqPlacement() {
|
||||
case PlacementHeader:
|
||||
return "X-Seq"
|
||||
case PlacementCookie, PlacementQuery:
|
||||
return "x_seq"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) ApplyMetaToRequest(req *http.Request, sessionId string, seqStr string) {
|
||||
sessionPlacement := c.GetNormalizedSessionPlacement()
|
||||
seqPlacement := c.GetNormalizedSeqPlacement()
|
||||
sessionKey := c.GetNormalizedSessionKey()
|
||||
seqKey := c.GetNormalizedSeqKey()
|
||||
|
||||
if sessionId != "" {
|
||||
switch sessionPlacement {
|
||||
case PlacementPath:
|
||||
req.URL.Path = appendToPath(req.URL.Path, sessionId)
|
||||
case PlacementQuery:
|
||||
q := req.URL.Query()
|
||||
q.Set(sessionKey, sessionId)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
case PlacementHeader:
|
||||
req.Header.Set(sessionKey, sessionId)
|
||||
case PlacementCookie:
|
||||
req.AddCookie(&http.Cookie{Name: sessionKey, Value: sessionId})
|
||||
}
|
||||
}
|
||||
|
||||
if seqStr != "" {
|
||||
switch seqPlacement {
|
||||
case PlacementPath:
|
||||
req.URL.Path = appendToPath(req.URL.Path, seqStr)
|
||||
case PlacementQuery:
|
||||
q := req.URL.Query()
|
||||
q.Set(seqKey, seqStr)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
case PlacementHeader:
|
||||
req.Header.Set(seqKey, seqStr)
|
||||
case PlacementCookie:
|
||||
req.AddCookie(&http.Cookie{Name: seqKey, Value: seqStr})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) ExtractMetaFromRequest(req *http.Request, path string) (sessionId string, seqStr string) {
|
||||
sessionPlacement := c.GetNormalizedSessionPlacement()
|
||||
seqPlacement := c.GetNormalizedSeqPlacement()
|
||||
sessionKey := c.GetNormalizedSessionKey()
|
||||
seqKey := c.GetNormalizedSeqKey()
|
||||
|
||||
if sessionPlacement == PlacementPath && seqPlacement == PlacementPath {
|
||||
subpath := strings.Split(req.URL.Path[len(path):], "/")
|
||||
if len(subpath) > 0 {
|
||||
sessionId = subpath[0]
|
||||
}
|
||||
if len(subpath) > 1 {
|
||||
seqStr = subpath[1]
|
||||
}
|
||||
return sessionId, seqStr
|
||||
}
|
||||
|
||||
switch sessionPlacement {
|
||||
case PlacementQuery:
|
||||
sessionId = req.URL.Query().Get(sessionKey)
|
||||
case PlacementHeader:
|
||||
sessionId = req.Header.Get(sessionKey)
|
||||
case PlacementCookie:
|
||||
if cookie, e := req.Cookie(sessionKey); e == nil {
|
||||
sessionId = cookie.Value
|
||||
}
|
||||
}
|
||||
|
||||
switch seqPlacement {
|
||||
case PlacementQuery:
|
||||
seqStr = req.URL.Query().Get(seqKey)
|
||||
case PlacementHeader:
|
||||
seqStr = req.Header.Get(seqKey)
|
||||
case PlacementCookie:
|
||||
if cookie, e := req.Cookie(seqKey); e == nil {
|
||||
seqStr = cookie.Value
|
||||
}
|
||||
}
|
||||
|
||||
return sessionId, seqStr
|
||||
}
|
||||
|
||||
func (m *XmuxConfig) GetNormalizedMaxConcurrency() RangeConfig {
|
||||
if m.MaxConcurrency == nil {
|
||||
return RangeConfig{
|
||||
@@ -185,3 +298,10 @@ func init() {
|
||||
func (c RangeConfig) rand() int32 {
|
||||
return int32(crypto.RandBetween(int64(c.From), int64(c.To)))
|
||||
}
|
||||
|
||||
func appendToPath(path, value string) string {
|
||||
if strings.HasSuffix(path, "/") {
|
||||
return path + value
|
||||
}
|
||||
return path + "/" + value
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.35.1
|
||||
// protoc v5.28.2
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v3.21.12
|
||||
// source: transport/internet/splithttp/config.proto
|
||||
|
||||
package splithttp
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -22,12 +23,11 @@ const (
|
||||
)
|
||||
|
||||
type RangeConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
From int32 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"`
|
||||
To int32 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
From int32 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"`
|
||||
To int32 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"`
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RangeConfig) Reset() {
|
||||
@@ -75,16 +75,15 @@ func (x *RangeConfig) GetTo() int32 {
|
||||
}
|
||||
|
||||
type XmuxConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
MaxConcurrency *RangeConfig `protobuf:"bytes,1,opt,name=maxConcurrency,proto3" json:"maxConcurrency,omitempty"`
|
||||
MaxConnections *RangeConfig `protobuf:"bytes,2,opt,name=maxConnections,proto3" json:"maxConnections,omitempty"`
|
||||
CMaxReuseTimes *RangeConfig `protobuf:"bytes,3,opt,name=cMaxReuseTimes,proto3" json:"cMaxReuseTimes,omitempty"`
|
||||
HMaxRequestTimes *RangeConfig `protobuf:"bytes,4,opt,name=hMaxRequestTimes,proto3" json:"hMaxRequestTimes,omitempty"`
|
||||
HMaxReusableSecs *RangeConfig `protobuf:"bytes,5,opt,name=hMaxReusableSecs,proto3" json:"hMaxReusableSecs,omitempty"`
|
||||
HKeepAlivePeriod int64 `protobuf:"varint,6,opt,name=hKeepAlivePeriod,proto3" json:"hKeepAlivePeriod,omitempty"`
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
MaxConcurrency *RangeConfig `protobuf:"bytes,1,opt,name=maxConcurrency,proto3" json:"maxConcurrency,omitempty"`
|
||||
MaxConnections *RangeConfig `protobuf:"bytes,2,opt,name=maxConnections,proto3" json:"maxConnections,omitempty"`
|
||||
CMaxReuseTimes *RangeConfig `protobuf:"bytes,3,opt,name=cMaxReuseTimes,proto3" json:"cMaxReuseTimes,omitempty"`
|
||||
HMaxRequestTimes *RangeConfig `protobuf:"bytes,4,opt,name=hMaxRequestTimes,proto3" json:"hMaxRequestTimes,omitempty"`
|
||||
HMaxReusableSecs *RangeConfig `protobuf:"bytes,5,opt,name=hMaxReusableSecs,proto3" json:"hMaxReusableSecs,omitempty"`
|
||||
HKeepAlivePeriod int64 `protobuf:"varint,6,opt,name=hKeepAlivePeriod,proto3" json:"hKeepAlivePeriod,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *XmuxConfig) Reset() {
|
||||
@@ -160,14 +159,11 @@ func (x *XmuxConfig) GetHKeepAlivePeriod() int64 {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
||||
Mode string `protobuf:"bytes,3,opt,name=mode,proto3" json:"mode,omitempty"`
|
||||
Headers map[string]string `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
Headers map[string]string `protobuf:"bytes,4,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
XPaddingBytes *RangeConfig `protobuf:"bytes,5,opt,name=xPaddingBytes,proto3" json:"xPaddingBytes,omitempty"`
|
||||
NoGRPCHeader bool `protobuf:"varint,6,opt,name=noGRPCHeader,proto3" json:"noGRPCHeader,omitempty"`
|
||||
NoSSEHeader bool `protobuf:"varint,7,opt,name=noSSEHeader,proto3" json:"noSSEHeader,omitempty"`
|
||||
@@ -177,6 +173,21 @@ type Config struct {
|
||||
ScStreamUpServerSecs *RangeConfig `protobuf:"bytes,11,opt,name=scStreamUpServerSecs,proto3" json:"scStreamUpServerSecs,omitempty"`
|
||||
Xmux *XmuxConfig `protobuf:"bytes,12,opt,name=xmux,proto3" json:"xmux,omitempty"`
|
||||
DownloadSettings *internet.StreamConfig `protobuf:"bytes,13,opt,name=downloadSettings,proto3" json:"downloadSettings,omitempty"`
|
||||
XPaddingObfsMode bool `protobuf:"varint,14,opt,name=xPaddingObfsMode,proto3" json:"xPaddingObfsMode,omitempty"`
|
||||
XPaddingKey string `protobuf:"bytes,15,opt,name=xPaddingKey,proto3" json:"xPaddingKey,omitempty"`
|
||||
XPaddingHeader string `protobuf:"bytes,16,opt,name=xPaddingHeader,proto3" json:"xPaddingHeader,omitempty"`
|
||||
XPaddingPlacement string `protobuf:"bytes,17,opt,name=xPaddingPlacement,proto3" json:"xPaddingPlacement,omitempty"`
|
||||
XPaddingMethod string `protobuf:"bytes,18,opt,name=xPaddingMethod,proto3" json:"xPaddingMethod,omitempty"`
|
||||
UplinkHTTPMethod string `protobuf:"bytes,19,opt,name=uplinkHTTPMethod,proto3" json:"uplinkHTTPMethod,omitempty"`
|
||||
SessionPlacement string `protobuf:"bytes,20,opt,name=sessionPlacement,proto3" json:"sessionPlacement,omitempty"`
|
||||
SessionKey string `protobuf:"bytes,21,opt,name=sessionKey,proto3" json:"sessionKey,omitempty"`
|
||||
SeqPlacement string `protobuf:"bytes,22,opt,name=seqPlacement,proto3" json:"seqPlacement,omitempty"`
|
||||
SeqKey string `protobuf:"bytes,23,opt,name=seqKey,proto3" json:"seqKey,omitempty"`
|
||||
UplinkDataPlacement string `protobuf:"bytes,24,opt,name=uplinkDataPlacement,proto3" json:"uplinkDataPlacement,omitempty"`
|
||||
UplinkDataKey string `protobuf:"bytes,25,opt,name=uplinkDataKey,proto3" json:"uplinkDataKey,omitempty"`
|
||||
UplinkChunkSize uint32 `protobuf:"varint,26,opt,name=uplinkChunkSize,proto3" json:"uplinkChunkSize,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Config) Reset() {
|
||||
@@ -300,124 +311,157 @@ func (x *Config) GetDownloadSettings() *internet.StreamConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Config) GetXPaddingObfsMode() bool {
|
||||
if x != nil {
|
||||
return x.XPaddingObfsMode
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Config) GetXPaddingKey() string {
|
||||
if x != nil {
|
||||
return x.XPaddingKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetXPaddingHeader() string {
|
||||
if x != nil {
|
||||
return x.XPaddingHeader
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetXPaddingPlacement() string {
|
||||
if x != nil {
|
||||
return x.XPaddingPlacement
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetXPaddingMethod() string {
|
||||
if x != nil {
|
||||
return x.XPaddingMethod
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetUplinkHTTPMethod() string {
|
||||
if x != nil {
|
||||
return x.UplinkHTTPMethod
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetSessionPlacement() string {
|
||||
if x != nil {
|
||||
return x.SessionPlacement
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetSessionKey() string {
|
||||
if x != nil {
|
||||
return x.SessionKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetSeqPlacement() string {
|
||||
if x != nil {
|
||||
return x.SeqPlacement
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetSeqKey() string {
|
||||
if x != nil {
|
||||
return x.SeqKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetUplinkDataPlacement() string {
|
||||
if x != nil {
|
||||
return x.UplinkDataPlacement
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetUplinkDataKey() string {
|
||||
if x != nil {
|
||||
return x.UplinkDataKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Config) GetUplinkChunkSize() uint32 {
|
||||
if x != nil {
|
||||
return x.UplinkChunkSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_transport_internet_splithttp_config_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_transport_internet_splithttp_config_proto_rawDesc = []byte{
|
||||
0x0a, 0x29, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x78, 0x72, 0x61,
|
||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x1a, 0x1f,
|
||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
|
||||
0x31, 0x0a, 0x0b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12,
|
||||
0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x66, 0x72,
|
||||
0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02,
|
||||
0x74, 0x6f, 0x22, 0xf8, 0x03, 0x0a, 0x0a, 0x58, 0x6d, 0x75, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x12, 0x56, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65,
|
||||
0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61,
|
||||
0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f,
|
||||
0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x56, 0x0a, 0x0e, 0x6d, 0x61, 0x78,
|
||||
0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
||||
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69,
|
||||
0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||
0x67, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x73, 0x12, 0x56, 0x0a, 0x0e, 0x63, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x65, 0x54, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||
0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61,
|
||||
0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x63, 0x4d, 0x61, 0x78, 0x52,
|
||||
0x65, 0x75, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x10, 0x68, 0x4d, 0x61,
|
||||
0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
|
||||
0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70,
|
||||
0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75,
|
||||
0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
||||
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68,
|
||||
0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
||||
0x10, 0x68, 0x4d, 0x61, 0x78, 0x52, 0x65, 0x75, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63,
|
||||
0x73, 0x12, 0x2a, 0x0a, 0x10, 0x68, 0x4b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x50,
|
||||
0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x68, 0x4b, 0x65,
|
||||
0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0xdc, 0x06,
|
||||
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
|
||||
0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||
0x6d, 0x6f, 0x64, 0x65, 0x12, 0x50, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18,
|
||||
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||
0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68,
|
||||
0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x54, 0x0a, 0x0d, 0x78, 0x50, 0x61, 0x64, 0x64, 0x69,
|
||||
0x6e, 0x67, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e,
|
||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
|
||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74,
|
||||
0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x78,
|
||||
0x50, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0c,
|
||||
0x6e, 0x6f, 0x47, 0x52, 0x50, 0x43, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x47, 0x52, 0x50, 0x43, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
|
||||
0x12, 0x20, 0x0a, 0x0b, 0x6e, 0x6f, 0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18,
|
||||
0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x53, 0x53, 0x45, 0x48, 0x65, 0x61, 0x64,
|
||||
0x65, 0x72, 0x12, 0x5e, 0x0a, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x45, 0x61, 0x63, 0x68, 0x50,
|
||||
0x6f, 0x73, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e,
|
||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
|
||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74,
|
||||
0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x12,
|
||||
0x73, 0x63, 0x4d, 0x61, 0x78, 0x45, 0x61, 0x63, 0x68, 0x50, 0x6f, 0x73, 0x74, 0x42, 0x79, 0x74,
|
||||
0x65, 0x73, 0x12, 0x62, 0x0a, 0x14, 0x73, 0x63, 0x4d, 0x69, 0x6e, 0x50, 0x6f, 0x73, 0x74, 0x73,
|
||||
0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74,
|
||||
0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x52, 0x14, 0x73, 0x63, 0x4d, 0x69, 0x6e, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x49, 0x6e, 0x74, 0x65,
|
||||
0x72, 0x76, 0x61, 0x6c, 0x4d, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x42,
|
||||
0x75, 0x66, 0x66, 0x65, 0x72, 0x65, 0x64, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x12, 0x73, 0x63, 0x4d, 0x61, 0x78, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x65,
|
||||
0x64, 0x50, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x62, 0x0a, 0x14, 0x73, 0x63, 0x53, 0x74, 0x72, 0x65,
|
||||
0x61, 0x6d, 0x55, 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x63, 0x73, 0x18, 0x0b,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73,
|
||||
0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x52, 0x14, 0x73, 0x63, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x55, 0x70,
|
||||
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x65, 0x63, 0x73, 0x12, 0x41, 0x0a, 0x04, 0x78, 0x6d,
|
||||
0x75, 0x78, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||
0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x58, 0x6d, 0x75,
|
||||
0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x04, 0x78, 0x6d, 0x75, 0x78, 0x12, 0x51, 0x0a,
|
||||
0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
|
||||
0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
|
||||
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
|
||||
0x74, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10,
|
||||
0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
|
||||
0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
||||
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x85, 0x01, 0x0a,
|
||||
0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
|
||||
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x73, 0x70, 0x6c,
|
||||
0x69, 0x74, 0x68, 0x74, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
|
||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x68, 0x74, 0x74, 0x70,
|
||||
0xaa, 0x02, 0x21, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||
0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74,
|
||||
0x48, 0x74, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
const file_transport_internet_splithttp_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
")transport/internet/splithttp/config.proto\x12!xray.transport.internet.splithttp\x1a\x1ftransport/internet/config.proto\"1\n" +
|
||||
"\vRangeConfig\x12\x12\n" +
|
||||
"\x04from\x18\x01 \x01(\x05R\x04from\x12\x0e\n" +
|
||||
"\x02to\x18\x02 \x01(\x05R\x02to\"\xf8\x03\n" +
|
||||
"\n" +
|
||||
"XmuxConfig\x12V\n" +
|
||||
"\x0emaxConcurrency\x18\x01 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0emaxConcurrency\x12V\n" +
|
||||
"\x0emaxConnections\x18\x02 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0emaxConnections\x12V\n" +
|
||||
"\x0ecMaxReuseTimes\x18\x03 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x0ecMaxReuseTimes\x12Z\n" +
|
||||
"\x10hMaxRequestTimes\x18\x04 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxRequestTimes\x12Z\n" +
|
||||
"\x10hMaxReusableSecs\x18\x05 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x10hMaxReusableSecs\x12*\n" +
|
||||
"\x10hKeepAlivePeriod\x18\x06 \x01(\x03R\x10hKeepAlivePeriod\"\xde\n" +
|
||||
"\n" +
|
||||
"\x06Config\x12\x12\n" +
|
||||
"\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" +
|
||||
"\x04path\x18\x02 \x01(\tR\x04path\x12\x12\n" +
|
||||
"\x04mode\x18\x03 \x01(\tR\x04mode\x12P\n" +
|
||||
"\aheaders\x18\x04 \x03(\v26.xray.transport.internet.splithttp.Config.HeadersEntryR\aheaders\x12T\n" +
|
||||
"\rxPaddingBytes\x18\x05 \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\rxPaddingBytes\x12\"\n" +
|
||||
"\fnoGRPCHeader\x18\x06 \x01(\bR\fnoGRPCHeader\x12 \n" +
|
||||
"\vnoSSEHeader\x18\a \x01(\bR\vnoSSEHeader\x12^\n" +
|
||||
"\x12scMaxEachPostBytes\x18\b \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x12scMaxEachPostBytes\x12b\n" +
|
||||
"\x14scMinPostsIntervalMs\x18\t \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x14scMinPostsIntervalMs\x12.\n" +
|
||||
"\x12scMaxBufferedPosts\x18\n" +
|
||||
" \x01(\x03R\x12scMaxBufferedPosts\x12b\n" +
|
||||
"\x14scStreamUpServerSecs\x18\v \x01(\v2..xray.transport.internet.splithttp.RangeConfigR\x14scStreamUpServerSecs\x12A\n" +
|
||||
"\x04xmux\x18\f \x01(\v2-.xray.transport.internet.splithttp.XmuxConfigR\x04xmux\x12Q\n" +
|
||||
"\x10downloadSettings\x18\r \x01(\v2%.xray.transport.internet.StreamConfigR\x10downloadSettings\x12*\n" +
|
||||
"\x10xPaddingObfsMode\x18\x0e \x01(\bR\x10xPaddingObfsMode\x12 \n" +
|
||||
"\vxPaddingKey\x18\x0f \x01(\tR\vxPaddingKey\x12&\n" +
|
||||
"\x0exPaddingHeader\x18\x10 \x01(\tR\x0exPaddingHeader\x12,\n" +
|
||||
"\x11xPaddingPlacement\x18\x11 \x01(\tR\x11xPaddingPlacement\x12&\n" +
|
||||
"\x0exPaddingMethod\x18\x12 \x01(\tR\x0exPaddingMethod\x12*\n" +
|
||||
"\x10uplinkHTTPMethod\x18\x13 \x01(\tR\x10uplinkHTTPMethod\x12*\n" +
|
||||
"\x10sessionPlacement\x18\x14 \x01(\tR\x10sessionPlacement\x12\x1e\n" +
|
||||
"\n" +
|
||||
"sessionKey\x18\x15 \x01(\tR\n" +
|
||||
"sessionKey\x12\"\n" +
|
||||
"\fseqPlacement\x18\x16 \x01(\tR\fseqPlacement\x12\x16\n" +
|
||||
"\x06seqKey\x18\x17 \x01(\tR\x06seqKey\x120\n" +
|
||||
"\x13uplinkDataPlacement\x18\x18 \x01(\tR\x13uplinkDataPlacement\x12$\n" +
|
||||
"\ruplinkDataKey\x18\x19 \x01(\tR\ruplinkDataKey\x12(\n" +
|
||||
"\x0fuplinkChunkSize\x18\x1a \x01(\rR\x0fuplinkChunkSize\x1a:\n" +
|
||||
"\fHeadersEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x85\x01\n" +
|
||||
"%com.xray.transport.internet.splithttpP\x01Z6github.com/xtls/xray-core/transport/internet/splithttp\xaa\x02!Xray.Transport.Internet.SplitHttpb\x06proto3"
|
||||
|
||||
var (
|
||||
file_transport_internet_splithttp_config_proto_rawDescOnce sync.Once
|
||||
file_transport_internet_splithttp_config_proto_rawDescData = file_transport_internet_splithttp_config_proto_rawDesc
|
||||
file_transport_internet_splithttp_config_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_transport_internet_splithttp_config_proto_rawDescGZIP() []byte {
|
||||
file_transport_internet_splithttp_config_proto_rawDescOnce.Do(func() {
|
||||
file_transport_internet_splithttp_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_splithttp_config_proto_rawDescData)
|
||||
file_transport_internet_splithttp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_splithttp_config_proto_rawDesc), len(file_transport_internet_splithttp_config_proto_rawDesc)))
|
||||
})
|
||||
return file_transport_internet_splithttp_config_proto_rawDescData
|
||||
}
|
||||
@@ -459,7 +503,7 @@ func file_transport_internet_splithttp_config_proto_init() {
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_transport_internet_splithttp_config_proto_rawDesc,
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_splithttp_config_proto_rawDesc), len(file_transport_internet_splithttp_config_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
@@ -470,7 +514,6 @@ func file_transport_internet_splithttp_config_proto_init() {
|
||||
MessageInfos: file_transport_internet_splithttp_config_proto_msgTypes,
|
||||
}.Build()
|
||||
File_transport_internet_splithttp_config_proto = out.File
|
||||
file_transport_internet_splithttp_config_proto_rawDesc = nil
|
||||
file_transport_internet_splithttp_config_proto_goTypes = nil
|
||||
file_transport_internet_splithttp_config_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
@@ -36,4 +36,17 @@ message Config {
|
||||
RangeConfig scStreamUpServerSecs = 11;
|
||||
XmuxConfig xmux = 12;
|
||||
xray.transport.internet.StreamConfig downloadSettings = 13;
|
||||
bool xPaddingObfsMode = 14;
|
||||
string xPaddingKey = 15;
|
||||
string xPaddingHeader = 16;
|
||||
string xPaddingPlacement = 17;
|
||||
string xPaddingMethod = 18;
|
||||
string uplinkHTTPMethod = 19;
|
||||
string sessionPlacement = 20;
|
||||
string sessionKey = 21;
|
||||
string seqPlacement = 22;
|
||||
string seqKey = 23;
|
||||
string uplinkDataPlacement = 24;
|
||||
string uplinkDataKey = 25;
|
||||
uint32 uplinkChunkSize = 26;
|
||||
}
|
||||
|
||||
@@ -272,8 +272,12 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
requestURL.Host = dest.Address.String()
|
||||
}
|
||||
|
||||
sessionIdUuid := uuid.New()
|
||||
requestURL.Path = transportConfiguration.GetNormalizedPath() + sessionIdUuid.String()
|
||||
sessionId := ""
|
||||
if transportConfiguration.Mode != "stream-one" {
|
||||
sessionIdUuid := uuid.New()
|
||||
sessionId = sessionIdUuid.String()
|
||||
}
|
||||
requestURL.Path = transportConfiguration.GetNormalizedPath()
|
||||
requestURL.RawQuery = transportConfiguration.GetNormalizedQuery()
|
||||
|
||||
httpClient, xmuxClient := getHTTPClient(ctx, dest, streamSettings)
|
||||
@@ -327,7 +331,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
if requestURL2.Host == "" {
|
||||
requestURL2.Host = dest2.Address.String()
|
||||
}
|
||||
requestURL2.Path = config2.GetNormalizedPath() + sessionIdUuid.String()
|
||||
requestURL2.Path = config2.GetNormalizedPath()
|
||||
requestURL2.RawQuery = config2.GetNormalizedQuery()
|
||||
httpClient2, xmuxClient2 = getHTTPClient(ctx, dest2, memory2)
|
||||
errors.LogInfo(ctx, fmt.Sprintf("XHTTP is downloading from %s, mode %s, HTTP version %s, host %s", dest2, "stream-down", httpVersion2, requestURL2.Host))
|
||||
@@ -363,7 +367,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
if xmuxClient != nil {
|
||||
xmuxClient.LeftRequests.Add(-1)
|
||||
}
|
||||
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient.OpenStream(ctx, requestURL.String(), reader, false)
|
||||
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient.OpenStream(ctx, requestURL.String(), sessionId, reader, false)
|
||||
if err != nil { // browser dialer only
|
||||
return nil, err
|
||||
}
|
||||
@@ -372,7 +376,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
if xmuxClient2 != nil {
|
||||
xmuxClient2.LeftRequests.Add(-1)
|
||||
}
|
||||
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient2.OpenStream(ctx, requestURL2.String(), nil, false)
|
||||
conn.reader, conn.remoteAddr, conn.localAddr, err = httpClient2.OpenStream(ctx, requestURL2.String(), sessionId, nil, false)
|
||||
if err != nil { // browser dialer only
|
||||
return nil, err
|
||||
}
|
||||
@@ -381,7 +385,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
if xmuxClient != nil {
|
||||
xmuxClient.LeftRequests.Add(-1)
|
||||
}
|
||||
_, _, _, err = httpClient.OpenStream(ctx, requestURL.String(), reader, true)
|
||||
_, _, _, err = httpClient.OpenStream(ctx, requestURL.String(), sessionId, reader, true)
|
||||
if err != nil { // browser dialer only
|
||||
return nil, err
|
||||
}
|
||||
@@ -423,8 +427,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
// this intentionally makes a shallow-copy of the struct so we
|
||||
// can reassign Path (potentially concurrently)
|
||||
url := requestURL
|
||||
url.Path += "/" + strconv.FormatInt(seq, 10)
|
||||
|
||||
seqStr := strconv.FormatInt(seq, 10)
|
||||
seq += 1
|
||||
|
||||
if scMinPostsIntervalMs.From > 0 {
|
||||
@@ -450,6 +453,8 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
err := httpClient.PostPacket(
|
||||
ctx,
|
||||
url.String(),
|
||||
sessionId,
|
||||
seqStr,
|
||||
&buf.MultiBufferContainer{MultiBuffer: chunk},
|
||||
int64(chunk.Len()),
|
||||
)
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
gotls "crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -100,6 +101,24 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
}
|
||||
|
||||
h.config.WriteResponseHeader(writer)
|
||||
length := int(h.config.GetNormalizedXPaddingBytes().rand())
|
||||
config := XPaddingConfig{Length: length}
|
||||
|
||||
if h.config.XPaddingObfsMode {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: h.config.XPaddingPlacement,
|
||||
Key: h.config.XPaddingKey,
|
||||
Header: h.config.XPaddingHeader,
|
||||
}
|
||||
config.Method = PaddingMethod(h.config.XPaddingMethod)
|
||||
} else {
|
||||
config.Placement = XPaddingPlacement{
|
||||
Placement: PlacementHeader,
|
||||
Header: "X-Padding",
|
||||
}
|
||||
}
|
||||
|
||||
h.config.ApplyXPaddingToHeader(writer.Header(), config)
|
||||
|
||||
/*
|
||||
clientVer := []int{0, 0, 0}
|
||||
@@ -110,29 +129,15 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
*/
|
||||
|
||||
validRange := h.config.GetNormalizedXPaddingBytes()
|
||||
paddingLength := 0
|
||||
paddingValue, paddingPlacement := h.config.ExtractXPaddingFromRequest(request, h.config.XPaddingObfsMode)
|
||||
|
||||
referrer := request.Header.Get("Referer")
|
||||
if referrer != "" {
|
||||
if referrerURL, err := url.Parse(referrer); err == nil {
|
||||
// Browser dialer cannot control the host part of referrer header, so only check the query
|
||||
paddingLength = len(referrerURL.Query().Get("x_padding"))
|
||||
}
|
||||
} else {
|
||||
paddingLength = len(request.URL.Query().Get("x_padding"))
|
||||
}
|
||||
|
||||
if int32(paddingLength) < validRange.From || int32(paddingLength) > validRange.To {
|
||||
errors.LogInfo(context.Background(), "invalid x_padding length:", int32(paddingLength))
|
||||
if !h.config.IsPaddingValid(paddingValue, validRange.From, validRange.To, PaddingMethod(h.config.XPaddingMethod)) {
|
||||
errors.LogInfo(context.Background(), "invalid padding ("+paddingPlacement+") length:", int32(len(paddingValue)))
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
sessionId := ""
|
||||
subpath := strings.Split(request.URL.Path[len(h.path):], "/")
|
||||
if len(subpath) > 0 {
|
||||
sessionId = subpath[0]
|
||||
}
|
||||
sessionId, seqStr := h.config.ExtractMetaFromRequest(request, h.path)
|
||||
|
||||
if sessionId == "" && h.config.Mode != "" && h.config.Mode != "auto" && h.config.Mode != "stream-one" && h.config.Mode != "stream-up" {
|
||||
errors.LogInfo(context.Background(), "stream-one mode is not allowed")
|
||||
@@ -178,14 +183,29 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
currentSession = h.upsertSession(sessionId)
|
||||
}
|
||||
scMaxEachPostBytes := int(h.ln.config.GetNormalizedScMaxEachPostBytes().To)
|
||||
uplinkHTTPMethod := h.config.GetNormalizedUplinkHTTPMethod()
|
||||
isUplinkRequest := false
|
||||
|
||||
if request.Method == "POST" && sessionId != "" { // stream-up, packet-up
|
||||
seq := ""
|
||||
if len(subpath) > 1 {
|
||||
seq = subpath[1]
|
||||
if uplinkHTTPMethod != "GET" && request.Method == uplinkHTTPMethod {
|
||||
isUplinkRequest = true
|
||||
}
|
||||
|
||||
uplinkDataPlacement := h.config.GetNormalizedUplinkDataPlacement()
|
||||
uplinkDataKey := h.config.UplinkDataKey
|
||||
|
||||
switch uplinkDataPlacement {
|
||||
case PlacementHeader:
|
||||
if request.Header.Get(uplinkDataKey+"-Upstream") == "1" {
|
||||
isUplinkRequest = true
|
||||
}
|
||||
case PlacementCookie:
|
||||
if c, _ := request.Cookie(uplinkDataKey + "_upstream"); c != nil && c.Value == "1" {
|
||||
isUplinkRequest = true
|
||||
}
|
||||
}
|
||||
|
||||
if seq == "" {
|
||||
if isUplinkRequest && sessionId != "" { // stream-up, packet-up
|
||||
if seqStr == "" {
|
||||
if h.config.Mode != "" && h.config.Mode != "auto" && h.config.Mode != "stream-up" {
|
||||
errors.LogInfo(context.Background(), "stream-up mode is not allowed")
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
@@ -207,6 +227,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
writer.Header().Set("Cache-Control", "no-store")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
scStreamUpServerSecs := h.config.GetNormalizedScStreamUpServerSecs()
|
||||
referrer := request.Header.Get("Referer")
|
||||
if referrer != "" && scStreamUpServerSecs.To > 0 {
|
||||
go func() {
|
||||
for {
|
||||
@@ -233,7 +254,62 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
return
|
||||
}
|
||||
|
||||
payload, err := io.ReadAll(io.LimitReader(request.Body, int64(scMaxEachPostBytes)+1))
|
||||
var payload []byte
|
||||
|
||||
if uplinkDataPlacement != PlacementBody {
|
||||
var encodedStr string
|
||||
switch uplinkDataPlacement {
|
||||
case PlacementHeader:
|
||||
dataLenStr := request.Header.Get(uplinkDataKey + "-Length")
|
||||
|
||||
if dataLenStr != "" {
|
||||
dataLen, _ := strconv.Atoi(dataLenStr)
|
||||
var chunks []string
|
||||
i := 0
|
||||
|
||||
for {
|
||||
chunk := request.Header.Get(fmt.Sprintf("%s-%d", uplinkDataKey, i))
|
||||
if chunk == "" {
|
||||
break
|
||||
}
|
||||
chunks = append(chunks, chunk)
|
||||
i++
|
||||
}
|
||||
|
||||
encodedStr = strings.Join(chunks, "")
|
||||
if len(encodedStr) != dataLen {
|
||||
encodedStr = ""
|
||||
}
|
||||
}
|
||||
case PlacementCookie:
|
||||
var chunks []string
|
||||
i := 0
|
||||
|
||||
for {
|
||||
cookieName := fmt.Sprintf("%s_%d", uplinkDataKey, i)
|
||||
if c, _ := request.Cookie(cookieName); c != nil {
|
||||
chunks = append(chunks, c.Value)
|
||||
i++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(chunks) > 0 {
|
||||
encodedStr = strings.Join(chunks, "")
|
||||
}
|
||||
}
|
||||
|
||||
if encodedStr != "" {
|
||||
payload, err = base64.RawURLEncoding.DecodeString(encodedStr)
|
||||
} else {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to extract data from key "+uplinkDataKey+" placed in "+uplinkDataPlacement)
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
payload, err = io.ReadAll(io.LimitReader(request.Body, int64(scMaxEachPostBytes)+1))
|
||||
}
|
||||
|
||||
if len(payload) > scMaxEachPostBytes {
|
||||
errors.LogInfo(context.Background(), "Too large upload. scMaxEachPostBytes is set to ", scMaxEachPostBytes, "but request size exceed it. Adjust scMaxEachPostBytes on the server to be at least as large as client.")
|
||||
@@ -247,7 +323,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
return
|
||||
}
|
||||
|
||||
seqInt, err := strconv.ParseUint(seq, 10, 64)
|
||||
seq, err := strconv.ParseUint(seqStr, 10, 64)
|
||||
if err != nil {
|
||||
errors.LogInfoInner(context.Background(), err, "failed to upload (ParseUint)")
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
@@ -256,7 +332,7 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
|
||||
|
||||
err = currentSession.uploadQueue.Push(Packet{
|
||||
Payload: payload,
|
||||
Seq: seqInt,
|
||||
Seq: seq,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
||||
307
transport/internet/splithttp/xpadding.go
Normal file
307
transport/internet/splithttp/xpadding.go
Normal file
@@ -0,0 +1,307 @@
|
||||
package splithttp
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
type PaddingMethod string
|
||||
|
||||
const (
|
||||
PaddingMethodRepeatX PaddingMethod = "repeat-x"
|
||||
PaddingMethodTokenish PaddingMethod = "tokenish"
|
||||
)
|
||||
|
||||
const charsetBase62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
// Huffman encoding gives ~20% size reduction for base62 sequences
|
||||
const avgHuffmanBytesPerCharBase62 = 0.8
|
||||
|
||||
const validationTolerance = 2
|
||||
|
||||
type XPaddingPlacement struct {
|
||||
Placement string
|
||||
Key string
|
||||
Header string
|
||||
RawURL string
|
||||
}
|
||||
|
||||
type XPaddingConfig struct {
|
||||
Length int
|
||||
Placement XPaddingPlacement
|
||||
Method PaddingMethod
|
||||
}
|
||||
|
||||
func randStringFromCharset(n int, charset string) (string, bool) {
|
||||
if n <= 0 || len(charset) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
m := len(charset)
|
||||
limit := byte(256 - (256 % m))
|
||||
|
||||
result := make([]byte, n)
|
||||
i := 0
|
||||
|
||||
buf := make([]byte, 256)
|
||||
for i < n {
|
||||
if _, err := rand.Read(buf); err != nil {
|
||||
return "", false
|
||||
}
|
||||
for _, rb := range buf {
|
||||
if rb >= limit {
|
||||
continue
|
||||
}
|
||||
result[i] = charset[int(rb)%m]
|
||||
i++
|
||||
if i == n {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string(result), true
|
||||
}
|
||||
|
||||
func absInt(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func GenerateTokenishPaddingBase62(targetHuffmanBytes int) string {
|
||||
n := int(math.Ceil(float64(targetHuffmanBytes) / avgHuffmanBytesPerCharBase62))
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
|
||||
randBase62Str, ok := randStringFromCharset(n, charsetBase62)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
const maxIter = 150
|
||||
adjustChar := byte('X')
|
||||
|
||||
// Adjust until close enough
|
||||
for iter := 0; iter < maxIter; iter++ {
|
||||
currentLength := int(hpack.HuffmanEncodeLength(randBase62Str))
|
||||
diff := currentLength - targetHuffmanBytes
|
||||
|
||||
if absInt(diff) <= validationTolerance {
|
||||
return randBase62Str
|
||||
}
|
||||
|
||||
if diff < 0 {
|
||||
// Too small -> append padding char(s)
|
||||
randBase62Str += string(adjustChar)
|
||||
|
||||
// Avoid a long run of identical chars
|
||||
if adjustChar == 'X' {
|
||||
adjustChar = 'Z'
|
||||
} else {
|
||||
adjustChar = 'X'
|
||||
}
|
||||
} else {
|
||||
// Too big -> remove from the end
|
||||
if len(randBase62Str) <= 1 {
|
||||
return randBase62Str
|
||||
}
|
||||
randBase62Str = randBase62Str[:len(randBase62Str)-1]
|
||||
}
|
||||
}
|
||||
|
||||
return randBase62Str
|
||||
}
|
||||
|
||||
func GeneratePadding(method PaddingMethod, length int) string {
|
||||
if length <= 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc7541.html#appendix-B
|
||||
// h2's HPACK Header Compression feature employs a huffman encoding using a static table.
|
||||
// 'X' and 'Z' are assigned an 8 bit code, so HPACK compression won't change actual padding length on the wire.
|
||||
// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2-2
|
||||
// h3's similar QPACK feature uses the same huffman table.
|
||||
|
||||
switch method {
|
||||
case PaddingMethodRepeatX:
|
||||
return strings.Repeat("X", length)
|
||||
case PaddingMethodTokenish:
|
||||
paddingValue := GenerateTokenishPaddingBase62(length)
|
||||
if paddingValue == "" {
|
||||
return strings.Repeat("X", length)
|
||||
}
|
||||
return paddingValue
|
||||
default:
|
||||
return strings.Repeat("X", length)
|
||||
}
|
||||
}
|
||||
|
||||
func ApplyPaddingToCookie(req *http.Request, name, value string) {
|
||||
if req == nil || name == "" || value == "" {
|
||||
return
|
||||
}
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: name,
|
||||
Value: value,
|
||||
Path: "/",
|
||||
})
|
||||
}
|
||||
|
||||
func ApplyPaddingToQuery(u *url.URL, key, value string) {
|
||||
if u == nil || key == "" || value == "" {
|
||||
return
|
||||
}
|
||||
q := u.Query()
|
||||
q.Set(key, value)
|
||||
u.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
func (c *Config) GetNormalizedXPaddingBytes() RangeConfig {
|
||||
if c.XPaddingBytes == nil || c.XPaddingBytes.To == 0 {
|
||||
return RangeConfig{
|
||||
From: 100,
|
||||
To: 1000,
|
||||
}
|
||||
}
|
||||
|
||||
return *c.XPaddingBytes
|
||||
}
|
||||
|
||||
func (c *Config) ApplyXPaddingToHeader(h http.Header, config XPaddingConfig) {
|
||||
if h == nil {
|
||||
return
|
||||
}
|
||||
|
||||
paddingValue := GeneratePadding(config.Method, config.Length)
|
||||
|
||||
switch p := config.Placement; p.Placement {
|
||||
case PlacementHeader:
|
||||
h.Set(p.Header, paddingValue)
|
||||
case PlacementQueryInHeader:
|
||||
u, err := url.Parse(p.RawURL)
|
||||
if err != nil || u == nil {
|
||||
return
|
||||
}
|
||||
u.RawQuery = p.Key + "=" + paddingValue
|
||||
h.Set(p.Header, u.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) ApplyXPaddingToRequest(req *http.Request, config XPaddingConfig) {
|
||||
if req == nil {
|
||||
return
|
||||
}
|
||||
if req.Header == nil {
|
||||
req.Header = make(http.Header)
|
||||
}
|
||||
|
||||
placement := config.Placement.Placement
|
||||
|
||||
if placement == PlacementHeader || placement == PlacementQueryInHeader {
|
||||
c.ApplyXPaddingToHeader(req.Header, config)
|
||||
return
|
||||
}
|
||||
|
||||
paddingValue := GeneratePadding(config.Method, config.Length)
|
||||
|
||||
switch placement {
|
||||
case PlacementCookie:
|
||||
ApplyPaddingToCookie(req, config.Placement.Key, paddingValue)
|
||||
case PlacementQuery:
|
||||
ApplyPaddingToQuery(req.URL, config.Placement.Key, paddingValue)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) ExtractXPaddingFromRequest(req *http.Request, obfsMode bool) (string, string) {
|
||||
if req == nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
if !obfsMode {
|
||||
referrer := req.Header.Get("Referer")
|
||||
|
||||
if referrer != "" {
|
||||
if referrerURL, err := url.Parse(referrer); err == nil {
|
||||
paddingValue := referrerURL.Query().Get("x_padding")
|
||||
paddingPlacement := PlacementQueryInHeader + "=Referer, key=x_padding"
|
||||
return paddingValue, paddingPlacement
|
||||
}
|
||||
} else {
|
||||
paddingValue := req.URL.Query().Get("x_padding")
|
||||
return paddingValue, PlacementQuery + ", key=x_padding"
|
||||
}
|
||||
}
|
||||
|
||||
key := c.XPaddingKey
|
||||
header := c.XPaddingHeader
|
||||
|
||||
if cookie, err := req.Cookie(key); err == nil {
|
||||
if cookie != nil && cookie.Value != "" {
|
||||
paddingValue := cookie.Value
|
||||
paddingPlacement := PlacementCookie + ", key=" + key
|
||||
return paddingValue, paddingPlacement
|
||||
}
|
||||
}
|
||||
|
||||
headerValue := req.Header.Get(header)
|
||||
|
||||
if headerValue != "" {
|
||||
if c.XPaddingPlacement == PlacementHeader {
|
||||
paddingPlacement := PlacementHeader + "=" + header
|
||||
return headerValue, paddingPlacement
|
||||
}
|
||||
|
||||
if parsedURL, err := url.Parse(headerValue); err == nil {
|
||||
paddingPlacement := PlacementQueryInHeader + "=" + header + ", key=" + key
|
||||
|
||||
return parsedURL.Query().Get(key), paddingPlacement
|
||||
}
|
||||
}
|
||||
|
||||
queryValue := req.URL.Query().Get(key)
|
||||
|
||||
if queryValue != "" {
|
||||
paddingPlacement := PlacementQuery + ", key=" + key
|
||||
return queryValue, paddingPlacement
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (c *Config) IsPaddingValid(paddingValue string, from, to int32, method PaddingMethod) bool {
|
||||
if paddingValue == "" {
|
||||
return false
|
||||
}
|
||||
if to <= 0 {
|
||||
r := c.GetNormalizedXPaddingBytes()
|
||||
from, to = r.From, r.To
|
||||
}
|
||||
|
||||
switch method {
|
||||
case PaddingMethodRepeatX:
|
||||
n := int32(len(paddingValue))
|
||||
return n >= from && n <= to
|
||||
case PaddingMethodTokenish:
|
||||
const tolerance = int32(validationTolerance)
|
||||
|
||||
n := int32(hpack.HuffmanEncodeLength(paddingValue))
|
||||
f := from - tolerance
|
||||
t := to + tolerance
|
||||
if f < 0 {
|
||||
f = 0
|
||||
}
|
||||
return n >= f && n <= t
|
||||
default:
|
||||
n := int32(len(paddingValue))
|
||||
return n >= from && n <= to
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,8 @@ func dnsQuery(server string, domain string, sockopt *internet.SocketConfig) ([]b
|
||||
}
|
||||
req.Header.Set("Accept", "application/dns-message")
|
||||
req.Header.Set("Content-Type", "application/dns-message")
|
||||
req.Header.Set("X-Padding", strings.Repeat("X", int(crypto.RandBetween(100, 1000))))
|
||||
req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000)))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
|
||||
Reference in New Issue
Block a user