Skip to content

Commit

Permalink
Draft rule set support
Browse files Browse the repository at this point in the history
  • Loading branch information
nekohasekai committed Nov 29, 2023
1 parent 927865e commit 733c14d
Show file tree
Hide file tree
Showing 57 changed files with 2,729 additions and 98 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/.idea/
/vendor/
/*.json
/*.srs
/*.db
/site/
/bin/
Expand Down
28 changes: 27 additions & 1 deletion adapter/experimental.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package adapter

import (
"bytes"
"context"
"encoding/binary"
"net"
"time"

"github.com/sagernet/sing-box/common/urltest"
N "github.com/sagernet/sing/common/network"
Expand All @@ -23,14 +26,37 @@ type CacheFile interface {
PreStarter

StoreFakeIP() bool
FakeIPStorage

LoadMode() string
StoreMode(mode string) error
LoadSelected(group string) string
StoreSelected(group string, selected string) error
LoadGroupExpand(group string) (isExpand bool, loaded bool)
StoreGroupExpand(group string, expand bool) error
FakeIPStorage
LoadRuleSet(tag string) *SavedRuleSet
SaveRuleSet(tag string, set *SavedRuleSet) error
}

type SavedRuleSet struct {
Content []byte
LastUpdated time.Time
}

func (s *SavedRuleSet) MarshalBinary() ([]byte, error) {
var buffer bytes.Buffer
err := binary.Write(&buffer, binary.BigEndian, s.LastUpdated.Unix())
if err != nil {
return nil, err
}
buffer.Write(s.Content)
return buffer.Bytes(), nil
}

func (s *SavedRuleSet) UnmarshalBinary(data []byte) error {
s.LastUpdated = time.Unix(int64(binary.BigEndian.Uint64(data)), 0)
s.Content = data[8:]
return nil
}

type Tracker interface {
Expand Down
15 changes: 13 additions & 2 deletions adapter/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Router interface {

Outbounds() []Outbound
Outbound(tag string) (Outbound, bool)
DefaultOutbound(network string) Outbound
DefaultOutbound(network string) (Outbound, error)

FakeIPStore() FakeIPStore

Expand All @@ -27,6 +27,8 @@ type Router interface {
GeoIPReader() *geoip.Reader
LoadGeosite(code string) (Rule, error)

RuleSet(tag string) (RuleSet, bool)

Exchange(ctx context.Context, message *mdns.Msg) (*mdns.Msg, error)
Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error)
LookupDefault(ctx context.Context, domain string) ([]netip.Addr, error)
Expand Down Expand Up @@ -61,11 +63,15 @@ func RouterFromContext(ctx context.Context) Router {
return service.FromContext[Router](ctx)
}

type HeadlessRule interface {
Match(metadata *InboundContext) bool
}

type Rule interface {
HeadlessRule
Service
Type() string
UpdateGeosite() error
Match(metadata *InboundContext) bool
Outbound() string
String() string
}
Expand All @@ -76,6 +82,11 @@ type DNSRule interface {
RewriteTTL() *uint32
}

type RuleSet interface {
HeadlessRule
Service
}

type InterfaceUpdateListener interface {
InterfaceUpdated()
}
Expand Down
39 changes: 0 additions & 39 deletions cmd/sing-box/cmd_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -69,41 +68,3 @@ func format() error {
}
return nil
}

func formatOne(configPath string) error {
configContent, err := os.ReadFile(configPath)
if err != nil {
return E.Cause(err, "read config")
}
var options option.Options
err = options.UnmarshalJSON(configContent)
if err != nil {
return E.Cause(err, "decode config")
}
buffer := new(bytes.Buffer)
encoder := json.NewEncoder(buffer)
encoder.SetIndent("", " ")
err = encoder.Encode(options)
if err != nil {
return E.Cause(err, "encode config")
}
if !commandFormatFlagWrite {
os.Stdout.WriteString(buffer.String() + "\n")
return nil
}
if bytes.Equal(configContent, buffer.Bytes()) {
return nil
}
output, err := os.Create(configPath)
if err != nil {
return E.Cause(err, "open output")
}
_, err = output.Write(buffer.Bytes())
output.Close()
if err != nil {
return E.Cause(err, "write output")
}
outputPath, _ := filepath.Abs(configPath)
os.Stderr.WriteString(outputPath + "\n")
return nil
}
43 changes: 43 additions & 0 deletions cmd/sing-box/cmd_geoip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"github.com/sagernet/sing-box/log"
E "github.com/sagernet/sing/common/exceptions"

"github.com/oschwald/maxminddb-golang"
"github.com/spf13/cobra"
)

var (
geoipReader *maxminddb.Reader
commandGeoIPFlagFile string
)

var commandGeoip = &cobra.Command{
Use: "geoip",
Short: "GeoIP tools",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
err := geoipPreRun()
if err != nil {
log.Fatal(err)
}
},
}

func init() {
commandGeoip.PersistentFlags().StringVarP(&commandGeoIPFlagFile, "file", "f", "geoip.db", "geoip file")
mainCommand.AddCommand(commandGeoip)
}

func geoipPreRun() error {
reader, err := maxminddb.Open(commandGeoIPFlagFile)
if err != nil {
return err
}
if reader.Metadata.DatabaseType != "sing-geoip" {
reader.Close()
return E.New("incorrect database type, expected sing-geoip, got ", reader.Metadata.DatabaseType)
}
geoipReader = reader
return nil
}
98 changes: 98 additions & 0 deletions cmd/sing-box/cmd_geoip_export.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package main

import (
"io"
"net"
"os"
"strings"

"github.com/sagernet/sing-box/common/json"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"

"github.com/oschwald/maxminddb-golang"
"github.com/spf13/cobra"
)

var flagGeoipExportOutput string

const flagGeoipExportDefaultOutput = "geoip-<country>.srs"

var commandGeoipExport = &cobra.Command{
Use: "export <country>",
Short: "Export geoip country as rule-set",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
err := geoipExport(args[0])
if err != nil {
log.Fatal(err)
}
},
}

func init() {
commandGeoipExport.Flags().StringVarP(&flagGeoipExportOutput, "output", "o", flagGeoipExportDefaultOutput, "Output path")
commandGeoip.AddCommand(commandGeoipExport)
}

func geoipExport(countryCode string) error {
networks := geoipReader.Networks(maxminddb.SkipAliasedNetworks)
countryMap := make(map[string][]*net.IPNet)
var (
ipNet *net.IPNet
nextCountryCode string
err error
)
for networks.Next() {
ipNet, err = networks.Network(&nextCountryCode)
if err != nil {
return err
}
countryMap[nextCountryCode] = append(countryMap[nextCountryCode], ipNet)
}
ipNets := countryMap[strings.ToLower(countryCode)]
if len(ipNets) == 0 {
return E.New("country code not found: ", countryCode)
}

var (
outputFile *os.File
outputWriter io.Writer
)
if flagGeoipExportOutput == "stdout" {
outputWriter = os.Stdout
} else if flagGeoipExportOutput == flagGeoipExportDefaultOutput {
outputFile, err = os.Create("geoip-" + countryCode + ".json")
if err != nil {
return err
}
defer outputFile.Close()
outputWriter = outputFile
} else {
outputFile, err = os.Create(flagGeoipExportOutput)
if err != nil {
return err
}
defer outputFile.Close()
outputWriter = outputFile
}

encoder := json.NewEncoder(outputWriter)
encoder.SetIndent("", " ")
var headlessRule option.DefaultHeadlessRule
headlessRule.IPCIDR = make([]string, 0, len(ipNets))
for _, cidr := range ipNets {
headlessRule.IPCIDR = append(headlessRule.IPCIDR, cidr.String())
}
var plainRuleSet option.PlainRuleSetCompat
plainRuleSet.Version = C.RuleSetVersion1
plainRuleSet.Options.Rules = []option.HeadlessRule{
{
Type: C.RuleTypeDefault,
DefaultOptions: headlessRule,
},
}
return encoder.Encode(plainRuleSet)
}
31 changes: 31 additions & 0 deletions cmd/sing-box/cmd_geoip_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"os"

"github.com/sagernet/sing-box/log"

"github.com/spf13/cobra"
)

var commandGeoipList = &cobra.Command{
Use: "list",
Short: "List geoip country codes",
Run: func(cmd *cobra.Command, args []string) {
err := listGeoip()
if err != nil {
log.Fatal(err)
}
},
}

func init() {
commandGeoip.AddCommand(commandGeoipList)
}

func listGeoip() error {
for _, code := range geoipReader.Metadata.Languages {
os.Stdout.WriteString(code + "\n")
}
return nil
}
41 changes: 41 additions & 0 deletions cmd/sing-box/cmd_geosite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"github.com/sagernet/sing-box/common/geosite"
"github.com/sagernet/sing-box/log"
E "github.com/sagernet/sing/common/exceptions"

"github.com/spf13/cobra"
)

var (
commandGeoSiteFlagFile string
geositeReader *geosite.Reader
geositeCodeList []string
)

var commandGeoSite = &cobra.Command{
Use: "geosite",
Short: "Geosite tools",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
err := geositePreRun()
if err != nil {
log.Fatal(err)
}
},
}

func init() {
commandGeoSite.PersistentFlags().StringVarP(&commandGeoSiteFlagFile, "file", "f", "geosite.db", "geosite file")
mainCommand.AddCommand(commandGeoSite)
}

func geositePreRun() error {
reader, codeList, err := geosite.Open(commandGeoSiteFlagFile)
if err != nil {
return E.Cause(err, "open geosite file")
}
geositeReader = reader
geositeCodeList = codeList
return nil
}
Loading

0 comments on commit 733c14d

Please sign in to comment.