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  }