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

Add SSHClientVersionPicker

- Configures on-the-wire client version for
  "SSH" protocol
- Report new field in notices and stats
- Now a tunnel always has a dialStats
- Refactored noticeServerDialStats to only
  relevant emit fields
Rod Hynes 9 лет назад
Родитель
Сommit
5fcd3533c0
4 измененных файлов с 166 добавлено и 77 удалено
  1. 54 22
      psiphon/notice.go
  2. 36 25
      psiphon/serverApi.go
  3. 38 0
      psiphon/sshClientVersionPicker.go
  4. 38 30
      psiphon/tunnel.go

+ 54 - 22
psiphon/notice.go

@@ -168,29 +168,61 @@ func NoticeAvailableEgressRegions(regions []string) {
 }
 }
 
 
 func noticeServerDialStats(noticeType, ipAddress, region, protocol string, tunnelDialStats *TunnelDialStats) {
 func noticeServerDialStats(noticeType, ipAddress, region, protocol string, tunnelDialStats *TunnelDialStats) {
-	if tunnelDialStats != nil {
-		outputNotice(noticeType, noticeIsDiagnostic,
-			"ipAddress", ipAddress,
-			"region", region,
-			"protocol", protocol,
-			"upstreamProxyType", tunnelDialStats.UpstreamProxyType,
-			"upstreamProxyCustomHeaderNames", strings.Join(tunnelDialStats.UpstreamProxyCustomHeaderNames, ","),
-			"meekDialAddress", tunnelDialStats.MeekDialAddress,
-			"meekDialAddress", tunnelDialStats.MeekDialAddress,
-			"meekResolvedIPAddress", tunnelDialStats.MeekResolvedIPAddress,
-			"meekSNIServerName", tunnelDialStats.MeekSNIServerName,
-			"meekHostHeader", tunnelDialStats.MeekHostHeader,
-			"meekTransformedHostName", tunnelDialStats.MeekTransformedHostName,
-			"selectedUserAgent", tunnelDialStats.SelectedUserAgent,
-			"userAgent", tunnelDialStats.UserAgent,
-			"selectedTLSProfile", tunnelDialStats.SelectedTLSProfile,
-			"TLSProfile", tunnelDialStats.TLSProfile)
-	} else {
-		outputNotice(noticeType, noticeIsDiagnostic,
-			"ipAddress", ipAddress,
-			"region", region,
-			"protocol", protocol)
+
+	args := []interface{}{
+		"ipAddress", ipAddress,
+		"region", region,
+		"protocol", protocol,
+	}
+
+	if tunnelDialStats.SelectedSSHClientVersion {
+		args = append(args, "SSHClientVersion", tunnelDialStats.SSHClientVersion)
+	}
+
+	if tunnelDialStats.UpstreamProxyType != "" {
+		args = append(args, "upstreamProxyType", tunnelDialStats.UpstreamProxyType)
+	}
+
+	if tunnelDialStats.UpstreamProxyCustomHeaderNames != nil {
+		args = append(args, "upstreamProxyCustomHeaderNames", strings.Join(tunnelDialStats.UpstreamProxyCustomHeaderNames, ","))
+	}
+
+	if tunnelDialStats.MeekDialAddress != "" {
+		args = append(args, "meekDialAddress", tunnelDialStats.MeekDialAddress)
+	}
+
+	if tunnelDialStats.MeekResolvedIPAddress != "" {
+		args = append(args, "meekResolvedIPAddress", tunnelDialStats.MeekResolvedIPAddress)
 	}
 	}
+
+	if tunnelDialStats.MeekSNIServerName != "" {
+		args = append(args, "meekSNIServerName", tunnelDialStats.MeekSNIServerName)
+	}
+
+	if tunnelDialStats.MeekHostHeader != "" {
+		args = append(args, "meekHostHeader", tunnelDialStats.MeekHostHeader)
+	}
+
+	if tunnelDialStats.MeekDialAddress != "" {
+		transformedHostName := "0"
+		if tunnelDialStats.MeekTransformedHostName {
+			transformedHostName = "1"
+		}
+		args = append(args, "meekTransformedHostName", transformedHostName)
+	}
+
+	if tunnelDialStats.SelectedUserAgent {
+		args = append(args, "userAgent", tunnelDialStats.UserAgent)
+	}
+
+	if tunnelDialStats.SelectedTLSProfile {
+		args = append(args, "TLSProfile", tunnelDialStats.TLSProfile)
+	}
+
+	outputNotice(
+		noticeType,
+		noticeIsDiagnostic,
+		args...)
 }
 }
 
 
 // NoticeConnectingServer reports parameters and details for a single connection attempt
 // NoticeConnectingServer reports parameters and details for a single connection attempt

