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