Selaa lähdekoodia

feat: set default User-Agent to Chrome with dynamic version based on date

Co-authored-by: RPRX <[email protected]>
copilot-swe-agent[bot] 4 kuukautta sitten
vanhempi
sitoutus
06fd58d8f8

+ 2 - 0
app/dns/nameserver_doh.go

@@ -18,6 +18,7 @@ import (
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/common/net/cnc"
 	"github.com/xtls/xray-core/common/protocol/dns"
+	http_proto "github.com/xtls/xray-core/common/protocol/http"
 	"github.com/xtls/xray-core/common/session"
 	"github.com/xtls/xray-core/common/utils"
 	dns_feature "github.com/xtls/xray-core/features/dns"
@@ -214,6 +215,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("User-Agent", http_proto.ChromeUA())
 	req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000)))
 
 	hc := s.httpClient

+ 3 - 0
app/observatory/burst/ping.go

@@ -6,6 +6,8 @@ import (
 	"net/http"
 	"time"
 
+	http_proto "github.com/xtls/xray-core/common/protocol/http"
+
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/features/routing"
 	"github.com/xtls/xray-core/transport/internet/tagged"
@@ -61,6 +63,7 @@ func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) {
 	if err != nil {
 		return rttFailed, err
 	}
+	req.Header.Set("User-Agent", http_proto.ChromeUA())
 
 	start := time.Now()
 	resp, err := s.httpClient.Do(req)

+ 10 - 0
common/protocol/http/headers.go

@@ -4,10 +4,20 @@ import (
 	"net/http"
 	"strconv"
 	"strings"
+	"time"
 
 	"github.com/xtls/xray-core/common/net"
 )
 
+// ChromeUA generates a Chrome browser User-Agent string.
+// The version number changes monthly, starting from 143 in January 2026.
+func ChromeUA() string {
+	t := time.Now()
+	majorVersion := 143 + (t.Year()-2026)*12 + int(t.Month()) - 1
+	return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" +
+		strconv.Itoa(majorVersion) + ".0.0.0 Safari/537.36"
+}
+
 // ParseXForwardedFor parses X-Forwarded-For header in http headers, and return the IP list in it.
 func ParseXForwardedFor(header http.Header) []net.Address {
 	xff := header.Get("X-Forwarded-For")

+ 13 - 0
common/protocol/http/headers_test.go

@@ -115,3 +115,16 @@ func TestParseHost(t *testing.T) {
 		}
 	}
 }
+
+func TestChromeUA(t *testing.T) {
+ua := ChromeUA()
+if !strings.Contains(ua, "Chrome/") {
+t.Error("ChromeUA should contain Chrome/ identifier")
+}
+if !strings.Contains(ua, "Mozilla/5.0") {
+t.Error("ChromeUA should contain Mozilla/5.0 prefix")
+}
+if !strings.HasSuffix(ua, "Safari/537.36") {
+t.Error("ChromeUA should end with Safari/537.36")
+}
+}

+ 3 - 5
infra/conf/transport_authenticators.go

@@ -4,6 +4,7 @@ import (
 	"sort"
 
 	"github.com/xtls/xray-core/common/errors"
+	http_proto "github.com/xtls/xray-core/common/protocol/http"
 	"github.com/xtls/xray-core/transport/internet/headers/http"
 	"github.com/xtls/xray-core/transport/internet/headers/noop"
 	"google.golang.org/protobuf/proto"
@@ -40,11 +41,8 @@ func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) {
 				Value: []string{"www.baidu.com", "www.bing.com"},
 			},
 			{
-				Name: "User-Agent",
-				Value: []string{
-					"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
-					"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46",
-				},
+				Name:  "User-Agent",
+				Value: []string{http_proto.ChromeUA()},
 			},
 			{
 				Name:  "Accept-Encoding",

+ 1 - 1
proxy/http/server.go

@@ -229,7 +229,7 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
 
 	// Prevent UA from being set to golang's default ones
 	if request.Header.Get("User-Agent") == "" {
-		request.Header.Set("User-Agent", "")
+		request.Header.Set("User-Agent", http_proto.ChromeUA())
 	}
 
 	content := &session.Content{

+ 2 - 1
transport/internet/reality/reality.go

@@ -27,6 +27,7 @@ import (
 	"github.com/xtls/xray-core/common/crypto"
 	"github.com/xtls/xray-core/common/errors"
 	"github.com/xtls/xray-core/common/net"
+	http_proto "github.com/xtls/xray-core/common/protocol/http"
 	"github.com/xtls/xray-core/core"
 	"github.com/xtls/xray-core/transport/internet/tls"
 	"golang.org/x/crypto/hkdf"
@@ -222,7 +223,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
 				if req == nil {
 					return
 				}
-				req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map
+				req.Header.Set("User-Agent", http_proto.ChromeUA())
 				if first && config.Show {
 					fmt.Printf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())
 				}

+ 2 - 0
transport/internet/tls/ech.go

@@ -27,6 +27,7 @@ import (
 	"github.com/xtls/reality/hpke"
 	"github.com/xtls/xray-core/common/errors"
 	"github.com/xtls/xray-core/common/net"
+	http_proto "github.com/xtls/xray-core/common/protocol/http"
 	"github.com/xtls/xray-core/common/utils"
 	"github.com/xtls/xray-core/transport/internet"
 	"golang.org/x/crypto/cryptobyte"
@@ -257,6 +258,7 @@ 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("User-Agent", http_proto.ChromeUA())
 		req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000)))
 
 		resp, err := client.Do(req)