github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/router/condition_geoip_test.go (about)

     1  package router_test
     2  
     3  import (
     4  	"errors"
     5  	"io/fs"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"google.golang.org/protobuf/proto"
    12  
    13  	"github.com/v2fly/v2ray-core/v5/app/router"
    14  	"github.com/v2fly/v2ray-core/v5/app/router/routercommon"
    15  	"github.com/v2fly/v2ray-core/v5/common"
    16  	"github.com/v2fly/v2ray-core/v5/common/net"
    17  	"github.com/v2fly/v2ray-core/v5/common/platform/filesystem"
    18  )
    19  
    20  func init() {
    21  	const geoipURL = "https://raw.githubusercontent.com/v2fly/geoip/release/geoip.dat"
    22  
    23  	wd, err := os.Getwd()
    24  	common.Must(err)
    25  
    26  	tempPath := filepath.Join(wd, "..", "..", "testing", "temp")
    27  	geoipPath := filepath.Join(tempPath, "geoip.dat")
    28  
    29  	os.Setenv("v2ray.location.asset", tempPath)
    30  
    31  	if _, err := os.Stat(geoipPath); err != nil && errors.Is(err, fs.ErrNotExist) {
    32  		common.Must(os.MkdirAll(tempPath, 0o755))
    33  		geoipBytes, err := common.FetchHTTPContent(geoipURL)
    34  		common.Must(err)
    35  		common.Must(filesystem.WriteFile(geoipPath, geoipBytes))
    36  	}
    37  }
    38  
    39  func TestGeoIPMatcherContainer(t *testing.T) {
    40  	container := &router.GeoIPMatcherContainer{}
    41  
    42  	m1, err := container.Add(&routercommon.GeoIP{
    43  		CountryCode: "CN",
    44  	})
    45  	common.Must(err)
    46  
    47  	m2, err := container.Add(&routercommon.GeoIP{
    48  		CountryCode: "US",
    49  	})
    50  	common.Must(err)
    51  
    52  	m3, err := container.Add(&routercommon.GeoIP{
    53  		CountryCode: "CN",
    54  	})
    55  	common.Must(err)
    56  
    57  	if m1 != m3 {
    58  		t.Error("expect same matcher for same geoip, but not")
    59  	}
    60  
    61  	if m1 == m2 {
    62  		t.Error("expect different matcher for different geoip, but actually same")
    63  	}
    64  }
    65  
    66  func TestGeoIPMatcher(t *testing.T) {
    67  	cidrList := []*routercommon.CIDR{
    68  		{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
    69  		{Ip: []byte{10, 0, 0, 0}, Prefix: 8},
    70  		{Ip: []byte{100, 64, 0, 0}, Prefix: 10},
    71  		{Ip: []byte{127, 0, 0, 0}, Prefix: 8},
    72  		{Ip: []byte{169, 254, 0, 0}, Prefix: 16},
    73  		{Ip: []byte{172, 16, 0, 0}, Prefix: 12},
    74  		{Ip: []byte{192, 0, 0, 0}, Prefix: 24},
    75  		{Ip: []byte{192, 0, 2, 0}, Prefix: 24},
    76  		{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
    77  		{Ip: []byte{192, 18, 0, 0}, Prefix: 15},
    78  		{Ip: []byte{198, 51, 100, 0}, Prefix: 24},
    79  		{Ip: []byte{203, 0, 113, 0}, Prefix: 24},
    80  		{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
    81  		{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
    82  	}
    83  
    84  	matcher := &router.GeoIPMatcher{}
    85  	common.Must(matcher.Init(cidrList))
    86  
    87  	testCases := []struct {
    88  		Input  string
    89  		Output bool
    90  	}{
    91  		{
    92  			Input:  "192.168.1.1",
    93  			Output: true,
    94  		},
    95  		{
    96  			Input:  "192.0.0.0",
    97  			Output: true,
    98  		},
    99  		{
   100  			Input:  "192.0.1.0",
   101  			Output: false,
   102  		},
   103  		{
   104  			Input:  "0.1.0.0",
   105  			Output: true,
   106  		},
   107  		{
   108  			Input:  "1.0.0.1",
   109  			Output: false,
   110  		},
   111  		{
   112  			Input:  "8.8.8.7",
   113  			Output: false,
   114  		},
   115  		{
   116  			Input:  "8.8.8.8",
   117  			Output: true,
   118  		},
   119  		{
   120  			Input:  "2001:cdba::3257:9652",
   121  			Output: false,
   122  		},
   123  		{
   124  			Input:  "91.108.255.254",
   125  			Output: true,
   126  		},
   127  	}
   128  
   129  	for _, testCase := range testCases {
   130  		ip := net.ParseAddress(testCase.Input).IP()
   131  		actual := matcher.Match(ip)
   132  		if actual != testCase.Output {
   133  			t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
   134  		}
   135  	}
   136  }
   137  
   138  func TestGeoIPReverseMatcher(t *testing.T) {
   139  	cidrList := []*routercommon.CIDR{
   140  		{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
   141  		{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
   142  	}
   143  	matcher := &router.GeoIPMatcher{}
   144  	matcher.SetReverseMatch(true) // Reverse match
   145  	common.Must(matcher.Init(cidrList))
   146  
   147  	testCases := []struct {
   148  		Input  string
   149  		Output bool
   150  	}{
   151  		{
   152  			Input:  "8.8.8.8",
   153  			Output: false,
   154  		},
   155  		{
   156  			Input:  "2001:cdba::3257:9652",
   157  			Output: true,
   158  		},
   159  		{
   160  			Input:  "91.108.255.254",
   161  			Output: false,
   162  		},
   163  	}
   164  
   165  	for _, testCase := range testCases {
   166  		ip := net.ParseAddress(testCase.Input).IP()
   167  		actual := matcher.Match(ip)
   168  		if actual != testCase.Output {
   169  			t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
   170  		}
   171  	}
   172  }
   173  
   174  func TestGeoIPMatcher4CN(t *testing.T) {
   175  	ips, err := loadGeoIP("CN")
   176  	common.Must(err)
   177  
   178  	matcher := &router.GeoIPMatcher{}
   179  	common.Must(matcher.Init(ips))
   180  
   181  	if matcher.Match([]byte{8, 8, 8, 8}) {
   182  		t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does")
   183  	}
   184  }
   185  
   186  func TestGeoIPMatcher6US(t *testing.T) {
   187  	ips, err := loadGeoIP("US")
   188  	common.Must(err)
   189  
   190  	matcher := &router.GeoIPMatcher{}
   191  	common.Must(matcher.Init(ips))
   192  
   193  	if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
   194  		t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not")
   195  	}
   196  }
   197  
   198  func loadGeoIP(country string) ([]*routercommon.CIDR, error) {
   199  	geoipBytes, err := filesystem.ReadAsset("geoip.dat")
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	var geoipList routercommon.GeoIPList
   204  	if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	for _, geoip := range geoipList.Entry {
   209  		if strings.EqualFold(geoip.CountryCode, country) {
   210  			return geoip.Cidr, nil
   211  		}
   212  	}
   213  
   214  	panic("country not found: " + country)
   215  }
   216  
   217  func BenchmarkGeoIPMatcher4CN(b *testing.B) {
   218  	ips, err := loadGeoIP("CN")
   219  	common.Must(err)
   220  
   221  	matcher := &router.GeoIPMatcher{}
   222  	common.Must(matcher.Init(ips))
   223  
   224  	b.ResetTimer()
   225  
   226  	for i := 0; i < b.N; i++ {
   227  		_ = matcher.Match([]byte{8, 8, 8, 8})
   228  	}
   229  }
   230  
   231  func BenchmarkGeoIPMatcher6US(b *testing.B) {
   232  	ips, err := loadGeoIP("US")
   233  	common.Must(err)
   234  
   235  	matcher := &router.GeoIPMatcher{}
   236  	common.Must(matcher.Init(ips))
   237  
   238  	b.ResetTimer()
   239  
   240  	for i := 0; i < b.N; i++ {
   241  		_ = matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP())
   242  	}
   243  }