github.com/astaguna/popon-core@v0.0.0-20231019235610-96e42d76a5ff/psiphon/dialParameters.go (about)

     1  /*
     2   * Copyright (c) 2018, 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  	"bytes"
    24  	"context"
    25  	"crypto/md5"
    26  	"encoding/binary"
    27  	"net"
    28  	"net/http"
    29  	"strconv"
    30  	"strings"
    31  	"sync/atomic"
    32  	"time"
    33  
    34  	"github.com/astaguna/popon-core/psiphon/common"
    35  	"github.com/astaguna/popon-core/psiphon/common/errors"
    36  	"github.com/astaguna/popon-core/psiphon/common/fragmentor"
    37  	"github.com/astaguna/popon-core/psiphon/common/obfuscator"
    38  	"github.com/astaguna/popon-core/psiphon/common/parameters"
    39  	"github.com/astaguna/popon-core/psiphon/common/prng"
    40  	"github.com/astaguna/popon-core/psiphon/common/protocol"
    41  	"github.com/astaguna/popon-core/psiphon/common/regen"
    42  	"github.com/astaguna/popon-core/psiphon/common/resolver"
    43  	"github.com/astaguna/popon-core/psiphon/common/transforms"
    44  	"github.com/astaguna/popon-core/psiphon/common/values"
    45  	"golang.org/x/net/bpf"
    46  )
    47  
    48  // DialParameters represents a selected protocol and all the related selected
    49  // protocol attributes, many chosen at random, for a tunnel dial attempt.
    50  //
    51  // DialParameters is used:
    52  // - to configure dialers
    53  // - as a persistent record to store successful dial parameters for replay
    54  // - to report dial stats in notices and Psiphon API calls.
    55  //
    56  // MeekResolvedIPAddress is set asynchronously, as it is not known until the
    57  // dial process has begun. The atomic.Value will contain a string, initialized
    58  // to "", and set to the resolved IP address once that part of the dial
    59  // process has completed.
    60  //
    61  // DialParameters is not safe for concurrent use.
    62  type DialParameters struct {
    63  	ServerEntry             *protocol.ServerEntry `json:"-"`
    64  	NetworkID               string                `json:"-"`
    65  	IsReplay                bool                  `json:"-"`
    66  	CandidateNumber         int                   `json:"-"`
    67  	EstablishedTunnelsCount int                   `json:"-"`
    68  
    69  	IsExchanged bool
    70  
    71  	LastUsedTimestamp       time.Time
    72  	LastUsedConfigStateHash []byte
    73  	LastUsedServerEntryHash []byte
    74  
    75  	NetworkLatencyMultiplier float64
    76  
    77  	TunnelProtocol string
    78  
    79  	DirectDialAddress              string
    80  	DialPortNumber                 string
    81  	UpstreamProxyType              string   `json:"-"`
    82  	UpstreamProxyCustomHeaderNames []string `json:"-"`
    83  
    84  	BPFProgramName         string
    85  	BPFProgramInstructions []bpf.RawInstruction
    86  
    87  	SelectedSSHClientVersion bool
    88  	SSHClientVersion         string
    89  	SSHKEXSeed               *prng.Seed
    90  
    91  	ObfuscatorPaddingSeed                   *prng.Seed
    92  	OSSHObfuscatorSeedTransformerParameters *transforms.ObfuscatorSeedTransformerParameters
    93  
    94  	OSSHPrefixSpec        *obfuscator.OSSHPrefixSpec
    95  	OSSHPrefixSplitConfig *obfuscator.OSSHPrefixSplitConfig
    96  
    97  	FragmentorSeed *prng.Seed
    98  
    99  	FrontingProviderID string
   100  
   101  	MeekFrontingDialAddress   string
   102  	MeekFrontingHost          string
   103  	MeekDialAddress           string
   104  	MeekTransformedHostName   bool
   105  	MeekSNIServerName         string
   106  	MeekVerifyServerName      string
   107  	MeekVerifyPins            []string
   108  	MeekHostHeader            string
   109  	MeekObfuscatorPaddingSeed *prng.Seed
   110  	MeekTLSPaddingSize        int
   111  	MeekResolvedIPAddress     atomic.Value `json:"-"`
   112  
   113  	TLSOSSHTransformedSNIServerName bool
   114  	TLSOSSHSNIServerName            string
   115  	TLSOSSHObfuscatorPaddingSeed    *prng.Seed
   116  
   117  	SelectedUserAgent bool
   118  	UserAgent         string
   119  
   120  	SelectedTLSProfile       bool
   121  	TLSProfile               string
   122  	NoDefaultTLSSessionID    bool
   123  	TLSVersion               string
   124  	RandomizedTLSProfileSeed *prng.Seed
   125  
   126  	QUICVersion                              string
   127  	QUICDialSNIAddress                       string
   128  	QUICClientHelloSeed                      *prng.Seed
   129  	ObfuscatedQUICPaddingSeed                *prng.Seed
   130  	ObfuscatedQUICNonceTransformerParameters *transforms.ObfuscatorSeedTransformerParameters
   131  	QUICDisablePathMTUDiscovery              bool
   132  
   133  	ConjureCachedRegistrationTTL        time.Duration
   134  	ConjureAPIRegistration              bool
   135  	ConjureAPIRegistrarBidirectionalURL string
   136  	ConjureAPIRegistrarDelay            time.Duration
   137  	ConjureDecoyRegistration            bool
   138  	ConjureDecoyRegistrarDelay          time.Duration
   139  	ConjureDecoyRegistrarWidth          int
   140  	ConjureTransport                    string
   141  
   142  	LivenessTestSeed *prng.Seed
   143  
   144  	APIRequestPaddingSeed *prng.Seed
   145  
   146  	HoldOffTunnelDuration time.Duration
   147  
   148  	DialConnMetrics          common.MetricsSource       `json:"-"`
   149  	DialConnNoticeMetrics    common.NoticeMetricsSource `json:"-"`
   150  	ObfuscatedSSHConnMetrics common.MetricsSource       `json:"-"`
   151  
   152  	DialDuration time.Duration `json:"-"`
   153  
   154  	resolver          *resolver.Resolver `json:"-"`
   155  	ResolveParameters *resolver.ResolveParameters
   156  
   157  	HTTPTransformerParameters *transforms.HTTPTransformerParameters
   158  
   159  	dialConfig *DialConfig `json:"-"`
   160  	meekConfig *MeekConfig `json:"-"`
   161  }
   162  
   163  // MakeDialParameters creates a new DialParameters for the candidate server
   164  // entry, including selecting a protocol and all the various protocol
   165  // attributes. The input selectProtocol is used to comply with any active
   166  // protocol selection constraints.
   167  //
   168  // When stored dial parameters are available and may be used,
   169  // MakeDialParameters may replay previous dial parameters in an effort to
   170  // leverage "known working" values instead of always chosing at random from a
   171  // large space.
   172  //
   173  // MakeDialParameters will return nil/nil in cases where the candidate server
   174  // entry should be skipped.
   175  //
   176  // To support replay, the caller must call DialParameters.Succeeded when a
   177  // successful tunnel is established with the returned DialParameters; and must
   178  // call DialParameters.Failed when a tunnel dial or activation fails, except
   179  // when establishment is cancelled.
   180  func MakeDialParameters(
   181  	config *Config,
   182  	upstreamProxyErrorCallback func(error),
   183  	canReplay func(serverEntry *protocol.ServerEntry, replayProtocol string) bool,
   184  	selectProtocol func(serverEntry *protocol.ServerEntry) (string, bool),
   185  	serverEntry *protocol.ServerEntry,
   186  	isTactics bool,
   187  	candidateNumber int,
   188  	establishedTunnelsCount int) (*DialParameters, error) {
   189  
   190  	networkID := config.GetNetworkID()
   191  
   192  	p := config.GetParameters().Get()
   193  
   194  	ttl := p.Duration(parameters.ReplayDialParametersTTL)
   195  	replayIgnoreChangedConfigState := p.Bool(parameters.ReplayIgnoreChangedConfigState)
   196  	replayBPF := p.Bool(parameters.ReplayBPF)
   197  	replaySSH := p.Bool(parameters.ReplaySSH)
   198  	replayObfuscatorPadding := p.Bool(parameters.ReplayObfuscatorPadding)
   199  	replayFragmentor := p.Bool(parameters.ReplayFragmentor)
   200  	replayTLSProfile := p.Bool(parameters.ReplayTLSProfile)
   201  	replayFronting := p.Bool(parameters.ReplayFronting)
   202  	replayHostname := p.Bool(parameters.ReplayHostname)
   203  	replayQUICVersion := p.Bool(parameters.ReplayQUICVersion)
   204  	replayObfuscatedQUIC := p.Bool(parameters.ReplayObfuscatedQUIC)
   205  	replayObfuscatedQUICNonceTransformer := p.Bool(parameters.ReplayObfuscatedQUICNonceTransformer)
   206  	replayConjureRegistration := p.Bool(parameters.ReplayConjureRegistration)
   207  	replayConjureTransport := p.Bool(parameters.ReplayConjureTransport)
   208  	replayLivenessTest := p.Bool(parameters.ReplayLivenessTest)
   209  	replayUserAgent := p.Bool(parameters.ReplayUserAgent)
   210  	replayAPIRequestPadding := p.Bool(parameters.ReplayAPIRequestPadding)
   211  	replayHoldOffTunnel := p.Bool(parameters.ReplayHoldOffTunnel)
   212  	replayResolveParameters := p.Bool(parameters.ReplayResolveParameters)
   213  	replayHTTPTransformerParameters := p.Bool(parameters.ReplayHTTPTransformerParameters)
   214  	replayOSSHSeedTransformerParameters := p.Bool(parameters.ReplayOSSHSeedTransformerParameters)
   215  	replayOSSHPrefix := p.Bool(parameters.ReplayOSSHPrefix)
   216  
   217  	// Check for existing dial parameters for this server/network ID.
   218  
   219  	dialParams, err := GetDialParameters(
   220  		config, serverEntry.IpAddress, networkID)
   221  	if err != nil {
   222  		NoticeWarning("GetDialParameters failed: %s", err)
   223  		dialParams = nil
   224  		// Proceed, without existing dial parameters.
   225  	}
   226  
   227  	// Check if replay is permitted:
   228  	// - TTL must be > 0 and existing dial parameters must not have expired
   229  	//   as indicated by LastUsedTimestamp + TTL.
   230  	// - Config/tactics/server entry values must be unchanged from when
   231  	//   previous dial parameters were established.
   232  	// - The protocol selection constraints must permit replay, as indicated
   233  	//   by canReplay.
   234  	// - Must not be using an obsolete TLS profile that is no longer supported.
   235  	// - Must be using the latest Conjure API URL.
   236  	//
   237  	// When existing dial parameters don't meet these conditions, dialParams
   238  	// is reset to nil and new dial parameters will be generated.
   239  
   240  	var currentTimestamp time.Time
   241  	var configStateHash []byte
   242  	var serverEntryHash []byte
   243  
   244  	// When TTL is 0, replay is disabled; the timestamp remains 0 and the
   245  	// output DialParameters will not be stored by Success.
   246  
   247  	if ttl > 0 {
   248  		currentTimestamp = time.Now()
   249  		configStateHash, serverEntryHash = getDialStateHashes(config, p, serverEntry)
   250  	}
   251  
   252  	if dialParams != nil &&
   253  		(ttl <= 0 ||
   254  			dialParams.LastUsedTimestamp.Before(currentTimestamp.Add(-ttl)) ||
   255  
   256  			// Replay is disabled when the current config state hash -- config
   257  			// dial parameters and the current tactics tag -- have changed
   258  			// since the last dial. This prioritizes applying any potential
   259  			// tactics change over redialing with parameters that may have
   260  			// changed in tactics.
   261  			//
   262  			// Because of this, frequent tactics changes may degrade replay
   263  			// effectiveness. When ReplayIgnoreChangedConfigState is set,
   264  			// differences in the config state hash are ignored.
   265  			//
   266  			// Limitation: some code which previously assumed that replay
   267  			// always implied unchanged tactics parameters may now use newer
   268  			// tactics parameters in replay cases when
   269  			// ReplayIgnoreChangedConfigState is set. One case is the call
   270  			// below to fragmentor.NewUpstreamConfig, made when initializing
   271  			// dialParams.dialConfig.
   272  			(!replayIgnoreChangedConfigState && !bytes.Equal(dialParams.LastUsedConfigStateHash, configStateHash)) ||
   273  
   274  			// Replay is disabled when the server entry has changed.
   275  			!bytes.Equal(dialParams.LastUsedServerEntryHash, serverEntryHash) ||
   276  
   277  			(dialParams.TLSProfile != "" &&
   278  				!common.Contains(protocol.SupportedTLSProfiles, dialParams.TLSProfile)) ||
   279  			(dialParams.QUICVersion != "" &&
   280  				!common.Contains(protocol.SupportedQUICVersions, dialParams.QUICVersion)) ||
   281  
   282  			// Legacy clients use ConjureAPIRegistrarURL with
   283  			// gotapdance.tapdance.APIRegistrar and new clients use
   284  			// ConjureAPIRegistrarBidirectionalURL with
   285  			// gotapdance.tapdance.APIRegistrarBidirectional. Updated clients
   286  			// may have replay dial parameters with the old
   287  			// ConjureAPIRegistrarURL field, which is now ignored. In this
   288  			// case, ConjureAPIRegistrarBidirectionalURL will be blank. Reset
   289  			// this replay.
   290  			(dialParams.ConjureAPIRegistration && dialParams.ConjureAPIRegistrarBidirectionalURL == "")) {
   291  
   292  		// In these cases, existing dial parameters are expired or no longer
   293  		// match the config state and so are cleared to avoid rechecking them.
   294  
   295  		err = DeleteDialParameters(serverEntry.IpAddress, networkID)
   296  		if err != nil {
   297  			NoticeWarning("DeleteDialParameters failed: %s", err)
   298  		}
   299  		dialParams = nil
   300  	}
   301  
   302  	if dialParams != nil {
   303  		if config.DisableReplay ||
   304  			!canReplay(serverEntry, dialParams.TunnelProtocol) {
   305  
   306  			// In these ephemeral cases, existing dial parameters may still be valid
   307  			// and used in future establishment phases, and so are retained.
   308  
   309  			dialParams = nil
   310  		}
   311  	}
   312  
   313  	// IsExchanged:
   314  	//
   315  	// Dial parameters received via client-to-client exchange are partially
   316  	// initialized. Only the exchange fields are retained, and all other dial
   317  	// parameters fields must be initialized. This is not considered or logged as
   318  	// a replay. The exchange case is identified by the IsExchanged flag.
   319  	//
   320  	// When previously stored, IsExchanged dial parameters will have set the same
   321  	// timestamp and state hash used for regular dial parameters and the same
   322  	// logic above should invalidate expired or invalid exchanged dial
   323  	// parameters.
   324  	//
   325  	// Limitation: metrics will indicate when an exchanged server entry is used
   326  	// (source "EXCHANGED") but will not indicate when exchanged dial parameters
   327  	// are used vs. a redial after discarding dial parameters.
   328  
   329  	isReplay := (dialParams != nil)
   330  	isExchanged := isReplay && dialParams.IsExchanged
   331  
   332  	if !isReplay {
   333  		dialParams = &DialParameters{}
   334  	}
   335  
   336  	// Point to the current resolver to be used in dials.
   337  	dialParams.resolver = config.GetResolver()
   338  	if dialParams.resolver == nil {
   339  		return nil, errors.TraceNew("missing resolver")
   340  	}
   341  
   342  	if isExchanged {
   343  		// Set isReplay to false to cause all non-exchanged values to be
   344  		// initialized; this also causes the exchange case to not log as replay.
   345  		isReplay = false
   346  	}
   347  
   348  	// Set IsExchanged such that full dial parameters are stored and replayed
   349  	// upon success.
   350  	dialParams.IsExchanged = false
   351  
   352  	dialParams.ServerEntry = serverEntry
   353  	dialParams.NetworkID = networkID
   354  	dialParams.IsReplay = isReplay
   355  	dialParams.CandidateNumber = candidateNumber
   356  	dialParams.EstablishedTunnelsCount = establishedTunnelsCount
   357  
   358  	// Even when replaying, LastUsedTimestamp is updated to extend the TTL of
   359  	// replayed dial parameters which will be updated in the datastore upon
   360  	// success.
   361  
   362  	dialParams.LastUsedTimestamp = currentTimestamp
   363  	dialParams.LastUsedConfigStateHash = configStateHash
   364  	dialParams.LastUsedServerEntryHash = serverEntryHash
   365  
   366  	// Initialize dial parameters.
   367  	//
   368  	// When not replaying, all required parameters are initialized. When
   369  	// replaying, existing parameters are retained, subject to the replay-X
   370  	// tactics flags.
   371  
   372  	// Select a network latency multiplier for this dial. This allows clients to
   373  	// explore and discover timeout values appropriate for the current network.
   374  	// The selection applies per tunnel, to avoid delaying all establishment
   375  	// candidates due to excessive timeouts. The random selection is bounded by a
   376  	// min/max set in tactics and an exponential distribution is used so as to
   377  	// heavily favor values close to the min, which should be set to the
   378  	// singleton NetworkLatencyMultiplier tactics value.
   379  	//
   380  	// For NetworkLatencyMultiplierLambda close to 2.0, values near min are
   381  	// very approximately 10x more likely to be selected than values near
   382  	// max, while for NetworkLatencyMultiplierLambda close to 0.1, the
   383  	// distribution is close to uniform.
   384  	//
   385  	// Not all existing, persisted DialParameters will have a custom
   386  	// NetworkLatencyMultiplier value. Its zero value will cause the singleton
   387  	// NetworkLatencyMultiplier tactics value to be used instead, which is
   388  	// consistent with the pre-custom multiplier behavior in the older client
   389  	// version which persisted that DialParameters.
   390  
   391  	networkLatencyMultiplierMin := p.Float(parameters.NetworkLatencyMultiplierMin)
   392  	networkLatencyMultiplierMax := p.Float(parameters.NetworkLatencyMultiplierMax)
   393  
   394  	if !isReplay ||
   395  		// Was selected...
   396  		(dialParams.NetworkLatencyMultiplier != 0.0 &&
   397  			//  But is now outside tactics range...
   398  			(dialParams.NetworkLatencyMultiplier < networkLatencyMultiplierMin ||
   399  				dialParams.NetworkLatencyMultiplier > networkLatencyMultiplierMax)) {
   400  
   401  		dialParams.NetworkLatencyMultiplier = prng.ExpFloat64Range(
   402  			networkLatencyMultiplierMin,
   403  			networkLatencyMultiplierMax,
   404  			p.Float(parameters.NetworkLatencyMultiplierLambda))
   405  	}
   406  
   407  	// After this point, any tactics parameters that apply the network latency
   408  	// multiplier will use this selected value.
   409  	p = config.GetParameters().GetCustom(dialParams.NetworkLatencyMultiplier)
   410  
   411  	if !isReplay && !isExchanged {
   412  
   413  		// TODO: should there be a pre-check of selectProtocol before incurring
   414  		// overhead of unmarshaling dial parameters? In may be that a server entry
   415  		// is fully incapable of satisfying the current protocol selection
   416  		// constraints.
   417  
   418  		selectedProtocol, ok := selectProtocol(serverEntry)
   419  		if !ok {
   420  			return nil, nil
   421  		}
   422  
   423  		dialParams.TunnelProtocol = selectedProtocol
   424  	}
   425  
   426  	// Skip this candidate when the clients tactics restrict usage of the
   427  	// fronting provider ID. See the corresponding server-side enforcement
   428  	// comments in server.TacticsListener.accept.
   429  	if protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) &&
   430  		common.Contains(
   431  			p.Strings(parameters.RestrictFrontingProviderIDs),
   432  			dialParams.ServerEntry.FrontingProviderID) {
   433  		if p.WeightedCoinFlip(
   434  			parameters.RestrictFrontingProviderIDsClientProbability) {
   435  
   436  			// When skipping, return nil/nil as no error should be logged.
   437  			// NoticeSkipServerEntry emits each skip reason, regardless
   438  			// of server entry, at most once per session.
   439  
   440  			NoticeSkipServerEntry(
   441  				"restricted fronting provider ID: %s",
   442  				dialParams.ServerEntry.FrontingProviderID)
   443  
   444  			return nil, nil
   445  		}
   446  	}
   447  
   448  	if config.UseUpstreamProxy() {
   449  
   450  		// When UpstreamProxy is configured, ServerEntry.GetSupportedProtocols, when
   451  		// called via selectProtocol, will filter out protocols such that will not
   452  		// select a protocol incompatible with UpstreamProxy. This additional check
   453  		// will catch cases where selectProtocol does not apply this filter.
   454  		if !protocol.TunnelProtocolSupportsUpstreamProxy(dialParams.TunnelProtocol) {
   455  
   456  			NoticeSkipServerEntry(
   457  				"protocol does not support upstream proxy: %s",
   458  				dialParams.TunnelProtocol)
   459  
   460  			return nil, nil
   461  		}
   462  
   463  		// Skip this candidate when the server entry is not to be used with an
   464  		// upstream proxy. By not exposing servers from sources that are
   465  		// relatively hard to enumerate, this mechanism mitigates the risk of
   466  		// a malicious upstream proxy enumerating Psiphon servers. Populate
   467  		// the allowed sources with fronted servers to provide greater
   468  		// blocking resistence for clients using upstream proxy clients that
   469  		// are subject to blocking.
   470  		source := dialParams.ServerEntry.LocalSource
   471  		if !protocol.AllowServerEntrySourceWithUpstreamProxy(source) &&
   472  			!p.Bool(parameters.UpstreamProxyAllowAllServerEntrySources) {
   473  
   474  			NoticeSkipServerEntry(
   475  				"server entry source disallowed with upstream proxy: %s",
   476  				source)
   477  
   478  			return nil, nil
   479  		}
   480  	}
   481  
   482  	if (!isReplay || !replayBPF) &&
   483  		ClientBPFEnabled() &&
   484  		protocol.TunnelProtocolUsesTCP(dialParams.TunnelProtocol) {
   485  
   486  		if p.WeightedCoinFlip(parameters.BPFClientTCPProbability) {
   487  			dialParams.BPFProgramName = ""
   488  			dialParams.BPFProgramInstructions = nil
   489  			ok, name, rawInstructions := p.BPFProgram(parameters.BPFClientTCPProgram)
   490  			if ok {
   491  				dialParams.BPFProgramName = name
   492  				dialParams.BPFProgramInstructions = rawInstructions
   493  			}
   494  		}
   495  	}
   496  
   497  	if !isReplay || !replaySSH {
   498  		dialParams.SelectedSSHClientVersion = true
   499  		dialParams.SSHClientVersion = values.GetSSHClientVersion()
   500  		dialParams.SSHKEXSeed, err = prng.NewSeed()
   501  		if err != nil {
   502  			return nil, errors.Trace(err)
   503  		}
   504  	}
   505  
   506  	if !isReplay || !replayObfuscatorPadding {
   507  		dialParams.ObfuscatorPaddingSeed, err = prng.NewSeed()
   508  		if err != nil {
   509  			return nil, errors.Trace(err)
   510  		}
   511  
   512  		if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) {
   513  			dialParams.MeekObfuscatorPaddingSeed, err = prng.NewSeed()
   514  			if err != nil {
   515  				return nil, errors.Trace(err)
   516  			}
   517  		} else if protocol.TunnelProtocolUsesTLSOSSH(dialParams.TunnelProtocol) {
   518  			dialParams.TLSOSSHObfuscatorPaddingSeed, err = prng.NewSeed()
   519  			if err != nil {
   520  				return nil, errors.Trace(err)
   521  			}
   522  		}
   523  	}
   524  
   525  	if !isReplay || !replayFragmentor {
   526  		dialParams.FragmentorSeed, err = prng.NewSeed()
   527  		if err != nil {
   528  			return nil, errors.Trace(err)
   529  		}
   530  	}
   531  
   532  	if (!isReplay || !replayConjureRegistration) &&
   533  		protocol.TunnelProtocolUsesConjure(dialParams.TunnelProtocol) {
   534  
   535  		dialParams.ConjureCachedRegistrationTTL = p.Duration(parameters.ConjureCachedRegistrationTTL)
   536  
   537  		apiURL := p.String(parameters.ConjureAPIRegistrarBidirectionalURL)
   538  		decoyWidth := p.Int(parameters.ConjureDecoyRegistrarWidth)
   539  
   540  		dialParams.ConjureAPIRegistration = apiURL != ""
   541  		dialParams.ConjureDecoyRegistration = decoyWidth != 0
   542  
   543  		// We select only one of API or decoy registration. When both are enabled,
   544  		// ConjureDecoyRegistrarProbability determines the probability of using
   545  		// decoy registration.
   546  		//
   547  		// In general, we disable retries in gotapdance and rely on Psiphon
   548  		// establishment to try/retry different registration schemes. This allows us
   549  		// to control the proportion of registration types attempted. And, in good
   550  		// network conditions, individual candidates are most likely to be cancelled
   551  		// before they exhaust their retry options.
   552  
   553  		if dialParams.ConjureAPIRegistration && dialParams.ConjureDecoyRegistration {
   554  			if p.WeightedCoinFlip(parameters.ConjureDecoyRegistrarProbability) {
   555  				dialParams.ConjureAPIRegistration = false
   556  			}
   557  		}
   558  
   559  		if dialParams.ConjureAPIRegistration {
   560  
   561  			// While Conjure API registration uses MeekConn and specifies common meek
   562  			// parameters, the meek address and SNI configuration is implemented in this
   563  			// code block and not in common code blocks below. The exception is TLS
   564  			// configuration.
   565  			//
   566  			// Accordingly, replayFronting/replayHostname have no effect on Conjure API
   567  			// registration replay.
   568  
   569  			dialParams.ConjureAPIRegistrarBidirectionalURL = apiURL
   570  
   571  			frontingSpecs := p.FrontingSpecs(parameters.ConjureAPIRegistrarFrontingSpecs)
   572  			dialParams.FrontingProviderID,
   573  				dialParams.MeekFrontingDialAddress,
   574  				dialParams.MeekSNIServerName,
   575  				dialParams.MeekVerifyServerName,
   576  				dialParams.MeekVerifyPins,
   577  				dialParams.MeekFrontingHost,
   578  				err = frontingSpecs.SelectParameters()
   579  			if err != nil {
   580  				return nil, errors.Trace(err)
   581  			}
   582  
   583  			if config.DisableSystemRootCAs &&
   584  				(len(dialParams.MeekVerifyPins) == 0 || dialParams.MeekVerifyServerName == "") {
   585  				return nil, errors.TraceNew("TLS certificates must be verified in Conjure API registration")
   586  			}
   587  
   588  			dialParams.MeekDialAddress = net.JoinHostPort(dialParams.MeekFrontingDialAddress, "443")
   589  			dialParams.MeekHostHeader = dialParams.MeekFrontingHost
   590  
   591  			// For a FrontingSpec, an SNI value of "" indicates to disable/omit SNI, so
   592  			// never transform in that case.
   593  			if dialParams.MeekSNIServerName != "" {
   594  				if p.WeightedCoinFlip(parameters.TransformHostNameProbability) {
   595  					dialParams.MeekSNIServerName = selectHostName(dialParams.TunnelProtocol, p)
   596  					dialParams.MeekTransformedHostName = true
   597  				}
   598  			}
   599  
   600  			// The minimum delay value is determined by the Conjure station, which
   601  			// performs an asynchronous "liveness test" against the selected phantom
   602  			// IPs. The min/max range allows us to introduce some jitter so that we
   603  			// don't present a trivial inter-flow fingerprint: CDN connection, fixed
   604  			// delay, phantom dial.
   605  
   606  			minDelay := p.Duration(parameters.ConjureAPIRegistrarMinDelay)
   607  			maxDelay := p.Duration(parameters.ConjureAPIRegistrarMaxDelay)
   608  			dialParams.ConjureAPIRegistrarDelay = prng.Period(minDelay, maxDelay)
   609  
   610  		} else if dialParams.ConjureDecoyRegistration {
   611  
   612  			dialParams.ConjureDecoyRegistrarWidth = decoyWidth
   613  			minDelay := p.Duration(parameters.ConjureDecoyRegistrarMinDelay)
   614  			maxDelay := p.Duration(parameters.ConjureDecoyRegistrarMaxDelay)
   615  			dialParams.ConjureAPIRegistrarDelay = prng.Period(minDelay, maxDelay)
   616  
   617  		} else {
   618  
   619  			return nil, errors.TraceNew("no Conjure registrar configured")
   620  		}
   621  	}
   622  
   623  	if (!isReplay || !replayConjureTransport) &&
   624  		protocol.TunnelProtocolUsesConjure(dialParams.TunnelProtocol) {
   625  
   626  		dialParams.ConjureTransport = protocol.CONJURE_TRANSPORT_MIN_OSSH
   627  		if p.WeightedCoinFlip(
   628  			parameters.ConjureTransportObfs4Probability) {
   629  			dialParams.ConjureTransport = protocol.CONJURE_TRANSPORT_OBFS4_OSSH
   630  		}
   631  	}
   632  
   633  	usingTLS := protocol.TunnelProtocolUsesMeekHTTPS(dialParams.TunnelProtocol) ||
   634  		protocol.TunnelProtocolUsesTLSOSSH(dialParams.TunnelProtocol) ||
   635  		dialParams.ConjureAPIRegistration
   636  
   637  	if (!isReplay || !replayTLSProfile) && usingTLS {
   638  
   639  		dialParams.SelectedTLSProfile = true
   640  
   641  		requireTLS12SessionTickets := protocol.TunnelProtocolRequiresTLS12SessionTickets(
   642  			dialParams.TunnelProtocol)
   643  
   644  		requireTLS13Support := protocol.TunnelProtocolRequiresTLS13Support(dialParams.TunnelProtocol)
   645  
   646  		isFronted := protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) ||
   647  			dialParams.ConjureAPIRegistration
   648  
   649  		dialParams.TLSProfile, dialParams.TLSVersion, dialParams.RandomizedTLSProfileSeed, err = SelectTLSProfile(
   650  			requireTLS12SessionTickets, requireTLS13Support, isFronted, serverEntry.FrontingProviderID, p)
   651  		if err != nil {
   652  			return nil, errors.Trace(err)
   653  		}
   654  
   655  		if dialParams.TLSProfile == "" && (requireTLS12SessionTickets || requireTLS13Support) {
   656  			return nil, errors.TraceNew("required TLS profile not found")
   657  		}
   658  
   659  		dialParams.NoDefaultTLSSessionID = p.WeightedCoinFlip(
   660  			parameters.NoDefaultTLSSessionIDProbability)
   661  	}
   662  
   663  	if (!isReplay || !replayFronting) &&
   664  		protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) {
   665  
   666  		dialParams.FrontingProviderID = serverEntry.FrontingProviderID
   667  
   668  		dialParams.MeekFrontingDialAddress, dialParams.MeekFrontingHost, err =
   669  			selectFrontingParameters(serverEntry)
   670  		if err != nil {
   671  			return nil, errors.Trace(err)
   672  		}
   673  	}
   674  
   675  	if !isReplay || !replayHostname {
   676  
   677  		// Any MeekHostHeader selections made here will be overridden below,
   678  		// as required, for fronting cases.
   679  
   680  		if protocol.TunnelProtocolUsesMeekHTTPS(dialParams.TunnelProtocol) ||
   681  			protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol) {
   682  
   683  			dialParams.MeekSNIServerName = ""
   684  			hostname := ""
   685  			if p.WeightedCoinFlip(parameters.TransformHostNameProbability) {
   686  				dialParams.MeekSNIServerName = selectHostName(dialParams.TunnelProtocol, p)
   687  				hostname = dialParams.MeekSNIServerName
   688  				dialParams.MeekTransformedHostName = true
   689  			} else {
   690  
   691  				// Always select a hostname for the Host header in this case.
   692  				// Unlike HTTP, the Host header isn't plaintext on the wire,
   693  				// and so there's no anti-fingerprint benefit from presenting
   694  				// the server IP address in the Host header. Omitting the
   695  				// server IP here can prevent exposing it in certain
   696  				// scenarios where the traffic is rerouted and arrives at a
   697  				// different HTTPS server.
   698  
   699  				hostname = selectHostName(dialParams.TunnelProtocol, p)
   700  			}
   701  			if serverEntry.MeekServerPort == 443 {
   702  				dialParams.MeekHostHeader = hostname
   703  			} else {
   704  				dialParams.MeekHostHeader = net.JoinHostPort(
   705  					hostname, strconv.Itoa(serverEntry.MeekServerPort))
   706  			}
   707  
   708  		} else if protocol.TunnelProtocolUsesTLSOSSH(dialParams.TunnelProtocol) {
   709  
   710  			dialParams.TLSOSSHSNIServerName = ""
   711  			if p.WeightedCoinFlip(parameters.TransformHostNameProbability) {
   712  				dialParams.TLSOSSHSNIServerName = selectHostName(dialParams.TunnelProtocol, p)
   713  				dialParams.TLSOSSHTransformedSNIServerName = true
   714  			}
   715  
   716  		} else if protocol.TunnelProtocolUsesMeekHTTP(dialParams.TunnelProtocol) {
   717  
   718  			dialParams.MeekHostHeader = ""
   719  			hostname := serverEntry.IpAddress
   720  			if p.WeightedCoinFlip(parameters.TransformHostNameProbability) {
   721  				hostname = selectHostName(dialParams.TunnelProtocol, p)
   722  				dialParams.MeekTransformedHostName = true
   723  			}
   724  			if serverEntry.MeekServerPort == 80 {
   725  				dialParams.MeekHostHeader = hostname
   726  			} else {
   727  				dialParams.MeekHostHeader = net.JoinHostPort(
   728  					hostname, strconv.Itoa(serverEntry.MeekServerPort))
   729  			}
   730  		} else if protocol.TunnelProtocolUsesQUIC(dialParams.TunnelProtocol) {
   731  			dialParams.QUICDialSNIAddress = selectHostName(dialParams.TunnelProtocol, p)
   732  		}
   733  	}
   734  
   735  	if (!isReplay || !replayQUICVersion) &&
   736  		protocol.TunnelProtocolUsesQUIC(dialParams.TunnelProtocol) {
   737  
   738  		isFronted := protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol)
   739  		dialParams.QUICVersion = selectQUICVersion(isFronted, serverEntry, p)
   740  
   741  		// Due to potential tactics configurations, it may be that no QUIC
   742  		// version is selected. Abort immediately, with no error, as in the
   743  		// selectProtocol case. quic.Dial and quic.NewQUICTransporter will
   744  		// check for a missing QUIC version, but at that later stage an
   745  		// unnecessary failed_tunnel can be logged in this scenario.
   746  		if dialParams.QUICVersion == "" {
   747  			return nil, nil
   748  		}
   749  
   750  		if protocol.QUICVersionHasRandomizedClientHello(dialParams.QUICVersion) {
   751  			dialParams.QUICClientHelloSeed, err = prng.NewSeed()
   752  			if err != nil {
   753  				return nil, errors.Trace(err)
   754  			}
   755  		}
   756  
   757  		dialParams.QUICDisablePathMTUDiscovery =
   758  			protocol.QUICVersionUsesPathMTUDiscovery(dialParams.QUICVersion) &&
   759  				p.WeightedCoinFlip(parameters.QUICDisableClientPathMTUDiscoveryProbability)
   760  	}
   761  
   762  	if (!isReplay || !replayObfuscatedQUIC) &&
   763  		protocol.QUICVersionIsObfuscated(dialParams.QUICVersion) {
   764  
   765  		dialParams.ObfuscatedQUICPaddingSeed, err = prng.NewSeed()
   766  		if err != nil {
   767  			return nil, errors.Trace(err)
   768  		}
   769  	}
   770  
   771  	if protocol.QUICVersionIsObfuscated(dialParams.QUICVersion) {
   772  
   773  		if serverEntry.DisableObfuscatedQUICTransforms {
   774  
   775  			dialParams.ObfuscatedQUICNonceTransformerParameters = nil
   776  
   777  		} else if !isReplay || !replayObfuscatedQUICNonceTransformer {
   778  
   779  			params, err := makeSeedTransformerParameters(
   780  				p,
   781  				parameters.ObfuscatedQUICNonceTransformProbability,
   782  				parameters.ObfuscatedQUICNonceTransformSpecs,
   783  				parameters.ObfuscatedQUICNonceTransformScopedSpecNames)
   784  			if err != nil {
   785  				return nil, errors.Trace(err)
   786  			}
   787  
   788  			if params.TransformSpec != nil {
   789  				dialParams.ObfuscatedQUICNonceTransformerParameters = params
   790  			} else {
   791  				dialParams.ObfuscatedQUICNonceTransformerParameters = nil
   792  			}
   793  		}
   794  	}
   795  
   796  	if !isReplay || !replayLivenessTest {
   797  
   798  		// TODO: initialize only when LivenessTestMaxUp/DownstreamBytes > 0?
   799  		dialParams.LivenessTestSeed, err = prng.NewSeed()
   800  		if err != nil {
   801  			return nil, errors.Trace(err)
   802  		}
   803  	}
   804  
   805  	if !isReplay || !replayAPIRequestPadding {
   806  		dialParams.APIRequestPaddingSeed, err = prng.NewSeed()
   807  		if err != nil {
   808  			return nil, errors.Trace(err)
   809  		}
   810  	}
   811  
   812  	// Initialize dialParams.ResolveParameters for dials that will resolve
   813  	// domain names, which currently includes fronted meek and Conjure API
   814  	// registration, where the dial address is not an IP address.
   815  	//
   816  	// dialParams.ResolveParameters must be nil when the dial address is an IP
   817  	// address to ensure that no DNS dial parameters are reported in metrics
   818  	// or diagnostics when when no domain is resolved.
   819  
   820  	useResolver := (protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) ||
   821  		dialParams.ConjureAPIRegistration) &&
   822  		net.ParseIP(dialParams.MeekFrontingDialAddress) == nil
   823  
   824  	if (!isReplay || !replayResolveParameters) && useResolver {
   825  
   826  		dialParams.ResolveParameters, err = dialParams.resolver.MakeResolveParameters(
   827  			p, dialParams.FrontingProviderID)
   828  		if err != nil {
   829  			return nil, errors.Trace(err)
   830  		}
   831  	}
   832  
   833  	if !isReplay || !replayHoldOffTunnel {
   834  
   835  		if common.Contains(
   836  			p.TunnelProtocols(parameters.HoldOffTunnelProtocols), dialParams.TunnelProtocol) ||
   837  
   838  			(protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol) &&
   839  				common.Contains(
   840  					p.Strings(parameters.HoldOffTunnelFrontingProviderIDs),
   841  					dialParams.FrontingProviderID)) {
   842  
   843  			if p.WeightedCoinFlip(parameters.HoldOffTunnelProbability) {
   844  
   845  				dialParams.HoldOffTunnelDuration = prng.Period(
   846  					p.Duration(parameters.HoldOffTunnelMinDuration),
   847  					p.Duration(parameters.HoldOffTunnelMaxDuration))
   848  			}
   849  		}
   850  
   851  	}
   852  
   853  	// OSSH prefix and seed transform are applied only to the OSSH tunnel protocol,
   854  	// and not to any other protocol layered over OSSH.
   855  	if dialParams.TunnelProtocol == protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH {
   856  
   857  		if serverEntry.DisableOSSHTransforms {
   858  
   859  			dialParams.OSSHObfuscatorSeedTransformerParameters = nil
   860  
   861  		} else if !isReplay || !replayOSSHSeedTransformerParameters {
   862  
   863  			params, err := makeSeedTransformerParameters(
   864  				p,
   865  				parameters.OSSHObfuscatorSeedTransformProbability,
   866  				parameters.OSSHObfuscatorSeedTransformSpecs,
   867  				parameters.OSSHObfuscatorSeedTransformScopedSpecNames)
   868  			if err != nil {
   869  				return nil, errors.Trace(err)
   870  			}
   871  
   872  			if params.TransformSpec != nil {
   873  				dialParams.OSSHObfuscatorSeedTransformerParameters = params
   874  			} else {
   875  				dialParams.OSSHObfuscatorSeedTransformerParameters = nil
   876  			}
   877  		}
   878  
   879  		if serverEntry.DisableOSSHPrefix {
   880  			dialParams.OSSHPrefixSpec = nil
   881  			dialParams.OSSHPrefixSplitConfig = nil
   882  
   883  		} else if !isReplay || !replayOSSHPrefix {
   884  
   885  			dialPortNumber, err := serverEntry.GetDialPortNumber(dialParams.TunnelProtocol)
   886  			if err != nil {
   887  				return nil, errors.Trace(err)
   888  			}
   889  			prefixSpec, err := makeOSSHPrefixSpecParameters(p, strconv.Itoa(dialPortNumber))
   890  			if err != nil {
   891  				return nil, errors.Trace(err)
   892  			}
   893  
   894  			splitConfig, err := makeOSSHPrefixSplitConfig(p)
   895  			if err != nil {
   896  				return nil, errors.Trace(err)
   897  			}
   898  
   899  			if prefixSpec.Spec != nil {
   900  				dialParams.OSSHPrefixSpec = prefixSpec
   901  				dialParams.OSSHPrefixSplitConfig = splitConfig
   902  			} else {
   903  				dialParams.OSSHPrefixSpec = nil
   904  				dialParams.OSSHPrefixSplitConfig = nil
   905  			}
   906  		}
   907  
   908  		// OSSHPrefix supersedes OSSHObfuscatorSeedTransform.
   909  		// This ensures both tactics are not used simultaneously,
   910  		// until OSSHObfuscatorSeedTransform is removed.
   911  		if dialParams.OSSHPrefixSpec != nil {
   912  			dialParams.OSSHObfuscatorSeedTransformerParameters = nil
   913  		}
   914  
   915  	}
   916  
   917  	if protocol.TunnelProtocolUsesMeekHTTP(dialParams.TunnelProtocol) {
   918  
   919  		if serverEntry.DisableHTTPTransforms {
   920  
   921  			dialParams.HTTPTransformerParameters = nil
   922  
   923  		} else if !isReplay || !replayHTTPTransformerParameters {
   924  
   925  			isFronted := protocol.TunnelProtocolUsesFrontedMeek(dialParams.TunnelProtocol)
   926  
   927  			params, err := makeHTTPTransformerParameters(config.GetParameters().Get(), serverEntry.FrontingProviderID, isFronted)
   928  			if err != nil {
   929  				return nil, errors.Trace(err)
   930  			}
   931  
   932  			if params.ProtocolTransformSpec != nil {
   933  				dialParams.HTTPTransformerParameters = params
   934  			} else {
   935  				dialParams.HTTPTransformerParameters = nil
   936  			}
   937  		}
   938  	}
   939  
   940  	// Set dial address fields. This portion of configuration is
   941  	// deterministic, given the parameters established or replayed so far.
   942  
   943  	dialPortNumber, err := serverEntry.GetDialPortNumber(dialParams.TunnelProtocol)
   944  	if err != nil {
   945  		return nil, errors.Trace(err)
   946  	}
   947  
   948  	dialParams.DialPortNumber = strconv.Itoa(dialPortNumber)
   949  
   950  	switch dialParams.TunnelProtocol {
   951  
   952  	case protocol.TUNNEL_PROTOCOL_SSH,
   953  		protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH,
   954  		protocol.TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH,
   955  		protocol.TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH,
   956  		protocol.TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH,
   957  		protocol.TUNNEL_PROTOCOL_TLS_OBFUSCATED_SSH:
   958  
   959  		dialParams.DirectDialAddress = net.JoinHostPort(serverEntry.IpAddress, dialParams.DialPortNumber)
   960  
   961  	case protocol.TUNNEL_PROTOCOL_FRONTED_MEEK,
   962  		protocol.TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH:
   963  
   964  		dialParams.MeekDialAddress = net.JoinHostPort(dialParams.MeekFrontingDialAddress, dialParams.DialPortNumber)
   965  		dialParams.MeekHostHeader = dialParams.MeekFrontingHost
   966  		if serverEntry.MeekFrontingDisableSNI {
   967  			dialParams.MeekSNIServerName = ""
   968  			// When SNI is omitted, the transformed host name is not used.
   969  			dialParams.MeekTransformedHostName = false
   970  		} else if !dialParams.MeekTransformedHostName {
   971  			dialParams.MeekSNIServerName = dialParams.MeekFrontingDialAddress
   972  		}
   973  
   974  	case protocol.TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP:
   975  
   976  		dialParams.MeekDialAddress = net.JoinHostPort(dialParams.MeekFrontingDialAddress, dialParams.DialPortNumber)
   977  		dialParams.MeekHostHeader = dialParams.MeekFrontingHost
   978  		// For FRONTED HTTP, the Host header cannot be transformed.
   979  		dialParams.MeekTransformedHostName = false
   980  
   981  	case protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK:
   982  
   983  		dialParams.MeekDialAddress = net.JoinHostPort(serverEntry.IpAddress, dialParams.DialPortNumber)
   984  
   985  	case protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS,
   986  		protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET:
   987  
   988  		dialParams.MeekDialAddress = net.JoinHostPort(serverEntry.IpAddress, dialParams.DialPortNumber)
   989  		if !dialParams.MeekTransformedHostName {
   990  			// Note: IP address in SNI field will be omitted.
   991  			dialParams.MeekSNIServerName = serverEntry.IpAddress
   992  		}
   993  
   994  	default:
   995  		return nil, errors.Tracef(
   996  			"unknown tunnel protocol: %s", dialParams.TunnelProtocol)
   997  	}
   998  
   999  	if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) {
  1000  
  1001  		host, _, _ := net.SplitHostPort(dialParams.MeekDialAddress)
  1002  
  1003  		if p.Bool(parameters.MeekDialDomainsOnly) {
  1004  			if net.ParseIP(host) != nil {
  1005  				// No error, as this is a "not supported" case.
  1006  				return nil, nil
  1007  			}
  1008  		}
  1009  
  1010  		// The underlying TLS implementation will automatically omit SNI for
  1011  		// IP address server name values; we have this explicit check here so
  1012  		// we record the correct value for stats.
  1013  		if net.ParseIP(dialParams.MeekSNIServerName) != nil {
  1014  			dialParams.MeekSNIServerName = ""
  1015  		}
  1016  	}
  1017  
  1018  	// Initialize/replay User-Agent header for HTTP upstream proxy and meek protocols.
  1019  
  1020  	if config.UseUpstreamProxy() {
  1021  		// Note: UpstreamProxyURL will be validated in the dial
  1022  		proxyURL, err := common.SafeParseURL(config.UpstreamProxyURL)
  1023  		if err == nil {
  1024  			dialParams.UpstreamProxyType = proxyURL.Scheme
  1025  		}
  1026  	}
  1027  
  1028  	dialCustomHeaders := makeDialCustomHeaders(config, p)
  1029  
  1030  	if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) ||
  1031  		dialParams.UpstreamProxyType == "http" ||
  1032  		dialParams.ConjureAPIRegistration {
  1033  
  1034  		if !isReplay || !replayUserAgent {
  1035  			dialParams.SelectedUserAgent, dialParams.UserAgent = selectUserAgentIfUnset(p, dialCustomHeaders)
  1036  		}
  1037  
  1038  		if dialParams.SelectedUserAgent {
  1039  			dialCustomHeaders.Set("User-Agent", dialParams.UserAgent)
  1040  		}
  1041  
  1042  	}
  1043  
  1044  	// UpstreamProxyCustomHeaderNames is a reported metric. Just the names and
  1045  	// not the values are reported, in case the values are identifying.
  1046  
  1047  	if len(config.CustomHeaders) > 0 {
  1048  		dialParams.UpstreamProxyCustomHeaderNames = make([]string, 0)
  1049  		for name := range dialCustomHeaders {
  1050  			if name == "User-Agent" && dialParams.SelectedUserAgent {
  1051  				continue
  1052  			}
  1053  			dialParams.UpstreamProxyCustomHeaderNames = append(dialParams.UpstreamProxyCustomHeaderNames, name)
  1054  		}
  1055  	}
  1056  
  1057  	// Initialize Dial/MeekConfigs to be passed to the corresponding dialers.
  1058  
  1059  	// Custom ResolveParameters are set only when useResolver is true, but
  1060  	// DialConfig.ResolveIP is required and wired up unconditionally. Any
  1061  	// misconfigured or miscoded domain dial cases will use default
  1062  	// ResolveParameters.
  1063  	//
  1064  	// ResolveIP will use the networkID obtained above, as it will be used
  1065  	// almost immediately, instead of incurring the overhead of calling
  1066  	// GetNetworkID again.
  1067  	resolveIP := func(ctx context.Context, hostname string) ([]net.IP, error) {
  1068  		IPs, err := dialParams.resolver.ResolveIP(
  1069  			ctx,
  1070  			networkID,
  1071  			dialParams.ResolveParameters,
  1072  			hostname)
  1073  		if err != nil {
  1074  			return nil, errors.Trace(err)
  1075  		}
  1076  		return IPs, nil
  1077  	}
  1078  
  1079  	// Fragmentor configuration.
  1080  	// Note: fragmentorConfig is nil if fragmentor is disabled for prefixed OSSH.
  1081  	//
  1082  	// Limitation: when replaying and with ReplayIgnoreChangedConfigState set,
  1083  	// fragmentor.NewUpstreamConfig may select a config using newer tactics
  1084  	// parameters.
  1085  	fragmentorConfig := fragmentor.NewUpstreamConfig(p, dialParams.TunnelProtocol, dialParams.FragmentorSeed)
  1086  	if !p.Bool(parameters.OSSHPrefixEnableFragmentor) && dialParams.OSSHPrefixSpec != nil {
  1087  		fragmentorConfig = nil
  1088  	}
  1089  
  1090  	dialParams.dialConfig = &DialConfig{
  1091  		DiagnosticID:                  serverEntry.GetDiagnosticID(),
  1092  		UpstreamProxyURL:              config.UpstreamProxyURL,
  1093  		CustomHeaders:                 dialCustomHeaders,
  1094  		BPFProgramInstructions:        dialParams.BPFProgramInstructions,
  1095  		DeviceBinder:                  config.deviceBinder,
  1096  		IPv6Synthesizer:               config.IPv6Synthesizer,
  1097  		ResolveIP:                     resolveIP,
  1098  		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
  1099  		FragmentorConfig:              fragmentorConfig,
  1100  		UpstreamProxyErrorCallback:    upstreamProxyErrorCallback,
  1101  	}
  1102  
  1103  	// Unconditionally initialize MeekResolvedIPAddress, so a valid string can
  1104  	// always be read.
  1105  	dialParams.MeekResolvedIPAddress.Store("")
  1106  
  1107  	if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) ||
  1108  		dialParams.ConjureAPIRegistration {
  1109  
  1110  		// For tactics requests, AddPsiphonFrontingHeader is set when set for
  1111  		// the related tunnel protocol. E.g., FRONTED-OSSH-MEEK for
  1112  		// FRONTED-MEEK-TACTICS. AddPsiphonFrontingHeader is not replayed.
  1113  		addPsiphonFrontingHeader := false
  1114  		if dialParams.FrontingProviderID != "" {
  1115  			addPsiphonFrontingHeader = common.Contains(
  1116  				p.LabeledTunnelProtocols(
  1117  					parameters.AddFrontingProviderPsiphonFrontingHeader, dialParams.FrontingProviderID),
  1118  				dialParams.TunnelProtocol)
  1119  		}
  1120  
  1121  		dialParams.meekConfig = &MeekConfig{
  1122  			DiagnosticID:                  serverEntry.GetDiagnosticID(),
  1123  			Parameters:                    config.GetParameters(),
  1124  			DialAddress:                   dialParams.MeekDialAddress,
  1125  			UseQUIC:                       protocol.TunnelProtocolUsesFrontedMeekQUIC(dialParams.TunnelProtocol),
  1126  			QUICVersion:                   dialParams.QUICVersion,
  1127  			QUICClientHelloSeed:           dialParams.QUICClientHelloSeed,
  1128  			QUICDisablePathMTUDiscovery:   dialParams.QUICDisablePathMTUDiscovery,
  1129  			UseHTTPS:                      usingTLS,
  1130  			TLSProfile:                    dialParams.TLSProfile,
  1131  			LegacyPassthrough:             serverEntry.ProtocolUsesLegacyPassthrough(dialParams.TunnelProtocol),
  1132  			NoDefaultTLSSessionID:         dialParams.NoDefaultTLSSessionID,
  1133  			RandomizedTLSProfileSeed:      dialParams.RandomizedTLSProfileSeed,
  1134  			UseObfuscatedSessionTickets:   dialParams.TunnelProtocol == protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET,
  1135  			SNIServerName:                 dialParams.MeekSNIServerName,
  1136  			AddPsiphonFrontingHeader:      addPsiphonFrontingHeader,
  1137  			VerifyServerName:              dialParams.MeekVerifyServerName,
  1138  			VerifyPins:                    dialParams.MeekVerifyPins,
  1139  			DisableSystemRootCAs:          config.DisableSystemRootCAs,
  1140  			HostHeader:                    dialParams.MeekHostHeader,
  1141  			TransformedHostName:           dialParams.MeekTransformedHostName,
  1142  			ClientTunnelProtocol:          dialParams.TunnelProtocol,
  1143  			MeekCookieEncryptionPublicKey: serverEntry.MeekCookieEncryptionPublicKey,
  1144  			MeekObfuscatedKey:             serverEntry.MeekObfuscatedKey,
  1145  			MeekObfuscatorPaddingSeed:     dialParams.MeekObfuscatorPaddingSeed,
  1146  			NetworkLatencyMultiplier:      dialParams.NetworkLatencyMultiplier,
  1147  			HTTPTransformerParameters:     dialParams.HTTPTransformerParameters,
  1148  		}
  1149  
  1150  		// Use an asynchronous callback to record the resolved IP address when
  1151  		// dialing a domain name. Note that DialMeek doesn't immediately
  1152  		// establish any HTTP connections, so the resolved IP address won't be
  1153  		// reported in all cases until after SSH traffic is relayed or a
  1154  		// endpoint request is made over the meek connection.
  1155  		dialParams.dialConfig.ResolvedIPCallback = func(IPAddress string) {
  1156  			dialParams.MeekResolvedIPAddress.Store(IPAddress)
  1157  		}
  1158  
  1159  		if isTactics {
  1160  			dialParams.meekConfig.Mode = MeekModeObfuscatedRoundTrip
  1161  		} else if dialParams.ConjureAPIRegistration {
  1162  			dialParams.meekConfig.Mode = MeekModePlaintextRoundTrip
  1163  		} else {
  1164  			dialParams.meekConfig.Mode = MeekModeRelay
  1165  		}
  1166  	}
  1167  
  1168  	return dialParams, nil
  1169  }
  1170  
  1171  func (dialParams *DialParameters) GetDialConfig() *DialConfig {
  1172  	return dialParams.dialConfig
  1173  }
  1174  
  1175  func (dialParams *DialParameters) GetTLSOSSHConfig(config *Config) *TLSTunnelConfig {
  1176  
  1177  	return &TLSTunnelConfig{
  1178  		CustomTLSConfig: &CustomTLSConfig{
  1179  			Parameters:               config.GetParameters(),
  1180  			DialAddr:                 dialParams.DirectDialAddress,
  1181  			SNIServerName:            dialParams.TLSOSSHSNIServerName,
  1182  			SkipVerify:               true,
  1183  			VerifyServerName:         "",
  1184  			VerifyPins:               nil,
  1185  			TLSProfile:               dialParams.TLSProfile,
  1186  			NoDefaultTLSSessionID:    &dialParams.NoDefaultTLSSessionID,
  1187  			RandomizedTLSProfileSeed: dialParams.RandomizedTLSProfileSeed,
  1188  		},
  1189  		// Obfuscated session tickets are not used because TLS-OSSH uses TLS 1.3.
  1190  		UseObfuscatedSessionTickets: false,
  1191  		// Meek obfuscated key used to allow clients with legacy unfronted
  1192  		// meek-https server entries, that have the passthrough capability, to
  1193  		// connect with TLS-OSSH to the servers corresponding to those server
  1194  		// entries, which now support TLS-OSSH by demultiplexing meek-https and
  1195  		// TLS-OSSH over the meek-https port.
  1196  		ObfuscatedKey:         dialParams.ServerEntry.MeekObfuscatedKey,
  1197  		ObfuscatorPaddingSeed: dialParams.TLSOSSHObfuscatorPaddingSeed,
  1198  	}
  1199  }
  1200  
  1201  func (dialParams *DialParameters) GetMeekConfig() *MeekConfig {
  1202  	return dialParams.meekConfig
  1203  }
  1204  
  1205  // GetNetworkType returns a network type name, suitable for metrics, which is
  1206  // derived from the network ID.
  1207  func (dialParams *DialParameters) GetNetworkType() string {
  1208  
  1209  	// Unlike the logic in loggingNetworkIDGetter.GetNetworkID, we don't take the
  1210  	// arbitrary text before the first "-" since some platforms without network
  1211  	// detection support stub in random values to enable tactics. Instead we
  1212  	// check for and use the common network type prefixes currently used in
  1213  	// NetworkIDGetter implementations.
  1214  
  1215  	if strings.HasPrefix(dialParams.NetworkID, "VPN") {
  1216  		return "VPN"
  1217  	}
  1218  	if strings.HasPrefix(dialParams.NetworkID, "WIFI") {
  1219  		return "WIFI"
  1220  	}
  1221  	if strings.HasPrefix(dialParams.NetworkID, "MOBILE") {
  1222  		return "MOBILE"
  1223  	}
  1224  	return "UNKNOWN"
  1225  }
  1226  
  1227  func (dialParams *DialParameters) Succeeded() {
  1228  
  1229  	// When TTL is 0, don't store dial parameters.
  1230  	if dialParams.LastUsedTimestamp.IsZero() {
  1231  		return
  1232  	}
  1233  
  1234  	NoticeInfo("Set dial parameters for %s", dialParams.ServerEntry.GetDiagnosticID())
  1235  	err := SetDialParameters(dialParams.ServerEntry.IpAddress, dialParams.NetworkID, dialParams)
  1236  	if err != nil {
  1237  		NoticeWarning("SetDialParameters failed: %s", err)
  1238  	}
  1239  }
  1240  
  1241  func (dialParams *DialParameters) Failed(config *Config) {
  1242  
  1243  	// When a tunnel fails, and the dial is a replay, clear the stored dial
  1244  	// parameters which are now presumed to be blocked, impaired or otherwise
  1245  	// no longer effective.
  1246  	//
  1247  	// It may be the case that a dial is not using stored dial parameters
  1248  	// (!IsReplay), and in this case we retain those dial parameters since they
  1249  	// were not exercised and may still be effective.
  1250  	//
  1251  	// Failed tunnel dial parameters may be retained with a configurable
  1252  	// probability; this is intended to help mitigate false positive failures due
  1253  	// to, e.g., temporary network disruptions or server load limiting.
  1254  
  1255  	if dialParams.IsReplay &&
  1256  		!config.GetParameters().Get().WeightedCoinFlip(
  1257  			parameters.ReplayRetainFailedProbability) {
  1258  
  1259  		NoticeInfo("Delete dial parameters for %s", dialParams.ServerEntry.GetDiagnosticID())
  1260  		err := DeleteDialParameters(dialParams.ServerEntry.IpAddress, dialParams.NetworkID)
  1261  		if err != nil {
  1262  			NoticeWarning("DeleteDialParameters failed: %s", err)
  1263  		}
  1264  	}
  1265  }
  1266  
  1267  func (dialParams *DialParameters) GetTLSVersionForMetrics() string {
  1268  	return getTLSVersionForMetrics(dialParams.TLSVersion, dialParams.NoDefaultTLSSessionID)
  1269  }
  1270  
  1271  func getTLSVersionForMetrics(tlsVersion string, noDefaultTLSSessionID bool) string {
  1272  	version := tlsVersion
  1273  	if noDefaultTLSSessionID {
  1274  		version += "-no_def_id"
  1275  	}
  1276  	return version
  1277  }
  1278  
  1279  // ExchangedDialParameters represents the subset of DialParameters that is
  1280  // shared in a client-to-client exchange of server connection info.
  1281  //
  1282  // The purpose of client-to-client exchange if for one user that can connect
  1283  // to help another user that cannot connect by sharing their connected
  1284  // configuration, including the server entry and dial parameters.
  1285  //
  1286  // There are two concerns regarding which dial parameter fields are safe to
  1287  // exchange:
  1288  //
  1289  //   - Unlike signed server entries, there's no independent trust anchor
  1290  //     that can certify that the exchange data is valid.
  1291  //
  1292  //   - While users should only perform the exchange with trusted peers,
  1293  //     the user's trust in their peer may be misplaced.
  1294  //
  1295  // This presents the possibility of attack such as the peer sending dial
  1296  // parameters that could be used to trace/monitor/flag the importer; or
  1297  // sending dial parameters, including dial address and SNI, to cause the peer
  1298  // to appear to connect to a banned service.
  1299  //
  1300  // To mitigate these risks, only a subset of dial parameters are exchanged.
  1301  // When exchanged dial parameters and imported and used, all unexchanged
  1302  // parameters are generated locally. At this time, only the tunnel protocol is
  1303  // exchanged. We consider tunnel protocol selection one of the key connection
  1304  // success factors.
  1305  //
  1306  // In addition, the exchange peers may not be on the same network with the
  1307  // same blocking and circumvention characteristics, which is another reason
  1308  // to limit exchanged dial parameter values to broadly applicable fields.
  1309  //
  1310  // Unlike the exchanged (and otherwise acquired) server entry,
  1311  // ExchangedDialParameters does not use the ServerEntry_Fields_ representation
  1312  // which allows older clients to receive and store new, unknown fields. Such a
  1313  // facility is less useful in this case, since exchanged dial parameters and
  1314  // used immediately and have a short lifespan.
  1315  //
  1316  // TODO: exchange more dial parameters, such as TLS profile, QUIC version, etc.
  1317  type ExchangedDialParameters struct {
  1318  	TunnelProtocol string
  1319  }
  1320  
  1321  // NewExchangedDialParameters creates a new ExchangedDialParameters from a
  1322  // DialParameters, including only the exchanged values.
  1323  // NewExchangedDialParameters assumes the input DialParameters has been
  1324  // initialized and populated by MakeDialParameters.
  1325  func NewExchangedDialParameters(dialParams *DialParameters) *ExchangedDialParameters {
  1326  	return &ExchangedDialParameters{
  1327  		TunnelProtocol: dialParams.TunnelProtocol,
  1328  	}
  1329  }
  1330  
  1331  // Validate checks that the ExchangedDialParameters contains only valid values
  1332  // and is compatible with the specified server entry.
  1333  func (dialParams *ExchangedDialParameters) Validate(serverEntry *protocol.ServerEntry) error {
  1334  	if !common.Contains(protocol.SupportedTunnelProtocols, dialParams.TunnelProtocol) {
  1335  		return errors.Tracef("unknown tunnel protocol: %s", dialParams.TunnelProtocol)
  1336  	}
  1337  	if !serverEntry.SupportsProtocol(dialParams.TunnelProtocol) {
  1338  		return errors.Tracef("unsupported tunnel protocol: %s", dialParams.TunnelProtocol)
  1339  	}
  1340  	return nil
  1341  }
  1342  
  1343  // MakeDialParameters creates a new, partially intitialized DialParameters
  1344  // from the values in ExchangedDialParameters. The returned DialParameters
  1345  // must not be used directly for dialing. It is intended to be stored, and
  1346  // then later fully initialized by MakeDialParameters.
  1347  func (dialParams *ExchangedDialParameters) MakeDialParameters(
  1348  	config *Config,
  1349  	p parameters.ParametersAccessor,
  1350  	serverEntry *protocol.ServerEntry) *DialParameters {
  1351  
  1352  	configStateHash, serverEntryHash := getDialStateHashes(config, p, serverEntry)
  1353  
  1354  	return &DialParameters{
  1355  		IsExchanged:             true,
  1356  		LastUsedTimestamp:       time.Now(),
  1357  		LastUsedConfigStateHash: configStateHash,
  1358  		LastUsedServerEntryHash: serverEntryHash,
  1359  		TunnelProtocol:          dialParams.TunnelProtocol,
  1360  	}
  1361  }
  1362  
  1363  // getDialStateHashes returns two hashes: the config state hash reflects the
  1364  // config dial parameters and tactics tag used for a dial; and the server
  1365  // entry hash relects the server entry used for a dial.
  1366  //
  1367  // These hashes change if the input values change in a way that invalidates
  1368  // any stored dial parameters.
  1369  func getDialStateHashes(
  1370  	config *Config,
  1371  	p parameters.ParametersAccessor,
  1372  	serverEntry *protocol.ServerEntry) ([]byte, []byte) {
  1373  
  1374  	// MD5 hash is used solely as a data checksum and not for any security
  1375  	// purpose.
  1376  	hash := md5.New()
  1377  
  1378  	// Add a hash of relevant dial parameter config fields. Config fields
  1379  	// that change due to user preference changes, such as selected egress
  1380  	// region, are not to be included in config.dialParametersHash.
  1381  	//
  1382  	// Limitation: the config hash may change even when tactics will override the
  1383  	// changed config field.
  1384  	hash.Write(config.dialParametersHash)
  1385  
  1386  	// Add the active tactics tag.
  1387  	hash.Write([]byte(p.Tag()))
  1388  
  1389  	clientStateHash := hash.Sum(nil)
  1390  
  1391  	hash = md5.New()
  1392  
  1393  	// Add the server entry version and local timestamp, both of which should
  1394  	// change when the server entry contents change and/or a new local copy is
  1395  	// imported.
  1396  	// TODO: marshal entire server entry?
  1397  	var serverEntryConfigurationVersion [8]byte
  1398  	binary.BigEndian.PutUint64(
  1399  		serverEntryConfigurationVersion[:],
  1400  		uint64(serverEntry.ConfigurationVersion))
  1401  	hash.Write(serverEntryConfigurationVersion[:])
  1402  	hash.Write([]byte(serverEntry.LocalTimestamp))
  1403  
  1404  	serverEntryHash := hash.Sum(nil)
  1405  
  1406  	return clientStateHash, serverEntryHash
  1407  }
  1408  
  1409  func selectFrontingParameters(
  1410  	serverEntry *protocol.ServerEntry) (string, string, error) {
  1411  
  1412  	frontingDialHost := ""
  1413  	frontingHost := ""
  1414  
  1415  	if len(serverEntry.MeekFrontingAddressesRegex) > 0 {
  1416  
  1417  		// Generate a front address based on the regex.
  1418  
  1419  		var err error
  1420  		frontingDialHost, err = regen.GenerateString(serverEntry.MeekFrontingAddressesRegex)
  1421  		if err != nil {
  1422  			return "", "", errors.Trace(err)
  1423  		}
  1424  
  1425  	} else {
  1426  
  1427  		// Randomly select, for this connection attempt, one front address for
  1428  		// fronting-capable servers.
  1429  
  1430  		if len(serverEntry.MeekFrontingAddresses) == 0 {
  1431  			return "", "", errors.TraceNew("MeekFrontingAddresses is empty")
  1432  		}
  1433  
  1434  		index := prng.Intn(len(serverEntry.MeekFrontingAddresses))
  1435  		frontingDialHost = serverEntry.MeekFrontingAddresses[index]
  1436  	}
  1437  
  1438  	if len(serverEntry.MeekFrontingHosts) > 0 {
  1439  
  1440  		index := prng.Intn(len(serverEntry.MeekFrontingHosts))
  1441  		frontingHost = serverEntry.MeekFrontingHosts[index]
  1442  
  1443  	} else {
  1444  
  1445  		// Backwards compatibility case
  1446  		frontingHost = serverEntry.MeekFrontingHost
  1447  	}
  1448  
  1449  	return frontingDialHost, frontingHost, nil
  1450  }
  1451  
  1452  func selectQUICVersion(
  1453  	isFronted bool,
  1454  	serverEntry *protocol.ServerEntry,
  1455  	p parameters.ParametersAccessor) string {
  1456  
  1457  	limitQUICVersions := p.QUICVersions(parameters.LimitQUICVersions)
  1458  
  1459  	var disableQUICVersions protocol.QUICVersions
  1460  
  1461  	if isFronted {
  1462  		if serverEntry.FrontingProviderID == "" {
  1463  			// Legacy server entry case
  1464  			disableQUICVersions = protocol.QUICVersions{
  1465  				protocol.QUIC_VERSION_V1,
  1466  				protocol.QUIC_VERSION_RANDOMIZED_V1,
  1467  				protocol.QUIC_VERSION_OBFUSCATED_V1,
  1468  				protocol.QUIC_VERSION_DECOY_V1,
  1469  			}
  1470  		} else {
  1471  			disableQUICVersions = p.LabeledQUICVersions(
  1472  				parameters.DisableFrontingProviderQUICVersions,
  1473  				serverEntry.FrontingProviderID)
  1474  		}
  1475  	}
  1476  
  1477  	quicVersions := make([]string, 0)
  1478  
  1479  	// Don't use gQUIC versions when the server entry specifies QUICv1-only.
  1480  	supportedQUICVersions := protocol.SupportedQUICVersions
  1481  	if serverEntry.SupportsOnlyQUICv1() {
  1482  		supportedQUICVersions = protocol.SupportedQUICv1Versions
  1483  	}
  1484  
  1485  	for _, quicVersion := range supportedQUICVersions {
  1486  
  1487  		if len(limitQUICVersions) > 0 &&
  1488  			!common.Contains(limitQUICVersions, quicVersion) {
  1489  			continue
  1490  		}
  1491  
  1492  		// Both tactics and the server entry can specify LimitQUICVersions. In
  1493  		// tactics, the parameter is intended to direct certain clients to
  1494  		// use a successful protocol variant. In the server entry, the
  1495  		// parameter may be used to direct all clients to send
  1496  		// consistent-looking protocol variants to a particular server; e.g.,
  1497  		// only regular QUIC, or only obfuscated QUIC.
  1498  		//
  1499  		// The isFronted/QUICVersionIsObfuscated logic predates
  1500  		// ServerEntry.LimitQUICVersions; ServerEntry.LimitQUICVersions could
  1501  		// now be used to achieve a similar outcome.
  1502  		if len(serverEntry.LimitQUICVersions) > 0 &&
  1503  			!common.Contains(serverEntry.LimitQUICVersions, quicVersion) {
  1504  			continue
  1505  		}
  1506  
  1507  		if isFronted &&
  1508  			protocol.QUICVersionIsObfuscated(quicVersion) {
  1509  			continue
  1510  		}
  1511  
  1512  		if common.Contains(disableQUICVersions, quicVersion) {
  1513  			continue
  1514  		}
  1515  
  1516  		quicVersions = append(quicVersions, quicVersion)
  1517  	}
  1518  
  1519  	if len(quicVersions) == 0 {
  1520  		return ""
  1521  	}
  1522  
  1523  	choice := prng.Intn(len(quicVersions))
  1524  
  1525  	return quicVersions[choice]
  1526  }
  1527  
  1528  // selectUserAgentIfUnset selects a User-Agent header if one is not set.
  1529  func selectUserAgentIfUnset(
  1530  	p parameters.ParametersAccessor, headers http.Header) (bool, string) {
  1531  
  1532  	if _, ok := headers["User-Agent"]; !ok {
  1533  
  1534  		userAgent := ""
  1535  		if p.WeightedCoinFlip(parameters.PickUserAgentProbability) {
  1536  			userAgent = values.GetUserAgent()
  1537  		}
  1538  
  1539  		return true, userAgent
  1540  	}
  1541  
  1542  	return false, ""
  1543  }
  1544  
  1545  func makeDialCustomHeaders(
  1546  	config *Config,
  1547  	p parameters.ParametersAccessor) http.Header {
  1548  
  1549  	dialCustomHeaders := make(http.Header)
  1550  	if config.CustomHeaders != nil {
  1551  		for k, v := range config.CustomHeaders {
  1552  			dialCustomHeaders[k] = make([]string, len(v))
  1553  			copy(dialCustomHeaders[k], v)
  1554  		}
  1555  	}
  1556  
  1557  	additionalCustomHeaders := p.HTTPHeaders(parameters.AdditionalCustomHeaders)
  1558  	for k, v := range additionalCustomHeaders {
  1559  		dialCustomHeaders[k] = make([]string, len(v))
  1560  		copy(dialCustomHeaders[k], v)
  1561  	}
  1562  	return dialCustomHeaders
  1563  }
  1564  
  1565  func selectHostName(
  1566  	tunnelProtocol string, p parameters.ParametersAccessor) string {
  1567  
  1568  	limitProtocols := p.TunnelProtocols(parameters.CustomHostNameLimitProtocols)
  1569  	if len(limitProtocols) > 0 && !common.Contains(limitProtocols, tunnelProtocol) {
  1570  		return values.GetHostName()
  1571  	}
  1572  
  1573  	if !p.WeightedCoinFlip(parameters.CustomHostNameProbability) {
  1574  		return values.GetHostName()
  1575  	}
  1576  
  1577  	regexStrings := p.RegexStrings(parameters.CustomHostNameRegexes)
  1578  	if len(regexStrings) == 0 {
  1579  		return values.GetHostName()
  1580  	}
  1581  
  1582  	choice := prng.Intn(len(regexStrings))
  1583  	hostName, err := regen.GenerateString(regexStrings[choice])
  1584  	if err != nil {
  1585  		NoticeWarning("selectHostName: regen.Generate failed: %v", errors.Trace(err))
  1586  		return values.GetHostName()
  1587  	}
  1588  
  1589  	return hostName
  1590  }
  1591  
  1592  // makeHTTPTransformerParameters generates HTTPTransformerParameters using the
  1593  // input tactics parameters and optional frontingProviderID context.
  1594  func makeHTTPTransformerParameters(p parameters.ParametersAccessor,
  1595  	frontingProviderID string, isFronted bool) (*transforms.HTTPTransformerParameters, error) {
  1596  
  1597  	params := transforms.HTTPTransformerParameters{}
  1598  
  1599  	// Select an HTTP transform. If the request is fronted, HTTP request
  1600  	// transforms are "scoped" by fronting provider ID. Otherwise, a transform
  1601  	// from the default scope (transforms.SCOPE_ANY == "") is selected.
  1602  
  1603  	var specsKey string
  1604  	var scopedSpecsNamesKey string
  1605  
  1606  	useTransform := false
  1607  	scope := transforms.SCOPE_ANY
  1608  
  1609  	if isFronted {
  1610  		if p.WeightedCoinFlip(parameters.FrontedHTTPProtocolTransformProbability) {
  1611  			useTransform = true
  1612  			scope = frontingProviderID
  1613  			specsKey = parameters.FrontedHTTPProtocolTransformSpecs
  1614  			scopedSpecsNamesKey = parameters.FrontedHTTPProtocolTransformScopedSpecNames
  1615  		}
  1616  	} else {
  1617  		// unfronted
  1618  		if p.WeightedCoinFlip(parameters.DirectHTTPProtocolTransformProbability) {
  1619  			useTransform = true
  1620  			specsKey = parameters.DirectHTTPProtocolTransformSpecs
  1621  			scopedSpecsNamesKey = parameters.DirectHTTPProtocolTransformScopedSpecNames
  1622  		}
  1623  	}
  1624  
  1625  	if useTransform {
  1626  
  1627  		specs := p.ProtocolTransformSpecs(
  1628  			specsKey)
  1629  		scopedSpecNames := p.ProtocolTransformScopedSpecNames(
  1630  			scopedSpecsNamesKey)
  1631  
  1632  		name, spec := specs.Select(scope, scopedSpecNames)
  1633  
  1634  		if spec != nil {
  1635  			params.ProtocolTransformName = name
  1636  			params.ProtocolTransformSpec = spec
  1637  			var err error
  1638  			// transform seed generated
  1639  			params.ProtocolTransformSeed, err = prng.NewSeed()
  1640  			if err != nil {
  1641  				return nil, errors.Trace(err)
  1642  			}
  1643  		}
  1644  	}
  1645  
  1646  	return &params, nil
  1647  }
  1648  
  1649  // makeSeedTransformerParameters generates ObfuscatorSeedTransformerParameters
  1650  // using the input tactics parameters.
  1651  func makeSeedTransformerParameters(p parameters.ParametersAccessor,
  1652  	probabilityFieldName, specsKey, scopedSpecsKey string) (*transforms.ObfuscatorSeedTransformerParameters, error) {
  1653  
  1654  	if !p.WeightedCoinFlip(probabilityFieldName) {
  1655  		return &transforms.ObfuscatorSeedTransformerParameters{}, nil
  1656  	}
  1657  
  1658  	seed, err := prng.NewSeed()
  1659  	if err != nil {
  1660  		return nil, errors.Trace(err)
  1661  	}
  1662  
  1663  	specs := p.ProtocolTransformSpecs(specsKey)
  1664  	scopedSpecNames := p.ProtocolTransformScopedSpecNames(scopedSpecsKey)
  1665  
  1666  	name, spec := specs.Select(transforms.SCOPE_ANY, scopedSpecNames)
  1667  
  1668  	if spec == nil {
  1669  		return &transforms.ObfuscatorSeedTransformerParameters{}, nil
  1670  	} else {
  1671  		return &transforms.ObfuscatorSeedTransformerParameters{
  1672  			TransformName: name,
  1673  			TransformSpec: spec,
  1674  			TransformSeed: seed,
  1675  		}, nil
  1676  	}
  1677  }
  1678  
  1679  func makeOSSHPrefixSpecParameters(
  1680  	p parameters.ParametersAccessor,
  1681  	dialPortNumber string) (*obfuscator.OSSHPrefixSpec, error) {
  1682  
  1683  	if !p.WeightedCoinFlip(parameters.OSSHPrefixProbability) {
  1684  		return &obfuscator.OSSHPrefixSpec{}, nil
  1685  	}
  1686  
  1687  	specs := p.ProtocolTransformSpecs(parameters.OSSHPrefixSpecs)
  1688  	scopedSpecNames := p.ProtocolTransformScopedSpecNames(parameters.OSSHPrefixScopedSpecNames)
  1689  
  1690  	name, spec := specs.Select(dialPortNumber, scopedSpecNames)
  1691  
  1692  	if spec == nil {
  1693  		return &obfuscator.OSSHPrefixSpec{}, nil
  1694  	} else {
  1695  		seed, err := prng.NewSeed()
  1696  		if err != nil {
  1697  			return nil, errors.Trace(err)
  1698  		}
  1699  		return &obfuscator.OSSHPrefixSpec{
  1700  			Name: name,
  1701  			Spec: spec,
  1702  			Seed: seed,
  1703  		}, nil
  1704  	}
  1705  }
  1706  
  1707  func makeOSSHPrefixSplitConfig(p parameters.ParametersAccessor) (*obfuscator.OSSHPrefixSplitConfig, error) {
  1708  
  1709  	minDelay := p.Duration(parameters.OSSHPrefixSplitMinDelay)
  1710  	maxDelay := p.Duration(parameters.OSSHPrefixSplitMaxDelay)
  1711  
  1712  	seed, err := prng.NewSeed()
  1713  	if err != nil {
  1714  		return nil, errors.Trace(err)
  1715  	}
  1716  
  1717  	return &obfuscator.OSSHPrefixSplitConfig{
  1718  		Seed:     seed,
  1719  		MinDelay: minDelay,
  1720  		MaxDelay: maxDelay,
  1721  	}, nil
  1722  }