| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- /*
- * Copyright (c) 2022, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- package server
- import (
- "encoding/json"
- "io/ioutil"
- "os"
- "reflect"
- "testing"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
- )
- func TestTrafficRulesFilters(t *testing.T) {
- trafficRulesJSON := `
- {
- "DefaultRules" : {
- "RateLimits" : {
- "WriteUnthrottledBytes": 1,
- "WriteBytesPerSecond": 2,
- "ReadUnthrottledBytes": 3,
- "ReadBytesPerSecond": 4,
- "UnthrottleFirstTunnelOnly": true
- },
- "AllowTCPPorts" : [5],
- "AllowUDPPorts" : [6]
- },
-
- "FilteredRules" : [
-
- {
- "Filter" : {
- "Regions" : ["R2"],
- "HandshakeParameters" : {
- "client_version" : ["1"]
- }
- },
- "Rules" : {
- "RateLimits" : {
- "WriteBytesPerSecond": 7,
- "ReadBytesPerSecond": 8
- },
- "AllowTCPPorts" : [5,9],
- "AllowUDPPorts" : [6,10]
- }
- },
- {
- "Filter" : {
- "TunnelProtocols" : ["P2"],
- "Regions" : ["R3", "R4"],
- "HandshakeParameters" : {
- "client_version" : ["1", "2"]
- }
- },
- "ExceptFilter" : {
- "ISPs" : ["I2", "I3"],
- "HandshakeParameters" : {
- "client_version" : ["1"]
- }
- },
- "Rules" : {
- "RateLimits" : {
- "WriteBytesPerSecond": 11,
- "ReadBytesPerSecond": 12
- },
- "AllowTCPPorts" : [5,13],
- "AllowUDPPorts" : [6,14]
- }
- },
- {
- "Filter" : {
- "Regions" : ["R3", "R4"],
- "HandshakeParameters" : {
- "client_version" : ["1", "2"]
- }
- },
- "ExceptFilter" : {
- "ISPs" : ["I2", "I3"],
- "HandshakeParameters" : {
- "client_version" : ["1"]
- }
- },
- "Rules" : {
- "RateLimits" : {
- "WriteBytesPerSecond": 15,
- "ReadBytesPerSecond": 16
- },
- "AllowTCPPorts" : [5,17],
- "AllowUDPPorts" : [6,18]
- }
- }
- ]
- }
- `
- file, err := ioutil.TempFile("", "trafficRules.config")
- if err != nil {
- t.Fatalf("TempFile create failed: %s", err)
- }
- _, err = file.Write([]byte(trafficRulesJSON))
- if err != nil {
- t.Fatalf("TempFile write failed: %s", err)
- }
- file.Close()
- configFileName := file.Name()
- defer os.Remove(configFileName)
- trafficRules, err := NewTrafficRulesSet(configFileName)
- if err != nil {
- t.Fatalf("NewTrafficRulesSet failed: %s", err)
- }
- err = trafficRules.Validate()
- if err != nil {
- t.Fatalf("TrafficRulesSet.Validate failed: %s", err)
- }
- makePortList := func(portsJSON string) common.PortList {
- var p common.PortList
- _ = json.Unmarshal([]byte(portsJSON), &p)
- return p
- }
- testCases := []struct {
- description string
- isFirstTunnelInSession bool
- tunnelProtocol string
- geoIPData GeoIPData
- state handshakeState
- expectedWriteUnthrottledBytes int64
- expectedWriteBytesPerSecond int64
- expectedReadUnthrottledBytes int64
- expectedReadBytesPerSecond int64
- expectedAllowTCPPorts common.PortList
- expectedAllowUDPPorts common.PortList
- }{
- {
- "get defaults",
- true,
- "P1",
- GeoIPData{Country: "R1", ISP: "I1"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
- 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
- },
- {
- "get defaults for not first tunnel in session",
- false,
- "P1",
- GeoIPData{Country: "R1", ISP: "I1"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
- 0, 2, 0, 4, makePortList("[5]"), makePortList("[6]"),
- },
- {
- "get first filtered rule",
- true,
- "P1",
- GeoIPData{Country: "R2", ISP: "I1"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
- 1, 7, 3, 8, makePortList("[5,9]"), makePortList("[6,10]"),
- },
- {
- "don't get first filtered rule with incomplete match",
- true,
- "P1",
- GeoIPData{Country: "R2", ISP: "I1"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "2"}, completed: true},
- 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
- },
- {
- "get second filtered rule",
- true,
- "P2",
- GeoIPData{Country: "R3", ISP: "I1"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "2"}, completed: true},
- 1, 11, 3, 12, makePortList("[5,13]"), makePortList("[6,14]"),
- },
- {
- "get second filtered rule with incomplete exception",
- true,
- "P2",
- GeoIPData{Country: "R3", ISP: "I2"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "2"}, completed: true},
- 1, 11, 3, 12, makePortList("[5,13]"), makePortList("[6,14]"),
- },
- {
- "don't get second filtered rule due to exception",
- true,
- "P2",
- GeoIPData{Country: "R3", ISP: "I2"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
- 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
- },
- {
- "get third filtered rule",
- true,
- "P1",
- GeoIPData{Country: "R3", ISP: "I1"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
- 1, 15, 3, 16, makePortList("[5,17]"), makePortList("[6,18]"),
- },
- {
- "don't get third filtered rule due to exception",
- true,
- "P1",
- GeoIPData{Country: "R3", ISP: "I2"},
- handshakeState{apiParams: map[string]interface{}{"client_version": "1"}, completed: true},
- 1, 2, 3, 4, makePortList("[5]"), makePortList("[6]"),
- },
- }
- for _, testCase := range testCases {
- t.Run(testCase.description, func(t *testing.T) {
- rules := trafficRules.GetTrafficRules(
- testCase.isFirstTunnelInSession,
- testCase.tunnelProtocol,
- testCase.geoIPData,
- testCase.state)
- if *rules.RateLimits.WriteUnthrottledBytes != testCase.expectedWriteUnthrottledBytes {
- t.Errorf("unexpected rules.RateLimits.WriteUnthrottledBytes: %v != %v",
- *rules.RateLimits.WriteUnthrottledBytes, testCase.expectedWriteUnthrottledBytes)
- }
- if *rules.RateLimits.WriteBytesPerSecond != testCase.expectedWriteBytesPerSecond {
- t.Errorf("unexpected rules.RateLimits.WriteBytesPerSecond: %v != %v",
- *rules.RateLimits.WriteBytesPerSecond, testCase.expectedWriteBytesPerSecond)
- }
- if *rules.RateLimits.ReadUnthrottledBytes != testCase.expectedReadUnthrottledBytes {
- t.Errorf("unexpected rules.RateLimits.ReadUnthrottledBytes: %v != %v",
- *rules.RateLimits.ReadUnthrottledBytes, testCase.expectedReadUnthrottledBytes)
- }
- if *rules.RateLimits.ReadBytesPerSecond != testCase.expectedReadBytesPerSecond {
- t.Errorf("unexpected rules.RateLimits.ReadBytesPerSecond: %v != %v",
- *rules.RateLimits.ReadBytesPerSecond, testCase.expectedReadBytesPerSecond)
- }
- if !reflect.DeepEqual(*rules.AllowTCPPorts, testCase.expectedAllowTCPPorts) {
- t.Errorf("unexpected rules.RateLimits.AllowTCPPorts: %v != %v",
- *rules.AllowTCPPorts, testCase.expectedAllowTCPPorts)
- }
- if !reflect.DeepEqual(*rules.AllowUDPPorts, testCase.expectedAllowUDPPorts) {
- t.Errorf("unexpected rules.RateLimits.AllowUDPPorts: %v != %v",
- *rules.AllowUDPPorts, testCase.expectedAllowUDPPorts)
- }
- })
- }
- }
|