From 45a02e343937c6ab402c44e478bed5de7cc8f6d5 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 2 May 2022 00:19:28 +0800 Subject: [PATCH 1/6] chore: workflow --- .github/workflows/docker.yaml | 17 ++++--- .../workflows/{alpha.yml => prerelease.yml} | 44 +++++++++---------- .github/workflows/release.yaml | 44 +++++++++++++++++++ Dockerfile | 31 ++++--------- Makefile | 13 +++++- listener/tun/ipstack/system/mars/nat/nat.go | 2 +- .../system/mars/tcpip/tcpip_arm64_test.go | 4 +- 7 files changed, 97 insertions(+), 58 deletions(-) rename .github/workflows/{alpha.yml => prerelease.yml} (63%) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index ad930bd0..c506bf9c 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -1,11 +1,13 @@ name: Docker -on: [push] - +on: + push: + branches: + - Beta + tags: + - "v*" env: REGISTRY: docker.io - IMAGE_NAME: '{{ env.DOCKERHUB_ACCOUNT }}/{{ env.DOCKERHUB_REPO }}' - jobs: build: runs-on: ubuntu-latest @@ -23,6 +25,8 @@ jobs: - name: Setup Docker buildx uses: docker/setup-buildx-action@v1 + with: + version: latest # Extract metadata (tags, labels) for Docker # https://github.com/docker/metadata-action @@ -30,9 +34,9 @@ jobs: id: meta uses: docker/metadata-action@v3 with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + images: ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_ACCOUNT }}/${{secrets.DOCKERHUB_REPO}} - - name: Log into registry ${{ env.REGISTRY }} + - name: Log into registry if: github.event_name != 'pull_request' uses: docker/login-action@v1 with: @@ -47,6 +51,7 @@ jobs: uses: docker/build-push-action@v2 with: context: . + file: ./Dockerfile push: ${{ github.event_name != 'pull_request' }} platforms: | linux/386 diff --git a/.github/workflows/alpha.yml b/.github/workflows/prerelease.yml similarity index 63% rename from .github/workflows/alpha.yml rename to .github/workflows/prerelease.yml index ea859c42..9a4b370a 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/prerelease.yml @@ -1,8 +1,15 @@ -name: alpha -on: [push] +name: prerelease +on: + push: + branches: + - Alpha + - Beta + pull_request: + branches: + - Alpha + - Beta jobs: Build: - if: ${{ !contains(github.event.head_commit.message, '[Skip CI]') }} runs-on: ubuntu-latest steps: - name: Get latest go version @@ -24,9 +31,10 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - # - name: Get dependencies, run test - # run: | - # go test ./... + - name: Test + if: ${{env.GITHUB_REF_NAME=='Beta'}} + run: | + go test ./... - name: Build if: success() env: @@ -38,34 +46,22 @@ jobs: uses: andreaswilli/delete-release-assets-action@v2.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} - tag: alpha + tag: ${{ env.GITHUB_REF_NAME }} deleteOnlyFromDrafts: false - name: Tag Repo uses: richardsimko/update-tag@v1 with: - tag_name: alpha + tag_name: ${{env.GITHUB_REF_NAME}} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Alpha uses: softprops/action-gh-release@v1 - if: ${{ env.GIT_BRANCH != 'Meta' && success() }} + if: ${{ success() }} with: - tag: ${{ github.ref }} - tag_name: alpha + tag: ${{env.GITHUB_REF_NAME}} + tag_name: ${{env.GITHUB_REF_NAME}} files: bin/* prerelease: true - -# - name: send telegram message on push -# uses: appleboy/telegram-action@master -# with: -# to: ${{ secrets.TTELEGRAM_CHAT_ID }} -# token: ${{ secrets.TELEGRAM_TOKEN }} -# message: | -# ${{ github.actor }} created commit: -# Commit message: ${{ github.event.commits[0].message }} -# -# Repository: ${{ github.repository }} -# -# See changes: https://github.com/${{ github.repository }}/commit/${{github.sha}} \ No newline at end of file + generate_release_notes: true diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..2b7b70a7 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,44 @@ +name: release +on: + push: + tags: + - "v*" +jobs: + Build: + 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@v3 + - 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: Test + run: | + go test ./... + - name: Build + if: success() + env: + NAME: Clash.Meta + BINDIR: bin + run: make -j releases + + - name: Upload Release + uses: softprops/action-gh-release@v1 + if: ${{ success() && startsWith(github.ref, 'refs/tags/')}} + with: + tag: ${{ github.ref }} + files: bin/* + generate_release_notes: true diff --git a/Dockerfile b/Dockerfile index 498fbd44..44dc3b52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,41 +1,26 @@ FROM golang:alpine as builder -ARG TARGETOS -ARG TARGETARCH - RUN apk add --no-cache make git && \ mkdir /clash-config && \ wget -O /clash-config/Country.mmdb https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb && \ wget -O /clash-config/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat && \ wget -O /clash-config/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat -WORKDIR /clash-src + COPY . /clash-src -RUN go mod download -RUN /bin/ash -c 'set -ex && \ - if [ "$TARGETARCH" == "amd64" ]; then \ - GOOS=$TARGETOS GOARCH=$TARGETARCH GOAMD64=v1 make docker && \ - mv ./bin/Clash.Meta-docker ./bin/clash-amd64v1 && \ - GOOS=$TARGETOS GOARCH=$TARGETARCH GOAMD64=v2 make docker && \ - mv ./bin/Clash.Meta-docker ./bin/clash-amd64v2 && \ - GOOS=$TARGETOS GOARCH=$TARGETARCH GOAMD64=v3 make docker && \ - mv ./bin/Clash.Meta-docker ./bin/clash-amd64v3 && \ - ln -s clash-amd64v3 ./bin/clash-amd64v4 && \ - mv check_amd64.sh ./bin/ && \ - printf "#!/bin/sh\\nsh ./check_amd64.sh\\nexec ./clash-amd64v\$? \$@" > ./bin/clash && \ - chmod +x ./bin/check_amd64.sh ./bin/clash; \ - else \ - GOOS=$TARGETOS GOARCH=$TARGETARCH make docker && \ - mv ./bin/Clash.Meta-docker ./bin/clash; \ - fi' +WORKDIR /clash-src +RUN go mod download &&\ + make docker &&\ + mv ./bin/Clash.Meta-docker /clash + FROM alpine:latest LABEL org.opencontainers.image.source="https://github.com/MetaCubeX/Clash.Meta" RUN apk add --no-cache ca-certificates tzdata VOLUME ["/root/.config/clash/"] -EXPOSE 7890/tcp COPY --from=builder /clash-config/ /root/.config/clash/ -COPY --from=builder /clash-src/bin/ / +COPY --from=builder /clash /clash +RUN chmod +x /clash ENTRYPOINT [ "/clash" ] \ No newline at end of file diff --git a/Makefile b/Makefile index 365ecf35..edb5be99 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,16 @@ NAME=Clash.Meta BINDIR=bin -BRANCH=$(shell git rev-parse --abbrev-ref HEAD) +BRANCH=$(shell git branch --show-current) +ifeq ($(BRANCH),Alpha) VERSION=alpha-$(shell git rev-parse --short HEAD) +else ifeq ($(BRANCH),Beta) +VERSION=beta-$(shell git rev-parse --short HEAD) +else ifeq ($(BRANCH),HEAD) +VERSION=$(shell git describe --tags) +else +VERSION=unknown +endif + BUILDTIME=$(shell date -u) GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \ -X "github.com/Dreamacro/clash/constant.BuildTime=$(BUILDTIME)" \ @@ -43,7 +52,7 @@ all:linux-amd64 linux-arm64\ windows-amd64 windows-arm64\ docker: - $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ + GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ darwin-amd64v3: GOARCH=amd64 GOOS=darwin GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ diff --git a/listener/tun/ipstack/system/mars/nat/nat.go b/listener/tun/ipstack/system/mars/nat/nat.go index 6b809bb2..7954ace1 100644 --- a/listener/tun/ipstack/system/mars/nat/nat.go +++ b/listener/tun/ipstack/system/mars/nat/nat.go @@ -44,7 +44,7 @@ func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, * for { n, err := device.Read(buf) if err != nil { - log.Warnln("system error:%s", err.Error()) + log.Errorf("system error:%s", err.Error()) return } diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64_test.go b/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64_test.go index 131a8266..20a89095 100644 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64_test.go +++ b/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64_test.go @@ -22,7 +22,7 @@ func Test_SumNeon(t *testing.T) { } compat := SumCompat(bytes[:size]) - neon := sumNeon(bytes[:size]) + neon := SumNeon(bytes[:size]) if compat != neon { t.Errorf("Sum of length=%d mismatched", size) @@ -46,6 +46,6 @@ func Benchmark_SumNeon(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - sumNeon(bytes) + SumNeon(bytes) } } From 8681728b8df6252cec5e3afe91554f47a692ba82 Mon Sep 17 00:00:00 2001 From: MetaCubeX Date: Mon, 2 May 2022 05:01:07 +0800 Subject: [PATCH 2/6] chore: doq parameters --- dns/doq.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dns/doq.go b/dns/doq.go index a0682745..8764ebab 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -138,6 +138,8 @@ func (dc *quicClient) openSession() (quic.Connection, error) { quicConfig := &quic.Config{ ConnectionIDLength: 12, HandshakeIdleTimeout: time.Second * 8, + MaxIncomingStreams: 4, + MaxIdleTimeout: time.Second * 45, } log.Debugln("opening session to %s", dc.addr) From 6d704b9cd174d0a5827959c5f1897fb12f9ba4c4 Mon Sep 17 00:00:00 2001 From: MetaCubeX Date: Mon, 2 May 2022 05:10:18 +0800 Subject: [PATCH 3/6] feat: sniffer support http --- component/sniffer/http_sniffer.go | 109 ++++++++++++++++++++++++++++++ component/sniffer/quic_sniffer.go | 3 + constant/sniffer.go | 4 +- 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 component/sniffer/http_sniffer.go create mode 100644 component/sniffer/quic_sniffer.go diff --git a/component/sniffer/http_sniffer.go b/component/sniffer/http_sniffer.go new file mode 100644 index 00000000..84ab4103 --- /dev/null +++ b/component/sniffer/http_sniffer.go @@ -0,0 +1,109 @@ +package sniffer + +import ( + "bytes" + "errors" + C "github.com/Dreamacro/clash/constant" + "net" + "strconv" + "strings" +) + +var ( + // refer to https://pkg.go.dev/net/http@master#pkg-constants + methods = [...]string{"get", "post", "head", "put", "delete", "options", "connect", "patch", "trace"} + errNotHTTPMethod = errors.New("not an HTTP method") +) + +type version byte + +const ( + HTTP1 version = iota + HTTP2 +) + +type HTTPSniffer struct { + version version + host string +} + +func (http *HTTPSniffer) Protocol() string { + switch http.version { + case HTTP1: + return "http1" + case HTTP2: + return "http2" + default: + return "unknown" + } +} + +func (http *HTTPSniffer) SupportNetwork() C.NetWork { + return C.TCP +} + +func (http *HTTPSniffer) SniffTCP(bytes []byte) (string, error) { + domain, err := SniffHTTP(bytes) + if err == nil { + return *domain, nil + } else { + return "", err + } +} + +func beginWithHTTPMethod(b []byte) error { + for _, m := range &methods { + if len(b) >= len(m) && strings.EqualFold(string(b[:len(m)]), m) { + return nil + } + + if len(b) < len(m) { + return ErrNoClue + } + } + return errNotHTTPMethod +} + +func SniffHTTP(b []byte) (*string, error) { + if err := beginWithHTTPMethod(b); err != nil { + return nil, err + } + + _ = &HTTPSniffer{ + version: HTTP1, + } + + headers := bytes.Split(b, []byte{'\n'}) + for i := 1; i < len(headers); i++ { + header := headers[i] + if len(header) == 0 { + break + } + parts := bytes.SplitN(header, []byte{':'}, 2) + if len(parts) != 2 { + continue + } + key := strings.ToLower(string(parts[0])) + if key == "host" { + port := 80 + rawHost := strings.ToLower(string(bytes.TrimSpace(parts[1]))) + host, rawPort, err := net.SplitHostPort(rawHost) + if err != nil { + if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") { + host = rawHost + } else { + return nil, err + } + } else if len(rawPort) > 0 { + intPort, err := strconv.ParseUint(rawPort, 0, 16) + if err != nil { + return nil, err + } + port = int(intPort) + } + host = net.JoinHostPort(host, strconv.Itoa(port)) + return &host, nil + } + } + return nil, ErrNoClue +} diff --git a/component/sniffer/quic_sniffer.go b/component/sniffer/quic_sniffer.go new file mode 100644 index 00000000..de78cf82 --- /dev/null +++ b/component/sniffer/quic_sniffer.go @@ -0,0 +1,3 @@ +package sniffer + +//TODO diff --git a/constant/sniffer.go b/constant/sniffer.go index 5454279a..c19b068c 100644 --- a/constant/sniffer.go +++ b/constant/sniffer.go @@ -11,7 +11,7 @@ const ( ) var ( - SnifferList = []SnifferType{TLS} + SnifferList = []SnifferType{TLS, HTTP} ) type SnifferType int @@ -20,6 +20,8 @@ func (rt SnifferType) String() string { switch rt { case TLS: return "TLS" + case HTTP: + return "HTTP" default: return "Unknown" } From 5ccc047fe47563552a5aed3099d3932ce1eb69c9 Mon Sep 17 00:00:00 2001 From: MetaCubeX Date: Mon, 2 May 2022 05:17:13 +0800 Subject: [PATCH 4/6] chore: adjust sniffer err info --- component/sniffer/dispatcher.go | 1 + component/sniffer/tls_sniffer.go | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 9d47d36f..5ca44f06 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -19,6 +19,7 @@ import ( var ( ErrorUnsupportedSniffer = errors.New("unsupported sniffer") ErrorSniffFailed = errors.New("all sniffer failed") + ErrNoClue = errors.New("not enough information for making a decision") ) var Dispatcher SnifferDispatcher diff --git a/component/sniffer/tls_sniffer.go b/component/sniffer/tls_sniffer.go index 6af0b97b..f5a8fd99 100644 --- a/component/sniffer/tls_sniffer.go +++ b/component/sniffer/tls_sniffer.go @@ -11,7 +11,6 @@ import ( var ( errNotTLS = errors.New("not TLS header") errNotClientHello = errors.New("not client hello") - ErrNoClue = errors.New("not enough information for making a decision") ) type TLSSniffer struct { From 658f1f5cdaec1ead5e0381167144917db15cc6a0 Mon Sep 17 00:00:00 2001 From: Meta <84378451+MetaCubeX@users.noreply.github.com> Date: Mon, 2 May 2022 05:34:20 +0800 Subject: [PATCH 5/6] fix code mistake --- constant/sniffer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/constant/sniffer.go b/constant/sniffer.go index c19b068c..0592b87a 100644 --- a/constant/sniffer.go +++ b/constant/sniffer.go @@ -8,6 +8,7 @@ type Sniffer interface { const ( TLS SnifferType = iota + HTTP SnifferType ) var ( From fe2bc903b8643260840ec305aa19b6b35c47e8fe Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 2 May 2022 06:27:45 +0800 Subject: [PATCH 6/6] fix trojan and snell's normal udp --- adapter/outbound/snell.go | 8 +++++++- adapter/outbound/trojan.go | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 92343055..7b272d5d 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -99,7 +99,13 @@ func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o tcpKeepAlive(c) c = streamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption}) - return s.ListenPacketOnStreamConn(c, metadata) + err = snell.WriteUDPHeader(c, s.version) + if err != nil { + return nil, err + } + + pc := snell.PacketConn(c) + return newPacketConn(pc, s), nil } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 46586673..31811c51 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -161,7 +161,13 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } } - return t.ListenPacketOnStreamConn(c, metadata) + err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) + if err != nil { + return nil, err + } + + pc := t.instance.PacketConn(c) + return newPacketConn(pc, t), err } // ListenPacketOnStreamConn implements C.ProxyAdapter