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