Browse Source

Host name generator changes
- psiphond: use host name generator in meek server
- Replace HostNameTranformer interface with registered function

Rod Hynes 9 years ago
parent
commit
831d79e769

+ 38 - 0
psiphon/common/hostNameGenerator.go

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, 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 registeredHostNameGenerator atomic.Value
+
+func RegisterHostNameGenerator(generator func() string) {
+	registeredHostNameGenerator.Store(generator)
+}
+
+func GenerateHostName() string {
+	generator := registeredHostNameGenerator.Load()
+	if generator != nil {
+		return generator.(func() string)()
+	}
+	return "www.example.org"
+}

+ 11 - 5
psiphon/config.go

@@ -77,6 +77,8 @@ const (
 	IMPAIRED_PROTOCOL_CLASSIFICATION_DURATION            = 2 * time.Minute
 	IMPAIRED_PROTOCOL_CLASSIFICATION_THRESHOLD           = 3
 	TOTAL_BYTES_TRANSFERRED_NOTICE_PERIOD                = 5 * time.Minute
+	TRANSFORM_HOST_NAMES_ALWAYS                          = "always"
+	TRANSFORM_HOST_NAMES_NEVER                           = "never"
 )
 
 // To distinguish omitted timeout params from explicit 0 value timeout
@@ -233,9 +235,10 @@ type Config struct {
 	// This parameter is only applicable to library deployments.
 	DnsServerGetter DnsServerGetter
 
-	// HostNameTransformer is an interface that enables pluggable hostname
-	// transformation circumvention strategies.
-	HostNameTransformer HostNameTransformer
+	// TransformHostNames specifies whether to use hostname transformation circumvention
+	// strategies. Set to "always" to always transform, "never" to never transform, and
+	// "", the default, for the default transformation strategy.
+	TransformHostNames string
 
 	// TargetServerEntry is an encoded server entry. When specified, this server entry
 	// is used exclusively and all other known servers are ignored.
@@ -487,9 +490,12 @@ func LoadConfig(configJson []byte) (*Config, error) {
 			errors.New("DnsServerGetter interface must be set at runtime"))
 	}
 
