github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/limitProtocols_test.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 "context" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common" 32 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters" 33 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol" 34 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/server" 35 ) 36 37 func TestLimitTunnelProtocols(t *testing.T) { 38 39 testDataDirName, err := ioutil.TempDir("", "psiphon-limit-tunnel-protocols-test") 40 if err != nil { 41 t.Fatalf("TempDir failed: %s", err) 42 } 43 defer os.RemoveAll(testDataDirName) 44 45 initialLimitTunnelProtocols := protocol.TunnelProtocols{"OSSH", "UNFRONTED-MEEK-HTTPS-OSSH"} 46 initialLimitTunnelProtocolsCandidateCount := 100 47 limitTunnelProtocols := protocol.TunnelProtocols{"SSH", "UNFRONTED-MEEK-OSSH"} 48 49 initialConnectingCount := 0 50 connectingCount := 0 51 52 SetNoticeWriter(NewNoticeReceiver( 53 func(notice []byte) { 54 noticeType, payload, err := GetNotice(notice) 55 if err != nil { 56 return 57 } 58 59 if noticeType == "ConnectingServer" { 60 61 connectingCount += 1 62 63 protocolField := payload["protocol"] 64 protocol := protocolField.(string) 65 66 if common.Contains(initialLimitTunnelProtocols, protocol) { 67 initialConnectingCount += 1 68 } 69 70 if common.Contains(limitTunnelProtocols, protocol) { 71 connectingCount += 1 72 } 73 74 // At the end of the InitialLimit phase, the order of 75 // ConnectingServer notices isn't strictly synchronized and 76 // it's possible for a Limit candidate ConnectingServer notice 77 // to arrive before the last InitialLimit notice. So strict 78 // checking of notice order is performed only up to 90% of 79 // InitialLimitTunnelProtocolsCandidateCount. 80 81 if initialConnectingCount <= (initialLimitTunnelProtocolsCandidateCount*9)/10 { 82 83 var expectedProtocols []string 84 if connectingCount <= initialLimitTunnelProtocolsCandidateCount { 85 expectedProtocols = initialLimitTunnelProtocols 86 } else { 87 expectedProtocols = limitTunnelProtocols 88 } 89 90 if !common.Contains(expectedProtocols, protocol) { 91 t.Fatalf("unexpected protocol: %s (%d %+v)", protocol, connectingCount, expectedProtocols) 92 } 93 } 94 } 95 })) 96 97 clientConfigJSON := ` 98 { 99 "ClientPlatform" : "Windows", 100 "ClientVersion" : "0", 101 "SponsorId" : "0", 102 "PropagationChannelId" : "0", 103 "DisableRemoteServerListFetcher" : true 104 }` 105 clientConfig, err := LoadConfig([]byte(clientConfigJSON)) 106 if err != nil { 107 t.Fatalf("error processing configuration file: %s", err) 108 } 109 110 clientConfig.DataRootDirectory = testDataDirName 111 112 err = clientConfig.Commit(false) 113 if err != nil { 114 t.Fatalf("error committing configuration file: %s", err) 115 } 116 117 applyParameters := make(map[string]interface{}) 118 119 applyParameters[parameters.ConnectionWorkerPoolSize] = initialLimitTunnelProtocolsCandidateCount / 2 120 applyParameters[parameters.LimitIntensiveConnectionWorkers] = initialLimitTunnelProtocolsCandidateCount / 4 121 applyParameters[parameters.TunnelConnectTimeout] = "1s" 122 applyParameters[parameters.EstablishTunnelPausePeriod] = "1s" 123 applyParameters[parameters.InitialLimitTunnelProtocols] = initialLimitTunnelProtocols 124 applyParameters[parameters.InitialLimitTunnelProtocolsCandidateCount] = initialLimitTunnelProtocolsCandidateCount 125 applyParameters[parameters.LimitTunnelProtocols] = limitTunnelProtocols 126 127 err = clientConfig.SetParameters("", true, applyParameters) 128 if err != nil { 129 t.Fatalf("error setting client parameters: %s", err) 130 } 131 132 err = OpenDataStore(clientConfig) 133 if err != nil { 134 t.Fatalf("error initializing client datastore: %s", err) 135 } 136 defer CloseDataStore() 137 138 if CountServerEntries() > 0 { 139 t.Fatalf("unexpected server entries") 140 } 141 142 serverEntries := make([]map[string]interface{}, len(protocol.SupportedTunnelProtocols)) 143 144 for i, tunnelProtocol := range protocol.SupportedTunnelProtocols { 145 146 _, _, _, _, encodedServerEntry, err := server.GenerateConfig( 147 &server.GenerateConfigParams{ 148 ServerIPAddress: fmt.Sprintf("0.1.0.0"), 149 EnableSSHAPIRequests: true, 150 WebServerPort: 8000, 151 TunnelProtocolPorts: map[string]int{tunnelProtocol: 4000}, 152 }) 153 if err != nil { 154 t.Fatalf("error generating server config: %s", err) 155 } 156 157 serverEntryFields, err := protocol.DecodeServerEntryFields( 158 string(encodedServerEntry), 159 common.GetCurrentTimestamp(), 160 protocol.SERVER_ENTRY_SOURCE_REMOTE) 161 if err != nil { 162 t.Fatalf("error decoding server entry: %s", err) 163 } 164 165 serverEntries[i] = serverEntryFields 166 } 167 168 for i := 0; i < 1000; i++ { 169 170 serverEntryFields := serverEntries[i%len(protocol.SupportedTunnelProtocols)] 171 172 serverEntryFields["ipAddress"] = fmt.Sprintf("0.1.%d.%d", (i>>8)&0xFF, i&0xFF) 173 174 err = StoreServerEntry(serverEntryFields, true) 175 if err != nil { 176 t.Fatalf("error storing server entry: %s", err) 177 } 178 } 179 180 controller, err := NewController(clientConfig) 181 if err != nil { 182 t.Fatalf("error creating client controller: %s", err) 183 } 184 185 ctx, cancelFunc := context.WithCancel(context.Background()) 186 187 controllerWaitGroup := new(sync.WaitGroup) 188 189 controllerWaitGroup.Add(1) 190 go func() { 191 defer controllerWaitGroup.Done() 192 controller.Run(ctx) 193 }() 194 195 time.Sleep(10 * time.Second) 196 197 cancelFunc() 198 199 controllerWaitGroup.Wait() 200 201 t.Logf("initial-connecting and connecting count: %d/%d", initialConnectingCount, connectingCount) 202 203 if initialConnectingCount != initialLimitTunnelProtocolsCandidateCount { 204 t.Fatalf("unexpected initial-connecting count") 205 } 206 207 if connectingCount < 3*initialLimitTunnelProtocolsCandidateCount { 208 t.Fatalf("unexpected connecting count") 209 } 210 }