123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- package srs
- import (
- "bufio"
- "compress/zlib"
- "encoding/binary"
- "io"
- "net/netip"
- C "github.com/sagernet/sing-box/constant"
- "github.com/sagernet/sing-box/option"
- "github.com/sagernet/sing/common"
- "github.com/sagernet/sing/common/domain"
- E "github.com/sagernet/sing/common/exceptions"
- "github.com/sagernet/sing/common/varbin"
- "go4.org/netipx"
- )
- var MagicBytes = [3]byte{0x53, 0x52, 0x53} // SRS
- const (
- ruleItemQueryType uint8 = iota
- ruleItemNetwork
- ruleItemDomain
- ruleItemDomainKeyword
- ruleItemDomainRegex
- ruleItemSourceIPCIDR
- ruleItemIPCIDR
- ruleItemSourcePort
- ruleItemSourcePortRange
- ruleItemPort
- ruleItemPortRange
- ruleItemProcessName
- ruleItemProcessPath
- ruleItemPackageName
- ruleItemWIFISSID
- ruleItemWIFIBSSID
- ruleItemAdGuardDomain
- ruleItemProcessPathRegex
- ruleItemFinal uint8 = 0xFF
- )
- func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err error) {
- var magicBytes [3]byte
- _, err = io.ReadFull(reader, magicBytes[:])
- if err != nil {
- return
- }
- if magicBytes != MagicBytes {
- err = E.New("invalid sing-box rule-set file")
- return
- }
- var version uint8
- err = binary.Read(reader, binary.BigEndian, &version)
- if err != nil {
- return ruleSet, err
- }
- if version > C.RuleSetVersion2 {
- return ruleSet, E.New("unsupported version: ", version)
- }
- compressReader, err := zlib.NewReader(reader)
- if err != nil {
- return
- }
- bReader := bufio.NewReader(compressReader)
- length, err := binary.ReadUvarint(bReader)
- if err != nil {
- return
- }
- ruleSet.Rules = make([]option.HeadlessRule, length)
- for i := uint64(0); i < length; i++ {
- ruleSet.Rules[i], err = readRule(bReader, recover)
- if err != nil {
- err = E.Cause(err, "read rule[", i, "]")
- return
- }
- }
- return
- }
- func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateUnstable bool) error {
- _, err := writer.Write(MagicBytes[:])
- if err != nil {
- return err
- }
- var version uint8
- if generateUnstable {
- version = C.RuleSetVersion2
- } else {
- version = C.RuleSetVersion1
- }
- err = binary.Write(writer, binary.BigEndian, version)
- if err != nil {
- return err
- }
- compressWriter, err := zlib.NewWriterLevel(writer, zlib.BestCompression)
- if err != nil {
- return err
- }
- bWriter := bufio.NewWriter(compressWriter)
- _, err = varbin.WriteUvarint(bWriter, uint64(len(ruleSet.Rules)))
- if err != nil {
- return err
- }
- for _, rule := range ruleSet.Rules {
- err = writeRule(bWriter, rule, generateUnstable)
- if err != nil {
- return err
- }
- }
- err = bWriter.Flush()
- if err != nil {
- return err
- }
- return compressWriter.Close()
- }
- func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err error) {
- var ruleType uint8
- err = binary.Read(reader, binary.BigEndian, &ruleType)
- if err != nil {
- return
- }
- switch ruleType {
- case 0:
- rule.Type = C.RuleTypeDefault
- rule.DefaultOptions, err = readDefaultRule(reader, recover)
- case 1:
- rule.Type = C.RuleTypeLogical
- rule.LogicalOptions, err = readLogicalRule(reader, recover)
- default:
- err = E.New("unknown rule type: ", ruleType)
- }
- return
- }
- func writeRule(writer varbin.Writer, rule option.HeadlessRule, generateUnstable bool) error {
- switch rule.Type {
- case C.RuleTypeDefault:
- return writeDefaultRule(writer, rule.DefaultOptions, generateUnstable)
- case C.RuleTypeLogical:
- return writeLogicalRule(writer, rule.LogicalOptions, generateUnstable)
- default:
- panic("unknown rule type: " + rule.Type)
- }
- }
- func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHeadlessRule, err error) {
- var lastItemType uint8
- for {
- var itemType uint8
- err = binary.Read(reader, binary.BigEndian, &itemType)
- if err != nil {
- return
- }
- switch itemType {
- case ruleItemQueryType:
- var rawQueryType []uint16
- rawQueryType, err = readRuleItemUint16(reader)
- if err != nil {
- return
- }
- rule.QueryType = common.Map(rawQueryType, func(it uint16) option.DNSQueryType {
- return option.DNSQueryType(it)
- })
- case ruleItemNetwork:
- rule.Network, err = readRuleItemString(reader)
- case ruleItemDomain:
- var matcher *domain.Matcher
- matcher, err = domain.ReadMatcher(reader)
- if err != nil {
- return
- }
- rule.DomainMatcher = matcher
- if recover {
- rule.Domain, rule.DomainSuffix = matcher.Dump()
- }
- case ruleItemDomainKeyword:
- rule.DomainKeyword, err = readRuleItemString(reader)
- case ruleItemDomainRegex:
- rule.DomainRegex, err = readRuleItemString(reader)
- case ruleItemSourceIPCIDR:
- rule.SourceIPSet, err = readIPSet(reader)
- if err != nil {
- return
- }
- if recover {
- rule.SourceIPCIDR = common.Map(rule.SourceIPSet.Prefixes(), netip.Prefix.String)
- }
- case ruleItemIPCIDR:
- rule.IPSet, err = readIPSet(reader)
- if err != nil {
- return
- }
- if recover {
- rule.IPCIDR = common.Map(rule.IPSet.Prefixes(), netip.Prefix.String)
- }
- case ruleItemSourcePort:
- rule.SourcePort, err = readRuleItemUint16(reader)
- case ruleItemSourcePortRange:
- rule.SourcePortRange, err = readRuleItemString(reader)
- case ruleItemPort:
- rule.Port, err = readRuleItemUint16(reader)
- case ruleItemPortRange:
- rule.PortRange, err = readRuleItemString(reader)
- case ruleItemProcessName:
- rule.ProcessName, err = readRuleItemString(reader)
- case ruleItemProcessPath:
- rule.ProcessPath, err = readRuleItemString(reader)
- case ruleItemProcessPathRegex:
- rule.ProcessPathRegex, err = readRuleItemString(reader)
- case ruleItemPackageName:
- rule.PackageName, err = readRuleItemString(reader)
- case ruleItemWIFISSID:
- rule.WIFISSID, err = readRuleItemString(reader)
- case ruleItemWIFIBSSID:
- rule.WIFIBSSID, err = readRuleItemString(reader)
- case ruleItemAdGuardDomain:
- if recover {
- err = E.New("unable to decompile binary AdGuard rules to rule-set")
- return
- }
- var matcher *domain.AdGuardMatcher
- matcher, err = domain.ReadAdGuardMatcher(reader)
- if err != nil {
- return
- }
- rule.AdGuardDomainMatcher = matcher
- case ruleItemFinal:
- err = binary.Read(reader, binary.BigEndian, &rule.Invert)
- return
- default:
- err = E.New("unknown rule item type: ", itemType, ", last type: ", lastItemType)
- }
- if err != nil {
- return
- }
- lastItemType = itemType
- }
- }
- func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, generateUnstable bool) error {
- err := binary.Write(writer, binary.BigEndian, uint8(0))
- if err != nil {
- return err
- }
- if len(rule.QueryType) > 0 {
- err = writeRuleItemUint16(writer, ruleItemQueryType, common.Map(rule.QueryType, func(it option.DNSQueryType) uint16 {
- return uint16(it)
- }))
- if err != nil {
- return err
- }
- }
- if len(rule.Network) > 0 {
- err = writeRuleItemString(writer, ruleItemNetwork, rule.Network)
- if err != nil {
- return err
- }
- }
- if len(rule.Domain) > 0 || len(rule.DomainSuffix) > 0 {
- err = binary.Write(writer, binary.BigEndian, ruleItemDomain)
- if err != nil {
- return err
- }
- err = domain.NewMatcher(rule.Domain, rule.DomainSuffix, !generateUnstable).Write(writer)
- if err != nil {
- return err
- }
- }
- if len(rule.DomainKeyword) > 0 {
- err = writeRuleItemString(writer, ruleItemDomainKeyword, rule.DomainKeyword)
- if err != nil {
- return err
- }
- }
- if len(rule.DomainRegex) > 0 {
- err = writeRuleItemString(writer, ruleItemDomainRegex, rule.DomainRegex)
- if err != nil {
- return err
- }
- }
- if len(rule.SourceIPCIDR) > 0 {
- err = writeRuleItemCIDR(writer, ruleItemSourceIPCIDR, rule.SourceIPCIDR)
- if err != nil {
- return E.Cause(err, "source_ip_cidr")
- }
- }
- if len(rule.IPCIDR) > 0 {
- err = writeRuleItemCIDR(writer, ruleItemIPCIDR, rule.IPCIDR)
- if err != nil {
- return E.Cause(err, "ipcidr")
- }
- }
- if len(rule.SourcePort) > 0 {
- err = writeRuleItemUint16(writer, ruleItemSourcePort, rule.SourcePort)
- if err != nil {
- return err
- }
- }
- if len(rule.SourcePortRange) > 0 {
- err = writeRuleItemString(writer, ruleItemSourcePortRange, rule.SourcePortRange)
- if err != nil {
- return err
- }
- }
- if len(rule.Port) > 0 {
- err = writeRuleItemUint16(writer, ruleItemPort, rule.Port)
- if err != nil {
- return err
- }
- }
- if len(rule.PortRange) > 0 {
- err = writeRuleItemString(writer, ruleItemPortRange, rule.PortRange)
- if err != nil {
- return err
- }
- }
- if len(rule.ProcessName) > 0 {
- err = writeRuleItemString(writer, ruleItemProcessName, rule.ProcessName)
- if err != nil {
- return err
- }
- }
- if len(rule.ProcessPath) > 0 {
- err = writeRuleItemString(writer, ruleItemProcessPath, rule.ProcessPath)
- if err != nil {
- return err
- }
- }
- if len(rule.ProcessPathRegex) > 0 {
- err = writeRuleItemString(writer, ruleItemProcessPathRegex, rule.ProcessPathRegex)
- if err != nil {
- return err
- }
- }
- if len(rule.PackageName) > 0 {
- err = writeRuleItemString(writer, ruleItemPackageName, rule.PackageName)
- if err != nil {
- return err
- }
- }
- if len(rule.WIFISSID) > 0 {
- err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID)
- if err != nil {
- return err
- }
- }
- if len(rule.WIFIBSSID) > 0 {
- err = writeRuleItemString(writer, ruleItemWIFIBSSID, rule.WIFIBSSID)
- if err != nil {
- return err
- }
- }
- if len(rule.AdGuardDomain) > 0 {
- err = binary.Write(writer, binary.BigEndian, ruleItemAdGuardDomain)
- if err != nil {
- return err
- }
- err = domain.NewAdGuardMatcher(rule.AdGuardDomain).Write(writer)
- if err != nil {
- return err
- }
- }
- err = binary.Write(writer, binary.BigEndian, ruleItemFinal)
- if err != nil {
- return err
- }
- err = binary.Write(writer, binary.BigEndian, rule.Invert)
- if err != nil {
- return err
- }
- return nil
- }
- func readRuleItemString(reader varbin.Reader) ([]string, error) {
- return varbin.ReadValue[[]string](reader, binary.BigEndian)
- }
- func writeRuleItemString(writer varbin.Writer, itemType uint8, value []string) error {
- err := writer.WriteByte(itemType)
- if err != nil {
- return err
- }
- return varbin.Write(writer, binary.BigEndian, value)
- }
- func readRuleItemUint16(reader varbin.Reader) ([]uint16, error) {
- return varbin.ReadValue[[]uint16](reader, binary.BigEndian)
- }
- func writeRuleItemUint16(writer varbin.Writer, itemType uint8, value []uint16) error {
- err := writer.WriteByte(itemType)
- if err != nil {
- return err
- }
- return varbin.Write(writer, binary.BigEndian, value)
- }
- func writeRuleItemCIDR(writer varbin.Writer, itemType uint8, value []string) error {
- var builder netipx.IPSetBuilder
- for i, prefixString := range value {
- prefix, err := netip.ParsePrefix(prefixString)
- if err == nil {
- builder.AddPrefix(prefix)
- continue
- }
- addr, addrErr := netip.ParseAddr(prefixString)
- if addrErr == nil {
- builder.Add(addr)
- continue
- }
- return E.Cause(err, "parse [", i, "]")
- }
- ipSet, err := builder.IPSet()
- if err != nil {
- return err
- }
- err = binary.Write(writer, binary.BigEndian, itemType)
- if err != nil {
- return err
- }
- return writeIPSet(writer, ipSet)
- }
- func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.LogicalHeadlessRule, err error) {
- mode, err := reader.ReadByte()
- if err != nil {
- return
- }
- switch mode {
- case 0:
- logicalRule.Mode = C.LogicalTypeAnd
- case 1:
- logicalRule.Mode = C.LogicalTypeOr
- default:
- err = E.New("unknown logical mode: ", mode)
- return
- }
- length, err := binary.ReadUvarint(reader)
- if err != nil {
- return
- }
- logicalRule.Rules = make([]option.HeadlessRule, length)
- for i := uint64(0); i < length; i++ {
- logicalRule.Rules[i], err = readRule(reader, recovery)
- if err != nil {
- err = E.Cause(err, "read logical rule [", i, "]")
- return
- }
- }
- err = binary.Read(reader, binary.BigEndian, &logicalRule.Invert)
- if err != nil {
- return
- }
- return
- }
- func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule, generateUnstable bool) error {
- err := binary.Write(writer, binary.BigEndian, uint8(1))
- if err != nil {
- return err
- }
- switch logicalRule.Mode {
- case C.LogicalTypeAnd:
- err = binary.Write(writer, binary.BigEndian, uint8(0))
- case C.LogicalTypeOr:
- err = binary.Write(writer, binary.BigEndian, uint8(1))
- default:
- panic("unknown logical mode: " + logicalRule.Mode)
- }
- if err != nil {
- return err
- }
- _, err = varbin.WriteUvarint(writer, uint64(len(logicalRule.Rules)))
- if err != nil {
- return err
- }
- for _, rule := range logicalRule.Rules {
- err = writeRule(writer, rule, generateUnstable)
- if err != nil {
- return err
- }
- }
- err = binary.Write(writer, binary.BigEndian, logicalRule.Invert)
- if err != nil {
- return err
- }
- return nil
- }
|