github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/config.go (about)

     1  /*
     2   * Copyright (c) 2015, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package psiphon
    21  
    22  import (
    23  	"crypto/md5"
    24  	"encoding/base64"
    25  	"encoding/binary"
    26  	"encoding/json"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"net/http"
    30  	"os"
    31  	"path/filepath"
    32  	"regexp"
    33  	"strconv"
    34  	"strings"
    35  	"sync"
    36  	"unicode"
    37  
    38  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
    39  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    40  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
    41  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
    42  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/resolver"
    43  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/transforms"
    44  )
    45  
    46  const (
    47  	TUNNEL_POOL_SIZE     = 1
    48  	MAX_TUNNEL_POOL_SIZE = 32
    49  
    50  	// Psiphon data directory name, relative to config.DataRootDirectory.
    51  	// See config.GetPsiphonDataDirectory().
    52  	PsiphonDataDirectoryName = "ca.psiphon.PsiphonTunnel.tunnel-core"
    53  
    54  	// Filename constants, all relative to config.GetPsiphonDataDirectory().
    55  	HomepageFilename        = "homepage"
    56  	NoticesFilename         = "notices"
    57  	OldNoticesFilename      = "notices.1"
    58  	UpgradeDownloadFilename = "upgrade"
    59  )
    60  
    61  // Config is the Psiphon configuration specified by the application. This
    62  // configuration controls the behavior of the core tunnel functionality.
    63  //
    64  // To distinguish omitted timeout params from explicit 0 value timeout params,
    65  // corresponding fields are int pointers. nil means no value was supplied and
    66  // to use the default; a non-nil pointer to 0 means no timeout.
    67  type Config struct {
    68  
    69  	// DataRootDirectory is the directory in which to store persistent files,
    70  	// which contain information such as server entries. By default, current
    71  	// working directory.
    72  	//
    73  	// Psiphon will assume full control of files under this directory. They may
    74  	// be deleted, moved or overwritten.
    75  	DataRootDirectory string
    76  
    77  	// UseNoticeFiles configures notice files for writing. If set, homepages
    78  	// will be written to a file created at config.GetHomePageFilename()
    79  	// and notices will be written to a file created at
    80  	// config.GetNoticesFilename().
    81  	//
    82  	// The homepage file may be read after the Tunnels notice with count of 1.
    83  	//
    84  	// The value of UseNoticeFiles sets the size and frequency at which the
    85  	// notices file, config.GetNoticesFilename(), will be rotated. See the
    86  	// comment for UseNoticeFiles for more details. One rotated older file,
    87  	// config.GetOldNoticesFilename(), is retained.
    88  	//
    89  	// The notice files may be may be read at any time; and should be opened
    90  	// read-only for reading. Diagnostic notices are omitted from the notice
    91  	// files.
    92  	//
    93  	// See comment for setNoticeFiles in notice.go for further details.
    94  	UseNoticeFiles *UseNoticeFiles
    95  
    96  	// PropagationChannelId is a string identifier which indicates how the
    97  	// Psiphon client was distributed. This parameter is required. This value
    98  	// is supplied by and depends on the Psiphon Network, and is typically
    99  	// embedded in the client binary.
   100  	PropagationChannelId string
   101  
   102  	// SponsorId is a string identifier which indicates who is sponsoring this
   103  	// Psiphon client. One purpose of this value is to determine the home
   104  	// pages for display. This parameter is required. This value is supplied
   105  	// by and depends on the Psiphon Network, and is typically embedded in the
   106  	// client binary.
   107  	SponsorId string
   108  
   109  	// ClientVersion is the client version number that the client reports to
   110  	// the server. The version number refers to the host client application,
   111  	// not the core tunnel library. One purpose of this value is to enable
   112  	// automatic updates. This value is supplied by and depends on the Psiphon
   113  	// Network, and is typically embedded in the client binary.
   114  	//
   115  	// Note that sending a ClientPlatform string which includes "windows"
   116  	// (case insensitive) and a ClientVersion of <= 44 will cause an error in
   117  	// processing the response to DoConnectedRequest calls.
   118  	ClientVersion string
   119  
   120  	// ClientPlatform is the client platform ("Windows", "Android", etc.) that
   121  	// the client reports to the server.
   122  	ClientPlatform string
   123  
   124  	// ClientFeatures is a list of feature names denoting enabled application
   125  	// features. Clients report enabled features to the server for stats
   126  	// purposes.
   127  	ClientFeatures []string
   128  
   129  	// EgressRegion is a ISO 3166-1 alpha-2 country code which indicates which
   130  	// country to egress from. For the default, "", the best performing server
   131  	// in any country is selected.
   132  	EgressRegion string
   133  
   134  	// SplitTunnelOwnRegion enables split tunnel mode for the client's own
   135  	// country. When enabled, TCP port forward destinations that resolve to
   136  	// the same GeoIP country as the client are connected to directly,
   137  	// untunneled.
   138  	SplitTunnelOwnRegion bool
   139  
   140  	// SplitTunnelRegions enables selected split tunnel mode in which the
   141  	// client specifies a list of ISO 3166-1 alpha-2 country codes for which
   142  	// traffic should be untunneled. TCP port forwards destined to any
   143  	// country specified in SplitTunnelRegions will be untunneled, regardless
   144  	// of whether SplitTunnelOwnRegion is on or off.
   145  	SplitTunnelRegions []string
   146  
   147  	// ListenInterface specifies which interface to listen on.  If no
   148  	// interface is provided then listen on 127.0.0.1. If 'any' is provided
   149  	// then use 0.0.0.0. If there are multiple IP addresses on an interface
   150  	// use the first IPv4 address.
   151  	ListenInterface string
   152  
   153  	// DisableLocalSocksProxy disables running the local SOCKS proxy.
   154  	DisableLocalSocksProxy bool
   155  
   156  	// LocalSocksProxyPort specifies a port number for the local SOCKS proxy
   157  	// running at 127.0.0.1. For the default value, 0, the system selects a
   158  	// free port (a notice reporting the selected port is emitted).
   159  	LocalSocksProxyPort int
   160  
   161  	// LocalHttpProxyPort specifies a port number for the local HTTP proxy
   162  	// running at 127.0.0.1. For the default value, 0, the system selects a
   163  	// free port (a notice reporting the selected port is emitted).
   164  	LocalHttpProxyPort int
   165  
   166  	// DisableLocalHTTPProxy disables running the local HTTP proxy.
   167  	DisableLocalHTTPProxy bool
   168  
   169  	// NetworkLatencyMultiplier is a multiplier that is to be applied to
   170  	// default network event timeouts. Set this to tune performance for
   171  	// slow networks.
   172  	// When set, must be >= 1.0.
   173  	NetworkLatencyMultiplier float64
   174  
   175  	// LimitTunnelProtocols indicates which protocols to use. Valid values
   176  	// include: "SSH", "OSSH", "UNFRONTED-MEEK-OSSH",
   177  	// "UNFRONTED-MEEK-HTTPS-OSSH", "UNFRONTED-MEEK-SESSION-TICKET-OSSH",
   178  	// "FRONTED-MEEK-OSSH", "FRONTED-MEEK-HTTP-OSSH", "QUIC-OSSH",
   179  	// "FRONTED-MEEK-QUIC-OSSH", "TAPDANCE-OSSH", and "CONJURE-OSSH".
   180  
   181  	// For the default, an empty list, all protocols are used.
   182  	LimitTunnelProtocols []string
   183  
   184  	// InitialLimitTunnelProtocols is an optional initial phase of limited
   185  	// protocols for the first InitialLimitTunnelProtocolsCandidateCount
   186  	// candidates; after these candidates, LimitTunnelProtocols applies.
   187  	//
   188  	// For the default, an empty list, InitialLimitTunnelProtocols is off.
   189  	InitialLimitTunnelProtocols []string
   190  
   191  	// InitialLimitTunnelProtocolsCandidateCount is the number of candidates
   192  	// to which InitialLimitTunnelProtocols is applied instead of
   193  	// LimitTunnelProtocols.
   194  	//
   195  	// For the default, 0, InitialLimitTunnelProtocols is off.
   196  	InitialLimitTunnelProtocolsCandidateCount int
   197  
   198  	// LimitTLSProfiles indicates which TLS profiles to select from. Valid
   199  	// values are listed in protocols.SupportedTLSProfiles.
   200  	// For the default, an empty list, all profiles are candidates for
   201  	// selection.
   202  	LimitTLSProfiles []string
   203  
   204  	// LimitQUICVersions indicates which QUIC versions to select from. Valid
   205  	// values are listed in protocols.SupportedQUICVersions.
   206  	// For the default, an empty list, all versions are candidates for
   207  	// selection.
   208  	LimitQUICVersions []string
   209  
   210  	// EstablishTunnelTimeoutSeconds specifies a time limit after which to
   211  	// halt the core tunnel controller if no tunnel has been established. The
   212  	// default is parameters.EstablishTunnelTimeout.
   213  	EstablishTunnelTimeoutSeconds *int
   214  
   215  	// EstablishTunnelPausePeriodSeconds specifies the delay between attempts
   216  	// to establish tunnels. Briefly pausing allows for network conditions to
   217  	// improve and for asynchronous operations such as fetch remote server
   218  	// list to complete. If omitted, a default value is used. This value is
   219  	// typical overridden for testing.
   220  	EstablishTunnelPausePeriodSeconds *int
   221  
   222  	// EstablishTunnelPausePeriodSeconds specifies the grace period, or head
   223  	// start, provided to the affinity server candidate when establishing. The
   224  	// affinity server is the server used for the last established tunnel.
   225  	EstablishTunnelServerAffinityGracePeriodMilliseconds *int
   226  
   227  	// ConnectionWorkerPoolSize specifies how many connection attempts to
   228  	// attempt in parallel. If omitted of when 0, a default is used; this is
   229  	// recommended.
   230  	ConnectionWorkerPoolSize int
   231  
   232  	// TunnelPoolSize specifies how many tunnels to run in parallel. Port
   233  	// forwards are multiplexed over multiple tunnels. If omitted or when 0,
   234  	// the default is TUNNEL_POOL_SIZE, which is recommended. Any value over
   235  	// MAX_TUNNEL_POOL_SIZE is treated as MAX_TUNNEL_POOL_SIZE.
   236  	TunnelPoolSize int
   237  
   238  	// StaggerConnectionWorkersMilliseconds adds a specified delay before
   239  	// making each server candidate available to connection workers. This
   240  	// option is enabled when StaggerConnectionWorkersMilliseconds > 0.
   241  	StaggerConnectionWorkersMilliseconds int
   242  
   243  	// LimitIntensiveConnectionWorkers limits the number of concurrent
   244  	// connection workers attempting connections with resource intensive
   245  	// protocols. This option is enabled when LimitIntensiveConnectionWorkers
   246  	// > 0.
   247  	LimitIntensiveConnectionWorkers int
   248  
   249  	// LimitMeekBufferSizes selects smaller buffers for meek protocols.
   250  	LimitMeekBufferSizes bool
   251  
   252  	// LimitCPUThreads minimizes the number of CPU threads -- and associated
   253  	// overhead -- the are used.
   254  	LimitCPUThreads bool
   255  
   256  	// LimitRelayBufferSizes selects smaller buffers for port forward relaying.
   257  	LimitRelayBufferSizes bool
   258  
   259  	// IgnoreHandshakeStatsRegexps skips compiling and using stats regexes.
   260  	IgnoreHandshakeStatsRegexps bool
   261  
   262  	// UpstreamProxyURL is a URL specifying an upstream proxy to use for all
   263  	// outbound connections. The URL should include proxy type and
   264  	// authentication information, as required. See example URLs here:
   265  	// https://github.com/Psiphon-Labs/psiphon-tunnel-core/tree/master/psiphon/upstreamproxy
   266  	UpstreamProxyURL string
   267  
   268  	// CustomHeaders is a set of additional arbitrary HTTP headers that are
   269  	// added to all plaintext HTTP requests and requests made through an HTTP
   270  	// upstream proxy when specified by UpstreamProxyURL.
   271  	CustomHeaders http.Header
   272  
   273  	// NetworkConnectivityChecker is an interface that enables tunnel-core to
   274  	// call into the host application to check for network connectivity. See:
   275  	// NetworkConnectivityChecker doc.
   276  	NetworkConnectivityChecker NetworkConnectivityChecker
   277  
   278  	// DeviceBinder is an interface that enables tunnel-core to call into the
   279  	// host application to bind sockets to specific devices. See: DeviceBinder
   280  	// doc.
   281  	//
   282  	// When DeviceBinder is set, the "VPN" feature name is automatically added
   283  	// when reporting ClientFeatures.
   284  	DeviceBinder DeviceBinder
   285  
   286  	// AllowDefaultDNSResolverWithBindToDevice indicates that it's safe to use
   287  	// the default resolver when DeviceBinder is configured, as the host OS
   288  	// will automatically exclude DNS requests from the VPN.
   289  	AllowDefaultDNSResolverWithBindToDevice bool
   290  
   291  	// IPv6Synthesizer is an interface that allows tunnel-core to call into
   292  	// the host application to synthesize IPv6 addresses. See: IPv6Synthesizer
   293  	// doc.
   294  	IPv6Synthesizer IPv6Synthesizer
   295  
   296  	// HasIPv6RouteGetter is an interface that allows tunnel-core to call into
   297  	// the host application to determine if the host has an IPv6 route. See:
   298  	// HasIPv6RouteGetter doc.
   299  	HasIPv6RouteGetter HasIPv6RouteGetter
   300  
   301  	// DNSServerGetter is an interface that enables tunnel-core to call into
   302  	// the host application to discover the native network DNS server
   303  	// settings. See: DNSServerGetter doc.
   304  	DNSServerGetter DNSServerGetter
   305  
   306  	// NetworkIDGetter in an interface that enables tunnel-core to call into
   307  	// the host application to get an identifier for the host's current active
   308  	// network. See: NetworkIDGetter doc.
   309  	NetworkIDGetter NetworkIDGetter
   310  
   311  	// NetworkID, when not blank, is used as the identifier for the host's
   312  	// current active network.
   313  	// NetworkID is ignored when NetworkIDGetter is set.
   314  	NetworkID string
   315  
   316  	// DisableTactics disables tactics operations including requests, payload
   317  	// handling, and application of parameters.
   318  	DisableTactics bool
   319  
   320  	// DisableReplay causes any persisted dial parameters to be ignored when
   321  	// they would otherwise be used for replay.
   322  	DisableReplay bool
   323  
   324  	// TargetServerEntry is an encoded server entry. When specified, this
   325  	// server entry is used exclusively and all other known servers are
   326  	// ignored; also, when set, ConnectionWorkerPoolSize is ignored and
   327  	// the pool size is 1.
   328  	TargetServerEntry string
   329  
   330  	// DisableApi disables Psiphon server API calls including handshake,
   331  	// connected, status, etc. This is used for special case temporary tunnels
   332  	// (Windows VPN mode).
   333  	DisableApi bool
   334  
   335  	// TargetApiProtocol specifies whether to force use of "ssh" or "web" API
   336  	// protocol. When blank, the default, the optimal API protocol is used.
   337  	// Note that this capability check is not applied before the
   338  	// "CandidateServers" count is emitted.
   339  	//
   340  	// This parameter is intended for testing and debugging only. Not all
   341  	// parameters are supported in the legacy "web" API protocol, including
   342  	// speed test samples.
   343  	TargetApiProtocol string
   344  
   345  	// RemoteServerListURLs is list of URLs which specify locations to fetch
   346  	// out-of-band server entries. This facility is used when a tunnel cannot
   347  	// be established to known servers. This value is supplied by and depends
   348  	// on the Psiphon Network, and is typically embedded in the client binary.
   349  	// All URLs must point to the same entity with the same ETag. At least one
   350  	// TransferURL must have OnlyAfterAttempts = 0.
   351  	RemoteServerListURLs parameters.TransferURLs
   352  
   353  	// RemoteServerListSignaturePublicKey specifies a public key that's used
   354  	// to authenticate the remote server list payload. This value is supplied
   355  	// by and depends on the Psiphon Network, and is typically embedded in the
   356  	// client binary.
   357  	RemoteServerListSignaturePublicKey string
   358  
   359  	// DisableRemoteServerListFetcher disables fetching remote server lists.
   360  	// This is used for special case temporary tunnels.
   361  	DisableRemoteServerListFetcher bool
   362  
   363  	// FetchRemoteServerListRetryPeriodMilliseconds specifies the delay before
   364  	// resuming a remote server list download after a failure. If omitted, a
   365  	// default value is used. This value is typical overridden for testing.
   366  	FetchRemoteServerListRetryPeriodMilliseconds *int
   367  
   368  	// ObfuscatedServerListRootURLs is a list of URLs which specify root
   369  	// locations from which to fetch obfuscated server list files. This value
   370  	// is supplied by and depends on the Psiphon Network, and is typically
   371  	// embedded in the client binary. All URLs must point to the same entity
   372  	// with the same ETag. At least one DownloadURL must have
   373  	// OnlyAfterAttempts = 0.
   374  	ObfuscatedServerListRootURLs parameters.TransferURLs
   375  
   376  	// UpgradeDownloadURLs is list of URLs which specify locations from which
   377  	// to download a host client upgrade file, when one is available. The core
   378  	// tunnel controller provides a resumable download facility which
   379  	// downloads this resource and emits a notice when complete. This value is
   380  	// supplied by and depends on the Psiphon Network, and is typically
   381  	// embedded in the client binary. All URLs must point to the same entity
   382  	// with the same ETag. At least one DownloadURL must have
   383  	// OnlyAfterAttempts = 0.
   384  	UpgradeDownloadURLs parameters.TransferURLs
   385  
   386  	// UpgradeDownloadClientVersionHeader specifies the HTTP header name for
   387  	// the entity at UpgradeDownloadURLs which specifies the client version
   388  	// (an integer value). A HEAD request may be made to check the version
   389  	// number available at UpgradeDownloadURLs.
   390  	// UpgradeDownloadClientVersionHeader is required when UpgradeDownloadURLs
   391  	// is specified.
   392  	UpgradeDownloadClientVersionHeader string
   393  
   394  	// FetchUpgradeRetryPeriodMilliseconds specifies the delay before resuming
   395  	// a client upgrade download after a failure. If omitted, a default value
   396  	// is used. This value is typical overridden for testing.
   397  	FetchUpgradeRetryPeriodMilliseconds *int
   398  
   399  	// FeedbackUploadURLs is a list of SecureTransferURLs which specify
   400  	// locations where feedback data can be uploaded, pairing with each
   401  	// location a public key with which to encrypt the feedback data. This
   402  	// value is supplied by and depends on the Psiphon Network, and is
   403  	// typically embedded in the client binary. At least one TransferURL must
   404  	// have OnlyAfterAttempts = 0.
   405  	FeedbackUploadURLs parameters.TransferURLs
   406  
   407  	// FeedbackEncryptionPublicKey is a default base64-encoded, RSA public key
   408  	// value used to encrypt feedback data. Used when uploading feedback with a
   409  	// TransferURL which has no public key value configured, i.e.
   410  	// B64EncodedPublicKey = "".
   411  	FeedbackEncryptionPublicKey string
   412  
   413  	// TrustedCACertificatesFilename specifies a file containing trusted CA
   414  	// certs. When set, this toggles use of the trusted CA certs, specified in
   415  	// TrustedCACertificatesFilename, for tunneled TLS connections that expect
   416  	// server certificates signed with public certificate authorities
   417  	// (currently, only upgrade downloads). This option is used with stock Go
   418  	// TLS in cases where Go may fail to obtain a list of root CAs from the
   419  	// operating system.
   420  	TrustedCACertificatesFilename string
   421  
   422  	// DisablePeriodicSshKeepAlive indicates whether to send an SSH keepalive
   423  	// every 1-2 minutes, when the tunnel is idle. If the SSH keepalive times
   424  	// out, the tunnel is considered to have failed.
   425  	DisablePeriodicSshKeepAlive bool
   426  
   427  	// DeviceRegion is the optional, reported region the host device is
   428  	// running in. This input value should be a ISO 3166-1 alpha-2 country
   429  	// code. The device region is reported to the server in the connected
   430  	// request and recorded for Psiphon stats.
   431  	//
   432  	// When provided, this value may be used, pre-connection, to select
   433  	// performance or circumvention optimization strategies for the given
   434  	// region.
   435  	DeviceRegion string
   436  
   437  	// EmitDiagnosticNotices indicates whether to output notices containing
   438  	// detailed information about the Psiphon session. As these notices may
   439  	// contain sensitive information, they should not be insecurely distributed
   440  	// or displayed to users. Default is off.
   441  	EmitDiagnosticNotices bool
   442  
   443  	// EmitDiagnosticNetworkParameters indicates whether to include network
   444  	// parameters in diagnostic notices. As these parameters are sensitive
   445  	// circumvention network information, they should not be insecurely
   446  	// distributed or displayed to users. Default is off.
   447  	EmitDiagnosticNetworkParameters bool
   448  
   449  	// EmitBytesTransferred indicates whether to emit periodic notices showing
   450  	// bytes sent and received.
   451  	EmitBytesTransferred bool
   452  
   453  	// EmitSLOKs indicates whether to emit notices for each seeded SLOK. As
   454  	// this could reveal user browsing activity, it's intended for debugging
   455  	// and testing only.
   456  	EmitSLOKs bool
   457  
   458  	// EmitRefractionNetworkingLogs indicates whether to emit gotapdance log
   459  	// messages to stdout. Note that gotapdance log messages do not conform to
   460  	// the Notice format standard. Default is off.
   461  	EmitRefractionNetworkingLogs bool
   462  
   463  	// EmitServerAlerts indicates whether to emit notices for server alerts.
   464  	EmitServerAlerts bool
   465  
   466  	// EmitClientAddress indicates whether to emit the client's public network
   467  	// address, IP and port, as seen by the server.
   468  	EmitClientAddress bool
   469  
   470  	// RateLimits specify throttling configuration for the tunnel.
   471  	RateLimits common.RateLimits
   472  
   473  	// PacketTunnelTunDeviceFileDescriptor specifies a tun device file
   474  	// descriptor to use for running a packet tunnel. When this value is > 0,
   475  	// a packet tunnel is established through the server and packets are
   476  	// relayed via the tun device file descriptor. The file descriptor is
   477  	// duped in NewController. When PacketTunnelTunDeviceFileDescriptor is
   478  	// set, TunnelPoolSize must be 1.
   479  	PacketTunnelTunFileDescriptor int
   480  
   481  	// SessionID specifies a client session ID to use in the Psiphon API. The
   482  	// session ID should be a randomly generated value that is used only for a
   483  	// single session, which is defined as the period between a user starting
   484  	// a Psiphon client and stopping the client.
   485  	//
   486  	// A session ID must be 32 hex digits (lower case). When blank, a random
   487  	// session ID is automatically generated. Supply a session ID when a
   488  	// single client session will cross multiple Controller instances.
   489  	SessionID string
   490  
   491  	// Authorizations is a list of encoded, signed access control
   492  	// authorizations that the client has obtained and will present to the
   493  	// server.
   494  	Authorizations []string
   495  
   496  	// ServerEntrySignaturePublicKey is a base64-encoded, ed25519 public
   497  	// key value used to verify individual server entry signatures. This value
   498  	// is supplied by and depends on the Psiphon Network, and is typically
   499  	// embedded in the client binary.
   500  	ServerEntrySignaturePublicKey string
   501  
   502  	// ExchangeObfuscationKey is a base64-encoded, NaCl secretbox key used to
   503  	// obfuscate server info exchanges between clients.
   504  	// Required for the exchange functionality.
   505  	ExchangeObfuscationKey string
   506  
   507  	// MigrateHomepageNoticesFilename migrates a homepage file from the path
   508  	// previously configured with setNoticeFiles to the new path for homepage
   509  	// files under the data root directory. The file specified by this config
   510  	// value will be moved to config.GetHomePageFilename().
   511  	//
   512  	// Note: see comment for config.Commit() for a description of how file
   513  	// migrations are performed.
   514  	//
   515  	// If not set, no migration operation will be performed.
   516  	MigrateHomepageNoticesFilename string
   517  
   518  	// MigrateRotatingNoticesFilename migrates notice files from the path
   519  	// previously configured with setNoticeFiles to the new path for notice
   520  	// files under the data root directory.
   521  	//
   522  	// MigrateRotatingNoticesFilename will be moved to
   523  	// config.GetNoticesFilename().
   524  	//
   525  	// MigrateRotatingNoticesFilename.1 will be moved to
   526  	// config.GetOldNoticesFilename().
   527  	//
   528  	// Note: see comment for config.Commit() for a description of how file
   529  	// migrations are performed.
   530  	//
   531  	// If not set, no migration operation will be performed.
   532  	MigrateRotatingNoticesFilename string
   533  
   534  	// MigrateDataStoreDirectory indicates the location of the datastore
   535  	// directory, as previously configured with the deprecated
   536  	// DataStoreDirectory config field. Datastore files found in the specified
   537  	// directory will be moved under the data root directory.
   538  	//
   539  	// Note: see comment for config.Commit() for a description of how file
   540  	// migrations are performed.
   541  	MigrateDataStoreDirectory string
   542  
   543  	// MigrateRemoteServerListDownloadFilename indicates the location of
   544  	// remote server list download files. The remote server list files found at
   545  	// the specified path will be moved under the data root directory.
   546  	//
   547  	// Note: see comment for config.Commit() for a description of how file
   548  	// migrations are performed.
   549  	MigrateRemoteServerListDownloadFilename string
   550  
   551  	// MigrateObfuscatedServerListDownloadDirectory indicates the location of
   552  	// the obfuscated server list downloads directory, as previously configured
   553  	// with ObfuscatedServerListDownloadDirectory. Obfuscated server list
   554  	// download files found in the specified directory will be moved under the
   555  	// data root directory.
   556  	//
   557  	// Warning: if the directory is empty after obfuscated server
   558  	// list files are moved, then it will be deleted.
   559  	//
   560  	// Note: see comment for config.Commit() for a description of how file
   561  	// migrations are performed.
   562  	MigrateObfuscatedServerListDownloadDirectory string
   563  
   564  	// MigrateUpgradeDownloadFilename indicates the location of downloaded
   565  	// application upgrade files. Downloaded upgrade files found at the
   566  	// specified path will be moved under the data root directory.
   567  	//
   568  	// Note: see comment for config.Commit() for a description of how file
   569  	// migrations are performed.
   570  	MigrateUpgradeDownloadFilename string
   571  
   572  	//
   573  	// The following parameters are deprecated.
   574  	//
   575  
   576  	// DataStoreDirectory is the directory in which to store the persistent
   577  	// database, which contains information such as server entries. By
   578  	// default, current working directory.
   579  	//
   580  	// Deprecated:
   581  	// Use MigrateDataStoreDirectory. When MigrateDataStoreDirectory
   582  	// is set, this parameter is ignored.
   583  	//
   584  	// DataStoreDirectory has been subsumed by the new data root directory,
   585  	// which is configured with DataRootDirectory. If set, datastore files
   586  	// found in the specified directory will be moved under the data root
   587  	// directory.
   588  	DataStoreDirectory string
   589  
   590  	// RemoteServerListDownloadFilename specifies a target filename for
   591  	// storing the remote server list download. Data is stored in co-located
   592  	// files (RemoteServerListDownloadFilename.part*) to allow for resumable
   593  	// downloading.
   594  	//
   595  	// Deprecated:
   596  	// Use MigrateRemoteServerListDownloadFilename. When
   597  	// MigrateRemoteServerListDownloadFilename is set, this parameter is
   598  	// ignored.
   599  	//
   600  	// If set, remote server list download files found at the specified path
   601  	// will be moved under the data root directory.
   602  	RemoteServerListDownloadFilename string
   603  
   604  	// ObfuscatedServerListDownloadDirectory specifies a target directory for
   605  	// storing the obfuscated remote server list downloads. Data is stored in
   606  	// co-located files (<OSL filename>.part*) to allow for resumable
   607  	// downloading.
   608  	//
   609  	// Deprecated:
   610  	// Use MigrateObfuscatedServerListDownloadDirectory. When
   611  	// MigrateObfuscatedServerListDownloadDirectory is set, this parameter is
   612  	// ignored.
   613  	//
   614  	// If set, obfuscated server list download files found at the specified path
   615  	// will be moved under the data root directory.
   616  	ObfuscatedServerListDownloadDirectory string
   617  
   618  	// UpgradeDownloadFilename is the local target filename for an upgrade
   619  	// download. This parameter is required when UpgradeDownloadURLs (or
   620  	// UpgradeDownloadUrl) is specified. Data is stored in co-located files
   621  	// (UpgradeDownloadFilename.part*) to allow for resumable downloading.
   622  	//
   623  	// Deprecated:
   624  	// Use MigrateUpgradeDownloadFilename. When MigrateUpgradeDownloadFilename
   625  	// is set, this parameter is ignored.
   626  	//
   627  	// If set, upgrade download files found at the specified path will be moved
   628  	// under the data root directory.
   629  	UpgradeDownloadFilename string
   630  
   631  	// TunnelProtocol indicates which protocol to use. For the default, "",
   632  	// all protocols are used.
   633  	//
   634  	// Deprecated: Use LimitTunnelProtocols. When LimitTunnelProtocols is not
   635  	// nil, this parameter is ignored.
   636  	TunnelProtocol string
   637  
   638  	// Deprecated: Use CustomHeaders. When CustomHeaders is not nil, this
   639  	// parameter is ignored.
   640  	UpstreamProxyCustomHeaders http.Header
   641  
   642  	// RemoteServerListUrl is a URL which specifies a location to fetch out-
   643  	// of-band server entries. This facility is used when a tunnel cannot be
   644  	// established to known servers. This value is supplied by and depends on
   645  	// the Psiphon Network, and is typically embedded in the client binary.
   646  	//
   647  	// Deprecated: Use RemoteServerListURLs. When RemoteServerListURLs is not
   648  	// nil, this parameter is ignored.
   649  	RemoteServerListUrl string
   650  
   651  	// ObfuscatedServerListRootURL is a URL which specifies the root location
   652  	// from which to fetch obfuscated server list files. This value is
   653  	// supplied by and depends on the Psiphon Network, and is typically
   654  	// embedded in the client binary.
   655  	//
   656  	// Deprecated: Use ObfuscatedServerListRootURLs. When
   657  	// ObfuscatedServerListRootURLs is not nil, this parameter is ignored.
   658  	ObfuscatedServerListRootURL string
   659  
   660  	// UpgradeDownloadUrl specifies a URL from which to download a host client
   661  	// upgrade file, when one is available. The core tunnel controller
   662  	// provides a resumable download facility which downloads this resource
   663  	// and emits a notice when complete. This value is supplied by and depends
   664  	// on the Psiphon Network, and is typically embedded in the client binary.
   665  	//
   666  	// Deprecated: Use UpgradeDownloadURLs. When UpgradeDownloadURLs is not
   667  	// nil, this parameter is ignored.
   668  	UpgradeDownloadUrl string
   669  
   670  	//
   671  	// The following parameters are for testing purposes.
   672  	//
   673  
   674  	// TransformHostNameProbability is for testing purposes.
   675  	TransformHostNameProbability *float64
   676  
   677  	// FragmentorProbability and associated Fragmentor fields are for testing
   678  	// purposes.
   679  	FragmentorProbability          *float64
   680  	FragmentorLimitProtocols       []string
   681  	FragmentorMinTotalBytes        *int
   682  	FragmentorMaxTotalBytes        *int
   683  	FragmentorMinWriteBytes        *int
   684  	FragmentorMaxWriteBytes        *int
   685  	FragmentorMinDelayMicroseconds *int
   686  	FragmentorMaxDelayMicroseconds *int
   687  
   688  	// MeekTrafficShapingProbability and associated fields are for testing
   689  	// purposes.
   690  	MeekTrafficShapingProbability       *float64
   691  	MeekTrafficShapingLimitProtocols    []string
   692  	MeekMinTLSPadding                   *int
   693  	MeekMaxTLSPadding                   *int
   694  	MeekMinLimitRequestPayloadLength    *int
   695  	MeekMaxLimitRequestPayloadLength    *int
   696  	MeekRedialTLSProbability            *float64
   697  	MeekAlternateCookieNameProbability  *float64
   698  	MeekAlternateContentTypeProbability *float64
   699  
   700  	// ObfuscatedSSHAlgorithms and associated ObfuscatedSSH fields are for
   701  	// testing purposes. If specified, ObfuscatedSSHAlgorithms must have 4 SSH
   702  	// KEX elements in order: the kex algorithm, cipher, MAC, and server host
   703  	// key algorithm.
   704  	ObfuscatedSSHAlgorithms []string
   705  	ObfuscatedSSHMinPadding *int
   706  	ObfuscatedSSHMaxPadding *int
   707  
   708  	// LivenessTestMinUpstreamBytes and other LivenessTest fields are for
   709  	// testing purposes.
   710  	LivenessTestMinUpstreamBytes   *int
   711  	LivenessTestMaxUpstreamBytes   *int
   712  	LivenessTestMinDownstreamBytes *int
   713  	LivenessTestMaxDownstreamBytes *int
   714  
   715  	// ReplayCandidateCount and other Replay fields are for testing purposes.
   716  	ReplayCandidateCount                   *int
   717  	ReplayDialParametersTTLSeconds         *int
   718  	ReplayTargetUpstreamBytes              *int
   719  	ReplayTargetDownstreamBytes            *int
   720  	ReplayTargetTunnelDurationSeconds      *int
   721  	ReplayLaterRoundMoveToFrontProbability *float64
   722  	ReplayRetainFailedProbability          *float64
   723  
   724  	// NetworkLatencyMultiplierMin and other NetworkLatencyMultiplier fields are
   725  	// for testing purposes.
   726  	NetworkLatencyMultiplierMin    float64
   727  	NetworkLatencyMultiplierMax    float64
   728  	NetworkLatencyMultiplierLambda float64
   729  
   730  	// UseOnlyCustomTLSProfiles and other TLS configuration fields are for
   731  	// testing purposes.
   732  	UseOnlyCustomTLSProfiles              *bool
   733  	CustomTLSProfiles                     protocol.CustomTLSProfiles
   734  	SelectRandomizedTLSProfileProbability *float64
   735  	NoDefaultTLSSessionIDProbability      *float64
   736  	DisableFrontingProviderTLSProfiles    protocol.LabeledTLSProfiles
   737  
   738  	// ClientBurstUpstreamTargetBytes and other burst metric fields are for
   739  	// testing purposes.
   740  	ClientBurstUpstreamTargetBytes            *int
   741  	ClientBurstUpstreamDeadlineMilliseconds   *int
   742  	ClientBurstDownstreamTargetBytes          *int
   743  	ClientBurstDownstreamDeadlineMilliseconds *int
   744  
   745  	// ApplicationParameters is for testing purposes.
   746  	ApplicationParameters parameters.KeyValues
   747  
   748  	// CustomHostNameRegexes and other custom host name fields are for testing
   749  	// purposes.
   750  	CustomHostNameRegexes        []string
   751  	CustomHostNameProbability    *float64
   752  	CustomHostNameLimitProtocols []string
   753  
   754  	// ConjureCachedRegistrationTTLSeconds and other Conjure fields are for
   755  	// testing purposes.
   756  	ConjureCachedRegistrationTTLSeconds       *int
   757  	ConjureAPIRegistrarBidirectionalURL       string
   758  	ConjureAPIRegistrarFrontingSpecs          parameters.FrontingSpecs
   759  	ConjureAPIRegistrarMinDelayMilliseconds   *int
   760  	ConjureAPIRegistrarMaxDelayMilliseconds   *int
   761  	ConjureDecoyRegistrarProbability          *float64
   762  	ConjureDecoyRegistrarWidth                *int
   763  	ConjureDecoyRegistrarMinDelayMilliseconds *int
   764  	ConjureDecoyRegistrarMaxDelayMilliseconds *int
   765  
   766  	// HoldOffTunnelMinDurationMilliseconds and other HoldOffTunnel fields are
   767  	// for testing purposes.
   768  	HoldOffTunnelMinDurationMilliseconds *int
   769  	HoldOffTunnelMaxDurationMilliseconds *int
   770  	HoldOffTunnelProtocols               []string
   771  	HoldOffTunnelFrontingProviderIDs     []string
   772  	HoldOffTunnelProbability             *float64
   773  
   774  	// RestrictFrontingProviderIDs and other RestrictFrontingProviderIDs fields
   775  	// are for testing purposes.
   776  	RestrictFrontingProviderIDs                  []string
   777  	RestrictFrontingProviderIDsClientProbability *float64
   778  
   779  	// UpstreamProxyAllowAllServerEntrySources is for testing purposes.
   780  	UpstreamProxyAllowAllServerEntrySources *bool
   781  
   782  	// LimitTunnelDialPortNumbers is for testing purposes.
   783  	LimitTunnelDialPortNumbers parameters.TunnelProtocolPortLists
   784  
   785  	// QUICDisablePathMTUDiscoveryProbability is for testing purposes.
   786  	QUICDisablePathMTUDiscoveryProbability *float64
   787  
   788  	// DNSResolverAttemptsPerServer and other DNSResolver fields are for
   789  	// testing purposes.
   790  	DNSResolverAttemptsPerServer                     *int
   791  	DNSResolverAttemptsPerPreferredServer            *int
   792  	DNSResolverRequestTimeoutMilliseconds            *int
   793  	DNSResolverAwaitTimeoutMilliseconds              *int
   794  	DNSResolverPreresolvedIPAddressCIDRs             parameters.LabeledCIDRs
   795  	DNSResolverPreresolvedIPAddressProbability       *float64
   796  	DNSResolverAlternateServers                      []string
   797  	DNSResolverPreferredAlternateServers             []string
   798  	DNSResolverPreferAlternateServerProbability      *float64
   799  	DNSResolverProtocolTransformSpecs                transforms.Specs
   800  	DNSResolverProtocolTransformScopedSpecNames      transforms.ScopedSpecNames
   801  	DNSResolverProtocolTransformProbability          *float64
   802  	DNSResolverIncludeEDNS0Probability               *float64
   803  	DNSResolverCacheExtensionInitialTTLMilliseconds  *int
   804  	DNSResolverCacheExtensionVerifiedTTLMilliseconds *int
   805  
   806  	// params is the active parameters.Parameters with defaults, config values,
   807  	// and, optionally, tactics applied.
   808  	//
   809  	// New tactics must be applied by calling Config.SetParameters; calling
   810  	// params.Set directly will fail to add config values.
   811  	params *parameters.Parameters
   812  
   813  	dialParametersHash []byte
   814  
   815  	dynamicConfigMutex sync.Mutex
   816  	sponsorID          string
   817  	authorizations     []string
   818  
   819  	deviceBinder    DeviceBinder
   820  	networkIDGetter NetworkIDGetter
   821  
   822  	clientFeatures []string
   823  
   824  	resolverMutex sync.Mutex
   825  	resolver      *resolver.Resolver
   826  
   827  	committed bool
   828  
   829  	loadTimestamp string
   830  }
   831  
   832  // Config field which specifies if notice files should be used and at which
   833  // frequency and size they should be rotated.
   834  //
   835  // If either RotatingFileSize or RotatingSyncFrequency are <= 0, default values
   836  // are used.
   837  //
   838  // See comment for setNoticeFiles in notice.go for further details.
   839  type UseNoticeFiles struct {
   840  	RotatingFileSize      int
   841  	RotatingSyncFrequency int
   842  }
   843  
   844  // LoadConfig parses a JSON format Psiphon config JSON string and returns a
   845  // Config struct populated with config values.
   846  //
   847  // The Config struct may then be programmatically populated with additional
   848  // values, including callbacks such as DeviceBinder.
   849  //
   850  // Before using the Config, Commit must be called, which will perform further
   851  // validation and initialize internal data structures.
   852  func LoadConfig(configJson []byte) (*Config, error) {
   853  
   854  	var config Config
   855  	err := json.Unmarshal(configJson, &config)
   856  	if err != nil {
   857  		return nil, errors.Trace(err)
   858  	}
   859  
   860  	config.loadTimestamp = common.TruncateTimestampToHour(
   861  		common.GetCurrentTimestamp())
   862  
   863  	return &config, nil
   864  }
   865  
   866  // IsCommitted checks if Commit was called.
   867  func (config *Config) IsCommitted() bool {
   868  	return config.committed
   869  }
   870  
   871  // Commit validates Config fields finalizes initialization.
   872  //
   873  // Config fields should not be set after calling Config, as any changes may
   874  // not be reflected in internal data structures.
   875  //
   876  // If migrateFromLegacyFields is set to true, then an attempt to migrate from
   877  // legacy fields is made.
   878  //
   879  // Migration from legacy fields:
   880  // Config fields of the naming Migrate* (e.g. MigrateDataStoreDirectory) specify
   881  // a file migration operation which should be performed. These fields correspond
   882  // to deprecated fields, which previously could be used to specify where Psiphon
   883  // stored different sets of persistent files (e.g. MigrateDataStoreDirectory
   884  // corresponds to the deprecated field DataStoreDirectory).
   885  //
   886  // Psiphon now stores all persistent data under the configurable
   887  // DataRootDirectory (see Config.DataRootDirectory). The deprecated fields, and
   888  // corresponding Migrate* fields, are now used to specify the file or directory
   889  // path where, or under which, persistent files and directories created by
   890  // previous versions of Psiphon exist, so they can be moved under the
   891  // DataRootDirectory.
   892  //
   893  // For each migration operation:
   894  // - In the case of directories that could have defaulted to the current working
   895  //   directory, persistent files and directories created by Psiphon are
   896  //   precisely targeted to avoid moving files which were not created by Psiphon.
   897  // - If no file is found at the specified path, or an error is encountered while
   898  //   migrating the file, then an error is logged and execution continues
   899  //   normally.
   900  //
   901  // A sentinel file which signals that file migration has been completed, and
   902  // should not be attempted again, is created under DataRootDirectory after one
   903  // full pass through Commit(), regardless of whether file migration succeeds or
   904  // fails. It is better to not endlessly retry file migrations on each Commit()
   905  // because file system errors are expected to be rare and persistent files will
   906  // be re-populated over time.
   907  func (config *Config) Commit(migrateFromLegacyFields bool) error {
   908  
   909  	// Do SetEmitDiagnosticNotices first, to ensure config file errors are
   910  	// emitted.
   911  	if config.EmitDiagnosticNotices {
   912  		SetEmitDiagnosticNotices(
   913  			true, config.EmitDiagnosticNetworkParameters)
   914  	}
   915  
   916  	// Migrate and set notice files before any operations that may emit an
   917  	// error. This is to ensure config file errors are written to file when
   918  	// notice files are configured with config.UseNoticeFiles.
   919  	//
   920  	// Note:
   921  	// Errors encountered while configuring the data directory cannot be written
   922  	// to notice files. This is because notices files are created within the
   923  	// data directory.
   924  
   925  	if config.DataRootDirectory == "" {
   926  		wd, err := os.Getwd()
   927  		if err != nil {
   928  			return errors.Trace(common.RedactFilePathsError(err))
   929  		}
   930  		config.DataRootDirectory = wd
   931  	}
   932  
   933  	// Create root directory
   934  	dataDirectoryPath := config.GetPsiphonDataDirectory()
   935  	if !common.FileExists(dataDirectoryPath) {
   936  		err := os.Mkdir(dataDirectoryPath, os.ModePerm)
   937  		if err != nil {
   938  			return errors.Tracef(
   939  				"failed to create datastore directory with error: %s",
   940  				common.RedactFilePathsError(err, dataDirectoryPath))
   941  		}
   942  	}
   943  
   944  	// Check if the migration from legacy config fields has already been
   945  	// completed. See the Migrate* config fields for more details.
   946  	migrationCompleteFilePath := filepath.Join(config.GetPsiphonDataDirectory(), "migration_complete")
   947  	needMigration := !common.FileExists(migrationCompleteFilePath)
   948  
   949  	// Collect notices to emit them after notice files are set
   950  	var noticeMigrationAlertMsgs []string
   951  	var noticeMigrationInfoMsgs []string
   952  
   953  	// Migrate notices first to ensure notice files are used for notices if
   954  	// UseNoticeFiles is set.
   955  	homepageFilePath := config.GetHomePageFilename()
   956  	noticesFilePath := config.GetNoticesFilename()
   957  
   958  	if migrateFromLegacyFields {
   959  		if needMigration {
   960  
   961  			// Move notice files that exist at legacy file paths under the data root
   962  			// directory.
   963  
   964  			noticeMigrationInfoMsgs = append(noticeMigrationInfoMsgs, "Config migration: need migration")
   965  			noticeMigrations := migrationsFromLegacyNoticeFilePaths(config)
   966  
   967  			successfulMigrations := 0
   968  
   969  			for _, migration := range noticeMigrations {
   970  				err := DoFileMigration(migration)
   971  				if err != nil {
   972  					alertMsg := fmt.Sprintf("Config migration: %s", errors.Trace(err))
   973  					noticeMigrationAlertMsgs = append(noticeMigrationAlertMsgs, alertMsg)
   974  				} else {
   975  					successfulMigrations += 1
   976  				}
   977  			}
   978  			infoMsg := fmt.Sprintf("Config migration: %d/%d notice files successfully migrated", successfulMigrations, len(noticeMigrations))
   979  			noticeMigrationInfoMsgs = append(noticeMigrationInfoMsgs, infoMsg)
   980  		} else {
   981  			noticeMigrationInfoMsgs = append(noticeMigrationInfoMsgs, "Config migration: migration already completed")
   982  		}
   983  	}
   984  
   985  	if config.UseNoticeFiles != nil {
   986  		setNoticeFiles(
   987  			homepageFilePath,
   988  			noticesFilePath,
   989  			config.UseNoticeFiles.RotatingFileSize,
   990  			config.UseNoticeFiles.RotatingSyncFrequency)
   991  	}
   992  
   993  	// Emit notices now that notice files are set if configured
   994  	for _, msg := range noticeMigrationAlertMsgs {
   995  		NoticeWarning(msg)
   996  	}
   997  	for _, msg := range noticeMigrationInfoMsgs {
   998  		NoticeInfo(msg)
   999  	}
  1000  
  1001  	// Promote legacy fields.
  1002  
  1003  	if config.CustomHeaders == nil {
  1004  		config.CustomHeaders = config.UpstreamProxyCustomHeaders
  1005  		config.UpstreamProxyCustomHeaders = nil
  1006  	}
  1007  
  1008  	if config.RemoteServerListUrl != "" && config.RemoteServerListURLs == nil {
  1009  		config.RemoteServerListURLs = promoteLegacyTransferURL(config.RemoteServerListUrl)
  1010  	}
  1011  
  1012  	if config.ObfuscatedServerListRootURL != "" && config.ObfuscatedServerListRootURLs == nil {
  1013  		config.ObfuscatedServerListRootURLs = promoteLegacyTransferURL(config.ObfuscatedServerListRootURL)
  1014  	}
  1015  
  1016  	if config.UpgradeDownloadUrl != "" && config.UpgradeDownloadURLs == nil {
  1017  		config.UpgradeDownloadURLs = promoteLegacyTransferURL(config.UpgradeDownloadUrl)
  1018  	}
  1019  
  1020  	if config.TunnelProtocol != "" && len(config.LimitTunnelProtocols) == 0 {
  1021  		config.LimitTunnelProtocols = []string{config.TunnelProtocol}
  1022  	}
  1023  
  1024  	if config.DataStoreDirectory != "" && config.MigrateDataStoreDirectory == "" {
  1025  		config.MigrateDataStoreDirectory = config.DataStoreDirectory
  1026  	}
  1027  
  1028  	if config.RemoteServerListDownloadFilename != "" && config.MigrateRemoteServerListDownloadFilename == "" {
  1029  		config.MigrateRemoteServerListDownloadFilename = config.RemoteServerListDownloadFilename
  1030  	}
  1031  
  1032  	if config.ObfuscatedServerListDownloadDirectory != "" && config.MigrateObfuscatedServerListDownloadDirectory == "" {
  1033  		config.MigrateObfuscatedServerListDownloadDirectory = config.ObfuscatedServerListDownloadDirectory
  1034  	}
  1035  
  1036  	if config.UpgradeDownloadFilename != "" && config.MigrateUpgradeDownloadFilename == "" {
  1037  		config.MigrateUpgradeDownloadFilename = config.UpgradeDownloadFilename
  1038  	}
  1039  
  1040  	// Supply default values.
  1041  
  1042  	// Create datastore directory.
  1043  	dataStoreDirectoryPath := config.GetDataStoreDirectory()
  1044  	if !common.FileExists(dataStoreDirectoryPath) {
  1045  		err := os.Mkdir(dataStoreDirectoryPath, os.ModePerm)
  1046  		if err != nil {
  1047  			return errors.Tracef(
  1048  				"failed to create datastore directory with error: %s",
  1049  				common.RedactFilePathsError(err, dataStoreDirectoryPath))
  1050  		}
  1051  	}
  1052  
  1053  	// Create OSL directory.
  1054  	oslDirectoryPath := config.GetObfuscatedServerListDownloadDirectory()
  1055  	if !common.FileExists(oslDirectoryPath) {
  1056  		err := os.Mkdir(oslDirectoryPath, os.ModePerm)
  1057  		if err != nil {
  1058  			return errors.Tracef(
  1059  				"failed to create osl directory with error: %s",
  1060  				common.RedactFilePathsError(err, oslDirectoryPath))
  1061  		}
  1062  	}
  1063  
  1064  	if config.ClientVersion == "" {
  1065  		config.ClientVersion = "0"
  1066  	}
  1067  
  1068  	if config.TunnelPoolSize == 0 {
  1069  		config.TunnelPoolSize = TUNNEL_POOL_SIZE
  1070  	}
  1071  
  1072  	// Validate config fields.
  1073  
  1074  	if !common.FileExists(config.DataRootDirectory) {
  1075  		return errors.TraceNew("DataRootDirectory does not exist")
  1076  	}
  1077  
  1078  	if config.PropagationChannelId == "" {
  1079  		return errors.TraceNew("propagation channel ID is missing from the configuration file")
  1080  	}
  1081  	if config.SponsorId == "" {
  1082  		return errors.TraceNew("sponsor ID is missing from the configuration file")
  1083  	}
  1084  
  1085  	_, err := strconv.Atoi(config.ClientVersion)
  1086  	if err != nil {
  1087  		return errors.Tracef("invalid client version: %s", err)
  1088  	}
  1089  
  1090  	if !common.Contains(
  1091  		[]string{"", protocol.PSIPHON_SSH_API_PROTOCOL, protocol.PSIPHON_WEB_API_PROTOCOL},
  1092  		config.TargetApiProtocol) {
  1093  
  1094  		return errors.TraceNew("invalid TargetApiProtocol")
  1095  	}
  1096  
  1097  	if !config.DisableRemoteServerListFetcher {
  1098  
  1099  		if config.RemoteServerListURLs != nil {
  1100  			if config.RemoteServerListSignaturePublicKey == "" {
  1101  				return errors.TraceNew("missing RemoteServerListSignaturePublicKey")
  1102  			}
  1103  		}
  1104  
  1105  		if config.ObfuscatedServerListRootURLs != nil {
  1106  			if config.RemoteServerListSignaturePublicKey == "" {
  1107  				return errors.TraceNew("missing RemoteServerListSignaturePublicKey")
  1108  			}
  1109  		}
  1110  	}
  1111  
  1112  	if config.UpgradeDownloadURLs != nil {
  1113  		if config.UpgradeDownloadClientVersionHeader == "" {
  1114  			return errors.TraceNew("missing UpgradeDownloadClientVersionHeader")
  1115  		}
  1116  	}
  1117  
  1118  	if config.FeedbackUploadURLs != nil {
  1119  		if config.FeedbackEncryptionPublicKey == "" {
  1120  			return errors.TraceNew("missing FeedbackEncryptionPublicKey")
  1121  		}
  1122  	}
  1123  
  1124  	// This constraint is expected by logic in Controller.runTunnels().
  1125  
  1126  	if config.PacketTunnelTunFileDescriptor > 0 && config.TunnelPoolSize != 1 {
  1127  		return errors.TraceNew("packet tunnel mode requires TunnelPoolSize to be 1")
  1128  	}
  1129  
  1130  	// SessionID must be PSIPHON_API_CLIENT_SESSION_ID_LENGTH lowercase hex-encoded bytes.
  1131  
  1132  	if config.SessionID == "" {
  1133  		sessionID, err := MakeSessionId()
  1134  		if err != nil {
  1135  			return errors.Trace(err)
  1136  		}
  1137  		config.SessionID = sessionID
  1138  	}
  1139  
  1140  	if len(config.SessionID) != 2*protocol.PSIPHON_API_CLIENT_SESSION_ID_LENGTH ||
  1141  		-1 != strings.IndexFunc(config.SessionID, func(c rune) bool {
  1142  			return !unicode.Is(unicode.ASCII_Hex_Digit, c) || unicode.IsUpper(c)
  1143  		}) {
  1144  		return errors.TraceNew("invalid SessionID")
  1145  	}
  1146  
  1147  	config.params, err = parameters.NewParameters(
  1148  		func(err error) {
  1149  			NoticeWarning("Parameters getValue failed: %s", err)
  1150  		})
  1151  	if err != nil {
  1152  		return errors.Trace(err)
  1153  	}
  1154  
  1155  	if config.ObfuscatedSSHAlgorithms != nil &&
  1156  		len(config.ObfuscatedSSHAlgorithms) != 4 {
  1157  		// TODO: validate each algorithm?
  1158  		return errors.TraceNew("invalid ObfuscatedSSHAlgorithms")
  1159  	}
  1160  
  1161  	// parametersParameters.Set will validate the config fields applied to
  1162  	// parametersParameters.
  1163  
  1164  	err = config.SetParameters("", false, nil)
  1165  	if err != nil {
  1166  		return errors.Trace(err)
  1167  	}
  1168  
  1169  	// Calculate and set the dial parameters hash. After this point, related
  1170  	// config fields must not change.
  1171  
  1172  	config.setDialParametersHash()
  1173  
  1174  	// Set defaults for dynamic config fields.
  1175  
  1176  	config.SetDynamicConfig(config.SponsorId, config.Authorizations)
  1177  
  1178  	// Initialize config.deviceBinder and config.config.networkIDGetter. These
  1179  	// wrap config.DeviceBinder and config.NetworkIDGetter/NetworkID with
  1180  	// loggers.
  1181  	//
  1182  	// New variables are set to avoid mutating input config fields.
  1183  	// Internally, code must use config.deviceBinder and
  1184  	// config.networkIDGetter and not the input/exported fields.
  1185  
  1186  	if config.DeviceBinder != nil {
  1187  		config.deviceBinder = newLoggingDeviceBinder(config.DeviceBinder)
  1188  	}
  1189  
  1190  	networkIDGetter := config.NetworkIDGetter
  1191  
  1192  	if networkIDGetter == nil {
  1193  		// Limitation: unlike NetworkIDGetter, which calls back to platform APIs
  1194  		// this method of network identification is not dynamic and will not reflect
  1195  		// network changes that occur while running.
  1196  		if config.NetworkID != "" {
  1197  			networkIDGetter = newStaticNetworkGetter(config.NetworkID)
  1198  		} else {
  1199  			networkIDGetter = newStaticNetworkGetter("UNKNOWN")
  1200  		}
  1201  	}
  1202  
  1203  	config.networkIDGetter = newLoggingNetworkIDGetter(networkIDGetter)
  1204  
  1205  	// Initialize config.clientFeatures, which adds feature names on top of
  1206  	// those specified by the host application in config.ClientFeatures.
  1207  
  1208  	config.clientFeatures = config.ClientFeatures
  1209  
  1210  	feature := "VPN"
  1211  	if config.DeviceBinder != nil && !common.Contains(config.clientFeatures, feature) {
  1212  		config.clientFeatures = append(config.clientFeatures, feature)
  1213  	}
  1214  
  1215  	// Migrate from old config fields. This results in files being moved under
  1216  	// a config specified data root directory.
  1217  	if migrateFromLegacyFields && needMigration {
  1218  
  1219  		// If unset, set MigrateDataStoreDirectory to the previous default value for
  1220  		// DataStoreDirectory to ensure that datastore files are migrated.
  1221  		if config.MigrateDataStoreDirectory == "" {
  1222  			wd, err := os.Getwd()
  1223  			if err != nil {
  1224  				return errors.Trace(err)
  1225  			}
  1226  			NoticeInfo("MigrateDataStoreDirectory unset, using working directory")
  1227  			config.MigrateDataStoreDirectory = wd
  1228  		}
  1229  
  1230  		// Move files that exist at legacy file paths under the data root
  1231  		// directory.
  1232  
  1233  		migrations, err := migrationsFromLegacyFilePaths(config)
  1234  		if err != nil {
  1235  			return errors.Trace(err)
  1236  		}
  1237  
  1238  		// Do migrations
  1239  
  1240  		successfulMigrations := 0
  1241  		for _, migration := range migrations {
  1242  			err := DoFileMigration(migration)
  1243  			if err != nil {
  1244  				NoticeWarning("Config migration: %s", errors.Trace(err))
  1245  			} else {
  1246  				successfulMigrations += 1
  1247  			}
  1248  		}
  1249  		NoticeInfo(fmt.Sprintf(
  1250  			"Config migration: %d/%d legacy files successfully migrated",
  1251  			successfulMigrations, len(migrations)))
  1252  
  1253  		// Remove OSL directory if empty
  1254  		if config.MigrateObfuscatedServerListDownloadDirectory != "" {
  1255  			files, err := ioutil.ReadDir(config.MigrateObfuscatedServerListDownloadDirectory)
  1256  			if err != nil {
  1257  				NoticeWarning(
  1258  					"Error reading OSL directory: %s",
  1259  					errors.Trace(common.RedactFilePathsError(err, config.MigrateObfuscatedServerListDownloadDirectory)))
  1260  			} else if len(files) == 0 {
  1261  				err := os.Remove(config.MigrateObfuscatedServerListDownloadDirectory)
  1262  				if err != nil {
  1263  					NoticeWarning(
  1264  						"Error deleting empty OSL directory: %s",
  1265  						errors.Trace(common.RedactFilePathsError(err, config.MigrateObfuscatedServerListDownloadDirectory)))
  1266  				}
  1267  			}
  1268  		}
  1269  
  1270  		f, err := os.Create(migrationCompleteFilePath)
  1271  		if err != nil {
  1272  			NoticeWarning(
  1273  				"Config migration: failed to create migration completed file with error %s",
  1274  				errors.Trace(common.RedactFilePathsError(err, migrationCompleteFilePath)))
  1275  		} else {
  1276  			NoticeInfo("Config migration: completed")
  1277  			f.Close()
  1278  		}
  1279  	}
  1280  
  1281  	config.committed = true
  1282  
  1283  	return nil
  1284  }
  1285  
  1286  // GetParameters returns the current parameters.Parameters.
  1287  func (config *Config) GetParameters() *parameters.Parameters {
  1288  	return config.params
  1289  }
  1290  
  1291  // SetParameters resets the parameters.Parameters to the default values,
  1292  // applies any config file values, and then applies the input parameters (from
  1293  // tactics, etc.)
  1294  //
  1295  // Set skipOnError to false when initially applying only config values, as
  1296  // this will validate the values and should fail. Set skipOnError to true when
  1297  // applying tactics to ignore invalid or unknown parameter values from tactics.
  1298  //
  1299  // In the case of applying tactics, do not call Config.parameters.Set
  1300  // directly as this will not first apply config values.
  1301  //
  1302  // If there is an error, the existing Config.parameters are left
  1303  // entirely unmodified.
  1304  func (config *Config) SetParameters(tag string, skipOnError bool, applyParameters map[string]interface{}) error {
  1305  
  1306  	setParameters := []map[string]interface{}{config.makeConfigParameters()}
  1307  	if applyParameters != nil {
  1308  		setParameters = append(setParameters, applyParameters)
  1309  	}
  1310  
  1311  	counts, err := config.params.Set(tag, skipOnError, setParameters...)
  1312  	if err != nil {
  1313  		return errors.Trace(err)
  1314  	}
  1315  
  1316  	NoticeInfo("applied %v parameters with tag '%s'", counts, tag)
  1317  
  1318  	// Emit certain individual parameter values for quick reference in diagnostics.
  1319  	p := config.params.Get()
  1320  	NoticeInfo(
  1321  		"NetworkLatencyMultiplier Min/Max/Lambda: %f/%f/%f",
  1322  		p.Float(parameters.NetworkLatencyMultiplierMin),
  1323  		p.Float(parameters.NetworkLatencyMultiplierMax),
  1324  		p.Float(parameters.NetworkLatencyMultiplierLambda))
  1325  
  1326  	// Application Parameters are feature flags/config info, delivered as Client
  1327  	// Parameters via tactics/etc., to be communicated to the outer application.
  1328  	// Emit these now, as notices.
  1329  	if p.WeightedCoinFlip(parameters.ApplicationParametersProbability) {
  1330  		NoticeApplicationParameters(p.KeyValues(parameters.ApplicationParameters))
  1331  	}
  1332  
  1333  	return nil
  1334  }
  1335  
  1336  // SetResolver sets the current resolver.
  1337  func (config *Config) SetResolver(resolver *resolver.Resolver) {
  1338  	config.resolverMutex.Lock()
  1339  	defer config.resolverMutex.Unlock()
  1340  	config.resolver = resolver
  1341  }
  1342  
  1343  // GetResolver returns the current resolver. May return nil.
  1344  func (config *Config) GetResolver() *resolver.Resolver {
  1345  	config.resolverMutex.Lock()
  1346  	defer config.resolverMutex.Unlock()
  1347  	return config.resolver
  1348  }
  1349  
  1350  // SetDynamicConfig sets the current client sponsor ID and authorizations.
  1351  // Invalid values for sponsor ID are ignored. The caller must not modify the
  1352  // input authorizations slice.
  1353  func (config *Config) SetDynamicConfig(sponsorID string, authorizations []string) {
  1354  	config.dynamicConfigMutex.Lock()
  1355  	defer config.dynamicConfigMutex.Unlock()
  1356  	if sponsorID != "" {
  1357  		config.sponsorID = sponsorID
  1358  	}
  1359  	config.authorizations = authorizations
  1360  }
  1361  
  1362  // GetSponsorID returns the current client sponsor ID.
  1363  func (config *Config) GetSponsorID() string {
  1364  	config.dynamicConfigMutex.Lock()
  1365  	defer config.dynamicConfigMutex.Unlock()
  1366  	return config.sponsorID
  1367  }
  1368  
  1369  // IsSplitTunnelEnabled indicates if split tunnel mode is enabled, either for
  1370  // the client's own country, a specified list of countries, or both.
  1371  func (config *Config) IsSplitTunnelEnabled() bool {
  1372  	return config.SplitTunnelOwnRegion || len(config.SplitTunnelRegions) > 0
  1373  }
  1374  
  1375  // GetAuthorizations returns the current client authorizations.
  1376  // The caller must not modify the returned slice.
  1377  func (config *Config) GetAuthorizations() []string {
  1378  	config.dynamicConfigMutex.Lock()
  1379  	defer config.dynamicConfigMutex.Unlock()
  1380  	return config.authorizations
  1381  }
  1382  
  1383  // GetPsiphonDataDirectory returns the directory under which all persistent
  1384  // files should be stored. This directory is created under
  1385  // config.DataRootDirectory. The motivation for an additional directory is that
  1386  // config.DataRootDirectory defaults to the current working directory, which may
  1387  // include non-tunnel-core files that should be excluded from directory-spanning
  1388  // operations (e.g. excluding all tunnel-core files from backup).
  1389  func (config *Config) GetPsiphonDataDirectory() string {
  1390  	return filepath.Join(config.DataRootDirectory, PsiphonDataDirectoryName)
  1391  }
  1392  
  1393  // GetHomePageFilename the path where the homepage notices file will be created.
  1394  func (config *Config) GetHomePageFilename() string {
  1395  	return filepath.Join(config.GetPsiphonDataDirectory(), HomepageFilename)
  1396  }
  1397  
  1398  // GetNoticesFilename returns the path where the notices file will be created.
  1399  // When the file is rotated it will be moved to config.GetOldNoticesFilename().
  1400  func (config *Config) GetNoticesFilename() string {
  1401  	return filepath.Join(config.GetPsiphonDataDirectory(), NoticesFilename)
  1402  }
  1403  
  1404  // GetOldNoticeFilename returns the path where the rotated notices file will be
  1405  // created.
  1406  func (config *Config) GetOldNoticesFilename() string {
  1407  	return filepath.Join(config.GetPsiphonDataDirectory(), OldNoticesFilename)
  1408  }
  1409  
  1410  // GetDataStoreDirectory returns the directory in which the persistent database
  1411  // will be stored. Created in Config.Commit(). The persistent database contains
  1412  // information such as server entries.
  1413  func (config *Config) GetDataStoreDirectory() string {
  1414  	return filepath.Join(config.GetPsiphonDataDirectory(), "datastore")
  1415  }
  1416  
  1417  // GetObfuscatedServerListDownloadDirectory returns the directory in which
  1418  // obfuscated remote server list downloads will be stored. Created in
  1419  // Config.Commit().
  1420  func (config *Config) GetObfuscatedServerListDownloadDirectory() string {
  1421  	return filepath.Join(config.GetPsiphonDataDirectory(), "osl")
  1422  }
  1423  
  1424  // GetRemoteServerListDownloadFilename returns the filename where the remote
  1425  // server list download will be stored. Data is stored in co-located files
  1426  // (RemoteServerListDownloadFilename.part*) to allow for resumable downloading.
  1427  func (config *Config) GetRemoteServerListDownloadFilename() string {
  1428  	return filepath.Join(config.GetPsiphonDataDirectory(), "remote_server_list")
  1429  }
  1430  
  1431  // GetUpgradeDownloadFilename specifies the filename where upgrade downloads
  1432  // will be stored. This filename is valid when UpgradeDownloadURLs
  1433  // (or UpgradeDownloadUrl) is specified. Data is stored in co-located files
  1434  // (UpgradeDownloadFilename.part*) to allow for resumable downloading.
  1435  func (config *Config) GetUpgradeDownloadFilename() string {
  1436  	return filepath.Join(config.GetPsiphonDataDirectory(), UpgradeDownloadFilename)
  1437  }
  1438  
  1439  // UseUpstreamProxy indicates if an upstream proxy has been
  1440  // configured.
  1441  func (config *Config) UseUpstreamProxy() bool {
  1442  	return config.UpstreamProxyURL != ""
  1443  }
  1444  
  1445  // GetNetworkID returns the current network ID. When NetworkIDGetter
  1446  // is set, this calls into the host application; otherwise, a default
  1447  // value is returned.
  1448  func (config *Config) GetNetworkID() string {
  1449  	return config.networkIDGetter.GetNetworkID()
  1450  }
  1451  
  1452  func (config *Config) makeConfigParameters() map[string]interface{} {
  1453  
  1454  	// Build set of config values to apply to parameters.
  1455  	//
  1456  	// Note: names of some config fields such as
  1457  	// StaggerConnectionWorkersMilliseconds and LimitMeekBufferSizes have
  1458  	// changed in the parameters. The existing config fields are retained for
  1459  	// backwards compatibility.
  1460  
  1461  	applyParameters := make(map[string]interface{})
  1462  
  1463  	// To support platform clients that configure NetworkLatencyMultiplier, set
  1464  	// the NetworkLatencyMultiplierMin/NetworkLatencyMultiplierMax range to the
  1465  	// specified value. Also set the older NetworkLatencyMultiplier tactic, since
  1466  	// that will be used in the case of replaying with dial parameters persisted
  1467  	// by an older client version.
  1468  	if config.NetworkLatencyMultiplier > 0.0 {
  1469  		applyParameters[parameters.NetworkLatencyMultiplier] = config.NetworkLatencyMultiplier
  1470  		applyParameters[parameters.NetworkLatencyMultiplierMin] = config.NetworkLatencyMultiplier
  1471  		applyParameters[parameters.NetworkLatencyMultiplierMax] = config.NetworkLatencyMultiplier
  1472  	}
  1473  
  1474  	if config.NetworkLatencyMultiplierMin > 0.0 {
  1475  		applyParameters[parameters.NetworkLatencyMultiplierMin] = config.NetworkLatencyMultiplierMin
  1476  	}
  1477  
  1478  	if config.NetworkLatencyMultiplierMax > 0.0 {
  1479  		applyParameters[parameters.NetworkLatencyMultiplierMax] = config.NetworkLatencyMultiplierMax
  1480  	}
  1481  
  1482  	if config.NetworkLatencyMultiplierLambda > 0.0 {
  1483  		applyParameters[parameters.NetworkLatencyMultiplierLambda] = config.NetworkLatencyMultiplierLambda
  1484  	}
  1485  
  1486  	if len(config.LimitTunnelProtocols) > 0 {
  1487  		applyParameters[parameters.LimitTunnelProtocols] = protocol.TunnelProtocols(config.LimitTunnelProtocols)
  1488  	}
  1489  
  1490  	if len(config.InitialLimitTunnelProtocols) > 0 && config.InitialLimitTunnelProtocolsCandidateCount > 0 {
  1491  		applyParameters[parameters.InitialLimitTunnelProtocols] = protocol.TunnelProtocols(config.InitialLimitTunnelProtocols)
  1492  		applyParameters[parameters.InitialLimitTunnelProtocolsCandidateCount] = config.InitialLimitTunnelProtocolsCandidateCount
  1493  	}
  1494  
  1495  	if len(config.LimitTLSProfiles) > 0 {
  1496  		applyParameters[parameters.LimitTLSProfiles] = protocol.TunnelProtocols(config.LimitTLSProfiles)
  1497  	}
  1498  
  1499  	if len(config.LimitQUICVersions) > 0 {
  1500  		applyParameters[parameters.LimitQUICVersions] = protocol.QUICVersions(config.LimitQUICVersions)
  1501  	}
  1502  
  1503  	if config.EstablishTunnelTimeoutSeconds != nil {
  1504  		applyParameters[parameters.EstablishTunnelTimeout] = fmt.Sprintf("%ds", *config.EstablishTunnelTimeoutSeconds)
  1505  	}
  1506  
  1507  	if config.EstablishTunnelServerAffinityGracePeriodMilliseconds != nil {
  1508  		applyParameters[parameters.EstablishTunnelServerAffinityGracePeriod] = fmt.Sprintf("%dms", *config.EstablishTunnelServerAffinityGracePeriodMilliseconds)
  1509  	}
  1510  
  1511  	if config.EstablishTunnelPausePeriodSeconds != nil {
  1512  		applyParameters[parameters.EstablishTunnelPausePeriod] = fmt.Sprintf("%ds", *config.EstablishTunnelPausePeriodSeconds)
  1513  	}
  1514  
  1515  	if config.ConnectionWorkerPoolSize != 0 {
  1516  		applyParameters[parameters.ConnectionWorkerPoolSize] = config.ConnectionWorkerPoolSize
  1517  	}
  1518  
  1519  	if config.TunnelPoolSize != 0 {
  1520  		applyParameters[parameters.TunnelPoolSize] = config.TunnelPoolSize
  1521  	}
  1522  
  1523  	if config.StaggerConnectionWorkersMilliseconds > 0 {
  1524  		applyParameters[parameters.StaggerConnectionWorkersPeriod] = fmt.Sprintf("%dms", config.StaggerConnectionWorkersMilliseconds)
  1525  	}
  1526  
  1527  	if config.LimitIntensiveConnectionWorkers > 0 {
  1528  		applyParameters[parameters.LimitIntensiveConnectionWorkers] = config.LimitIntensiveConnectionWorkers
  1529  	}
  1530  
  1531  	applyParameters[parameters.MeekLimitBufferSizes] = config.LimitMeekBufferSizes
  1532  
  1533  	applyParameters[parameters.IgnoreHandshakeStatsRegexps] = config.IgnoreHandshakeStatsRegexps
  1534  
  1535  	if config.EstablishTunnelTimeoutSeconds != nil {
  1536  		applyParameters[parameters.EstablishTunnelTimeout] = fmt.Sprintf("%ds", *config.EstablishTunnelTimeoutSeconds)
  1537  	}
  1538  
  1539  	if config.FetchRemoteServerListRetryPeriodMilliseconds != nil {
  1540  		applyParameters[parameters.FetchRemoteServerListRetryPeriod] = fmt.Sprintf("%dms", *config.FetchRemoteServerListRetryPeriodMilliseconds)
  1541  	}
  1542  
  1543  	if config.FetchUpgradeRetryPeriodMilliseconds != nil {
  1544  		applyParameters[parameters.FetchUpgradeRetryPeriod] = fmt.Sprintf("%dms", *config.FetchUpgradeRetryPeriodMilliseconds)
  1545  	}
  1546  
  1547  	if !config.DisableRemoteServerListFetcher {
  1548  
  1549  		if config.RemoteServerListURLs != nil {
  1550  			applyParameters[parameters.RemoteServerListSignaturePublicKey] = config.RemoteServerListSignaturePublicKey
  1551  			applyParameters[parameters.RemoteServerListURLs] = config.RemoteServerListURLs
  1552  		}
  1553  
  1554  		if config.ObfuscatedServerListRootURLs != nil {
  1555  			applyParameters[parameters.RemoteServerListSignaturePublicKey] = config.RemoteServerListSignaturePublicKey
  1556  			applyParameters[parameters.ObfuscatedServerListRootURLs] = config.ObfuscatedServerListRootURLs
  1557  		}
  1558  
  1559  	}
  1560  
  1561  	if config.UpgradeDownloadURLs != nil {
  1562  		applyParameters[parameters.UpgradeDownloadClientVersionHeader] = config.UpgradeDownloadClientVersionHeader
  1563  		applyParameters[parameters.UpgradeDownloadURLs] = config.UpgradeDownloadURLs
  1564  	}
  1565  
  1566  	if len(config.FeedbackUploadURLs) > 0 {
  1567  		applyParameters[parameters.FeedbackUploadURLs] = config.FeedbackUploadURLs
  1568  	}
  1569  
  1570  	if config.FeedbackEncryptionPublicKey != "" {
  1571  		applyParameters[parameters.FeedbackEncryptionPublicKey] = config.FeedbackEncryptionPublicKey
  1572  	}
  1573  
  1574  	applyParameters[parameters.TunnelRateLimits] = config.RateLimits
  1575  
  1576  	if config.TransformHostNameProbability != nil {
  1577  		applyParameters[parameters.TransformHostNameProbability] = *config.TransformHostNameProbability
  1578  	}
  1579  
  1580  	if config.FragmentorProbability != nil {
  1581  		applyParameters[parameters.FragmentorProbability] = *config.FragmentorProbability
  1582  	}
  1583  
  1584  	if len(config.FragmentorLimitProtocols) > 0 {
  1585  		applyParameters[parameters.FragmentorLimitProtocols] = protocol.TunnelProtocols(config.FragmentorLimitProtocols)
  1586  	}
  1587  
  1588  	if config.FragmentorMinTotalBytes != nil {
  1589  		applyParameters[parameters.FragmentorMinTotalBytes] = *config.FragmentorMinTotalBytes
  1590  	}
  1591  
  1592  	if config.FragmentorMaxTotalBytes != nil {
  1593  		applyParameters[parameters.FragmentorMaxTotalBytes] = *config.FragmentorMaxTotalBytes
  1594  	}
  1595  
  1596  	if config.FragmentorMinWriteBytes != nil {
  1597  		applyParameters[parameters.FragmentorMinWriteBytes] = *config.FragmentorMinWriteBytes
  1598  	}
  1599  
  1600  	if config.FragmentorMaxWriteBytes != nil {
  1601  		applyParameters[parameters.FragmentorMaxWriteBytes] = *config.FragmentorMaxWriteBytes
  1602  	}
  1603  
  1604  	if config.FragmentorMinDelayMicroseconds != nil {
  1605  		applyParameters[parameters.FragmentorMinDelay] = fmt.Sprintf("%dus", *config.FragmentorMinDelayMicroseconds)
  1606  	}
  1607  
  1608  	if config.FragmentorMaxDelayMicroseconds != nil {
  1609  		applyParameters[parameters.FragmentorMaxDelay] = fmt.Sprintf("%dus", *config.FragmentorMaxDelayMicroseconds)
  1610  	}
  1611  
  1612  	if config.MeekTrafficShapingProbability != nil {
  1613  		applyParameters[parameters.MeekTrafficShapingProbability] = *config.MeekTrafficShapingProbability
  1614  	}
  1615  
  1616  	if len(config.MeekTrafficShapingLimitProtocols) > 0 {
  1617  		applyParameters[parameters.MeekTrafficShapingLimitProtocols] = protocol.TunnelProtocols(config.MeekTrafficShapingLimitProtocols)
  1618  	}
  1619  
  1620  	if config.MeekMinTLSPadding != nil {
  1621  		applyParameters[parameters.MeekMinTLSPadding] = *config.MeekMinTLSPadding
  1622  	}
  1623  
  1624  	if config.MeekMaxTLSPadding != nil {
  1625  		applyParameters[parameters.MeekMaxTLSPadding] = *config.MeekMaxTLSPadding
  1626  	}
  1627  
  1628  	if config.MeekMinLimitRequestPayloadLength != nil {
  1629  		applyParameters[parameters.MeekMinLimitRequestPayloadLength] = *config.MeekMinLimitRequestPayloadLength
  1630  	}
  1631  
  1632  	if config.MeekMaxLimitRequestPayloadLength != nil {
  1633  		applyParameters[parameters.MeekMaxLimitRequestPayloadLength] = *config.MeekMaxLimitRequestPayloadLength
  1634  	}
  1635  
  1636  	if config.MeekRedialTLSProbability != nil {
  1637  		applyParameters[parameters.MeekRedialTLSProbability] = *config.MeekRedialTLSProbability
  1638  	}
  1639  
  1640  	if config.MeekAlternateCookieNameProbability != nil {
  1641  		applyParameters[parameters.MeekAlternateCookieNameProbability] = *config.MeekAlternateCookieNameProbability
  1642  	}
  1643  
  1644  	if config.MeekAlternateContentTypeProbability != nil {
  1645  		applyParameters[parameters.MeekAlternateContentTypeProbability] = *config.MeekAlternateContentTypeProbability
  1646  	}
  1647  
  1648  	if config.ObfuscatedSSHMinPadding != nil {
  1649  		applyParameters[parameters.ObfuscatedSSHMinPadding] = *config.ObfuscatedSSHMinPadding
  1650  	}
  1651  
  1652  	if config.ObfuscatedSSHMaxPadding != nil {
  1653  		applyParameters[parameters.ObfuscatedSSHMaxPadding] = *config.ObfuscatedSSHMaxPadding
  1654  	}
  1655  
  1656  	if config.LivenessTestMinUpstreamBytes != nil {
  1657  		applyParameters[parameters.LivenessTestMinUpstreamBytes] = *config.LivenessTestMinUpstreamBytes
  1658  	}
  1659  
  1660  	if config.LivenessTestMaxUpstreamBytes != nil {
  1661  		applyParameters[parameters.LivenessTestMaxUpstreamBytes] = *config.LivenessTestMaxUpstreamBytes
  1662  	}
  1663  
  1664  	if config.LivenessTestMinDownstreamBytes != nil {
  1665  		applyParameters[parameters.LivenessTestMinDownstreamBytes] = *config.LivenessTestMinDownstreamBytes
  1666  	}
  1667  
  1668  	if config.LivenessTestMaxDownstreamBytes != nil {
  1669  		applyParameters[parameters.LivenessTestMaxDownstreamBytes] = *config.LivenessTestMaxDownstreamBytes
  1670  	}
  1671  
  1672  	if config.ReplayCandidateCount != nil {
  1673  		applyParameters[parameters.ReplayCandidateCount] = *config.ReplayCandidateCount
  1674  	}
  1675  
  1676  	if config.ReplayDialParametersTTLSeconds != nil {
  1677  		applyParameters[parameters.ReplayDialParametersTTL] = fmt.Sprintf("%ds", *config.ReplayDialParametersTTLSeconds)
  1678  	}
  1679  
  1680  	if config.ReplayTargetUpstreamBytes != nil {
  1681  		applyParameters[parameters.ReplayTargetUpstreamBytes] = *config.ReplayTargetUpstreamBytes
  1682  	}
  1683  
  1684  	if config.ReplayTargetDownstreamBytes != nil {
  1685  		applyParameters[parameters.ReplayTargetDownstreamBytes] = *config.ReplayTargetDownstreamBytes
  1686  	}
  1687  
  1688  	if config.ReplayTargetTunnelDurationSeconds != nil {
  1689  		applyParameters[parameters.ReplayTargetTunnelDuration] = fmt.Sprintf("%ds", *config.ReplayTargetTunnelDurationSeconds)
  1690  	}
  1691  
  1692  	if config.ReplayLaterRoundMoveToFrontProbability != nil {
  1693  		applyParameters[parameters.ReplayLaterRoundMoveToFrontProbability] = *config.ReplayLaterRoundMoveToFrontProbability
  1694  	}
  1695  
  1696  	if config.ReplayRetainFailedProbability != nil {
  1697  		applyParameters[parameters.ReplayRetainFailedProbability] = *config.ReplayRetainFailedProbability
  1698  	}
  1699  
  1700  	if config.UseOnlyCustomTLSProfiles != nil {
  1701  		applyParameters[parameters.UseOnlyCustomTLSProfiles] = *config.UseOnlyCustomTLSProfiles
  1702  	}
  1703  
  1704  	if len(config.CustomTLSProfiles) > 0 {
  1705  		applyParameters[parameters.CustomTLSProfiles] = config.CustomTLSProfiles
  1706  	}
  1707  
  1708  	if config.SelectRandomizedTLSProfileProbability != nil {
  1709  		applyParameters[parameters.SelectRandomizedTLSProfileProbability] = *config.SelectRandomizedTLSProfileProbability
  1710  	}
  1711  
  1712  	if config.NoDefaultTLSSessionIDProbability != nil {
  1713  		applyParameters[parameters.NoDefaultTLSSessionIDProbability] = *config.NoDefaultTLSSessionIDProbability
  1714  	}
  1715  
  1716  	if len(config.DisableFrontingProviderTLSProfiles) > 0 {
  1717  		applyParameters[parameters.DisableFrontingProviderTLSProfiles] = config.DisableFrontingProviderTLSProfiles
  1718  	}
  1719  
  1720  	if config.ClientBurstUpstreamTargetBytes != nil {
  1721  		applyParameters[parameters.ClientBurstUpstreamTargetBytes] = *config.ClientBurstUpstreamTargetBytes
  1722  	}
  1723  
  1724  	if config.ClientBurstUpstreamDeadlineMilliseconds != nil {
  1725  		applyParameters[parameters.ClientBurstUpstreamDeadline] = fmt.Sprintf("%dms", *config.ClientBurstUpstreamDeadlineMilliseconds)
  1726  	}
  1727  
  1728  	if config.ClientBurstDownstreamTargetBytes != nil {
  1729  		applyParameters[parameters.ClientBurstDownstreamTargetBytes] = *config.ClientBurstDownstreamTargetBytes
  1730  	}
  1731  
  1732  	if config.ClientBurstDownstreamDeadlineMilliseconds != nil {
  1733  		applyParameters[parameters.ClientBurstDownstreamDeadline] = fmt.Sprintf("%dms", *config.ClientBurstDownstreamDeadlineMilliseconds)
  1734  	}
  1735  
  1736  	if config.ApplicationParameters != nil {
  1737  		applyParameters[parameters.ApplicationParameters] = config.ApplicationParameters
  1738  	}
  1739  
  1740  	if config.CustomHostNameRegexes != nil {
  1741  		applyParameters[parameters.CustomHostNameRegexes] = parameters.RegexStrings(config.CustomHostNameRegexes)
  1742  	}
  1743  
  1744  	if config.CustomHostNameProbability != nil {
  1745  		applyParameters[parameters.CustomHostNameProbability] = *config.CustomHostNameProbability
  1746  	}
  1747  
  1748  	if config.CustomHostNameLimitProtocols != nil {
  1749  		applyParameters[parameters.CustomHostNameLimitProtocols] = protocol.TunnelProtocols(config.CustomHostNameLimitProtocols)
  1750  	}
  1751  
  1752  	if config.ConjureCachedRegistrationTTLSeconds != nil {
  1753  		applyParameters[parameters.ConjureCachedRegistrationTTL] = fmt.Sprintf("%ds", *config.ConjureCachedRegistrationTTLSeconds)
  1754  	}
  1755  
  1756  	if config.ConjureAPIRegistrarBidirectionalURL != "" {
  1757  		applyParameters[parameters.ConjureAPIRegistrarBidirectionalURL] = config.ConjureAPIRegistrarBidirectionalURL
  1758  	}
  1759  
  1760  	if len(config.ConjureAPIRegistrarFrontingSpecs) > 0 {
  1761  		applyParameters[parameters.ConjureAPIRegistrarFrontingSpecs] = config.ConjureAPIRegistrarFrontingSpecs
  1762  	}
  1763  
  1764  	if config.ConjureAPIRegistrarMinDelayMilliseconds != nil {
  1765  		applyParameters[parameters.ConjureAPIRegistrarMinDelay] = fmt.Sprintf("%dms", *config.ConjureAPIRegistrarMinDelayMilliseconds)
  1766  	}
  1767  
  1768  	if config.ConjureAPIRegistrarMaxDelayMilliseconds != nil {
  1769  		applyParameters[parameters.ConjureAPIRegistrarMaxDelay] = fmt.Sprintf("%dms", *config.ConjureAPIRegistrarMaxDelayMilliseconds)
  1770  	}
  1771  
  1772  	if config.ConjureDecoyRegistrarProbability != nil {
  1773  		applyParameters[parameters.ConjureDecoyRegistrarProbability] = *config.ConjureDecoyRegistrarProbability
  1774  	}
  1775  
  1776  	if config.ConjureDecoyRegistrarWidth != nil {
  1777  		applyParameters[parameters.ConjureDecoyRegistrarWidth] = *config.ConjureDecoyRegistrarWidth
  1778  	}
  1779  
  1780  	if config.ConjureDecoyRegistrarMinDelayMilliseconds != nil {
  1781  		applyParameters[parameters.ConjureDecoyRegistrarMinDelay] = fmt.Sprintf("%dms", *config.ConjureDecoyRegistrarMinDelayMilliseconds)
  1782  	}
  1783  
  1784  	if config.ConjureDecoyRegistrarMaxDelayMilliseconds != nil {
  1785  		applyParameters[parameters.ConjureDecoyRegistrarMaxDelay] = fmt.Sprintf("%dms", *config.ConjureDecoyRegistrarMaxDelayMilliseconds)
  1786  	}
  1787  
  1788  	if config.HoldOffTunnelMinDurationMilliseconds != nil {
  1789  		applyParameters[parameters.HoldOffTunnelMinDuration] = fmt.Sprintf("%dms", *config.HoldOffTunnelMinDurationMilliseconds)
  1790  	}
  1791  
  1792  	if config.HoldOffTunnelMaxDurationMilliseconds != nil {
  1793  		applyParameters[parameters.HoldOffTunnelMaxDuration] = fmt.Sprintf("%dms", *config.HoldOffTunnelMaxDurationMilliseconds)
  1794  	}
  1795  
  1796  	if len(config.HoldOffTunnelProtocols) > 0 {
  1797  		applyParameters[parameters.HoldOffTunnelProtocols] = protocol.TunnelProtocols(config.HoldOffTunnelProtocols)
  1798  	}
  1799  
  1800  	if len(config.HoldOffTunnelFrontingProviderIDs) > 0 {
  1801  		applyParameters[parameters.HoldOffTunnelFrontingProviderIDs] = config.HoldOffTunnelFrontingProviderIDs
  1802  	}
  1803  
  1804  	if config.HoldOffTunnelProbability != nil {
  1805  		applyParameters[parameters.HoldOffTunnelProbability] = *config.HoldOffTunnelProbability
  1806  	}
  1807  
  1808  	if len(config.RestrictFrontingProviderIDs) > 0 {
  1809  		applyParameters[parameters.RestrictFrontingProviderIDs] = config.RestrictFrontingProviderIDs
  1810  	}
  1811  
  1812  	if config.RestrictFrontingProviderIDsClientProbability != nil {
  1813  		applyParameters[parameters.RestrictFrontingProviderIDsClientProbability] = *config.RestrictFrontingProviderIDsClientProbability
  1814  	}
  1815  
  1816  	if config.UpstreamProxyAllowAllServerEntrySources != nil {
  1817  		applyParameters[parameters.UpstreamProxyAllowAllServerEntrySources] = *config.UpstreamProxyAllowAllServerEntrySources
  1818  	}
  1819  
  1820  	if len(config.LimitTunnelDialPortNumbers) > 0 {
  1821  		applyParameters[parameters.LimitTunnelDialPortNumbers] = config.LimitTunnelDialPortNumbers
  1822  	}
  1823  
  1824  	if config.QUICDisablePathMTUDiscoveryProbability != nil {
  1825  		applyParameters[parameters.QUICDisableClientPathMTUDiscoveryProbability] = *config.QUICDisablePathMTUDiscoveryProbability
  1826  	}
  1827  
  1828  	if config.DNSResolverAttemptsPerServer != nil {
  1829  		applyParameters[parameters.DNSResolverAttemptsPerServer] = *config.DNSResolverAttemptsPerServer
  1830  	}
  1831  
  1832  	if config.DNSResolverAttemptsPerPreferredServer != nil {
  1833  		applyParameters[parameters.DNSResolverAttemptsPerPreferredServer] = *config.DNSResolverAttemptsPerPreferredServer
  1834  	}
  1835  
  1836  	if config.DNSResolverRequestTimeoutMilliseconds != nil {
  1837  		applyParameters[parameters.DNSResolverRequestTimeout] = fmt.Sprintf("%dms", *config.DNSResolverRequestTimeoutMilliseconds)
  1838  	}
  1839  
  1840  	if config.DNSResolverAwaitTimeoutMilliseconds != nil {
  1841  		applyParameters[parameters.DNSResolverAwaitTimeout] = fmt.Sprintf("%dms", *config.DNSResolverAwaitTimeoutMilliseconds)
  1842  	}
  1843  
  1844  	if config.DNSResolverPreresolvedIPAddressProbability != nil {
  1845  		applyParameters[parameters.DNSResolverPreresolvedIPAddressProbability] = *config.DNSResolverPreresolvedIPAddressProbability
  1846  	}
  1847  
  1848  	if config.DNSResolverPreresolvedIPAddressCIDRs != nil {
  1849  		applyParameters[parameters.DNSResolverPreresolvedIPAddressCIDRs] = config.DNSResolverPreresolvedIPAddressCIDRs
  1850  	}
  1851  
  1852  	if config.DNSResolverAlternateServers != nil {
  1853  		applyParameters[parameters.DNSResolverAlternateServers] = config.DNSResolverAlternateServers
  1854  	}
  1855  
  1856  	if config.DNSResolverPreferredAlternateServers != nil {
  1857  		applyParameters[parameters.DNSResolverPreferredAlternateServers] = config.DNSResolverPreferredAlternateServers
  1858  	}
  1859  
  1860  	if config.DNSResolverPreferAlternateServerProbability != nil {
  1861  		applyParameters[parameters.DNSResolverPreferAlternateServerProbability] = *config.DNSResolverPreferAlternateServerProbability
  1862  	}
  1863  
  1864  	if config.DNSResolverProtocolTransformSpecs != nil {
  1865  		applyParameters[parameters.DNSResolverProtocolTransformSpecs] = config.DNSResolverProtocolTransformSpecs
  1866  	}
  1867  
  1868  	if config.DNSResolverProtocolTransformScopedSpecNames != nil {
  1869  		applyParameters[parameters.DNSResolverProtocolTransformScopedSpecNames] = config.DNSResolverProtocolTransformScopedSpecNames
  1870  	}
  1871  
  1872  	if config.DNSResolverProtocolTransformProbability != nil {
  1873  		applyParameters[parameters.DNSResolverProtocolTransformProbability] = *config.DNSResolverProtocolTransformProbability
  1874  	}
  1875  
  1876  	if config.DNSResolverIncludeEDNS0Probability != nil {
  1877  		applyParameters[parameters.DNSResolverIncludeEDNS0Probability] = *config.DNSResolverIncludeEDNS0Probability
  1878  	}
  1879  
  1880  	if config.DNSResolverCacheExtensionInitialTTLMilliseconds != nil {
  1881  		applyParameters[parameters.DNSResolverCacheExtensionInitialTTL] = fmt.Sprintf("%dms", *config.DNSResolverCacheExtensionInitialTTLMilliseconds)
  1882  	}
  1883  
  1884  	if config.DNSResolverCacheExtensionVerifiedTTLMilliseconds != nil {
  1885  		applyParameters[parameters.DNSResolverCacheExtensionVerifiedTTL] = fmt.Sprintf("%dms", *config.DNSResolverCacheExtensionVerifiedTTLMilliseconds)
  1886  	}
  1887  
  1888  	// When adding new config dial parameters that may override tactics, also
  1889  	// update setDialParametersHash.
  1890  
  1891  	return applyParameters
  1892  }
  1893  
  1894  func (config *Config) setDialParametersHash() {
  1895  
  1896  	// Calculate and store a hash of the config values that may impact
  1897  	// dial parameters. This hash is used as part of the dial parameters
  1898  	// replay mechanism to detect when persisted dial parameters should
  1899  	// be discarded due to conflicting config changes.
  1900  	//
  1901  	// With a couple of minor exceptions, configuring dial parameters via the
  1902  	// config is intended for testing only, and so these parameters are expected
  1903  	// to be present in test runs only. It remains an important case to discard
  1904  	// replay dial parameters when test config parameters are varied.
  1905  	//
  1906  	// Hashing the parameter names detects some ambiguous hash cases, such as two
  1907  	// consecutive int64 parameters, one omitted and one not, that are flipped.
  1908  	// The serialization is not completely unambiguous, and the format is
  1909  	// currently limited by legacy cases (not invalidating replay dial parameters
  1910  	// for production clients is more important than invalidating for test runs).
  1911  	// We cannot hash the entire config JSON as it contains non-dial parameter
  1912  	// fields which may frequently change across runs.
  1913  	//
  1914  	// MD5 hash is used solely as a data checksum and not for any security
  1915  	// purpose.
  1916  
  1917  	hash := md5.New()
  1918  
  1919  	if len(config.LimitTunnelProtocols) > 0 {
  1920  		hash.Write([]byte("LimitTunnelProtocols"))
  1921  		for _, protocol := range config.LimitTunnelProtocols {
  1922  			hash.Write([]byte(protocol))
  1923  		}
  1924  	}
  1925  
  1926  	if len(config.InitialLimitTunnelProtocols) > 0 && config.InitialLimitTunnelProtocolsCandidateCount > 0 {
  1927  		hash.Write([]byte("InitialLimitTunnelProtocols"))
  1928  		for _, protocol := range config.InitialLimitTunnelProtocols {
  1929  			hash.Write([]byte(protocol))
  1930  		}
  1931  		binary.Write(hash, binary.LittleEndian, int64(config.InitialLimitTunnelProtocolsCandidateCount))
  1932  	}
  1933  
  1934  	if len(config.LimitTLSProfiles) > 0 {
  1935  		hash.Write([]byte("LimitTLSProfiles"))
  1936  		for _, profile := range config.LimitTLSProfiles {
  1937  			hash.Write([]byte(profile))
  1938  		}
  1939  	}
  1940  
  1941  	if len(config.LimitQUICVersions) > 0 {
  1942  		hash.Write([]byte("LimitQUICVersions"))
  1943  		for _, version := range config.LimitQUICVersions {
  1944  			hash.Write([]byte(version))
  1945  		}
  1946  	}
  1947  
  1948  	// Whether a custom User-Agent is specified is a binary flag: when not set,
  1949  	// the replay dial parameters value applies. When set, external
  1950  	// considerations apply.
  1951  	if _, ok := config.CustomHeaders["User-Agent"]; ok {
  1952  		hash.Write([]byte("CustomHeaders User-Agent"))
  1953  		hash.Write([]byte{1})
  1954  	}
  1955  
  1956  	if config.UpstreamProxyURL != "" {
  1957  		hash.Write([]byte("UpstreamProxyURL"))
  1958  		hash.Write([]byte(config.UpstreamProxyURL))
  1959  	}
  1960  
  1961  	if config.TransformHostNameProbability != nil {
  1962  		hash.Write([]byte("TransformHostNameProbability"))
  1963  		binary.Write(hash, binary.LittleEndian, *config.TransformHostNameProbability)
  1964  	}
  1965  
  1966  	if config.FragmentorProbability != nil {
  1967  		hash.Write([]byte("FragmentorProbability"))
  1968  		binary.Write(hash, binary.LittleEndian, *config.FragmentorProbability)
  1969  	}
  1970  
  1971  	if len(config.FragmentorLimitProtocols) > 0 {
  1972  		hash.Write([]byte("FragmentorLimitProtocols"))
  1973  		for _, protocol := range config.FragmentorLimitProtocols {
  1974  			hash.Write([]byte(protocol))
  1975  		}
  1976  	}
  1977  
  1978  	if config.FragmentorMinTotalBytes != nil {
  1979  		hash.Write([]byte("FragmentorMinTotalBytes"))
  1980  		binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMinTotalBytes))
  1981  	}
  1982  
  1983  	if config.FragmentorMaxTotalBytes != nil {
  1984  		hash.Write([]byte("FragmentorMaxTotalBytes"))
  1985  		binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMaxTotalBytes))
  1986  	}
  1987  
  1988  	if config.FragmentorMinWriteBytes != nil {
  1989  		hash.Write([]byte("FragmentorMinWriteBytes"))
  1990  		binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMinWriteBytes))
  1991  	}
  1992  
  1993  	if config.FragmentorMaxWriteBytes != nil {
  1994  		hash.Write([]byte("FragmentorMaxWriteBytes"))
  1995  		binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMaxWriteBytes))
  1996  	}
  1997  
  1998  	if config.FragmentorMinDelayMicroseconds != nil {
  1999  		hash.Write([]byte("FragmentorMinDelayMicroseconds"))
  2000  		binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMinDelayMicroseconds))
  2001  	}
  2002  
  2003  	if config.FragmentorMaxDelayMicroseconds != nil {
  2004  		hash.Write([]byte("FragmentorMaxDelayMicroseconds"))
  2005  		binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMaxDelayMicroseconds))
  2006  	}
  2007  
  2008  	if config.MeekTrafficShapingProbability != nil {
  2009  		hash.Write([]byte("MeekTrafficShapingProbability"))
  2010  		binary.Write(hash, binary.LittleEndian, int64(*config.MeekTrafficShapingProbability))
  2011  	}
  2012  
  2013  	if len(config.MeekTrafficShapingLimitProtocols) > 0 {
  2014  		hash.Write([]byte("MeekTrafficShapingLimitProtocols"))
  2015  		for _, protocol := range config.MeekTrafficShapingLimitProtocols {
  2016  			hash.Write([]byte(protocol))
  2017  		}
  2018  	}
  2019  
  2020  	if config.MeekMinLimitRequestPayloadLength != nil {
  2021  		hash.Write([]byte("MeekMinLimitRequestPayloadLength"))
  2022  		binary.Write(hash, binary.LittleEndian, int64(*config.MeekMinLimitRequestPayloadLength))
  2023  	}
  2024  
  2025  	if config.MeekMaxLimitRequestPayloadLength != nil {
  2026  		hash.Write([]byte("MeekMaxLimitRequestPayloadLength"))
  2027  		binary.Write(hash, binary.LittleEndian, int64(*config.MeekMaxLimitRequestPayloadLength))
  2028  	}
  2029  
  2030  	if config.MeekRedialTLSProbability != nil {
  2031  		hash.Write([]byte("MeekRedialTLSProbability"))
  2032  		binary.Write(hash, binary.LittleEndian, *config.MeekRedialTLSProbability)
  2033  	}
  2034  
  2035  	if config.ObfuscatedSSHMinPadding != nil {
  2036  		hash.Write([]byte("ObfuscatedSSHMinPadding"))
  2037  		binary.Write(hash, binary.LittleEndian, int64(*config.ObfuscatedSSHMinPadding))
  2038  	}
  2039  
  2040  	if config.ObfuscatedSSHMaxPadding != nil {
  2041  		hash.Write([]byte("ObfuscatedSSHMaxPadding"))
  2042  		binary.Write(hash, binary.LittleEndian, int64(*config.ObfuscatedSSHMaxPadding))
  2043  	}
  2044  
  2045  	if config.LivenessTestMinUpstreamBytes != nil {
  2046  		hash.Write([]byte("LivenessTestMinUpstreamBytes"))
  2047  		binary.Write(hash, binary.LittleEndian, int64(*config.LivenessTestMinUpstreamBytes))
  2048  	}
  2049  
  2050  	if config.LivenessTestMaxUpstreamBytes != nil {
  2051  		hash.Write([]byte("LivenessTestMaxUpstreamBytes"))
  2052  		binary.Write(hash, binary.LittleEndian, int64(*config.LivenessTestMaxUpstreamBytes))
  2053  	}
  2054  
  2055  	if config.LivenessTestMinDownstreamBytes != nil {
  2056  		hash.Write([]byte("LivenessTestMinDownstreamBytes"))
  2057  		binary.Write(hash, binary.LittleEndian, int64(*config.LivenessTestMinDownstreamBytes))
  2058  	}
  2059  
  2060  	if config.LivenessTestMaxDownstreamBytes != nil {
  2061  		hash.Write([]byte("LivenessTestMaxDownstreamBytes"))
  2062  		binary.Write(hash, binary.LittleEndian, int64(*config.LivenessTestMaxDownstreamBytes))
  2063  	}
  2064  
  2065  	// Legacy case: these parameters are included in the hash unconditionally,
  2066  	// and so will impact almost all production clients. These parameter names
  2067  	// are not hashed since that would invalidate all replay dial parameters for
  2068  	// existing clients whose hashes predate the inclusion of parameter names.
  2069  	binary.Write(hash, binary.LittleEndian, config.NetworkLatencyMultiplierMin)
  2070  	binary.Write(hash, binary.LittleEndian, config.NetworkLatencyMultiplierMax)
  2071  	binary.Write(hash, binary.LittleEndian, config.NetworkLatencyMultiplierLambda)
  2072  
  2073  	if config.UseOnlyCustomTLSProfiles != nil {
  2074  		hash.Write([]byte("UseOnlyCustomTLSProfiles"))
  2075  		binary.Write(hash, binary.LittleEndian, *config.UseOnlyCustomTLSProfiles)
  2076  	}
  2077  
  2078  	if len(config.CustomTLSProfiles) > 0 {
  2079  		hash.Write([]byte("CustomTLSProfiles"))
  2080  		for _, customTLSProfile := range config.CustomTLSProfiles {
  2081  			encodedCustomTLSProofile, _ := json.Marshal(customTLSProfile)
  2082  			hash.Write(encodedCustomTLSProofile)
  2083  		}
  2084  	}
  2085  
  2086  	if config.SelectRandomizedTLSProfileProbability != nil {
  2087  		hash.Write([]byte("SelectRandomizedTLSProfileProbability"))
  2088  		binary.Write(hash, binary.LittleEndian, *config.SelectRandomizedTLSProfileProbability)
  2089  	}
  2090  
  2091  	if config.NoDefaultTLSSessionIDProbability != nil {
  2092  		hash.Write([]byte("NoDefaultTLSSessionIDProbability"))
  2093  		binary.Write(hash, binary.LittleEndian, *config.NoDefaultTLSSessionIDProbability)
  2094  	}
  2095  
  2096  	if len(config.DisableFrontingProviderTLSProfiles) > 0 {
  2097  		hash.Write([]byte("DisableFrontingProviderTLSProfiles"))
  2098  		encodedDisableFrontingProviderTLSProfiles, _ :=
  2099  			json.Marshal(config.DisableFrontingProviderTLSProfiles)
  2100  		hash.Write(encodedDisableFrontingProviderTLSProfiles)
  2101  	}
  2102  
  2103  	if len(config.CustomHostNameRegexes) > 0 {
  2104  		hash.Write([]byte("CustomHostNameRegexes"))
  2105  		for _, customHostNameRegex := range config.CustomHostNameRegexes {
  2106  			hash.Write([]byte(customHostNameRegex))
  2107  		}
  2108  	}
  2109  
  2110  	if config.CustomHostNameProbability != nil {
  2111  		hash.Write([]byte("CustomHostNameProbability"))
  2112  		binary.Write(hash, binary.LittleEndian, *config.CustomHostNameProbability)
  2113  	}
  2114  
  2115  	if len(config.CustomHostNameLimitProtocols) > 0 {
  2116  		hash.Write([]byte("CustomHostNameLimitProtocols"))
  2117  		for _, protocol := range config.CustomHostNameLimitProtocols {
  2118  			hash.Write([]byte(protocol))
  2119  		}
  2120  	}
  2121  
  2122  	if config.ConjureCachedRegistrationTTLSeconds != nil {
  2123  		hash.Write([]byte("ConjureCachedRegistrationTTLSeconds"))
  2124  		binary.Write(hash, binary.LittleEndian, int64(*config.ConjureCachedRegistrationTTLSeconds))
  2125  	}
  2126  
  2127  	if config.ConjureAPIRegistrarBidirectionalURL != "" {
  2128  		hash.Write([]byte("ConjureAPIRegistrarBidirectionalURL"))
  2129  		hash.Write([]byte(config.ConjureAPIRegistrarBidirectionalURL))
  2130  	}
  2131  
  2132  	if len(config.ConjureAPIRegistrarFrontingSpecs) > 0 {
  2133  		hash.Write([]byte("ConjureAPIRegistrarFrontingSpecs"))
  2134  		for _, frontingSpec := range config.ConjureAPIRegistrarFrontingSpecs {
  2135  			encodedFrontSpec, _ := json.Marshal(frontingSpec)
  2136  			hash.Write(encodedFrontSpec)
  2137  		}
  2138  	}
  2139  
  2140  	if config.ConjureAPIRegistrarMinDelayMilliseconds != nil {
  2141  		hash.Write([]byte("ConjureAPIRegistrarMinDelayMilliseconds"))
  2142  		binary.Write(hash, binary.LittleEndian, int64(*config.ConjureAPIRegistrarMinDelayMilliseconds))
  2143  	}
  2144  
  2145  	if config.ConjureAPIRegistrarMaxDelayMilliseconds != nil {
  2146  		hash.Write([]byte("ConjureAPIRegistrarMaxDelayMilliseconds"))
  2147  		binary.Write(hash, binary.LittleEndian, int64(*config.ConjureAPIRegistrarMaxDelayMilliseconds))
  2148  	}
  2149  
  2150  	if config.ConjureDecoyRegistrarWidth != nil {
  2151  		hash.Write([]byte("ConjureDecoyRegistrarWidth"))
  2152  		binary.Write(hash, binary.LittleEndian, int64(*config.ConjureDecoyRegistrarWidth))
  2153  	}
  2154  
  2155  	if config.ConjureDecoyRegistrarMinDelayMilliseconds != nil {
  2156  		hash.Write([]byte("ConjureDecoyRegistrarMinDelayMilliseconds"))
  2157  		binary.Write(hash, binary.LittleEndian, int64(*config.ConjureDecoyRegistrarMinDelayMilliseconds))
  2158  	}
  2159  
  2160  	if config.ConjureDecoyRegistrarMaxDelayMilliseconds != nil {
  2161  		hash.Write([]byte("ConjureDecoyRegistrarMaxDelayMilliseconds"))
  2162  		binary.Write(hash, binary.LittleEndian, int64(*config.ConjureDecoyRegistrarMaxDelayMilliseconds))
  2163  	}
  2164  
  2165  	if config.HoldOffTunnelMinDurationMilliseconds != nil {
  2166  		hash.Write([]byte("HoldOffTunnelMinDurationMilliseconds"))
  2167  		binary.Write(hash, binary.LittleEndian, int64(*config.HoldOffTunnelMinDurationMilliseconds))
  2168  	}
  2169  
  2170  	if config.HoldOffTunnelMaxDurationMilliseconds != nil {
  2171  		hash.Write([]byte("HoldOffTunnelMaxDurationMilliseconds"))
  2172  		binary.Write(hash, binary.LittleEndian, int64(*config.HoldOffTunnelMaxDurationMilliseconds))
  2173  	}
  2174  
  2175  	if len(config.HoldOffTunnelProtocols) > 0 {
  2176  		hash.Write([]byte("HoldOffTunnelProtocols"))
  2177  		for _, protocol := range config.HoldOffTunnelProtocols {
  2178  			hash.Write([]byte(protocol))
  2179  		}
  2180  	}
  2181  
  2182  	if len(config.HoldOffTunnelFrontingProviderIDs) > 0 {
  2183  		hash.Write([]byte("HoldOffTunnelFrontingProviderIDs"))
  2184  		for _, providerID := range config.HoldOffTunnelFrontingProviderIDs {
  2185  			hash.Write([]byte(providerID))
  2186  		}
  2187  	}
  2188  
  2189  	if config.HoldOffTunnelProbability != nil {
  2190  		hash.Write([]byte("HoldOffTunnelProbability"))
  2191  		binary.Write(hash, binary.LittleEndian, *config.HoldOffTunnelProbability)
  2192  	}
  2193  
  2194  	if len(config.RestrictFrontingProviderIDs) > 0 {
  2195  		hash.Write([]byte("RestrictFrontingProviderIDs"))
  2196  		for _, providerID := range config.RestrictFrontingProviderIDs {
  2197  			hash.Write([]byte(providerID))
  2198  		}
  2199  	}
  2200  
  2201  	if config.RestrictFrontingProviderIDsClientProbability != nil {
  2202  		hash.Write([]byte("RestrictFrontingProviderIDsClientProbability"))
  2203  		binary.Write(hash, binary.LittleEndian, *config.RestrictFrontingProviderIDsClientProbability)
  2204  	}
  2205  
  2206  	if config.UpstreamProxyAllowAllServerEntrySources != nil {
  2207  		hash.Write([]byte("UpstreamProxyAllowAllServerEntrySources"))
  2208  		binary.Write(hash, binary.LittleEndian, *config.UpstreamProxyAllowAllServerEntrySources)
  2209  	}
  2210  
  2211  	if len(config.LimitTunnelDialPortNumbers) > 0 {
  2212  		hash.Write([]byte("LimitTunnelDialPortNumbers"))
  2213  		encodedLimitTunnelDialPortNumbers, _ :=
  2214  			json.Marshal(config.LimitTunnelDialPortNumbers)
  2215  		hash.Write(encodedLimitTunnelDialPortNumbers)
  2216  	}
  2217  
  2218  	if config.QUICDisablePathMTUDiscoveryProbability != nil {
  2219  		hash.Write([]byte("QUICDisablePathMTUDiscoveryProbability"))
  2220  		binary.Write(hash, binary.LittleEndian, *config.QUICDisablePathMTUDiscoveryProbability)
  2221  	}
  2222  
  2223  	if config.DNSResolverAttemptsPerServer != nil {
  2224  		hash.Write([]byte("DNSResolverAttemptsPerServer"))
  2225  		binary.Write(hash, binary.LittleEndian, int64(*config.DNSResolverAttemptsPerServer))
  2226  	}
  2227  
  2228  	if config.DNSResolverRequestTimeoutMilliseconds != nil {
  2229  		hash.Write([]byte("DNSResolverRequestTimeoutMilliseconds"))
  2230  		binary.Write(hash, binary.LittleEndian, int64(*config.DNSResolverRequestTimeoutMilliseconds))
  2231  	}
  2232  
  2233  	if config.DNSResolverAwaitTimeoutMilliseconds != nil {
  2234  		hash.Write([]byte("DNSResolverAwaitTimeoutMilliseconds"))
  2235  		binary.Write(hash, binary.LittleEndian, int64(*config.DNSResolverAwaitTimeoutMilliseconds))
  2236  	}
  2237  
  2238  	if config.DNSResolverPreresolvedIPAddressCIDRs != nil {
  2239  		hash.Write([]byte("DNSResolverPreresolvedIPAddressCIDRs"))
  2240  		encodedDNSResolverPreresolvedIPAddressCIDRs, _ :=
  2241  			json.Marshal(config.DNSResolverPreresolvedIPAddressCIDRs)
  2242  		hash.Write(encodedDNSResolverPreresolvedIPAddressCIDRs)
  2243  	}
  2244  
  2245  	if config.DNSResolverPreresolvedIPAddressProbability != nil {
  2246  		hash.Write([]byte("DNSResolverPreresolvedIPAddressProbability"))
  2247  		binary.Write(hash, binary.LittleEndian, *config.DNSResolverPreresolvedIPAddressProbability)
  2248  	}
  2249  
  2250  	if config.DNSResolverAlternateServers != nil {
  2251  		hash.Write([]byte("DNSResolverAlternateServers"))
  2252  		for _, server := range config.DNSResolverAlternateServers {
  2253  			hash.Write([]byte(server))
  2254  		}
  2255  	}
  2256  
  2257  	if config.DNSResolverPreferAlternateServerProbability != nil {
  2258  		hash.Write([]byte("DNSResolverPreferAlternateServerProbability"))
  2259  		binary.Write(hash, binary.LittleEndian, *config.DNSResolverPreferAlternateServerProbability)
  2260  	}
  2261  
  2262  	if config.DNSResolverProtocolTransformSpecs != nil {
  2263  		hash.Write([]byte("DNSResolverProtocolTransformSpecs"))
  2264  		encodedDNSResolverProtocolTransformSpecs, _ :=
  2265  			json.Marshal(config.DNSResolverProtocolTransformSpecs)
  2266  		hash.Write(encodedDNSResolverProtocolTransformSpecs)
  2267  	}
  2268  
  2269  	if config.DNSResolverProtocolTransformScopedSpecNames != nil {
  2270  		hash.Write([]byte(""))
  2271  		encodedDNSResolverProtocolTransformScopedSpecNames, _ :=
  2272  			json.Marshal(config.DNSResolverProtocolTransformScopedSpecNames)
  2273  		hash.Write(encodedDNSResolverProtocolTransformScopedSpecNames)
  2274  	}
  2275  
  2276  	if config.DNSResolverProtocolTransformProbability != nil {
  2277  		hash.Write([]byte("DNSResolverProtocolTransformProbability"))
  2278  		binary.Write(hash, binary.LittleEndian, *config.DNSResolverProtocolTransformProbability)
  2279  	}
  2280  
  2281  	if config.DNSResolverIncludeEDNS0Probability != nil {
  2282  		hash.Write([]byte("DNSResolverIncludeEDNS0Probability"))
  2283  		binary.Write(hash, binary.LittleEndian, *config.DNSResolverIncludeEDNS0Probability)
  2284  	}
  2285  
  2286  	if config.DNSResolverCacheExtensionInitialTTLMilliseconds != nil {
  2287  		hash.Write([]byte("DNSResolverCacheExtensionInitialTTLMilliseconds"))
  2288  		binary.Write(hash, binary.LittleEndian, int64(*config.DNSResolverCacheExtensionInitialTTLMilliseconds))
  2289  	}
  2290  
  2291  	if config.DNSResolverCacheExtensionVerifiedTTLMilliseconds != nil {
  2292  		hash.Write([]byte("DNSResolverCacheExtensionVerifiedTTLMilliseconds"))
  2293  		binary.Write(hash, binary.LittleEndian, int64(*config.DNSResolverCacheExtensionVerifiedTTLMilliseconds))
  2294  	}
  2295  
  2296  	config.dialParametersHash = hash.Sum(nil)
  2297  }
  2298  
  2299  func promoteLegacyTransferURL(URL string) parameters.TransferURLs {
  2300  	transferURLs := make(parameters.TransferURLs, 1)
  2301  	transferURLs[0] = &parameters.TransferURL{
  2302  		URL:               base64.StdEncoding.EncodeToString([]byte(URL)),
  2303  		SkipVerify:        false,
  2304  		OnlyAfterAttempts: 0,
  2305  	}
  2306  	return transferURLs
  2307  }
  2308  
  2309  type loggingDeviceBinder struct {
  2310  	d DeviceBinder
  2311  }
  2312  
  2313  func newLoggingDeviceBinder(d DeviceBinder) *loggingDeviceBinder {
  2314  	return &loggingDeviceBinder{d: d}
  2315  }
  2316  
  2317  func (d *loggingDeviceBinder) BindToDevice(fileDescriptor int) (string, error) {
  2318  	deviceInfo, err := d.d.BindToDevice(fileDescriptor)
  2319  	if err == nil && deviceInfo != "" {
  2320  		NoticeBindToDevice(deviceInfo)
  2321  	}
  2322  	return deviceInfo, err
  2323  }
  2324  
  2325  type staticNetworkGetter struct {
  2326  	networkID string
  2327  }
  2328  
  2329  func newStaticNetworkGetter(networkID string) *staticNetworkGetter {
  2330  	return &staticNetworkGetter{networkID: networkID}
  2331  }
  2332  
  2333  func (n *staticNetworkGetter) GetNetworkID() string {
  2334  	return n.networkID
  2335  }
  2336  
  2337  type loggingNetworkIDGetter struct {
  2338  	n NetworkIDGetter
  2339  }
  2340  
  2341  func newLoggingNetworkIDGetter(n NetworkIDGetter) *loggingNetworkIDGetter {
  2342  	return &loggingNetworkIDGetter{n: n}
  2343  }
  2344  
  2345  func (n *loggingNetworkIDGetter) GetNetworkID() string {
  2346  	networkID := n.n.GetNetworkID()
  2347  
  2348  	// All PII must appear after the initial "-"
  2349  	// See: https://godoc.org/github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon#NetworkIDGetter
  2350  	logNetworkID := networkID
  2351  	index := strings.Index(logNetworkID, "-")
  2352  	if index != -1 {
  2353  		logNetworkID = logNetworkID[:index]
  2354  	}
  2355  	if len(logNetworkID)+1 < len(networkID) {
  2356  		// Indicate when additional network info was present after the first "-".
  2357  		logNetworkID += "+[redacted]"
  2358  	}
  2359  	NoticeNetworkID(logNetworkID)
  2360  
  2361  	return networkID
  2362  }
  2363  
  2364  // migrationsFromLegacyNoticeFilePaths returns the file migrations which must be
  2365  // performed to move notice files from legacy file paths, which were configured
  2366  // with the legacy config fields HomepageNoticesFilename and
  2367  // RotatingNoticesFilename, to the new file paths used by Psiphon which exist
  2368  // under the data root directory.
  2369  func migrationsFromLegacyNoticeFilePaths(config *Config) []FileMigration {
  2370  	var noticeMigrations []FileMigration
  2371  
  2372  	if config.MigrateHomepageNoticesFilename != "" {
  2373  		noticeMigrations = append(noticeMigrations, FileMigration{
  2374  			Name:    "hompage",
  2375  			OldPath: config.MigrateHomepageNoticesFilename,
  2376  			NewPath: config.GetHomePageFilename(),
  2377  		})
  2378  	}
  2379  
  2380  	if config.MigrateRotatingNoticesFilename != "" {
  2381  		migrations := []FileMigration{
  2382  			{
  2383  				Name:    "notices",
  2384  				OldPath: config.MigrateRotatingNoticesFilename,
  2385  				NewPath: config.GetNoticesFilename(),
  2386  				IsDir:   false,
  2387  			},
  2388  			{
  2389  				Name:    "notices.1",
  2390  				OldPath: config.MigrateRotatingNoticesFilename + ".1",
  2391  				NewPath: config.GetNoticesFilename() + ".1",
  2392  			},
  2393  		}
  2394  		noticeMigrations = append(noticeMigrations, migrations...)
  2395  	}
  2396  
  2397  	return noticeMigrations
  2398  }
  2399  
  2400  // migrationsFromLegacyFilePaths returns the file migrations which must be
  2401  // performed to move files from legacy file paths, which were configured with
  2402  // legacy config fields, to the new file paths used by Psiphon which exist
  2403  // under the data root directory.
  2404  // Note: an attempt is made to redact any file paths from the returned error.
  2405  func migrationsFromLegacyFilePaths(config *Config) ([]FileMigration, error) {
  2406  
  2407  	migrations := []FileMigration{
  2408  		{
  2409  			Name:    "psiphon.boltdb",
  2410  			OldPath: filepath.Join(config.MigrateDataStoreDirectory, "psiphon.boltdb"),
  2411  			NewPath: filepath.Join(config.GetDataStoreDirectory(), "psiphon.boltdb"),
  2412  		},
  2413  		{
  2414  			Name:    "psiphon.boltdb.lock",
  2415  			OldPath: filepath.Join(config.MigrateDataStoreDirectory, "psiphon.boltdb.lock"),
  2416  			NewPath: filepath.Join(config.GetDataStoreDirectory(), "psiphon.boltdb.lock"),
  2417  		},
  2418  	}
  2419  
  2420  	if config.MigrateRemoteServerListDownloadFilename != "" {
  2421  
  2422  		// Migrate remote server list files
  2423  
  2424  		rslMigrations := []FileMigration{
  2425  			{
  2426  				Name:    "remote_server_list",
  2427  				OldPath: config.MigrateRemoteServerListDownloadFilename,
  2428  				NewPath: config.GetRemoteServerListDownloadFilename(),
  2429  			},
  2430  			{
  2431  				Name:    "remote_server_list.part",
  2432  				OldPath: config.MigrateRemoteServerListDownloadFilename + ".part",
  2433  				NewPath: config.GetRemoteServerListDownloadFilename() + ".part",
  2434  			},
  2435  			{
  2436  				Name:    "remote_server_list.part.etag",
  2437  				OldPath: config.MigrateRemoteServerListDownloadFilename + ".part.etag",
  2438  				NewPath: config.GetRemoteServerListDownloadFilename() + ".part.etag",
  2439  			},
  2440  		}
  2441  
  2442  		migrations = append(migrations, rslMigrations...)
  2443  	}
  2444  
  2445  	if config.MigrateObfuscatedServerListDownloadDirectory != "" {
  2446  
  2447  		// Migrate OSL registry file and downloads
  2448  
  2449  		oslFileRegex, err := regexp.Compile(`^osl-.+$`)
  2450  		if err != nil {
  2451  			return nil, errors.TraceMsg(err, "failed to compile regex for osl files")
  2452  		}
  2453  
  2454  		files, err := ioutil.ReadDir(config.MigrateObfuscatedServerListDownloadDirectory)
  2455  		if err != nil {
  2456  			NoticeWarning(
  2457  				"Migration: failed to read OSL download directory with error %s",
  2458  				common.RedactFilePathsError(err, config.MigrateObfuscatedServerListDownloadDirectory))
  2459  		} else {
  2460  			for _, file := range files {
  2461  				if oslFileRegex.MatchString(file.Name()) {
  2462  					fileMigration := FileMigration{
  2463  						Name:    "osl",
  2464  						OldPath: filepath.Join(config.MigrateObfuscatedServerListDownloadDirectory, file.Name()),
  2465  						NewPath: filepath.Join(config.GetObfuscatedServerListDownloadDirectory(), file.Name()),
  2466  					}
  2467  					migrations = append(migrations, fileMigration)
  2468  				}
  2469  			}
  2470  		}
  2471  	}
  2472  
  2473  	if config.MigrateUpgradeDownloadFilename != "" {
  2474  
  2475  		// Migrate downloaded upgrade files
  2476  
  2477  		oldUpgradeDownloadFilename := filepath.Base(config.MigrateUpgradeDownloadFilename)
  2478  
  2479  		// Create regex for:
  2480  		// <old_upgrade_download_filename>
  2481  		// <old_upgrade_download_filename>.<client_version_number>
  2482  		// <old_upgrade_download_filename>.<client_version_number>.part
  2483  		// <old_upgrade_download_filename>.<client_version_number>.part.etag
  2484  		upgradeDownloadFileRegex, err := regexp.Compile(`^` + oldUpgradeDownloadFilename + `(\.\d+(\.part(\.etag)?)?)?$`)
  2485  		if err != nil {
  2486  			return nil, errors.TraceMsg(err, "failed to compile regex for upgrade files")
  2487  		}
  2488  
  2489  		upgradeDownloadDir := filepath.Dir(config.MigrateUpgradeDownloadFilename)
  2490  
  2491  		files, err := ioutil.ReadDir(upgradeDownloadDir)
  2492  		if err != nil {
  2493  			NoticeWarning(
  2494  				"Migration: failed to read upgrade download directory with error %s",
  2495  				common.RedactFilePathsError(err, upgradeDownloadDir))
  2496  		} else {
  2497  
  2498  			for _, file := range files {
  2499  
  2500  				if upgradeDownloadFileRegex.MatchString(file.Name()) {
  2501  
  2502  					oldFileSuffix := strings.TrimPrefix(file.Name(), oldUpgradeDownloadFilename)
  2503  
  2504  					fileMigration := FileMigration{
  2505  						Name:    "upgrade",
  2506  						OldPath: filepath.Join(upgradeDownloadDir, file.Name()),
  2507  						NewPath: config.GetUpgradeDownloadFilename() + oldFileSuffix,
  2508  					}
  2509  					migrations = append(migrations, fileMigration)
  2510  				}
  2511  			}
  2512  		}
  2513  	}
  2514  
  2515  	return migrations, nil
  2516  }