mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-12-19 16:30:07 +08:00
128 lines
2.5 KiB
Go
128 lines
2.5 KiB
Go
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)
|