Skip to content

Commit

Permalink
feat: add mrs format domain ruleset
Browse files Browse the repository at this point in the history
  • Loading branch information
wwqgtxx committed Jul 26, 2024
1 parent 0d90a93 commit 303f6e4
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 26 deletions.
127 changes: 127 additions & 0 deletions component/trie/domain_set_bin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package trie

import (
"encoding/binary"
"errors"
"io"
)

func (ss *DomainSet) WriteBin(w io.Writer, count int64) (err error) {
// version
_, err = w.Write([]byte{1})
if err != nil {
return err
}

// count
err = binary.Write(w, binary.BigEndian, count)
if err != nil {
return err
}

// leaves
err = binary.Write(w, binary.BigEndian, int64(len(ss.leaves)))
if err != nil {
return err
}
for _, d := range ss.leaves {
err = binary.Write(w, binary.BigEndian, d)
if err != nil {
return err
}
}

// labelBitmap
err = binary.Write(w, binary.BigEndian, int64(len(ss.labelBitmap)))
if err != nil {
return err
}
for _, d := range ss.labelBitmap {
err = binary.Write(w, binary.BigEndian, d)
if err != nil {
return err
}
}

// labels
err = binary.Write(w, binary.BigEndian, int64(len(ss.labels)))
if err != nil {
return err
}
_, err = w.Write(ss.labels)
if err != nil {
return err
}

return nil
}

func ReadDomainSetBin(r io.Reader) (ds *DomainSet, count int64, err error) {
// version
version := make([]byte, 1)
_, err = io.ReadFull(r, version)
if err != nil {
return nil, 0, err
}
if version[0] != 1 {
return nil, 0, errors.New("version is invalid")
}

// count
err = binary.Read(r, binary.BigEndian, &count)
if err != nil {
return nil, 0, err
}

ds = &DomainSet{}
var length int64

// leaves
err = binary.Read(r, binary.BigEndian, &length)
if err != nil {
return nil, 0, err
}
if length < 1 {
return nil, 0, errors.New("length is invalid")
}
ds.leaves = make([]uint64, length)
for i := int64(0); i < length; i++ {
err = binary.Read(r, binary.BigEndian, &ds.leaves[i])
if err != nil {
return nil, 0, err
}
}

// labelBitmap
err = binary.Read(r, binary.BigEndian, &length)
if err != nil {
return nil, 0, err
}
if length < 1 {
return nil, 0, errors.New("length is invalid")
}
ds.labelBitmap = make([]uint64, length)
for i := int64(0); i < length; i++ {
err = binary.Read(r, binary.BigEndian, &ds.labelBitmap[i])
if err != nil {
return nil, 0, err
}
}

// labels
err = binary.Read(r, binary.BigEndian, &length)
if err != nil {
return nil, 0, err
}
if length < 1 {
return nil, 0, errors.New("length is invalid")
}
ds.labels = make([]byte, length)
_, err = io.ReadFull(r, ds.labels)
if err != nil {
return nil, 0, err
}

ds.init()
return ds, count, nil
}
33 changes: 33 additions & 0 deletions constant/provider/interface.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package provider

import (
"fmt"

"github.com/metacubex/mihomo/common/utils"
"github.com/metacubex/mihomo/constant"
)
Expand Down Expand Up @@ -110,9 +112,24 @@ func (rt RuleBehavior) String() string {
}
}

func ParseBehavior(s string) (behavior RuleBehavior, err error) {
switch s {
case "domain":
behavior = Domain
case "ipcidr":
behavior = IPCIDR
case "classical":
behavior = Classical
default:
err = fmt.Errorf("unsupported behavior type: %s", s)
}
return
}

const (
YamlRule RuleFormat = iota
TextRule
MrsRule
)

type RuleFormat int
Expand All @@ -123,11 +140,27 @@ func (rf RuleFormat) String() string {
return "YamlRule"
case TextRule:
return "TextRule"
case MrsRule:
return "MrsRule"
default:
return "Unknown"
}
}

func ParseRuleFormat(s string) (format RuleFormat, err error) {
switch s {
case "", "yaml":
format = YamlRule
case "text":
format = TextRule
case "mrs":
format = MrsRule
default:
err = fmt.Errorf("unsupported format type: %s", s)
}
return
}

