@@ -27,6 +27,7 @@ import (
2727 ss "github.com/Jigsaw-Code/outline-ss-server/shadowsocks"
2828 logging "github.com/op/go-logging"
2929 "github.com/shadowsocks/go-shadowsocks2/socks"
30+ "github.com/stretchr/testify/assert"
3031)
3132
3233const timeout = 5 * time .Minute
@@ -89,10 +90,16 @@ func (conn *fakePacketConn) Close() error {
8990 return nil
9091}
9192
93+ type udpReport struct {
94+ clientLocation , accessKey , status string
95+ clientProxyBytes , proxyTargetBytes int
96+ }
97+
9298// Stub metrics implementation for testing NAT behaviors.
9399type natTestMetrics struct {
94100 metrics.ShadowsocksMetrics
95101 natEntriesAdded int
102+ upstreamPackets []udpReport
96103}
97104
98105func (m * natTestMetrics ) AddTCPProbe (clientLocation , status , drainResult string , port int , data metrics.ProxyMetrics ) {
@@ -107,6 +114,7 @@ func (m *natTestMetrics) SetNumAccessKeys(numKeys int, numPorts int) {
107114func (m * natTestMetrics ) AddOpenTCPConnection (clientLocation string ) {
108115}
109116func (m * natTestMetrics ) AddUDPPacketFromClient (clientLocation , accessKey , status string , clientProxyBytes , proxyTargetBytes int , timeToCipher time.Duration ) {
117+ m .upstreamPackets = append (m .upstreamPackets , udpReport {clientLocation , accessKey , status , clientProxyBytes , proxyTargetBytes })
110118}
111119func (m * natTestMetrics ) AddUDPPacketFromTarget (clientLocation , accessKey , status string , targetProxyBytes , proxyClientBytes int ) {
112120}
@@ -115,21 +123,20 @@ func (m *natTestMetrics) AddUDPNatEntry() {
115123}
116124func (m * natTestMetrics ) RemoveUDPNatEntry () {}
117125
118- func TestIPFilter (t * testing.T ) {
119- // Takes a validation policy, and returns the metrics it
120- // generates when localhost access is attempted
121- checkLocalhost := func (validator onet.TargetIPValidator ) * natTestMetrics {
122- ciphers , _ := MakeTestCiphers ([]string {"asdf" })
123- cipher := ciphers .SnapshotForClientIP (nil )[0 ].Value .(* CipherEntry ).Cipher
124- clientConn := makePacketConn ()
125- metrics := & natTestMetrics {}
126- service := NewUDPService (timeout , ciphers , metrics )
127- service .SetTargetIPValidator (validator )
128- go service .Serve (clientConn )
129-
130- // Send one packet to the "discard" port on localhost
131- targetAddr := socks .ParseAddr ("127.0.0.1:9" )
132- payload := []byte ("payload" )
126+ // Takes a validation policy, and returns the metrics it
127+ // generates when localhost access is attempted
128+ func sendToDiscard (payloads [][]byte , validator onet.TargetIPValidator ) * natTestMetrics {
129+ ciphers , _ := MakeTestCiphers ([]string {"asdf" })
130+ cipher := ciphers .SnapshotForClientIP (nil )[0 ].Value .(* CipherEntry ).Cipher
131+ clientConn := makePacketConn ()
132+ metrics := & natTestMetrics {}
133+ service := NewUDPService (timeout , ciphers , metrics )
134+ service .SetTargetIPValidator (validator )
135+ go service .Serve (clientConn )
136+
137+ // Send one packet to the "discard" port on localhost
138+ targetAddr := socks .ParseAddr ("127.0.0.1:9" )
139+ for _ , payload := range payloads {
133140 plaintext := append (targetAddr , payload ... )
134141 ciphertext := make ([]byte , cipher .SaltSize ()+ len (plaintext )+ cipher .TagSize ())
135142 ss .Pack (ciphertext , plaintext , cipher )
@@ -140,26 +147,52 @@ func TestIPFilter(t *testing.T) {
140147 },
141148 payload : ciphertext ,
142149 }
143-
144- service .GracefulStop ()
145- return metrics
146150 }
147151
152+ service .GracefulStop ()
153+ return metrics
154+ }
155+
156+ func TestIPFilter (t * testing.T ) {
157+ // Test both the first-packet and subsequent-packet cases.
158+ payloads := [][]byte {[]byte ("payload1" ), []byte ("payload2" )}
159+
148160 t .Run ("Localhost allowed" , func (t * testing.T ) {
149- metrics := checkLocalhost (allowAll )
150- if metrics .natEntriesAdded != 1 {
151- t .Errorf ("Expected 1 NAT entry, not %d" , metrics .natEntriesAdded )
152- }
161+ metrics := sendToDiscard (payloads , allowAll )
162+ assert .Equal (t , metrics .natEntriesAdded , 1 , "Expected 1 NAT entry, not %d" , metrics .natEntriesAdded )
153163 })
154164
155165 t .Run ("Localhost not allowed" , func (t * testing.T ) {
156- metrics := checkLocalhost (onet .RequirePublicIP )
157- if metrics .natEntriesAdded != 0 {
158- t .Error ("Unexpected NAT entry on rejected packet" )
166+ metrics := sendToDiscard (payloads , onet .RequirePublicIP )
167+ assert .Equal (t , 0 , metrics .natEntriesAdded , "Unexpected NAT entry on rejected packet" )
168+ assert .Equal (t , 2 , len (metrics .upstreamPackets ), "Expected 2 reports, not %v" , metrics .upstreamPackets )
169+ for _ , report := range metrics .upstreamPackets {
170+ assert .Greater (t , report .clientProxyBytes , 0 , "Expected nonzero input packet size" )
171+ assert .Equal (t , 0 , report .proxyTargetBytes , "No bytes should be sent due to a disallowed packet" )
172+ assert .Equal (t , report .accessKey , "id-0" , "Unexpected access key: %s" , report .accessKey )
159173 }
160174 })
161175}
162176
177+ func TestUpstreamMetrics (t * testing.T ) {
178+ // Test both the first-packet and subsequent-packet cases.
179+ const N = 10
180+ payloads := make ([][]byte , 0 )
181+ for i := 1 ; i <= N ; i ++ {
182+ payloads = append (payloads , make ([]byte , i ))
183+ }
184+
185+ metrics := sendToDiscard (payloads , allowAll )
186+
187+ assert .Equal (t , N , len (metrics .upstreamPackets ), "Expected %d reports, not %v" , N , metrics .upstreamPackets )
188+ for i , report := range metrics .upstreamPackets {
189+ assert .Equal (t , i + 1 , report .proxyTargetBytes , "Expected %d payload bytes, not %d" , i + 1 , report .proxyTargetBytes )
190+ assert .Greater (t , report .clientProxyBytes , report .proxyTargetBytes , "Expected nonzero input overhead (%d > %d)" , report .clientProxyBytes , report .proxyTargetBytes )
191+ assert .Equal (t , "id-0" , report .accessKey , "Unexpected access key name: %s" , report .accessKey )
192+ assert .Equal (t , "OK" , report .status , "Wrong status: %s" , report .status )
193+ }
194+ }
195+
163196func assertAlmostEqual (t * testing.T , a , b time.Time ) {
164197 delta := a .Sub (b )
165198 limit := 100 * time .Millisecond
0 commit comments