add address bypass
This commit is contained in:
parent
a1de1af1bd
commit
74ed44d87d
130
bypass.go
Normal file
130
bypass.go
Normal file
@ -0,0 +1,130 @@
|
||||
package gost
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
glob "github.com/gobwas/glob"
|
||||
)
|
||||
|
||||
// Matcher is a generic pattern matcher,
|
||||
// it gives the match result of the given pattern for specific v.
|
||||
type Matcher interface {
|
||||
Match(v string) bool
|
||||
}
|
||||
|
||||
// NewMatcher creates a Matcher for the given pattern.
|
||||
// The acutal Matcher depends on the pattern:
|
||||
// IP Matcher if pattern is a valid IP address.
|
||||
// CIDR Matcher if pattern is a valid CIDR address.
|
||||
// Domain Matcher if both of the above are not.
|
||||
func NewMatcher(pattern string) Matcher {
|
||||
if pattern == "" {
|
||||
return nil
|
||||
}
|
||||
if ip := net.ParseIP(pattern); ip != nil {
|
||||
return IPMatcher(ip)
|
||||
}
|
||||
if _, inet, err := net.ParseCIDR(pattern); err == nil {
|
||||
return CIDRMatcher(inet)
|
||||
}
|
||||
return DomainMatcher(pattern)
|
||||
}
|
||||
|
||||
type ipMatcher struct {
|
||||
ip net.IP
|
||||
}
|
||||
|
||||
// IPMatcher creates a Matcher for a specific IP address.
|
||||
func IPMatcher(ip net.IP) Matcher {
|
||||
return &ipMatcher{
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ipMatcher) Match(ip string) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
return m.ip.Equal(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
type cidrMatcher struct {
|
||||
ipNet *net.IPNet
|
||||
}
|
||||
|
||||
// CIDRMatcher creates a Matcher for a specific CIDR notation IP address.
|
||||
func CIDRMatcher(inet *net.IPNet) Matcher {
|
||||
return &cidrMatcher{
|
||||
ipNet: inet,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *cidrMatcher) Match(ip string) bool {
|
||||
if m == nil || m.ipNet == nil {
|
||||
return false
|
||||
}
|
||||
return m.ipNet.Contains(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
type domainMatcher struct {
|
||||
glob glob.Glob
|
||||
}
|
||||
|
||||
// DomainMatcher creates a Matcher for a specific domain pattern,
|
||||
// the pattern can be a plain domain such as 'example.com'
|
||||
// or a wildcard such as '*.exmaple.com'.
|
||||
func DomainMatcher(pattern string) Matcher {
|
||||
return &domainMatcher{
|
||||
glob: glob.MustCompile(pattern),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *domainMatcher) Match(domain string) bool {
|
||||
if m == nil || m.glob == nil {
|
||||
return false
|
||||
}
|
||||
return m.glob.Match(domain)
|
||||
}
|
||||
|
||||
// Bypass is a filter for address (IP or domain).
|
||||
// It contains a list of matchers.
|
||||
type Bypass struct {
|
||||
matchers []Matcher
|
||||
reverse bool
|
||||
}
|
||||
|
||||
// NewBypass creates and initializes a new Bypass using matchers as its match rules.
|
||||
// The rules will be reversed if the reversed is true.
|
||||
func NewBypass(matchers []Matcher, reverse bool) *Bypass {
|
||||
return &Bypass{
|
||||
matchers: matchers,
|
||||
reverse: reverse,
|
||||
}
|
||||
}
|
||||
|
||||
// NewBypassPatterns creates and initializes a new Bypass using matcher patterns as its match rules.
|
||||
// The rules will be reversed if the reverse is true.
|
||||
func NewBypassPatterns(patterns []string, reverse bool) *Bypass {
|
||||
var matchers []Matcher
|
||||
for _, pattern := range patterns {
|
||||
if pattern != "" {
|
||||
matchers = append(matchers, NewMatcher(pattern))
|
||||
}
|
||||
}
|
||||
return NewBypass(matchers, reverse)
|
||||
}
|
||||
|
||||
// Contains reports whether the bypass includes addr.
|
||||
func (bp *Bypass) Contains(addr string) bool {
|
||||
for _, matcher := range bp.matchers {
|
||||
if matcher == nil {
|
||||
continue
|
||||
}
|
||||
matched := matcher.Match(addr)
|
||||
if (matched && !bp.reverse) ||
|
||||
(!matched && bp.reverse) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
91
bypass_test.go
Normal file
91
bypass_test.go
Normal file
@ -0,0 +1,91 @@
|
||||
package gost
|
||||
|
||||
import "testing"
|
||||
|
||||
var bypassTests = []struct {
|
||||
patterns []string
|
||||
reversed bool
|
||||
addr string
|
||||
bypassed bool
|
||||
}{
|
||||
// IP address
|
||||
{[]string{"192.168.1.1"}, false, "192.168.1.1", true},
|
||||
{[]string{"192.168.1.1"}, false, "192.168.1.2", false},
|
||||
{[]string{"0.0.0.0"}, false, "0.0.0.0", true},
|
||||
|
||||
// CIDR address
|
||||
{[]string{"192.168.1.0/0"}, false, "1.2.3.4", true},
|
||||
{[]string{"192.168.1.0/8"}, false, "192.1.0.255", true},
|
||||
{[]string{"192.168.1.0/16"}, false, "192.168.0.255", true},
|
||||
{[]string{"192.168.1.0/24"}, false, "192.168.1.255", true},
|
||||
{[]string{"192.168.1.1/32"}, false, "192.168.1.1", true},
|
||||
{[]string{"192.168.1.1/32"}, false, "192.168.1.2", false},
|
||||
|
||||
// plain domain
|
||||
{[]string{"www.example.com"}, false, "www.example.com", true},
|
||||
{[]string{"http://www.example.com"}, false, "http://www.example.com", true},
|
||||
{[]string{"http://www.example.com"}, false, "http://example.com", false},
|
||||
{[]string{"www.example.com"}, false, "example.com", false},
|
||||
|
||||
// domain wildcard
|
||||
|
||||
// sub-domain
|
||||
{[]string{"*.example.com"}, false, "example.com", false},
|
||||
{[]string{"*.example.com"}, false, "http://example.com", false},
|
||||
{[]string{"*.example.com"}, false, "www.example.com", true},
|
||||
{[]string{"*.example.com"}, false, "http://www.example.com", true},
|
||||
{[]string{"*.example.com"}, false, "abc.def.example.com", true},
|
||||
|
||||
{[]string{"*.*.example.com"}, false, "example.com", false},
|
||||
{[]string{"*.*.example.com"}, false, "www.example.com", false},
|
||||
{[]string{"*.*.example.com"}, false, "abc.def.example.com", true},
|
||||
{[]string{"*.*.example.com"}, false, "abc.def.ghi.example.com", true},
|
||||
|
||||
{[]string{"**.example.com"}, false, "example.com", false},
|
||||
{[]string{"**.example.com"}, false, "www.example.com", true},
|
||||
{[]string{"**.example.com"}, false, "abc.def.ghi.example.com", true},
|
||||
|
||||
// prefix wildcard
|
||||
{[]string{"*example.com"}, false, "example.com", true},
|
||||
{[]string{"*example.com"}, false, "www.example.com", true},
|
||||
{[]string{"*example.com"}, false, "abc.defexample.com", true},
|
||||
{[]string{"*example.com"}, false, "abc.def-example.com", true},
|
||||
{[]string{"*example.com"}, false, "abc.def.example.com", true},
|
||||
{[]string{"*example.com"}, false, "http://www.example.com", true},
|
||||
{[]string{"*example.com"}, false, "e-xample.com", false},
|
||||
|
||||
{[]string{"http://*.example.com"}, false, "example.com", false},
|
||||
{[]string{"http://*.example.com"}, false, "http://example.com", false},
|
||||
{[]string{"http://*.example.com"}, false, "http://www.example.com", true},
|
||||
{[]string{"http://*.example.com"}, false, "https://www.example.com", false},
|
||||
{[]string{"http://*.example.com"}, false, "http://abc.def.example.com", true},
|
||||
|
||||
{[]string{"www.*.com"}, false, "www.example.com", true},
|
||||
{[]string{"www.*.com"}, false, "www.abc.def.com", true},
|
||||
|
||||
{[]string{"www.*.*.com"}, false, "www.example.com", false},
|
||||
{[]string{"www.*.*.com"}, false, "www.abc.def.com", true},
|
||||
{[]string{"www.*.*.com"}, false, "www.abc.def.ghi.com", true},
|
||||
|
||||
{[]string{"www.*example*.com"}, false, "www.example.com", true},
|
||||
{[]string{"www.*example*.com"}, false, "www.abc.example.def.com", true},
|
||||
{[]string{"www.*example*.com"}, false, "www.e-xample.com", false},
|
||||
|
||||
{[]string{"www.example.*"}, false, "www.example.com", true},
|
||||
{[]string{"www.example.*"}, false, "www.example.io", true},
|
||||
{[]string{"www.example.*"}, false, "www.example.com.cn", true},
|
||||
}
|
||||
|
||||
func TestBypass(t *testing.T) {
|
||||
for i, test := range bypassTests {
|
||||
bp := NewBypassPatterns(test.patterns, test.reversed)
|
||||
if bp.Contains(test.addr) != test.bypassed {
|
||||
t.Errorf("test %d failed", i)
|
||||
}
|
||||
|
||||
rbp := NewBypassPatterns(test.patterns, !test.reversed)
|
||||
if rbp.Contains(test.addr) == test.bypassed {
|
||||
t.Errorf("reverse test %d failed", i)
|
||||
}
|
||||
}
|
||||
}
|
2
gost.go
2
gost.go
@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// Version is the gost version.
|
||||
const Version = "2.5"
|
||||
const Version = "2.6-dev"
|
||||
|
||||
// Debug is a flag that enables the debug log.
|
||||
var Debug bool
|
||||
|
@ -35,7 +35,7 @@ func TestParseNode(t *testing.T) {
|
||||
actual, err := ParseNode(test.in)
|
||||
if err != nil {
|
||||
if test.hasError {
|
||||
t.Logf("ParseNode(%q) got expected error: %v", test.in, err)
|
||||
// t.Logf("ParseNode(%q) got expected error: %v", test.in, err)
|
||||
continue
|
||||
}
|
||||
t.Errorf("ParseNode(%q) got error: %v", test.in, err)
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: gost
|
||||
version: '2.5'
|
||||
version: '2.6'
|
||||
summary: GO Simple Tunnel
|
||||
description: |
|
||||
A simple tunnel written in golang
|
||||
|
Loading…
Reference in New Issue
Block a user