github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/fragmentor/fragmentor_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 fragmentor 21 22 import ( 23 "bytes" 24 "context" 25 "math/rand" 26 "net" 27 "testing" 28 "time" 29 30 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors" 31 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters" 32 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng" 33 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol" 34 "golang.org/x/sync/errgroup" 35 ) 36 37 func TestFragmentor(t *testing.T) { 38 39 listener, err := net.Listen("tcp", "127.0.0.1:0") 40 if err != nil { 41 t.Fatalf("net.Listen failed: %s", err) 42 } 43 44 address := listener.Addr().String() 45 46 data := make([]byte, 1<<18) 47 rand.Read(data) 48 49 // This test is sensitive to OS buffering and timing; the delays are 50 // intended to be sufficiently long to ensure fragmented writes are read 51 // before additional data is written, even when running tests with the 52 // race detector. 53 54 tunnelProtocol := protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH 55 bytesToFragment := 1 << 15 56 minWriteBytes := 1 57 maxWriteBytes := 512 58 minDelay := 2 * time.Millisecond 59 maxDelay := 2 * time.Millisecond 60 61 params, err := parameters.NewParameters(nil) 62 if err != nil { 63 t.Fatalf("parameters.NewParameters failed: %s", err) 64 } 65 _, err = params.Set("", false, map[string]interface{}{ 66 "FragmentorProbability": 1.0, 67 "FragmentorLimitProtocols": protocol.TunnelProtocols{tunnelProtocol}, 68 "FragmentorMinTotalBytes": bytesToFragment, 69 "FragmentorMaxTotalBytes": bytesToFragment, 70 "FragmentorMinWriteBytes": minWriteBytes, 71 "FragmentorMaxWriteBytes": maxWriteBytes, 72 "FragmentorMinDelay": minDelay, 73 "FragmentorMaxDelay": maxDelay, 74 "FragmentorDownstreamProbability": 1.0, 75 "FragmentorDownstreamLimitProtocols": protocol.TunnelProtocols{tunnelProtocol}, 76 "FragmentorDownstreamMinTotalBytes": bytesToFragment, 77 "FragmentorDownstreamMaxTotalBytes": bytesToFragment, 78 "FragmentorDownstreamMinWriteBytes": minWriteBytes, 79 "FragmentorDownstreamMaxWriteBytes": maxWriteBytes, 80 "FragmentorDownstreamMinDelay": minDelay, 81 "FragmentorDownstreamMaxDelay": maxDelay, 82 }) 83 if err != nil { 84 t.Fatalf("parameters.Parameters.Set failed: %s", err) 85 } 86 87 testGroup, testCtx := errgroup.WithContext(context.Background()) 88 89 testGroup.Go(func() error { 90 91 conn, err := listener.Accept() 92 if err != nil { 93 return errors.Trace(err) 94 } 95 fragConn := NewConn( 96 NewDownstreamConfig(params.Get(), tunnelProtocol, nil), 97 func(message string) { t.Logf(message) }, 98 conn) 99 defer fragConn.Close() 100 101 readData := make([]byte, len(data)) 102 n := 0 103 for n < len(data) { 104 m, err := fragConn.Read(readData[n:]) 105 if err != nil { 106 return errors.Trace(err) 107 } 108 if m > maxWriteBytes && n+maxWriteBytes <= bytesToFragment { 109 return errors.Tracef("unexpected write size: %d, %d", m, n) 110 } 111 n += m 112 } 113 if !bytes.Equal(data, readData) { 114 return errors.Tracef("data mismatch") 115 } 116 117 PRNG, err := prng.NewPRNG() 118 if err != nil { 119 return errors.Trace(err) 120 } 121 fragConn.SetReplay(PRNG) 122 _, err = fragConn.Write(data) 123 if err != nil { 124 return errors.Trace(err) 125 } 126 return nil 127 }) 128 129 testGroup.Go(func() error { 130 131 conn, err := net.Dial("tcp", address) 132 if err != nil { 133 return errors.Trace(err) 134 } 135 seed, err := prng.NewSeed() 136 if err != nil { 137 return errors.Trace(err) 138 } 139 fragConn := NewConn( 140 NewUpstreamConfig(params.Get(), tunnelProtocol, seed), 141 func(message string) { t.Logf(message) }, 142 conn) 143 defer fragConn.Close() 144 145 _, err = fragConn.Write(data) 146 if err != nil { 147 return errors.Trace(err) 148 } 149 t.Logf("%+v", fragConn.GetMetrics()) 150 151 readData := make([]byte, len(data)) 152 n := 0 153 for n < len(data) { 154 m, err := fragConn.Read(readData[n:]) 155 if err != nil { 156 return errors.Trace(err) 157 } 158 if m > maxWriteBytes && n+maxWriteBytes <= bytesToFragment { 159 return errors.Tracef("unexpected write size: %d, %d", m, n) 160 } 161 n += m 162 } 163 if !bytes.Equal(data, readData) { 164 return errors.Tracef("data mismatch") 165 } 166 return nil 167 }) 168 169 go func() { 170 testGroup.Wait() 171 }() 172 173 <-testCtx.Done() 174 listener.Close() 175 176 err = testGroup.Wait() 177 if err != nil { 178 t.Errorf("goroutine failed: %s", err) 179 } 180 }