Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add two new rule types for parental control and fix a issue for subrule #1339

Open
wants to merge 7 commits into
base: Alpha
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions constant/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const (
AND
OR
NOT
SrcMAC
Schedule
)

type RuleType int
Expand Down Expand Up @@ -104,6 +106,10 @@ func (rt RuleType) String() string {
return "OR"
case NOT:
return "NOT"
case SrcMAC:
return "SrcMAC"
case Schedule:
return "Schedule"
default:
return "Unknown"
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ require (
github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ github.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs
github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
127 changes: 127 additions & 0 deletions rules/common/mac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package common

import (
"bytes"
"os/exec"
"regexp"
"runtime"
"strings"
"time"

C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/patrickmn/go-cache"
"golang.org/x/net/idna"
)

var lc = cache.New(5*time.Minute, 10*time.Minute)
var arpCommand = "arp"
var arpVar = "-a"

func init() {
switch os := runtime.GOOS; os {
case "linux":
arpCommand = "cat"
arpVar = "/proc/net/arp"
case "windows":

default:

}
}

type SrcMAC struct {
*Base
mac string
adapter string
}

func (d *SrcMAC) RuleType() C.RuleType {
return C.SrcMAC
}

func (d *SrcMAC) Match(metadata *C.Metadata) (bool, string) {
arpTable, err := getARPTable(false)
if err != nil {
log.Errorln("can't initial arp table: %s", err)
return false, ""
}

mac, exists := arpTable[metadata.SrcIP.String()]
if exists {
if mac == d.mac {
return true, d.adapter
}
} else {
arpTable, err := getARPTable(true)
if err != nil {
log.Errorln("can't initial arp table: %s", err)
return false, ""
}
mac, exists := arpTable[metadata.SrcIP.String()]
if exists {
if mac == d.mac {
return true, d.adapter
}
} else {
log.Errorln("can't find the IP address in arp table: %s", metadata.SrcIP.String())
}
}

return false, d.adapter
}

func (d *SrcMAC) Adapter() string {
return d.adapter
}

func (d *SrcMAC) Payload() string {
return d.mac
}

func NewMAC(mac string, adapter string) *SrcMAC {
punycode, _ := idna.ToASCII(strings.ToLower(mac))
return &SrcMAC{
Base: &Base{},
mac: punycode,
adapter: adapter,
}
}

func getARPTable(forceReload bool) (map[string]string, error) {

item, found := lc.Get("arpTable")
if found && !forceReload {
arpTable := item.(map[string]string)
//log.Infoln("get arpTable from cache")
return arpTable, nil
}

// 执行arp命令
cmd := exec.Command(arpCommand, arpVar)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return nil, err
}
ipRegex := regexp.MustCompile(`(([0-9]{1,3}\.){3}[0-9]{1,3})`)
macRegex := regexp.MustCompile(`(?i)(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}`)

// 解析arp命令的输出
arpTable := make(map[string]string)
lines := strings.Split(out.String(), "\n")
for _, line := range lines {
ip := ipRegex.FindString(line)
mac := macRegex.FindString(line)

if len(ip) > 0 && len(mac) > 0 {
punycode, _ := idna.ToASCII(strings.ToLower(mac))
arpTable[ip] = punycode
}
}
lc.Set("arpTable", arpTable, cache.DefaultExpiration)
return arpTable, nil
}

//var _ C.Rule = (*Mac)(nil)
117 changes: 117 additions & 0 deletions rules/common/schedule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package common

import (
"fmt"
"strconv"
"strings"
"time"

C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"golang.org/x/net/idna"
)

// make sure the system has install related zoneinfo for local time zone!
func init() {
log.Infoln("current system time is %s", time.Now().Format("2006-01-02 15:04:05"))
}

type Schedule struct {
*Base
weekDayArr [7]bool
startHour int
startMinute int
endHour int
endMinute int
schedule string
adapter string
}

func (d *Schedule) RuleType() C.RuleType {
return C.Schedule
}

func (d *Schedule) Match(metadata *C.Metadata) (bool, string) {
now := time.Now()
//log.Infoln("system time is %", now.Format("2006-01-02 15:04:05.000 Mon Jan"))
if d.weekDayArr[now.Weekday()] {
startTime := time.Date(now.Year(), now.Month(), now.Day(), d.startHour, d.startMinute, 0, 0, now.Location())
endTime := time.Date(now.Year(), now.Month(), now.Day(), d.endHour, d.endMinute, 59, 999999999, now.Location())
if now.After(startTime) && now.Before(endTime) {
//log.Infoln("src ip %s in the time %d:%d~%d:%d. adapter is %s.", metadata.SrcIP.String(), d.startHour, d.startMinute, d.endHour, d.endMinute, d.adapter)
return true, d.adapter
}
}
return false, d.adapter
}

func (d *Schedule) Adapter() string {
return d.adapter
}

func (d *Schedule) Payload() string {
return d.schedule
}

func NewSchedule(schedule string, adapter string) (*Schedule, error) {
punycode, _ := idna.ToASCII(strings.ToUpper(schedule))
weekDayArr := [7]bool{false, false, false, false, false, false, false}
if len(punycode) != 19 {
return nil, fmt.Errorf("could you initial Schedule rule %s, the rule format is not correct!", punycode)
}
if punycode[0] == 'S' {
weekDayArr[0] = true
}
if punycode[1] == 'M' {
weekDayArr[1] = true
}
if punycode[2] == 'T' {
weekDayArr[2] = true
}
if punycode[3] == 'W' {
weekDayArr[3] = true
}
if punycode[4] == 'T' {
weekDayArr[4] = true
}
if punycode[5] == 'F' {
weekDayArr[5] = true
}
if punycode[6] == 'S' {
weekDayArr[6] = true
}
startHour, err := strconv.Atoi(punycode[8:10])
if err != nil {
return nil, fmt.Errorf("could you initial Schedule rule %s, the time format is not correct!", punycode)
}
startMinute, err := strconv.Atoi(punycode[11:13])
if err != nil {
return nil, fmt.Errorf("could you initial Schedule rule %s, the time format is not correct!", punycode)
}
endHour, err := strconv.Atoi(punycode[14:16])
if err != nil {
return nil, fmt.Errorf("could you initial Schedule rule %s, the time format is not correct!", punycode)
}
endMinute, err := strconv.Atoi(punycode[17:19])
if err != nil {
return nil, fmt.Errorf("could you initial Schedule rule %s, the time format is not correct!", punycode)
}
if startHour > endHour {
return nil, fmt.Errorf("could you initial Schedule rule %s, the end time should not be earlier than start time!", punycode)
}
if startHour == endHour && startMinute > endMinute {
return nil, fmt.Errorf("could you initial Schedule rule %s, the end time should not be earlier than start time!", punycode)
}
return &Schedule{
Base: &Base{},
weekDayArr: weekDayArr,
startHour: startHour,
startMinute: startMinute,
endHour: endHour,
endMinute: endMinute,
schedule: punycode,
adapter: adapter,
}, nil
}

//var _ C.Rule = (*Schedule)(nil)
4 changes: 2 additions & 2 deletions rules/logic/logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"strings"
"sync"

list "github.com/bahlo/generic-list-go"

C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/rules/common"

list "github.com/bahlo/generic-list-go"
)

type Logic struct {
Expand Down
4 changes: 4 additions & 0 deletions rules/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
parsed, parseErr = logic.NewOR(payload, target, ParseRule)
case "NOT":
parsed, parseErr = logic.NewNOT(payload, target, ParseRule)
case "SRC-MAC":
parsed = RC.NewMAC(payload, target)
case "SCHEDULE":
parsed, parseErr = RC.NewSchedule(payload, target)
case "RULE-SET":
isSrc, noResolve := RC.ParseParams(params)
parsed, parseErr = RP.NewRuleSet(payload, target, isSrc, noResolve)
Expand Down