XHTTP, WS, HU: Forbid "host" in headers, read serverName instead (#4142)

WebSocket's config files should be updated ASAP.
This commit is contained in:
RPRX 2024-12-11 00:58:14 +00:00 committed by GitHub
parent 9cb6816383
commit a2b773135a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 61 additions and 45 deletions

View file

@ -204,9 +204,7 @@ func getConfig() string {
"security": "none", "security": "none",
"wsSettings": { "wsSettings": {
"path": "/?ed=2048", "path": "/?ed=2048",
"headers": { "host": "bing.com"
"Host": "bing.com"
}
} }
} }
} }

View file

@ -163,13 +163,13 @@ func (c *WebSocketConfig) Build() (proto.Message, error) {
path = u.String() path = u.String()
} }
} }
// If http host is not set in the Host field, but in headers field, we add it to Host Field here. // Priority (client): host > serverName > address
// If we don't do that, http host will be overwritten as address. for k, v := range c.Headers {
// Host priority: Host field > headers field > address. errors.PrintDeprecatedFeatureWarning(`"host" in "headers"`, `independent "host"`)
if c.Host == "" && c.Headers["host"] != "" { if c.Host == "" {
c.Host = c.Headers["host"] c.Host = v
} else if c.Host == "" && c.Headers["Host"] != "" { }
c.Host = c.Headers["Host"] delete(c.Headers, k)
} }
config := &websocket.Config{ config := &websocket.Config{
Path: path, Path: path,
@ -202,15 +202,11 @@ func (c *HttpUpgradeConfig) Build() (proto.Message, error) {
path = u.String() path = u.String()
} }
} }
// If http host is not set in the Host field, but in headers field, we add it to Host Field here. // Priority (client): host > serverName > address
// If we don't do that, http host will be overwritten as address. for k := range c.Headers {
// Host priority: Host field > headers field > address. if strings.ToLower(k) == "host" {
if c.Host == "" && c.Headers["host"] != "" { return nil, errors.New(`"headers" can't contain "host"`)
c.Host = c.Headers["host"] }
delete(c.Headers, "host")
} else if c.Host == "" && c.Headers["Host"] != "" {
c.Host = c.Headers["Host"]
delete(c.Headers, "Host")
} }
config := &httpupgrade.Config{ config := &httpupgrade.Config{
Path: path, Path: path,
@ -274,13 +270,11 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
c = &extra c = &extra
} }
// If http host is not set in the Host field, but in headers field, we add it to Host Field here. // Priority (client): host > serverName > address
// If we don't do that, http host will be overwritten as address. for k := range c.Headers {
// Host priority: Host field > headers field > address. if strings.ToLower(k) == "host" {
if c.Host == "" && c.Headers["host"] != "" { return nil, errors.New(`"headers" can't contain "host"`)
c.Host = c.Headers["host"] }
} else if c.Host == "" && c.Headers["Host"] != "" {
c.Host = c.Headers["Host"]
} }
if c.Xmux.MaxConnections != nil && c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency != nil && c.Xmux.MaxConcurrency.To > 0 { if c.Xmux.MaxConnections != nil && c.Xmux.MaxConnections.To > 0 && c.Xmux.MaxConcurrency != nil && c.Xmux.MaxConcurrency.To > 0 {

View file

@ -48,9 +48,7 @@ func TestXrayConfig(t *testing.T) {
"streamSettings": { "streamSettings": {
"network": "ws", "network": "ws",
"wsSettings": { "wsSettings": {
"headers": { "host": "example.domain",
"host": "example.domain"
},
"path": "" "path": ""
}, },
"tlsSettings": { "tlsSettings": {
@ -139,9 +137,6 @@ func TestXrayConfig(t *testing.T) {
ProtocolName: "websocket", ProtocolName: "websocket",
Settings: serial.ToTypedMessage(&websocket.Config{ Settings: serial.ToTypedMessage(&websocket.Config{
Host: "example.domain", Host: "example.domain",
Header: map[string]string{
"host": "example.domain",
},
}), }),
}, },
}, },

View file

@ -53,9 +53,10 @@ func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *
var conn net.Conn var conn net.Conn
var requestURL url.URL var requestURL url.URL
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { tConfig := tls.ConfigFromStreamSettings(streamSettings)
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) if tConfig != nil {
if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil { tlsConfig := tConfig.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1"))
if fingerprint := tls.GetFingerprint(tConfig.Fingerprint); fingerprint != nil {
conn = tls.UClient(pconn, tlsConfig, fingerprint) conn = tls.UClient(pconn, tlsConfig, fingerprint)
if err := conn.(*tls.UConn).WebsocketHandshakeContext(ctx); err != nil { if err := conn.(*tls.UConn).WebsocketHandshakeContext(ctx); err != nil {
return nil, err return nil, err
@ -69,12 +70,17 @@ func dialhttpUpgrade(ctx context.Context, dest net.Destination, streamSettings *
requestURL.Scheme = "http" requestURL.Scheme = "http"
} }
requestURL.Host = dest.NetAddr() requestURL.Host = transportConfiguration.Host
if requestURL.Host == "" && tConfig != nil {
requestURL.Host = tConfig.ServerName
}
if requestURL.Host == "" {
requestURL.Host = dest.Address.String()
}
requestURL.Path = transportConfiguration.GetNormalizedPath() requestURL.Path = transportConfiguration.GetNormalizedPath()
req := &http.Request{ req := &http.Request{
Method: http.MethodGet, Method: http.MethodGet,
URL: &requestURL, URL: &requestURL,
Host: transportConfiguration.Host,
Header: make(http.Header), Header: make(http.Header),
} }
for key, value := range transportConfiguration.Header { for key, value := range transportConfiguration.Header {

View file

@ -259,8 +259,14 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
requestURL.Scheme = "http" requestURL.Scheme = "http"
} }
requestURL.Host = transportConfiguration.Host requestURL.Host = transportConfiguration.Host
if requestURL.Host == "" && tlsConfig != nil {
requestURL.Host = tlsConfig.ServerName
}
if requestURL.Host == "" && realityConfig != nil {
requestURL.Host = realityConfig.ServerName
}
if requestURL.Host == "" { if requestURL.Host == "" {
requestURL.Host = dest.NetAddr() requestURL.Host = dest.Address.String()
} }
sessionIdUuid := uuid.New() sessionIdUuid := uuid.New()
@ -279,16 +285,25 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
} }
globalDialerAccess.Unlock() globalDialerAccess.Unlock()
memory2 := streamSettings.DownloadSettings memory2 := streamSettings.DownloadSettings
httpClient2, muxRes2 = getHTTPClient(ctx, *memory2.Destination, memory2) // just panic dest2 := *memory2.Destination // just panic
if tls.ConfigFromStreamSettings(memory2) != nil || reality.ConfigFromStreamSettings(memory2) != nil { httpClient2, muxRes2 = getHTTPClient(ctx, dest2, memory2)
tlsConfig2 := tls.ConfigFromStreamSettings(memory2)
realityConfig2 := reality.ConfigFromStreamSettings(memory2)
if tlsConfig2 != nil || realityConfig2 != nil {
requestURL2.Scheme = "https" requestURL2.Scheme = "https"
} else { } else {
requestURL2.Scheme = "http" requestURL2.Scheme = "http"
} }
config2 := memory2.ProtocolSettings.(*Config) config2 := memory2.ProtocolSettings.(*Config)
requestURL2.Host = config2.Host requestURL2.Host = config2.Host
if requestURL2.Host == "" && tlsConfig2 != nil {
requestURL2.Host = tlsConfig2.ServerName
}
if requestURL2.Host == "" && realityConfig2 != nil {
requestURL2.Host = realityConfig2.ServerName
}
if requestURL2.Host == "" { if requestURL2.Host == "" {
requestURL2.Host = memory2.Destination.NetAddr() requestURL2.Host = dest2.Address.String()
} }
requestURL2.Path = config2.GetNormalizedPath() + sessionIdUuid.String() requestURL2.Path = config2.GetNormalizedPath() + sessionIdUuid.String()
requestURL2.RawQuery = config2.GetNormalizedQuery() requestURL2.RawQuery = config2.GetNormalizedQuery()

View file

@ -23,7 +23,6 @@ func (c *Config) GetRequestHeader() http.Header {
for k, v := range c.Header { for k, v := range c.Header {
header.Add(k, v) header.Add(k, v)
} }
header.Set("Host", c.Host)
return header return header
} }

View file

@ -58,11 +58,12 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
protocol := "ws" protocol := "ws"
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { tConfig := tls.ConfigFromStreamSettings(streamSettings)
if tConfig != nil {
protocol = "wss" protocol = "wss"
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1")) tlsConfig := tConfig.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("http/1.1"))
dialer.TLSClientConfig = tlsConfig dialer.TLSClientConfig = tlsConfig
if fingerprint := tls.GetFingerprint(config.Fingerprint); fingerprint != nil { if fingerprint := tls.GetFingerprint(tConfig.Fingerprint); fingerprint != nil {
dialer.NetDialTLSContext = func(_ context.Context, _, addr string) (gonet.Conn, error) { dialer.NetDialTLSContext = func(_ context.Context, _, addr string) (gonet.Conn, error) {
// Like the NetDial in the dialer // Like the NetDial in the dialer
pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings) pconn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
@ -103,6 +104,14 @@ func dialWebSocket(ctx context.Context, dest net.Destination, streamSettings *in
} }
header := wsSettings.GetRequestHeader() header := wsSettings.GetRequestHeader()
// See dialer.DialContext()
header.Set("Host", wsSettings.Host)
if header.Get("Host") == "" && tConfig != nil {
header.Set("Host", tConfig.ServerName)
}
if header.Get("Host") == "" {
header.Set("Host", dest.Address.String())
}
if ed != nil { if ed != nil {
// RawURLEncoding is support by both V2Ray/V2Fly and XRay. // RawURLEncoding is support by both V2Ray/V2Fly and XRay.
header.Set("Sec-WebSocket-Protocol", base64.RawURLEncoding.EncodeToString(ed)) header.Set("Sec-WebSocket-Protocol", base64.RawURLEncoding.EncodeToString(ed))