github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/server/replay_test.go (about) 1 /* 2 * Copyright (c) 2020, 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 server 21 22 import ( 23 "context" 24 "encoding/json" 25 "fmt" 26 "io/ioutil" 27 "os" 28 "path/filepath" 29 "reflect" 30 "sync" 31 "testing" 32 "time" 33 34 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon" 35 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol" 36 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tactics" 37 ) 38 39 func TestServerFragmentorReplay(t *testing.T) { 40 runServerReplayTests(t, false) 41 } 42 43 func runServerReplayTests(t *testing.T, runPacketManipulation bool) { 44 45 // Do not use OSSH, which has a different fragmentor replay mechanism. Meek 46 // has a unique code path for passing around replay parameters and metrics. 47 testCases := protocol.TunnelProtocols{ 48 protocol.TUNNEL_PROTOCOL_SSH, 49 protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK, 50 } 51 52 for _, tunnelProtocol := range testCases { 53 t.Run(tunnelProtocol, func(t *testing.T) { 54 runServerReplayTest(t, runPacketManipulation, tunnelProtocol) 55 }) 56 } 57 } 58 59 func runServerReplayTest( 60 t *testing.T, 61 runPacketManipulation bool, 62 tunnelProtocol string) { 63 64 psiphon.SetEmitDiagnosticNotices(true, true) 65 66 // Configure tactics 67 68 tacticsConfigJSONFormat := ` 69 { 70 "RequestPublicKey" : "%s", 71 "RequestPrivateKey" : "%s", 72 "RequestObfuscatedKey" : "%s", 73 "DefaultTactics" : { 74 "TTL" : "60s", 75 "Probability" : 1.0, 76 "Parameters" : { 77 "LimitTunnelProtocols" : ["%s"], 78 "FragmentorDownstreamLimitProtocols" : ["%s"], 79 "FragmentorDownstreamProbability" : 1.0, 80 "FragmentorDownstreamMinTotalBytes" : 10, 81 "FragmentorDownstreamMaxTotalBytes" : 10, 82 "FragmentorDownstreamMinWriteBytes" : 1, 83 "FragmentorDownstreamMaxWriteBytes" : 1, 84 "FragmentorDownstreamMinDelay" : "1ms", 85 "FragmentorDownstreamMaxDelay" : "1ms", 86 "ServerPacketManipulationSpecs" : [{"Name": "test-packetman-spec", "PacketSpecs": [[]]}], 87 "ServerPacketManipulationProbability" : 1.0, 88 "ServerProtocolPacketManipulations": {"%s" : ["test-packetman-spec"]}, 89 "ServerReplayPacketManipulation" : true, 90 "ServerReplayFragmentor" : true, 91 "ServerReplayUnknownGeoIP" : true, 92 "ServerReplayTTL" : "5s", 93 "ServerReplayTargetWaitDuration" : "200ms", 94 "ServerReplayTargetTunnelDuration" : "50ms", 95 "ServerReplayTargetUpstreamBytes" : 0, 96 "ServerReplayTargetDownstreamBytes" : 0, 97 "ServerReplayFailedCountThreshold" : 1, 98 "ServerReplayFailedCountThreshold" : 1 99 } 100 } 101 } 102 ` 103 104 tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey, err := 105 tactics.GenerateKeys() 106 if err != nil { 107 t.Fatalf("error generating tactics keys: %s", err) 108 } 109 110 tacticsConfigJSON := fmt.Sprintf( 111 tacticsConfigJSONFormat, 112 tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey, 113 tunnelProtocol, tunnelProtocol, tunnelProtocol) 114 115 tacticsConfigFilename := filepath.Join(testDataDirName, "tactics_config.json") 116 117 err = ioutil.WriteFile(tacticsConfigFilename, []byte(tacticsConfigJSON), 0600) 118 if err != nil { 119 t.Fatalf("error paving tactics config file: %s", err) 120 } 121 122 // Run Psiphon server 123 124 generateConfigParams := &GenerateConfigParams{ 125 ServerIPAddress: "127.0.0.1", 126 EnableSSHAPIRequests: true, 127 WebServerPort: 8000, 128 TunnelProtocolPorts: map[string]int{tunnelProtocol: 4000}, 129 } 130 131 serverConfigJSON, _, _, _, encodedServerEntry, err := GenerateConfig(generateConfigParams) 132 if err != nil { 133 t.Fatalf("error generating server config: %s", err) 134 } 135 136 var serverConfig map[string]interface{} 137 json.Unmarshal(serverConfigJSON, &serverConfig) 138 139 serverConfig["LogFilename"] = filepath.Join(testDataDirName, "psiphond.log") 140 serverConfig["LogLevel"] = "debug" 141 serverConfig["TacticsConfigFilename"] = tacticsConfigFilename 142 143 // Ensure server_tunnels emit quickly. 144 serverConfig["MeekMaxSessionStalenessMilliseconds"] = 500 145 146 if runPacketManipulation { 147 serverConfig["RunPacketManipulator"] = true 148 } 149 150 serverConfigJSON, _ = json.Marshal(serverConfig) 151 152 serverTunnelLog := make(chan map[string]interface{}, 1) 153 154 setLogCallback(func(log []byte) { 155 logFields := make(map[string]interface{}) 156 err := json.Unmarshal(log, &logFields) 157 if err != nil { 158 return 159 } 160 if logFields["event_name"] == nil { 161 return 162 } 163 if logFields["event_name"].(string) == "server_tunnel" { 164 select { 165 case serverTunnelLog <- logFields: 166 default: 167 } 168 } 169 }) 170 171 serverWaitGroup := new(sync.WaitGroup) 172 serverWaitGroup.Add(1) 173 go func() { 174 defer serverWaitGroup.Done() 175 err := RunServices(serverConfigJSON) 176 if err != nil { 177 t.Errorf("error running server: %s", err) 178 } 179 }() 180 181 defer func() { 182 p, _ := os.FindProcess(os.Getpid()) 183 p.Signal(os.Interrupt) 184 serverWaitGroup.Wait() 185 }() 186 187 // TODO: monitor logs for more robust wait-until-loaded. 188 time.Sleep(1 * time.Second) 189 190 checkServerTunnelLog := func(expectReplay bool) { 191 192 // Numbers are float64 due to JSON decoding. 193 expectedServerTunnelFields := map[string]interface{}{ 194 "downstream_bytes_fragmented": float64(10), 195 "downstream_min_bytes_written": float64(1), 196 "downstream_max_bytes_written": float64(1), 197 "downstream_min_delayed": float64(1000), 198 "downstream_max_delayed": float64(1000), 199 "server_replay_fragmentation": expectReplay, 200 "server_replay_packet_manipulation": expectReplay && runPacketManipulation, 201 } 202 if runPacketManipulation { 203 expectedServerTunnelFields["server_packet_manipulation"] = "test-packetman-spec" 204 } 205 206 logFields := <-serverTunnelLog 207 208 for name, value := range expectedServerTunnelFields { 209 logValue, ok := logFields[name] 210 if !ok { 211 t.Fatalf("Missing expected server_tunnel field: %s", name) 212 } 213 if !reflect.DeepEqual(logValue, value) { 214 t.Fatalf( 215 "Unexpected server_tunnel %s value: got %T(%v); expected %T(%v)", 216 name, logValue, logValue, value, value) 217 } 218 } 219 } 220 221 t.Log("first client run; no replay") 222 223 runServerReplayClient(t, encodedServerEntry, true) 224 checkServerTunnelLog(false) 225 226 t.Log("second client run; is replay") 227 228 runServerReplayClient(t, encodedServerEntry, true) 229 checkServerTunnelLog(true) 230 231 t.Log("TTL expires; no replay") 232 233 // Wait until TTL expires. 234 time.Sleep(5100 * time.Millisecond) 235 236 runServerReplayClient(t, encodedServerEntry, true) 237 checkServerTunnelLog(false) 238 239 t.Log("failure clears replay; no replay") 240 241 runServerReplayClient(t, encodedServerEntry, true) 242 checkServerTunnelLog(true) 243 244 runServerReplayClient(t, encodedServerEntry, false) 245 // No server_tunnel for SSH handshake failure. 246 247 // Wait for session to be retired, which will trigger replay failure. 248 if protocol.TunnelProtocolUsesMeek(tunnelProtocol) { 249 time.Sleep(1000 * time.Millisecond) 250 } 251 252 runServerReplayClient(t, encodedServerEntry, true) 253 checkServerTunnelLog(false) 254 } 255 256 func runServerReplayClient( 257 t *testing.T, 258 encodedServerEntry []byte, 259 handshakeSuccess bool) { 260 261 if !handshakeSuccess { 262 serverEntry, err := protocol.DecodeServerEntry(string(encodedServerEntry), "", "") 263 if err != nil { 264 t.Fatalf("error decoding server entry: %s", err) 265 } 266 serverEntry.SshPassword = "" 267 encodedServerEntryStr, err := protocol.EncodeServerEntry(serverEntry) 268 if err != nil { 269 t.Fatalf("error encoding server entry: %s", err) 270 } 271 encodedServerEntry = []byte(encodedServerEntryStr) 272 } 273 274 dataRootDir, err := ioutil.TempDir(testDataDirName, "serverReplayClient") 275 if err != nil { 276 t.Fatalf("error createing temp dir: %s", err) 277 } 278 defer os.RemoveAll(dataRootDir) 279 280 clientConfigJSON := fmt.Sprintf(` 281 { 282 "DataRootDirectory" : "%s", 283 "ClientPlatform" : "Windows", 284 "ClientVersion" : "0", 285 "SponsorId" : "0", 286 "PropagationChannelId" : "0", 287 "TargetServerEntry" : "%s" 288 }`, dataRootDir, string(encodedServerEntry)) 289 290 clientConfig, err := psiphon.LoadConfig([]byte(clientConfigJSON)) 291 if err != nil { 292 t.Fatalf("error processing configuration file: %s", err) 293 } 294 295 err = clientConfig.Commit(false) 296 if err != nil { 297 t.Fatalf("error committing configuration file: %s", err) 298 } 299 300 err = psiphon.OpenDataStore(clientConfig) 301 if err != nil { 302 t.Fatalf("error initializing client datastore: %s", err) 303 } 304 defer psiphon.CloseDataStore() 305 306 controller, err := psiphon.NewController(clientConfig) 307 if err != nil { 308 t.Fatalf("error creating client controller: %s", err) 309 } 310 311 tunnelEstablished := make(chan struct{}, 1) 312 313 psiphon.SetNoticeWriter(psiphon.NewNoticeReceiver( 314 func(notice []byte) { 315 noticeType, payload, err := psiphon.GetNotice(notice) 316 if err != nil { 317 return 318 } 319 if noticeType == "Tunnels" { 320 count := int(payload["count"].(float64)) 321 if count >= 1 { 322 tunnelEstablished <- struct{}{} 323 } 324 } 325 })) 326 327 ctx, cancelFunc := context.WithCancel(context.Background()) 328 controllerWaitGroup := new(sync.WaitGroup) 329 controllerWaitGroup.Add(1) 330 go func() { 331 defer controllerWaitGroup.Done() 332 controller.Run(ctx) 333 }() 334 335 if handshakeSuccess { 336 <-tunnelEstablished 337 } 338 339 // Meet tunnel duration critera. 340 for i := 0; i < 20; i++ { 341 time.Sleep(10 * time.Millisecond) 342 _, _ = controller.Dial("127.0.0.1:80", nil) 343 } 344 345 cancelFunc() 346 controllerWaitGroup.Wait() 347 }