github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/controller_test.go (about) 1 /* 2 * Copyright (c) 2015, Psiphon Inc. 3 * All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package psiphon 21 22 import ( 23 "context" 24 "encoding/json" 25 "flag" 26 "fmt" 27 "io" 28 "io/ioutil" 29 "log" 30 "net" 31 "net/http" 32 "net/url" 33 "os" 34 "strings" 35 "sync" 36 "sync/atomic" 37 "testing" 38 "time" 39 40 socks "github.com/Psiphon-Labs/goptlib" 41 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common" 42 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol" 43 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/quic" 44 "github.com/elazarl/goproxy" 45 "github.com/elazarl/goproxy/ext/auth" 46 ) 47 48 const testClientPlatform = "test_github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon" 49 50 func TestMain(m *testing.M) { 51 flag.Parse() 52 53 SetEmitDiagnosticNotices(true, true) 54 55 initDisruptor() 56 initUpstreamProxy() 57 58 os.Exit(m.Run()) 59 } 60 61 // Test case notes/limitations/dependencies: 62 // 63 // * Untunneled upgrade tests must execute before 64 // the other tests to ensure no tunnel is established. 65 // We need a way to reset the datastore after it's been 66 // initialized in order to to clear out its data entries 67 // and be able to arbitrarily order the tests. 68 // 69 // * The resumable download tests using disruptNetwork 70 // depend on the download object being larger than the 71 // disruptorMax limits so that the disruptor will actually 72 // interrupt the first download attempt. Specifically, the 73 // upgrade and remote server list at the URLs specified in 74 // controller_test.config.enc. 75 // 76 // * The protocol tests assume there is at least one server 77 // supporting each protocol in the server list at the URL 78 // specified in controller_test.config.enc, and that these 79 // servers are not overloaded. 80 // 81 // * fetchAndVerifyWebsite depends on the target URL being 82 // available and responding. 83 // 84 85 func TestUntunneledUpgradeDownload(t *testing.T) { 86 controllerRun(t, 87 &controllerRunConfig{ 88 expectNoServerEntries: true, 89 protocol: "", 90 clientIsLatestVersion: false, 91 disableUntunneledUpgrade: false, 92 disableEstablishing: true, 93 disableApi: false, 94 tunnelPoolSize: 1, 95 useUpstreamProxy: false, 96 disruptNetwork: false, 97 transformHostNames: false, 98 useFragmentor: false, 99 }) 100 } 101 102 func TestUntunneledResumableUpgradeDownload(t *testing.T) { 103 controllerRun(t, 104 &controllerRunConfig{ 105 expectNoServerEntries: true, 106 protocol: "", 107 clientIsLatestVersion: false, 108 disableUntunneledUpgrade: false, 109 disableEstablishing: true, 110 disableApi: false, 111 tunnelPoolSize: 1, 112 useUpstreamProxy: false, 113 disruptNetwork: true, 114 transformHostNames: false, 115 useFragmentor: false, 116 }) 117 } 118 119 func TestUntunneledUpgradeClientIsLatestVersion(t *testing.T) { 120 controllerRun(t, 121 &controllerRunConfig{ 122 expectNoServerEntries: true, 123 protocol: "", 124 clientIsLatestVersion: true, 125 disableUntunneledUpgrade: false, 126 disableEstablishing: true, 127 disableApi: false, 128 tunnelPoolSize: 1, 129 useUpstreamProxy: false, 130 disruptNetwork: false, 131 transformHostNames: false, 132 useFragmentor: false, 133 }) 134 } 135 136 func TestUntunneledResumableFetchRemoteServerList(t *testing.T) { 137 controllerRun(t, 138 &controllerRunConfig{ 139 expectNoServerEntries: true, 140 protocol: "", 141 clientIsLatestVersion: true, 142 disableUntunneledUpgrade: false, 143 disableEstablishing: false, 144 disableApi: false, 145 tunnelPoolSize: 1, 146 useUpstreamProxy: false, 147 disruptNetwork: true, 148 transformHostNames: false, 149 useFragmentor: false, 150 }) 151 } 152 153 func TestTunneledUpgradeClientIsLatestVersion(t *testing.T) { 154 controllerRun(t, 155 &controllerRunConfig{ 156 expectNoServerEntries: false, 157 protocol: "", 158 clientIsLatestVersion: true, 159 disableUntunneledUpgrade: true, 160 disableEstablishing: false, 161 disableApi: false, 162 tunnelPoolSize: 1, 163 useUpstreamProxy: false, 164 disruptNetwork: false, 165 transformHostNames: false, 166 useFragmentor: false, 167 }) 168 } 169 170 func TestSSH(t *testing.T) { 171 controllerRun(t, 172 &controllerRunConfig{ 173 expectNoServerEntries: false, 174 protocol: protocol.TUNNEL_PROTOCOL_SSH, 175 clientIsLatestVersion: false, 176 disableUntunneledUpgrade: true, 177 disableEstablishing: false, 178 disableApi: false, 179 tunnelPoolSize: 1, 180 useUpstreamProxy: false, 181 disruptNetwork: false, 182 transformHostNames: false, 183 useFragmentor: false, 184 }) 185 } 186 187 func TestObfuscatedSSH(t *testing.T) { 188 controllerRun(t, 189 &controllerRunConfig{ 190 expectNoServerEntries: false, 191 protocol: protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH, 192 clientIsLatestVersion: false, 193 disableUntunneledUpgrade: true, 194 disableEstablishing: false, 195 disableApi: false, 196 tunnelPoolSize: 1, 197 useUpstreamProxy: false, 198 disruptNetwork: false, 199 transformHostNames: false, 200 useFragmentor: false, 201 }) 202 } 203 204 func TestUnfrontedMeek(t *testing.T) { 205 controllerRun(t, 206 &controllerRunConfig{ 207 expectNoServerEntries: false, 208 protocol: protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK, 209 clientIsLatestVersion: false, 210 disableUntunneledUpgrade: true, 211 disableEstablishing: false, 212 disableApi: false, 213 tunnelPoolSize: 1, 214 useUpstreamProxy: false, 215 disruptNetwork: false, 216 transformHostNames: false, 217 useFragmentor: false, 218 }) 219 } 220 221 func TestUnfrontedMeekWithTransformer(t *testing.T) { 222 controllerRun(t, 223 &controllerRunConfig{ 224 expectNoServerEntries: false, 225 protocol: protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK, 226 clientIsLatestVersion: true, 227 disableUntunneledUpgrade: true, 228 disableEstablishing: false, 229 disableApi: false, 230 tunnelPoolSize: 1, 231 useUpstreamProxy: false, 232 disruptNetwork: false, 233 transformHostNames: true, 234 useFragmentor: false, 235 }) 236 } 237 238 func TestFrontedMeek(t *testing.T) { 239 controllerRun(t, 240 &controllerRunConfig{ 241 expectNoServerEntries: false, 242 protocol: protocol.TUNNEL_PROTOCOL_FRONTED_MEEK, 243 clientIsLatestVersion: false, 244 disableUntunneledUpgrade: true, 245 disableEstablishing: false, 246 disableApi: false, 247 tunnelPoolSize: 1, 248 useUpstreamProxy: false, 249 disruptNetwork: false, 250 transformHostNames: false, 251 useFragmentor: false, 252 }) 253 } 254 255 func TestFrontedMeekWithTransformer(t *testing.T) { 256 controllerRun(t, 257 &controllerRunConfig{ 258 expectNoServerEntries: false, 259 protocol: protocol.TUNNEL_PROTOCOL_FRONTED_MEEK, 260 clientIsLatestVersion: true, 261 disableUntunneledUpgrade: true, 262 disableEstablishing: false, 263 disableApi: false, 264 tunnelPoolSize: 1, 265 useUpstreamProxy: false, 266 disruptNetwork: false, 267 transformHostNames: true, 268 useFragmentor: false, 269 }) 270 } 271 272 func TestFrontedMeekHTTP(t *testing.T) { 273 controllerRun(t, 274 &controllerRunConfig{ 275 expectNoServerEntries: false, 276 protocol: protocol.TUNNEL_PROTOCOL_FRONTED_MEEK_HTTP, 277 clientIsLatestVersion: true, 278 disableUntunneledUpgrade: true, 279 disableEstablishing: false, 280 disableApi: false, 281 tunnelPoolSize: 1, 282 useUpstreamProxy: false, 283 disruptNetwork: false, 284 transformHostNames: false, 285 useFragmentor: false, 286 }) 287 } 288 289 func TestUnfrontedMeekHTTPS(t *testing.T) { 290 controllerRun(t, 291 &controllerRunConfig{ 292 expectNoServerEntries: false, 293 protocol: protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS, 294 clientIsLatestVersion: false, 295 disableUntunneledUpgrade: true, 296 disableEstablishing: false, 297 disableApi: false, 298 tunnelPoolSize: 1, 299 useUpstreamProxy: false, 300 disruptNetwork: false, 301 transformHostNames: false, 302 useFragmentor: false, 303 }) 304 } 305 306 func TestUnfrontedMeekHTTPSWithTransformer(t *testing.T) { 307 controllerRun(t, 308 &controllerRunConfig{ 309 expectNoServerEntries: false, 310 protocol: protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS, 311 clientIsLatestVersion: true, 312 disableUntunneledUpgrade: true, 313 disableEstablishing: false, 314 disableApi: false, 315 tunnelPoolSize: 1, 316 useUpstreamProxy: false, 317 disruptNetwork: false, 318 transformHostNames: true, 319 useFragmentor: false, 320 }) 321 } 322 323 func TestDisabledApi(t *testing.T) { 324 controllerRun(t, 325 &controllerRunConfig{ 326 expectNoServerEntries: false, 327 protocol: "", 328 clientIsLatestVersion: true, 329 disableUntunneledUpgrade: true, 330 disableEstablishing: false, 331 disableApi: true, 332 tunnelPoolSize: 1, 333 useUpstreamProxy: false, 334 disruptNetwork: false, 335 transformHostNames: false, 336 useFragmentor: false, 337 }) 338 } 339 340 func TestObfuscatedSSHWithUpstreamProxy(t *testing.T) { 341 controllerRun(t, 342 &controllerRunConfig{ 343 expectNoServerEntries: false, 344 protocol: protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH, 345 clientIsLatestVersion: false, 346 disableUntunneledUpgrade: true, 347 disableEstablishing: false, 348 disableApi: false, 349 tunnelPoolSize: 1, 350 useUpstreamProxy: true, 351 disruptNetwork: false, 352 transformHostNames: false, 353 useFragmentor: false, 354 }) 355 } 356 357 func TestUnfrontedMeekWithUpstreamProxy(t *testing.T) { 358 controllerRun(t, 359 &controllerRunConfig{ 360 expectNoServerEntries: false, 361 protocol: protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK, 362 clientIsLatestVersion: false, 363 disableUntunneledUpgrade: true, 364 disableEstablishing: false, 365 disableApi: false, 366 tunnelPoolSize: 1, 367 useUpstreamProxy: true, 368 disruptNetwork: false, 369 transformHostNames: false, 370 useFragmentor: false, 371 }) 372 } 373 374 func TestUnfrontedMeekHTTPSWithUpstreamProxy(t *testing.T) { 375 controllerRun(t, 376 &controllerRunConfig{ 377 expectNoServerEntries: false, 378 protocol: protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS, 379 clientIsLatestVersion: false, 380 disableUntunneledUpgrade: true, 381 disableEstablishing: false, 382 disableApi: false, 383 tunnelPoolSize: 1, 384 useUpstreamProxy: true, 385 disruptNetwork: false, 386 transformHostNames: false, 387 useFragmentor: false, 388 }) 389 } 390 391 func TestObfuscatedSSHFragmentor(t *testing.T) { 392 controllerRun(t, 393 &controllerRunConfig{ 394 expectNoServerEntries: false, 395 protocol: protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH, 396 clientIsLatestVersion: false, 397 disableUntunneledUpgrade: true, 398 disableEstablishing: false, 399 disableApi: false, 400 tunnelPoolSize: 1, 401 useUpstreamProxy: false, 402 disruptNetwork: false, 403 transformHostNames: false, 404 useFragmentor: true, 405 }) 406 } 407 408 func TestFrontedMeekFragmentor(t *testing.T) { 409 controllerRun(t, 410 &controllerRunConfig{ 411 expectNoServerEntries: false, 412 protocol: protocol.TUNNEL_PROTOCOL_FRONTED_MEEK, 413 clientIsLatestVersion: false, 414 disableUntunneledUpgrade: true, 415 disableEstablishing: false, 416 disableApi: false, 417 tunnelPoolSize: 1, 418 useUpstreamProxy: false, 419 disruptNetwork: false, 420 transformHostNames: false, 421 useFragmentor: true, 422 }) 423 } 424 425 func TestQUIC(t *testing.T) { 426 if !quic.Enabled() { 427 t.Skip("QUIC is not enabled") 428 } 429 controllerRun(t, 430 &controllerRunConfig{ 431 expectNoServerEntries: false, 432 protocol: protocol.TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH, 433 clientIsLatestVersion: false, 434 disableUntunneledUpgrade: true, 435 disableEstablishing: false, 436 disableApi: false, 437 tunnelPoolSize: 1, 438 useUpstreamProxy: false, 439 disruptNetwork: false, 440 transformHostNames: false, 441 useFragmentor: false, 442 }) 443 } 444 445 func TestFrontedQUIC(t *testing.T) { 446 447 t.Skipf("temporarily disabled") 448 449 if !quic.Enabled() { 450 t.Skip("QUIC is not enabled") 451 } 452 controllerRun(t, 453 &controllerRunConfig{ 454 expectNoServerEntries: false, 455 protocol: protocol.TUNNEL_PROTOCOL_FRONTED_MEEK_QUIC_OBFUSCATED_SSH, 456 clientIsLatestVersion: false, 457 disableUntunneledUpgrade: true, 458 disableEstablishing: false, 459 disableApi: false, 460 tunnelPoolSize: 1, 461 useUpstreamProxy: false, 462 disruptNetwork: false, 463 transformHostNames: false, 464 useFragmentor: false, 465 }) 466 } 467 468 func TestTunnelPool(t *testing.T) { 469 controllerRun(t, 470 &controllerRunConfig{ 471 expectNoServerEntries: false, 472 protocol: protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH, 473 clientIsLatestVersion: false, 474 disableUntunneledUpgrade: true, 475 disableEstablishing: false, 476 disableApi: false, 477 tunnelPoolSize: 2, 478 useUpstreamProxy: false, 479 disruptNetwork: false, 480 transformHostNames: false, 481 useFragmentor: false, 482 }) 483 } 484 485 type controllerRunConfig struct { 486 expectNoServerEntries bool 487 protocol string 488 clientIsLatestVersion bool 489 disableUntunneledUpgrade bool 490 disableEstablishing bool 491 disableApi bool 492 tunnelPoolSize int 493 useUpstreamProxy bool 494 disruptNetwork bool 495 transformHostNames bool 496 useFragmentor bool 497 } 498 499 func controllerRun(t *testing.T, runConfig *controllerRunConfig) { 500 501 testDataDirName, err := ioutil.TempDir("", "psiphon-controller-test") 502 if err != nil { 503 t.Fatalf("TempDir failed: %s\n", err) 504 } 505 defer os.RemoveAll(testDataDirName) 506 507 configJSON, err := ioutil.ReadFile("controller_test.config") 508 if err != nil { 509 // Skip, don't fail, if config file is not present 510 t.Skipf("error loading configuration file: %s", err) 511 } 512 513 // Note: a successful tactics request may modify config parameters. 514 515 var modifyConfig map[string]interface{} 516 json.Unmarshal(configJSON, &modifyConfig) 517 518 modifyConfig["DataRootDirectory"] = testDataDirName 519 520 if runConfig.protocol != "" { 521 modifyConfig["LimitTunnelProtocols"] = protocol.TunnelProtocols{runConfig.protocol} 522 } 523 524 // Override client retry throttle values to speed up automated 525 // tests and ensure tests complete within fixed deadlines. 526 modifyConfig["FetchRemoteServerListRetryPeriodMilliseconds"] = 250 527 modifyConfig["FetchUpgradeRetryPeriodMilliseconds"] = 250 528 modifyConfig["EstablishTunnelPausePeriodSeconds"] = 1 529 530 if runConfig.disableUntunneledUpgrade { 531 // Disable untunneled upgrade downloader to ensure tunneled case is tested 532 modifyConfig["UpgradeDownloadClientVersionHeader"] = "invalid-value" 533 } 534 535 if runConfig.transformHostNames { 536 modifyConfig["TransformHostNames"] = "always" 537 } else { 538 modifyConfig["TransformHostNames"] = "never" 539 } 540 541 if runConfig.useFragmentor { 542 modifyConfig["UseFragmentor"] = "always" 543 modifyConfig["FragmentorLimitProtocols"] = protocol.TunnelProtocols{runConfig.protocol} 544 modifyConfig["FragmentorMinTotalBytes"] = 1000 545 modifyConfig["FragmentorMaxTotalBytes"] = 2000 546 modifyConfig["FragmentorMinWriteBytes"] = 1 547 modifyConfig["FragmentorMaxWriteBytes"] = 100 548 modifyConfig["FragmentorMinDelayMicroseconds"] = 1000 549 modifyConfig["FragmentorMaxDelayMicroseconds"] = 10000 550 modifyConfig["ObfuscatedSSHMinPadding"] = 4096 551 modifyConfig["ObfuscatedSSHMaxPadding"] = 8192 552 } 553 554 configJSON, _ = json.Marshal(modifyConfig) 555 556 config, err := LoadConfig(configJSON) 557 if err != nil { 558 t.Fatalf("error processing configuration file: %s", err) 559 } 560 561 if config.ClientPlatform == "" { 562 config.ClientPlatform = testClientPlatform 563 } 564 565 if runConfig.clientIsLatestVersion { 566 config.ClientVersion = "999999999" 567 } 568 569 if runConfig.disableEstablishing { 570 // Clear remote server list so tunnel cannot be established. 571 // TODO: also delete all server entries in the datastore. 572 config.DisableRemoteServerListFetcher = true 573 } 574 575 if runConfig.disableApi { 576 config.DisableApi = true 577 } 578 579 config.TunnelPoolSize = runConfig.tunnelPoolSize 580 581 if runConfig.useUpstreamProxy && runConfig.disruptNetwork { 582 t.Fatalf("cannot use multiple upstream proxies") 583 } 584 if runConfig.disruptNetwork { 585 config.UpstreamProxyURL = disruptorProxyURL 586 } else if runConfig.useUpstreamProxy { 587 config.UpstreamProxyURL = upstreamProxyURL 588 config.CustomHeaders = upstreamProxyCustomHeaders 589 } 590 591 // All config fields should be set before calling Commit. 592 err = config.Commit(false) 593 if err != nil { 594 t.Fatalf("error committing configuration file: %s", err) 595 } 596 597 err = OpenDataStore(config) 598 if err != nil { 599 t.Fatalf("error initializing datastore: %s", err) 600 } 601 defer CloseDataStore() 602 603 serverEntryCount := CountServerEntries() 604 605 if runConfig.expectNoServerEntries && serverEntryCount > 0 { 606 // TODO: replace expectNoServerEntries with resetServerEntries 607 // so tests can run in arbitrary order 608 t.Fatalf("unexpected server entries") 609 } 610 611 controller, err := NewController(config) 612 if err != nil { 613 t.Fatalf("error creating controller: %s", err) 614 } 615 616 // Monitor notices for "Tunnels" with count > 1, the 617 // indication of tunnel establishment success. 618 // Also record the selected HTTP proxy port to use 619 // when fetching websites through the tunnel. 620 621 httpProxyPort := 0 622 623 tunnelEstablished := make(chan struct{}, 1) 624 upgradeDownloaded := make(chan struct{}, 1) 625 remoteServerListDownloaded := make(chan struct{}, 1) 626 confirmedLatestVersion := make(chan struct{}, 1) 627 candidateServers := make(chan struct{}, 1) 628 availableEgressRegions := make(chan struct{}, 1) 629 630 var clientUpgradeDownloadedBytesCount int32 631 var remoteServerListDownloadedBytesCount int32 632 633 SetNoticeWriter(NewNoticeReceiver( 634 func(notice []byte) { 635 // TODO: log notices without logging server IPs: 636 //fmt.Fprintf(os.Stderr, "%s\n", string(notice)) 637 noticeType, payload, err := GetNotice(notice) 638 if err != nil { 639 return 640 } 641 switch noticeType { 642 643 case "ListeningHttpProxyPort": 644 645 httpProxyPort = int(payload["port"].(float64)) 646 647 case "ConnectingServer": 648 649 serverProtocol := payload["protocol"].(string) 650 651 if runConfig.protocol != "" && serverProtocol != runConfig.protocol { 652 // TODO: wrong goroutine for t.FatalNow() 653 t.Fatalf("wrong protocol selected: %s", serverProtocol) 654 } 655 656 case "Tunnels": 657 658 count := int(payload["count"].(float64)) 659 if count > 0 { 660 if runConfig.disableEstablishing { 661 // TODO: wrong goroutine for t.FatalNow() 662 t.Fatalf("tunnel established unexpectedly") 663 } else { 664 select { 665 case tunnelEstablished <- struct{}{}: 666 default: 667 } 668 } 669 } 670 671 case "ClientUpgradeDownloadedBytes": 672 673 atomic.AddInt32(&clientUpgradeDownloadedBytesCount, 1) 674 t.Logf("ClientUpgradeDownloadedBytes: %d", int(payload["bytes"].(float64))) 675 676 case "ClientUpgradeDownloaded": 677 678 select { 679 case upgradeDownloaded <- struct{}{}: 680 default: 681 } 682 683 case "ClientIsLatestVersion": 684 685 select { 686 case confirmedLatestVersion <- struct{}{}: 687 default: 688 } 689 690 case "RemoteServerListResourceDownloadedBytes": 691 692 url := payload["url"].(string) 693 if url == config.RemoteServerListUrl { 694 t.Logf("RemoteServerListResourceDownloadedBytes: %d", int(payload["bytes"].(float64))) 695 atomic.AddInt32(&remoteServerListDownloadedBytesCount, 1) 696 } 697 698 case "RemoteServerListResourceDownloaded": 699 700 url := payload["url"].(string) 701 if url == config.RemoteServerListUrl { 702 t.Logf("RemoteServerListResourceDownloaded") 703 select { 704 case remoteServerListDownloaded <- struct{}{}: 705 default: 706 } 707 } 708 709 case "CandidateServers": 710 711 select { 712 case candidateServers <- struct{}{}: 713 default: 714 } 715 716 case "AvailableEgressRegions": 717 718 select { 719 case availableEgressRegions <- struct{}{}: 720 default: 721 } 722 } 723 })) 724 725 // Run controller, which establishes tunnels 726 727 ctx, cancelFunc := context.WithCancel(context.Background()) 728 729 controllerWaitGroup := new(sync.WaitGroup) 730 731 controllerWaitGroup.Add(1) 732 go func() { 733 defer controllerWaitGroup.Done() 734 controller.Run(ctx) 735 }() 736 737 defer func() { 738 739 // Test: shutdown must complete within 20 seconds 740 741 cancelFunc() 742 743 shutdownTimeout := time.NewTimer(20 * time.Second) 744 745 shutdownOk := make(chan struct{}, 1) 746 go func() { 747 controllerWaitGroup.Wait() 748 shutdownOk <- struct{}{} 749 }() 750 751 select { 752 case <-shutdownOk: 753 case <-shutdownTimeout.C: 754 t.Fatalf("controller shutdown timeout exceeded") 755 } 756 }() 757 758 if !runConfig.disableEstablishing { 759 760 // Test: tunnel must be established within 120 seconds 761 762 establishTimeout := time.NewTimer(120 * time.Second) 763 764 select { 765 case <-tunnelEstablished: 766 case <-establishTimeout.C: 767 t.Fatalf("tunnel establish timeout exceeded") 768 } 769 770 // Test: asynchronous server entry scans must complete 771 772 select { 773 case <-candidateServers: 774 case <-establishTimeout.C: 775 t.Fatalf("missing candidate servers notice") 776 } 777 778 select { 779 case <-availableEgressRegions: 780 case <-establishTimeout.C: 781 t.Fatalf("missing available egress regions notice") 782 } 783 784 // Test: if starting with no server entries, a fetch remote 785 // server list must have succeeded. With disruptNetwork, the 786 // fetch must have been resumed at least once. 787 788 if serverEntryCount == 0 { 789 select { 790 case <-remoteServerListDownloaded: 791 default: 792 t.Fatalf("expected remote server list downloaded") 793 } 794 795 if runConfig.disruptNetwork { 796 count := atomic.LoadInt32(&remoteServerListDownloadedBytesCount) 797 if count <= 1 { 798 t.Fatalf("unexpected remote server list download progress: %d", count) 799 } 800 } 801 } 802 803 // Cannot establish port forwards in DisableApi mode 804 if !runConfig.disableApi { 805 806 // Test: fetch website through tunnel 807 808 // Allow for known race condition described in NewHttpProxy(): 809 time.Sleep(1 * time.Second) 810 811 if !runConfig.disruptNetwork { 812 fetchAndVerifyWebsite(t, httpProxyPort) 813 } 814 } 815 } 816 817 // Test: upgrade check/download must be downloaded within 180 seconds 818 819 expectUpgrade := !runConfig.disableApi && !runConfig.disableUntunneledUpgrade 820 821 if expectUpgrade { 822 upgradeTimeout := time.NewTimer(120 * time.Second) 823 824 select { 825 case <-upgradeDownloaded: 826 // TODO: verify downloaded file 827 if runConfig.clientIsLatestVersion { 828 t.Fatalf("upgrade downloaded unexpectedly") 829 } 830 831 // Test: with disruptNetwork, must be multiple download progress notices 832 833 if runConfig.disruptNetwork { 834 count := atomic.LoadInt32(&clientUpgradeDownloadedBytesCount) 835 if count <= 1 { 836 t.Fatalf("unexpected upgrade download progress: %d", count) 837 } 838 } 839 840 case <-confirmedLatestVersion: 841 if !runConfig.clientIsLatestVersion { 842 t.Fatalf("confirmed latest version unexpectedly") 843 } 844 845 case <-upgradeTimeout.C: 846 t.Fatalf("upgrade download timeout exceeded") 847 } 848 } 849 } 850 851 func fetchAndVerifyWebsite(t *testing.T, httpProxyPort int) error { 852 853 testUrl := "https://psiphon.ca" 854 roundTripTimeout := 30 * time.Second 855 expectedResponseContains := "Psiphon" 856 checkResponse := func(responseBody string) bool { 857 return strings.Contains(responseBody, expectedResponseContains) 858 } 859 860 // Retries are made to compensate for intermittent failures due 861 // to external network conditions. 862 fetchWithRetries := func(fetchName string, fetchFunc func() error) error { 863 retryCount := 6 864 retryDelay := 5 * time.Second 865 var err error 866 for i := 0; i < retryCount; i++ { 867 err = fetchFunc() 868 if err == nil || i == retryCount-1 { 869 break 870 } 871 time.Sleep(retryDelay) 872 t.Logf("retrying %s...", fetchName) 873 } 874 return err 875 } 876 877 // Test: use HTTP proxy 878 879 fetchUsingHTTPProxy := func() error { 880 881 proxyUrl, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", httpProxyPort)) 882 if err != nil { 883 return fmt.Errorf("error initializing proxied HTTP request: %s", err) 884 } 885 886 httpTransport := &http.Transport{ 887 Proxy: http.ProxyURL(proxyUrl), 888 DisableKeepAlives: true, 889 } 890 891 httpClient := &http.Client{ 892 Transport: httpTransport, 893 Timeout: roundTripTimeout, 894 } 895 896 request, err := http.NewRequest("GET", testUrl, nil) 897 if err != nil { 898 return fmt.Errorf("error preparing proxied HTTP request: %s", err) 899 } 900 901 response, err := httpClient.Do(request) 902 if err != nil { 903 return fmt.Errorf("error sending proxied HTTP request: %s", err) 904 } 905 defer response.Body.Close() 906 907 body, err := ioutil.ReadAll(response.Body) 908 if err != nil { 909 return fmt.Errorf("error reading proxied HTTP response: %s", err) 910 } 911 912 if !checkResponse(string(body)) { 913 return fmt.Errorf("unexpected proxied HTTP response") 914 } 915 916 return nil 917 } 918 919 err := fetchWithRetries("proxied HTTP request", fetchUsingHTTPProxy) 920 if err != nil { 921 return err 922 } 923 924 // Delay before requesting from external service again 925 time.Sleep(1 * time.Second) 926 927 // Test: use direct URL proxy 928 929 fetchUsingURLProxyDirect := func() error { 930 931 httpTransport := &http.Transport{ 932 DisableKeepAlives: true, 933 } 934 935 httpClient := &http.Client{ 936 Transport: httpTransport, 937 Timeout: roundTripTimeout, 938 } 939 940 request, err := http.NewRequest( 941 "GET", 942 fmt.Sprintf("http://127.0.0.1:%d/direct/%s", 943 httpProxyPort, url.QueryEscape(testUrl)), 944 nil) 945 if err != nil { 946 return fmt.Errorf("error preparing direct URL request: %s", err) 947 } 948 949 response, err := httpClient.Do(request) 950 if err != nil { 951 return fmt.Errorf("error sending direct URL request: %s", err) 952 } 953 defer response.Body.Close() 954 955 body, err := ioutil.ReadAll(response.Body) 956 if err != nil { 957 return fmt.Errorf("error reading direct URL response: %s", err) 958 } 959 960 if !checkResponse(string(body)) { 961 return fmt.Errorf("unexpected direct URL response") 962 } 963 964 return nil 965 } 966 967 err = fetchWithRetries("direct URL request", fetchUsingURLProxyDirect) 968 if err != nil { 969 return err 970 } 971 972 // Delay before requesting from external service again 973 time.Sleep(1 * time.Second) 974 975 // Test: use tunneled URL proxy 976 977 fetchUsingURLProxyTunneled := func() error { 978 979 httpTransport := &http.Transport{ 980 DisableKeepAlives: true, 981 } 982 983 httpClient := &http.Client{ 984 Transport: httpTransport, 985 Timeout: roundTripTimeout, 986 } 987 988 request, err := http.NewRequest( 989 "GET", 990 fmt.Sprintf("http://127.0.0.1:%d/tunneled/%s", 991 httpProxyPort, url.QueryEscape(testUrl)), 992 nil) 993 if err != nil { 994 return fmt.Errorf("error preparing tunneled URL request: %s", err) 995 } 996 997 response, err := httpClient.Do(request) 998 if err != nil { 999 return fmt.Errorf("error sending tunneled URL request: %s", err) 1000 } 1001 defer response.Body.Close() 1002 1003 body, err := ioutil.ReadAll(response.Body) 1004 if err != nil { 1005 return fmt.Errorf("error reading tunneled URL response: %s", err) 1006 } 1007 1008 if !checkResponse(string(body)) { 1009 return fmt.Errorf("unexpected tunneled URL response") 1010 } 1011 1012 return nil 1013 } 1014 1015 err = fetchWithRetries("tunneled URL request", fetchUsingURLProxyTunneled) 1016 if err != nil { 1017 return err 1018 } 1019 1020 return nil 1021 } 1022 1023 // Note: Valid values for disruptorMaxConnectionBytes depend on the production 1024 // network; for example, the size of the remote server list resource must exceed 1025 // disruptorMaxConnectionBytes or else TestUntunneledResumableFetchRemoteServerList 1026 // will fail since no retries are required. But if disruptorMaxConnectionBytes is 1027 // too small, the test will take longer to run since more retries are necessary. 1028 // 1029 // Tests such as TestUntunneledResumableFetchRemoteServerList could be rewritten to 1030 // use mock components (for example, see TestObfuscatedRemoteServerLists); however 1031 // these test in controller_test serve the dual purpose of ensuring that tunnel 1032 // core works with the production network. 1033 // 1034 // TODO: set disruptorMaxConnectionBytes (and disruptorMaxConnectionTime) dynamically, 1035 // based on current production network configuration? 1036 1037 const disruptorProxyAddress = "127.0.0.1:2160" 1038 const disruptorProxyURL = "socks4a://" + disruptorProxyAddress 1039 const disruptorMaxConnectionBytes = 150000 1040 const disruptorMaxConnectionTime = 10 * time.Second 1041 1042 func initDisruptor() { 1043 1044 go func() { 1045 listener, err := socks.ListenSocks("tcp", disruptorProxyAddress) 1046 if err != nil { 1047 fmt.Printf("disruptor proxy listen error: %s\n", err) 1048 return 1049 } 1050 for { 1051 localConn, err := listener.AcceptSocks() 1052 if err != nil { 1053 if e, ok := err.(net.Error); ok && e.Temporary() { 1054 fmt.Printf("disruptor proxy temporary accept error: %s\n", err) 1055 continue 1056 } 1057 fmt.Printf("disruptor proxy accept error: %s\n", err) 1058 return 1059 } 1060 go func() { 1061 defer localConn.Close() 1062 remoteConn, err := net.Dial("tcp", localConn.Req.Target) 1063 if err != nil { 1064 // TODO: log "err" without logging server IPs 1065 fmt.Printf("disruptor proxy dial error\n") 1066 return 1067 } 1068 defer remoteConn.Close() 1069 err = localConn.Grant(&net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 0}) 1070 if err != nil { 1071 fmt.Printf("disruptor proxy grant error: %s\n", err) 1072 return 1073 } 1074 1075 // Cut connection after disruptorMaxConnectionTime 1076 time.AfterFunc(disruptorMaxConnectionTime, func() { 1077 localConn.Close() 1078 remoteConn.Close() 1079 }) 1080 1081 // Relay connection, but only up to disruptorMaxConnectionBytes 1082 waitGroup := new(sync.WaitGroup) 1083 waitGroup.Add(1) 1084 go func() { 1085 defer waitGroup.Done() 1086 io.CopyN(localConn, remoteConn, disruptorMaxConnectionBytes) 1087 localConn.Close() 1088 remoteConn.Close() 1089 }() 1090 io.CopyN(remoteConn, localConn, disruptorMaxConnectionBytes) 1091 localConn.Close() 1092 remoteConn.Close() 1093 waitGroup.Wait() 1094 }() 1095 } 1096 }() 1097 } 1098 1099 const upstreamProxyURL = "http://testUser:testPassword@127.0.0.1:2161" 1100 1101 var upstreamProxyCustomHeaders = map[string][]string{"X-Test-Header-Name": {"test-header-value1", "test-header-value2"}} 1102 1103 func hasExpectedCustomHeaders(h http.Header) bool { 1104 for name, values := range upstreamProxyCustomHeaders { 1105 if h[name] == nil { 1106 return false 1107 } 1108 // Order may not be the same 1109 for _, value := range values { 1110 if !common.Contains(h[name], value) { 1111 return false 1112 } 1113 } 1114 } 1115 return true 1116 } 1117 1118 func initUpstreamProxy() { 1119 go func() { 1120 proxy := goproxy.NewProxyHttpServer() 1121 proxy.Logger = log.New(ioutil.Discard, "", 0) 1122 1123 auth.ProxyBasic( 1124 proxy, 1125 "testRealm", 1126 func(user, passwd string) bool { return user == "testUser" && passwd == "testPassword" }) 1127 1128 proxy.OnRequest().DoFunc( 1129 func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { 1130 if !hasExpectedCustomHeaders(r.Header) { 1131 fmt.Printf("missing expected headers: %+v\n", ctx.Req.Header) 1132 return nil, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusUnauthorized, "") 1133 } 1134 return r, nil 1135 }) 1136 1137 proxy.OnRequest().HandleConnectFunc( 1138 func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) { 1139 if !hasExpectedCustomHeaders(ctx.Req.Header) { 1140 fmt.Printf("missing expected headers: %+v\n", ctx.Req.Header) 1141 return goproxy.RejectConnect, host 1142 } 1143 return goproxy.OkConnect, host 1144 }) 1145 1146 err := http.ListenAndServe("127.0.0.1:2161", proxy) 1147 if err != nil { 1148 fmt.Printf("upstream proxy failed: %s\n", err) 1149 } 1150 }() 1151 1152 // TODO: wait until listener is active? 1153 }