-	if config.HostNameTransformer != nil {
+	if !common.Contains(
+		[]string{"", TRANSFORM_HOST_NAMES_ALWAYS, TRANSFORM_HOST_NAMES_NEVER},
+		config.TransformHostNames) {
+
 		return nil, common.ContextError(
-			errors.New("HostNameTransformer interface must be set at runtime"))
+			errors.New("invalid TransformHostNames"))
 	}
 
 	if !common.Contains(

+ 0 - 5
psiphon/controller.go

@@ -80,11 +80,6 @@ func NewController(config *Config) (controller *Controller, err error) {
 	// Needed by regen, at least
 	rand.Seed(int64(time.Now().Nanosecond()))
 
-	// Supply a default HostNameTransformer
-	if config.HostNameTransformer == nil {
-		config.HostNameTransformer = &IdentityHostNameTransformer{}
-	}
-
 	// Generate a session ID for the Psiphon server API. This session ID is
 	// used across all tunnels established by the controller.
 	sessionId, err := MakeSessionId()

+ 24 - 22
psiphon/controller_test.go

@@ -102,7 +102,7 @@ func TestUntunneledUpgradeDownload(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -119,7 +119,7 @@ func TestUntunneledResumableUpgradeDownload(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           true,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -136,7 +136,7 @@ func TestUntunneledUpgradeClientIsLatestVersion(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -153,7 +153,7 @@ func TestUntunneledResumableFetchRemoteServerList(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           true,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -170,7 +170,7 @@ func TestTunneledUpgradeClientIsLatestVersion(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -195,7 +195,7 @@ func TestImpairedProtocols(t *testing.T) {
 			tunnelPoolSize:           40,
 			useUpstreamProxy:         false,
 			disruptNetwork:           true,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              1 * time.Minute,
 		})
 }
@@ -212,7 +212,7 @@ func TestSSH(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -229,7 +229,7 @@ func TestObfuscatedSSH(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -246,7 +246,7 @@ func TestUnfrontedMeek(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -263,7 +263,7 @@ func TestUnfrontedMeekWithTransformer(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   true,
+			transformHostNames:       true,
 			runDuration:              0,
 		})
 }
@@ -280,7 +280,7 @@ func TestFrontedMeek(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -297,7 +297,7 @@ func TestFrontedMeekWithTransformer(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   true,
+			transformHostNames:       true,
 			runDuration:              0,
 		})
 }
@@ -314,7 +314,7 @@ func TestFrontedMeekHTTP(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -331,7 +331,7 @@ func TestUnfrontedMeekHTTPS(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -348,7 +348,7 @@ func TestUnfrontedMeekHTTPSWithTransformer(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   true,
+			transformHostNames:       true,
 			runDuration:              0,
 		})
 }
@@ -365,7 +365,7 @@ func TestDisabledApi(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         false,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -382,7 +382,7 @@ func TestObfuscatedSSHWithUpstreamProxy(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         true,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -399,7 +399,7 @@ func TestUnfrontedMeekWithUpstreamProxy(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         true,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -416,7 +416,7 @@ func TestUnfrontedMeekHTTPSWithUpstreamProxy(t *testing.T) {
 			tunnelPoolSize:           1,
 			useUpstreamProxy:         true,
 			disruptNetwork:           false,
-			useHostNameTransformer:   false,
+			transformHostNames:       false,
 			runDuration:              0,
 		})
 }
@@ -431,7 +431,7 @@ type controllerRunConfig struct {
 	tunnelPoolSize           int
 	useUpstreamProxy         bool
 	disruptNetwork           bool
-	useHostNameTransformer   bool
+	transformHostNames       bool
 	runDuration              time.Duration
 }
 
@@ -489,8 +489,10 @@ func controllerRun(t *testing.T, runConfig *controllerRunConfig) {
 		config.UpstreamProxyCustomHeaders = upstreamProxyCustomHeaders
 	}
 
-	if runConfig.useHostNameTransformer {
-		config.HostNameTransformer = &TestHostNameTransformer{}
+	if runConfig.transformHostNames {
+		config.TransformHostNames = "always"
+	} else {
+		config.TransformHostNames = "never"
 	}
 
 	// Override client retry throttle values to speed up automated

+ 2 - 2
psiphon/meekConn.go

@@ -84,8 +84,8 @@ type MeekConfig struct {
 	// HostHeader is the value to place in the HTTP request Host header.
 	HostHeader string
 
-	// TransformedHostName records whether a HostNameTransformer
-	// transformation is in effect. This value is used for stats reporting.
+	// TransformedHostName records whether a hostname transformation is
+	// in effect. This value is used for stats reporting.
 	TransformedHostName bool
 
 	// The following values are used to create the obfuscated meek cookie.

+ 0 - 14
psiphon/net.go

@@ -123,20 +123,6 @@ type DnsServerGetter interface {
 	GetSecondaryDnsServer() string
 }
 
-// HostNameTransformer defines the interface for pluggable hostname
-// transformation circumvention strategies.
-type HostNameTransformer interface {
-	TransformHostName(hostname string) (string, bool)
-}
-
-// IdentityHostNameTransformer is the default HostNameTransformer, which
-// returns the hostname unchanged.
-type IdentityHostNameTransformer struct{}
-
-func (IdentityHostNameTransformer) TransformHostName(hostname string) (string, bool) {
-	return hostname, false
-}
-
 // TimeoutError implements the error interface
 type TimeoutError struct{}
 

+ 0 - 14
psiphon/server/config.go

@@ -157,12 +157,6 @@ type Config struct {
 	// meek protocols run by this server instance.
 	MeekObfuscatedKey string
 
-	// MeekCertificateCommonName is the value used for the hostname
-	// in the self-signed certificate generated and used for meek
-	// HTTPS modes. The same value is used for all HTTPS meek
-	// protocols.
-	MeekCertificateCommonName string
-
 	// MeekProhibitedHeaders is a list of HTTP headers to check for
 	// in client requests. If one of these headers is found, the
 	// request fails. This is used to defend against abuse.
@@ -306,13 +300,6 @@ func LoadConfig(configJSON []byte) (*Config, error) {
 					tunnelProtocol)
 			}
 		}
-		if protocol.TunnelProtocolUsesMeekHTTPS(tunnelProtocol) {
-			if config.MeekCertificateCommonName == "" {
-				return nil, fmt.Errorf(
-					"Tunnel protocol %s requires MeekCertificateCommonName",
-					tunnelProtocol)
-			}
-		}
 	}
 
 	if config.UDPInterceptUdpgwServerAddress != "" {
@@ -521,7 +508,6 @@ func GenerateConfig(params *GenerateConfigParams) ([]byte, []byte, []byte, error
 		UDPInterceptUdpgwServerAddress: "127.0.0.1:7300",
 		MeekCookieEncryptionPrivateKey: meekCookieEncryptionPrivateKey,
 		MeekObfuscatedKey:              meekObfuscatedKey,
-		MeekCertificateCommonName:      "www.example.org",
 		MeekProhibitedHeaders:          nil,
 		MeekProxyForwardedForHeaders:   []string{"X-Forwarded-For"},
 		LoadMonitorPeriodSeconds:       300,

+ 1 - 2
psiphon/server/meek.go

@@ -474,8 +474,7 @@ func makeMeekTLSConfig(
 	support *SupportServices,
 	useObfuscatedSessionTickets bool) (*tls.Config, error) {
 
-	certificate, privateKey, err := GenerateWebServerCertificate(
-		support.Config.MeekCertificateCommonName)
+	certificate, privateKey, err := GenerateWebServerCertificate(common.GenerateHostName())
 	if err != nil {
 		return nil, common.ContextError(err)
 	}

+ 24 - 5
psiphon/tunnel.go

@@ -445,6 +445,16 @@ func selectFrontingParameters(
 	return
 }
 
+func doMeekTransformHostName(config *Config) bool {
+	switch config.TransformHostNames {
+	case TRANSFORM_HOST_NAMES_ALWAYS:
+		return true
+	case TRANSFORM_HOST_NAMES_NEVER:
+		return false
+	}
+	return common.FlipCoin()
+}
+
 // initMeekConfig is a helper that creates a MeekConfig suitable for the
 // selected meek tunnel protocol.
 func initMeekConfig(
@@ -472,8 +482,11 @@ func initMeekConfig(
 		dialAddress = fmt.Sprintf("%s:443", frontingAddress)
 		useHTTPS = true
 		if !serverEntry.MeekFrontingDisableSNI {
-			SNIServerName, transformedHostName =
-				config.HostNameTransformer.TransformHostName(frontingAddress)
+			SNIServerName = frontingAddress
+			if doMeekTransformHostName(config) {
+				SNIServerName = common.GenerateHostName()
+				transformedHostName = true
+			}
 		}
 		hostHeader = frontingHost
 
@@ -490,7 +503,10 @@ func initMeekConfig(
 
 		dialAddress = fmt.Sprintf("%s:%d", serverEntry.IpAddress, serverEntry.MeekServerPort)
 		hostname := serverEntry.IpAddress
-		hostname, transformedHostName = config.HostNameTransformer.TransformHostName(hostname)
+		if doMeekTransformHostName(config) {
+			hostname = common.GenerateHostName()
+			transformedHostName = true
+		}
 		if serverEntry.MeekServerPort == 80 {
 			hostHeader = hostname
 		} else {
@@ -505,8 +521,11 @@ func initMeekConfig(
 		if selectedProtocol == protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET {
 			useObfuscatedSessionTickets = true
 		}
-		SNIServerName, transformedHostName =
-			config.HostNameTransformer.TransformHostName(serverEntry.IpAddress)
+		SNIServerName = serverEntry.IpAddress
+		if doMeekTransformHostName(config) {
+			SNIServerName = common.GenerateHostName()
+			transformedHostName = true
+		}
 		if serverEntry.MeekServerPort == 443 {
 			hostHeader = serverEntry.IpAddress
 		} else {