mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-05 01:20:26 +08:00
IP范围支持多行
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/checkpoints"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/utils"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/values"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
@@ -47,6 +48,10 @@ type Rule struct {
|
||||
isIP bool
|
||||
ipValue net.IP
|
||||
|
||||
ipRangeListValue *values.IPRangeList
|
||||
numberListValue *values.NumberList
|
||||
stringListValue *values.StringList
|
||||
|
||||
floatValue float64
|
||||
reg *re.Regexp
|
||||
}
|
||||
@@ -104,32 +109,7 @@ func (this *Rule) Init() error {
|
||||
return errors.New("value should be a valid ip")
|
||||
}
|
||||
case RuleOperatorIPRange, RuleOperatorNotIPRange:
|
||||
if strings.Contains(this.Value, ",") {
|
||||
ipList := strings.SplitN(this.Value, ",", 2)
|
||||
ipString1 := strings.TrimSpace(ipList[0])
|
||||
ipString2 := strings.TrimSpace(ipList[1])
|
||||
|
||||
if len(ipString1) > 0 {
|
||||
ip1 := net.ParseIP(ipString1)
|
||||
if ip1 == nil {
|
||||
return errors.New("start ip is invalid")
|
||||
}
|
||||
}
|
||||
|
||||
if len(ipString2) > 0 {
|
||||
ip2 := net.ParseIP(ipString2)
|
||||
if ip2 == nil {
|
||||
return errors.New("end ip is invalid")
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(this.Value, "/") {
|
||||
_, _, err := net.ParseCIDR(this.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New("invalid ip range")
|
||||
}
|
||||
this.ipRangeListValue = values.ParseIPRangeList(this.Value)
|
||||
}
|
||||
|
||||
if singleParamRegexp.MatchString(this.Param) {
|
||||
@@ -643,50 +623,11 @@ func (this *Rule) unescape(v string) string {
|
||||
return v
|
||||
}
|
||||
|
||||
func (this *Rule) containsIP(value interface{}) bool {
|
||||
ip := net.ParseIP(types.String(value))
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查IP范围格式
|
||||
if strings.Contains(this.Value, ",") {
|
||||
ipList := strings.SplitN(this.Value, ",", 2)
|
||||
ipString1 := strings.TrimSpace(ipList[0])
|
||||
ipString2 := strings.TrimSpace(ipList[1])
|
||||
|
||||
if len(ipString1) > 0 {
|
||||
ip1 := net.ParseIP(ipString1)
|
||||
if ip1 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if bytes.Compare(ip, ip1) < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(ipString2) > 0 {
|
||||
ip2 := net.ParseIP(ipString2)
|
||||
if ip2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if bytes.Compare(ip, ip2) > 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
} else if strings.Contains(this.Value, "/") {
|
||||
_, ipNet, err := net.ParseCIDR(this.Value)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return ipNet.Contains(ip)
|
||||
} else {
|
||||
func (this *Rule) containsIP(value any) bool {
|
||||
if this.ipRangeListValue == nil {
|
||||
return false
|
||||
}
|
||||
return this.ipRangeListValue.Contains(types.String(value))
|
||||
}
|
||||
|
||||
func (this *Rule) ipToInt64(ip net.IP) int64 {
|
||||
|
||||
7
internal/waf/values/ip_list.go
Normal file
7
internal/waf/values/ip_list.go
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package values
|
||||
|
||||
func ParseIPList(v string) *StringList {
|
||||
return ParseStringList(v)
|
||||
}
|
||||
26
internal/waf/values/ip_list_test.go
Normal file
26
internal/waf/values/ip_list_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package values_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/values"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseIPList(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
{
|
||||
var list = values.ParseIPList("")
|
||||
a.IsFalse(list.Contains("192.168.1.100"))
|
||||
}
|
||||
|
||||
{
|
||||
var list = values.ParseIPList(`
|
||||
192.168.1.1
|
||||
192.168.1.101`)
|
||||
a.IsFalse(list.Contains("192.168.1.100"))
|
||||
a.IsTrue(list.Contains("192.168.1.101"))
|
||||
}
|
||||
}
|
||||
132
internal/waf/values/ip_range.go
Normal file
132
internal/waf/values/ip_range.go
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package values
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IPRangeType = string
|
||||
|
||||
const (
|
||||
IPRangeTypeCIDR IPRangeType = "cidr" // CIDR
|
||||
IPRangeTypeSingeIP IPRangeType = "singleIP" // 单个IP
|
||||
IPRangeTypeRange IPRangeType = "range" // IP范围,IP1-IP2
|
||||
)
|
||||
|
||||
type IPRange struct {
|
||||
Type IPRangeType
|
||||
CIDR *net.IPNet
|
||||
IPFrom net.IP
|
||||
IPTo net.IP
|
||||
}
|
||||
|
||||
func (this *IPRange) Contains(netIP net.IP) bool {
|
||||
if netIP == nil {
|
||||
return false
|
||||
}
|
||||
switch this.Type {
|
||||
case IPRangeTypeCIDR:
|
||||
if this.CIDR != nil {
|
||||
return this.CIDR.Contains(netIP)
|
||||
}
|
||||
case IPRangeTypeSingeIP:
|
||||
if this.IPFrom != nil {
|
||||
return bytes.Equal(this.IPFrom, netIP)
|
||||
}
|
||||
case IPRangeTypeRange:
|
||||
return bytes.Compare(this.IPFrom, netIP) <= 0 && bytes.Compare(this.IPTo, netIP) >= 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type IPRangeList struct {
|
||||
Ranges []*IPRange
|
||||
}
|
||||
|
||||
func NewIPRangeList() *IPRangeList {
|
||||
return &IPRangeList{}
|
||||
}
|
||||
|
||||
func ParseIPRangeList(value string) *IPRangeList {
|
||||
var list = NewIPRangeList()
|
||||
|
||||
if len(value) == 0 {
|
||||
return list
|
||||
}
|
||||
|
||||
var lines = strings.Split(value, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(line, ",") { // IPFrom,IPTo
|
||||
var pieces = strings.SplitN(line, ",", 2)
|
||||
if len(pieces) == 2 {
|
||||
var ipFrom = net.ParseIP(strings.TrimSpace(pieces[0]))
|
||||
var ipTo = net.ParseIP(strings.TrimSpace(pieces[1]))
|
||||
if ipFrom != nil && ipTo != nil {
|
||||
if bytes.Compare(ipFrom, ipTo) > 0 {
|
||||
ipFrom, ipTo = ipTo, ipFrom
|
||||
}
|
||||
list.Ranges = append(list.Ranges, &IPRange{
|
||||
Type: IPRangeTypeRange,
|
||||
IPFrom: ipFrom,
|
||||
IPTo: ipTo,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(line, "-") { // IPFrom-IPTo
|
||||
var pieces = strings.SplitN(line, "-", 2)
|
||||
if len(pieces) == 2 {
|
||||
var ipFrom = net.ParseIP(strings.TrimSpace(pieces[0]))
|
||||
var ipTo = net.ParseIP(strings.TrimSpace(pieces[1]))
|
||||
if ipFrom != nil && ipTo != nil {
|
||||
if bytes.Compare(ipFrom, ipTo) > 0 {
|
||||
ipFrom, ipTo = ipTo, ipFrom
|
||||
}
|
||||
list.Ranges = append(list.Ranges, &IPRange{
|
||||
Type: IPRangeTypeRange,
|
||||
IPFrom: ipFrom,
|
||||
IPTo: ipTo,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(line, "/") { // CIDR
|
||||
_, cidr, _ := net.ParseCIDR(line)
|
||||
if cidr != nil {
|
||||
list.Ranges = append(list.Ranges, &IPRange{
|
||||
Type: IPRangeTypeCIDR,
|
||||
CIDR: cidr,
|
||||
})
|
||||
}
|
||||
} else { // single ip
|
||||
var netIP = net.ParseIP(line)
|
||||
if netIP != nil {
|
||||
list.Ranges = append(list.Ranges, &IPRange{
|
||||
Type: IPRangeTypeSingeIP,
|
||||
IPFrom: netIP,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func (this *IPRangeList) Contains(ip string) bool {
|
||||
var netIP = net.ParseIP(ip)
|
||||
if netIP == nil {
|
||||
return false
|
||||
}
|
||||
for _, r := range this.Ranges {
|
||||
if r.Contains(netIP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
54
internal/waf/values/ip_range_test.go
Normal file
54
internal/waf/values/ip_range_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package values_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/values"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIPRange_ParseIPRangeList(t *testing.T) {
|
||||
{
|
||||
var r = values.ParseIPRangeList("")
|
||||
logs.PrintAsJSON(r, t)
|
||||
}
|
||||
{
|
||||
var r = values.ParseIPRangeList("192.168.2.1")
|
||||
logs.PrintAsJSON(r, t)
|
||||
}
|
||||
{
|
||||
var r = values.ParseIPRangeList(`192.168.2.1
|
||||
192.168.1.100/24
|
||||
192.168.1.1-192.168.2.100
|
||||
192.168.1.2,192.168.2.200
|
||||
192.168.2.200 - 192.168.2.100
|
||||
# 以下是错误的
|
||||
192.168
|
||||
192.168.100.1-1`)
|
||||
logs.PrintAsJSON(r, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPRange_Contains(t *testing.T) {
|
||||
{
|
||||
var a = assert.NewAssertion(t)
|
||||
var r = values.ParseIPRangeList(`192.168.2.1
|
||||
192.168.1.100/24
|
||||
192.168.1.1-192.168.2.100
|
||||
192.168.2.2,192.168.2.200
|
||||
192.168.3.200 - 192.168.3.100
|
||||
192.168.4.100
|
||||
192.168.5.1/26`)
|
||||
a.IsTrue(r.Contains("192.168.1.102"))
|
||||
a.IsTrue(r.Contains("192.168.2.101"))
|
||||
a.IsTrue(r.Contains("192.168.1.1"))
|
||||
a.IsTrue(r.Contains("192.168.2.100"))
|
||||
a.IsFalse(r.Contains("192.168.2.201"))
|
||||
a.IsTrue(r.Contains("192.168.3.101"))
|
||||
a.IsTrue(r.Contains("192.168.4.100"))
|
||||
a.IsTrue(r.Contains("192.168.5.63"))
|
||||
a.IsFalse(r.Contains("192.168.5.128"))
|
||||
}
|
||||
}
|
||||
48
internal/waf/values/number_list.go
Normal file
48
internal/waf/values/number_list.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package values
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type NumberList struct {
|
||||
ValueMap map[float64]zero.Zero
|
||||
}
|
||||
|
||||
func NewNumberList() *NumberList {
|
||||
return &NumberList{
|
||||
ValueMap: map[float64]zero.Zero{},
|
||||
}
|
||||
}
|
||||
|
||||
func ParseNumberList(v string) *NumberList {
|
||||
var list = NewNumberList()
|
||||
if len(v) == 0 {
|
||||
return list
|
||||
}
|
||||
|
||||
var lines = strings.Split(v, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var values = strings.Split(line, ",")
|
||||
for _, value := range values {
|
||||
value = strings.TrimSpace(value)
|
||||
if len(value) > 0 {
|
||||
list.ValueMap[types.Float64(value)] = zero.Zero{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (this *NumberList) Contains(f float64) bool {
|
||||
_, ok := this.ValueMap[f]
|
||||
return ok
|
||||
}
|
||||
29
internal/waf/values/number_list_test.go
Normal file
29
internal/waf/values/number_list_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package values_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/values"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseNumberList(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
{
|
||||
var list = values.ParseNumberList("")
|
||||
a.IsFalse(list.Contains(123))
|
||||
}
|
||||
|
||||
{
|
||||
var list = values.ParseNumberList(`123
|
||||
456
|
||||
|
||||
789.1234`)
|
||||
a.IsTrue(list.Contains(123))
|
||||
a.IsFalse(list.Contains(0))
|
||||
a.IsFalse(list.Contains(789.123))
|
||||
a.IsTrue(list.Contains(789.1234))
|
||||
}
|
||||
}
|
||||
47
internal/waf/values/string_list.go
Normal file
47
internal/waf/values/string_list.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package values
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StringList struct {
|
||||
ValueMap map[string]zero.Zero
|
||||
}
|
||||
|
||||
func NewStringList() *StringList {
|
||||
return &StringList{
|
||||
ValueMap: map[string]zero.Zero{},
|
||||
}
|
||||
}
|
||||
|
||||
func ParseStringList(v string) *StringList {
|
||||
var list = NewStringList()
|
||||
if len(v) == 0 {
|
||||
return list
|
||||
}
|
||||
|
||||
var lines = strings.Split(v, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var values = strings.Split(line, ",")
|
||||
for _, value := range values {
|
||||
value = strings.TrimSpace(value)
|
||||
if len(value) > 0 {
|
||||
list.ValueMap[value] = zero.Zero{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (this *StringList) Contains(f string) bool {
|
||||
_, ok := this.ValueMap[f]
|
||||
return ok
|
||||
}
|
||||
30
internal/waf/values/string_list_test.go
Normal file
30
internal/waf/values/string_list_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package values_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/values"
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseStringList(t *testing.T) {
|
||||
var a = assert.NewAssertion(t)
|
||||
|
||||
{
|
||||
var list = values.ParseStringList("")
|
||||
a.IsFalse(list.Contains("hello"))
|
||||
}
|
||||
|
||||
{
|
||||
var list = values.ParseStringList(`hello
|
||||
|
||||
world
|
||||
hi
|
||||
|
||||
people`)
|
||||
a.IsTrue(list.Contains("hello"))
|
||||
a.IsFalse(list.Contains("hello1"))
|
||||
a.IsTrue(list.Contains("hi"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user