实现WAF策略部分功能

This commit is contained in:
刘祥超
2020-10-06 21:02:21 +08:00
parent 0a3c46ac0f
commit 88d4aa183a
33 changed files with 3086 additions and 252 deletions

View File

@@ -0,0 +1,147 @@
package shared
import (
"bytes"
"errors"
"github.com/iwind/TeaGo/utils/string"
"net"
"regexp"
"strings"
)
// IP Range类型
type IPRangeType = int
const (
IPRangeTypeRange IPRangeType = 1
IPRangeTypeCIDR IPRangeType = 2
IPRangeTypeAll IPRangeType = 3
IPRangeTypeWildcard IPRangeType = 4 // 通配符,可以使用*
)
// IP Range
type IPRangeConfig struct {
Id string `yaml:"id" json:"id"`
Type IPRangeType `yaml:"type" json:"type"`
Param string `yaml:"param" json:"param"`
CIDR string `yaml:"cidr" json:"cidr"`
IPFrom string `yaml:"ipFrom" json:"ipFrom"`
IPTo string `yaml:"ipTo" json:"ipTo"`
cidr *net.IPNet
ipFrom net.IP
ipTo net.IP
reg *regexp.Regexp
}
// 获取新对象
func NewIPRangeConfig() *IPRangeConfig {
return &IPRangeConfig{
Id: stringutil.Rand(16),
}
}
// 从字符串中分析
func ParseIPRange(s string) (*IPRangeConfig, error) {
if len(s) == 0 {
return nil, errors.New("invalid ip range")
}
ipRange := &IPRangeConfig{}
if s == "*" || s == "all" || s == "ALL" || s == "0.0.0.0" {
ipRange.Type = IPRangeTypeAll
return ipRange, nil
}
if strings.Contains(s, "/") {
ipRange.Type = IPRangeTypeCIDR
ipRange.CIDR = strings.Replace(s, " ", "", -1)
} else if strings.Contains(s, "-") {
ipRange.Type = IPRangeTypeRange
pieces := strings.SplitN(s, "-", 2)
ipRange.IPFrom = strings.TrimSpace(pieces[0])
ipRange.IPTo = strings.TrimSpace(pieces[1])
} else if strings.Contains(s, ",") {
ipRange.Type = IPRangeTypeRange
pieces := strings.SplitN(s, ",", 2)
ipRange.IPFrom = strings.TrimSpace(pieces[0])
ipRange.IPTo = strings.TrimSpace(pieces[1])
} else if strings.Contains(s, "*") {
ipRange.Type = IPRangeTypeWildcard
s = "^" + strings.Replace(regexp.QuoteMeta(s), `\*`, `\d+`, -1) + "$"
ipRange.reg = regexp.MustCompile(s)
} else {
ipRange.Type = IPRangeTypeRange
ipRange.IPFrom = s
ipRange.IPTo = s
}
err := ipRange.Validate()
if err != nil {
return nil, err
}
return ipRange, nil
}
// 校验
func (this *IPRangeConfig) Validate() error {
if this.Type == IPRangeTypeCIDR {
if len(this.CIDR) == 0 {
return errors.New("cidr should not be empty")
}
_, cidr, err := net.ParseCIDR(this.CIDR)
if err != nil {
return err
}
this.cidr = cidr
}
if this.Type == IPRangeTypeRange {
this.ipFrom = net.ParseIP(this.IPFrom)
this.ipTo = net.ParseIP(this.IPTo)
if this.ipFrom.To4() == nil && this.ipFrom.To16() == nil {
return errors.New("from ip should in IPv4 or IPV6 format")
}
if this.ipTo.To4() == nil && this.ipTo.To16() == nil {
return errors.New("to ip should in IPv4 or IPV6 format")
}
}
return nil
}
// 是否包含某个IP
func (this *IPRangeConfig) Contains(ipString string) bool {
ip := net.ParseIP(ipString)
if ip.To4() == nil {
return false
}
if this.Type == IPRangeTypeCIDR {
if this.cidr == nil {
return false
}
return this.cidr.Contains(ip)
}
if this.Type == IPRangeTypeRange {
if this.ipFrom == nil || this.ipTo == nil {
return false
}
return bytes.Compare(ip, this.ipFrom) >= 0 && bytes.Compare(ip, this.ipTo) <= 0
}
if this.Type == IPRangeTypeWildcard {
if this.reg == nil {
return false
}
return this.reg.MatchString(ipString)
}
if this.Type == IPRangeTypeAll {
return true
}
return false
}