+ 36 - 25
psiphon/serverApi.go

@@ -825,38 +825,49 @@ func (serverContext *ServerContext) getBaseParams() requestJSONObject {
 	if tunnel.config.DeviceRegion != "" {
 	if tunnel.config.DeviceRegion != "" {
 		params["device_region"] = tunnel.config.DeviceRegion
 		params["device_region"] = tunnel.config.DeviceRegion
 	}
 	}
-	if tunnel.dialStats != nil {
-		if tunnel.dialStats.UpstreamProxyType != "" {
-			params["upstream_proxy_type"] = tunnel.dialStats.UpstreamProxyType
-		}
-		if tunnel.dialStats.UpstreamProxyCustomHeaderNames != nil {
-			params["upstream_proxy_custom_header_names"] = tunnel.dialStats.UpstreamProxyCustomHeaderNames
-		}
-		if tunnel.dialStats.MeekDialAddress != "" {
-			params["meek_dial_address"] = tunnel.dialStats.MeekDialAddress
-		}
-		if tunnel.dialStats.MeekResolvedIPAddress != "" {
-			params["meek_resolved_ip_address"] = tunnel.dialStats.MeekResolvedIPAddress
-		}
-		if tunnel.dialStats.MeekSNIServerName != "" {
-			params["meek_sni_server_name"] = tunnel.dialStats.MeekSNIServerName
-		}
-		if tunnel.dialStats.MeekHostHeader != "" {
-			params["meek_host_header"] = tunnel.dialStats.MeekHostHeader
-		}
+
+	if tunnel.dialStats.SelectedSSHClientVersion {
+		params["ssh_client_version"] = tunnel.dialStats.SSHClientVersion
+	}
+
+	if tunnel.dialStats.UpstreamProxyType != "" {
+		params["upstream_proxy_type"] = tunnel.dialStats.UpstreamProxyType
+	}
+
+	if tunnel.dialStats.UpstreamProxyCustomHeaderNames != nil {
+		params["upstream_proxy_custom_header_names"] = tunnel.dialStats.UpstreamProxyCustomHeaderNames
+	}
+
+	if tunnel.dialStats.MeekDialAddress != "" {
+		params["meek_dial_address"] = tunnel.dialStats.MeekDialAddress
+	}
+
+	if tunnel.dialStats.MeekResolvedIPAddress != "" {
+		params["meek_resolved_ip_address"] = tunnel.dialStats.MeekResolvedIPAddress
+	}
+
+	if tunnel.dialStats.MeekSNIServerName != "" {
+		params["meek_sni_server_name"] = tunnel.dialStats.MeekSNIServerName
+	}
+
+	if tunnel.dialStats.MeekHostHeader != "" {
+		params["meek_host_header"] = tunnel.dialStats.MeekHostHeader
+	}
+
+	if tunnel.dialStats.MeekDialAddress != "" {
 		transformedHostName := "0"
 		transformedHostName := "0"
 		if tunnel.dialStats.MeekTransformedHostName {
 		if tunnel.dialStats.MeekTransformedHostName {
 			transformedHostName = "1"
 			transformedHostName = "1"
 		}
 		}
 		params["meek_transformed_host_name"] = transformedHostName
 		params["meek_transformed_host_name"] = transformedHostName
+	}
 
 
-		if tunnel.dialStats.SelectedUserAgent {
-			params["user_agent"] = tunnel.dialStats.UserAgent
-		}
+	if tunnel.dialStats.SelectedUserAgent {
+		params["user_agent"] = tunnel.dialStats.UserAgent
+	}
 
 
-		if tunnel.dialStats.SelectedTLSProfile {
-			params["tls_profile"] = tunnel.dialStats.TLSProfile
-		}
+	if tunnel.dialStats.SelectedTLSProfile {
+		params["tls_profile"] = tunnel.dialStats.TLSProfile
 	}
 	}
 
 
 	if tunnel.serverEntry.Region != "" {
 	if tunnel.serverEntry.Region != "" {

+ 38 - 0
psiphon/sshClientVersionPicker.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 psiphon
+
+import (
+	"sync/atomic"
+)
+
+var registeredSSHClientVersionPicker atomic.Value
+
+func RegisterSSHClientVersionPicker(picker func() string) {
+	registeredSSHClientVersionPicker.Store(picker)
+}
+
+func pickSSHClientVersion() string {
+	picker := registeredSSHClientVersionPicker.Load()
+	if picker != nil {
+		return picker.(func() string)()
+	}
+	return ""
+}

+ 38 - 30
psiphon/tunnel.go

@@ -96,6 +96,8 @@ type Tunnel struct {
 // For upstream proxy, only proxy type and custom header names are recorded; proxy
 // For upstream proxy, only proxy type and custom header names are recorded; proxy
 // address and custom header values are considered PII.
 // address and custom header values are considered PII.
 type TunnelDialStats struct {
 type TunnelDialStats struct {
+	SelectedSSHClientVersion       bool
+	SSHClientVersion               string
 	UpstreamProxyType              string
 	UpstreamProxyType              string
 	UpstreamProxyCustomHeaderNames []string
 	UpstreamProxyCustomHeaderNames []string
 	MeekDialAddress                string
 	MeekDialAddress                string
@@ -598,6 +600,8 @@ func dialSsh(
 	// The meek protocols tunnel obfuscated SSH. Obfuscated SSH is layered on top of SSH.
 	// The meek protocols tunnel obfuscated SSH. Obfuscated SSH is layered on top of SSH.
 	// So depending on which protocol is used, multiple layers are initialized.
 	// So depending on which protocol is used, multiple layers are initialized.
 
 
+	var selectedSSHClientVersion bool
+	SSHClientVersion := ""
 	useObfuscatedSsh := false
 	useObfuscatedSsh := false
 	dialCustomHeaders := config.CustomHeaders
 	dialCustomHeaders := config.CustomHeaders
 	var directTCPDialAddress string
 	var directTCPDialAddress string
@@ -611,6 +615,8 @@ func dialSsh(
 		directTCPDialAddress = fmt.Sprintf("%s:%d", serverEntry.IpAddress, serverEntry.SshObfuscatedPort)
 		directTCPDialAddress = fmt.Sprintf("%s:%d", serverEntry.IpAddress, serverEntry.SshObfuscatedPort)
 
 
 	case protocol.TUNNEL_PROTOCOL_SSH:
 	case protocol.TUNNEL_PROTOCOL_SSH:
+		selectedSSHClientVersion = true
+		SSHClientVersion = pickSSHClientVersion()
 		directTCPDialAddress = fmt.Sprintf("%s:%d", serverEntry.IpAddress, serverEntry.SshPort)
 		directTCPDialAddress = fmt.Sprintf("%s:%d", serverEntry.IpAddress, serverEntry.SshPort)
 
 
 	default:
 	default:
@@ -651,44 +657,45 @@ func dialSsh(
 
 
 	// Gather dial parameters for diagnostic logging and stats reporting
 	// Gather dial parameters for diagnostic logging and stats reporting
 
 
-	var dialStats *TunnelDialStats
+	dialStats := &TunnelDialStats{}
 
 
-	if dialConfig.UpstreamProxyUrl != "" || meekConfig != nil {
-		dialStats = &TunnelDialStats{}
+	if selectedSSHClientVersion {
+		dialStats.SelectedSSHClientVersion = true
+		dialStats.SSHClientVersion = SSHClientVersion
+	}
 
 
-		if selectedUserAgent {
-			dialStats.SelectedUserAgent = true
-			dialStats.UserAgent = dialConfig.CustomHeaders.Get("User-Agent")
-		}
+	if selectedUserAgent {
+		dialStats.SelectedUserAgent = true
+		dialStats.UserAgent = dialConfig.CustomHeaders.Get("User-Agent")
+	}
 
 
-		if dialConfig.UpstreamProxyUrl != "" {
+	if dialConfig.UpstreamProxyUrl != "" {
 
 
-			// Note: UpstreamProxyUrl will be validated in the dial
-			proxyURL, err := url.Parse(dialConfig.UpstreamProxyUrl)
-			if err == nil {
-				dialStats.UpstreamProxyType = proxyURL.Scheme
-			}
+		// Note: UpstreamProxyUrl will be validated in the dial
+		proxyURL, err := url.Parse(dialConfig.UpstreamProxyUrl)
+		if err == nil {
+			dialStats.UpstreamProxyType = proxyURL.Scheme
+		}
 
 
-			dialStats.UpstreamProxyCustomHeaderNames = make([]string, 0)
-			for name, _ := range dialConfig.CustomHeaders {
-				if selectedUserAgent && name == "User-Agent" {
-					continue
-				}
-				dialStats.UpstreamProxyCustomHeaderNames = append(dialStats.UpstreamProxyCustomHeaderNames, name)
+		dialStats.UpstreamProxyCustomHeaderNames = make([]string, 0)
+		for name, _ := range dialConfig.CustomHeaders {
+			if selectedUserAgent && name == "User-Agent" {
+				continue
 			}
 			}
+			dialStats.UpstreamProxyCustomHeaderNames = append(dialStats.UpstreamProxyCustomHeaderNames, name)
 		}
 		}
+	}
 
 
-		if meekConfig != nil {
-			// Note: dialStats.MeekResolvedIPAddress isn't set until the dial begins,
-			// so it will always be blank in NoticeConnectingServer.
-			dialStats.MeekDialAddress = meekConfig.DialAddress
-			dialStats.MeekResolvedIPAddress = ""
-			dialStats.MeekSNIServerName = meekConfig.SNIServerName
-			dialStats.MeekHostHeader = meekConfig.HostHeader
-			dialStats.MeekTransformedHostName = meekConfig.TransformedHostName
-			dialStats.SelectedTLSProfile = true
-			dialStats.TLSProfile = meekConfig.TLSProfile
-		}
+	if meekConfig != nil {
+		// Note: dialStats.MeekResolvedIPAddress isn't set until the dial begins,
+		// so it will always be blank in NoticeConnectingServer.
+		dialStats.MeekDialAddress = meekConfig.DialAddress
+		dialStats.MeekResolvedIPAddress = ""
+		dialStats.MeekSNIServerName = meekConfig.SNIServerName
+		dialStats.MeekHostHeader = meekConfig.HostHeader
+		dialStats.MeekTransformedHostName = meekConfig.TransformedHostName
+		dialStats.SelectedTLSProfile = true
+		dialStats.TLSProfile = meekConfig.TLSProfile
 	}
 	}
 
 
 	NoticeConnectingServer(
 	NoticeConnectingServer(
@@ -806,6 +813,7 @@ func dialSsh(
 			ssh.Password(string(payload)),
 			ssh.Password(string(payload)),
 		},
 		},
 		HostKeyCallback: sshCertChecker.CheckHostKey,
 		HostKeyCallback: sshCertChecker.CheckHostKey,
+		ClientVersion:   SSHClientVersion,
 	}
 	}
 
 
 	// The ssh session establishment (via ssh.NewClientConn) is wrapped
 	// The ssh session establishment (via ssh.NewClientConn) is wrapped