type Tunnel interface {
Providers() map[string]ProxyProvider
RuleProviders() map[string]RuleProvider
Expand Down
6 changes: 6 additions & 0 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,12 @@ rule-providers:
interval: 259200
path: /path/to/save/file.yaml
type: file
rule3: # mrs类型ruleset,目前仅支持domain,可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换得到
type: http
url: "url"
format: mrs
behavior: domain
path: /path/to/save/file.mrs
rules:
- RULE-SET,rule1,REJECT
- IP-ASN,1,PROXY
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/gobwas/ws v1.4.0
github.com/gofrs/uuid/v5 v5.2.0
github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6
github.com/klauspost/compress v1.17.9
github.com/klauspost/cpuid/v2 v2.2.8
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/mdlayher/netlink v1.7.2
Expand Down Expand Up @@ -82,7 +83,6 @@ require (
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
Expand Down
7 changes: 7 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/metacubex/mihomo/hub"
"github.com/metacubex/mihomo/hub/executor"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/rules/provider"

"go.uber.org/automaxprocs/maxprocs"
)
Expand Down Expand Up @@ -48,6 +49,12 @@ func init() {

func main() {
_, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))

if len(os.Args) > 1 && os.Args[1] == "convert-ruleset" {
provider.ConvertMain(os.Args[2:])
return
}

if version {
fmt.Printf("Mihomo Meta %s %s %s with %s %s\n",
C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime)
Expand Down
22 changes: 22 additions & 0 deletions rules/provider/domain_strategy.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package provider

import (
"errors"
"io"

"github.com/metacubex/mihomo/component/trie"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
Expand Down Expand Up @@ -48,6 +51,25 @@ func (d *domainStrategy) FinishInsert() {
d.domainTrie = nil
}

func (d *domainStrategy) FromMrs(r io.Reader) error {
domainSet, count, err := trie.ReadDomainSetBin(r)
if err != nil {
return err
}
d.count = int(count)
d.domainSet = domainSet
return nil
}

func (d *domainStrategy) WriteMrs(w io.Writer) error {
if d.domainSet == nil {
return errors.New("nil domainSet")
}
return d.domainSet.WriteBin(w, int64(d.count))
}

var _ mrsRuleStrategy = (*domainStrategy)(nil)

func NewDomainStrategy() *domainStrategy {
return &domainStrategy{}
}
71 changes: 71 additions & 0 deletions rules/provider/mrs_converter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package provider

import (
"io"
"os"

P "github.com/metacubex/mihomo/constant/provider"

"github.com/klauspost/compress/zstd"
)

func ConvertToMrs(buf []byte, behavior P.RuleBehavior, format P.RuleFormat, w io.Writer) (err error) {
strategy := newStrategy(behavior, nil)
strategy, err = rulesParse(buf, strategy, format)
if err != nil {
return err
}
if _strategy, ok := strategy.(mrsRuleStrategy); ok {
var encoder *zstd.Encoder
encoder, err = zstd.NewWriter(w)
if err != nil {
return err
}
defer func() {
zstdErr := encoder.Close()
if err == nil {
err = zstdErr
}
}()
return _strategy.WriteMrs(encoder)
} else {
return ErrInvalidFormat
}
}

func ConvertMain(args []string) {
if len(args) > 3 {
behavior, err := P.ParseBehavior(args[0])
if err != nil {
panic(err)
}
format, err := P.ParseRuleFormat(args[1])
if err != nil {
panic(err)
}
source := args[2]
target := args[3]

sourceFile, err := os.ReadFile(source)
if err != nil {
panic(err)
}

targetFile, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
panic(err)
}

err = ConvertToMrs(sourceFile, behavior, format, targetFile)
if err != nil {
panic(err)
}

err = targetFile.Close()
if err != nil {
panic(err)
}
} else {
panic("Usage: convert-ruleset <behavior> <format> <source file> <target file>")
}
}
27 changes: 6 additions & 21 deletions rules/provider/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,13 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t
if err := decoder.Decode(mapping, schema); err != nil {
return nil, err
}
var behavior P.RuleBehavior

switch schema.Behavior {
case "domain":
behavior = P.Domain
case "ipcidr":
behavior = P.IPCIDR
case "classical":
behavior = P.Classical
default:
return nil, fmt.Errorf("unsupported behavior type: %s", schema.Behavior)
behavior, err := P.ParseBehavior(schema.Behavior)
if err != nil {
return nil, err
}

var format P.RuleFormat

switch schema.Format {
case "", "yaml":
format = P.YamlRule
case "text":
format = P.TextRule
default:
return nil, fmt.Errorf("unsupported format type: %s", schema.Format)
format, err := P.ParseRuleFormat(schema.Format)
if err != nil {
return nil, err
}

var vehicle P.Vehicle
Expand Down
Loading

0 comments on commit 303f6e4

Please sign in to comment.