Просмотр исходного кода

Enable pluggable user agent selection

Michael Goldberger 9 лет назад
Родитель
Сommit
446e1d588b
5 измененных файлов с 71 добавлено и 5 удалено
  1. 38 0
      psiphon/common/userAgentPicker.go
  2. 0 4
      psiphon/meekConn.go
  3. 2 1
      psiphon/notice.go
  4. 4 0
      psiphon/serverApi.go
  5. 27 0
      psiphon/tunnel.go

+ 38 - 0
psiphon/common/userAgentPicker.go

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package common
+
+import (
+	"sync/atomic"
+)
+
+var registeredUserAgentPicker atomic.Value
+
+func RegisterUserAgentPicker(generator func() string) {
+	registeredUserAgentPicker.Store(generator)
+}
+
+func PickUserAgent() string {
+	generator := registeredUserAgentPicker.Load()
+	if generator != nil {
+		return generator.(func() string)()
+	}
+	return ""
+}

+ 0 - 4
psiphon/meekConn.go

@@ -582,10 +582,6 @@ func (meek *MeekConn) roundTrip(sendPayload []byte) (io.ReadCloser, error) {
 			break
 		}
 
-		// Don't use the default user agent ("Go 1.1 package http").
-		// For now, just omit the header (net/http/request.go: "may be blank to not send the header").
-		request.Header.Set("User-Agent", "")
-
 		request.Header.Set("Content-Type", "application/octet-stream")
 
 		// Set additional headers to the HTTP request using the same method we use for adding

+ 2 - 1
psiphon/notice.go

@@ -351,7 +351,8 @@ func NoticeConnectedTunnelDialStats(ipAddress string, tunnelDialStats *TunnelDia
 		"meekResolvedIPAddress", tunnelDialStats.MeekResolvedIPAddress,
 		"meekSNIServerName", tunnelDialStats.MeekSNIServerName,
 		"meekHostHeader", tunnelDialStats.MeekHostHeader,
-		"meekTransformedHostName", tunnelDialStats.MeekTransformedHostName)
+		"meekTransformedHostName", tunnelDialStats.MeekTransformedHostName,
+		"userAgent", tunnelDialStats.UserAgent)
 }
 
 // NoticeBuildInfo reports build version info.

+ 4 - 0
psiphon/serverApi.go

@@ -833,6 +833,10 @@ func (serverContext *ServerContext) getBaseParams() requestJSONObject {
 			transformedHostName = "1"
 		}
 		params["meek_transformed_host_name"] = transformedHostName
+
+		if tunnel.dialStats.HasUserAgent {
+			params["user_agent"] = tunnel.dialStats.UserAgent
+		}
 	}
 
 	if tunnel.serverEntry.Region != "" {

+ 27 - 0
psiphon/tunnel.go

@@ -103,6 +103,8 @@ type TunnelDialStats struct {
 	MeekSNIServerName              string
 	MeekHostHeader                 string
 	MeekTransformedHostName        bool
+	HasUserAgent                   bool
+	UserAgent                      string
 }
 
 // EstablishTunnel first makes a network transport connection to the
@@ -621,6 +623,23 @@ func dialSsh(
 		resolvedIPAddress.Store(IPAddress)
 	}
 
+	// If "User-Agent" is not already present in the custom headers,
+	// flip a coin, either pick a user agent or omit the header entirely.
+	hasUserAgent := false
+	if _, ok := config.UpstreamProxyCustomHeaders["User-Agent"]; !ok {
+		if config.UpstreamProxyCustomHeaders == nil {
+			config.UpstreamProxyCustomHeaders = make(map[string][]string)
+		}
+
+		if common.FlipCoin() {
+			config.UpstreamProxyCustomHeaders.Set("User-Agent", common.PickUserAgent())
+		} else {
+			config.UpstreamProxyCustomHeaders.Set("User-Agent", "")
+		}
+
+		hasUserAgent = true
+	}
+
 	// Create the base transport: meek or direct connection
 	dialConfig := &DialConfig{
 		UpstreamProxyUrl:              config.UpstreamProxyUrl,
@@ -754,6 +773,11 @@ func dialSsh(
 	if dialConfig.UpstreamProxyUrl != "" || meekConfig != nil {
 		dialStats = &TunnelDialStats{}
 
+		if hasUserAgent {
+			dialStats.HasUserAgent = true
+			dialStats.UserAgent = dialConfig.UpstreamProxyCustomHeaders.Get("User-Agent")
+		}
+
 		if dialConfig.UpstreamProxyUrl != "" {
 
 			// Note: UpstreamProxyUrl should have parsed correctly in the dial
@@ -764,6 +788,9 @@ func dialSsh(
 
 			dialStats.UpstreamProxyCustomHeaderNames = make([]string, 0)
 			for name, _ := range dialConfig.UpstreamProxyCustomHeaders {
+				if hasUserAgent && name == "User-Agent" {
+					continue
+				}
 				dialStats.UpstreamProxyCustomHeaderNames = append(dialStats.UpstreamProxyCustomHeaderNames, name)
 			}
 		}