diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml new file mode 100644 index 00000000..1c0b0a62 --- /dev/null +++ b/.github/workflows/dev.yml @@ -0,0 +1,44 @@ +name: Dev +on: [push] +jobs: + dev-build: + if: ${{ !contains(github.event.head_commit.message, '[Skip CI]') }} + runs-on: ubuntu-latest + steps: + - name: Get latest go version + id: version + run: | + echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: ${{ steps.version.outputs.go_version }} + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Cache go module + uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- +# - name: Get dependencies, run test +# run: | +# go test ./... + - name: Build + if: success() + env: + NAME: Clash.Meta + BINDIR: bin + run: make -j releases + + - name: Upload Dev + uses: softprops/action-gh-release@v1 + if: ${{ env.GIT_BRANCH != 'Meta' && success() }} + with: + tag_name: develop + files: bin/* + prerelease: true + diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index f761bd9b..ebb2095e 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -26,10 +26,20 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext { metadata.NetWork = C.TCP metadata.Type = C.INNER metadata.DNSMode = C.DNSMapping - metadata.AddrType = C.AtypDomainName metadata.Host = host - if _, port, err := parseAddr(dst); err == nil { + metadata.AddrType = C.AtypDomainName + metadata.Process = C.ClashName + if ip, port, err := parseAddr(dst); err == nil { metadata.DstPort = port + if host == "" { + metadata.DstIP = ip + if ip.To4() == nil { + metadata.AddrType = C.AtypIPv6 + } else { + metadata.AddrType = C.AtypIPv4 + } + } } + return context.NewConnContext(conn, metadata) } diff --git a/constant/version.go b/constant/version.go index 5a511382..1d4e6e7b 100644 --- a/constant/version.go +++ b/constant/version.go @@ -5,4 +5,5 @@ var ( Version = "1.9.0" BuildTime = "unknown time" AutoIptables string + ClashName = "Clash.Meta" ) diff --git a/rule/common/process.go b/rule/common/process.go index 9481e1fc..51e12ef5 100644 --- a/rule/common/process.go +++ b/rule/common/process.go @@ -35,26 +35,28 @@ func (ps *Process) Match(metadata *C.Metadata) bool { } key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort) - cached, hit := processCache.Get(key) - if !hit { - srcPort, err := strconv.Atoi(metadata.SrcPort) - if err != nil { - processCache.Set(key, "") - return false + if strings.TrimSpace(metadata.Process) == "" { + cached, hit := processCache.Get(key) + if !hit { + srcPort, err := strconv.Atoi(metadata.SrcPort) + if err != nil { + processCache.Set(key, "") + return false + } + + name, err := process.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, srcPort) + if err != nil { + log.Debugln("[Rule] find process name %s error: %s", C.Process.String(), err.Error()) + } + + processCache.Set(key, name) + + cached = name } - name, err := process.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, srcPort) - if err != nil { - log.Debugln("[Rule] find process name %s error: %s", C.Process.String(), err.Error()) - } - - processCache.Set(key, name) - - cached = name + metadata.Process = cached.(string) } - metadata.Process = cached.(string) - return strings.EqualFold(metadata.Process, ps.process) } diff --git a/rule/logic/and.go b/rule/logic/and.go index f75de048..01862e51 100644 --- a/rule/logic/and.go +++ b/rule/logic/and.go @@ -11,7 +11,7 @@ type AND struct { func NewAND(payload string, adapter string) (*AND, error) { and := &AND{payload: payload, adapter: adapter} - rules, err := parseRule(payload) + rules, err := parseRuleByPayload(payload) if err != nil { return nil, err } diff --git a/rule/logic/common.go b/rule/logic/common.go index 4c1aa44c..11d67915 100644 --- a/rule/logic/common.go +++ b/rule/logic/common.go @@ -10,63 +10,29 @@ import ( "strings" ) -func parseRule(payload string) ([]C.Rule, error) { +func parseRuleByPayload(payload string) ([]C.Rule, error) { regex, err := regexp.Compile("\\(.*\\)") if err != nil { return nil, err } if regex.MatchString(payload) { - subRanges, err := format(payload) + subAllRanges, err := format(payload) if err != nil { return nil, err } - rules := make([]C.Rule, 0, len(subRanges)) + rules := make([]C.Rule, 0, len(subAllRanges)) + + subRanges := findSubRuleRange(payload, subAllRanges) + for _, subRange := range subRanges { + subPayload := payload[subRange.start+1 : subRange.end] - if len(subRanges) == 1 { - subPayload := payload[subRanges[0].start+1 : subRanges[0].end-1] rule, err := payloadToRule(subPayload) if err != nil { return nil, err } rules = append(rules, rule) - } else { - preStart := subRanges[0].start - preEnd := subRanges[0].end - for _, sr := range subRanges[1:] { - if containRange(sr, preStart, preEnd) && sr.start-preStart > 1 { - str := "" - if preStart+1 <= sr.start-1 { - str = strings.TrimSpace(payload[preStart+1 : sr.start-1]) - } - - if str == "AND" || str == "OR" || str == "NOT" { - subPayload := payload[preStart+1 : preEnd] - rule, err := payloadToRule(subPayload) - if err != nil { - return nil, err - } - - rules = append(rules, rule) - preStart = sr.start - preEnd = sr.end - } - - continue - } - - preStart = sr.start - preEnd = sr.end - - subPayload := payload[sr.start+1 : sr.end] - rule, err := payloadToRule(subPayload) - if err != nil { - return nil, err - } - - rules = append(rules, rule) - } } return rules, nil @@ -84,23 +50,14 @@ func payloadToRule(subPayload string) (C.Rule, error) { tp := splitStr[0] payload := splitStr[1] if tp == "NOT" || tp == "OR" || tp == "AND" { - return parseSubRule(tp, payload, nil) + return parseRule(tp, payload, nil) } param := strings.Split(payload, ",") - return parseSubRule(tp, param[0], param[1:]) + return parseRule(tp, param[0], param[1:]) } -func splitSubRule(subRuleStr string) (string, string, []string, error) { - typeAndRule := strings.Split(subRuleStr, ",") - if len(typeAndRule) < 2 { - return "", "", nil, fmt.Errorf("format error:[%s]", typeAndRule) - } - - return strings.TrimSpace(typeAndRule[0]), strings.TrimSpace(typeAndRule[1]), typeAndRule[2:], nil -} - -func parseSubRule(tp, payload string, params []string) (C.Rule, error) { +func parseRule(tp, payload string, params []string) (C.Rule, error) { var ( parseErr error parsed C.Rule @@ -183,3 +140,29 @@ func format(payload string) ([]Range, error) { return sortResult, nil } + +func findSubRuleRange(payload string, ruleRanges []Range) []Range { + payloadLen := len(payload) + subRuleRange := make([]Range, 0) + for _, rr := range ruleRanges { + if rr.start == 0 && rr.end == payloadLen-1 { + // 最大范围跳过 + continue + } + + containInSub := false + for _, r := range subRuleRange { + if containRange(rr, r.start, r.end) { + // The subRuleRange contains a range of rr, which is the next level node of the tree + containInSub = true + break + } + } + + if !containInSub { + subRuleRange = append(subRuleRange, rr) + } + } + + return subRuleRange +} diff --git a/rule/logic/not.go b/rule/logic/not.go index 94ca0e3c..79102d4e 100644 --- a/rule/logic/not.go +++ b/rule/logic/not.go @@ -13,7 +13,7 @@ type NOT struct { func NewNOT(payload string, adapter string) (*NOT, error) { not := &NOT{payload: payload, adapter: adapter} - rule, err := parseRule(payload) + rule, err := parseRuleByPayload(payload) if err != nil { return nil, err } diff --git a/rule/logic/or.go b/rule/logic/or.go index fb38a20f..3822e315 100644 --- a/rule/logic/or.go +++ b/rule/logic/or.go @@ -41,7 +41,7 @@ func (or *OR) RuleExtra() *C.RuleExtra { func NewOR(payload string, adapter string) (*OR, error) { or := &OR{payload: payload, adapter: adapter} - rules, err := parseRule(payload) + rules, err := parseRuleByPayload(payload) if err != nil { return nil, err }