github.com/EagleQL/Xray-core@v1.4.3/app/router/condition_test.go (about)

     1  package router_test
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strconv"
     7  	"testing"
     8  
     9  	"github.com/golang/protobuf/proto"
    10  
    11  	. "github.com/xtls/xray-core/app/router"
    12  	"github.com/xtls/xray-core/common"
    13  	"github.com/xtls/xray-core/common/errors"
    14  	"github.com/xtls/xray-core/common/net"
    15  	"github.com/xtls/xray-core/common/platform"
    16  	"github.com/xtls/xray-core/common/platform/filesystem"
    17  	"github.com/xtls/xray-core/common/protocol"
    18  	"github.com/xtls/xray-core/common/protocol/http"
    19  	"github.com/xtls/xray-core/common/session"
    20  	"github.com/xtls/xray-core/features/routing"
    21  	routing_session "github.com/xtls/xray-core/features/routing/session"
    22  )
    23  
    24  func init() {
    25  	wd, err := os.Getwd()
    26  	common.Must(err)
    27  
    28  	if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) {
    29  		common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "release", "config", "geoip.dat")))
    30  	}
    31  	if _, err := os.Stat(platform.GetAssetLocation("geosite.dat")); err != nil && os.IsNotExist(err) {
    32  		common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "release", "config", "geosite.dat")))
    33  	}
    34  }
    35  
    36  func withBackground() routing.Context {
    37  	return &routing_session.Context{}
    38  }
    39  
    40  func withOutbound(outbound *session.Outbound) routing.Context {
    41  	return &routing_session.Context{Outbound: outbound}
    42  }
    43  
    44  func withInbound(inbound *session.Inbound) routing.Context {
    45  	return &routing_session.Context{Inbound: inbound}
    46  }
    47  
    48  func withContent(content *session.Content) routing.Context {
    49  	return &routing_session.Context{Content: content}
    50  }
    51  
    52  func TestRoutingRule(t *testing.T) {
    53  	type ruleTest struct {
    54  		input  routing.Context
    55  		output bool
    56  	}
    57  
    58  	cases := []struct {
    59  		rule *RoutingRule
    60  		test []ruleTest
    61  	}{
    62  		{
    63  			rule: &RoutingRule{
    64  				Domain: []*Domain{
    65  					{
    66  						Value: "example.com",
    67  						Type:  Domain_Plain,
    68  					},
    69  					{
    70  						Value: "google.com",
    71  						Type:  Domain_Domain,
    72  					},
    73  					{
    74  						Value: "^facebook\\.com$",
    75  						Type:  Domain_Regex,
    76  					},
    77  				},
    78  			},
    79  			test: []ruleTest{
    80  				{
    81  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.com"), 80)}),
    82  					output: true,
    83  				},
    84  				{
    85  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("www.example.com.www"), 80)}),
    86  					output: true,
    87  				},
    88  				{
    89  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("example.co"), 80)}),
    90  					output: false,
    91  				},
    92  				{
    93  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("www.google.com"), 80)}),
    94  					output: true,
    95  				},
    96  				{
    97  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("facebook.com"), 80)}),
    98  					output: true,
    99  				},
   100  				{
   101  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("www.facebook.com"), 80)}),
   102  					output: false,
   103  				},
   104  				{
   105  					input:  withBackground(),
   106  					output: false,
   107  				},
   108  			},
   109  		},
   110  		{
   111  			rule: &RoutingRule{
   112  				Cidr: []*CIDR{
   113  					{
   114  						Ip:     []byte{8, 8, 8, 8},
   115  						Prefix: 32,
   116  					},
   117  					{
   118  						Ip:     []byte{8, 8, 8, 8},
   119  						Prefix: 32,
   120  					},
   121  					{
   122  						Ip:     net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(),
   123  						Prefix: 128,
   124  					},
   125  				},
   126  			},
   127  			test: []ruleTest{
   128  				{
   129  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}),
   130  					output: true,
   131  				},
   132  				{
   133  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.4.4"), 80)}),
   134  					output: false,
   135  				},
   136  				{
   137  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 80)}),
   138  					output: true,
   139  				},
   140  				{
   141  					input:  withBackground(),
   142  					output: false,
   143  				},
   144  			},
   145  		},
   146  		{
   147  			rule: &RoutingRule{
   148  				Geoip: []*GeoIP{
   149  					{
   150  						Cidr: []*CIDR{
   151  							{
   152  								Ip:     []byte{8, 8, 8, 8},
   153  								Prefix: 32,
   154  							},
   155  							{
   156  								Ip:     []byte{8, 8, 8, 8},
   157  								Prefix: 32,
   158  							},
   159  							{
   160  								Ip:     net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(),
   161  								Prefix: 128,
   162  							},
   163  						},
   164  					},
   165  				},
   166  			},
   167  			test: []ruleTest{
   168  				{
   169  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)}),
   170  					output: true,
   171  				},
   172  				{
   173  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.4.4"), 80)}),
   174  					output: false,
   175  				},
   176  				{
   177  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), 80)}),
   178  					output: true,
   179  				},
   180  				{
   181  					input:  withBackground(),
   182  					output: false,
   183  				},
   184  			},
   185  		},
   186  		{
   187  			rule: &RoutingRule{
   188  				SourceCidr: []*CIDR{
   189  					{
   190  						Ip:     []byte{192, 168, 0, 0},
   191  						Prefix: 16,
   192  					},
   193  				},
   194  			},
   195  			test: []ruleTest{
   196  				{
   197  					input:  withInbound(&session.Inbound{Source: net.TCPDestination(net.ParseAddress("192.168.0.1"), 80)}),
   198  					output: true,
   199  				},
   200  				{
   201  					input:  withInbound(&session.Inbound{Source: net.TCPDestination(net.ParseAddress("10.0.0.1"), 80)}),
   202  					output: false,
   203  				},
   204  			},
   205  		},
   206  		{
   207  			rule: &RoutingRule{
   208  				UserEmail: []string{
   209  					"admin@example.com",
   210  				},
   211  			},
   212  			test: []ruleTest{
   213  				{
   214  					input:  withInbound(&session.Inbound{User: &protocol.MemoryUser{Email: "admin@example.com"}}),
   215  					output: true,
   216  				},
   217  				{
   218  					input:  withInbound(&session.Inbound{User: &protocol.MemoryUser{Email: "love@example.com"}}),
   219  					output: false,
   220  				},
   221  				{
   222  					input:  withBackground(),
   223  					output: false,
   224  				},
   225  			},
   226  		},
   227  		{
   228  			rule: &RoutingRule{
   229  				Protocol: []string{"http"},
   230  			},
   231  			test: []ruleTest{
   232  				{
   233  					input:  withContent(&session.Content{Protocol: (&http.SniffHeader{}).Protocol()}),
   234  					output: true,
   235  				},
   236  			},
   237  		},
   238  		{
   239  			rule: &RoutingRule{
   240  				InboundTag: []string{"test", "test1"},
   241  			},
   242  			test: []ruleTest{
   243  				{
   244  					input:  withInbound(&session.Inbound{Tag: "test"}),
   245  					output: true,
   246  				},
   247  				{
   248  					input:  withInbound(&session.Inbound{Tag: "test2"}),
   249  					output: false,
   250  				},
   251  			},
   252  		},
   253  		{
   254  			rule: &RoutingRule{
   255  				PortList: &net.PortList{
   256  					Range: []*net.PortRange{
   257  						{From: 443, To: 443},
   258  						{From: 1000, To: 1100},
   259  					},
   260  				},
   261  			},
   262  			test: []ruleTest{
   263  				{
   264  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 443)}),
   265  					output: true,
   266  				},
   267  				{
   268  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 1100)}),
   269  					output: true,
   270  				},
   271  				{
   272  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 1005)}),
   273  					output: true,
   274  				},
   275  				{
   276  					input:  withOutbound(&session.Outbound{Target: net.TCPDestination(net.LocalHostIP, 53)}),
   277  					output: false,
   278  				},
   279  			},
   280  		},
   281  		{
   282  			rule: &RoutingRule{
   283  				SourcePortList: &net.PortList{
   284  					Range: []*net.PortRange{
   285  						{From: 123, To: 123},
   286  						{From: 9993, To: 9999},
   287  					},
   288  				},
   289  			},
   290  			test: []ruleTest{
   291  				{
   292  					input:  withInbound(&session.Inbound{Source: net.UDPDestination(net.LocalHostIP, 123)}),
   293  					output: true,
   294  				},
   295  				{
   296  					input:  withInbound(&session.Inbound{Source: net.UDPDestination(net.LocalHostIP, 9999)}),
   297  					output: true,
   298  				},
   299  				{
   300  					input:  withInbound(&session.Inbound{Source: net.UDPDestination(net.LocalHostIP, 9994)}),
   301  					output: true,
   302  				},
   303  				{
   304  					input:  withInbound(&session.Inbound{Source: net.UDPDestination(net.LocalHostIP, 53)}),
   305  					output: false,
   306  				},
   307  			},
   308  		},
   309  		{
   310  			rule: &RoutingRule{
   311  				Protocol:   []string{"http"},
   312  				Attributes: "attrs[':path'].startswith('/test')",
   313  			},
   314  			test: []ruleTest{
   315  				{
   316  					input:  withContent(&session.Content{Protocol: "http/1.1", Attributes: map[string]string{":path": "/test/1"}}),
   317  					output: true,
   318  				},
   319  			},
   320  		},
   321  	}
   322  
   323  	for _, test := range cases {
   324  		cond, err := test.rule.BuildCondition()
   325  		common.Must(err)
   326  
   327  		for _, subtest := range test.test {
   328  			actual := cond.Apply(subtest.input)
   329  			if actual != subtest.output {
   330  				t.Error("test case failed: ", subtest.input, " expected ", subtest.output, " but got ", actual)
   331  			}
   332  		}
   333  	}
   334  }
   335  
   336  func loadGeoSite(country string) ([]*Domain, error) {
   337  	geositeBytes, err := filesystem.ReadAsset("geosite.dat")
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	var geositeList GeoSiteList
   342  	if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
   343  		return nil, err
   344  	}
   345  
   346  	for _, site := range geositeList.Entry {
   347  		if site.CountryCode == country {
   348  			return site.Domain, nil
   349  		}
   350  	}
   351  
   352  	return nil, errors.New("country not found: " + country)
   353  }
   354  
   355  func TestChinaSites(t *testing.T) {
   356  	domains, err := loadGeoSite("CN")
   357  	common.Must(err)
   358  
   359  	matcher, err := NewDomainMatcher(domains)
   360  	common.Must(err)
   361  
   362  	acMatcher, err := NewMphMatcherGroup(domains)
   363  	common.Must(err)
   364  
   365  	type TestCase struct {
   366  		Domain string
   367  		Output bool
   368  	}
   369  	testCases := []TestCase{
   370  		{
   371  			Domain: "163.com",
   372  			Output: true,
   373  		},
   374  		{
   375  			Domain: "163.com",
   376  			Output: true,
   377  		},
   378  		{
   379  			Domain: "164.com",
   380  			Output: false,
   381  		},
   382  		{
   383  			Domain: "164.com",
   384  			Output: false,
   385  		},
   386  	}
   387  
   388  	for i := 0; i < 1024; i++ {
   389  		testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
   390  	}
   391  
   392  	for _, testCase := range testCases {
   393  		r1 := matcher.ApplyDomain(testCase.Domain)
   394  		r2 := acMatcher.ApplyDomain(testCase.Domain)
   395  		if r1 != testCase.Output {
   396  			t.Error("DomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r1)
   397  		} else if r2 != testCase.Output {
   398  			t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r2)
   399  		}
   400  	}
   401  }
   402  
   403  func BenchmarkMphDomainMatcher(b *testing.B) {
   404  	domains, err := loadGeoSite("CN")
   405  	common.Must(err)
   406  
   407  	matcher, err := NewMphMatcherGroup(domains)
   408  	common.Must(err)
   409  
   410  	type TestCase struct {
   411  		Domain string
   412  		Output bool
   413  	}
   414  	testCases := []TestCase{
   415  		{
   416  			Domain: "163.com",
   417  			Output: true,
   418  		},
   419  		{
   420  			Domain: "163.com",
   421  			Output: true,
   422  		},
   423  		{
   424  			Domain: "164.com",
   425  			Output: false,
   426  		},
   427  		{
   428  			Domain: "164.com",
   429  			Output: false,
   430  		},
   431  	}
   432  
   433  	for i := 0; i < 1024; i++ {
   434  		testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
   435  	}
   436  
   437  	b.ResetTimer()
   438  	for i := 0; i < b.N; i++ {
   439  		for _, testCase := range testCases {
   440  			_ = matcher.ApplyDomain(testCase.Domain)
   441  		}
   442  	}
   443  }
   444  
   445  func BenchmarkDomainMatcher(b *testing.B) {
   446  	domains, err := loadGeoSite("CN")
   447  	common.Must(err)
   448  
   449  	matcher, err := NewDomainMatcher(domains)
   450  	common.Must(err)
   451  
   452  	type TestCase struct {
   453  		Domain string
   454  		Output bool
   455  	}
   456  	testCases := []TestCase{
   457  		{
   458  			Domain: "163.com",
   459  			Output: true,
   460  		},
   461  		{
   462  			Domain: "163.com",
   463  			Output: true,
   464  		},
   465  		{
   466  			Domain: "164.com",
   467  			Output: false,
   468  		},
   469  		{
   470  			Domain: "164.com",
   471  			Output: false,
   472  		},
   473  	}
   474  
   475  	for i := 0; i < 1024; i++ {
   476  		testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
   477  	}
   478  
   479  	b.ResetTimer()
   480  	for i := 0; i < b.N; i++ {
   481  		for _, testCase := range testCases {
   482  			_ = matcher.ApplyDomain(testCase.Domain)
   483  		}
   484  	}
   485  }
   486  
   487  func BenchmarkMultiGeoIPMatcher(b *testing.B) {
   488  	var geoips []*GeoIP
   489  
   490  	{
   491  		ips, err := loadGeoIP("CN")
   492  		common.Must(err)
   493  		geoips = append(geoips, &GeoIP{
   494  			CountryCode: "CN",
   495  			Cidr:        ips,
   496  		})
   497  	}
   498  
   499  	{
   500  		ips, err := loadGeoIP("JP")
   501  		common.Must(err)
   502  		geoips = append(geoips, &GeoIP{
   503  			CountryCode: "JP",
   504  			Cidr:        ips,
   505  		})
   506  	}
   507  
   508  	{
   509  		ips, err := loadGeoIP("CA")
   510  		common.Must(err)
   511  		geoips = append(geoips, &GeoIP{
   512  			CountryCode: "CA",
   513  			Cidr:        ips,
   514  		})
   515  	}
   516  
   517  	{
   518  		ips, err := loadGeoIP("US")
   519  		common.Must(err)
   520  		geoips = append(geoips, &GeoIP{
   521  			CountryCode: "US",
   522  			Cidr:        ips,
   523  		})
   524  	}
   525  
   526  	matcher, err := NewMultiGeoIPMatcher(geoips, false)
   527  	common.Must(err)
   528  
   529  	ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)})
   530  
   531  	b.ResetTimer()
   532  
   533  	for i := 0; i < b.N; i++ {
   534  		_ = matcher.Apply(ctx)
   535  	}
   536  }