View File

@@ -0,0 +1,125 @@
package shared
import (
"github.com/iwind/TeaGo/assert"
"testing"
)
func TestGeoConfig_Contains(t *testing.T) {
a := assert.NewAssertion(t)
{
geo := NewIPRangeConfig()
geo.Type = IPRangeTypeRange
geo.IPFrom = "192.168.1.100"
geo.IPTo = "192.168.1.110"
a.IsNil(geo.Validate())
a.IsTrue(geo.Contains("192.168.1.100"))
a.IsTrue(geo.Contains("192.168.1.101"))
a.IsTrue(geo.Contains("192.168.1.110"))
a.IsFalse(geo.Contains("192.168.1.111"))
}
{
geo := NewIPRangeConfig()
geo.Type = IPRangeTypeCIDR
geo.CIDR = "192.168.1.1/24"
a.IsNil(geo.Validate())
a.IsTrue(geo.Contains("192.168.1.100"))
a.IsFalse(geo.Contains("192.168.2.100"))
}
{
geo := NewIPRangeConfig()
geo.Type = IPRangeTypeCIDR
geo.CIDR = "192.168.1.1/16"
a.IsNil(geo.Validate())
a.IsTrue(geo.Contains("192.168.2.100"))
}
}
func TestParseIPRange(t *testing.T) {
a := assert.NewAssertion(t)
{
_, err := ParseIPRange("")
a.IsNotNil(err)
}
{
r, err := ParseIPRange("192.168.1.100")
a.IsNil(err)
a.IsTrue(r.IPFrom == r.IPTo)
a.IsTrue(r.IPFrom == "192.168.1.100")
a.IsTrue(r.Contains("192.168.1.100"))
a.IsFalse(r.Contains("192.168.1.99"))
}
{
r, err := ParseIPRange("192.168.1.100/24")
a.IsNil(err)
a.IsTrue(r.CIDR == "192.168.1.100/24")
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.1.99"))
a.IsFalse(r.Contains("192.168.2.100"))
}
{
r, err := ParseIPRange("192.168.1.100, 192.168.1.200")
a.IsNil(err)
a.IsTrue(r.IPFrom == "192.168.1.100")
a.IsTrue(r.IPTo == "192.168.1.200")
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.1.150"))
a.IsFalse(r.Contains("192.168.2.100"))
}
{
r, err := ParseIPRange("192.168.1.100-192.168.1.200")
a.IsNil(err)
a.IsTrue(r.IPFrom == "192.168.1.100")
a.IsTrue(r.IPTo == "192.168.1.200")
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.1.150"))
a.IsFalse(r.Contains("192.168.2.100"))
}
{
r, err := ParseIPRange("all")
a.IsNil(err)
a.IsTrue(r.Type == IPRangeTypeAll)
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.1.150"))
a.IsTrue(r.Contains("192.168.2.100"))
}
{
r, err := ParseIPRange("192.168.1.*")
a.IsNil(err)
if r != nil {
a.IsTrue(r.Type == IPRangeTypeWildcard)
a.IsTrue(r.Contains("192.168.1.100"))
a.IsFalse(r.Contains("192.168.2.100"))
}
}
{
r, err := ParseIPRange("192.168.*.*")
a.IsNil(err)
if r != nil {
a.IsTrue(r.Type == IPRangeTypeWildcard)
a.IsTrue(r.Contains("192.168.1.100"))
a.IsTrue(r.Contains("192.168.2.100"))
}
}
}
func BenchmarkIPRangeConfig_Contains(b *testing.B) {
r, err := ParseIPRange("192.168.1.*")
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ {
_ = r.Contains("192.168.1.100")
}
}