| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383 |
- /*
- * Copyright (c) 2015, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- package psiphon
- import (
- "crypto/md5"
- "encoding/base64"
- "encoding/binary"
- "encoding/json"
- "fmt"
- "net/http"
- "os"
- "strconv"
- "strings"
- "sync"
- "unicode"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
- )
- const (
- TUNNEL_POOL_SIZE = 1
- )
- // Config is the Psiphon configuration specified by the application. This
- // configuration controls the behavior of the core tunnel functionality.
- //
- // To distinguish omitted timeout params from explicit 0 value timeout params,
- // corresponding fieldss are int pointers. nil means no value was supplied and
- // to use the default; a non-nil pointer to 0 means no timeout.
- type Config struct {
- // DataStoreDirectory is the directory in which to store the persistent
- // database, which contains information such as server entries. By
- // default, current working directory.
- //
- // Warning: If the datastore file, DataStoreDirectory/DATA_STORE_FILENAME,
- // exists but fails to open for any reason (checksum error, unexpected
- // file format, etc.) it will be deleted in order to pave a new datastore
- // and continue running.
- DataStoreDirectory string
- // PropagationChannelId is a string identifier which indicates how the
- // Psiphon client was distributed. This parameter is required. This value
- // is supplied by and depends on the Psiphon Network, and is typically
- // embedded in the client binary.
- PropagationChannelId string
- // SponsorId is a string identifier which indicates who is sponsoring this
- // Psiphon client. One purpose of this value is to determine the home
- // pages for display. This parameter is required. This value is supplied
- // by and depends on the Psiphon Network, and is typically embedded in the
- // client binary.
- SponsorId string
- // ClientVersion is the client version number that the client reports to
- // the server. The version number refers to the host client application,
- // not the core tunnel library. One purpose of this value is to enable
- // automatic updates. This value is supplied by and depends on the Psiphon
- // Network, and is typically embedded in the client binary.
- //
- // Note that sending a ClientPlatform string which includes "windows"
- // (case insensitive) and a ClientVersion of <= 44 will cause an error in
- // processing the response to DoConnectedRequest calls.
- ClientVersion string
- // ClientPlatform is the client platform ("Windows", "Android", etc.) that
- // the client reports to the server.
- ClientPlatform string
- // TunnelWholeDevice is a flag that is passed through to the handshake
- // request for stats purposes. Set to 1 when the host application is
- // tunneling the whole device, 0 otherwise.
- TunnelWholeDevice int
- // EgressRegion is a ISO 3166-1 alpha-2 country code which indicates which
- // country to egress from. For the default, "", the best performing server
- // in any country is selected.
- EgressRegion string
- // ListenInterface specifies which interface to listen on. If no
- // interface is provided then listen on 127.0.0.1. If 'any' is provided
- // then use 0.0.0.0. If there are multiple IP addresses on an interface
- // use the first IPv4 address.
- ListenInterface string
- // DisableLocalSocksProxy disables running the local SOCKS proxy.
- DisableLocalSocksProxy bool
- // LocalSocksProxyPort specifies a port number for the local SOCKS proxy
- // running at 127.0.0.1. For the default value, 0, the system selects a
- // free port (a notice reporting the selected port is emitted).
- LocalSocksProxyPort int
- // LocalHttpProxyPort specifies a port number for the local HTTP proxy
- // running at 127.0.0.1. For the default value, 0, the system selects a
- // free port (a notice reporting the selected port is emitted).
- LocalHttpProxyPort int
- // DisableLocalHTTPProxy disables running the local HTTP proxy.
- DisableLocalHTTPProxy bool
- // NetworkLatencyMultiplier is a multiplier that is to be applied to
- // default network event timeouts. Set this to tune performance for
- // slow networks.
- // When set, must be >= 1.0.
- NetworkLatencyMultiplier float64
- // TunnelProtocol indicates which protocol to use. For the default, "",
- // all protocols are used.
- //
- // Deprecated: Use LimitTunnelProtocols. When LimitTunnelProtocols is not
- // nil, this parameter is ignored.
- TunnelProtocol string
- // LimitTunnelProtocols indicates which protocols to use. Valid values
- // include:
- // "SSH", "OSSH", "UNFRONTED-MEEK-OSSH", "UNFRONTED-MEEK-HTTPS-OSSH",
- // "UNFRONTED-MEEK-SESSION-TICKET-OSSH", "FRONTED-MEEK-OSSH",
- // "FRONTED-MEEK-HTTP-OSSH", "QUIC-OSSH", "MARIONETTE-OSSH", and
- // "TAPDANCE-OSSH".
- // For the default, an empty list, all protocols are used.
- LimitTunnelProtocols []string
- // InitialLimitTunnelProtocols is an optional initial phase of limited
- // protocols for the first InitialLimitTunnelProtocolsCandidateCount
- // candidates; after these candidates, LimitTunnelProtocols applies.
- //
- // For the default, an empty list, InitialLimitTunnelProtocols is off.
- InitialLimitTunnelProtocols []string
- // InitialLimitTunnelProtocolsCandidateCount is the number of candidates
- // to which InitialLimitTunnelProtocols is applied instead of
- // LimitTunnelProtocols.
- //
- // For the default, 0, InitialLimitTunnelProtocols is off.
- InitialLimitTunnelProtocolsCandidateCount int
- // LimitTLSProfiles indicates which TLS profiles to select from. Valid
- // values are listed in protocols.SupportedTLSProfiles.
- // For the default, an empty list, all profiles are candidates for
- // selection.
- LimitTLSProfiles []string
- // LimitQUICVersions indicates which QUIC versions to select from. Valid
- // values are listed in protocols.SupportedQUICVersions.
- // For the default, an empty list, all versions are candidates for
- // selection.
- LimitQUICVersions []string
- // EstablishTunnelTimeoutSeconds specifies a time limit after which to
- // halt the core tunnel controller if no tunnel has been established. The
- // default is parameters.EstablishTunnelTimeout.
- EstablishTunnelTimeoutSeconds *int
- // EstablishTunnelPausePeriodSeconds specifies the delay between attempts
- // to establish tunnels. Briefly pausing allows for network conditions to
- // improve and for asynchronous operations such as fetch remote server
- // list to complete. If omitted, a default value is used. This value is
- // typical overridden for testing.
- EstablishTunnelPausePeriodSeconds *int
- // EstablishTunnelPausePeriodSeconds specifies the grace period, or head
- // start, provided to the affinity server candidate when establishing. The
- // affinity server is the server used for the last established tunnel.
- EstablishTunnelServerAffinityGracePeriodMilliseconds *int
- // ConnectionWorkerPoolSize specifies how many connection attempts to
- // attempt in parallel. If omitted of when 0, a default is used; this is
- // recommended.
- ConnectionWorkerPoolSize int
- // TunnelPoolSize specifies how many tunnels to run in parallel. Port
- // forwards are multiplexed over multiple tunnels. If omitted or when 0,
- // the default is TUNNEL_POOL_SIZE, which is recommended.
- TunnelPoolSize int
- // StaggerConnectionWorkersMilliseconds adds a specified delay before
- // making each server candidate available to connection workers. This
- // option is enabled when StaggerConnectionWorkersMilliseconds > 0.
- StaggerConnectionWorkersMilliseconds int
- // LimitIntensiveConnectionWorkers limits the number of concurrent
- // connection workers attempting connections with resource intensive
- // protocols. This option is enabled when LimitIntensiveConnectionWorkers
- // > 0.
- LimitIntensiveConnectionWorkers int
- // LimitMeekBufferSizes selects smaller buffers for meek protocols.
- LimitMeekBufferSizes bool
- // IgnoreHandshakeStatsRegexps skips compiling and using stats regexes.
- IgnoreHandshakeStatsRegexps bool
- // UpstreamProxyURL is a URL specifying an upstream proxy to use for all
- // outbound connections. The URL should include proxy type and
- // authentication information, as required. See example URLs here:
- // https://github.com/Psiphon-Labs/psiphon-tunnel-core/tree/master/psiphon/upstreamproxy
- UpstreamProxyURL string
- // CustomHeaders is a set of additional arbitrary HTTP headers that are
- // added to all plaintext HTTP requests and requests made through an HTTP
- // upstream proxy when specified by UpstreamProxyURL.
- CustomHeaders http.Header
- // Deprecated: Use CustomHeaders. When CustomHeaders is not nil, this
- // parameter is ignored.
- UpstreamProxyCustomHeaders http.Header
- // NetworkConnectivityChecker is an interface that enables tunnel-core to
- // call into the host application to check for network connectivity. See:
- // NetworkConnectivityChecker doc.
- //
- // This parameter is only applicable to library deployments.
- NetworkConnectivityChecker NetworkConnectivityChecker
- // DeviceBinder is an interface that enables tunnel-core to call into the
- // host application to bind sockets to specific devices. See: DeviceBinder
- // doc.
- //
- // This parameter is only applicable to library deployments.
- DeviceBinder DeviceBinder
- // IPv6Synthesizer is an interface that allows tunnel-core to call into
- // the host application to synthesize IPv6 addresses. See: IPv6Synthesizer
- // doc.
- //
- // This parameter is only applicable to library deployments.
- IPv6Synthesizer IPv6Synthesizer
- // DnsServerGetter is an interface that enables tunnel-core to call into
- // the host application to discover the native network DNS server
- // settings. See: DnsServerGetter doc.
- //
- // This parameter is only applicable to library deployments.
- DnsServerGetter DnsServerGetter
- // NetworkIDGetter in an interface that enables tunnel-core to call into
- // the host application to get an identifier for the host's current active
- // network. See: NetworkIDGetter doc.
- //
- // This parameter is only applicable to library deployments.
- NetworkIDGetter NetworkIDGetter
- // NetworkID, when not blank, is used as the identifier for the host's
- // current active network.
- // NetworkID is ignored when NetworkIDGetter is set.
- NetworkID string
- // DisableTactics disables tactics operations including requests, payload
- // handling, and application of parameters.
- DisableTactics bool
- // DisableReplay causes any persisted dial parameters to be ignored when
- // they would otherwise be used for replay.
- DisableReplay bool
- // TargetServerEntry is an encoded server entry. When specified, this
- // server entry is used exclusively and all other known servers are
- // ignored; also, when set, ConnectionWorkerPoolSize is ignored and
- // the pool size is 1.
- TargetServerEntry string
- // DisableApi disables Psiphon server API calls including handshake,
- // connected, status, etc. This is used for special case temporary tunnels
- // (Windows VPN mode).
- DisableApi bool
- // TargetApiProtocol specifies whether to force use of "ssh" or "web" API
- // protocol. When blank, the default, the optimal API protocol is used.
- // Note that this capability check is not applied before the
- // "CandidateServers" count is emitted.
- //
- // This parameter is intended for testing and debugging only. Not all
- // parameters are supported in the legacy "web" API protocol, including
- // speed test samples.
- TargetApiProtocol string
- // RemoteServerListUrl is a URL which specifies a location to fetch out-
- // of-band server entries. This facility is used when a tunnel cannot be
- // established to known servers. This value is supplied by and depends on
- // the Psiphon Network, and is typically embedded in the client binary.
- //
- // Deprecated: Use RemoteServerListURLs. When RemoteServerListURLs is not
- // nil, this parameter is ignored.
- RemoteServerListUrl string
- // RemoteServerListURLs is list of URLs which specify locations to fetch
- // out-of-band server entries. This facility is used when a tunnel cannot
- // be established to known servers. This value is supplied by and depends
- // on the Psiphon Network, and is typically embedded in the client binary.
- // All URLs must point to the same entity with the same ETag. At least one
- // DownloadURL must have OnlyAfterAttempts = 0.
- RemoteServerListURLs parameters.DownloadURLs
- // RemoteServerListDownloadFilename specifies a target filename for
- // storing the remote server list download. Data is stored in co-located
- // files (RemoteServerListDownloadFilename.part*) to allow for resumable
- // downloading.
- RemoteServerListDownloadFilename string
- // RemoteServerListSignaturePublicKey specifies a public key that's used
- // to authenticate the remote server list payload. This value is supplied
- // by and depends on the Psiphon Network, and is typically embedded in the
- // client binary.
- RemoteServerListSignaturePublicKey string
- // DisableRemoteServerListFetcher disables fetching remote server lists.
- // This is used for special case temporary tunnels.
- DisableRemoteServerListFetcher bool
- // FetchRemoteServerListRetryPeriodMilliseconds specifies the delay before
- // resuming a remote server list download after a failure. If omitted, a
- // default value is used. This value is typical overridden for testing.
- FetchRemoteServerListRetryPeriodMilliseconds *int
- // ObfuscatedServerListRootURL is a URL which specifies the root location
- // from which to fetch obfuscated server list files. This value is
- // supplied by and depends on the Psiphon Network, and is typically
- // embedded in the client binary.
- //
- // Deprecated: Use ObfuscatedServerListRootURLs. When
- // ObfuscatedServerListRootURLs is not nil, this parameter is ignored.
- ObfuscatedServerListRootURL string
- // ObfuscatedServerListRootURLs is a list of URLs which specify root
- // locations from which to fetch obfuscated server list files. This value
- // is supplied by and depends on the Psiphon Network, and is typically
- // embedded in the client binary. All URLs must point to the same entity
- // with the same ETag. At least one DownloadURL must have
- // OnlyAfterAttempts = 0.
- ObfuscatedServerListRootURLs parameters.DownloadURLs
- // ObfuscatedServerListDownloadDirectory specifies a target directory for
- // storing the obfuscated remote server list downloads. Data is stored in
- // co-located files (<OSL filename>.part*) to allow for resumable
- // downloading.
- ObfuscatedServerListDownloadDirectory string
- // SplitTunnelRoutesURLFormat is a URL which specifies the location of a
- // routes file to use for split tunnel mode. The URL must include a
- // placeholder for the client region to be supplied. Split tunnel mode
- // uses the routes file to classify port forward destinations as foreign
- // or domestic and does not tunnel domestic destinations. Split tunnel
- // mode is on when all the SplitTunnel parameters are supplied. This value
- // is supplied by and depends on the Psiphon Network, and is typically
- // embedded in the client binary.
- SplitTunnelRoutesURLFormat string
- // SplitTunnelRoutesSignaturePublicKey specifies a public key that's used
- // to authenticate the split tunnel routes payload. This value is supplied
- // by and depends on the Psiphon Network, and is typically embedded in the
- // client binary.
- SplitTunnelRoutesSignaturePublicKey string
- // SplitTunnelDNSServer specifies a DNS server to use when resolving port
- // forward target domain names to IP addresses for classification. The DNS
- // server must support TCP requests.
- SplitTunnelDNSServer string
- // UpgradeDownloadUrl specifies a URL from which to download a host client
- // upgrade file, when one is available. The core tunnel controller
- // provides a resumable download facility which downloads this resource
- // and emits a notice when complete. This value is supplied by and depends
- // on the Psiphon Network, and is typically embedded in the client binary.
- //
- // Deprecated: Use UpgradeDownloadURLs. When UpgradeDownloadURLs is not
- // nil, this parameter is ignored.
- UpgradeDownloadUrl string
- // UpgradeDownloadURLs is list of URLs which specify locations from which
- // to download a host client upgrade file, when one is available. The core
- // tunnel controller provides a resumable download facility which
- // downloads this resource and emits a notice when complete. This value is
- // supplied by and depends on the Psiphon Network, and is typically
- // embedded in the client binary. All URLs must point to the same entity
- // with the same ETag. At least one DownloadURL must have
- // OnlyAfterAttempts = 0.
- UpgradeDownloadURLs parameters.DownloadURLs
- // UpgradeDownloadClientVersionHeader specifies the HTTP header name for
- // the entity at UpgradeDownloadURLs which specifies the client version
- // (an integer value). A HEAD request may be made to check the version
- // number available at UpgradeDownloadURLs.
- // UpgradeDownloadClientVersionHeader is required when UpgradeDownloadURLs
- // is specified.
- UpgradeDownloadClientVersionHeader string
- // UpgradeDownloadFilename is the local target filename for an upgrade
- // download. This parameter is required when UpgradeDownloadURLs (or
- // UpgradeDownloadUrl) is specified. Data is stored in co-located files
- // (UpgradeDownloadFilename.part*) to allow for resumable downloading.
- UpgradeDownloadFilename string
- // FetchUpgradeRetryPeriodMilliseconds specifies the delay before resuming
- // a client upgrade download after a failure. If omitted, a default value
- // is used. This value is typical overridden for testing.
- FetchUpgradeRetryPeriodMilliseconds *int
- // EmitBytesTransferred indicates whether to emit periodic notices showing
- // bytes sent and received.
- EmitBytesTransferred bool
- // TrustedCACertificatesFilename specifies a file containing trusted CA
- // certs. When set, this toggles use of the trusted CA certs, specified in
- // TrustedCACertificatesFilename, for tunneled TLS connections that expect
- // server certificates signed with public certificate authorities
- // (currently, only upgrade downloads). This option is used with stock Go
- // TLS in cases where Go may fail to obtain a list of root CAs from the
- // operating system.
- TrustedCACertificatesFilename string
- // DisablePeriodicSshKeepAlive indicates whether to send an SSH keepalive
- // every 1-2 minutes, when the tunnel is idle. If the SSH keepalive times
- // out, the tunnel is considered to have failed.
- DisablePeriodicSshKeepAlive bool
- // DeviceRegion is the optional, reported region the host device is
- // running in. This input value should be a ISO 3166-1 alpha-2 country
- // code. The device region is reported to the server in the connected
- // request and recorded for Psiphon stats.
- //
- // When provided, this value may be used, pre-connection, to select
- // performance or circumvention optimization strategies for the given
- // region.
- DeviceRegion string
- // EmitDiagnosticNotices indicates whether to output notices containing
- // detailed information about the Psiphon session. As these notices may
- // contain sensitive information, they should not be insecurely distributed
- // or displayed to users. Default is off.
- EmitDiagnosticNotices bool
- // EmitDiagnosticNetworkParameters indicates whether to include network
- // parameters in diagnostic notices. As these parameters are sensitive
- // circumvention network information, they should not be insecurely
- // distributed or displayed to users. Default is off.
- EmitDiagnosticNetworkParameters bool
- // RateLimits specify throttling configuration for the tunnel.
- RateLimits common.RateLimits
- // EmitSLOKs indicates whether to emit notices for each seeded SLOK. As
- // this could reveal user browsing activity, it's intended for debugging
- // and testing only.
- EmitSLOKs bool
- // PacketTunnelTunDeviceFileDescriptor specifies a tun device file
- // descriptor to use for running a packet tunnel. When this value is > 0,
- // a packet tunnel is established through the server and packets are
- // relayed via the tun device file descriptor. The file descriptor is
- // duped in NewController. When PacketTunnelTunDeviceFileDescriptor is
- // set, TunnelPoolSize must be 1.
- PacketTunnelTunFileDescriptor int
- // SessionID specifies a client session ID to use in the Psiphon API. The
- // session ID should be a randomly generated value that is used only for a
- // single session, which is defined as the period between a user starting
- // a Psiphon client and stopping the client.
- //
- // A session ID must be 32 hex digits (lower case). When blank, a random
- // session ID is automatically generated. Supply a session ID when a
- // single client session will cross multiple Controller instances.
- SessionID string
- // Authorizations is a list of encoded, signed access control
- // authorizations that the client has obtained and will present to the
- // server.
- Authorizations []string
- // ServerEntrySignaturePublicKey is a base64-encoded, ed25519 public
- // key value used to verify individual server entry signatures. This value
- // is supplied by and depends on the Psiphon Network, and is typically
- // embedded in the client binary.
- ServerEntrySignaturePublicKey string
- // ExchangeObfuscationKey is a base64-encoded, NaCl secretbox key used to
- // obfuscate server info exchanges between clients.
- // Required for the exchange functionality.
- ExchangeObfuscationKey string
- // EmitTapdanceLogs indicates whether to emit gotapdance log messages
- // to stdout. Note that gotapdance log messages do not conform to the
- // Notice format standard. Default is off.
- EmitTapdanceLogs bool
- // TransformHostNameProbability is for testing purposes.
- TransformHostNameProbability *float64
- // FragmentorProbability and associated Fragmentor fields are for testing
- // purposes.
- FragmentorProbability *float64
- FragmentorLimitProtocols []string
- FragmentorMinTotalBytes *int
- FragmentorMaxTotalBytes *int
- FragmentorMinWriteBytes *int
- FragmentorMaxWriteBytes *int
- FragmentorMinDelayMicroseconds *int
- FragmentorMaxDelayMicroseconds *int
- // MeekTrafficShapingProbability and associated fields are for testing
- // purposes.
- MeekTrafficShapingProbability *float64
- MeekTrafficShapingLimitProtocols []string
- MeekMinTLSPadding *int
- MeekMaxTLSPadding *int
- MeekMinLimitRequestPayloadLength *int
- MeekMaxLimitRequestPayloadLength *int
- MeekRedialTLSProbability *float64
- // ObfuscatedSSHAlgorithms and associated ObfuscatedSSH fields are for
- // testing purposes. If specified, ObfuscatedSSHAlgorithms must have 4 SSH
- // KEX elements in order: the kex algorithm, cipher, MAC, and server host
- // key algorithm.
- ObfuscatedSSHAlgorithms []string
- ObfuscatedSSHMinPadding *int
- ObfuscatedSSHMaxPadding *int
- // LivenessTestMinUpstreamBytes and other LivenessTest fields are for
- // testing purposes.
- LivenessTestMinUpstreamBytes *int
- LivenessTestMaxUpstreamBytes *int
- LivenessTestMinDownstreamBytes *int
- LivenessTestMaxDownstreamBytes *int
- // ReplayCandidateCount and other Replay fields are for testing purposes.
- ReplayCandidateCount *int
- ReplayDialParametersTTLSeconds *int
- ReplayTargetUpstreamBytes *int
- ReplayTargetDownstreamBytes *int
- ReplayTargetTunnelDurationSeconds *int
- ReplayLaterRoundMoveToFrontProbability *float64
- ReplayRetainFailedProbability *float64
- // NetworkLatencyMultiplierMin and other NetworkLatencyMultiplier fields are
- // for testing purposes.
- NetworkLatencyMultiplierMin float64
- NetworkLatencyMultiplierMax float64
- NetworkLatencyMultiplierLambda float64
- // UseOnlyCustomTLSProfiles and other TLS configuration fields are for
- // testing purposes.
- UseOnlyCustomTLSProfiles *bool
- CustomTLSProfiles protocol.CustomTLSProfiles
- SelectRandomizedTLSProfileProbability *float64
- NoDefaultTLSSessionIDProbability *float64
- // ApplicationParameters is for testing purposes.
- ApplicationParameters parameters.KeyValues
- // clientParameters is the active ClientParameters with defaults, config
- // values, and, optionally, tactics applied.
- //
- // New tactics must be applied by calling Config.SetClientParameters;
- // calling clientParameters.Set directly will fail to add config values.
- clientParameters *parameters.ClientParameters
- dialParametersHash []byte
- dynamicConfigMutex sync.Mutex
- sponsorID string
- authorizations []string
- deviceBinder DeviceBinder
- networkIDGetter NetworkIDGetter
- committed bool
- loadTimestamp string
- }
- // LoadConfig parses a JSON format Psiphon config JSON string and returns a
- // Config struct populated with config values.
- //
- // The Config struct may then be programmatically populated with additional
- // values, including callbacks such as DeviceBinder.
- //
- // Before using the Config, Commit must be called, which will perform further
- // validation and initialize internal data structures.
- func LoadConfig(configJson []byte) (*Config, error) {
- var config Config
- err := json.Unmarshal(configJson, &config)
- if err != nil {
- return nil, errors.Trace(err)
- }
- config.loadTimestamp = common.TruncateTimestampToHour(
- common.GetCurrentTimestamp())
- return &config, nil
- }
- // IsCommitted checks if Commit was called.
- func (config *Config) IsCommitted() bool {
- return config.committed
- }
- // Commit validates Config fields finalizes initialization.
- //
- // Config fields should not be set after calling Config, as any changes may
- // not be reflected in internal data structures.
- func (config *Config) Commit() error {
- // Do SetEmitDiagnosticNotices first, to ensure config file errors are
- // emitted.
- if config.EmitDiagnosticNotices {
- SetEmitDiagnosticNotices(
- true, config.EmitDiagnosticNetworkParameters)
- }
- // Promote legacy fields.
- if config.CustomHeaders == nil {
- config.CustomHeaders = config.UpstreamProxyCustomHeaders
- config.UpstreamProxyCustomHeaders = nil
- }
- if config.RemoteServerListUrl != "" && config.RemoteServerListURLs == nil {
- config.RemoteServerListURLs = promoteLegacyDownloadURL(config.RemoteServerListUrl)
- }
- if config.ObfuscatedServerListRootURL != "" && config.ObfuscatedServerListRootURLs == nil {
- config.ObfuscatedServerListRootURLs = promoteLegacyDownloadURL(config.ObfuscatedServerListRootURL)
- }
- if config.UpgradeDownloadUrl != "" && config.UpgradeDownloadURLs == nil {
- config.UpgradeDownloadURLs = promoteLegacyDownloadURL(config.UpgradeDownloadUrl)
- }
- if config.TunnelProtocol != "" && len(config.LimitTunnelProtocols) == 0 {
- config.LimitTunnelProtocols = []string{config.TunnelProtocol}
- }
- // Supply default values.
- if config.DataStoreDirectory == "" {
- wd, err := os.Getwd()
- if err != nil {
- return errors.Trace(err)
- }
- config.DataStoreDirectory = wd
- }
- if config.ClientVersion == "" {
- config.ClientVersion = "0"
- }
- if config.TunnelPoolSize == 0 {
- config.TunnelPoolSize = TUNNEL_POOL_SIZE
- }
- // Validate config fields.
- if config.PropagationChannelId == "" {
- return errors.TraceNew("propagation channel ID is missing from the configuration file")
- }
- if config.SponsorId == "" {
- return errors.TraceNew("sponsor ID is missing from the configuration file")
- }
- _, err := strconv.Atoi(config.ClientVersion)
- if err != nil {
- return errors.Tracef("invalid client version: %s", err)
- }
- if !common.Contains(
- []string{"", protocol.PSIPHON_SSH_API_PROTOCOL, protocol.PSIPHON_WEB_API_PROTOCOL},
- config.TargetApiProtocol) {
- return errors.TraceNew("invalid TargetApiProtocol")
- }
- if !config.DisableRemoteServerListFetcher {
- if config.RemoteServerListURLs != nil {
- if config.RemoteServerListSignaturePublicKey == "" {
- return errors.TraceNew("missing RemoteServerListSignaturePublicKey")
- }
- if config.RemoteServerListDownloadFilename == "" {
- return errors.TraceNew("missing RemoteServerListDownloadFilename")
- }
- }
- if config.ObfuscatedServerListRootURLs != nil {
- if config.RemoteServerListSignaturePublicKey == "" {
- return errors.TraceNew("missing RemoteServerListSignaturePublicKey")
- }
- if config.ObfuscatedServerListDownloadDirectory == "" {
- return errors.TraceNew("missing ObfuscatedServerListDownloadDirectory")
- }
- }
- }
- if config.SplitTunnelRoutesURLFormat != "" {
- if config.SplitTunnelRoutesSignaturePublicKey == "" {
- return errors.TraceNew("missing SplitTunnelRoutesSignaturePublicKey")
- }
- if config.SplitTunnelDNSServer == "" {
- return errors.TraceNew("missing SplitTunnelDNSServer")
- }
- }
- if config.UpgradeDownloadURLs != nil {
- if config.UpgradeDownloadClientVersionHeader == "" {
- return errors.TraceNew("missing UpgradeDownloadClientVersionHeader")
- }
- if config.UpgradeDownloadFilename == "" {
- return errors.TraceNew("missing UpgradeDownloadFilename")
- }
- }
- // This constraint is expected by logic in Controller.runTunnels().
- if config.PacketTunnelTunFileDescriptor > 0 && config.TunnelPoolSize != 1 {
- return errors.TraceNew("packet tunnel mode requires TunnelPoolSize to be 1")
- }
- // SessionID must be PSIPHON_API_CLIENT_SESSION_ID_LENGTH lowercase hex-encoded bytes.
- if config.SessionID == "" {
- sessionID, err := MakeSessionId()
- if err != nil {
- return errors.Trace(err)
- }
- config.SessionID = sessionID
- }
- if len(config.SessionID) != 2*protocol.PSIPHON_API_CLIENT_SESSION_ID_LENGTH ||
- -1 != strings.IndexFunc(config.SessionID, func(c rune) bool {
- return !unicode.Is(unicode.ASCII_Hex_Digit, c) || unicode.IsUpper(c)
- }) {
- return errors.TraceNew("invalid SessionID")
- }
- config.clientParameters, err = parameters.NewClientParameters(
- func(err error) {
- NoticeAlert("ClientParameters getValue failed: %s", err)
- })
- if err != nil {
- return errors.Trace(err)
- }
- if config.ObfuscatedSSHAlgorithms != nil &&
- len(config.ObfuscatedSSHAlgorithms) != 4 {
- // TODO: validate each algorithm?
- return errors.TraceNew("invalid ObfuscatedSSHAlgorithms")
- }
- // clientParameters.Set will validate the config fields applied to parameters.
- err = config.SetClientParameters("", false, nil)
- if err != nil {
- return errors.Trace(err)
- }
- // Calculate and set the dial parameters hash. After this point, related
- // config fields must not change.
- config.setDialParametersHash()
- // Set defaults for dynamic config fields.
- config.SetDynamicConfig(config.SponsorId, config.Authorizations)
- // Initialize config.deviceBinder and config.config.networkIDGetter. These
- // wrap config.DeviceBinder and config.NetworkIDGetter/NetworkID with
- // loggers.
- //
- // New variables are set to avoid mutating input config fields.
- // Internally, code must use config.deviceBinder and
- // config.networkIDGetter and not the input/exported fields.
- if config.DeviceBinder != nil {
- config.deviceBinder = newLoggingDeviceBinder(config.DeviceBinder)
- }
- networkIDGetter := config.NetworkIDGetter
- if networkIDGetter == nil {
- // Limitation: unlike NetworkIDGetter, which calls back to platform APIs
- // this method of network identification is not dynamic and will not reflect
- // network changes that occur while running.
- if config.NetworkID != "" {
- networkIDGetter = newStaticNetworkGetter(config.NetworkID)
- } else {
- networkIDGetter = newStaticNetworkGetter("UNKNOWN")
- }
- }
- config.networkIDGetter = newLoggingNetworkIDGetter(networkIDGetter)
- config.committed = true
- return nil
- }
- // GetClientParameters returns a the current client parameters.
- func (config *Config) GetClientParameters() *parameters.ClientParameters {
- return config.clientParameters
- }
- // SetClientParameters resets Config.clientParameters to the default values,
- // applies any config file values, and then applies the input parameters (from
- // tactics, etc.)
- //
- // Set skipOnError to false when initially applying only config values, as
- // this will validate the values and should fail. Set skipOnError to true when
- // applying tactics to ignore invalid or unknown parameter values from tactics.
- //
- // In the case of applying tactics, do not call Config.clientParameters.Set
- // directly as this will not first apply config values.
- //
- // If there is an error, the existing Config.clientParameters are left
- // entirely unmodified.
- func (config *Config) SetClientParameters(tag string, skipOnError bool, applyParameters map[string]interface{}) error {
- setParameters := []map[string]interface{}{config.makeConfigParameters()}
- if applyParameters != nil {
- setParameters = append(setParameters, applyParameters)
- }
- counts, err := config.clientParameters.Set(tag, skipOnError, setParameters...)
- if err != nil {
- return errors.Trace(err)
- }
- NoticeInfo("applied %v parameters with tag '%s'", counts, tag)
- // Emit certain individual parameter values for quick reference in diagnostics.
- p := config.clientParameters.Get()
- NoticeInfo(
- "NetworkLatencyMultiplier Min/Max/Lambda: %f/%f/%f",
- p.Float(parameters.NetworkLatencyMultiplierMin),
- p.Float(parameters.NetworkLatencyMultiplierMax),
- p.Float(parameters.NetworkLatencyMultiplierLambda))
- // Application Parameters are feature flags/config info, delivered as Client
- // Parameters via tactics/etc., to be communicated to the outer application.
- // Emit these now, as notices.
- if p.WeightedCoinFlip(parameters.ApplicationParametersProbability) {
- NoticeApplicationParameters(p.KeyValues(parameters.ApplicationParameters))
- }
- return nil
- }
- // SetDynamicConfig sets the current client sponsor ID and authorizations.
- // Invalid values for sponsor ID are ignored. The caller must not modify the
- // input authorizations slice.
- func (config *Config) SetDynamicConfig(sponsorID string, authorizations []string) {
- config.dynamicConfigMutex.Lock()
- defer config.dynamicConfigMutex.Unlock()
- if sponsorID != "" {
- config.sponsorID = sponsorID
- }
- config.authorizations = authorizations
- }
- // GetSponsorID returns the current client sponsor ID.
- func (config *Config) GetSponsorID() string {
- config.dynamicConfigMutex.Lock()
- defer config.dynamicConfigMutex.Unlock()
- return config.sponsorID
- }
- // GetAuthorizations returns the current client authorizations.
- // The caller must not modify the returned slice.
- func (config *Config) GetAuthorizations() []string {
- config.dynamicConfigMutex.Lock()
- defer config.dynamicConfigMutex.Unlock()
- return config.authorizations
- }
- // UseUpstreamProxy indicates if an upstream proxy has been
- // configured.
- func (config *Config) UseUpstreamProxy() bool {
- return config.UpstreamProxyURL != ""
- }
- // GetNetworkID returns the current network ID. When NetworkIDGetter
- // is set, this calls into the host application; otherwise, a default
- // value is returned.
- func (config *Config) GetNetworkID() string {
- return config.networkIDGetter.GetNetworkID()
- }
- func (config *Config) makeConfigParameters() map[string]interface{} {
- // Build set of config values to apply to parameters.
- //
- // Note: names of some config fields such as
- // StaggerConnectionWorkersMilliseconds and LimitMeekBufferSizes have
- // changed in the parameters. The existing config fields are retained for
- // backwards compatibility.
- applyParameters := make(map[string]interface{})
- // To support platform clients that configure NetworkLatencyMultiplier, set
- // the NetworkLatencyMultiplierMin/NetworkLatencyMultiplierMax range to the
- // specified value. Also set the older NetworkLatencyMultiplier tactic, since
- // that will be used in the case of replaying with dial parameters persisted
- // by an older client version.
- if config.NetworkLatencyMultiplier > 0.0 {
- applyParameters[parameters.NetworkLatencyMultiplier] = config.NetworkLatencyMultiplier
- applyParameters[parameters.NetworkLatencyMultiplierMin] = config.NetworkLatencyMultiplier
- applyParameters[parameters.NetworkLatencyMultiplierMax] = config.NetworkLatencyMultiplier
- }
- if config.NetworkLatencyMultiplierMin > 0.0 {
- applyParameters[parameters.NetworkLatencyMultiplierMin] = config.NetworkLatencyMultiplierMin
- }
- if config.NetworkLatencyMultiplierMax > 0.0 {
- applyParameters[parameters.NetworkLatencyMultiplierMax] = config.NetworkLatencyMultiplierMax
- }
- if config.NetworkLatencyMultiplierLambda > 0.0 {
- applyParameters[parameters.NetworkLatencyMultiplierLambda] = config.NetworkLatencyMultiplierLambda
- }
- if len(config.LimitTunnelProtocols) > 0 {
- applyParameters[parameters.LimitTunnelProtocols] = protocol.TunnelProtocols(config.LimitTunnelProtocols)
- }
- if len(config.InitialLimitTunnelProtocols) > 0 && config.InitialLimitTunnelProtocolsCandidateCount > 0 {
- applyParameters[parameters.InitialLimitTunnelProtocols] = protocol.TunnelProtocols(config.InitialLimitTunnelProtocols)
- applyParameters[parameters.InitialLimitTunnelProtocolsCandidateCount] = config.InitialLimitTunnelProtocolsCandidateCount
- }
- if len(config.LimitTLSProfiles) > 0 {
- applyParameters[parameters.LimitTLSProfiles] = protocol.TunnelProtocols(config.LimitTLSProfiles)
- }
- if len(config.LimitQUICVersions) > 0 {
- applyParameters[parameters.LimitQUICVersions] = protocol.QUICVersions(config.LimitQUICVersions)
- }
- if config.EstablishTunnelTimeoutSeconds != nil {
- applyParameters[parameters.EstablishTunnelTimeout] = fmt.Sprintf("%ds", *config.EstablishTunnelTimeoutSeconds)
- }
- if config.EstablishTunnelServerAffinityGracePeriodMilliseconds != nil {
- applyParameters[parameters.EstablishTunnelServerAffinityGracePeriod] = fmt.Sprintf("%dms", *config.EstablishTunnelServerAffinityGracePeriodMilliseconds)
- }
- if config.EstablishTunnelPausePeriodSeconds != nil {
- applyParameters[parameters.EstablishTunnelPausePeriod] = fmt.Sprintf("%ds", *config.EstablishTunnelPausePeriodSeconds)
- }
- if config.ConnectionWorkerPoolSize != 0 {
- applyParameters[parameters.ConnectionWorkerPoolSize] = config.ConnectionWorkerPoolSize
- }
- if config.StaggerConnectionWorkersMilliseconds > 0 {
- applyParameters[parameters.StaggerConnectionWorkersPeriod] = fmt.Sprintf("%dms", config.StaggerConnectionWorkersMilliseconds)
- }
- if config.LimitIntensiveConnectionWorkers > 0 {
- applyParameters[parameters.LimitIntensiveConnectionWorkers] = config.LimitIntensiveConnectionWorkers
- }
- applyParameters[parameters.MeekLimitBufferSizes] = config.LimitMeekBufferSizes
- applyParameters[parameters.IgnoreHandshakeStatsRegexps] = config.IgnoreHandshakeStatsRegexps
- if config.EstablishTunnelTimeoutSeconds != nil {
- applyParameters[parameters.EstablishTunnelTimeout] = fmt.Sprintf("%ds", *config.EstablishTunnelTimeoutSeconds)
- }
- if config.FetchRemoteServerListRetryPeriodMilliseconds != nil {
- applyParameters[parameters.FetchRemoteServerListRetryPeriod] = fmt.Sprintf("%dms", *config.FetchRemoteServerListRetryPeriodMilliseconds)
- }
- if config.FetchUpgradeRetryPeriodMilliseconds != nil {
- applyParameters[parameters.FetchUpgradeRetryPeriod] = fmt.Sprintf("%dms", *config.FetchUpgradeRetryPeriodMilliseconds)
- }
- if !config.DisableRemoteServerListFetcher {
- if config.RemoteServerListURLs != nil {
- applyParameters[parameters.RemoteServerListSignaturePublicKey] = config.RemoteServerListSignaturePublicKey
- applyParameters[parameters.RemoteServerListURLs] = config.RemoteServerListURLs
- }
- if config.ObfuscatedServerListRootURLs != nil {
- applyParameters[parameters.RemoteServerListSignaturePublicKey] = config.RemoteServerListSignaturePublicKey
- applyParameters[parameters.ObfuscatedServerListRootURLs] = config.ObfuscatedServerListRootURLs
- }
- }
- applyParameters[parameters.SplitTunnelRoutesURLFormat] = config.SplitTunnelRoutesURLFormat
- applyParameters[parameters.SplitTunnelRoutesSignaturePublicKey] = config.SplitTunnelRoutesSignaturePublicKey
- applyParameters[parameters.SplitTunnelDNSServer] = config.SplitTunnelDNSServer
- if config.UpgradeDownloadURLs != nil {
- applyParameters[parameters.UpgradeDownloadClientVersionHeader] = config.UpgradeDownloadClientVersionHeader
- applyParameters[parameters.UpgradeDownloadURLs] = config.UpgradeDownloadURLs
- }
- applyParameters[parameters.TunnelRateLimits] = config.RateLimits
- if config.TransformHostNameProbability != nil {
- applyParameters[parameters.TransformHostNameProbability] = *config.TransformHostNameProbability
- }
- if config.FragmentorProbability != nil {
- applyParameters[parameters.FragmentorProbability] = *config.FragmentorProbability
- }
- if len(config.FragmentorLimitProtocols) > 0 {
- applyParameters[parameters.FragmentorLimitProtocols] = protocol.TunnelProtocols(config.FragmentorLimitProtocols)
- }
- if config.FragmentorMinTotalBytes != nil {
- applyParameters[parameters.FragmentorMinTotalBytes] = *config.FragmentorMinTotalBytes
- }
- if config.FragmentorMaxTotalBytes != nil {
- applyParameters[parameters.FragmentorMaxTotalBytes] = *config.FragmentorMaxTotalBytes
- }
- if config.FragmentorMinWriteBytes != nil {
- applyParameters[parameters.FragmentorMinWriteBytes] = *config.FragmentorMinWriteBytes
- }
- if config.FragmentorMaxWriteBytes != nil {
- applyParameters[parameters.FragmentorMaxWriteBytes] = *config.FragmentorMaxWriteBytes
- }
- if config.FragmentorMinDelayMicroseconds != nil {
- applyParameters[parameters.FragmentorMinDelay] = fmt.Sprintf("%dus", *config.FragmentorMinDelayMicroseconds)
- }
- if config.FragmentorMaxDelayMicroseconds != nil {
- applyParameters[parameters.FragmentorMaxDelay] = fmt.Sprintf("%dus", *config.FragmentorMaxDelayMicroseconds)
- }
- if config.MeekTrafficShapingProbability != nil {
- applyParameters[parameters.MeekTrafficShapingProbability] = *config.MeekTrafficShapingProbability
- }
- if len(config.MeekTrafficShapingLimitProtocols) > 0 {
- applyParameters[parameters.MeekTrafficShapingLimitProtocols] = protocol.TunnelProtocols(config.MeekTrafficShapingLimitProtocols)
- }
- if config.MeekMinTLSPadding != nil {
- applyParameters[parameters.MeekMinTLSPadding] = *config.MeekMinTLSPadding
- }
- if config.MeekMaxTLSPadding != nil {
- applyParameters[parameters.MeekMaxTLSPadding] = *config.MeekMaxTLSPadding
- }
- if config.MeekMinLimitRequestPayloadLength != nil {
- applyParameters[parameters.MeekMinLimitRequestPayloadLength] = *config.MeekMinLimitRequestPayloadLength
- }
- if config.MeekMaxLimitRequestPayloadLength != nil {
- applyParameters[parameters.MeekMaxLimitRequestPayloadLength] = *config.MeekMaxLimitRequestPayloadLength
- }
- if config.MeekRedialTLSProbability != nil {
- applyParameters[parameters.MeekRedialTLSProbability] = *config.MeekRedialTLSProbability
- }
- if config.ObfuscatedSSHMinPadding != nil {
- applyParameters[parameters.ObfuscatedSSHMinPadding] = *config.ObfuscatedSSHMinPadding
- }
- if config.ObfuscatedSSHMaxPadding != nil {
- applyParameters[parameters.ObfuscatedSSHMaxPadding] = *config.ObfuscatedSSHMaxPadding
- }
- if config.LivenessTestMinUpstreamBytes != nil {
- applyParameters[parameters.LivenessTestMinUpstreamBytes] = *config.LivenessTestMinUpstreamBytes
- }
- if config.LivenessTestMaxUpstreamBytes != nil {
- applyParameters[parameters.LivenessTestMaxUpstreamBytes] = *config.LivenessTestMaxUpstreamBytes
- }
- if config.LivenessTestMinDownstreamBytes != nil {
- applyParameters[parameters.LivenessTestMinDownstreamBytes] = *config.LivenessTestMinDownstreamBytes
- }
- if config.LivenessTestMaxDownstreamBytes != nil {
- applyParameters[parameters.LivenessTestMaxDownstreamBytes] = *config.LivenessTestMaxDownstreamBytes
- }
- if config.ReplayCandidateCount != nil {
- applyParameters[parameters.ReplayCandidateCount] = *config.ReplayCandidateCount
- }
- if config.ReplayDialParametersTTLSeconds != nil {
- applyParameters[parameters.ReplayDialParametersTTL] = fmt.Sprintf("%ds", *config.ReplayDialParametersTTLSeconds)
- }
- if config.ReplayTargetUpstreamBytes != nil {
- applyParameters[parameters.ReplayTargetUpstreamBytes] = *config.ReplayTargetUpstreamBytes
- }
- if config.ReplayTargetDownstreamBytes != nil {
- applyParameters[parameters.ReplayTargetDownstreamBytes] = *config.ReplayTargetDownstreamBytes
- }
- if config.ReplayTargetTunnelDurationSeconds != nil {
- applyParameters[parameters.ReplayTargetTunnelDuration] = fmt.Sprintf("%ds", *config.ReplayTargetTunnelDurationSeconds)
- }
- if config.ReplayLaterRoundMoveToFrontProbability != nil {
- applyParameters[parameters.ReplayLaterRoundMoveToFrontProbability] = *config.ReplayLaterRoundMoveToFrontProbability
- }
- if config.ReplayRetainFailedProbability != nil {
- applyParameters[parameters.ReplayRetainFailedProbability] = *config.ReplayRetainFailedProbability
- }
- if config.UseOnlyCustomTLSProfiles != nil {
- applyParameters[parameters.UseOnlyCustomTLSProfiles] = *config.UseOnlyCustomTLSProfiles
- }
- if config.CustomTLSProfiles != nil {
- applyParameters[parameters.CustomTLSProfiles] = config.CustomTLSProfiles
- }
- if config.SelectRandomizedTLSProfileProbability != nil {
- applyParameters[parameters.SelectRandomizedTLSProfileProbability] = *config.SelectRandomizedTLSProfileProbability
- }
- if config.NoDefaultTLSSessionIDProbability != nil {
- applyParameters[parameters.NoDefaultTLSSessionIDProbability] = *config.NoDefaultTLSSessionIDProbability
- }
- if config.ApplicationParameters != nil {
- applyParameters[parameters.ApplicationParameters] = config.ApplicationParameters
- }
- return applyParameters
- }
- func (config *Config) setDialParametersHash() {
- // Calculate and store a hash of the config values that may impact
- // dial parameters. This hash is used as part of the dial parameters
- // replay mechanism to detect when persisted dial parameters should
- // be discarded due to conflicting config changes.
- //
- // MD5 hash is used solely as a data checksum and not for any security
- // purpose; serialization is not strictly unambiguous.
- hash := md5.New()
- if len(config.LimitTunnelProtocols) > 0 {
- for _, protocol := range config.LimitTunnelProtocols {
- hash.Write([]byte(protocol))
- }
- }
- if len(config.InitialLimitTunnelProtocols) > 0 && config.InitialLimitTunnelProtocolsCandidateCount > 0 {
- for _, protocol := range config.InitialLimitTunnelProtocols {
- hash.Write([]byte(protocol))
- }
- binary.Write(hash, binary.LittleEndian, int64(config.InitialLimitTunnelProtocolsCandidateCount))
- }
- if len(config.LimitTLSProfiles) > 0 {
- for _, profile := range config.LimitTLSProfiles {
- hash.Write([]byte(profile))
- }
- }
- if len(config.LimitQUICVersions) > 0 {
- for _, version := range config.LimitQUICVersions {
- hash.Write([]byte(version))
- }
- }
- // Whether a custom User-Agent is specified is a binary flag: when not set,
- // the replay dial parameters value applies. When set, external
- // considerations apply.
- if _, ok := config.CustomHeaders["User-Agent"]; ok {
- hash.Write([]byte{1})
- }
- if config.UpstreamProxyURL != "" {
- hash.Write([]byte(config.UpstreamProxyURL))
- }
- if config.TransformHostNameProbability != nil {
- binary.Write(hash, binary.LittleEndian, *config.TransformHostNameProbability)
- }
- if config.FragmentorProbability != nil {
- binary.Write(hash, binary.LittleEndian, *config.FragmentorProbability)
- }
- if len(config.FragmentorLimitProtocols) > 0 {
- for _, protocol := range config.FragmentorLimitProtocols {
- hash.Write([]byte(protocol))
- }
- }
- if config.FragmentorMinTotalBytes != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMinTotalBytes))
- }
- if config.FragmentorMaxTotalBytes != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMaxTotalBytes))
- }
- if config.FragmentorMinWriteBytes != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMinWriteBytes))
- }
- if config.FragmentorMaxWriteBytes != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMaxWriteBytes))
- }
- if config.FragmentorMinDelayMicroseconds != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMinDelayMicroseconds))
- }
- if config.FragmentorMaxDelayMicroseconds != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.FragmentorMaxDelayMicroseconds))
- }
- if config.MeekTrafficShapingProbability != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.MeekTrafficShapingProbability))
- }
- if len(config.MeekTrafficShapingLimitProtocols) > 0 {
- for _, protocol := range config.MeekTrafficShapingLimitProtocols {
- hash.Write([]byte(protocol))
- }
- }
- if config.MeekMinLimitRequestPayloadLength != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.MeekMinLimitRequestPayloadLength))
- }
- if config.MeekMaxLimitRequestPayloadLength != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.MeekMaxLimitRequestPayloadLength))
- }
- if config.MeekRedialTLSProbability != nil {
- binary.Write(hash, binary.LittleEndian, *config.MeekRedialTLSProbability)
- }
- if config.ObfuscatedSSHMinPadding != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.ObfuscatedSSHMinPadding))
- }
- if config.ObfuscatedSSHMaxPadding != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.ObfuscatedSSHMaxPadding))
- }
- if config.LivenessTestMinUpstreamBytes != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.LivenessTestMinUpstreamBytes))
- }
- if config.LivenessTestMaxUpstreamBytes != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.LivenessTestMaxUpstreamBytes))
- }
- if config.LivenessTestMinDownstreamBytes != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.LivenessTestMinDownstreamBytes))
- }
- if config.LivenessTestMaxDownstreamBytes != nil {
- binary.Write(hash, binary.LittleEndian, int64(*config.LivenessTestMaxDownstreamBytes))
- }
- binary.Write(hash, binary.LittleEndian, config.NetworkLatencyMultiplierMin)
- binary.Write(hash, binary.LittleEndian, config.NetworkLatencyMultiplierMax)
- binary.Write(hash, binary.LittleEndian, config.NetworkLatencyMultiplierLambda)
- if config.UseOnlyCustomTLSProfiles != nil {
- binary.Write(hash, binary.LittleEndian, *config.UseOnlyCustomTLSProfiles)
- }
- for _, customTLSProfile := range config.CustomTLSProfiles {
- // Assumes consistent definition for a given profile name
- hash.Write([]byte(customTLSProfile.Name))
- }
- if config.SelectRandomizedTLSProfileProbability != nil {
- binary.Write(hash, binary.LittleEndian, *config.SelectRandomizedTLSProfileProbability)
- }
- if config.NoDefaultTLSSessionIDProbability != nil {
- binary.Write(hash, binary.LittleEndian, *config.NoDefaultTLSSessionIDProbability)
- }
- config.dialParametersHash = hash.Sum(nil)
- }
- func promoteLegacyDownloadURL(URL string) parameters.DownloadURLs {
- downloadURLs := make(parameters.DownloadURLs, 1)
- downloadURLs[0] = ¶meters.DownloadURL{
- URL: base64.StdEncoding.EncodeToString([]byte(URL)),
- SkipVerify: false,
- OnlyAfterAttempts: 0,
- }
- return downloadURLs
- }
- type loggingDeviceBinder struct {
- d DeviceBinder
- }
- func newLoggingDeviceBinder(d DeviceBinder) *loggingDeviceBinder {
- return &loggingDeviceBinder{d: d}
- }
- func (d *loggingDeviceBinder) BindToDevice(fileDescriptor int) (string, error) {
- deviceInfo, err := d.d.BindToDevice(fileDescriptor)
- if err == nil && deviceInfo != "" {
- NoticeBindToDevice(deviceInfo)
- }
- return deviceInfo, err
- }
- type staticNetworkGetter struct {
- networkID string
- }
- func newStaticNetworkGetter(networkID string) *staticNetworkGetter {
- return &staticNetworkGetter{networkID: networkID}
- }
- func (n *staticNetworkGetter) GetNetworkID() string {
- return n.networkID
- }
- type loggingNetworkIDGetter struct {
- n NetworkIDGetter
- }
- func newLoggingNetworkIDGetter(n NetworkIDGetter) *loggingNetworkIDGetter {
- return &loggingNetworkIDGetter{n: n}
- }
- func (n *loggingNetworkIDGetter) GetNetworkID() string {
- networkID := n.n.GetNetworkID()
- // All PII must appear after the initial "-"
- // See: https://godoc.org/github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon#NetworkIDGetter
- logNetworkID := networkID
- index := strings.Index(logNetworkID, "-")
- if index != -1 {
- logNetworkID = logNetworkID[:index]
- }
- if len(logNetworkID)+1 < len(networkID) {
- // Indicate when additional network info was present after the first "-".
- logNetworkID += "+[redacted]"
- }
- NoticeNetworkID(logNetworkID)
- return networkID
- }
|