diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index b56b9865..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Bug report -description: Create a report to help us improve -title: "[Bug] " -body: - - type: checkboxes - id: ensure - attributes: - label: Verify steps - description: " -在提交之前,请确认 -Please verify that you've followed these steps -" - options: - - label: " -如果你可以自己 debug 并解决的话,提交 PR 吧 -Is this something you can **debug and fix**? Send a pull request! Bug fixes and documentation fixes are welcome. -" - required: true - - label: " -我已经在 [Issue Tracker](……/) 中找过我要提出的问题 -I have searched on the [issue tracker](……/) for a related issue. -" - required: true - - label: " -我已经使用 dev 分支版本测试过,问题依旧存在 -I have tested using the dev branch, and the issue still exists. -" - required: true - - label: " -我已经仔细看过 [Documentation](https://github.com/Dreamacro/clash/wiki/) 并无法自行解决问题 -I have read the [documentation](https://github.com/Dreamacro/clash/wiki/) and was unable to solve the issue. -" - required: true - - label: " -这是 Clash 核心的问题,并非我所使用的 Clash 衍生版本(如 OpenClash、KoolClash 等)的特定问题 -This is an issue of the Clash core *per se*, not to the derivatives of Clash, like OpenClash or KoolClash. -" - required: true - - type: input - attributes: - label: Clash version - validations: - required: true - - type: dropdown - id: os - attributes: - label: What OS are you seeing the problem on? - multiple: true - options: - - macOS - - Windows - - Linux - - OpenBSD/FreeBSD - - type: textarea - attributes: - render: yaml - label: "Clash config" - description: " -在下方附上 Clash core 脱敏后配置文件的内容 -Paste the Clash core configuration below. -" - validations: - required: true - - type: textarea - attributes: - render: shell - label: Clash log - description: " -在下方附上 Clash Core 的日志,log level 使用 DEBUG -Paste the Clash core log below with the log level set to `DEBUG`. -" - - type: textarea - attributes: - label: Description - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 7404fe27..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,6 +0,0 @@ -blank_issues_enabled: false - -contact_links: - - name: Get help in GitHub Discussions - url: https://github.com/Dreamacro/clash/discussions - about: Have a question? Not sure if your issue affects everyone reproducibly? The quickest way to get help is on Clash's GitHub Discussions! diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index 34668d1a..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Feature request -description: Suggest an idea for this project -title: "[Feature] " -body: - - type: checkboxes - id: ensure - attributes: - label: Verify steps - description: " -在提交之前,请确认 -Please verify that you've followed these steps -" - options: - - label: " -我已经在 [Issue Tracker](……/) 中找过我要提出的请求 -I have searched on the [issue tracker](……/) for a related feature request. -" - required: true - - label: " -我已经仔细看过 [Documentation](https://github.com/Dreamacro/clash/wiki/) 并无法自行解决问题 -I have read the [documentation](https://github.com/Dreamacro/clash/wiki/) and was unable to solve the issue. -" - required: true - - type: textarea - attributes: - label: Description - description: 请详细、清晰地表达你要提出的论述,例如这个问题如何影响到你?你想实现什么功能?目前 Clash Core 的行为是什麽? - validations: - required: true - - type: textarea - attributes: - label: Possible Solution - description: " -此项非必须,但是如果你有想法的话欢迎提出。 -Not obligatory, but suggest a fix/reason for the bug, or ideas how to implement the addition or change -" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index ae1a2793..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: [ master, dev ] - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index 35083e51..00000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Publish Docker Image -on: - push: - branches: - - dev - tags: - - '*' -jobs: - - build: - name: Build - runs-on: ubuntu-latest - steps: - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - with: - platforms: all - - - name: Set up docker buildx - id: buildx - uses: docker/setup-buildx-action@v1 - with: - version: latest - - - name: Login to DockerHub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Login to Github Package - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: Dreamacro - password: ${{ secrets.PACKAGE_TOKEN }} - - - name: Build dev branch and push - if: github.ref == 'refs/heads/dev' - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/amd64,linux/arm/v7,linux/arm64 - push: true - tags: 'dreamacro/clash:dev,ghcr.io/dreamacro/clash:dev' - - - name: Get all docker tags - if: startsWith(github.ref, 'refs/tags/') - uses: actions/github-script@v4 - id: tags - with: - script: | - const ref = `${context.payload.ref.replace(/\/?refs\/tags\//, '')}` - const tags = [ - 'dreamacro/clash:latest', - `dreamacro/clash:${ref}`, - 'ghcr.io/dreamacro/clash:latest', - `ghcr.io/dreamacro/clash:${ref}` - ] - return tags.join(',') - result-encoding: string - - - name: Build release and push - if: startsWith(github.ref, 'refs/tags/') - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/amd64,linux/arm/v7,linux/arm64 - push: true - tags: ${{steps.tags.outputs.result}} diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml deleted file mode 100644 index 26d318c3..00000000 --- a/.github/workflows/linter.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Linter -on: [push, pull_request] -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: golangci-lint - uses: golangci/golangci-lint-action@v2 - with: - version: latest - args: --disable-all -E govet -E gofumpt -E megacheck ./... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index d4bad8a3..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Release -on: [push] -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@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: startsWith(github.ref, 'refs/tags/') - env: - NAME: clash - BINDIR: bin - run: make -j releases - - - name: Upload Release - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') - with: - files: bin/* - draft: true diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 68f986ff..00000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,18 +0,0 @@ - -name: Mark stale issues and pull requests - -on: - schedule: - - cron: "30 1 * * *" - -jobs: - stale: - - runs-on: ubuntu-latest - - steps: - - uses: actions/stale@v4 - with: - stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 5 days' - days-before-stale: 60 - days-before-close: 5 diff --git a/Makefile b/Makefile index b6a5b22a..dffe8e3b 100644 --- a/Makefile +++ b/Makefile @@ -1,103 +1,67 @@ +GOCMD=go +XGOCMD=xgo -go=go-1.17.x +GOBUILD=CGO_ENABLED=1 $(GOCMD) build -trimpath +GOCLEAN=$(GOCMD) clean NAME=clash -BINDIR=bin -VERSION=$(shell git describe --tags || echo "unknown version") +BINDIR=$(shell pwd)/bin +VERSION=$(shell git describe --tags --always 2>/dev/null || date +%F) 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)" \ - -w -s -buildid=' +BUILD_PACKAGE=. +RELEASE_LDFLAGS='-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \ + -X "github.com/Dreamacro/clash/constant.BuildTime=$(BUILDTIME)" \ + -w -s -buildid=' +STATIC_LDFLAGS='-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \ + -X "github.com/Dreamacro/clash/constant.BuildTime=$(BUILDTIME)" \ + -extldflags "-static" \ + -w -s -buildid=' PLATFORM_LIST = \ darwin-amd64 \ darwin-arm64 \ - linux-386 \ - linux-amd64 \ - linux-armv5 \ - linux-armv6 \ - linux-armv7 \ - linux-armv8 \ - linux-mips-softfloat \ - linux-mips-hardfloat \ - linux-mipsle-softfloat \ - linux-mipsle-hardfloat \ - linux-mips64 \ - linux-mips64le \ - freebsd-386 \ - freebsd-amd64 \ - freebsd-arm64 + linux-amd64 +# linux-arm64 +# linux-386 WINDOWS_ARCH_LIST = \ - windows-386 \ windows-amd64 \ - windows-arm64 \ - windows-arm32v7 + windows-386 +# windows-arm64 all: linux-amd64 darwin-amd64 windows-amd64 # Most used -docker: - $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ +build: + $(GOBUILD) -ldflags $(RELEASE_LDFLAGS) -tags build_local -o $(BINDIR)/$(NAME)-$@ darwin-amd64: - GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ + $(XGOCMD) -dest=$(BINDIR) -out=$(NAME) -trimpath=true -ldflags=$(RELEASE_LDFLAGS) -targets=darwin-10.12/amd64 $(BUILD_PACKAGE) && \ + mv $(BINDIR)/$(NAME)-darwin-10.12-amd64 $(BINDIR)/$(NAME)-darwin-amd64 darwin-arm64: - GOARCH=arm64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ + $(XGOCMD) -dest=$(BINDIR) -out=$(NAME) -trimpath=true -ldflags=$(RELEASE_LDFLAGS) -targets=darwin-11.1/arm64 $(BUILD_PACKAGE) && \ + mv $(BINDIR)/$(NAME)-darwin-11.1-arm64 $(BINDIR)/$(NAME)-darwin-arm64 linux-386: - GOARCH=386 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ + $(XGOCMD) -dest=$(BINDIR) -out=$(NAME) -trimpath=true -ldflags=$(STATIC_LDFLAGS) -targets=linux/386 $(BUILD_PACKAGE) linux-amd64: - GOARCH=amd64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ + $(GOBUILD) -ldflags $(RELEASE_LDFLAGS) -o $(BINDIR)/$(NAME)-$@ + #GOARCH=amd64 GOOS=linux $(GOBUILD) -ldflags $(RELEASE_LDFLAGS) -o $(BINDIR)/$(NAME)-$@ + #$(XGOCMD) -dest=$(BINDIR) -out=$(NAME) -trimpath=true -ldflags=$(STATIC_LDFLAGS) -targets=linux/amd64 $(BUILD_PACKAGE) -linux-armv5: - GOARCH=arm GOOS=linux GOARM=5 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-armv6: - GOARCH=arm GOOS=linux GOARM=6 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-armv7: - GOARCH=arm GOOS=linux GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-armv8: - GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-mips-softfloat: - GOARCH=mips GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-mips-hardfloat: - GOARCH=mips GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-mipsle-softfloat: - GOARCH=mipsle GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-mipsle-hardfloat: - GOARCH=mipsle GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-mips64: - GOARCH=mips64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -linux-mips64le: - GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -freebsd-386: - GOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -freebsd-amd64: - GOARCH=amd64 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - -freebsd-arm64: - GOARCH=arm64 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ +linux-arm64: + $(XGOCMD) -dest=$(BINDIR) -out=$(NAME) -trimpath=true -ldflags=$(STATIC_LDFLAGS) -targets=linux/arm64 $(BUILD_PACKAGE) windows-386: - GOARCH=386 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe + $(XGOCMD) -dest=$(BINDIR) -out=$(NAME) -trimpath=true -ldflags=$(RELEASE_LDFLAGS) -targets=windows-4.0/386 $(BUILD_PACKAGE) && \ + mv $(BINDIR)/$(NAME)-windows-4.0-386.exe $(BINDIR)/$(NAME)-windows-386.exe windows-amd64: - GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe + $(XGOCMD) -dest=$(BINDIR) -out=$(NAME) -trimpath=true -ldflags=$(RELEASE_LDFLAGS) -targets=windows-4.0/amd64 $(BUILD_PACKAGE) && \ + mv $(BINDIR)/$(NAME)-windows-4.0-amd64.exe $(BINDIR)/$(NAME)-windows-amd64.exe -windows-arm64: - GOARCH=arm64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe - -windows-arm32v7: - GOARCH=arm GOOS=windows GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe +#windows-arm64: +# $(XGOCMD) -dest=$(BINDIR) -out=$(NAME) -trimpath=true -ldflags=$(RELEASE_LDFLAGS) -targets=windows/arm64 $(BUILD_PACKAGE) +# mv $(NAME)-windows-4.0-arm64.exe $(NAME)-windows-arm64.exe gz_releases=$(addsuffix .gz, $(PLATFORM_LIST)) zip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST)) @@ -113,8 +77,18 @@ all-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST) releases: $(gz_releases) $(zip_releases) +vet: + $(GOCMD) vet -tags build_local ./... + lint: - golangci-lint run --disable-all -E govet -E gofumpt -E megacheck ./... + golangci-lint run --build-tags=build_local --disable-all -E govet -E gofumpt -E megacheck ./... clean: - rm $(BINDIR)/* + rm -rf $(BINDIR)/ + mkdir -p $(BINDIR) + +cleancache: + # go build cache may need to cleanup if changing C source code + $(GOCLEAN) -cache + rm -rf $(BINDIR)/ + mkdir -p $(BINDIR) \ No newline at end of file diff --git a/README.md b/README.md index c0d7c7f7..7090f752 100644 --- a/README.md +++ b/README.md @@ -32,17 +32,254 @@ - Netfilter TCP redirecting. Deploy Clash on your Internet gateway with `iptables`. - Comprehensive HTTP RESTful API controller -## Premium Features - -- TUN mode on macOS, Linux and Windows. [Doc](https://github.com/Dreamacro/clash/wiki/premium-core-features#tun-device) -- Match your tunnel by [Script](https://github.com/Dreamacro/clash/wiki/premium-core-features#script) -- [Rule Provider](https://github.com/Dreamacro/clash/wiki/premium-core-features#rule-providers) - ## Getting Started Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash/wiki). -## Premium Release -[Release](https://github.com/Dreamacro/clash/releases/tag/premium) +## Advanced usage for this branch +### DNS configuration +Support resolve ip with a proxy tunnel. + +Support `geosite` with `fallback-filter`. +```yaml +dns: + enable: true + use-hosts: true + ipv6: false + enhanced-mode: fake-ip + fake-ip-range: 198.18.0.1/16 + listen: 127.0.0.1:6868 + default-nameserver: + - 119.29.29.29 + - 114.114.114.114 + nameserver: + - https://doh.pub/dns-query + - tls://223.5.5.5:853 + fallback: + - 'https://1.0.0.1/dns-query#Proxy' # append the proxy adapter name to the end of DNS URL with '#' prefix. + - 'tls://8.8.4.4:853#Proxy' + fallback-filter: + geoip: false + geosite: + - gfw # `geosite` filter only use fallback server to resolve ip, prevent DNS leaks to unsafe DNS providers. + domain: + - +.example.com + ipcidr: + - 0.0.0.0/32 +``` + +### TUN configuration +Supports macOS, Linux and Windows. + +Support lwIP stack, a lightweight TCP/IP stack, it's recommended set to tun. + +On Windows, you should download the [Wintun](https://www.wintun.net) driver and copy `wintun.dll` into Clash home directory. +```yaml +# Enable the TUN listener +tun: + enable: true + stack: lwip # lwip(recommended), system or gvisor + dns-listen: 0.0.0.0:53 # additional dns server listen on TUN + auto-route: true # auto set global route +``` +### Rules configuration +- Support rule `GEOSITE`. +- Support rule `SCRIPT`. +- Support `multiport` condition for rule `SRC-PORT` and `DST-PORT`. +- Support `network` condition for all rules. +- Support source IPCIDR condition for all rules, just append to the end. + +The `GEOSITE` databases via https://github.com/Loyalsoldier/v2ray-rules-dat. +```yaml +mode: rule + +script: + shortcuts: + quic: 'network == "udp" and dst_port == 443' + privacy: '"analytics" in host or "adservice" in host or "firebase" in host or "safebrowsing" in host or "doubleclick" in host' + +rules: + # rule SCRIPT + - SCRIPT,quic,REJECT # Disable QUIC, same as rule "DST-PORT,443,REJECT,udp" + - SCRIPT,privacy,REJECT + + # network(tcp/udp) condition for all rules + - DOMAIN-SUFFIX,bilibili.com,DIRECT,tcp + - DOMAIN-SUFFIX,bilibili.com,REJECT,udp + + # multiport condition for rules SRC-PORT and DST-PORT + - DST-PORT,123/136/137-139,DIRECT,udp + + # rule GEOSITE + - GEOSITE,category-ads-all,REJECT + - GEOSITE,icloud@cn,DIRECT + - GEOSITE,apple@cn,DIRECT + - GEOSITE,apple-cn,DIRECT + - GEOSITE,microsoft@cn,DIRECT + - GEOSITE,facebook,PROXY + - GEOSITE,youtube,PROXY + - GEOSITE,geolocation-cn,DIRECT + - GEOSITE,geolocation-!cn,PROXY + + # source IPCIDR condition for all rules in gateway proxy + #- GEOSITE,geolocation-!cn,REJECT,192.168.1.88/32,192.168.1.99/32 + + - GEOIP,telegram,PROXY,no-resolve + - GEOIP,private,DIRECT,no-resolve + - GEOIP,cn,DIRECT + + - MATCH,PROXY +``` + +### Script configuration +Script enables users to programmatically select a policy for the packets with more flexibility. + +```yaml +mode: script + +rules: + # the rule GEOSITE just as a rule provider in mode script + - GEOSITE,category-ads-all,Whatever + - GEOSITE,youtube,Whatever + - GEOSITE,geolocation-cn,Whatever + +script: + code: | + def main(ctx, metadata): + if metadata["process_name"] == 'apsd': + return "DIRECT" + + if metadata["network"] == 'udp' and metadata["dst_port"] == 443: + return "REJECT" + + host = metadata["host"] + for kw in ['analytics', 'adservice', 'firebase', 'bugly', 'safebrowsing', 'doubleclick']: + if kw in host: + return "REJECT" + + now = time.now() + if (now.hour < 8 or now.hour > 17) and metadata["src_ip"] == '192.168.1.99': + return "REJECT" + + if ctx.rule_providers["geosite:category-ads-all"].match(metadata): + return "REJECT" + + if ctx.rule_providers["geosite:youtube"].match(metadata): + ctx.log('[Script] domain %s matched youtube' % host) + return "Proxy" + + if ctx.rule_providers["geosite:geolocation-cn"].match(metadata): + ctx.log('[Script] domain %s matched geolocation-cn' % host) + return "DIRECT" + + ip = metadata["dst_ip"] + if host != "": + ip = ctx.resolve_ip(host) + if ip == "": + return "Proxy" + + code = ctx.geoip(ip) + if code == "LAN" or code == "CN": + return "DIRECT" + + return "Proxy" # default policy for requests which are not matched by any other script +``` +the context and metadata +```ts +interface Metadata { + type: string // socks5、http + network: string // tcp + host: string + process_name: string + src_ip: string + src_port: int + dst_ip: string + dst_port: int +} + +interface Context { + resolve_ip: (host: string) => string // ip string + geoip: (ip: string) => string // country code + log: (log: string) => void + rule_providers: Record boolean }> +} +``` + +### Proxies configuration +Support outbound transport protocol `VLESS`. + +The XTLS only support TCP transport by the XRAY-CORE. +```yaml +proxies: + - name: "vless-tcp" + type: vless + server: server + port: 443 + uuid: uuid + network: tcp + servername: example.com # AKA SNI + # flow: xtls-rprx-direct # xtls-rprx-origin # enable XTLS + # skip-cert-verify: true + + - name: "vless-ws" + type: vless + server: server + port: 443 + uuid: uuid + udp: true + network: ws + servername: example.com # priority over wss host + # skip-cert-verify: true + ws-path: /path + ws-headers: + Host: example.com +``` + +### IPTABLES auto-configuration +Only work on Linux OS who support `iptables`, Clash will auto-configuration iptables for tproxy listener when `tproxy-port` value isn't zero. + +If `TPROXY` is enabled, the `TUN` must be disabled. +```yaml +# Enable the TPROXY listener +tproxy-port: 9898 +# Disable the TUN listener +tun: + enable: false +``` +Create user given name `clash`. + +Run Clash by user `clash` as a daemon. + +Create the systemd configuration file at /etc/systemd/system/clash.service: +``` +[Unit] +Description=Clash daemon, A rule-based proxy in Go. +After=network.target + +[Service] +Type=simple +User=clash +Group=clash +CapabilityBoundingSet=cap_net_admin +AmbientCapabilities=cap_net_admin +Restart=always +ExecStart=/usr/local/bin/clash -d /etc/clash + +[Install] +WantedBy=multi-user.target +``` +Launch clashd on system startup with: +```shell +$ systemctl enable clash +``` +Launch clashd immediately with: +```shell +$ systemctl start clash +``` + +### Display Process name +Clash add field `Process` to `Metadata` and prepare to get process name for Restful API `GET /connections`. + +To display process name in GUI please use https://yaling888.github.io/yacd/. ## Development If you want to build an application that uses clash as a library, check out the the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/use-clash-as-a-library) diff --git a/adapter/parser.go b/adapter/parser.go index 62d15225..70f589e4 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -60,6 +60,13 @@ func ParseProxy(mapping map[string]interface{}) (C.Proxy, error) { break } proxy, err = outbound.NewVmess(*vmessOption) + case "vless": + vlessOption := &outbound.VlessOption{} + err = decoder.Decode(mapping, vlessOption) + if err != nil { + break + } + proxy, err = outbound.NewVless(*vlessOption) case "snell": snellOption := &outbound.SnellOption{} err = decoder.Decode(mapping, snellOption) diff --git a/component/dialer/bind_darwin.go b/component/dialer/bind_darwin.go index b3ae9d81..bf51b305 100644 --- a/component/dialer/bind_darwin.go +++ b/component/dialer/bind_darwin.go @@ -27,14 +27,21 @@ func bindControl(ifaceIdx int, chain controlFn) controlFn { } } - return c.Control(func(fd uintptr) { + var innerErr error + err = c.Control(func(fd uintptr) { switch network { case "tcp4", "udp4": - unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, ifaceIdx) + innerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, ifaceIdx) case "tcp6", "udp6": - unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, ifaceIdx) + innerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, ifaceIdx) } }) + + if innerErr != nil { + err = innerErr + } + + return } } diff --git a/component/dialer/bind_linux.go b/component/dialer/bind_linux.go index ae772a71..97d37cfa 100644 --- a/component/dialer/bind_linux.go +++ b/component/dialer/bind_linux.go @@ -25,9 +25,16 @@ func bindControl(ifaceName string, chain controlFn) controlFn { } } - return c.Control(func(fd uintptr) { - unix.BindToDevice(int(fd), ifaceName) + var innerErr error + err = c.Control(func(fd uintptr) { + innerErr = unix.BindToDevice(int(fd), ifaceName) }) + + if innerErr != nil { + err = innerErr + } + + return } } diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index c84fcaa4..211db5e0 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -9,6 +9,18 @@ import ( ) func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) { + opt := &option{ + interfaceName: DefaultInterface.Load(), + } + + for _, o := range DefaultOptions { + o(opt) + } + + for _, o := range options { + o(opt) + } + switch network { case "tcp4", "tcp6", "udp4", "udp6": host, port, err := net.SplitHostPort(address) @@ -19,17 +31,25 @@ func DialContext(ctx context.Context, network, address string, options ...Option var ip net.IP switch network { case "tcp4", "udp4": - ip, err = resolver.ResolveIPv4(host) + if opt.interfaceName != "" { + ip, err = resolver.ResolveIPv4WithMain(host) + } else { + ip, err = resolver.ResolveIPv4(host) + } default: - ip, err = resolver.ResolveIPv6(host) + if opt.interfaceName != "" { + ip, err = resolver.ResolveIPv6WithMain(host) + } else { + ip, err = resolver.ResolveIPv6(host) + } } if err != nil { return nil, err } - return dialContext(ctx, network, ip, port, options) + return dialContext(ctx, network, ip, port, opt) case "tcp", "udp": - return dualStackDialContext(ctx, network, address, options) + return dualStackDialContext(ctx, network, address, opt) default: return nil, errors.New("network invalid") } @@ -66,19 +86,7 @@ func ListenPacket(ctx context.Context, network, address string, options ...Optio return lc.ListenPacket(ctx, network, address) } -func dialContext(ctx context.Context, network string, destination net.IP, port string, options []Option) (net.Conn, error) { - opt := &option{ - interfaceName: DefaultInterface.Load(), - } - - for _, o := range DefaultOptions { - o(opt) - } - - for _, o := range options { - o(opt) - } - +func dialContext(ctx context.Context, network string, destination net.IP, port string, opt *option) (net.Conn, error) { dialer := &net.Dialer{} if opt.interfaceName != "" { if err := bindIfaceToDialer(opt.interfaceName, dialer, network, destination); err != nil { @@ -92,7 +100,7 @@ func dialContext(ctx context.Context, network string, destination net.IP, port s return dialer.DialContext(ctx, network, net.JoinHostPort(destination.String(), port)) } -func dualStackDialContext(ctx context.Context, network, address string, options []Option) (net.Conn, error) { +func dualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { host, port, err := net.SplitHostPort(address) if err != nil { return nil, err @@ -125,16 +133,24 @@ func dualStackDialContext(ctx context.Context, network, address string, options var ip net.IP if ipv6 { - ip, result.error = resolver.ResolveIPv6(host) + if opt.interfaceName != "" { + ip, result.error = resolver.ResolveIPv6WithMain(host) + } else { + ip, result.error = resolver.ResolveIPv6(host) + } } else { - ip, result.error = resolver.ResolveIPv4(host) + if opt.interfaceName != "" { + ip, result.error = resolver.ResolveIPv4WithMain(host) + } else { + ip, result.error = resolver.ResolveIPv4(host) + } } if result.error != nil { return } result.resolved = true - result.Conn, result.error = dialContext(ctx, network, ip, port, options) + result.Conn, result.error = dialContext(ctx, network, ip, port, opt) } go startRacer(ctx, network+"4", host, false) diff --git a/component/geodata/attr.go b/component/geodata/attr.go new file mode 100644 index 00000000..e35a25ca --- /dev/null +++ b/component/geodata/attr.go @@ -0,0 +1,51 @@ +package geodata + +import ( + "strings" + + "github.com/Dreamacro/clash/component/geodata/router" +) + +type AttributeList struct { + matcher []AttributeMatcher +} + +func (al *AttributeList) Match(domain *router.Domain) bool { + for _, matcher := range al.matcher { + if !matcher.Match(domain) { + return false + } + } + return true +} + +func (al *AttributeList) IsEmpty() bool { + return len(al.matcher) == 0 +} + +func parseAttrs(attrs []string) *AttributeList { + al := new(AttributeList) + for _, attr := range attrs { + trimmedAttr := strings.ToLower(strings.TrimSpace(attr)) + if len(trimmedAttr) == 0 { + continue + } + al.matcher = append(al.matcher, BooleanMatcher(trimmedAttr)) + } + return al +} + +type AttributeMatcher interface { + Match(*router.Domain) bool +} + +type BooleanMatcher string + +func (m BooleanMatcher) Match(domain *router.Domain) bool { + for _, attr := range domain.Attribute { + if strings.EqualFold(attr.GetKey(), string(m)) { + return true + } + } + return false +} diff --git a/component/geodata/geodata.go b/component/geodata/geodata.go new file mode 100644 index 00000000..cdd141fb --- /dev/null +++ b/component/geodata/geodata.go @@ -0,0 +1,86 @@ +package geodata + +import ( + "errors" + "fmt" + "strings" + + "github.com/Dreamacro/clash/component/geodata/router" + "github.com/Dreamacro/clash/log" +) + +type loader struct { + LoaderImplementation +} + +func (l *loader) LoadGeoSite(list string) ([]*router.Domain, error) { + return l.LoadGeoSiteWithAttr("geosite.dat", list) +} + +func (l *loader) LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) { + parts := strings.Split(siteWithAttr, "@") + if len(parts) == 0 { + return nil, errors.New("empty rule") + } + list := strings.TrimSpace(parts[0]) + attrVal := parts[1:] + + if len(list) == 0 { + return nil, fmt.Errorf("empty listname in rule: %s", siteWithAttr) + } + + domains, err := l.LoadSite(file, list) + if err != nil { + return nil, err + } + + attrs := parseAttrs(attrVal) + if attrs.IsEmpty() { + if strings.Contains(siteWithAttr, "@") { + log.Warnln("empty attribute list: %s", siteWithAttr) + } + return domains, nil + } + + filteredDomains := make([]*router.Domain, 0, len(domains)) + hasAttrMatched := false + for _, domain := range domains { + if attrs.Match(domain) { + hasAttrMatched = true + filteredDomains = append(filteredDomains, domain) + } + } + if !hasAttrMatched { + log.Warnln("attribute match no rule: geosite: %s", siteWithAttr) + } + + return filteredDomains, nil +} + +func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) { + return l.LoadIP("geoip.dat", country) +} + +var loaders map[string]func() LoaderImplementation + +func RegisterGeoDataLoaderImplementationCreator(name string, loader func() LoaderImplementation) { + if loaders == nil { + loaders = map[string]func() LoaderImplementation{} + } + loaders[name] = loader +} + +func getGeoDataLoaderImplementation(name string) (LoaderImplementation, error) { + if geoLoader, ok := loaders[name]; ok { + return geoLoader(), nil + } + return nil, fmt.Errorf("unable to locate GeoData loader %s", name) +} + +func GetGeoDataLoader(name string) (Loader, error) { + loadImpl, err := getGeoDataLoaderImplementation(name) + if err == nil { + return &loader{loadImpl}, nil + } + return nil, err +} diff --git a/component/geodata/geodataproto.go b/component/geodata/geodataproto.go new file mode 100644 index 00000000..a63b25bc --- /dev/null +++ b/component/geodata/geodataproto.go @@ -0,0 +1,17 @@ +package geodata + +import ( + "github.com/Dreamacro/clash/component/geodata/router" +) + +type LoaderImplementation interface { + LoadSite(filename, list string) ([]*router.Domain, error) + LoadIP(filename, country string) ([]*router.CIDR, error) +} + +type Loader interface { + LoaderImplementation + LoadGeoSite(list string) ([]*router.Domain, error) + LoadGeoSiteWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) + LoadGeoIP(country string) ([]*router.CIDR, error) +} diff --git a/component/geodata/memconservative/cache.go b/component/geodata/memconservative/cache.go new file mode 100644 index 00000000..28c2c238 --- /dev/null +++ b/component/geodata/memconservative/cache.go @@ -0,0 +1,142 @@ +package memconservative + +import ( + "fmt" + "os" + "strings" + + "github.com/Dreamacro/clash/component/geodata/router" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "google.golang.org/protobuf/proto" +) + +type GeoIPCache map[string]*router.GeoIP + +func (g GeoIPCache) Has(key string) bool { + return !(g.Get(key) == nil) +} + +func (g GeoIPCache) Get(key string) *router.GeoIP { + if g == nil { + return nil + } + return g[key] +} + +func (g GeoIPCache) Set(key string, value *router.GeoIP) { + if g == nil { + g = make(map[string]*router.GeoIP) + } + g[key] = value +} + +func (g GeoIPCache) Unmarshal(filename, code string) (*router.GeoIP, error) { + asset := C.Path.GetAssetLocation(filename) + idx := strings.ToLower(asset + ":" + code) + if g.Has(idx) { + return g.Get(idx), nil + } + + geoipBytes, err := Decode(asset, code) + switch err { + case nil: + var geoip router.GeoIP + if err := proto.Unmarshal(geoipBytes, &geoip); err != nil { + return nil, err + } + g.Set(idx, &geoip) + return &geoip, nil + + case errCodeNotFound: + return nil, fmt.Errorf("country code %s%s%s", code, " not found in ", filename) + + case errFailedToReadBytes, errFailedToReadExpectedLenBytes, + errInvalidGeodataFile, errInvalidGeodataVarintLength: + log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method") + geoipBytes, err = os.ReadFile(asset) + if err != nil { + return nil, err + } + var geoipList router.GeoIPList + if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { + return nil, err + } + for _, geoip := range geoipList.GetEntry() { + if strings.EqualFold(code, geoip.GetCountryCode()) { + g.Set(idx, geoip) + return geoip, nil + } + } + + default: + return nil, err + } + + return nil, fmt.Errorf("country code %s%s%s", code, " not found in ", filename) +} + +type GeoSiteCache map[string]*router.GeoSite + +func (g GeoSiteCache) Has(key string) bool { + return !(g.Get(key) == nil) +} + +func (g GeoSiteCache) Get(key string) *router.GeoSite { + if g == nil { + return nil + } + return g[key] +} + +func (g GeoSiteCache) Set(key string, value *router.GeoSite) { + if g == nil { + g = make(map[string]*router.GeoSite) + } + g[key] = value +} + +func (g GeoSiteCache) Unmarshal(filename, code string) (*router.GeoSite, error) { + asset := C.Path.GetAssetLocation(filename) + idx := strings.ToLower(asset + ":" + code) + if g.Has(idx) { + return g.Get(idx), nil + } + + geositeBytes, err := Decode(asset, code) + switch err { + case nil: + var geosite router.GeoSite + if err := proto.Unmarshal(geositeBytes, &geosite); err != nil { + return nil, err + } + g.Set(idx, &geosite) + return &geosite, nil + + case errCodeNotFound: + return nil, fmt.Errorf("list %s%s%s", code, " not found in ", filename) + + case errFailedToReadBytes, errFailedToReadExpectedLenBytes, + errInvalidGeodataFile, errInvalidGeodataVarintLength: + log.Warnln("failed to decode geoip file: %s%s", filename, ", fallback to the original ReadFile method") + geositeBytes, err = os.ReadFile(asset) + if err != nil { + return nil, err + } + var geositeList router.GeoSiteList + if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { + return nil, err + } + for _, geosite := range geositeList.GetEntry() { + if strings.EqualFold(code, geosite.GetCountryCode()) { + g.Set(idx, geosite) + return geosite, nil + } + } + + default: + return nil, err + } + + return nil, fmt.Errorf("list %s%s%s", code, " not found in ", filename) +} diff --git a/component/geodata/memconservative/decode.go b/component/geodata/memconservative/decode.go new file mode 100644 index 00000000..7eb86400 --- /dev/null +++ b/component/geodata/memconservative/decode.go @@ -0,0 +1,107 @@ +package memconservative + +import ( + "errors" + "fmt" + "io" + "os" + "strings" + + "google.golang.org/protobuf/encoding/protowire" +) + +var ( + errFailedToReadBytes = errors.New("failed to read bytes") + errFailedToReadExpectedLenBytes = errors.New("failed to read expected length of bytes") + errInvalidGeodataFile = errors.New("invalid geodata file") + errInvalidGeodataVarintLength = errors.New("invalid geodata varint length") + errCodeNotFound = errors.New("code not found") +) + +func emitBytes(f io.ReadSeeker, code string) ([]byte, error) { + count := 1 + isInner := false + tempContainer := make([]byte, 0, 5) + + var result []byte + var advancedN uint64 = 1 + var geoDataVarintLength, codeVarintLength, varintLenByteLen uint64 = 0, 0, 0 + +Loop: + for { + container := make([]byte, advancedN) + bytesRead, err := f.Read(container) + if err == io.EOF { + return nil, errCodeNotFound + } + if err != nil { + return nil, errFailedToReadBytes + } + if bytesRead != len(container) { + return nil, errFailedToReadExpectedLenBytes + } + + switch count { + case 1, 3: // data type ((field_number << 3) | wire_type) + if container[0] != 10 { // byte `0A` equals to `10` in decimal + return nil, errInvalidGeodataFile + } + advancedN = 1 + count++ + case 2, 4: // data length + tempContainer = append(tempContainer, container...) + if container[0] > 127 { // max one-byte-length byte `7F`(0FFF FFFF) equals to `127` in decimal + advancedN = 1 + goto Loop + } + lenVarint, n := protowire.ConsumeVarint(tempContainer) + if n < 0 { + return nil, errInvalidGeodataVarintLength + } + tempContainer = nil + if !isInner { + isInner = true + geoDataVarintLength = lenVarint + advancedN = 1 + } else { + isInner = false + codeVarintLength = lenVarint + varintLenByteLen = uint64(n) + advancedN = codeVarintLength + } + count++ + case 5: // data value + if strings.EqualFold(string(container), code) { + count++ + offset := -(1 + int64(varintLenByteLen) + int64(codeVarintLength)) + _, _ = f.Seek(offset, 1) // back to the start of GeoIP or GeoSite varint + advancedN = geoDataVarintLength // the number of bytes to be read in next round + } else { + count = 1 + offset := int64(geoDataVarintLength) - int64(codeVarintLength) - int64(varintLenByteLen) - 1 + _, _ = f.Seek(offset, 1) // skip the unmatched GeoIP or GeoSite varint + advancedN = 1 // the next round will be the start of another GeoIPList or GeoSiteList + } + case 6: // matched GeoIP or GeoSite varint + result = container + break Loop + } + } + return result, nil +} + +func Decode(filename, code string) ([]byte, error) { + f, err := os.Open(filename) + if err != nil { + return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error()) + } + defer func(f *os.File) { + _ = f.Close() + }(f) + + geoBytes, err := emitBytes(f, code) + if err != nil { + return nil, err + } + return geoBytes, nil +} diff --git a/component/geodata/memconservative/memc.go b/component/geodata/memconservative/memc.go new file mode 100644 index 00000000..2961f6eb --- /dev/null +++ b/component/geodata/memconservative/memc.go @@ -0,0 +1,40 @@ +package memconservative + +import ( + "fmt" + "runtime" + + "github.com/Dreamacro/clash/component/geodata" + "github.com/Dreamacro/clash/component/geodata/router" +) + +type memConservativeLoader struct { + geoipcache GeoIPCache + geositecache GeoSiteCache +} + +func (m *memConservativeLoader) LoadIP(filename, country string) ([]*router.CIDR, error) { + defer runtime.GC() + geoip, err := m.geoipcache.Unmarshal(filename, country) + if err != nil { + return nil, fmt.Errorf("failed to decode geodata file: %s, base error: %s", filename, err.Error()) + } + return geoip.Cidr, nil +} + +func (m *memConservativeLoader) LoadSite(filename, list string) ([]*router.Domain, error) { + defer runtime.GC() + geosite, err := m.geositecache.Unmarshal(filename, list) + if err != nil { + return nil, fmt.Errorf("failed to decode geodata file: %s, base error: %s", filename, err.Error()) + } + return geosite.Domain, nil +} + +func newMemConservativeLoader() geodata.LoaderImplementation { + return &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)} +} + +func init() { + geodata.RegisterGeoDataLoaderImplementationCreator("memconservative", newMemConservativeLoader) +} diff --git a/component/geodata/package_info.go b/component/geodata/package_info.go new file mode 100644 index 00000000..4644ccc2 --- /dev/null +++ b/component/geodata/package_info.go @@ -0,0 +1,4 @@ +// Modified from: https://github.com/v2fly/v2ray-core/tree/master/infra/conf/geodata +// License: MIT + +package geodata diff --git a/component/geodata/router/condition.go b/component/geodata/router/condition.go new file mode 100644 index 00000000..4b63ddfc --- /dev/null +++ b/component/geodata/router/condition.go @@ -0,0 +1,71 @@ +package router + +import ( + "fmt" + "strings" + + "github.com/Dreamacro/clash/component/geodata/strmatcher" +) + +var matcherTypeMap = map[Domain_Type]strmatcher.Type{ + Domain_Plain: strmatcher.Substr, + Domain_Regex: strmatcher.Regex, + Domain_Domain: strmatcher.Domain, + Domain_Full: strmatcher.Full, +} + +func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) { + matcherType, f := matcherTypeMap[domain.Type] + if !f { + return nil, fmt.Errorf("unsupported domain type %v", domain.Type) + } + + matcher, err := matcherType.New(domain.Value) + if err != nil { + return nil, fmt.Errorf("failed to create domain matcher, base error: %s", err.Error()) + } + + return matcher, nil +} + +type DomainMatcher struct { + matchers strmatcher.IndexMatcher +} + +func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) { + g := strmatcher.NewMphMatcherGroup() + for _, d := range domains { + matcherType, f := matcherTypeMap[d.Type] + if !f { + return nil, fmt.Errorf("unsupported domain type %v", d.Type) + } + _, err := g.AddPattern(d.Value, matcherType) + if err != nil { + return nil, err + } + } + g.Build() + return &DomainMatcher{ + matchers: g, + }, nil +} + +// NewDomainMatcher new domain matcher. +func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) { + g := new(strmatcher.MatcherGroup) + for _, d := range domains { + m, err := domainToMatcher(d) + if err != nil { + return nil, err + } + g.Add(m) + } + + return &DomainMatcher{ + matchers: g, + }, nil +} + +func (m *DomainMatcher) ApplyDomain(domain string) bool { + return len(m.matchers.Match(strings.ToLower(domain))) > 0 +} diff --git a/component/geodata/router/config.pb.go b/component/geodata/router/config.pb.go new file mode 100644 index 00000000..b0e8f20f --- /dev/null +++ b/component/geodata/router/config.pb.go @@ -0,0 +1,725 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: component/geodata/router/config.proto + +package router + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Type of domain value. +type Domain_Type int32 + +const ( + // The value is used as is. + Domain_Plain Domain_Type = 0 + // The value is used as a regular expression. + Domain_Regex Domain_Type = 1 + // The value is a root domain. + Domain_Domain Domain_Type = 2 + // The value is a domain. + Domain_Full Domain_Type = 3 +) + +// Enum value maps for Domain_Type. +var ( + Domain_Type_name = map[int32]string{ + 0: "Plain", + 1: "Regex", + 2: "Domain", + 3: "Full", + } + Domain_Type_value = map[string]int32{ + "Plain": 0, + "Regex": 1, + "Domain": 2, + "Full": 3, + } +) + +func (x Domain_Type) Enum() *Domain_Type { + p := new(Domain_Type) + *p = x + return p +} + +func (x Domain_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Domain_Type) Descriptor() protoreflect.EnumDescriptor { + return file_component_geodata_router_config_proto_enumTypes[0].Descriptor() +} + +func (Domain_Type) Type() protoreflect.EnumType { + return &file_component_geodata_router_config_proto_enumTypes[0] +} + +func (x Domain_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Domain_Type.Descriptor instead. +func (Domain_Type) EnumDescriptor() ([]byte, []int) { + return file_component_geodata_router_config_proto_rawDescGZIP(), []int{0, 0} +} + +// Domain for routing decision. +type Domain struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Domain matching type. + Type Domain_Type `protobuf:"varint,1,opt,name=type,proto3,enum=clash.component.geodata.router.Domain_Type" json:"type,omitempty"` + // Domain value. + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + // Attributes of this domain. May be used for filtering. + Attribute []*Domain_Attribute `protobuf:"bytes,3,rep,name=attribute,proto3" json:"attribute,omitempty"` +} + +func (x *Domain) Reset() { + *x = Domain{} + if protoimpl.UnsafeEnabled { + mi := &file_component_geodata_router_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Domain) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Domain) ProtoMessage() {} + +func (x *Domain) ProtoReflect() protoreflect.Message { + mi := &file_component_geodata_router_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Domain.ProtoReflect.Descriptor instead. +func (*Domain) Descriptor() ([]byte, []int) { + return file_component_geodata_router_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Domain) GetType() Domain_Type { + if x != nil { + return x.Type + } + return Domain_Plain +} + +func (x *Domain) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +func (x *Domain) GetAttribute() []*Domain_Attribute { + if x != nil { + return x.Attribute + } + return nil +} + +// IP for routing decision, in CIDR form. +type CIDR struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // IP address, should be either 4 or 16 bytes. + Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` + // Number of leading ones in the network mask. + Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"` +} + +func (x *CIDR) Reset() { + *x = CIDR{} + if protoimpl.UnsafeEnabled { + mi := &file_component_geodata_router_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CIDR) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CIDR) ProtoMessage() {} + +func (x *CIDR) ProtoReflect() protoreflect.Message { + mi := &file_component_geodata_router_config_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CIDR.ProtoReflect.Descriptor instead. +func (*CIDR) Descriptor() ([]byte, []int) { + return file_component_geodata_router_config_proto_rawDescGZIP(), []int{1} +} + +func (x *CIDR) GetIp() []byte { + if x != nil { + return x.Ip + } + return nil +} + +func (x *CIDR) GetPrefix() uint32 { + if x != nil { + return x.Prefix + } + return 0 +} + +type GeoIP struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"` + Cidr []*CIDR `protobuf:"bytes,2,rep,name=cidr,proto3" json:"cidr,omitempty"` + ReverseMatch bool `protobuf:"varint,3,opt,name=reverse_match,json=reverseMatch,proto3" json:"reverse_match,omitempty"` +} + +func (x *GeoIP) Reset() { + *x = GeoIP{} + if protoimpl.UnsafeEnabled { + mi := &file_component_geodata_router_config_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GeoIP) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GeoIP) ProtoMessage() {} + +func (x *GeoIP) ProtoReflect() protoreflect.Message { + mi := &file_component_geodata_router_config_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GeoIP.ProtoReflect.Descriptor instead. +func (*GeoIP) Descriptor() ([]byte, []int) { + return file_component_geodata_router_config_proto_rawDescGZIP(), []int{2} +} + +func (x *GeoIP) GetCountryCode() string { + if x != nil { + return x.CountryCode + } + return "" +} + +func (x *GeoIP) GetCidr() []*CIDR { + if x != nil { + return x.Cidr + } + return nil +} + +func (x *GeoIP) GetReverseMatch() bool { + if x != nil { + return x.ReverseMatch + } + return false +} + +type GeoIPList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Entry []*GeoIP `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"` +} + +func (x *GeoIPList) Reset() { + *x = GeoIPList{} + if protoimpl.UnsafeEnabled { + mi := &file_component_geodata_router_config_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GeoIPList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GeoIPList) ProtoMessage() {} + +func (x *GeoIPList) ProtoReflect() protoreflect.Message { + mi := &file_component_geodata_router_config_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GeoIPList.ProtoReflect.Descriptor instead. +func (*GeoIPList) Descriptor() ([]byte, []int) { + return file_component_geodata_router_config_proto_rawDescGZIP(), []int{3} +} + +func (x *GeoIPList) GetEntry() []*GeoIP { + if x != nil { + return x.Entry + } + return nil +} + +type GeoSite struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"` + Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"` +} + +func (x *GeoSite) Reset() { + *x = GeoSite{} + if protoimpl.UnsafeEnabled { + mi := &file_component_geodata_router_config_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GeoSite) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GeoSite) ProtoMessage() {} + +func (x *GeoSite) ProtoReflect() protoreflect.Message { + mi := &file_component_geodata_router_config_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GeoSite.ProtoReflect.Descriptor instead. +func (*GeoSite) Descriptor() ([]byte, []int) { + return file_component_geodata_router_config_proto_rawDescGZIP(), []int{4} +} + +func (x *GeoSite) GetCountryCode() string { + if x != nil { + return x.CountryCode + } + return "" +} + +func (x *GeoSite) GetDomain() []*Domain { + if x != nil { + return x.Domain + } + return nil +} + +type GeoSiteList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Entry []*GeoSite `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"` +} + +func (x *GeoSiteList) Reset() { + *x = GeoSiteList{} + if protoimpl.UnsafeEnabled { + mi := &file_component_geodata_router_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GeoSiteList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GeoSiteList) ProtoMessage() {} + +func (x *GeoSiteList) ProtoReflect() protoreflect.Message { + mi := &file_component_geodata_router_config_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GeoSiteList.ProtoReflect.Descriptor instead. +func (*GeoSiteList) Descriptor() ([]byte, []int) { + return file_component_geodata_router_config_proto_rawDescGZIP(), []int{5} +} + +func (x *GeoSiteList) GetEntry() []*GeoSite { + if x != nil { + return x.Entry + } + return nil +} + +type Domain_Attribute struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + // Types that are assignable to TypedValue: + // *Domain_Attribute_BoolValue + // *Domain_Attribute_IntValue + TypedValue isDomain_Attribute_TypedValue `protobuf_oneof:"typed_value"` +} + +func (x *Domain_Attribute) Reset() { + *x = Domain_Attribute{} + if protoimpl.UnsafeEnabled { + mi := &file_component_geodata_router_config_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Domain_Attribute) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Domain_Attribute) ProtoMessage() {} + +func (x *Domain_Attribute) ProtoReflect() protoreflect.Message { + mi := &file_component_geodata_router_config_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Domain_Attribute.ProtoReflect.Descriptor instead. +func (*Domain_Attribute) Descriptor() ([]byte, []int) { + return file_component_geodata_router_config_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *Domain_Attribute) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (m *Domain_Attribute) GetTypedValue() isDomain_Attribute_TypedValue { + if m != nil { + return m.TypedValue + } + return nil +} + +func (x *Domain_Attribute) GetBoolValue() bool { + if x, ok := x.GetTypedValue().(*Domain_Attribute_BoolValue); ok { + return x.BoolValue + } + return false +} + +func (x *Domain_Attribute) GetIntValue() int64 { + if x, ok := x.GetTypedValue().(*Domain_Attribute_IntValue); ok { + return x.IntValue + } + return 0 +} + +type isDomain_Attribute_TypedValue interface { + isDomain_Attribute_TypedValue() +} + +type Domain_Attribute_BoolValue struct { + BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"` +} + +type Domain_Attribute_IntValue struct { + IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"` +} + +func (*Domain_Attribute_BoolValue) isDomain_Attribute_TypedValue() {} + +func (*Domain_Attribute_IntValue) isDomain_Attribute_TypedValue() {} + +var File_component_geodata_router_config_proto protoreflect.FileDescriptor + +var file_component_geodata_router_config_proto_rawDesc = []byte{ + 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2f, 0x67, 0x65, 0x6f, 0x64, + 0x61, 0x74, 0x61, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x22, 0xd1, 0x02, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x12, 0x3f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x2b, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, + 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63, + 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, + 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, + 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x1a, 0x6c, 0x0a, 0x09, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09, + 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08, + 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, + 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x32, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x09, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65, + 0x67, 0x65, 0x78, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, + 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x03, 0x22, 0x2e, 0x0a, 0x04, 0x43, + 0x49, 0x44, 0x52, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x02, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x89, 0x01, 0x0a, 0x05, + 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, + 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x52, 0x04, 0x63, 0x69, + 0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6d, 0x61, + 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, + 0x73, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x48, 0x0a, 0x09, 0x47, 0x65, 0x6f, 0x49, 0x50, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, + 0x79, 0x22, 0x6c, 0x0a, 0x07, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, + 0x3e, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x26, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, + 0x4c, 0x0a, 0x0b, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3d, + 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, + 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, + 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x7c, 0x0a, + 0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, + 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x2f, 0x63, 0x6c, 0x61, 0x73, + 0x68, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2f, 0x67, 0x65, 0x6f, 0x64, + 0x61, 0x74, 0x61, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x1e, 0x43, 0x6c, 0x61, + 0x73, 0x68, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x6f, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_component_geodata_router_config_proto_rawDescOnce sync.Once + file_component_geodata_router_config_proto_rawDescData = file_component_geodata_router_config_proto_rawDesc +) + +func file_component_geodata_router_config_proto_rawDescGZIP() []byte { + file_component_geodata_router_config_proto_rawDescOnce.Do(func() { + file_component_geodata_router_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_component_geodata_router_config_proto_rawDescData) + }) + return file_component_geodata_router_config_proto_rawDescData +} + +var file_component_geodata_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_component_geodata_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_component_geodata_router_config_proto_goTypes = []interface{}{ + (Domain_Type)(0), // 0: clash.component.geodata.router.Domain.Type + (*Domain)(nil), // 1: clash.component.geodata.router.Domain + (*CIDR)(nil), // 2: clash.component.geodata.router.CIDR + (*GeoIP)(nil), // 3: clash.component.geodata.router.GeoIP + (*GeoIPList)(nil), // 4: clash.component.geodata.router.GeoIPList + (*GeoSite)(nil), // 5: clash.component.geodata.router.GeoSite + (*GeoSiteList)(nil), // 6: clash.component.geodata.router.GeoSiteList + (*Domain_Attribute)(nil), // 7: clash.component.geodata.router.Domain.Attribute +} +var file_component_geodata_router_config_proto_depIdxs = []int32{ + 0, // 0: clash.component.geodata.router.Domain.type:type_name -> clash.component.geodata.router.Domain.Type + 7, // 1: clash.component.geodata.router.Domain.attribute:type_name -> clash.component.geodata.router.Domain.Attribute + 2, // 2: clash.component.geodata.router.GeoIP.cidr:type_name -> clash.component.geodata.router.CIDR + 3, // 3: clash.component.geodata.router.GeoIPList.entry:type_name -> clash.component.geodata.router.GeoIP + 1, // 4: clash.component.geodata.router.GeoSite.domain:type_name -> clash.component.geodata.router.Domain + 5, // 5: clash.component.geodata.router.GeoSiteList.entry:type_name -> clash.component.geodata.router.GeoSite + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_component_geodata_router_config_proto_init() } +func file_component_geodata_router_config_proto_init() { + if File_component_geodata_router_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_component_geodata_router_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Domain); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_component_geodata_router_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CIDR); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_component_geodata_router_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoIP); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_component_geodata_router_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoIPList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_component_geodata_router_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoSite); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_component_geodata_router_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GeoSiteList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_component_geodata_router_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Domain_Attribute); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_component_geodata_router_config_proto_msgTypes[6].OneofWrappers = []interface{}{ + (*Domain_Attribute_BoolValue)(nil), + (*Domain_Attribute_IntValue)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_component_geodata_router_config_proto_rawDesc, + NumEnums: 1, + NumMessages: 7, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_component_geodata_router_config_proto_goTypes, + DependencyIndexes: file_component_geodata_router_config_proto_depIdxs, + EnumInfos: file_component_geodata_router_config_proto_enumTypes, + MessageInfos: file_component_geodata_router_config_proto_msgTypes, + }.Build() + File_component_geodata_router_config_proto = out.File + file_component_geodata_router_config_proto_rawDesc = nil + file_component_geodata_router_config_proto_goTypes = nil + file_component_geodata_router_config_proto_depIdxs = nil +} diff --git a/component/geodata/router/config.proto b/component/geodata/router/config.proto new file mode 100644 index 00000000..245faadf --- /dev/null +++ b/component/geodata/router/config.proto @@ -0,0 +1,68 @@ +syntax = "proto3"; + +package clash.component.geodata.router; +option csharp_namespace = "Clash.Component.Geodata.Router"; +option go_package = "github.com/Dreamacro/clash/component/geodata/router"; +option java_package = "com.clash.component.geodata.router"; +option java_multiple_files = true; + +// Domain for routing decision. +message Domain { + // Type of domain value. + enum Type { + // The value is used as is. + Plain = 0; + // The value is used as a regular expression. + Regex = 1; + // The value is a root domain. + Domain = 2; + // The value is a domain. + Full = 3; + } + + // Domain matching type. + Type type = 1; + + // Domain value. + string value = 2; + + message Attribute { + string key = 1; + + oneof typed_value { + bool bool_value = 2; + int64 int_value = 3; + } + } + + // Attributes of this domain. May be used for filtering. + repeated Attribute attribute = 3; +} + +// IP for routing decision, in CIDR form. +message CIDR { + // IP address, should be either 4 or 16 bytes. + bytes ip = 1; + + // Number of leading ones in the network mask. + uint32 prefix = 2; +} + +message GeoIP { + string country_code = 1; + repeated CIDR cidr = 2; + bool reverse_match = 3; +} + +message GeoIPList { + repeated GeoIP entry = 1; +} + +message GeoSite { + string country_code = 1; + repeated Domain domain = 2; +} + +message GeoSiteList { + repeated GeoSite entry = 1; +} diff --git a/component/geodata/standard/standard.go b/component/geodata/standard/standard.go new file mode 100644 index 00000000..d65dd68c --- /dev/null +++ b/component/geodata/standard/standard.go @@ -0,0 +1,83 @@ +package standard + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/Dreamacro/clash/component/geodata" + "github.com/Dreamacro/clash/component/geodata/router" + C "github.com/Dreamacro/clash/constant" + "google.golang.org/protobuf/proto" +) + +func ReadFile(path string) ([]byte, error) { + reader, err := os.Open(path) + if err != nil { + return nil, err + } + defer func(reader *os.File) { + _ = reader.Close() + }(reader) + + return io.ReadAll(reader) +} + +func ReadAsset(file string) ([]byte, error) { + return ReadFile(C.Path.GetAssetLocation(file)) +} + +func loadIP(filename, country string) ([]*router.CIDR, error) { + geoipBytes, err := ReadAsset(filename) + if err != nil { + return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error()) + } + var geoipList router.GeoIPList + if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { + return nil, err + } + + for _, geoip := range geoipList.Entry { + if strings.EqualFold(geoip.CountryCode, country) { + return geoip.Cidr, nil + } + } + + return nil, fmt.Errorf("country not found in %s%s%s", filename, ": ", country) +} + +func loadSite(filename, list string) ([]*router.Domain, error) { + geositeBytes, err := ReadAsset(filename) + if err != nil { + return nil, fmt.Errorf("failed to open file: %s, base error: %s", filename, err.Error()) + } + var geositeList router.GeoSiteList + if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { + return nil, err + } + + for _, site := range geositeList.Entry { + if strings.EqualFold(site.CountryCode, list) { + return site.Domain, nil + } + } + + return nil, fmt.Errorf("list not found in %s%s%s", filename, ": ", list) +} + +type standardLoader struct{} + +func (d standardLoader) LoadSite(filename, list string) ([]*router.Domain, error) { + return loadSite(filename, list) +} + +func (d standardLoader) LoadIP(filename, country string) ([]*router.CIDR, error) { + return loadIP(filename, country) +} + +func init() { + geodata.RegisterGeoDataLoaderImplementationCreator("standard", func() geodata.LoaderImplementation { + return standardLoader{} + }) +} diff --git a/component/geodata/strmatcher/ac_automaton_matcher.go b/component/geodata/strmatcher/ac_automaton_matcher.go new file mode 100644 index 00000000..ef0bc5d9 --- /dev/null +++ b/component/geodata/strmatcher/ac_automaton_matcher.go @@ -0,0 +1,241 @@ +package strmatcher + +import ( + "container/list" +) + +const validCharCount = 53 + +type MatchType struct { + matchType Type + exist bool +} + +const ( + TrieEdge bool = true + FailEdge bool = false +) + +type Edge struct { + edgeType bool + nextNode int +} + +type ACAutomaton struct { + trie [][validCharCount]Edge + fail []int + exists []MatchType + count int +} + +func newNode() [validCharCount]Edge { + var s [validCharCount]Edge + for i := range s { + s[i] = Edge{ + edgeType: FailEdge, + nextNode: 0, + } + } + return s +} + +var char2Index = []int{ + 'A': 0, + 'a': 0, + 'B': 1, + 'b': 1, + 'C': 2, + 'c': 2, + 'D': 3, + 'd': 3, + 'E': 4, + 'e': 4, + 'F': 5, + 'f': 5, + 'G': 6, + 'g': 6, + 'H': 7, + 'h': 7, + 'I': 8, + 'i': 8, + 'J': 9, + 'j': 9, + 'K': 10, + 'k': 10, + 'L': 11, + 'l': 11, + 'M': 12, + 'm': 12, + 'N': 13, + 'n': 13, + 'O': 14, + 'o': 14, + 'P': 15, + 'p': 15, + 'Q': 16, + 'q': 16, + 'R': 17, + 'r': 17, + 'S': 18, + 's': 18, + 'T': 19, + 't': 19, + 'U': 20, + 'u': 20, + 'V': 21, + 'v': 21, + 'W': 22, + 'w': 22, + 'X': 23, + 'x': 23, + 'Y': 24, + 'y': 24, + 'Z': 25, + 'z': 25, + '!': 26, + '$': 27, + '&': 28, + '\'': 29, + '(': 30, + ')': 31, + '*': 32, + '+': 33, + ',': 34, + ';': 35, + '=': 36, + ':': 37, + '%': 38, + '-': 39, + '.': 40, + '_': 41, + '~': 42, + '0': 43, + '1': 44, + '2': 45, + '3': 46, + '4': 47, + '5': 48, + '6': 49, + '7': 50, + '8': 51, + '9': 52, +} + +func NewACAutomaton() *ACAutomaton { + ac := new(ACAutomaton) + ac.trie = append(ac.trie, newNode()) + ac.fail = append(ac.fail, 0) + ac.exists = append(ac.exists, MatchType{ + matchType: Full, + exist: false, + }) + return ac +} + +func (ac *ACAutomaton) Add(domain string, t Type) { + node := 0 + for i := len(domain) - 1; i >= 0; i-- { + idx := char2Index[domain[i]] + if ac.trie[node][idx].nextNode == 0 { + ac.count++ + if len(ac.trie) < ac.count+1 { + ac.trie = append(ac.trie, newNode()) + ac.fail = append(ac.fail, 0) + ac.exists = append(ac.exists, MatchType{ + matchType: Full, + exist: false, + }) + } + ac.trie[node][idx] = Edge{ + edgeType: TrieEdge, + nextNode: ac.count, + } + } + node = ac.trie[node][idx].nextNode + } + ac.exists[node] = MatchType{ + matchType: t, + exist: true, + } + switch t { + case Domain: + ac.exists[node] = MatchType{ + matchType: Full, + exist: true, + } + idx := char2Index['.'] + if ac.trie[node][idx].nextNode == 0 { + ac.count++ + if len(ac.trie) < ac.count+1 { + ac.trie = append(ac.trie, newNode()) + ac.fail = append(ac.fail, 0) + ac.exists = append(ac.exists, MatchType{ + matchType: Full, + exist: false, + }) + } + ac.trie[node][idx] = Edge{ + edgeType: TrieEdge, + nextNode: ac.count, + } + } + node = ac.trie[node][idx].nextNode + ac.exists[node] = MatchType{ + matchType: t, + exist: true, + } + default: + break + } +} + +func (ac *ACAutomaton) Build() { + queue := list.New() + for i := 0; i < validCharCount; i++ { + if ac.trie[0][i].nextNode != 0 { + queue.PushBack(ac.trie[0][i]) + } + } + for { + front := queue.Front() + if front == nil { + break + } else { + node := front.Value.(Edge).nextNode + queue.Remove(front) + for i := 0; i < validCharCount; i++ { + if ac.trie[node][i].nextNode != 0 { + ac.fail[ac.trie[node][i].nextNode] = ac.trie[ac.fail[node]][i].nextNode + queue.PushBack(ac.trie[node][i]) + } else { + ac.trie[node][i] = Edge{ + edgeType: FailEdge, + nextNode: ac.trie[ac.fail[node]][i].nextNode, + } + } + } + } + } +} + +func (ac *ACAutomaton) Match(s string) bool { + node := 0 + fullMatch := true + // 1. the match string is all through trie edge. FULL MATCH or DOMAIN + // 2. the match string is through a fail edge. NOT FULL MATCH + // 2.1 Through a fail edge, but there exists a valid node. SUBSTR + for i := len(s) - 1; i >= 0; i-- { + idx := char2Index[s[i]] + fullMatch = fullMatch && ac.trie[node][idx].edgeType + node = ac.trie[node][idx].nextNode + switch ac.exists[node].matchType { + case Substr: + return true + case Domain: + if fullMatch { + return true + } + } + } + return fullMatch && ac.exists[node].exist +} diff --git a/component/geodata/strmatcher/domain_matcher.go b/component/geodata/strmatcher/domain_matcher.go new file mode 100644 index 00000000..ae8e65bc --- /dev/null +++ b/component/geodata/strmatcher/domain_matcher.go @@ -0,0 +1,98 @@ +package strmatcher + +import "strings" + +func breakDomain(domain string) []string { + return strings.Split(domain, ".") +} + +type node struct { + values []uint32 + sub map[string]*node +} + +// DomainMatcherGroup is a IndexMatcher for a large set of Domain matchers. +// Visible for testing only. +type DomainMatcherGroup struct { + root *node +} + +func (g *DomainMatcherGroup) Add(domain string, value uint32) { + if g.root == nil { + g.root = new(node) + } + + current := g.root + parts := breakDomain(domain) + for i := len(parts) - 1; i >= 0; i-- { + part := parts[i] + if current.sub == nil { + current.sub = make(map[string]*node) + } + next := current.sub[part] + if next == nil { + next = new(node) + current.sub[part] = next + } + current = next + } + + current.values = append(current.values, value) +} + +func (g *DomainMatcherGroup) addMatcher(m domainMatcher, value uint32) { + g.Add(string(m), value) +} + +func (g *DomainMatcherGroup) Match(domain string) []uint32 { + if domain == "" { + return nil + } + + current := g.root + if current == nil { + return nil + } + + nextPart := func(idx int) int { + for i := idx - 1; i >= 0; i-- { + if domain[i] == '.' { + return i + } + } + return -1 + } + + matches := [][]uint32{} + idx := len(domain) + for { + if idx == -1 || current.sub == nil { + break + } + + nidx := nextPart(idx) + part := domain[nidx+1 : idx] + next := current.sub[part] + if next == nil { + break + } + current = next + idx = nidx + if len(current.values) > 0 { + matches = append(matches, current.values) + } + } + switch len(matches) { + case 0: + return nil + case 1: + return matches[0] + default: + result := []uint32{} + for idx := range matches { + // Insert reversely, the subdomain that matches further ranks higher + result = append(result, matches[len(matches)-1-idx]...) + } + return result + } +} diff --git a/component/geodata/strmatcher/full_matcher.go b/component/geodata/strmatcher/full_matcher.go new file mode 100644 index 00000000..e00d02aa --- /dev/null +++ b/component/geodata/strmatcher/full_matcher.go @@ -0,0 +1,25 @@ +package strmatcher + +type FullMatcherGroup struct { + matchers map[string][]uint32 +} + +func (g *FullMatcherGroup) Add(domain string, value uint32) { + if g.matchers == nil { + g.matchers = make(map[string][]uint32) + } + + g.matchers[domain] = append(g.matchers[domain], value) +} + +func (g *FullMatcherGroup) addMatcher(m fullMatcher, value uint32) { + g.Add(string(m), value) +} + +func (g *FullMatcherGroup) Match(str string) []uint32 { + if g.matchers == nil { + return nil + } + + return g.matchers[str] +} diff --git a/component/geodata/strmatcher/matchers.go b/component/geodata/strmatcher/matchers.go new file mode 100644 index 00000000..b5ab09c4 --- /dev/null +++ b/component/geodata/strmatcher/matchers.go @@ -0,0 +1,52 @@ +package strmatcher + +import ( + "regexp" + "strings" +) + +type fullMatcher string + +func (m fullMatcher) Match(s string) bool { + return string(m) == s +} + +func (m fullMatcher) String() string { + return "full:" + string(m) +} + +type substrMatcher string + +func (m substrMatcher) Match(s string) bool { + return strings.Contains(s, string(m)) +} + +func (m substrMatcher) String() string { + return "keyword:" + string(m) +} + +type domainMatcher string + +func (m domainMatcher) Match(s string) bool { + pattern := string(m) + if !strings.HasSuffix(s, pattern) { + return false + } + return len(s) == len(pattern) || s[len(s)-len(pattern)-1] == '.' +} + +func (m domainMatcher) String() string { + return "domain:" + string(m) +} + +type regexMatcher struct { + pattern *regexp.Regexp +} + +func (m *regexMatcher) Match(s string) bool { + return m.pattern.MatchString(s) +} + +func (m *regexMatcher) String() string { + return "regexp:" + m.pattern.String() +} diff --git a/component/geodata/strmatcher/mph_matcher.go b/component/geodata/strmatcher/mph_matcher.go new file mode 100644 index 00000000..3c10cb49 --- /dev/null +++ b/component/geodata/strmatcher/mph_matcher.go @@ -0,0 +1,304 @@ +package strmatcher + +import ( + "math/bits" + "regexp" + "sort" + "strings" + "unsafe" +) + +// PrimeRK is the prime base used in Rabin-Karp algorithm. +const PrimeRK = 16777619 + +// calculate the rolling murmurHash of given string +func RollingHash(s string) uint32 { + h := uint32(0) + for i := len(s) - 1; i >= 0; i-- { + h = h*PrimeRK + uint32(s[i]) + } + return h +} + +// A MphMatcherGroup is divided into three parts: +// 1. `full` and `domain` patterns are matched by Rabin-Karp algorithm and minimal perfect hash table; +// 2. `substr` patterns are matched by ac automaton; +// 3. `regex` patterns are matched with the regex library. +type MphMatcherGroup struct { + ac *ACAutomaton + otherMatchers []matcherEntry + rules []string + level0 []uint32 + level0Mask int + level1 []uint32 + level1Mask int + count uint32 + ruleMap *map[string]uint32 +} + +func (g *MphMatcherGroup) AddFullOrDomainPattern(pattern string, t Type) { + h := RollingHash(pattern) + switch t { + case Domain: + (*g.ruleMap)["."+pattern] = h*PrimeRK + uint32('.') + fallthrough + case Full: + (*g.ruleMap)[pattern] = h + default: + } +} + +func NewMphMatcherGroup() *MphMatcherGroup { + return &MphMatcherGroup{ + ac: nil, + otherMatchers: nil, + rules: nil, + level0: nil, + level0Mask: 0, + level1: nil, + level1Mask: 0, + count: 1, + ruleMap: &map[string]uint32{}, + } +} + +// AddPattern adds a pattern to MphMatcherGroup +func (g *MphMatcherGroup) AddPattern(pattern string, t Type) (uint32, error) { + switch t { + case Substr: + if g.ac == nil { + g.ac = NewACAutomaton() + } + g.ac.Add(pattern, t) + case Full, Domain: + pattern = strings.ToLower(pattern) + g.AddFullOrDomainPattern(pattern, t) + case Regex: + r, err := regexp.Compile(pattern) + if err != nil { + return 0, err + } + g.otherMatchers = append(g.otherMatchers, matcherEntry{ + m: ®exMatcher{pattern: r}, + id: g.count, + }) + default: + panic("Unknown type") + } + return g.count, nil +} + +// Build builds a minimal perfect hash table and ac automaton from insert rules +func (g *MphMatcherGroup) Build() { + if g.ac != nil { + g.ac.Build() + } + keyLen := len(*g.ruleMap) + if keyLen == 0 { + keyLen = 1 + (*g.ruleMap)["empty___"] = RollingHash("empty___") + } + g.level0 = make([]uint32, nextPow2(keyLen/4)) + g.level0Mask = len(g.level0) - 1 + g.level1 = make([]uint32, nextPow2(keyLen)) + g.level1Mask = len(g.level1) - 1 + sparseBuckets := make([][]int, len(g.level0)) + var ruleIdx int + for rule, hash := range *g.ruleMap { + n := int(hash) & g.level0Mask + g.rules = append(g.rules, rule) + sparseBuckets[n] = append(sparseBuckets[n], ruleIdx) + ruleIdx++ + } + g.ruleMap = nil + var buckets []indexBucket + for n, vals := range sparseBuckets { + if len(vals) > 0 { + buckets = append(buckets, indexBucket{n, vals}) + } + } + sort.Sort(bySize(buckets)) + + occ := make([]bool, len(g.level1)) + var tmpOcc []int + for _, bucket := range buckets { + seed := uint32(0) + for { + findSeed := true + tmpOcc = tmpOcc[:0] + for _, i := range bucket.vals { + n := int(strhashFallback(unsafe.Pointer(&g.rules[i]), uintptr(seed))) & g.level1Mask + if occ[n] { + for _, n := range tmpOcc { + occ[n] = false + } + seed++ + findSeed = false + break + } + occ[n] = true + tmpOcc = append(tmpOcc, n) + g.level1[n] = uint32(i) + } + if findSeed { + g.level0[bucket.n] = seed + break + } + } + } +} + +func nextPow2(v int) int { + if v <= 1 { + return 1 + } + const MaxUInt = ^uint(0) + n := (MaxUInt >> bits.LeadingZeros(uint(v))) + 1 + return int(n) +} + +// Lookup searches for s in t and returns its index and whether it was found. +func (g *MphMatcherGroup) Lookup(h uint32, s string) bool { + i0 := int(h) & g.level0Mask + seed := g.level0[i0] + i1 := int(strhashFallback(unsafe.Pointer(&s), uintptr(seed))) & g.level1Mask + n := g.level1[i1] + return s == g.rules[int(n)] +} + +// Match implements IndexMatcher.Match. +func (g *MphMatcherGroup) Match(pattern string) []uint32 { + result := []uint32{} + hash := uint32(0) + for i := len(pattern) - 1; i >= 0; i-- { + hash = hash*PrimeRK + uint32(pattern[i]) + if pattern[i] == '.' { + if g.Lookup(hash, pattern[i:]) { + result = append(result, 1) + return result + } + } + } + if g.Lookup(hash, pattern) { + result = append(result, 1) + return result + } + if g.ac != nil && g.ac.Match(pattern) { + result = append(result, 1) + return result + } + for _, e := range g.otherMatchers { + if e.m.Match(pattern) { + result = append(result, e.id) + return result + } + } + return nil +} + +type indexBucket struct { + n int + vals []int +} + +type bySize []indexBucket + +func (s bySize) Len() int { return len(s) } +func (s bySize) Less(i, j int) bool { return len(s[i].vals) > len(s[j].vals) } +func (s bySize) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type stringStruct struct { + str unsafe.Pointer + len int +} + +func strhashFallback(a unsafe.Pointer, h uintptr) uintptr { + x := (*stringStruct)(a) + return memhashFallback(x.str, h, uintptr(x.len)) +} + +const ( + // Constants for multiplication: four random odd 64-bit numbers. + m1 = 16877499708836156737 + m2 = 2820277070424839065 + m3 = 9497967016996688599 + m4 = 15839092249703872147 +) + +var hashkey = [4]uintptr{1, 1, 1, 1} + +func memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr { + h := uint64(seed + s*hashkey[0]) +tail: + switch { + case s == 0: + case s < 4: + h ^= uint64(*(*byte)(p)) + h ^= uint64(*(*byte)(add(p, s>>1))) << 8 + h ^= uint64(*(*byte)(add(p, s-1))) << 16 + h = rotl31(h*m1) * m2 + case s <= 8: + h ^= uint64(readUnaligned32(p)) + h ^= uint64(readUnaligned32(add(p, s-4))) << 32 + h = rotl31(h*m1) * m2 + case s <= 16: + h ^= readUnaligned64(p) + h = rotl31(h*m1) * m2 + h ^= readUnaligned64(add(p, s-8)) + h = rotl31(h*m1) * m2 + case s <= 32: + h ^= readUnaligned64(p) + h = rotl31(h*m1) * m2 + h ^= readUnaligned64(add(p, 8)) + h = rotl31(h*m1) * m2 + h ^= readUnaligned64(add(p, s-16)) + h = rotl31(h*m1) * m2 + h ^= readUnaligned64(add(p, s-8)) + h = rotl31(h*m1) * m2 + default: + v1 := h + v2 := uint64(seed * hashkey[1]) + v3 := uint64(seed * hashkey[2]) + v4 := uint64(seed * hashkey[3]) + for s >= 32 { + v1 ^= readUnaligned64(p) + v1 = rotl31(v1*m1) * m2 + p = add(p, 8) + v2 ^= readUnaligned64(p) + v2 = rotl31(v2*m2) * m3 + p = add(p, 8) + v3 ^= readUnaligned64(p) + v3 = rotl31(v3*m3) * m4 + p = add(p, 8) + v4 ^= readUnaligned64(p) + v4 = rotl31(v4*m4) * m1 + p = add(p, 8) + s -= 32 + } + h = v1 ^ v2 ^ v3 ^ v4 + goto tail + } + + h ^= h >> 29 + h *= m3 + h ^= h >> 32 + return uintptr(h) +} + +func add(p unsafe.Pointer, x uintptr) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +func readUnaligned32(p unsafe.Pointer) uint32 { + q := (*[4]byte)(p) + return uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24 +} + +func rotl31(x uint64) uint64 { + return (x << 31) | (x >> (64 - 31)) +} + +func readUnaligned64(p unsafe.Pointer) uint64 { + q := (*[8]byte)(p) + return uint64(q[0]) | uint64(q[1])<<8 | uint64(q[2])<<16 | uint64(q[3])<<24 | uint64(q[4])<<32 | uint64(q[5])<<40 | uint64(q[6])<<48 | uint64(q[7])<<56 +} diff --git a/component/geodata/strmatcher/package_info.go b/component/geodata/strmatcher/package_info.go new file mode 100644 index 00000000..8d6e169f --- /dev/null +++ b/component/geodata/strmatcher/package_info.go @@ -0,0 +1,4 @@ +// Modified from: https://github.com/v2fly/v2ray-core/tree/master/common/strmatcher +// License: MIT + +package strmatcher diff --git a/component/geodata/strmatcher/strmatcher.go b/component/geodata/strmatcher/strmatcher.go new file mode 100644 index 00000000..294e6e73 --- /dev/null +++ b/component/geodata/strmatcher/strmatcher.go @@ -0,0 +1,107 @@ +package strmatcher + +import ( + "regexp" +) + +// Matcher is the interface to determine a string matches a pattern. +type Matcher interface { + // Match returns true if the given string matches a predefined pattern. + Match(string) bool + String() string +} + +// Type is the type of the matcher. +type Type byte + +const ( + // Full is the type of matcher that the input string must exactly equal to the pattern. + Full Type = iota + // Substr is the type of matcher that the input string must contain the pattern as a sub-string. + Substr + // Domain is the type of matcher that the input string must be a sub-domain or itself of the pattern. + Domain + // Regex is the type of matcher that the input string must matches the regular-expression pattern. + Regex +) + +// New creates a new Matcher based on the given pattern. +func (t Type) New(pattern string) (Matcher, error) { + // 1. regex matching is case-sensitive + switch t { + case Full: + return fullMatcher(pattern), nil + case Substr: + return substrMatcher(pattern), nil + case Domain: + return domainMatcher(pattern), nil + case Regex: + r, err := regexp.Compile(pattern) + if err != nil { + return nil, err + } + return ®exMatcher{ + pattern: r, + }, nil + default: + panic("Unknown type") + } +} + +// IndexMatcher is the interface for matching with a group of matchers. +type IndexMatcher interface { + // Match returns the index of a matcher that matches the input. It returns empty array if no such matcher exists. + Match(input string) []uint32 +} + +type matcherEntry struct { + m Matcher + id uint32 +} + +// MatcherGroup is an implementation of IndexMatcher. +// Empty initialization works. +type MatcherGroup struct { + count uint32 + fullMatcher FullMatcherGroup + domainMatcher DomainMatcherGroup + otherMatchers []matcherEntry +} + +// Add adds a new Matcher into the MatcherGroup, and returns its index. The index will never be 0. +func (g *MatcherGroup) Add(m Matcher) uint32 { + g.count++ + c := g.count + + switch tm := m.(type) { + case fullMatcher: + g.fullMatcher.addMatcher(tm, c) + case domainMatcher: + g.domainMatcher.addMatcher(tm, c) + default: + g.otherMatchers = append(g.otherMatchers, matcherEntry{ + m: m, + id: c, + }) + } + + return c +} + +// Match implements IndexMatcher.Match. +func (g *MatcherGroup) Match(pattern string) []uint32 { + result := []uint32{} + result = append(result, g.fullMatcher.Match(pattern)...) + result = append(result, g.domainMatcher.Match(pattern)...) + for _, e := range g.otherMatchers { + if e.m.Match(pattern) { + result = append(result, e.id) + } + } + return result +} + +// Size returns the number of matchers in the MatcherGroup. +func (g *MatcherGroup) Size() uint32 { + return g.count +} diff --git a/component/geodata/utils.go b/component/geodata/utils.go new file mode 100644 index 00000000..3a48dc86 --- /dev/null +++ b/component/geodata/utils.go @@ -0,0 +1,30 @@ +package geodata + +import ( + "github.com/Dreamacro/clash/component/geodata/router" +) + +func LoadGeoSiteMatcher(countryCode string) (*router.DomainMatcher, int, error) { + geoLoaderName := "standard" + geoLoader, err := GetGeoDataLoader(geoLoaderName) + if err != nil { + return nil, 0, err + } + + domains, err := geoLoader.LoadGeoSite(countryCode) + if err != nil { + return nil, 0, err + } + + /** + linear: linear algorithm + matcher, err := router.NewDomainMatcher(domains) + mph:minimal perfect hash algorithm + */ + matcher, err := router.NewMphMatcherGroup(domains) + if err != nil { + return nil, 0, err + } + + return matcher, len(domains), nil +} diff --git a/component/resolver/patch.go b/component/resolver/patch.go new file mode 100644 index 00000000..be84e693 --- /dev/null +++ b/component/resolver/patch.go @@ -0,0 +1,18 @@ +package resolver + +import D "github.com/miekg/dns" + +var DefaultLocalServer LocalServer + +type LocalServer interface { + ServeMsg(msg *D.Msg) (*D.Msg, error) +} + +// ServeMsg with a dns.Msg, return resolve dns.Msg +func ServeMsg(msg *D.Msg) (*D.Msg, error) { + if server := DefaultLocalServer; server != nil { + return server.ServeMsg(msg) + } + + return nil, ErrIPNotFound +} diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index d10e39cb..a7300bd7 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -15,6 +15,9 @@ var ( // DefaultResolver aim to resolve ip DefaultResolver Resolver + // MainResolver resolve ip with main domain server + MainResolver Resolver + // DisableIPv6 means don't resolve ipv6 host // default value is true DisableIPv6 = true @@ -40,6 +43,14 @@ type Resolver interface { // ResolveIPv4 with a host, return ipv4 func ResolveIPv4(host string) (net.IP, error) { + return ResolveIPv4WithResolver(host, DefaultResolver) +} + +func ResolveIPv4WithMain(host string) (net.IP, error) { + return ResolveIPv4WithResolver(host, MainResolver) +} + +func ResolveIPv4WithResolver(host string, r Resolver) (net.IP, error) { if node := DefaultHosts.Search(host); node != nil { if ip := node.Data.(net.IP).To4(); ip != nil { return ip, nil @@ -54,8 +65,8 @@ func ResolveIPv4(host string) (net.IP, error) { return nil, ErrIPVersion } - if DefaultResolver != nil { - return DefaultResolver.ResolveIPv4(host) + if r != nil { + return r.ResolveIPv4(host) } ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) @@ -72,6 +83,14 @@ func ResolveIPv4(host string) (net.IP, error) { // ResolveIPv6 with a host, return ipv6 func ResolveIPv6(host string) (net.IP, error) { + return ResolveIPv6WithResolver(host, DefaultResolver) +} + +func ResolveIPv6WithMain(host string) (net.IP, error) { + return ResolveIPv6WithResolver(host, MainResolver) +} + +func ResolveIPv6WithResolver(host string, r Resolver) (net.IP, error) { if DisableIPv6 { return nil, ErrIPv6Disabled } @@ -90,8 +109,8 @@ func ResolveIPv6(host string) (net.IP, error) { return nil, ErrIPVersion } - if DefaultResolver != nil { - return DefaultResolver.ResolveIPv6(host) + if r != nil { + return r.ResolveIPv6(host) } ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) @@ -138,3 +157,8 @@ func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) { func ResolveIP(host string) (net.IP, error) { return ResolveIPWithResolver(host, DefaultResolver) } + +// ResolveIPWithMainResolver with a host, use main resolver, return ip +func ResolveIPWithMainResolver(host string) (net.IP, error) { + return ResolveIPWithResolver(host, MainResolver) +} diff --git a/component/script/build_local.go b/component/script/build_local.go new file mode 100644 index 00000000..b3e5640e --- /dev/null +++ b/component/script/build_local.go @@ -0,0 +1,9 @@ +//go:build build_local +// +build build_local + +package script + +/* +#cgo pkg-config: python3-embed +*/ +import "C" diff --git a/component/script/build_xgo.go b/component/script/build_xgo.go new file mode 100644 index 00000000..4a066c90 --- /dev/null +++ b/component/script/build_xgo.go @@ -0,0 +1,25 @@ +//go:build !build_local && cgo +// +build !build_local,cgo + +package script + +/* +#cgo linux,amd64 pkg-config: python3-embed + +#cgo darwin,amd64 CFLAGS: -I/build/python/python-3.9.7-darwin-amd64/include/python3.9 +#cgo darwin,arm64 CFLAGS: -I/build/python/python-3.9.7-darwin-arm64/include/python3.9 +#cgo windows,amd64 CFLAGS: -I/build/python/python-3.9.7-windows-amd64/include -DMS_WIN64 +#cgo windows,386 CFLAGS: -I/build/python/python-3.9.7-windows-386/include +//#cgo linux,amd64 CFLAGS: -I/home/runner/work/clash/clash/bin/python/python-3.9.7-linux-amd64/include/python3.9 +//#cgo linux,arm64 CFLAGS: -I/build/python/python-3.9.7-linux-arm64/include/python3.9 +//#cgo linux,386 CFLAGS: -I/build/python/python-3.9.7-linux-386/include/python3.9 + +#cgo darwin,amd64 LDFLAGS: -L/build/python/python-3.9.7-darwin-amd64/lib -lpython3.9 -ldl -framework CoreFoundation +#cgo darwin,arm64 LDFLAGS: -L/build/python/python-3.9.7-darwin-arm64/lib -lpython3.9 -ldl -framework CoreFoundation +#cgo windows,amd64 LDFLAGS: -L/build/python/python-3.9.7-windows-amd64/lib -lpython39 -lpthread -lm +#cgo windows,386 LDFLAGS: -L/build/python/python-3.9.7-windows-386/lib -lpython39 -lpthread -lm +//#cgo linux,amd64 LDFLAGS: -L/home/runner/work/clash/clash/bin/python/python-3.9.7-linux-amd64/lib -lpython3.9 -lpthread -ldl -lutil -lm +//#cgo linux,arm64 LDFLAGS: -L/build/python/python-3.9.7-linux-arm64/lib -lpython3.9 -lpthread -ldl -lutil -lm +//#cgo linux,386 LDFLAGS: -L/build/python/python-3.9.7-linux-386/lib -lpython3.9 -lpthread -ldl -lutil -lm +*/ +import "C" diff --git a/component/script/clash_module.c b/component/script/clash_module.c new file mode 100644 index 00000000..f15f0191 --- /dev/null +++ b/component/script/clash_module.c @@ -0,0 +1,735 @@ +#define PY_SSIZE_T_CLEAN + +#include "clash_module.h" +#include + +PyObject *clash_module; +PyObject *main_fn; +PyObject *clash_context; + +// init_python +void init_python(const char *program, const char *path) { + +// Py_NoSiteFlag = 1; +// Py_FrozenFlag = 1; +// Py_IgnoreEnvironmentFlag = 1; +// Py_IsolatedFlag = 1; + + append_inittab(); + + wchar_t *programName = Py_DecodeLocale(program, NULL); + if (programName != NULL) { + Py_SetProgramName(programName); + PyMem_RawFree(programName); + } + +// wchar_t *newPath = Py_DecodeLocale(path, NULL); +// if (newPath != NULL) { +// Py_SetPath(newPath); +// PyMem_RawFree(newPath); +// } + +// Py_Initialize(); + Py_InitializeEx(0); + + char *pathPrefix = "import sys; sys.path.append('"; + char *pathSuffix = "')"; + char *newPath = (char *) malloc(strlen(pathPrefix) + strlen(path) + strlen(pathSuffix)); + sprintf(newPath, "%s%s%s", pathPrefix, path, pathSuffix); + + PyRun_SimpleString(newPath); + free(newPath); + + /* Optionally import the module; alternatively, + import can be deferred until the embedded script + imports it. */ + clash_module = PyImport_ImportModule("clash"); +} + +// Load function, same as "import module_name.func_name as obj" in Python +// Returns the function object or NULL if not found +PyObject *load_func(const char *module_name, char *func_name) { + // Import the module + PyObject *py_mod_name = PyUnicode_FromString(module_name); + if (py_mod_name == NULL) { + return NULL; + } + + PyObject *module = PyImport_Import(py_mod_name); + Py_DECREF(py_mod_name); + if (module == NULL) { + return NULL; + } + + // Get function, same as "getattr(module, func_name)" in Python + PyObject *func = PyObject_GetAttrString(module, func_name); + Py_DECREF(module); + return func; +} + +// Return last error as char *, NULL if there was no error +const char *py_last_error() { + PyObject *err = PyErr_Occurred(); + if (err == NULL) { + return NULL; + } + + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + + if (value == NULL) { + return NULL; + } + + PyObject *str = PyObject_Str(value); + const char *utf8 = PyUnicode_AsUTF8(str); + Py_DECREF(str); + PyErr_Clear(); + return utf8; +} + +void py_clear(PyObject *obj) { + Py_CLEAR(obj); +} + +void load_main_func() { + main_fn = load_func(CLASH_SCRIPT_MODULE_NAME, "main"); +} + +/** callback function, that call go function by python3 script. **/ + +resolve_ip_callback resolve_ip_callback_fn; + +geoip_callback geoip_callback_fn; + +rule_provider_callback rule_provider_callback_fn; + +log_callback log_callback_fn; + +void +set_resolve_ip_callback(resolve_ip_callback cb) +{ + resolve_ip_callback_fn = cb; +} + +void +set_geoip_callback(geoip_callback cb) +{ + geoip_callback_fn = cb; +} + +void +set_rule_provider_callback(rule_provider_callback cb) +{ + rule_provider_callback_fn = cb; +} + +void +set_log_callback(log_callback cb) +{ + log_callback_fn = cb; +} + +/** end callback function **/ + +/* --------------------------------------------------------------------- */ + +/* RuleProvider objects */ + +typedef struct { + PyObject_HEAD + PyObject *name; /* rule provider name */ +} RuleProviderObject; + +static int +RuleProvider_traverse(RuleProviderObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->name); + return 0; +} + +static int +RuleProvider_clear(RuleProviderObject *self) +{ + Py_CLEAR(self->name); + return 0; +} + +static void +RuleProvider_dealloc(RuleProviderObject *self) +{ + PyObject_GC_UnTrack(self); + RuleProvider_clear(self); + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject * +RuleProvider_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + RuleProviderObject *self; + self = (RuleProviderObject *) type->tp_alloc(type, 0); + if (self != NULL) { + self->name = PyUnicode_FromString(""); + if (self->name == NULL) { + Py_DECREF(self); + return NULL; + } + } + return (PyObject *) self; +} + +static int +RuleProvider_init(RuleProviderObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", NULL}; + PyObject *name = NULL, *tmp; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Us", kwlist, &name)) + return -1; + + if (name) { + tmp = self->name; + Py_INCREF(name); + self->name = name; + Py_DECREF(tmp); + } + return 0; +} + +//static PyMemberDef RuleProvider_members[] = { +// {"adapter_type", T_STRING, offsetof(RuleProviderObject, adapter_type), 0, +// "adapter type"}, +// {NULL} /* Sentinel */ +//}; + +static PyObject * +RuleProvider_getname(RuleProviderObject *self, void *closure) +{ + Py_INCREF(self->name); + return self->name; +} + +static int +RuleProvider_setname(RuleProviderObject *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the name attribute"); + return -1; + } + if (!PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "The name attribute value must be a string"); + return -1; + } + Py_INCREF(value); + Py_CLEAR(self->name); + self->name = value; + return 0; +} + +static PyGetSetDef RuleProvider_getsetters[] = { + {"name", (getter) RuleProvider_getname, (setter) RuleProvider_setname, + "name", NULL}, + {NULL} /* Sentinel */ +}; + +static PyObject * +RuleProvider_name(RuleProviderObject *self, PyObject *Py_UNUSED(ignored)) +{ + Py_INCREF(self->name); + return self->name; +} + +static PyObject * +RuleProvider_match(RuleProviderObject *self, PyObject *args) +{ + PyObject *result; + PyObject *tmp; + const char *provider_name; + + if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &tmp)) //Format "O","O!","O&": Borrowed reference. + return NULL; + + if (tmp == NULL) + Py_RETURN_FALSE; + + Py_INCREF(tmp); +// PyObject *py_src_port = PyDict_GetItemString(tmp, "src_port"); //Return value: Borrowed reference. +// PyObject *py_dst_port = PyDict_GetItemString(tmp, "dst_port"); //Return value: Borrowed reference. +// Py_INCREF(py_src_port); +// Py_INCREF(py_dst_port); +// char *c_src_port = (char *) malloc(PyLong_AsSize_t(py_src_port)); +// char *c_dst_port = (char *) malloc(PyLong_AsSize_t(py_dst_port)); +// sprintf(c_src_port, "%ld", PyLong_AsLong(py_src_port)); +// sprintf(c_dst_port, "%ld", PyLong_AsLong(py_dst_port)); + + struct Metadata metadata = { + .type = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "type")), // PyDict_GetItemString() Return value: Borrowed reference. + .network = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "network")), + .process_name = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "process_name")), + .host = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "host")), + .src_ip = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "src_ip")), + .src_port = (unsigned short)PyLong_AsUnsignedLong(PyDict_GetItemString(tmp, "src_port")), + .dst_ip = PyUnicode_AsUTF8(PyDict_GetItemString(tmp, "dst_ip")), + .dst_port = (unsigned short)PyLong_AsUnsignedLong(PyDict_GetItemString(tmp, "dst_port")) + }; + +// Py_DECREF(py_src_port); +// Py_DECREF(py_dst_port); + + Py_INCREF(self->name); + provider_name = PyUnicode_AsUTF8(self->name); + Py_DECREF(self->name); + Py_DECREF(tmp); + + int rs = rule_provider_callback_fn(provider_name, &metadata); + + result = (rs == 1) ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +static PyMethodDef RuleProvider_methods[] = { + {"name", (PyCFunction) RuleProvider_name, METH_NOARGS, + "Return the RuleProvider name" + }, + {"match", (PyCFunction) RuleProvider_match, METH_VARARGS, + "Match the rule by the RuleProvider, match(metadata) -> boolean" + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject RuleProviderType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "clash.RuleProvider", + .tp_doc = "Clash RuleProvider objects", + .tp_basicsize = sizeof(RuleProviderObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + .tp_new = RuleProvider_new, + .tp_init = (initproc) RuleProvider_init, + .tp_dealloc = (destructor) RuleProvider_dealloc, + .tp_traverse = (traverseproc) RuleProvider_traverse, + .tp_clear = (inquiry) RuleProvider_clear, +// .tp_members = RuleProvider_members, + .tp_methods = RuleProvider_methods, + .tp_getset = RuleProvider_getsetters, +}; + +/* end RuleProvider objects */ +/* --------------------------------------------------------------------- */ + +/* Context objects */ + +typedef struct { + PyObject_HEAD + PyObject *rule_providers; /* Dict */ +} ContextObject; + +static int +Context_traverse(ContextObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->rule_providers); + return 0; +} + +static int +Context_clear(ContextObject *self) +{ + Py_CLEAR(self->rule_providers); + return 0; +} + +static void +Context_dealloc(ContextObject *self) +{ + PyObject_GC_UnTrack(self); + Context_clear(self); + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static PyObject * +Context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + ContextObject *self; + self = (ContextObject *) type->tp_alloc(type, 0); + if (self != NULL) { + self->rule_providers = PyDict_New(); + if (self->rule_providers == NULL) { + Py_DECREF(self); + return NULL; + } + } + return (PyObject *) self; +} + +static int +Context_init(ContextObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"rule_providers", NULL}; + PyObject *rule_providers = NULL, *tmp; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, + &rule_providers)) + return -1; + + if (rule_providers) { + tmp = self->rule_providers; + Py_INCREF(rule_providers); + self->rule_providers = rule_providers; + Py_DECREF(tmp); + } + return 0; +} + +static PyObject * +Context_getrule_providers(ContextObject *self, void *closure) +{ + Py_INCREF(self->rule_providers); + return self->rule_providers; +} + +static int +Context_setrule_providers(ContextObject *self, PyObject *value, void *closure) +{ + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "Cannot delete the rule_providers attribute"); + return -1; + } + if (!PyDict_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "The rule_providers attribute value must be a dict"); + return -1; + } + Py_INCREF(value); + Py_CLEAR(self->rule_providers); + self->rule_providers = value; + return 0; +} + +static PyGetSetDef Context_getsetters[] = { + {"rule_providers", (getter) Context_getrule_providers, (setter) Context_setrule_providers, + "rule_providers", NULL}, + {NULL} /* Sentinel */ +}; + +static PyObject * +Context_resolve_ip(PyObject *self, PyObject *args) +{ + const char *host; + const char *ip; + + if (!PyArg_ParseTuple(args, "s", &host)) + return NULL; + + if (host == NULL) + return PyUnicode_FromString(""); + + ip = resolve_ip_callback_fn(host); + + return PyUnicode_FromString(ip); +} + +static PyObject * +Context_geoip(PyObject *self, PyObject *args) +{ + const char *ip; + const char *countryCode; + + if (!PyArg_ParseTuple(args, "s", &ip)) + return NULL; + + if (ip == NULL) + return PyUnicode_FromString(""); + + countryCode = geoip_callback_fn(ip); + + return PyUnicode_FromString(countryCode); +} + +static PyObject * +Context_log(PyObject *self, PyObject *args) +{ + const char *msg; + + if (!PyArg_ParseTuple(args, "s", &msg)) + return NULL; + + log_callback_fn(msg); + + Py_RETURN_NONE; +} + +static PyMethodDef Context_methods[] = { + {"resolve_ip", (PyCFunction) Context_resolve_ip, METH_VARARGS, + "resolve_ip(host) -> string" + }, + {"geoip", (PyCFunction) Context_geoip, METH_VARARGS, + "geoip(ip) -> string" + }, + {"log", (PyCFunction) Context_log, METH_VARARGS, + "log(msg) -> void" + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject ContextType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "clash.Context", + .tp_doc = "Clash Context objects", + .tp_basicsize = sizeof(ContextObject), + .tp_itemsize = 0, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + .tp_new = Context_new, + .tp_init = (initproc) Context_init, + .tp_dealloc = (destructor) Context_dealloc, + .tp_traverse = (traverseproc) Context_traverse, + .tp_clear = (inquiry) Context_clear, + .tp_methods = Context_methods, + .tp_getset = Context_getsetters, +}; + +static PyModuleDef clashmodule = { + PyModuleDef_HEAD_INIT, + .m_name = "clash", + .m_doc = "Clash module that creates an extension module for python3.", + .m_size = -1, +}; + +PyMODINIT_FUNC +PyInit_clash(void) +{ + PyObject *m; + + m = PyModule_Create(&clashmodule); + if (m == NULL) + return NULL; + + if (PyType_Ready(&RuleProviderType) < 0) + return NULL; + + Py_INCREF(&RuleProviderType); + if (PyModule_AddObject(m, "RuleProvider", (PyObject *) &RuleProviderType) < 0) { + Py_DECREF(&RuleProviderType); + Py_DECREF(m); + return NULL; + } + + if (PyType_Ready(&ContextType) < 0) + return NULL; + + Py_INCREF(&ContextType); + if (PyModule_AddObject(m, "Context", (PyObject *) &ContextType) < 0) { + Py_DECREF(&ContextType); + Py_DECREF(m); + return NULL; + } + + return m; +} + +/* end Context objects */ + +/* --------------------------------------------------------------------- */ + +void +append_inittab() +{ + /* Add a built-in module, before Py_Initialize */ + PyImport_AppendInittab("clash", PyInit_clash); +} + +int new_clash_py_context(const char *provider_name_arr[], int size) { + PyObject *dict = PyDict_New(); //Return value: New reference. + if (dict == NULL) { + PyErr_SetString(PyExc_TypeError, + "PyDict_New failure"); + return 0; + } + + for (int i = 0; i < size; i++) { + PyObject *rule_provider = RuleProvider_new(&RuleProviderType, NULL, NULL); + if (rule_provider == NULL) { + Py_DECREF(dict); + PyErr_SetString(PyExc_TypeError, + "RuleProvider_new failure"); + return 0; + } + + RuleProviderObject *providerObj = (RuleProviderObject *) rule_provider; + + PyObject *py_name = PyUnicode_FromString(provider_name_arr[i]); //Return value: New reference. + RuleProvider_setname(providerObj, py_name, NULL); + Py_DECREF(py_name); + + PyDict_SetItemString(dict, provider_name_arr[i], rule_provider); //Parameter value: New reference. + Py_DECREF(rule_provider); + } + + clash_context = Context_new(&ContextType, NULL, NULL); + + if (clash_context == NULL) { + Py_DECREF(dict); + PyErr_SetString(PyExc_TypeError, + "Context_new failure"); + return 0; + } + + Context_setrule_providers((ContextObject *) clash_context, dict, NULL); + Py_DECREF(dict); + return 1; +} + +const char *call_main( + const char *type, + const char *network, + const char *process_name, + const char *host, + const char *src_ip, + unsigned short src_port, + const char *dst_ip, + unsigned short dst_port) { + + PyObject *metadataDict; + PyObject *tupleArgs; + PyObject *result; + + metadataDict = PyDict_New(); //Return value: New reference. + + if (metadataDict == NULL) { + PyErr_SetString(PyExc_TypeError, + "PyDict_New failure"); + return "-1"; + } + + PyObject *p_type = PyUnicode_FromString(type); //Return value: New reference. + PyObject *p_network = PyUnicode_FromString(network); //Return value: New reference. + PyObject *p_process_name = PyUnicode_FromString(process_name); //Return value: New reference. + PyObject *p_host = PyUnicode_FromString(host); //Return value: New reference. + PyObject *p_src_ip = PyUnicode_FromString(src_ip); //Return value: New reference. + PyObject *p_src_port = PyLong_FromUnsignedLong((unsigned long)src_port); //Return value: New reference. + PyObject *p_dst_ip = PyUnicode_FromString(dst_ip); //Return value: New reference. + PyObject *p_dst_port = PyLong_FromUnsignedLong((unsigned long)dst_port); //Return value: New reference. + + PyDict_SetItemString(metadataDict, "type", p_type); //Parameter value: New reference. + PyDict_SetItemString(metadataDict, "network", p_network); //Parameter value: New reference. + PyDict_SetItemString(metadataDict, "process_name", p_process_name); //Parameter value: New reference. + PyDict_SetItemString(metadataDict, "host", p_host); //Parameter value: New reference. + PyDict_SetItemString(metadataDict, "src_ip", p_src_ip); //Parameter value: New reference. + PyDict_SetItemString(metadataDict, "src_port", p_src_port); //Parameter value: New reference. + PyDict_SetItemString(metadataDict, "dst_ip", p_dst_ip); //Parameter value: New reference. + PyDict_SetItemString(metadataDict, "dst_port", p_dst_port); //Parameter value: New reference. + + Py_DECREF(p_type); + Py_DECREF(p_network); + Py_DECREF(p_process_name); + Py_DECREF(p_host); + Py_DECREF(p_src_ip); + Py_DECREF(p_src_port); + Py_DECREF(p_dst_ip); + Py_DECREF(p_dst_port); + + tupleArgs = PyTuple_New(2); //Return value: New reference. + if (tupleArgs == NULL) { + Py_DECREF(metadataDict); + PyErr_SetString(PyExc_TypeError, + "PyTuple_New failure"); + return "-1"; + } + + Py_INCREF(clash_context); + PyTuple_SetItem(tupleArgs, 0, clash_context); //clash_context Parameter value: Stolen reference. + PyTuple_SetItem(tupleArgs, 1, metadataDict); //metadataDict Parameter value: Stolen reference. + + Py_INCREF(main_fn); + result = PyObject_CallObject(main_fn, tupleArgs); //Return value: New reference. + Py_DECREF(main_fn); + Py_DECREF(tupleArgs); + + if (result == NULL) { + return "-1"; + } + + if (!PyUnicode_Check(result)) { + Py_DECREF(result); + PyErr_SetString(PyExc_TypeError, + "script main function return value must be a string"); + return "-1"; + } + + const char *adapter = PyUnicode_AsUTF8(result); + + Py_DECREF(result); + + return adapter; +} + +int call_shortcut(PyObject *shortcut_fn, + const char *type, + const char *network, + const char *process_name, + const char *host, + const char *src_ip, + unsigned short src_port, + const char *dst_ip, + unsigned short dst_port) { + + PyObject *args; + PyObject *result; + + args = Py_BuildValue("{s:O, s:s, s:s, s:s, s:s, s:H, s:s, s:H}", + "ctx", clash_context, + "network", network, + "process_name", process_name, + "host", host, + "src_ip", src_ip, + "src_port", src_port, + "dst_ip", dst_ip, + "dst_port", dst_port); //Return value: New reference. + + if (args == NULL) { + PyErr_SetString(PyExc_TypeError, + "Py_BuildValue failure"); + return -1; + } + + PyObject *tupleArgs = PyTuple_New(0); //Return value: New reference. + + Py_INCREF(clash_context); + Py_INCREF(shortcut_fn); + result = PyObject_Call(shortcut_fn, tupleArgs, args); //Return value: New reference. + Py_DECREF(shortcut_fn); + Py_DECREF(clash_context); + Py_DECREF(tupleArgs); + Py_DECREF(args); + + if (result == NULL) { + return -1; + } + + if (!PyBool_Check(result)) { + Py_DECREF(result); + PyErr_SetString(PyExc_TypeError, + "script shortcut return value must be as boolean"); + return -1; + } + + int rs = (result == Py_True) ? 1 : 0; + + Py_DECREF(result); + + return rs; +} + +void finalize_Python() { + Py_CLEAR(main_fn); + Py_CLEAR(clash_context); + Py_CLEAR(clash_module); + Py_FinalizeEx(); + +// clash_module = NULL; +// main_fn = NULL; +// clash_context = NULL; +} + +/* --------------------------------------------------------------------- */ \ No newline at end of file diff --git a/component/script/clash_module.go b/component/script/clash_module.go new file mode 100644 index 00000000..5fccb4c1 --- /dev/null +++ b/component/script/clash_module.go @@ -0,0 +1,337 @@ +package script + +/* +#include "clash_module.h" + +extern const char *resolveIPCallbackFn(const char *host); + +void +go_set_resolve_ip_callback() { + set_resolve_ip_callback(resolveIPCallbackFn); +} + +extern const char *geoipCallbackFn(const char *ip); + +void +go_set_geoip_callback() { + set_geoip_callback(geoipCallbackFn); +} + +extern const int ruleProviderCallbackFn(const char *provider_name, struct Metadata *metadata); + +void +go_set_rule_provider_callback() { + set_rule_provider_callback(ruleProviderCallbackFn); +} + +extern void logCallbackFn(const char *msg); + +void +go_set_log_callback() { + set_log_callback(logCallbackFn); +} +*/ +import "C" +import ( + "errors" + "fmt" + "os" + "runtime" + "strconv" + "strings" + "sync" + "syscall" + "unsafe" + + "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" +) + +const ClashScriptModuleName = C.CLASH_SCRIPT_MODULE_NAME + +var lock sync.Mutex + +type PyObject C.PyObject + +func togo(cobject *C.PyObject) *PyObject { + return (*PyObject)(cobject) +} + +func toc(object *PyObject) *C.PyObject { + return (*C.PyObject)(object) +} + +func (pyObject *PyObject) IncRef() { + C.Py_IncRef(toc(pyObject)) +} + +func (pyObject *PyObject) DecRef() { + C.Py_DecRef(toc(pyObject)) +} + +func (pyObject *PyObject) Clear() { + C.py_clear(toc(pyObject)) +} + +// Py_Initialize initialize Python3 +func Py_Initialize(program string, path string) error { + lock.Lock() + defer lock.Unlock() + + if C.Py_IsInitialized() != 0 { + if pyThreadState != nil { + PyEval_RestoreThread(pyThreadState) + } + C.finalize_Python() + } + + path = strings.ReplaceAll(path, "\\", "/") + cPath := C.CString(path) + //defer C.free(unsafe.Pointer(cPath)) + + C.init_python(C.CString(program), cPath) + err := PyLastError() + + if err != nil { + if C.Py_IsInitialized() != 0 { + C.finalize_Python() + _ = os.RemoveAll(constant.Path.ScriptDir()) + } + return err + } else if C.Py_IsInitialized() == 0 { + err = errors.New("initialized script module failure") + return err + } + + initPython3Callback() + return nil +} + +func Py_IsInitialized() bool { + lock.Lock() + defer lock.Unlock() + + return C.Py_IsInitialized() != 0 +} + +func Py_Finalize() { + lock.Lock() + defer lock.Unlock() + + if C.Py_IsInitialized() != 0 { + if pyThreadState != nil { + PyEval_RestoreThread(pyThreadState) + } + C.finalize_Python() + _ = os.RemoveAll(constant.Path.ScriptDir()) + log.Warnln("Clash clean up script mode.") + } +} + +//Py_GetVersion get +func Py_GetVersion() string { + cversion := C.Py_GetVersion() + return strings.Split(C.GoString(cversion), "\n")[0] +} + +// loadPyFunc loads a Python function by module and function name +func loadPyFunc(moduleName, funcName string) (*C.PyObject, error) { + // Convert names to C char* + cMod := C.CString(moduleName) + cFunc := C.CString(funcName) + + // Free memory allocated by C.CString + defer func() { + C.free(unsafe.Pointer(cMod)) + C.free(unsafe.Pointer(cFunc)) + }() + + fnc := C.load_func(cMod, cFunc) + if fnc == nil { + return nil, PyLastError() + } + + return fnc, nil +} + +//PyLastError python last error +func PyLastError() error { + cp := C.py_last_error() + if cp == nil { + return nil + } + + return errors.New(C.GoString(cp)) +} + +func LoadShortcutFunction(shortcut string) (*PyObject, error) { + fnc, err := loadPyFunc(ClashScriptModuleName, shortcut) + if err != nil { + return nil, err + } + return togo(fnc), nil +} + +func LoadMainFunction() error { + C.load_main_func() + err := PyLastError() + if err != nil { + return err + } + return nil +} + +//CallPyMainFunction call python script main function +//return the proxy adapter name. +func CallPyMainFunction(mtd *constant.Metadata) (string, error) { + _type := C.CString(mtd.Type.String()) + network := C.CString(mtd.NetWork.String()) + processName := C.CString(mtd.Process) + host := C.CString(mtd.Host) + + srcPortGo, _ := strconv.ParseUint(mtd.SrcPort, 10, 16) + dstPortGo, _ := strconv.ParseUint(mtd.DstPort, 10, 16) + srcPort := C.ushort(srcPortGo) + dstPort := C.ushort(dstPortGo) + + dstIpGo := "" + srcIpGo := "" + if mtd.SrcIP != nil { + srcIpGo = mtd.SrcIP.String() + } + if mtd.DstIP != nil { + dstIpGo = mtd.DstIP.String() + } + srcIp := C.CString(srcIpGo) + dstIp := C.CString(dstIpGo) + + defer func() { + C.free(unsafe.Pointer(_type)) + C.free(unsafe.Pointer(network)) + C.free(unsafe.Pointer(processName)) + C.free(unsafe.Pointer(host)) + C.free(unsafe.Pointer(srcIp)) + C.free(unsafe.Pointer(dstIp)) + }() + + runtime.LockOSThread() + gilState := PyGILState_Ensure() + defer PyGILState_Release(gilState) + + cRs := C.call_main(_type, network, processName, host, srcIp, srcPort, dstIp, dstPort) + + rs := C.GoString(cRs) + if rs == "-1" { + err := PyLastError() + if err != nil { + log.Errorln("[Script] script code error: %s", err.Error()) + killSelf() + return "", fmt.Errorf("script code error: %w", err) + } else { + return "", fmt.Errorf("script code error, result: %v", rs) + } + } + + return rs, nil +} + +//CallPyShortcut call python script shortcuts function +//param: shortcut name +//return the match result. +func CallPyShortcut(fn *PyObject, mtd *constant.Metadata) (bool, error) { + _type := C.CString(mtd.Type.String()) + network := C.CString(mtd.NetWork.String()) + processName := C.CString(mtd.Process) + host := C.CString(mtd.Host) + + srcPortGo, _ := strconv.ParseUint(mtd.SrcPort, 10, 16) + dstPortGo, _ := strconv.ParseUint(mtd.DstPort, 10, 16) + srcPort := C.ushort(srcPortGo) + dstPort := C.ushort(dstPortGo) + + dstIpGo := "" + srcIpGo := "" + if mtd.SrcIP != nil { + srcIpGo = mtd.SrcIP.String() + } + if mtd.DstIP != nil { + dstIpGo = mtd.DstIP.String() + } + srcIp := C.CString(srcIpGo) + dstIp := C.CString(dstIpGo) + + defer func() { + C.free(unsafe.Pointer(_type)) + C.free(unsafe.Pointer(network)) + C.free(unsafe.Pointer(processName)) + C.free(unsafe.Pointer(host)) + C.free(unsafe.Pointer(srcIp)) + C.free(unsafe.Pointer(dstIp)) + }() + + runtime.LockOSThread() + gilState := PyGILState_Ensure() + defer PyGILState_Release(gilState) + + cRs := C.call_shortcut(toc(fn), _type, network, processName, host, srcIp, srcPort, dstIp, dstPort) + + rs := int(cRs) + if rs == -1 { + err := PyLastError() + if err != nil { + log.Errorln("[Script] script shortcut code error: %s", err.Error()) + killSelf() + return false, fmt.Errorf("script shortcut code error: %w", err) + } else { + return false, fmt.Errorf("script shortcut code error: result: %d", rs) + } + } + + if rs == 1 { + return true, nil + } else { + return false, nil + } +} + +func initPython3Callback() { + C.go_set_resolve_ip_callback() + C.go_set_geoip_callback() + C.go_set_rule_provider_callback() + C.go_set_log_callback() +} + +//NewClashPyContext new clash context for python +func NewClashPyContext(ruleProvidersName []string) error { + length := len(ruleProvidersName) + cStringArr := make([]*C.char, length) + for i, v := range ruleProvidersName { + cStringArr[i] = C.CString(v) + defer C.free(unsafe.Pointer(cStringArr[i])) + } + + cArrPointer := unsafe.Pointer(nil) + if length > 0 { + cArrPointer = unsafe.Pointer(&cStringArr[0]) + } + + rs := C.new_clash_py_context((**C.char)(cArrPointer), C.int(length)) + + if int(rs) == 0 { + err := PyLastError() + return fmt.Errorf("new script module context failure: %s", err.Error()) + } + + return nil +} + +func killSelf() { + p, err := os.FindProcess(os.Getpid()) + + if err != nil { + os.Exit(int(syscall.SIGINT)) + return + } + + _ = p.Signal(syscall.SIGINT) +} diff --git a/component/script/clash_module.h b/component/script/clash_module.h new file mode 100644 index 00000000..3e03e16d --- /dev/null +++ b/component/script/clash_module.h @@ -0,0 +1,62 @@ +#ifndef CLASH_CALLBACK_MODULE_H__ +#define CLASH_CALLBACK_MODULE_H__ + +#include + +#define CLASH_SCRIPT_MODULE_NAME "clash_script" + +struct Metadata { + const char *type; /* type socks5/http */ + const char *network; /* network tcp/udp */ + const char *process_name; + const char *host; + const char *src_ip; + unsigned short src_port; + const char *dst_ip; + unsigned short dst_port; +}; + +/** callback function, that call go function by python3 script. **/ +typedef const char *(*resolve_ip_callback)(const char *host); +typedef const char *(*geoip_callback)(const char *ip); +typedef const int (*rule_provider_callback)(const char *provider_name, struct Metadata *metadata); +typedef void (*log_callback)(const char *msg); + +void set_resolve_ip_callback(resolve_ip_callback cb); +void set_geoip_callback(geoip_callback cb); +void set_rule_provider_callback(rule_provider_callback cb); +void set_log_callback(log_callback cb); +/*---------------------------------------------------------------*/ + +void append_inittab(); +void init_python(const char *program, const char *path); +void load_main_func(); +void finalize_Python(); +void py_clear(PyObject *obj); +const char *py_last_error(); + +PyObject *load_func(const char *module_name, char *func_name); + +int new_clash_py_context(const char *provider_name_arr[], int size); + +const char *call_main( + const char *type, + const char *network, + const char *process_name, + const char *host, + const char *src_ip, + unsigned short src_port, + const char *dst_ip, + unsigned short dst_port); + +int call_shortcut(PyObject *shortcut_fn, + const char *type, + const char *network, + const char *process_name, + const char *host, + const char *src_ip, + unsigned short src_port, + const char *dst_ip, + unsigned short dst_port); + +#endif // CLASH_CALLBACK_MODULE_H__ \ No newline at end of file diff --git a/component/script/clash_module_export.go b/component/script/clash_module_export.go new file mode 100644 index 00000000..b3f5355b --- /dev/null +++ b/component/script/clash_module_export.go @@ -0,0 +1,136 @@ +package script + +/* +#include "clash_module.h" +*/ +import "C" +import ( + "net" + "strconv" + "strings" + "unsafe" + + "github.com/Dreamacro/clash/component/mmdb" + "github.com/Dreamacro/clash/component/resolver" + "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" +) + +var ( + ruleProviders = map[string]constant.Rule{} + pyThreadState *PyThreadState +) + +func UpdateRuleProviders(rpd map[string]constant.Rule) { + ruleProviders = rpd + if Py_IsInitialized() { + pyThreadState = PyEval_SaveThread() + } +} + +//export resolveIPCallbackFn +func resolveIPCallbackFn(cHost *C.char) *C.char { + host := C.GoString(cHost) + if len(host) == 0 { + cip := C.CString("") + defer C.free(unsafe.Pointer(cip)) + return cip + } + if ip, err := resolver.ResolveIP(host); err == nil { + cip := C.CString(ip.String()) + defer C.free(unsafe.Pointer(cip)) + return cip + } else { + log.Errorln("[Script] resolve ip error: %s", err.Error()) + cip := C.CString("") + defer C.free(unsafe.Pointer(cip)) + return cip + } +} + +//export geoipCallbackFn +func geoipCallbackFn(cIP *C.char) *C.char { + dstIP := net.ParseIP(C.GoString(cIP)) + + if dstIP == nil { + emptyC := C.CString("") + defer C.free(unsafe.Pointer(emptyC)) + + return emptyC + } + + if dstIP.IsPrivate() || constant.TunBroadcastAddr.Equal(dstIP) { + lanC := C.CString("LAN") + defer C.free(unsafe.Pointer(lanC)) + + return lanC + } + + record, _ := mmdb.Instance().Country(dstIP) + + rc := C.CString(strings.ToUpper(record.Country.IsoCode)) + defer C.free(unsafe.Pointer(rc)) + + return rc +} + +//export ruleProviderCallbackFn +func ruleProviderCallbackFn(cProviderName *C.char, cMetadata *C.struct_Metadata) C.int { + //_type := C.GoString(cMetadata._type) + //network := C.GoString(cMetadata.network) + processName := C.GoString(cMetadata.process_name) + host := C.GoString(cMetadata.host) + srcIp := C.GoString(cMetadata.src_ip) + srcPort := strconv.Itoa(int(cMetadata.src_port)) + dstIp := C.GoString(cMetadata.dst_ip) + dstPort := strconv.Itoa(int(cMetadata.dst_port)) + + dst := net.ParseIP(dstIp) + addrType := constant.AtypDomainName + + if dst != nil { + if dst.To4() != nil { + addrType = constant.AtypIPv4 + } else { + addrType = constant.AtypIPv6 + } + } + + metadata := &constant.Metadata{ + Process: processName, + SrcIP: net.ParseIP(srcIp), + DstIP: dst, + SrcPort: srcPort, + DstPort: dstPort, + AddrType: addrType, + Host: host, + } + + providerName := C.GoString(cProviderName) + + rule, ok := ruleProviders[providerName] + if !ok { + log.Warnln("[Script] rule provider [%s] not found", providerName) + return C.int(0) + } + + if strings.HasPrefix(providerName, "geosite:") { + if len(host) == 0 { + return C.int(0) + } + metadata.AddrType = constant.AtypDomainName + } + + rs := rule.Match(metadata) + + if rs { + return C.int(1) + } + return C.int(0) +} + +//export logCallbackFn +func logCallbackFn(msg *C.char) { + + log.Infoln(C.GoString(msg)) +} diff --git a/component/script/thread.go b/component/script/thread.go new file mode 100644 index 00000000..8b7735c0 --- /dev/null +++ b/component/script/thread.go @@ -0,0 +1,52 @@ +package script + +/* +#include "Python.h" +*/ +import "C" + +//PyThreadState : https://docs.python.org/3/c-api/init.html#c.PyThreadState +type PyThreadState C.PyThreadState + +//PyGILState is an opaque “handle” to the thread state when PyGILState_Ensure() was called, and must be passed to PyGILState_Release() to ensure Python is left in the same state +type PyGILState C.PyGILState_STATE + +//PyEval_SaveThread : https://docs.python.org/3/c-api/init.html#c.PyEval_SaveThread +func PyEval_SaveThread() *PyThreadState { + return (*PyThreadState)(C.PyEval_SaveThread()) +} + +//PyEval_RestoreThread : https://docs.python.org/3/c-api/init.html#c.PyEval_RestoreThread +func PyEval_RestoreThread(tstate *PyThreadState) { + C.PyEval_RestoreThread((*C.PyThreadState)(tstate)) +} + +//PyThreadState_Get : https://docs.python.org/3/c-api/init.html#c.PyThreadState_Get +func PyThreadState_Get() *PyThreadState { + return (*PyThreadState)(C.PyThreadState_Get()) +} + +//PyThreadState_Swap : https://docs.python.org/3/c-api/init.html#c.PyThreadState_Swap +func PyThreadState_Swap(tstate *PyThreadState) *PyThreadState { + return (*PyThreadState)(C.PyThreadState_Swap((*C.PyThreadState)(tstate))) +} + +//PyGILState_Ensure : https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure +func PyGILState_Ensure() PyGILState { + return PyGILState(C.PyGILState_Ensure()) +} + +//PyGILState_Release : https://docs.python.org/3/c-api/init.html#c.PyGILState_Release +func PyGILState_Release(state PyGILState) { + C.PyGILState_Release(C.PyGILState_STATE(state)) +} + +//PyGILState_GetThisThreadState : https://docs.python.org/3/c-api/init.html#c.PyGILState_GetThisThreadState +func PyGILState_GetThisThreadState() *PyThreadState { + return (*PyThreadState)(C.PyGILState_GetThisThreadState()) +} + +//PyGILState_Check : https://docs.python.org/3/c-api/init.html#c.PyGILState_Check +func PyGILState_Check() bool { + return C.PyGILState_Check() == 1 +} diff --git a/config/config.go b/config/config.go index aeb9e312..aca9156d 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,8 @@ import ( "net" "net/url" "os" + "regexp" + "runtime" "strings" "github.com/Dreamacro/clash/adapter" @@ -15,6 +17,9 @@ import ( "github.com/Dreamacro/clash/adapter/provider" "github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/fakeip" + "github.com/Dreamacro/clash/component/geodata" + "github.com/Dreamacro/clash/component/geodata/router" + S "github.com/Dreamacro/clash/component/script" "github.com/Dreamacro/clash/component/trie" C "github.com/Dreamacro/clash/constant" providerTypes "github.com/Dreamacro/clash/constant/provider" @@ -43,6 +48,7 @@ type Inbound struct { RedirPort int `json:"redir-port"` TProxyPort int `json:"tproxy-port"` MixedPort int `json:"mixed-port"` + Tun Tun `json:"tun"` Authentication []string `json:"authentication"` AllowLan bool `json:"allow-lan"` BindAddress string `json:"bind-address"` @@ -72,10 +78,11 @@ type DNS struct { // FallbackFilter config type FallbackFilter struct { - GeoIP bool `yaml:"geoip"` - GeoIPCode string `yaml:"geoip-code"` - IPCIDR []*net.IPNet `yaml:"ipcidr"` - Domain []string `yaml:"domain"` + GeoIP bool `yaml:"geoip"` + GeoIPCode string `yaml:"geoip-code"` + IPCIDR []*net.IPNet `yaml:"ipcidr"` + Domain []string `yaml:"domain"` + GeoSite []*router.DomainMatcher `yaml:"geosite"` } var ( @@ -90,20 +97,36 @@ type Profile struct { StoreFakeIP bool `yaml:"store-fake-ip"` } +// Tun config +type Tun struct { + Enable bool `yaml:"enable" json:"enable"` + Stack string `yaml:"stack" json:"stack"` + DNSListen string `yaml:"dns-listen" json:"dns-listen"` + AutoRoute bool `yaml:"auto-route" json:"auto-route"` +} + +// Script config +type Script struct { + MainCode string `yaml:"code" json:"code"` + ShortcutsCode map[string]string `yaml:"shortcuts" json:"shortcuts"` +} + // Experimental config type Experimental struct{} // Config is clash config manager type Config struct { - General *General - DNS *DNS - Experimental *Experimental - Hosts *trie.DomainTrie - Profile *Profile - Rules []C.Rule - Users []auth.AuthUser - Proxies map[string]C.Proxy - Providers map[string]providerTypes.ProxyProvider + General *General + Tun *Tun + DNS *DNS + Experimental *Experimental + Hosts *trie.DomainTrie + Profile *Profile + Rules []C.Rule + RuleProviders map[string]C.Rule + Users []auth.AuthUser + Proxies map[string]C.Proxy + Providers map[string]providerTypes.ProxyProvider } type RawDNS struct { @@ -126,6 +149,7 @@ type RawFallbackFilter struct { GeoIPCode string `yaml:"geoip-code"` IPCIDR []string `yaml:"ipcidr"` Domain []string `yaml:"domain"` + GeoSite []string `yaml:"geosite"` } type RawConfig struct { @@ -148,11 +172,13 @@ type RawConfig struct { ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"` Hosts map[string]string `yaml:"hosts"` DNS RawDNS `yaml:"dns"` + Tun Tun `yaml:"tun"` Experimental Experimental `yaml:"experimental"` Profile Profile `yaml:"profile"` Proxy []map[string]interface{} `yaml:"proxies"` ProxyGroup []map[string]interface{} `yaml:"proxy-groups"` Rule []string `yaml:"rules"` + Script Script `yaml:"script"` } // Parse config @@ -177,6 +203,12 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { Rule: []string{}, Proxy: []map[string]interface{}{}, ProxyGroup: []map[string]interface{}{}, + Tun: Tun{ + Enable: false, + Stack: "lwip", + DNSListen: "0.0.0.0:53", + AutoRoute: true, + }, DNS: RawDNS{ Enable: false, UseHosts: true, @@ -185,15 +217,20 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { GeoIP: true, GeoIPCode: "CN", IPCIDR: []string{}, + GeoSite: []string{}, }, DefaultNameserver: []string{ "114.114.114.114", - "8.8.8.8", + "223.5.5.5", }, }, Profile: Profile{ StoreSelected: true, }, + Script: Script{ + MainCode: "", + ShortcutsCode: map[string]string{}, + }, } if err := yaml.Unmarshal(buf, rawCfg); err != nil { @@ -222,11 +259,17 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.Proxies = proxies config.Providers = providers - rules, err := parseRules(rawCfg, proxies) + err = parseScript(rawCfg) + if err != nil { + return nil, err + } + + rules, ruleProviders, err := parseRules(rawCfg, proxies) if err != nil { return nil, err } config.Rules = rules + config.RuleProviders = ruleProviders hosts, err := parseHosts(rawCfg) if err != nil { @@ -234,7 +277,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.Hosts = hosts - dnsCfg, err := parseDNS(rawCfg, hosts) + dnsCfg, err := parseDNS(rawCfg, hosts, rules) if err != nil { return nil, err } @@ -264,6 +307,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { RedirPort: cfg.RedirPort, TProxyPort: cfg.TProxyPort, MixedPort: cfg.MixedPort, + Tun: cfg.Tun, AllowLan: cfg.AllowLan, BindAddress: cfg.BindAddress, }, @@ -337,10 +381,10 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ providersMap[name] = pd } - for _, provider := range providersMap { - log.Infoln("Start initial provider %s", provider.Name()) - if err := provider.Initial(); err != nil { - return nil, nil, fmt.Errorf("initial proxy provider %s error: %w", provider.Name(), err) + for _, rp := range providersMap { + log.Infoln("Start initial provider %s", rp.Name()) + if err := rp.Initial(); err != nil { + return nil, nil, fmt.Errorf("initial proxy provider %s error: %w", rp.Name(), err) } } @@ -395,23 +439,109 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ return proxies, providersMap, nil } -func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) { +func parseScript(cfg *RawConfig) error { + mode := cfg.Mode + script := cfg.Script + mainCode := cleanPyKeywords(script.MainCode) + shortcutsCode := script.ShortcutsCode + + if mode != T.Script && len(shortcutsCode) == 0 { + return nil + } else if mode == T.Script && len(mainCode) == 0 { + return fmt.Errorf("initialized script module failure, can't find script code in the config file") + } + + content := + `# -*- coding: UTF-8 -*- + +from datetime import datetime as whatever + +class ClashTime: + def now(self): + return whatever.now() + + def unix(self): + return int(whatever.now().timestamp()) + + def unix_nano(self): + return int(round(whatever.now().timestamp() * 1000)) + +time = ClashTime() + +` + + var shouldInitPy bool + if mode == T.Script { + content += mainCode + "\n\n" + shouldInitPy = true + } + + for k, v := range shortcutsCode { + v = cleanPyKeywords(v) + v = strings.TrimSpace(v) + if len(v) == 0 { + return fmt.Errorf("initialized rule SCRIPT failure, shortcut [%s] code invalid syntax", k) + } + + content += "def " + strings.ToLower(k) + "(ctx, network, process_name, host, src_ip, src_port, dst_ip, dst_port):\n return " + v + "\n\n" + shouldInitPy = true + } + + if !shouldInitPy { + return nil + } + + err := os.WriteFile(C.Path.Script(), []byte(content), 0o644) + if err != nil { + return fmt.Errorf("initialized script module failure, %s", err.Error()) + } + + if err = S.Py_Initialize(C.Path.GetExecutableFullPath(), C.Path.ScriptDir()); err != nil { + return fmt.Errorf("initialized script module failure, %s", err.Error()) + } else if mode == T.Script { + if err = S.LoadMainFunction(); err != nil { + return fmt.Errorf("initialized script module failure, %s", err.Error()) + } + } + + log.Infoln("Start initial script module successful, version: %s", S.Py_GetVersion()) + + return nil +} + +func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[string]C.Rule, error) { rules := []C.Rule{} + ruleProviders := map[string]C.Rule{} rulesConfig := cfg.Rule + mode := cfg.Mode + + providerNames := []string{} + isPyInit := S.Py_IsInitialized() // parse rules for idx, line := range rulesConfig { rule := trimArr(strings.Split(line, ",")) var ( - payload string - target string - params = []string{} + payload string + target string + params = []string{} + ruleName = strings.ToUpper(rule[0]) ) + if mode == T.Script && ruleName != "GEOSITE" { + continue + } + switch l := len(rule); { case l == 2: target = rule[1] case l == 3: + if ruleName == "MATCH" { + payload = "" + target = rule[1] + params = rule[2:] + break + } payload = rule[1] target = rule[2] case l >= 4: @@ -419,25 +549,45 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) { target = rule[2] params = rule[3:] default: - return nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line) + return nil, nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line) } - if _, ok := proxies[target]; !ok { - return nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target) + if _, ok := proxies[target]; mode != T.Script && !ok { + return nil, nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target) } - rule = trimArr(rule) params = trimArr(params) - parsed, parseErr := R.ParseRule(rule[0], payload, target, params) + parsed, parseErr := R.ParseRule(ruleName, payload, target, params) if parseErr != nil { - return nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error()) + return nil, nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error()) } - rules = append(rules, parsed) + if isPyInit { + if ruleName == "GEOSITE" { + pvName := "geosite:" + strings.ToLower(payload) + providerNames = append(providerNames, pvName) + ruleProviders[pvName] = parsed + } + } + + if mode != T.Script { + rules = append(rules, parsed) + } } - return rules, nil + runtime.GC() + + if isPyInit { + err := S.NewClashPyContext(providerNames) + if err != nil { + return nil, nil, err + } else { + log.Infoln("Start initial script context successful, provider records: %v", len(providerNames)) + } + } + + return rules, ruleProviders, nil } func parseHosts(cfg *RawConfig) (*trie.DomainTrie, error) { @@ -454,7 +604,7 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie, error) { if ip == nil { return nil, fmt.Errorf("%s is not a valid IP", ipStr) } - tree.Insert(domain, ip) + _ = tree.Insert(domain, ip) } } @@ -520,8 +670,9 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { nameservers = append( nameservers, dns.NameServer{ - Net: dnsNetType, - Addr: addr, + Net: dnsNetType, + Addr: addr, + ProxyAdapter: u.Fragment, }, ) } @@ -559,7 +710,37 @@ func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) { return ipNets, nil } -func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie) (*DNS, error) { +func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainMatcher, error) { + sites := []*router.DomainMatcher{} + + for _, country := range countries { + found := false + for _, rule := range rules { + if rule.RuleType() == C.GEOSITE { + if strings.EqualFold(country, rule.Payload()) { + found = true + sites = append(sites, rule.(C.RuleGeoSite).GetDomainMatcher()) + log.Infoln("Start initial GeoSite dns fallback filter from rule `%s`", country) + } + } + } + + if !found { + matcher, recordsCount, err := geodata.LoadGeoSiteMatcher(country) + if err != nil { + return nil, err + } + + sites = append(sites, matcher) + + log.Infoln("Start initial GeoSite dns fallback filter `%s`, records: %d", country, recordsCount) + } + } + runtime.GC() + return sites, nil +} + +func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie, rules []C.Rule) (*DNS, error) { cfg := rawCfg.DNS if cfg.Enable && len(cfg.NameServer) == 0 { return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") @@ -571,7 +752,8 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie) (*DNS, error) { IPv6: cfg.IPv6, EnhancedMode: cfg.EnhancedMode, FallbackFilter: FallbackFilter{ - IPCIDR: []*net.IPNet{}, + IPCIDR: []*net.IPNet{}, + GeoSite: []*router.DomainMatcher{}, }, } var err error @@ -612,7 +794,19 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie) (*DNS, error) { if len(cfg.FakeIPFilter) != 0 { host = trie.New() for _, domain := range cfg.FakeIPFilter { - host.Insert(domain, true) + _ = host.Insert(domain, true) + } + } + + if len(dnsCfg.Fallback) != 0 { + if host == nil { + host = trie.New() + } + for _, fb := range dnsCfg.Fallback { + if net.ParseIP(fb.Addr) != nil { + continue + } + host.Insert(fb.Addr, true) } } @@ -629,12 +823,19 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie) (*DNS, error) { dnsCfg.FakeIPRange = pool } - dnsCfg.FallbackFilter.GeoIP = cfg.FallbackFilter.GeoIP - dnsCfg.FallbackFilter.GeoIPCode = cfg.FallbackFilter.GeoIPCode - if fallbackip, err := parseFallbackIPCIDR(cfg.FallbackFilter.IPCIDR); err == nil { - dnsCfg.FallbackFilter.IPCIDR = fallbackip + if len(cfg.Fallback) != 0 { + dnsCfg.FallbackFilter.GeoIP = cfg.FallbackFilter.GeoIP + dnsCfg.FallbackFilter.GeoIPCode = cfg.FallbackFilter.GeoIPCode + if fallbackip, err := parseFallbackIPCIDR(cfg.FallbackFilter.IPCIDR); err == nil { + dnsCfg.FallbackFilter.IPCIDR = fallbackip + } + dnsCfg.FallbackFilter.Domain = cfg.FallbackFilter.Domain + fallbackGeoSite, err := parseFallbackGeoSite(cfg.FallbackFilter.GeoSite, rules) + if err != nil { + return nil, fmt.Errorf("load GeoSite dns fallback filter error, %w", err) + } + dnsCfg.FallbackFilter.GeoSite = fallbackGeoSite } - dnsCfg.FallbackFilter.Domain = cfg.FallbackFilter.Domain if cfg.UseHosts { dnsCfg.Hosts = hosts @@ -653,3 +854,16 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser { } return users } + +func cleanPyKeywords(code string) string { + if len(code) == 0 { + return code + } + keywords := []string{"import", "print"} + + for _, kw := range keywords { + reg := regexp.MustCompile("(?m)[\r\n]+^.*" + kw + ".*$") + code = reg.ReplaceAllString(code, "") + } + return code +} diff --git a/config/initial.go b/config/initial.go index 9d1a2db1..9d8288ba 100644 --- a/config/initial.go +++ b/config/initial.go @@ -12,7 +12,7 @@ import ( ) func downloadMMDB(path string) (err error) { - resp, err := http.Get("https://cdn.jsdelivr.net/gh/Dreamacro/maxmind-geoip@release/Country.mmdb") + resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/Country.mmdb") if err != nil { return } @@ -50,6 +50,65 @@ func initMMDB() error { return nil } +//func downloadGeoIP(path string) (err error) { +// resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat") +// if err != nil { +// return +// } +// defer resp.Body.Close() +// +// f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0644) +// if err != nil { +// return err +// } +// defer f.Close() +// _, err = io.Copy(f, resp.Body) +// +// return err +//} + +func downloadGeoSite(path string) (err error) { + resp, err := http.Get("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat") + if err != nil { + return + } + defer resp.Body.Close() + + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(f, resp.Body) + + return err +} + +// +//func initGeoIP() error { +// if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) { +// log.Infoln("Can't find GeoIP.dat, start download") +// if err := downloadGeoIP(C.Path.GeoIP()); err != nil { +// return fmt.Errorf("can't download GeoIP.dat: %s", err.Error()) +// } +// log.Infoln("Download GeoIP.dat finish") +// } +// +// return nil +//} + +func initGeoSite() error { + if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) { + log.Infoln("Can't find GeoSite.dat, start download") + if err := downloadGeoSite(C.Path.GeoSite()); err != nil { + return fmt.Errorf("can't download GeoSite.dat: %s", err.Error()) + } + log.Infoln("Download GeoSite.dat finish") + } + + return nil +} + // Init prepare necessary files func Init(dir string) error { // initial homedir @@ -70,9 +129,19 @@ func Init(dir string) error { f.Close() } + //// initial GeoIP + //if err := initGeoIP(); err != nil { + // return fmt.Errorf("can't initial GeoIP: %w", err) + //} + // initial mmdb if err := initMMDB(); err != nil { return fmt.Errorf("can't initial MMDB: %w", err) } + + // initial GeoSite + if err := initGeoSite(); err != nil { + return fmt.Errorf("can't initial GeoSite: %w", err) + } return nil } diff --git a/constant/adapters.go b/constant/adapters.go index 59fde960..0b394e10 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -20,6 +20,7 @@ const ( Socks5 Http Vmess + Vless Trojan Relay @@ -141,6 +142,8 @@ func (at AdapterType) String() string { return "Http" case Vmess: return "Vmess" + case Vless: + return "Vless" case Trojan: return "Trojan" diff --git a/constant/metadata.go b/constant/metadata.go index 9cc49973..6acd4202 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -14,6 +14,7 @@ const ( TCP NetWork = iota UDP + ALLNet HTTP Type = iota HTTPCONNECT @@ -21,6 +22,7 @@ const ( SOCKS5 REDIR TPROXY + TUN ) type NetWork int @@ -28,8 +30,10 @@ type NetWork int func (n NetWork) String() string { if n == TCP { return "tcp" + } else if n == UDP { + return "udp" } - return "udp" + return "all" } func (n NetWork) MarshalJSON() ([]byte, error) { @@ -52,6 +56,8 @@ func (t Type) String() string { return "Redir" case TPROXY: return "TProxy" + case TUN: + return "Tun" default: return "Unknown" } @@ -71,6 +77,7 @@ type Metadata struct { DstPort string `json:"destinationPort"` AddrType int `json:"-"` Host string `json:"host"` + Process string `json:"process"` DNSMode DNSMode `json:"dnsMode"` } diff --git a/constant/path.go b/constant/path.go index 781cc5f7..f0f4e8c3 100644 --- a/constant/path.go +++ b/constant/path.go @@ -22,6 +22,7 @@ var Path = func() *path { type path struct { homeDir string configFile string + scriptDir string } // SetHomeDir is used to set the configuration path @@ -62,3 +63,41 @@ func (p *path) OldCache() string { func (p *path) Cache() string { return P.Join(p.homeDir, "cache.db") } + +func (p *path) GeoIP() string { + return P.Join(p.homeDir, "geoip.dat") +} + +func (p *path) GeoSite() string { + return P.Join(p.homeDir, "geosite.dat") +} + +func (p *path) ScriptDir() string { + if len(p.scriptDir) != 0 { + return p.scriptDir + } + if dir, err := os.MkdirTemp("", Name+"-"); err == nil { + p.scriptDir = dir + } else { + p.scriptDir = P.Join(os.TempDir(), Name) + _ = os.MkdirAll(p.scriptDir, 0o644) + } + return p.scriptDir +} + +func (p *path) Script() string { + return P.Join(p.ScriptDir(), "clash_script.py") +} + +func (p *path) GetAssetLocation(file string) string { + return P.Join(p.homeDir, file) +} + +func (p *path) GetExecutableFullPath() string { + exePath, err := os.Executable() + if err != nil { + return "clash" + } + res, _ := filepath.EvalSymlinks(exePath) + return res +} diff --git a/constant/rule.go b/constant/rule.go index d7c43416..e2087604 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -5,12 +5,14 @@ const ( Domain RuleType = iota DomainSuffix DomainKeyword + GEOSITE GEOIP IPCIDR SrcIPCIDR SrcPort DstPort Process + Script MATCH ) @@ -24,6 +26,8 @@ func (rt RuleType) String() string { return "DomainSuffix" case DomainKeyword: return "DomainKeyword" + case GEOSITE: + return "GeoSite" case GEOIP: return "GeoIP" case IPCIDR: @@ -36,6 +40,8 @@ func (rt RuleType) String() string { return "DstPort" case Process: return "Process" + case Script: + return "Script" case MATCH: return "Match" default: @@ -49,4 +55,5 @@ type Rule interface { Adapter() string Payload() string ShouldResolveIP() bool + RuleExtra() *RuleExtra } diff --git a/constant/rule_extra.go b/constant/rule_extra.go new file mode 100644 index 00000000..119b42ca --- /dev/null +++ b/constant/rule_extra.go @@ -0,0 +1,35 @@ +package constant + +import ( + "net" + + "github.com/Dreamacro/clash/component/geodata/router" +) + +var TunBroadcastAddr = net.IPv4(198, 18, 255, 255) + +type RuleExtra struct { + Network NetWork + SourceIPs []*net.IPNet +} + +func (re *RuleExtra) NotMatchNetwork(network NetWork) bool { + return re.Network != ALLNet && re.Network != network +} + +func (re *RuleExtra) NotMatchSourceIP(srcIP net.IP) bool { + if re.SourceIPs == nil { + return false + } + + for _, ips := range re.SourceIPs { + if ips.Contains(srcIP) { + return false + } + } + return true +} + +type RuleGeoSite interface { + GetDomainMatcher() *router.DomainMatcher +} diff --git a/dns/client.go b/dns/client.go index 5cb1fe02..56ff1998 100644 --- a/dns/client.go +++ b/dns/client.go @@ -15,10 +15,11 @@ import ( type client struct { *D.Client - r *Resolver - port string - host string - iface string + r *Resolver + port string + host string + iface string + proxyAdapter string } func (c *client) Exchange(m *D.Msg) (*D.Msg, error) { @@ -30,14 +31,15 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) ip net.IP err error ) - if c.r == nil { - // a default ip dns - if ip = net.ParseIP(c.host); ip == nil { + + if ip = net.ParseIP(c.host); ip == nil { + if c.r == nil { return nil, fmt.Errorf("dns %s not a valid ip", c.host) - } - } else { - if ip, err = resolver.ResolveIPWithResolver(c.host, c.r); err != nil { - return nil, fmt.Errorf("use default dns resolve failed: %w", err) + } else { + if ip, err = resolver.ResolveIPWithResolver(c.host, c.r); err != nil { + return nil, fmt.Errorf("use default dns resolve failed: %w", err) + } + c.host = ip.String() } } @@ -50,7 +52,14 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) if c.iface != "" { options = append(options, dialer.WithInterface(c.iface)) } - conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), c.port), options...) + + var conn net.Conn + if c.proxyAdapter == "" { + conn, err = dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), c.port), options...) + } else { + conn, err = dialContextWithProxyAdapter(ctx, c.proxyAdapter, network, ip, c.port, options...) + } + if err != nil { return nil, err } diff --git a/dns/doh.go b/dns/doh.go index 94312355..36502265 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -19,8 +19,9 @@ const ( ) type dohClient struct { - url string - transport *http.Transport + url string + proxyAdapter string + transport *http.Transport } func (dc *dohClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { @@ -62,7 +63,7 @@ func (dc *dohClient) newRequest(m *D.Msg) (*http.Request, error) { return req, nil } -func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) { +func (dc *dohClient) doRequest(req *http.Request) (*D.Msg, error) { client := &http.Client{Transport: dc.transport} resp, err := client.Do(req) if err != nil { @@ -74,14 +75,15 @@ func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) { if err != nil { return nil, err } - msg = &D.Msg{} + msg := &D.Msg{} err = msg.Unpack(buf) return msg, err } -func newDoHClient(url string, r *Resolver) *dohClient { +func newDoHClient(url string, r *Resolver, proxyAdapter string) *dohClient { return &dohClient{ - url: url, + url: url, + proxyAdapter: proxyAdapter, transport: &http.Transport{ ForceAttemptHTTP2: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { @@ -95,7 +97,11 @@ func newDoHClient(url string, r *Resolver) *dohClient { return nil, err } - return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port)) + if proxyAdapter == "" { + return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port)) + } else { + return dialContextWithProxyAdapter(ctx, proxyAdapter, "tcp", ip, port) + } }, }, } diff --git a/dns/filters.go b/dns/filters.go index 30825a44..9c1be0d0 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -4,8 +4,10 @@ import ( "net" "strings" + "github.com/Dreamacro/clash/component/geodata/router" "github.com/Dreamacro/clash/component/mmdb" "github.com/Dreamacro/clash/component/trie" + C "github.com/Dreamacro/clash/constant" ) type fallbackIPFilter interface { @@ -18,7 +20,7 @@ type geoipFilter struct { func (gf *geoipFilter) Match(ip net.IP) bool { record, _ := mmdb.Instance().Country(ip) - return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate() + return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate() && !ip.Equal(C.TunBroadcastAddr) } type ipnetFilter struct { @@ -48,3 +50,16 @@ func NewDomainFilter(domains []string) *domainFilter { func (df *domainFilter) Match(domain string) bool { return df.tree.Search(domain) != nil } + +type geoSiteFilter struct { + matchers []*router.DomainMatcher +} + +func (gsf *geoSiteFilter) Match(domain string) bool { + for _, matcher := range gsf.matchers { + if matcher.ApplyDomain(domain) { + return true + } + } + return false +} diff --git a/dns/middleware.go b/dns/middleware.go index 9d0da596..dc7cbe33 100644 --- a/dns/middleware.go +++ b/dns/middleware.go @@ -172,7 +172,7 @@ func compose(middlewares []middleware, endpoint handler) handler { return h } -func newHandler(resolver *Resolver, mapper *ResolverEnhancer) handler { +func NewHandler(resolver *Resolver, mapper *ResolverEnhancer) handler { middlewares := []middleware{} if resolver.hosts != nil { diff --git a/dns/patch.go b/dns/patch.go new file mode 100644 index 00000000..76974243 --- /dev/null +++ b/dns/patch.go @@ -0,0 +1,16 @@ +package dns + +import D "github.com/miekg/dns" + +type LocalServer struct { + handler handler +} + +// ServeMsg implement resolver.LocalServer ResolveMsg +func (s *LocalServer) ServeMsg(msg *D.Msg) (*D.Msg, error) { + return handlerWithContext(s.handler, msg) +} + +func NewLocalServer(resolver *Resolver, mapper *ResolverEnhancer) *LocalServer { + return &LocalServer{handler: NewHandler(resolver, mapper)} +} diff --git a/dns/resolver.go b/dns/resolver.go index 91efbb8b..8a10f317 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -12,6 +12,7 @@ import ( "github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/common/picker" "github.com/Dreamacro/clash/component/fakeip" + "github.com/Dreamacro/clash/component/geodata/router" "github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/trie" C "github.com/Dreamacro/clash/constant" @@ -149,7 +150,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M return } -func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { +func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (*D.Msg, error) { fast, ctx := picker.WithTimeout(ctx, resolver.DefaultDNSTimeout) for _, client := range clients { r := client @@ -173,8 +174,8 @@ func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D. return nil, err } - msg = elm.(*D.Msg) - return + msg := elm.(*D.Msg) + return msg, nil } func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient { @@ -215,7 +216,7 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool { return false } -func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { +func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (*D.Msg, error) { if matched := r.matchPolicy(m); len(matched) != 0 { res := <-r.asyncExchange(ctx, matched, m) return res.Msg, res.Error @@ -230,27 +231,22 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er msgCh := r.asyncExchange(ctx, r.main, m) - if r.fallback == nil { // directly return if no fallback servers are available + if r.fallback == nil || len(r.fallback) == 0 { // directly return if no fallback servers are available res := <-msgCh - msg, err = res.Msg, res.Error - return + return res.Msg, res.Error } - fallbackMsg := r.asyncExchange(ctx, r.fallback, m) res := <-msgCh if res.Error == nil { if ips := msgToIP(res.Msg); len(ips) != 0 { if !r.shouldIPFallback(ips[0]) { - msg = res.Msg // no need to wait for fallback result - err = res.Error - return msg, err + return res.Msg, res.Error // no need to wait for fallback result } } } - res = <-fallbackMsg - msg, err = res.Msg, res.Error - return + res = <-r.asyncExchange(ctx, r.fallback, m) + return res.Msg, res.Error } func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) { @@ -302,9 +298,10 @@ func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D } type NameServer struct { - Net string - Addr string - Interface string + Net string + Addr string + Interface string + ProxyAdapter string } type FallbackFilter struct { @@ -312,6 +309,7 @@ type FallbackFilter struct { GeoIPCode string IPCIDR []*net.IPNet Domain []string + GeoSite []*router.DomainMatcher } type Config struct { @@ -360,10 +358,28 @@ func NewResolver(config Config) *Resolver { } r.fallbackIPFilters = fallbackIPFilters + fallbackDomainFilters := []fallbackDomainFilter{} if len(config.FallbackFilter.Domain) != 0 { - fallbackDomainFilters := []fallbackDomainFilter{NewDomainFilter(config.FallbackFilter.Domain)} - r.fallbackDomainFilters = fallbackDomainFilters + fallbackDomainFilters = append(fallbackDomainFilters, NewDomainFilter(config.FallbackFilter.Domain)) } + if len(config.FallbackFilter.GeoSite) != 0 { + fallbackDomainFilters = append(fallbackDomainFilters, &geoSiteFilter{ + matchers: config.FallbackFilter.GeoSite, + }) + } + r.fallbackDomainFilters = fallbackDomainFilters + + return r +} + +func NewMainResolver(old *Resolver) *Resolver { + r := &Resolver{ + ipv6: old.ipv6, + main: old.main, + lruCache: old.lruCache, + hosts: old.hosts, + policy: old.policy, + } return r } diff --git a/dns/server.go b/dns/server.go index 7abdd4d0..88277476 100644 --- a/dns/server.go +++ b/dns/server.go @@ -43,14 +43,14 @@ func handlerWithContext(handler handler, msg *D.Msg) (*D.Msg, error) { return handler(ctx, msg) } -func (s *Server) setHandler(handler handler) { +func (s *Server) SetHandler(handler handler) { s.handler = handler } func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) error { if addr == address && resolver != nil { - handler := newHandler(resolver, mapper) - server.setHandler(handler) + handler := NewHandler(resolver, mapper) + server.SetHandler(handler) return nil } @@ -81,7 +81,7 @@ func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) e } address = addr - handler := newHandler(resolver, mapper) + handler := NewHandler(resolver, mapper) server = &Server{handler: handler} server.Server = &D.Server{Addr: addr, PacketConn: p, Handler: server} diff --git a/dns/util.go b/dns/util.go index d11870f8..59eef1de 100644 --- a/dns/util.go +++ b/dns/util.go @@ -1,12 +1,17 @@ package dns import ( + "context" "crypto/tls" + "fmt" "net" "time" "github.com/Dreamacro/clash/common/cache" + "github.com/Dreamacro/clash/component/dialer" + C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/tunnel" D "github.com/miekg/dns" ) @@ -51,7 +56,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { for _, s := range servers { switch s.Net { case "https": - ret = append(ret, newDoHClient(s.Addr, resolver)) + ret = append(ret, newDoHClient(s.Addr, resolver, s.ProxyAdapter)) continue case "dhcp": ret = append(ret, newDHCPClient(s.Addr)) @@ -70,10 +75,11 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { UDPSize: 4096, Timeout: 5 * time.Second, }, - port: port, - host: host, - iface: s.Interface, - r: resolver, + port: port, + host: host, + iface: s.Interface, + r: resolver, + proxyAdapter: s.ProxyAdapter, }) } return ret @@ -104,3 +110,63 @@ func msgToIP(msg *D.Msg) []net.IP { return ips } + +type wrapPacketConn struct { + net.PacketConn + rAddr net.Addr +} + +func (wpc *wrapPacketConn) Read(b []byte) (n int, err error) { + n, _, err = wpc.PacketConn.ReadFrom(b) + return n, err +} + +func (wpc *wrapPacketConn) Write(b []byte) (n int, err error) { + return wpc.PacketConn.WriteTo(b, wpc.rAddr) +} + +func (wpc *wrapPacketConn) RemoteAddr() net.Addr { + return wpc.rAddr +} + +func dialContextWithProxyAdapter(ctx context.Context, adapterName string, network string, dstIP net.IP, port string, opts ...dialer.Option) (net.Conn, error) { + adapter, ok := tunnel.Proxies()[adapterName] + if !ok { + return nil, fmt.Errorf("proxy adapter [%s] not found", adapterName) + } + + networkType := C.TCP + if network == "udp" { + if !adapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", adapterName) + } + networkType = C.UDP + } + + addrType := C.AtypIPv4 + if dstIP.To4() == nil { + addrType = C.AtypIPv6 + } + + metadata := &C.Metadata{ + NetWork: networkType, + AddrType: addrType, + Host: "", + DstIP: dstIP, + DstPort: port, + } + + if networkType == C.UDP { + packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + return &wrapPacketConn{ + PacketConn: packetConn, + rAddr: metadata.UDPAddr(), + }, nil + } + + return adapter.DialContext(ctx, metadata, opts...) +} diff --git a/go.mod b/go.mod index e2ac4f2d..72225568 100644 --- a/go.mod +++ b/go.mod @@ -10,10 +10,13 @@ require ( github.com/gofrs/uuid v4.1.0+incompatible github.com/gorilla/websocket v1.4.2 github.com/insomniacslk/dhcp v0.0.0-20211026125128-ad197bcd36fd + github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 github.com/miekg/dns v1.1.43 github.com/oschwald/geoip2-golang v1.5.0 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 + github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 + github.com/yaling888/go-lwip v0.0.0-20211103185822-c9d650538091 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.9.0 go.uber.org/automaxprocs v1.4.0 @@ -21,14 +24,20 @@ require ( golang.org/x/net v0.0.0-20211105192438-b53810dc28af golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 + golang.zx2c4.com/wireguard/windows v0.5.1 + google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 + gvisor.dev/gvisor v0.0.0-20211104052249-2de3450f76d6 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/kr/pretty v0.2.1 // indirect github.com/oschwald/maxminddb-golang v1.8.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b // indirect + golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 6ef69f45..6f2f817f 100644 --- a/go.sum +++ b/go.sum @@ -1,38 +1,311 @@ +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.88.0/go.mod h1:dnKwfYbP9hQhefiUvpbcAyoGSHUrOxR20JVElLiUvEY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q= github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/bazelbuild/rules_go v0.27.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi/v5 v5.0.5 h1:l3RJ8T8TAqLsXFfah+RA6N4pydMbPwSdvNM+AFWvLUM= github.com/go-chi/chi/v5 v5.0.5/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE= github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.1.0+incompatible h1:sIa2eCvUTwgjbqXrPLfNwUf9S3i3mpH1O1atV+iL/Wk= github.com/gofrs/uuid v4.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github/v35 v35.1.0/go.mod h1:s0515YVTI+IMrDoy9Y4pHt9ShGpzHvHO8rZ7L7acgvs= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/insomniacslk/dhcp v0.0.0-20211026125128-ad197bcd36fd h1:jupbuQFZtwOBg/3EmK91/rGaYFkqCb9bwHOnwn7Cav0= github.com/insomniacslk/dhcp v0.0.0-20211026125128-ad197bcd36fd/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 h1:dkEFEnGUg2z/FAPywWr4yfR/sWDQK76qn3J4Y5H2hJs= +github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99/go.mod h1:FWfSixjrLgtK+dHkDoN6lHMNhvER24gnjUZd/wt8Z9o= +github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= @@ -42,85 +315,572 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc90/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lVJ1ONZzw= github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s= github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 h1:QHESTXtfgc1ABV+ArlbPVqUx9Ht5I0dDkYhxYoXFxNo= +github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs= +github.com/yaling888/go-lwip v0.0.0-20211103185822-c9d650538091 h1:uOaYhg8ue1gAzV7KNAz1uc/qvjEDRtl16nvTPYiaphM= +github.com/yaling888/go-lwip v0.0.0-20211103185822-c9d650538091/go.mod h1:Y+f95PkWh183q1oDJxdlxTHa2mpdHG5zvBhV0TUhhSY= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0= go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211105192438-b53810dc28af h1:SMeNJG/vclJ5wyBBd4xupMsSJIHTd1coW9g7q6KOjmY= golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 h1:G2DDmludOQZoWbpCr7OKDxnl478ZBGMcOhrv+ooX/Q4= golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0= +golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard/windows v0.5.1 h1:OnYw96PF+CsIMrqWo5QP3Q59q5hY1rFErk/yN3cS+JQ= +golang.zx2c4.com/wireguard/windows v0.5.1/go.mod h1:EApyTk/ZNrkbZjurHL1nleDYnsPpJYBO7LZEBCyDAHk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.52.0/go.mod h1:Him/adpjt0sxtkWViy0b6xyKW/SD71CwdJ7HqJo7SrU= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210721163202-f1cecdd8b78a/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gvisor.dev/gvisor v0.0.0-20211104052249-2de3450f76d6 h1:ZqN8yQG1UONNe/u1LJvvTg80BDevYvaRgmWPBlCT+0g= +gvisor.dev/gvisor v0.0.0-20211104052249-2de3450f76d6/go.mod h1:btyTBPTxT8AFMvW7yctFJ2nPCEDWZLpmKQEZ0gG+bbQ= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= +k8s.io/api v0.16.13/go.mod h1:QWu8UWSTiuQZMMeYjwLs6ILu5O74qKSJ0c+4vrchDxs= +k8s.io/apimachinery v0.16.13/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ= +k8s.io/apimachinery v0.16.14-rc.0/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ= +k8s.io/client-go v0.16.13/go.mod h1:UKvVT4cajC2iN7DCjLgT0KVY/cbY6DGdUCyRiIfws5M= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 59f94881..5b448061 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -2,7 +2,11 @@ package executor import ( "fmt" + "net" "os" + "runtime" + "strconv" + "strings" "sync" "github.com/Dreamacro/clash/adapter" @@ -13,6 +17,7 @@ import ( "github.com/Dreamacro/clash/component/profile" "github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/resolver" + S "github.com/Dreamacro/clash/component/script" "github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" @@ -20,6 +25,8 @@ import ( "github.com/Dreamacro/clash/dns" P "github.com/Dreamacro/clash/listener" authStore "github.com/Dreamacro/clash/listener/auth" + "github.com/Dreamacro/clash/listener/tproxy" + "github.com/Dreamacro/clash/listener/tun/dev" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" ) @@ -70,10 +77,12 @@ func ApplyConfig(cfg *config.Config, force bool) { updateUsers(cfg.Users) updateProxies(cfg.Proxies, cfg.Providers) updateRules(cfg.Rules) + updateRuleProviders(cfg.RuleProviders) updateHosts(cfg.Hosts) updateProfile(cfg) + updateIPTables(cfg.DNS, cfg.General) + updateDNS(cfg.DNS, cfg.General) updateGeneral(cfg.General, force) - updateDNS(cfg.DNS) updateExperimental(cfg) } @@ -91,6 +100,7 @@ func GetGeneral() *config.General { RedirPort: ports.RedirPort, TProxyPort: ports.TProxyPort, MixedPort: ports.MixedPort, + Tun: P.Tun(), Authentication: authenticator, AllowLan: P.AllowLan(), BindAddress: P.BindAddress(), @@ -105,9 +115,10 @@ func GetGeneral() *config.General { func updateExperimental(c *config.Config) {} -func updateDNS(c *config.DNS) { +func updateDNS(c *config.DNS, general *config.General) { if !c.Enable { resolver.DefaultResolver = nil + resolver.MainResolver = nil resolver.DefaultHostMapper = nil dns.ReCreateServer("", nil, nil) return @@ -125,12 +136,14 @@ func updateDNS(c *config.DNS) { GeoIPCode: c.FallbackFilter.GeoIPCode, IPCIDR: c.FallbackFilter.IPCIDR, Domain: c.FallbackFilter.Domain, + GeoSite: c.FallbackFilter.GeoSite, }, Default: c.DefaultNameserver, Policy: c.NameServerPolicy, } r := dns.NewResolver(cfg) + mr := dns.NewMainResolver(r) m := dns.NewEnhancer(cfg) // reuse cache of old host mapper @@ -139,7 +152,13 @@ func updateDNS(c *config.DNS) { } resolver.DefaultResolver = r + resolver.MainResolver = mr resolver.DefaultHostMapper = m + if general.Tun.Enable && !strings.EqualFold(general.Tun.Stack, "gvisor") { + resolver.DefaultLocalServer = dns.NewLocalServer(r, m) + } else { + resolver.DefaultLocalServer = nil + } if err := dns.ReCreateServer(c.Listen, r, m); err != nil { log.Errorln("Start DNS server error: %s", err.Error()) @@ -163,16 +182,35 @@ func updateRules(rules []C.Rule) { tunnel.UpdateRules(rules) } +func updateRuleProviders(providers map[string]C.Rule) { + S.UpdateRuleProviders(providers) +} + func updateGeneral(general *config.General, force bool) { - log.SetLevel(general.LogLevel) tunnel.SetMode(general.Mode) resolver.DisableIPv6 = !general.IPv6 + if (general.Tun.Enable || general.TProxyPort != 0) && general.Interface == "" { + autoDetectInterfaceName, err := dev.GetAutoDetectInterface() + if err == nil { + if autoDetectInterfaceName != "" && autoDetectInterfaceName != "" { + general.Interface = autoDetectInterfaceName + } else { + log.Debugln("Auto detect interface name is empty.") + } + } else { + log.Debugln("Can not find auto detect interface. %s", err.Error()) + } + } + dialer.DefaultInterface.Store(general.Interface) + log.Infoln("Use interface name: %s", general.Interface) + iface.FlushCache() if !force { + log.SetLevel(general.LogLevel) return } @@ -204,6 +242,14 @@ func updateGeneral(general *config.General, force bool) { if err := P.ReCreateMixed(general.MixedPort, tcpIn, udpIn); err != nil { log.Errorln("Start Mixed(http and socks) server error: %s", err.Error()) } + + if err := P.ReCreateTun(general.Tun, tcpIn, udpIn); err != nil { + log.Errorln("Start Tun interface error: %s", err.Error()) + S.Py_Finalize() + os.Exit(2) + } + + log.SetLevel(general.LogLevel) } func updateUsers(users []auth.AuthUser) { @@ -248,3 +294,38 @@ func patchSelectGroup(proxies map[string]C.Proxy) { selector.Set(selected) } } + +func updateIPTables(dns *config.DNS, general *config.General) { + if runtime.GOOS != "linux" || dns.Listen == "" || general.TProxyPort == 0 || general.Tun.Enable { + return + } + + _, dnsPortStr, err := net.SplitHostPort(dns.Listen) + if dnsPortStr == "0" || dnsPortStr == "" || err != nil { + return + } + + dnsPort, err := strconv.Atoi(dnsPortStr) + if err != nil { + return + } + + tproxy.CleanUpTProxyLinuxIPTables() + + err = tproxy.SetTProxyLinuxIPTables(general.Interface, general.TProxyPort, dnsPort) + + if err != nil { + log.Errorln("Can not setting iptables for TProxy on linux, %s", err.Error()) + os.Exit(2) + } +} + +func CleanUp() { + P.CleanUp() + + if runtime.GOOS == "linux" { + tproxy.CleanUpTProxyLinuxIPTables() + } + + S.Py_Finalize() +} diff --git a/hub/hub.go b/hub/hub.go index 471fdb5e..5b80ab66 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -48,3 +48,7 @@ func Parse(options ...Option) error { executor.ApplyConfig(cfg, true) return nil } + +func CleanUp() { + executor.CleanUp() +} diff --git a/hub/route/configs.go b/hub/route/configs.go index 48cb95ed..1a9ac8ce 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -30,6 +30,7 @@ type configSchema struct { RedirPort *int `json:"redir-port"` TProxyPort *int `json:"tproxy-port"` MixedPort *int `json:"mixed-port"` + Tun *config.Tun `json:"tun"` AllowLan *bool `json:"allow-lan"` BindAddress *string `json:"bind-address"` Mode *tunnel.TunnelMode `json:"mode"` @@ -77,6 +78,18 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn) P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn) + if general.Tun != nil { + err := P.ReCreateTun(*general.Tun, nil, nil) + if err == nil { + log.Infoln("Recreate tun success.") + } else { + log.Errorln("Recreate tun failed: %s", err.Error()) + render.Status(r, http.StatusBadRequest) + render.JSON(w, r, newError(err.Error())) + return + } + } + if general.Mode != nil { tunnel.SetMode(*general.Mode) } diff --git a/listener/listener.go b/listener/listener.go index d20af99d..5c739c7e 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -3,16 +3,20 @@ package proxy import ( "fmt" "net" + "runtime" "strconv" "sync" "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/http" "github.com/Dreamacro/clash/listener/mixed" "github.com/Dreamacro/clash/listener/redir" "github.com/Dreamacro/clash/listener/socks" "github.com/Dreamacro/clash/listener/tproxy" + "github.com/Dreamacro/clash/listener/tun" + "github.com/Dreamacro/clash/listener/tun/ipstack" "github.com/Dreamacro/clash/log" ) @@ -29,6 +33,7 @@ var ( tproxyUDPListener *tproxy.UDPListener mixedListener *mixed.Listener mixedUDPLister *socks.UDPListener + tunAdapter ipstack.TunAdapter // lock for recreate function socksMux sync.Mutex @@ -36,6 +41,7 @@ var ( redirMux sync.Mutex tproxyMux sync.Mutex mixedMux sync.Mutex + tunMux sync.Mutex ) type Ports struct { @@ -58,6 +64,18 @@ func SetAllowLan(al bool) { allowLan = al } +func Tun() config.Tun { + if tunAdapter == nil { + return config.Tun{} + } + return config.Tun{ + Enable: true, + Stack: tunAdapter.Stack(), + DNSListen: tunAdapter.DNSListen(), + AutoRoute: tunAdapter.AutoRoute(), + } +} + func SetBindAddress(host string) { bindAddress = host } @@ -275,6 +293,25 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P return nil } +func ReCreateTun(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error { + tunMux.Lock() + defer tunMux.Unlock() + + if tunAdapter != nil { + tunAdapter.Close() + tunAdapter = nil + } + + if !conf.Enable { + return nil + } + + var err error + tunAdapter, err = tun.New(conf, tcpIn, udpIn) + + return err +} + // GetPorts return the ports of proxy servers func GetPorts() *Ports { ports := &Ports{} @@ -330,3 +367,12 @@ func genAddr(host string, port int, allowLan bool) string { return fmt.Sprintf("127.0.0.1:%d", port) } + +// CleanUp clean up something +func CleanUp() { + if runtime.GOOS == "windows" { + if tunAdapter != nil { + tunAdapter.Close() + } + } +} diff --git a/listener/tproxy/tproxy_linux_iptables.go b/listener/tproxy/tproxy_linux_iptables.go new file mode 100644 index 00000000..09650837 --- /dev/null +++ b/listener/tproxy/tproxy_linux_iptables.go @@ -0,0 +1,186 @@ +package tproxy + +import ( + "errors" + "fmt" + "os/exec" + U "os/user" + "runtime" + "strings" + + "github.com/Dreamacro/clash/log" +) + +var ( + interfaceName = "" + tproxyPort = 0 + dnsPort = 0 +) + +const ( + PROXY_FWMARK = "0x2d0" + PROXY_ROUTE_TABLE = "0x2d0" + USERNAME = "clash" +) + +func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error { + var err error + if _, err = execCmd("iptables -V"); err != nil { + return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS) + } + + user, err := U.Lookup(USERNAME) + if err != nil { + return fmt.Errorf("the user \" %s\" does not exist, please create it", USERNAME) + } + + if ifname == "" { + return errors.New("the 'interface-name' can not be empty") + } + + ownerUid := user.Uid + + interfaceName = ifname + tproxyPort = tport + dnsPort = dport + + // add route + execCmd(fmt.Sprintf("ip -f inet rule add fwmark %s lookup %s", PROXY_FWMARK, PROXY_ROUTE_TABLE)) + execCmd(fmt.Sprintf("ip -f inet route add local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE)) + + // set FORWARD + execCmd("sysctl -w net.ipv4.ip_forward=1") + execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -j ACCEPT", interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName)) + + // set clash divert + execCmd("iptables -t mangle -N clash_divert") + execCmd("iptables -t mangle -F clash_divert") + execCmd(fmt.Sprintf("iptables -t mangle -A clash_divert -j MARK --set-mark %s", PROXY_FWMARK)) + execCmd("iptables -t mangle -A clash_divert -j ACCEPT") + + // set pre routing + execCmd("iptables -t mangle -N clash_prerouting") + execCmd("iptables -t mangle -F clash_prerouting") + execCmd("iptables -t mangle -A clash_prerouting -s 172.17.0.0/16 -j RETURN") + execCmd("iptables -t mangle -A clash_prerouting -p udp --dport 53 -j ACCEPT") + execCmd("iptables -t mangle -A clash_prerouting -p tcp --dport 53 -j ACCEPT") + execCmd("iptables -t mangle -A clash_prerouting -m addrtype --dst-type LOCAL -j RETURN") + addLocalnetworkToChain("clash_prerouting") + execCmd("iptables -t mangle -A clash_prerouting -p tcp -m socket -j clash_divert") + execCmd("iptables -t mangle -A clash_prerouting -p udp -m socket -j clash_divert") + execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p tcp -j TPROXY --on-port %d --tproxy-mark %s/%s", tproxyPort, PROXY_FWMARK, PROXY_FWMARK)) + execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p udp -j TPROXY --on-port %d --tproxy-mark %s/%s", tproxyPort, PROXY_FWMARK, PROXY_FWMARK)) + execCmd("iptables -t mangle -A PREROUTING -j clash_prerouting") + + execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + + // set post routing + execCmd(fmt.Sprintf("iptables -t nat -A POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName)) + + // set output + execCmd("iptables -t mangle -N clash_output") + execCmd("iptables -t mangle -F clash_output") + execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -m owner --uid-owner %s -j RETURN", ownerUid)) + execCmd("iptables -t mangle -A clash_output -p udp -m multiport --dports 53,123,137 -j ACCEPT") + execCmd("iptables -t mangle -A clash_output -p tcp --dport 53 -j ACCEPT") + execCmd("iptables -t mangle -A clash_output -m addrtype --dst-type LOCAL -j RETURN") + execCmd("iptables -t mangle -A clash_output -m addrtype --dst-type BROADCAST -j RETURN") + addLocalnetworkToChain("clash_output") + execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -p tcp -j MARK --set-mark %s", PROXY_FWMARK)) + execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -p udp -j MARK --set-mark %s", PROXY_FWMARK)) + execCmd(fmt.Sprintf("iptables -t mangle -I OUTPUT -o %s -j clash_output", interfaceName)) + + // set dns output + execCmd("iptables -t nat -N clash_dns_output") + execCmd("iptables -t nat -F clash_dns_output") + execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -m owner --uid-owner %s -j RETURN", ownerUid)) + execCmd("iptables -t nat -A clash_dns_output -s 172.17.0.0/16 -j RETURN") + execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -p udp -j REDIRECT --to-ports %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -p tcp -j REDIRECT --to-ports %d", dnsPort)) + execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j clash_dns_output") + execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j clash_dns_output") + + log.Infoln("[TProxy] Setting iptables completed") + return nil +} + +func CleanUpTProxyLinuxIPTables() { + if interfaceName == "" || tproxyPort == 0 || dnsPort == 0 { + return + } + + log.Warnln("Clean up tproxy linux iptables") + + if _, err := execCmd("iptables -t mangle -L clash_divert"); err != nil { + return + } + + // clean route + execCmd(fmt.Sprintf("ip -f inet rule del fwmark %s lookup %s", PROXY_FWMARK, PROXY_ROUTE_TABLE)) + execCmd(fmt.Sprintf("ip -f inet route del local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE)) + + // clean FORWARD + execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName)) + execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -j ACCEPT", interfaceName)) + + // clean PREROUTING + execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + execCmd("iptables -t mangle -D PREROUTING -j clash_prerouting") + + // clean POSTROUTING + execCmd(fmt.Sprintf("iptables -t nat -D POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName)) + + // clean OUTPUT + execCmd(fmt.Sprintf("iptables -t mangle -D OUTPUT -o %s -j clash_output", interfaceName)) + execCmd("iptables -t nat -D OUTPUT -p tcp --dport 53 -j clash_dns_output") + execCmd("iptables -t nat -D OUTPUT -p udp --dport 53 -j clash_dns_output") + + // clean chain + execCmd("iptables -t mangle -F clash_prerouting") + execCmd("iptables -t mangle -X clash_prerouting") + execCmd("iptables -t mangle -F clash_divert") + execCmd("iptables -t mangle -X clash_divert") + execCmd("iptables -t mangle -F clash_output") + execCmd("iptables -t mangle -X clash_output") + execCmd("iptables -t nat -F clash_dns_output") + execCmd("iptables -t nat -X clash_dns_output") +} + +func addLocalnetworkToChain(chain string) { + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 0.0.0.0/8 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 10.0.0.0/8 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 100.64.0.0/10 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 127.0.0.0/8 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 169.254.0.0/16 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 172.16.0.0/12 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.0.0.0/24 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.0.2.0/24 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.88.99.0/24 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.168.0.0/16 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 198.51.100.0/24 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 203.0.113.0/24 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 224.0.0.0/4 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 240.0.0.0/4 -j RETURN", chain)) + execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 255.255.255.255/32 -j RETURN", chain)) +} + +func execCmd(cmdstr string) (string, error) { + log.Debugln("[TProxy] %s", cmdstr) + + args := strings.Split(cmdstr, " ") + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.CombinedOutput() + if err != nil { + log.Errorln("[TProxy] error: %s, %s", err.Error(), string(out)) + return "", err + } + + return string(out), nil +} diff --git a/listener/tun/dev/dev.go b/listener/tun/dev/dev.go new file mode 100644 index 00000000..56d85f26 --- /dev/null +++ b/listener/tun/dev/dev.go @@ -0,0 +1,66 @@ +package dev + +import ( + "os/exec" + "runtime" + + "github.com/Dreamacro/clash/log" +) + +// TunDevice is cross-platform tun interface +type TunDevice interface { + Name() string + URL() string + MTU() (int, error) + IsClose() bool + Close() error + Read(buff []byte) (int, error) + Write(buff []byte) (int, error) +} + +func SetLinuxAutoRoute() { + log.Infoln("Tun adapter auto setting global route") + addLinuxSystemRoute("1") + addLinuxSystemRoute("2/7") + addLinuxSystemRoute("4/6") + addLinuxSystemRoute("8/5") + addLinuxSystemRoute("16/4") + addLinuxSystemRoute("32/3") + addLinuxSystemRoute("64/2") + addLinuxSystemRoute("128.0/1") + addLinuxSystemRoute("198.18.0/16") +} + +func RemoveLinuxAutoRoute() { + log.Infoln("Tun adapter removing global route") + delLinuxSystemRoute("1") + delLinuxSystemRoute("2/7") + delLinuxSystemRoute("4/6") + delLinuxSystemRoute("8/5") + delLinuxSystemRoute("16/4") + delLinuxSystemRoute("32/3") + delLinuxSystemRoute("64/2") + delLinuxSystemRoute("128.0/1") + delLinuxSystemRoute("198.18.0/16") +} + +func addLinuxSystemRoute(net string) { + if runtime.GOOS != "darwin" && runtime.GOOS != "linux" { + return + } + cmd := exec.Command("route", "add", "-net", net, "198.18.0.1") + if err := cmd.Run(); err != nil { + log.Errorln("[auto route] Failed to add system route: %s, cmd: %s", err.Error(), cmd.String()) + } +} + +func delLinuxSystemRoute(net string) { + if runtime.GOOS != "darwin" && runtime.GOOS != "linux" { + return + } + cmd := exec.Command("route", "delete", "-net", net, "198.18.0.1") + _ = cmd.Run() + //if err := cmd.Run(); err != nil { + // log.Errorln("[auto route]Failed to delete system route: %s, cmd: %s", err.Error(), cmd.String()) + //} +} diff --git a/listener/tun/dev/dev_darwin.go b/listener/tun/dev/dev_darwin.go new file mode 100644 index 00000000..9424c16b --- /dev/null +++ b/listener/tun/dev/dev_darwin.go @@ -0,0 +1,494 @@ +//go:build darwin +// +build darwin + +package dev + +import ( + "bytes" + "errors" + "fmt" + "net" + "os" + "os/exec" + "sync" + "syscall" + "unsafe" + + "golang.org/x/net/ipv6" + "golang.org/x/sys/unix" + + "github.com/Dreamacro/clash/common/pool" +) + +const ( + utunControlName = "com.apple.net.utun_control" + iocOut = 0x40000000 + iocIn = 0x80000000 + iocInout = iocIn | iocOut +) + +// _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h +// https://github.com/apple/darwin-xnu/blob/master/bsd/sys/ioccom.h + +// #define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) /* get id from name */ = 0xc0644e03 +const _CTLIOCGINFO = iocInout | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3 + +// #define SIOCPROTOATTACH_IN6 _IOWR('i', 110, struct in6_aliasreq_64) +const siocprotoattachIn6 = iocInout | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 110 + +// #define SIOCLL_START _IOWR('i', 130, struct in6Aliasreq) +const siocllStart = iocInout | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 130 + +// Following the wireguard-go solution: +// These unix.SYS_* constants were removed from golang.org/x/sys/unix +// so copy them here for now. +// See https://github.com/golang/go/issues/41868 +const ( + sysIoctl = 54 + sysConnect = 98 + sysGetsockopt = 118 +) + +type tunDarwin struct { + name string + tunAddress string + autoRoute bool + tunFile *os.File + errors chan error + + closed bool + stopOnce sync.Once +} + +// sockaddr_ctl specifeid in /usr/include/sys/kern_control.h +type sockaddrCtl struct { + scLen uint8 + scFamily uint8 + ssSysaddr uint16 + scID uint32 + scUnit uint32 + scReserved [5]uint32 +} + +type ctlInfo struct { + ctlID uint32 + ctlName [96]byte +} + +// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/sys/sockio.h#L107 +// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L570-L575 +// https://man.openbsd.org/netintro.4#SIOCAIFADDR +type aliasreq struct { + ifraName [unix.IFNAMSIZ]byte + ifraAddr unix.RawSockaddrInet4 + ifraDstaddr unix.RawSockaddrInet4 + ifraMask unix.RawSockaddrInet4 +} + +// SIOCAIFADDR_IN6 +// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L114-L119 +// https://opensource.apple.com/source/network_cmds/network_cmds-543.260.3/ +type in6Addrlifetime struct{} + +// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L336-L343 +// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6.h#L174-L181 +type in6Aliasreq struct { + ifraName [unix.IFNAMSIZ]byte + ifraAddr unix.RawSockaddrInet6 + ifraDstaddr unix.RawSockaddrInet6 + ifraPrefixmask unix.RawSockaddrInet6 + ifraFlags int32 + ifraLifetime in6Addrlifetime +} + +// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L402-L563 + +//type ifreqAddr struct { +// Name [unix.IFNAMSIZ]byte +// Addr unix.RawSockaddrInet4 +// Pad [8]byte +//} + +var sockaddrCtlSize uintptr = 32 + +// OpenTunDevice return a TunDevice according a URL +func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) { + name := "utun" + mtu := 9000 + + ifIndex := -1 + if name != "utun" { + _, err := fmt.Sscanf(name, "utun%d", &ifIndex) + if err != nil || ifIndex < 0 { + return nil, fmt.Errorf("interface name must be utun[0-9]*") + } + } + + fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2) + if err != nil { + return nil, err + } + + ctlInfo1 := &ctlInfo{} + + copy(ctlInfo1.ctlName[:], []byte(utunControlName)) + + _, _, errno := unix.Syscall( + sysIoctl, + uintptr(fd), + uintptr(_CTLIOCGINFO), + uintptr(unsafe.Pointer(ctlInfo1)), + ) + + if errno != 0 { + return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno) + } + + sc := sockaddrCtl{ + scLen: uint8(sockaddrCtlSize), + scFamily: unix.AF_SYSTEM, + ssSysaddr: 2, + scID: ctlInfo1.ctlID, + scUnit: uint32(ifIndex) + 1, + } + + scPointer := unsafe.Pointer(&sc) + + _, _, errno = unix.RawSyscall( + sysConnect, + uintptr(fd), + uintptr(scPointer), + uintptr(sockaddrCtlSize), + ) + + if errno != 0 { + return nil, fmt.Errorf("SYS_CONNECT: %v", errno) + } + + err = syscall.SetNonblock(fd, true) + if err != nil { + return nil, err + } + + tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu, tunAddress, autoRoute) + if err != nil { + return nil, err + } + + if autoRoute { + SetLinuxAutoRoute() + } + + return tun, nil +} + +func CreateTUNFromFile(file *os.File, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) { + tun := &tunDarwin{ + tunFile: file, + tunAddress: tunAddress, + autoRoute: autoRoute, + errors: make(chan error, 5), + } + + name, err := tun.getName() + if err != nil { + tun.tunFile.Close() + return nil, err + } + tun.name = name + + if err != nil { + tun.tunFile.Close() + return nil, err + } + + if mtu > 0 { + err = tun.setMTU(mtu) + if err != nil { + tun.Close() + return nil, err + } + } + + // This address doesn't mean anything here. NIC just net an IP address to set route upon. + p2pAddress := net.ParseIP(tunAddress) + err = tun.setTunAddress(p2pAddress) + if err != nil { + tun.Close() + return nil, err + } + err = tun.attachLinkLocal() + if err != nil { + tun.Close() + return nil, err + } + + return tun, nil +} + +func (t *tunDarwin) Name() string { + return t.name +} + +func (t *tunDarwin) URL() string { + return fmt.Sprintf("dev://%s", t.Name()) +} + +func (t *tunDarwin) MTU() (int, error) { + return t.getInterfaceMtu() +} + +func (t *tunDarwin) Read(buff []byte) (int, error) { + select { + case err := <-t.errors: + return 0, err + default: + n, err := t.tunFile.Read(buff) + if n < 4 { + return 0, err + } + + copy(buff[:], buff[4:]) + return n - 4, err + } +} + +func (t *tunDarwin) Write(buff []byte) (int, error) { + // reserve space for header + buf := pool.Get(pool.RelayBufferSize) + defer pool.Put(buf[:cap(buf)]) + + buf[0] = 0x00 + buf[1] = 0x00 + buf[2] = 0x00 + + copy(buf[4:], buff) + if buf[4]>>4 == ipv6.Version { + buf[3] = unix.AF_INET6 + } else { + buf[3] = unix.AF_INET + } + + // write + return t.tunFile.Write(buf[:4+len(buff)]) +} + +func (t *tunDarwin) IsClose() bool { + return t.closed +} + +func (t *tunDarwin) Close() error { + t.stopOnce.Do(func() { + if t.autoRoute { + RemoveLinuxAutoRoute() + } + t.closed = true + t.tunFile.Close() + }) + return nil +} + +func (t *tunDarwin) getInterfaceMtu() (int, error) { + // open datagram socket + + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + if err != nil { + return 0, err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [64]byte + copy(ifr[:], t.name) + _, _, errno := unix.Syscall( + sysIoctl, + uintptr(fd), + uintptr(unix.SIOCGIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + if errno != 0 { + return 0, fmt.Errorf("failed to get MTU on %s", t.name) + } + + return int(*(*int32)(unsafe.Pointer(&ifr[16]))), nil +} + +func (t *tunDarwin) getName() (string, error) { + var ifName struct { + name [16]byte + } + ifNameSize := uintptr(16) + + var errno syscall.Errno + t.operateOnFd(func(fd uintptr) { + _, _, errno = unix.Syscall6( + sysGetsockopt, + fd, + 2, /* #define SYSPROTO_CONTROL 2 */ + 2, /* #define UTUN_OPT_IFNAME 2 */ + uintptr(unsafe.Pointer(&ifName)), + uintptr(unsafe.Pointer(&ifNameSize)), 0) + }) + + if errno != 0 { + return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno) + } + + t.name = string(ifName.name[:ifNameSize-1]) + return t.name, nil +} + +func (t *tunDarwin) setMTU(n int) error { + // open datagram socket + fd, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + if err != nil { + return err + } + + defer unix.Close(fd) + + // do ioctl call + + var ifr [32]byte + copy(ifr[:], t.name) + *(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n) + _, _, errno := unix.Syscall( + sysIoctl, + uintptr(fd), + uintptr(unix.SIOCSIFMTU), + uintptr(unsafe.Pointer(&ifr[0])), + ) + + if errno != 0 { + return fmt.Errorf("failed to set MTU on %s", t.name) + } + + return nil +} + +func (t *tunDarwin) operateOnFd(fn func(fd uintptr)) { + sysconn, err := t.tunFile.SyscallConn() + // TODO: consume the errors + if err != nil { + t.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error()) + return + } + err = sysconn.Control(fn) + if err != nil { + t.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error()) + } +} + +func (t *tunDarwin) setTunAddress(addr net.IP) error { + var ifr [unix.IFNAMSIZ]byte + copy(ifr[:], t.name) + + // set IPv4 address + fd4, err := unix.Socket( + unix.AF_INET, + unix.SOCK_DGRAM, + 0, + ) + if err != nil { + return err + } + defer syscall.Close(fd4) + + var ip4 [4]byte + copy(ip4[:], addr.To4()) + ip4mask := [4]byte{255, 255, 0, 0} + ifra4 := aliasreq{ + ifraName: ifr, + ifraAddr: unix.RawSockaddrInet4{ + Len: unix.SizeofSockaddrInet4, + Family: unix.AF_INET, + Addr: ip4, + }, + ifraDstaddr: unix.RawSockaddrInet4{ + Len: unix.SizeofSockaddrInet4, + Family: unix.AF_INET, + Addr: ip4, + }, + ifraMask: unix.RawSockaddrInet4{ + Len: unix.SizeofSockaddrInet4, + Family: unix.AF_INET, + Addr: ip4mask, + }, + } + + if _, _, errno := unix.Syscall( + sysIoctl, + uintptr(fd4), + uintptr(unix.SIOCAIFADDR), + uintptr(unsafe.Pointer(&ifra4)), + ); errno != 0 { + return fmt.Errorf("failed to set ip address on %s: %v", t.name, errno) + } + + return nil +} + +func (t *tunDarwin) attachLinkLocal() error { + var ifr [unix.IFNAMSIZ]byte + copy(ifr[:], t.name) + + // attach link-local address + fd6, err := unix.Socket( + unix.AF_INET6, + unix.SOCK_DGRAM, + 0, + ) + if err != nil { + return err + } + defer syscall.Close(fd6) + + // Attach link-local address + ifra6 := in6Aliasreq{ + ifraName: ifr, + } + if _, _, errno := unix.Syscall( + sysIoctl, + uintptr(fd6), + uintptr(siocprotoattachIn6), + uintptr(unsafe.Pointer(&ifra6)), + ); errno != 0 { + return fmt.Errorf("failed to attach link-local address on %s: SIOCPROTOATTACH_IN6 %v", t.name, errno) + } + + if _, _, errno := unix.Syscall( + sysIoctl, + uintptr(fd6), + uintptr(siocllStart), + uintptr(unsafe.Pointer(&ifra6)), + ); errno != 0 { + return fmt.Errorf("failed to set ipv6 address on %s: SIOCLL_START %v", t.name, errno) + } + + return nil +} + +// GetAutoDetectInterface get ethernet interface +func GetAutoDetectInterface() (string, error) { + cmd := exec.Command("bash", "-c", "netstat -rnf inet | grep 'default' | awk -F ' ' 'NR==1{print $6}' | xargs echo -n") + var out bytes.Buffer + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + return "", err + } + if out.Len() == 0 { + return "", errors.New("interface not found by default route") + } + return out.String(), nil +} diff --git a/listener/tun/dev/dev_linux.go b/listener/tun/dev/dev_linux.go new file mode 100644 index 00000000..f949d432 --- /dev/null +++ b/listener/tun/dev/dev_linux.go @@ -0,0 +1,255 @@ +//go:build linux || android +// +build linux android + +package dev + +import ( + "bytes" + "errors" + "fmt" + "net/url" + "os" + "os/exec" + "strconv" + "sync" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + cloneDevicePath = "/dev/net/tun" + ifReqSize = unix.IFNAMSIZ + 64 +) + +type tunLinux struct { + url string + name string + tunAddress string + autoRoute bool + tunFile *os.File + mtu int + + closed bool + stopOnce sync.Once +} + +// OpenTunDevice return a TunDevice according a URL +func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) { + deviceURL, _ := url.Parse("dev://clash0") + mtu, _ := strconv.ParseInt(deviceURL.Query().Get("mtu"), 0, 32) + + t := &tunLinux{ + url: deviceURL.String(), + mtu: int(mtu), + tunAddress: tunAddress, + autoRoute: autoRoute, + } + switch deviceURL.Scheme { + case "dev": + var err error + var dev TunDevice + dev, err = t.openDeviceByName(deviceURL.Host) + if err != nil { + return nil, err + } + if autoRoute { + SetLinuxAutoRoute() + } + return dev, nil + case "fd": + fd, err := strconv.ParseInt(deviceURL.Host, 10, 32) + if err != nil { + return nil, err + } + var dev TunDevice + dev, err = t.openDeviceByFd(int(fd)) + if err != nil { + return nil, err + } + if autoRoute { + SetLinuxAutoRoute() + } + return dev, nil + } + return nil, fmt.Errorf("unsupported device type `%s`", deviceURL.Scheme) +} + +func (t *tunLinux) Name() string { + return t.name +} + +func (t *tunLinux) URL() string { + return t.url +} + +func (t *tunLinux) Write(buff []byte) (int, error) { + return t.tunFile.Write(buff) +} + +func (t *tunLinux) Read(buff []byte) (int, error) { + return t.tunFile.Read(buff) +} + +func (t *tunLinux) IsClose() bool { + return t.closed +} + +func (t *tunLinux) Close() error { + t.stopOnce.Do(func() { + if t.autoRoute { + RemoveLinuxAutoRoute() + } + t.closed = true + t.tunFile.Close() + }) + return nil +} + +func (t *tunLinux) MTU() (int, error) { + // Sometime, we can't read MTU by SIOCGIFMTU. Then we should return the preset MTU + if t.mtu > 0 { + return t.mtu, nil + } + mtu, err := t.getInterfaceMtu() + return int(mtu), err +} + +func (t *tunLinux) openDeviceByName(name string) (TunDevice, error) { + nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0) + if err != nil { + return nil, err + } + + var ifr [ifReqSize]byte + var flags uint16 = unix.IFF_TUN | unix.IFF_NO_PI + nameBytes := []byte(name) + if len(nameBytes) >= unix.IFNAMSIZ { + return nil, errors.New("interface name too long") + } + copy(ifr[:], nameBytes) + *(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags + + _, _, errno := unix.Syscall( + unix.SYS_IOCTL, + uintptr(nfd), + uintptr(unix.TUNSETIFF), + uintptr(unsafe.Pointer(&ifr[0])), + ) + if errno != 0 { + return nil, errno + } + err = unix.SetNonblock(nfd, true) + if err != nil { + return nil, err + } + + // Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line. + + t.tunFile = os.NewFile(uintptr(nfd), cloneDevicePath) + t.name, err = t.getName() + if err != nil { + t.tunFile.Close() + return nil, err + } + + return t, nil +} + +func (t *tunLinux) openDeviceByFd(fd int) (TunDevice, error) { + var ifr struct { + name [16]byte + flags uint16 + _ [22]byte + } + + fd, err := syscall.Dup(fd) + if err != nil { + return nil, err + } + + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TUNGETIFF, uintptr(unsafe.Pointer(&ifr))) + if errno != 0 { + return nil, errno + } + + if ifr.flags&syscall.IFF_TUN == 0 || ifr.flags&syscall.IFF_NO_PI == 0 { + return nil, errors.New("only tun device and no pi mode supported") + } + + nullStr := ifr.name[:] + i := bytes.IndexByte(nullStr, 0) + if i != -1 { + nullStr = nullStr[:i] + } + t.name = string(nullStr) + t.tunFile = os.NewFile(uintptr(fd), "/dev/tun") + + return t, nil +} + +func (t *tunLinux) getInterfaceMtu() (uint32, error) { + fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0) + if err != nil { + return 0, err + } + + defer syscall.Close(fd) + + var ifreq struct { + name [16]byte + mtu int32 + _ [20]byte + } + + copy(ifreq.name[:], t.name) + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq))) + if errno != 0 { + return 0, errno + } + + return uint32(ifreq.mtu), nil +} + +func (t *tunLinux) getName() (string, error) { + sysconn, err := t.tunFile.SyscallConn() + if err != nil { + return "", err + } + var ifr [ifReqSize]byte + var errno syscall.Errno + err = sysconn.Control(func(fd uintptr) { + _, _, errno = unix.Syscall( + unix.SYS_IOCTL, + fd, + uintptr(unix.TUNGETIFF), + uintptr(unsafe.Pointer(&ifr[0])), + ) + }) + if err != nil { + return "", errors.New("failed to get name of TUN device: " + err.Error()) + } + if errno != 0 { + return "", errors.New("failed to get name of TUN device: " + errno.Error()) + } + nullStr := ifr[:] + i := bytes.IndexByte(nullStr, 0) + if i != -1 { + nullStr = nullStr[:i] + } + t.name = string(nullStr) + return t.name, nil +} + +// GetAutoDetectInterface get ethernet interface +func GetAutoDetectInterface() (string, error) { + cmd := exec.Command("bash", "-c", "ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n") + var out bytes.Buffer + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + return "", err + } + return out.String(), nil +} diff --git a/listener/tun/dev/dev_unsupport.go b/listener/tun/dev/dev_unsupport.go new file mode 100644 index 00000000..00fd242b --- /dev/null +++ b/listener/tun/dev/dev_unsupport.go @@ -0,0 +1,18 @@ +//go:build !linux && !android && !darwin && !windows +// +build !linux,!android,!darwin,!windows + +package dev + +import ( + "errors" + "runtime" +) + +func OpenTunDevice(tunAddress string, autoRute bool) (TunDevice, error) { + return nil, errors.New("Unsupported platform " + runtime.GOOS + "/" + runtime.GOARCH) +} + +// GetAutoDetectInterface get ethernet interface +func GetAutoDetectInterface() (string, error) { + return "", nil +} diff --git a/listener/tun/dev/dev_windows.go b/listener/tun/dev/dev_windows.go new file mode 100644 index 00000000..e3cc5cc6 --- /dev/null +++ b/listener/tun/dev/dev_windows.go @@ -0,0 +1,162 @@ +//go:build windows +// +build windows + +// Modified from: https://git.zx2c4.com/wireguard-go/tree/tun/tun_windows.go and https://git.zx2c4.com/wireguard-windows/tree/tunnel/addressconfig.go +// SPDX-License-Identifier: MIT + +package dev + +import ( + "errors" + "fmt" + "os" + "sync/atomic" + "time" + _ "unsafe" + + "github.com/Dreamacro/clash/listener/tun/dev/wintun" + "golang.org/x/sys/windows" +) + +const ( + rateMeasurementGranularity = uint64((time.Second / 2) / time.Nanosecond) + spinloopRateThreshold = 800000000 / 8 // 800mbps + spinloopDuration = uint64(time.Millisecond / 80 / time.Nanosecond) // ~1gbit/s +) + +type rateJuggler struct { + current uint64 + nextByteCount uint64 + nextStartTime int64 + changing int32 +} + +var WintunTunnelType = "Clash" +var WintunStaticRequestedGUID *windows.GUID + +//go:linkname procyield runtime.procyield +func procyield(cycles uint32) + +//go:linkname nanotime runtime.nanotime +func nanotime() int64 + +func (tun *tunWindows) Close() error { + var err error + tun.closeOnce.Do(func() { + atomic.StoreInt32(&tun.close, 1) + windows.SetEvent(tun.readWait) + //tun.running.Wait() + tun.session.End() + if tun.wt != nil { + err = tun.wt.Close() + } + }) + return err +} + +func (tun *tunWindows) MTU() (int, error) { + return tun.forcedMTU, nil +} + +// TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes. +func (tun *tunWindows) ForceMTU(mtu int) { + tun.forcedMTU = mtu +} + +// Note: Read() and Write() assume the caller comes only from a single thread; there's no locking. + +func (tun *tunWindows) Read0(buff []byte, offset int) (int, error) { + tun.running.Add(1) + defer tun.running.Done() +retry: + if atomic.LoadInt32(&tun.close) == 1 { + return 0, os.ErrClosed + } + start := nanotime() + shouldSpin := atomic.LoadUint64(&tun.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&tun.rate.nextStartTime)) <= rateMeasurementGranularity*2 + for { + if atomic.LoadInt32(&tun.close) == 1 { + return 0, os.ErrClosed + } + packet, err := tun.session.ReceivePacket() + switch err { + case nil: + packetSize := len(packet) + copy(buff[offset:], packet) + tun.session.ReleaseReceivePacket(packet) + tun.rate.update(uint64(packetSize)) + return packetSize, nil + case windows.ERROR_NO_MORE_ITEMS: + if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration { + windows.WaitForSingleObject(tun.readWait, windows.INFINITE) + goto retry + } + procyield(1) + continue + case windows.ERROR_HANDLE_EOF: + return 0, os.ErrClosed + case windows.ERROR_INVALID_DATA: + return 0, errors.New("Send ring corrupt") + } + return 0, fmt.Errorf("Read failed: %w", err) + } +} + +func (tun *tunWindows) Flush() error { + return nil +} + +func (tun *tunWindows) Write0(buff []byte, offset int) (int, error) { + tun.running.Add(1) + defer tun.running.Done() + if atomic.LoadInt32(&tun.close) == 1 { + return 0, os.ErrClosed + } + + packetSize := len(buff) - offset + tun.rate.update(uint64(packetSize)) + + packet, err := tun.session.AllocateSendPacket(packetSize) + if err == nil { + copy(packet, buff[offset:]) + tun.session.SendPacket(packet) + return packetSize, nil + } + switch err { + case windows.ERROR_HANDLE_EOF: + return 0, os.ErrClosed + case windows.ERROR_BUFFER_OVERFLOW: + return 0, nil // Dropping when ring is full. + } + return 0, fmt.Errorf("Write failed: %w", err) +} + +// LUID returns Windows interface instance ID. +func (tun *tunWindows) LUID() uint64 { + tun.running.Add(1) + defer tun.running.Done() + if atomic.LoadInt32(&tun.close) == 1 { + return 0 + } + return tun.wt.LUID() +} + +// RunningVersion returns the running version of the Wintun driver. +func (tun *tunWindows) RunningVersion() (version uint32, err error) { + return wintun.RunningVersion() +} + +func (rate *rateJuggler) update(packetLen uint64) { + now := nanotime() + total := atomic.AddUint64(&rate.nextByteCount, packetLen) + period := uint64(now - atomic.LoadInt64(&rate.nextStartTime)) + if period >= rateMeasurementGranularity { + if !atomic.CompareAndSwapInt32(&rate.changing, 0, 1) { + return + } + atomic.StoreInt64(&rate.nextStartTime, now) + atomic.StoreUint64(&rate.current, total*uint64(time.Second/time.Nanosecond)/period) + atomic.StoreUint64(&rate.nextByteCount, 0) + atomic.StoreInt32(&rate.changing, 0) + } +} diff --git a/listener/tun/dev/dev_windows_extra.go b/listener/tun/dev/dev_windows_extra.go new file mode 100644 index 00000000..12899e0c --- /dev/null +++ b/listener/tun/dev/dev_windows_extra.go @@ -0,0 +1,398 @@ +//go:build windows +// +build windows + +package dev + +import ( + "bytes" + "errors" + "fmt" + "net" + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/Dreamacro/clash/listener/tun/dev/wintun" + "github.com/Dreamacro/clash/log" + + "golang.org/x/sys/windows" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" +) + +const messageTransportHeaderSize = 0 // size of data preceding content in transport message + +type tunWindows struct { + wt *wintun.Adapter + handle windows.Handle + close int32 + running sync.WaitGroup + forcedMTU int + rate rateJuggler + session wintun.Session + readWait windows.Handle + closeOnce sync.Once + + url string + name string + tunAddress string + autoRoute bool +} + +// OpenTunDevice return a TunDevice according a URL +func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) { + + requestedGUID, err := windows.GUIDFromString("{330EAEF8-7578-5DF2-D97B-8DADC0EA85CB}") + if err == nil { + WintunStaticRequestedGUID = &requestedGUID + log.Debugln("Generate GUID: %s", WintunStaticRequestedGUID.String()) + } else { + log.Warnln("Error parese GUID from string: %v", err) + } + + interfaceName := "Clash" + mtu := 9000 + + tun, err := CreateTUN(interfaceName, mtu, tunAddress, autoRoute) + if err != nil { + return nil, err + } + + return tun, nil +} + +// +// CreateTUN creates a Wintun interface with the given name. Should a Wintun +// interface with the same name exist, it is reused. +// +func CreateTUN(ifname string, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) { + return CreateTUNWithRequestedGUID(ifname, WintunStaticRequestedGUID, mtu, tunAddress, autoRoute) +} + +// +// CreateTUNWithRequestedGUID creates a Wintun interface with the given name and +// a requested GUID. Should a Wintun interface with the same name exist, it is reused. +// +func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) { + wt, err := wintun.CreateAdapter(ifname, WintunTunnelType, requestedGUID) + if err != nil { + return nil, fmt.Errorf("Error creating interface: %w", err) + } + + forcedMTU := 1420 + if mtu > 0 { + forcedMTU = mtu + } + + tun := &tunWindows{ + name: ifname, + wt: wt, + handle: windows.InvalidHandle, + forcedMTU: forcedMTU, + tunAddress: tunAddress, + autoRoute: autoRoute, + } + + // config tun ip + err = tun.configureInterface() + if err != nil { + tun.wt.Close() + return nil, fmt.Errorf("error configure interface: %w", err) + } + + tun.session, err = wt.StartSession(0x800000) // Ring capacity, 8 MiB + if err != nil { + tun.wt.Close() + return nil, fmt.Errorf("error starting session: %w", err) + } + tun.readWait = tun.session.ReadWaitEvent() + return tun, nil +} + +func (tun *tunWindows) Name() string { + return tun.name +} + +func (tun *tunWindows) IsClose() bool { + return atomic.LoadInt32(&tun.close) == 1 +} + +func (tun *tunWindows) Read(buff []byte) (int, error) { + return tun.Read0(buff, messageTransportHeaderSize) +} + +func (tun *tunWindows) Write(buff []byte) (int, error) { + return tun.Write0(buff, messageTransportHeaderSize) +} + +func (tun *tunWindows) URL() string { + return fmt.Sprintf("dev://%s", tun.Name()) +} + +func (tun *tunWindows) configureInterface() error { + retryOnFailure := wintun.StartedAtBoot() + tryTimes := 0 +startOver: + var err error + if tryTimes > 0 { + log.Infoln("Retrying interface configuration after failure because system just booted (T+%v): %v", windows.DurationSinceBoot(), err) + time.Sleep(time.Second) + retryOnFailure = retryOnFailure && tryTimes < 15 + } + tryTimes++ + + luid := winipcfg.LUID(tun.LUID()) + log.Infoln("[wintun]: tun adapter LUID: %d", luid) + mtu, err := tun.MTU() + + if err != nil { + return errors.New("unable to get device mtu") + } + + family := winipcfg.AddressFamily(windows.AF_INET) + familyV6 := winipcfg.AddressFamily(windows.AF_INET6) + + tunAddress := wintun.ParseIPCidr(tun.tunAddress + "/16") + + addresses := []net.IPNet{tunAddress.IPNet()} + + err = luid.FlushIPAddresses(familyV6) + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return err + } + err = luid.FlushDNS(family) + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return err + } + err = luid.FlushDNS(familyV6) + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return err + } + err = luid.FlushRoutes(familyV6) + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return err + } + + foundDefault4 := false + foundDefault6 := false + + if tun.autoRoute { + allowedIPs := []*wintun.IPCidr{ + //wintun.ParseIPCidr("0.0.0.0/0"), + wintun.ParseIPCidr("1.0.0.0/8"), + wintun.ParseIPCidr("2.0.0.0/7"), + wintun.ParseIPCidr("4.0.0.0/6"), + wintun.ParseIPCidr("8.0.0.0/5"), + wintun.ParseIPCidr("16.0.0.0/4"), + wintun.ParseIPCidr("32.0.0.0/3"), + wintun.ParseIPCidr("64.0.0.0/2"), + wintun.ParseIPCidr("128.0.0.0/1"), + wintun.ParseIPCidr("224.0.0.0/4"), + wintun.ParseIPCidr("255.255.255.255/32"), + } + + estimatedRouteCount := len(allowedIPs) + routes := make([]winipcfg.RouteData, 0, estimatedRouteCount) + var haveV4Address, haveV6Address bool = true, false + + for _, allowedip := range allowedIPs { + allowedip.MaskSelf() + if (allowedip.Bits() == 32 && !haveV4Address) || (allowedip.Bits() == 128 && !haveV6Address) { + continue + } + route := winipcfg.RouteData{ + Destination: allowedip.IPNet(), + Metric: 0, + } + if allowedip.Bits() == 32 { + if allowedip.Cidr == 0 { + foundDefault4 = true + } + route.NextHop = net.IPv4zero + } else if allowedip.Bits() == 128 { + if allowedip.Cidr == 0 { + foundDefault6 = true + } + route.NextHop = net.IPv6zero + } + routes = append(routes, route) + } + + deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes)) + sort.Slice(routes, func(i, j int) bool { + if routes[i].Metric != routes[j].Metric { + return routes[i].Metric < routes[j].Metric + } + if c := bytes.Compare(routes[i].NextHop, routes[j].NextHop); c != 0 { + return c < 0 + } + if c := bytes.Compare(routes[i].Destination.IP, routes[j].Destination.IP); c != 0 { + return c < 0 + } + if c := bytes.Compare(routes[i].Destination.Mask, routes[j].Destination.Mask); c != 0 { + return c < 0 + } + return false + }) + for i := 0; i < len(routes); i++ { + if i > 0 && routes[i].Metric == routes[i-1].Metric && + bytes.Equal(routes[i].NextHop, routes[i-1].NextHop) && + bytes.Equal(routes[i].Destination.IP, routes[i-1].Destination.IP) && + bytes.Equal(routes[i].Destination.Mask, routes[i-1].Destination.Mask) { + continue + } + deduplicatedRoutes = append(deduplicatedRoutes, &routes[i]) + } + + err = luid.SetRoutesForFamily(family, deduplicatedRoutes) + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return fmt.Errorf("unable to set routes: %w", err) + } + } + + err = luid.SetIPAddressesForFamily(family, addresses) + if err == windows.ERROR_OBJECT_ALREADY_EXISTS { + cleanupAddressesOnDisconnectedInterfaces(family, addresses) + err = luid.SetIPAddressesForFamily(family, addresses) + } + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return fmt.Errorf("unable to set ips: %w", err) + } + + var ipif *winipcfg.MibIPInterfaceRow + ipif, err = luid.IPInterface(family) + if err != nil { + return err + } + ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled + ipif.DadTransmits = 0 + ipif.ManagedAddressConfigurationSupported = false + ipif.OtherStatefulConfigurationSupported = false + if mtu > 0 { + ipif.NLMTU = uint32(mtu) + } + if (family == windows.AF_INET && foundDefault4) || (family == windows.AF_INET6 && foundDefault6) { + ipif.UseAutomaticMetric = false + ipif.Metric = 0 + } + err = ipif.Set() + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return fmt.Errorf("unable to set metric and MTU: %w", err) + } + + var ipif6 *winipcfg.MibIPInterfaceRow + ipif6, err = luid.IPInterface(familyV6) + if err != nil { + return err + } + ipif6.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled + ipif6.DadTransmits = 0 + ipif6.ManagedAddressConfigurationSupported = false + ipif6.OtherStatefulConfigurationSupported = false + + err = ipif6.Set() + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return fmt.Errorf("unable to set v6 metric and MTU: %w", err) + } + + err = luid.SetDNS(family, []net.IP{net.ParseIP("198.18.0.2")}, nil) + if err == windows.ERROR_NOT_FOUND && retryOnFailure { + goto startOver + } else if err != nil { + return fmt.Errorf("unable to set DNS %s %s: %w", "198.18.0.2", "nil", err) + } + return nil +} + +func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []net.IPNet) { + if len(addresses) == 0 { + return + } + addrToStr := func(ip *net.IP) string { + if ip4 := ip.To4(); ip4 != nil { + return string(ip4) + } + return string(*ip) + } + addrHash := make(map[string]bool, len(addresses)) + for i := range addresses { + addrHash[addrToStr(&addresses[i].IP)] = true + } + interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagDefault) + if err != nil { + return + } + for _, iface := range interfaces { + if iface.OperStatus == winipcfg.IfOperStatusUp { + continue + } + for address := iface.FirstUnicastAddress; address != nil; address = address.Next { + ip := address.Address.IP() + if addrHash[addrToStr(&ip)] { + ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))} + log.Infoln("Cleaning up stale address %s from interface ‘%s’", ipnet.String(), iface.FriendlyName()) + iface.LUID.DeleteIPAddress(ipnet) + } + } + } +} + +// GetAutoDetectInterface get ethernet interface +func GetAutoDetectInterface() (string, error) { + ifname, err := getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET)) + if err == nil { + return ifname, err + } + + return getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET6)) +} + +func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, error) { + interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagIncludeGateways) + if err != nil { + return "", fmt.Errorf("get ethernet interface failure. %w", err) + } + for _, iface := range interfaces { + if iface.OperStatus != winipcfg.IfOperStatusUp { + continue + } + + ifname := iface.FriendlyName() + if ifname == "Clash" { + continue + } + + for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next { + nextHop := gatewayAddress.Address.IP() + + var ipnet net.IPNet + if family == windows.AF_INET { + ipnet = net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)} + } else { + ipnet = net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)} + } + + if _, err = iface.LUID.Route(ipnet, nextHop); err == nil { + return ifname, nil + } + } + } + + return "", errors.New("ethernet interface not found") +} diff --git a/listener/tun/dev/wintun/boot.go b/listener/tun/dev/wintun/boot.go new file mode 100644 index 00000000..d66d3cdc --- /dev/null +++ b/listener/tun/dev/wintun/boot.go @@ -0,0 +1,41 @@ +//go:build windows +// +build windows + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +package wintun + +import ( + "errors" + "log" + "sync" + "time" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" +) + +var ( + startedAtBoot bool + startedAtBootOnce sync.Once +) + +func StartedAtBoot() bool { + startedAtBootOnce.Do(func() { + if isService, err := svc.IsWindowsService(); err == nil && !isService { + return + } + if reason, err := svc.DynamicStartReason(); err == nil { + startedAtBoot = (reason&svc.StartReasonAuto) != 0 || (reason&svc.StartReasonDelayedAuto) != 0 + } else if errors.Is(err, windows.ERROR_PROC_NOT_FOUND) { + // TODO: Below this line is Windows 7 compatibility code, which hopefully we can delete at some point. + startedAtBoot = windows.DurationSinceBoot() < time.Minute*10 + } else { + log.Printf("Unable to determine service start reason: %v", err) + } + }) + return startedAtBoot +} diff --git a/listener/tun/dev/wintun/config.go b/listener/tun/dev/wintun/config.go new file mode 100644 index 00000000..a0eceeb1 --- /dev/null +++ b/listener/tun/dev/wintun/config.go @@ -0,0 +1,57 @@ +//go:build windows +// +build windows + +package wintun + +import ( + "fmt" + "net" + "strconv" + "strings" +) + +type IPCidr struct { + IP net.IP + Cidr uint8 +} + +func (r *IPCidr) String() string { + return fmt.Sprintf("%s/%d", r.IP.String(), r.Cidr) +} + +func (r *IPCidr) Bits() uint8 { + if r.IP.To4() != nil { + return 32 + } + return 128 +} + +func (r *IPCidr) IPNet() net.IPNet { + return net.IPNet{ + IP: r.IP, + Mask: net.CIDRMask(int(r.Cidr), int(r.Bits())), + } +} + +func (r *IPCidr) MaskSelf() { + bits := int(r.Bits()) + mask := net.CIDRMask(int(r.Cidr), bits) + for i := 0; i < bits/8; i++ { + r.IP[i] &= mask[i] + } +} + +func ParseIPCidr(ipcidr string) *IPCidr { + s := strings.Split(ipcidr, "/") + if len(s) != 2 { + return nil + } + cidr, err := strconv.Atoi(s[1]) + if err != nil { + return nil + } + return &IPCidr{ + IP: net.ParseIP(s[0]), + Cidr: uint8(cidr), + } +} diff --git a/listener/tun/dev/wintun/dll_windows.go b/listener/tun/dev/wintun/dll_windows.go new file mode 100644 index 00000000..d9989c1f --- /dev/null +++ b/listener/tun/dev/wintun/dll_windows.go @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. + */ + +package wintun + +import ( + "fmt" + "sync" + "sync/atomic" + "unsafe" + + C "github.com/Dreamacro/clash/constant" + "golang.org/x/sys/windows" +) + +func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL { + return &lazyDLL{Name: name, onLoad: onLoad} +} + +func (d *lazyDLL) NewProc(name string) *lazyProc { + return &lazyProc{dll: d, Name: name} +} + +type lazyProc struct { + Name string + mu sync.Mutex + dll *lazyDLL + addr uintptr +} + +func (p *lazyProc) Find() error { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil { + return nil + } + p.mu.Lock() + defer p.mu.Unlock() + if p.addr != 0 { + return nil + } + + err := p.dll.Load() + if err != nil { + return fmt.Errorf("Error loading %v DLL: %w", p.dll.Name, err) + } + addr, err := p.nameToAddr() + if err != nil { + return fmt.Errorf("Error getting %v address: %w", p.Name, err) + } + + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr)) + return nil +} + +func (p *lazyProc) Addr() uintptr { + err := p.Find() + if err != nil { + panic(err) + } + return p.addr +} + +type lazyDLL struct { + Name string + mu sync.Mutex + module windows.Handle + onLoad func(d *lazyDLL) +} + +func (d *lazyDLL) Load() error { + if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil { + return nil + } + d.mu.Lock() + defer d.mu.Unlock() + if d.module != 0 { + return nil + } + + //const ( + // LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200 + // LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 + //) + //module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32) + module, err := windows.LoadLibraryEx(C.Path.GetAssetLocation(d.Name), 0, windows.LOAD_WITH_ALTERED_SEARCH_PATH) + if err != nil { + return fmt.Errorf("Unable to load library: %w", err) + } + + atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module)) + if d.onLoad != nil { + d.onLoad(d) + } + return nil +} + +func (p *lazyProc) nameToAddr() (uintptr, error) { + return windows.GetProcAddress(p.dll.module, p.Name) +} + +// Version returns the version of the Wintun DLL. +func Version() string { + if modwintun.Load() != nil { + return "unknown" + } + resInfo, err := windows.FindResource(modwintun.module, windows.ResourceID(1), windows.RT_VERSION) + if err != nil { + return "unknown" + } + data, err := windows.LoadResourceData(modwintun.module, resInfo) + if err != nil { + return "unknown" + } + + var fixedInfo *windows.VS_FIXEDFILEINFO + fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo)) + err = windows.VerQueryValue(unsafe.Pointer(&data[0]), `\`, unsafe.Pointer(&fixedInfo), &fixedInfoLen) + if err != nil { + return "unknown" + } + version := fmt.Sprintf("%d.%d", (fixedInfo.FileVersionMS>>16)&0xff, (fixedInfo.FileVersionMS>>0)&0xff) + if nextNibble := (fixedInfo.FileVersionLS >> 16) & 0xff; nextNibble != 0 { + version += fmt.Sprintf(".%d", nextNibble) + } + if nextNibble := (fixedInfo.FileVersionLS >> 0) & 0xff; nextNibble != 0 { + version += fmt.Sprintf(".%d", nextNibble) + } + return version +} diff --git a/listener/tun/dev/wintun/package_info.go b/listener/tun/dev/wintun/package_info.go new file mode 100644 index 00000000..22e9bd05 --- /dev/null +++ b/listener/tun/dev/wintun/package_info.go @@ -0,0 +1,11 @@ +//go:build windows +// +build windows + +// Modified from: https://git.zx2c4.com/wireguard-go/tree/tun/wintun + +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +package wintun diff --git a/listener/tun/dev/wintun/session_windows.go b/listener/tun/dev/wintun/session_windows.go new file mode 100644 index 00000000..f023baf7 --- /dev/null +++ b/listener/tun/dev/wintun/session_windows.go @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. + */ + +package wintun + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +type Session struct { + handle uintptr +} + +const ( + PacketSizeMax = 0xffff // Maximum packet size + RingCapacityMin = 0x20000 // Minimum ring capacity (128 kiB) + RingCapacityMax = 0x4000000 // Maximum ring capacity (64 MiB) +) + +// Packet with data +type Packet struct { + Next *Packet // Pointer to next packet in queue + Size uint32 // Size of packet (max WINTUN_MAX_IP_PACKET_SIZE) + Data *[PacketSizeMax]byte // Pointer to layer 3 IPv4 or IPv6 packet +} + +var ( + procWintunAllocateSendPacket = modwintun.NewProc("WintunAllocateSendPacket") + procWintunEndSession = modwintun.NewProc("WintunEndSession") + procWintunGetReadWaitEvent = modwintun.NewProc("WintunGetReadWaitEvent") + procWintunReceivePacket = modwintun.NewProc("WintunReceivePacket") + procWintunReleaseReceivePacket = modwintun.NewProc("WintunReleaseReceivePacket") + procWintunSendPacket = modwintun.NewProc("WintunSendPacket") + procWintunStartSession = modwintun.NewProc("WintunStartSession") +) + +func (wintun *Adapter) StartSession(capacity uint32) (session Session, err error) { + r0, _, e1 := syscall.Syscall(procWintunStartSession.Addr(), 2, uintptr(wintun.handle), uintptr(capacity), 0) + if r0 == 0 { + err = e1 + } else { + session = Session{r0} + } + return +} + +func (session Session) End() { + syscall.Syscall(procWintunEndSession.Addr(), 1, session.handle, 0, 0) + session.handle = 0 +} + +func (session Session) ReadWaitEvent() (handle windows.Handle) { + r0, _, _ := syscall.Syscall(procWintunGetReadWaitEvent.Addr(), 1, session.handle, 0, 0) + handle = windows.Handle(r0) + return +} + +func (session Session) ReceivePacket() (packet []byte, err error) { + var packetSize uint32 + r0, _, e1 := syscall.Syscall(procWintunReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packetSize)), 0) + if r0 == 0 { + err = e1 + return + } + packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize) + return +} + +func (session Session) ReleaseReceivePacket(packet []byte) { + syscall.Syscall(procWintunReleaseReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0) +} + +func (session Session) AllocateSendPacket(packetSize int) (packet []byte, err error) { + r0, _, e1 := syscall.Syscall(procWintunAllocateSendPacket.Addr(), 2, session.handle, uintptr(packetSize), 0) + if r0 == 0 { + err = e1 + return + } + packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize) + return +} + +func (session Session) SendPacket(packet []byte) { + syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0) +} diff --git a/listener/tun/dev/wintun/wintun_windows.go b/listener/tun/dev/wintun/wintun_windows.go new file mode 100644 index 00000000..54bd2f09 --- /dev/null +++ b/listener/tun/dev/wintun/wintun_windows.go @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved. + */ + +package wintun + +import ( + "runtime" + "syscall" + "unsafe" + + "github.com/Dreamacro/clash/log" + "golang.org/x/sys/windows" +) + +type loggerLevel int + +const ( + logInfo loggerLevel = iota + logWarn + logErr +) + +const AdapterNameMax = 128 + +type Adapter struct { + handle uintptr +} + +var ( + modwintun = newLazyDLL("wintun.dll", setupLogger) + procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter") + procWintunOpenAdapter = modwintun.NewProc("WintunOpenAdapter") + procWintunCloseAdapter = modwintun.NewProc("WintunCloseAdapter") + procWintunDeleteDriver = modwintun.NewProc("WintunDeleteDriver") + procWintunGetAdapterLUID = modwintun.NewProc("WintunGetAdapterLUID") + procWintunGetRunningDriverVersion = modwintun.NewProc("WintunGetRunningDriverVersion") +) + +func logMessage(level loggerLevel, _ uint64, msg *uint16) int { + var lv log.LogLevel + switch level { + case logInfo: + lv = log.INFO + case logWarn: + lv = log.WARNING + case logErr: + lv = log.ERROR + default: + lv = log.INFO + } + log.PrintLog(lv, "[Wintun] %s", windows.UTF16PtrToString(msg)) + return 0 +} + +func setupLogger(dll *lazyDLL) { + var callback uintptr + if runtime.GOARCH == "386" { + callback = windows.NewCallback(func(level loggerLevel, timestampLow, timestampHigh uint32, msg *uint16) int { + return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg) + }) + } else if runtime.GOARCH == "arm" { + callback = windows.NewCallback(func(level loggerLevel, _, timestampLow, timestampHigh uint32, msg *uint16) int { + return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg) + }) + } else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" { + callback = windows.NewCallback(logMessage) + } + syscall.Syscall(dll.NewProc("WintunSetLogger").Addr(), 1, callback, 0, 0) +} + +func closeAdapter(wintun *Adapter) { + syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0) +} + +// CreateAdapter creates a Wintun adapter. name is the cosmetic name of the adapter. +// tunnelType represents the type of adapter and should be "Wintun". requestedGUID is +// the GUID of the created network adapter, which then influences NLA generation +// deterministically. If it is set to nil, the GUID is chosen by the system at random, +// and hence a new NLA entry is created for each new adapter. +func CreateAdapter(name string, tunnelType string, requestedGUID *windows.GUID) (wintun *Adapter, err error) { + var name16 *uint16 + name16, err = windows.UTF16PtrFromString(name) + if err != nil { + return + } + var tunnelType16 *uint16 + tunnelType16, err = windows.UTF16PtrFromString(tunnelType) + if err != nil { + return + } + r0, _, e1 := syscall.Syscall(procWintunCreateAdapter.Addr(), 3, uintptr(unsafe.Pointer(name16)), uintptr(unsafe.Pointer(tunnelType16)), uintptr(unsafe.Pointer(requestedGUID))) + if r0 == 0 { + err = e1 + return + } + wintun = &Adapter{handle: r0} + runtime.SetFinalizer(wintun, closeAdapter) + return +} + +// OpenAdapter opens an existing Wintun adapter by name. +func OpenAdapter(name string) (wintun *Adapter, err error) { + var name16 *uint16 + name16, err = windows.UTF16PtrFromString(name) + if err != nil { + return + } + r0, _, e1 := syscall.Syscall(procWintunOpenAdapter.Addr(), 1, uintptr(unsafe.Pointer(name16)), 0, 0) + if r0 == 0 { + err = e1 + return + } + wintun = &Adapter{handle: r0} + runtime.SetFinalizer(wintun, closeAdapter) + return +} + +// Close closes a Wintun adapter. +func (wintun *Adapter) Close() (err error) { + runtime.SetFinalizer(wintun, nil) + r1, _, e1 := syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0) + if r1 == 0 { + err = e1 + } + return +} + +// Uninstall removes the driver from the system if no drivers are currently in use. +func Uninstall() (err error) { + r1, _, e1 := syscall.Syscall(procWintunDeleteDriver.Addr(), 0, 0, 0, 0) + if r1 == 0 { + err = e1 + } + return +} + +// RunningVersion returns the version of the loaded driver. +func RunningVersion() (version uint32, err error) { + r0, _, e1 := syscall.Syscall(procWintunGetRunningDriverVersion.Addr(), 0, 0, 0, 0) + version = uint32(r0) + if version == 0 { + err = e1 + } + return +} + +// LUID returns the LUID of the adapter. +func (wintun *Adapter) LUID() (luid uint64) { + syscall.Syscall(procWintunGetAdapterLUID.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&luid)), 0) + return +} diff --git a/listener/tun/ipstack/commons/dns.go b/listener/tun/ipstack/commons/dns.go new file mode 100644 index 00000000..4c4ca29b --- /dev/null +++ b/listener/tun/ipstack/commons/dns.go @@ -0,0 +1,30 @@ +package commons + +import ( + "github.com/Dreamacro/clash/component/resolver" + D "github.com/miekg/dns" +) + +func RelayDnsPacket(payload []byte) ([]byte, error) { + msg := &D.Msg{} + if err := msg.Unpack(payload); err != nil { + return nil, err + } + + r, err := resolver.ServeMsg(msg) + if err != nil { + return nil, err + } + + for _, ans := range r.Answer { + header := ans.Header() + + if header.Class == D.ClassINET && (header.Rrtype == D.TypeA || header.Rrtype == D.TypeAAAA) { + header.Ttl = 1 + } + } + + r.SetRcode(msg, r.Rcode) + r.Compress = true + return r.Pack() +} diff --git a/listener/tun/ipstack/gvisor/tun.go b/listener/tun/ipstack/gvisor/tun.go new file mode 100644 index 00000000..c9f57641 --- /dev/null +++ b/listener/tun/ipstack/gvisor/tun.go @@ -0,0 +1,263 @@ +package gvisor + +import ( + "encoding/binary" + "errors" + "fmt" + "net" + "strings" + "sync" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/common/pool" + "github.com/Dreamacro/clash/component/resolver" + "github.com/Dreamacro/clash/config" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/dns" + "github.com/Dreamacro/clash/listener/tun/dev" + "github.com/Dreamacro/clash/listener/tun/ipstack" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/socks5" + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" + "gvisor.dev/gvisor/pkg/tcpip/buffer" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/link/channel" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" + "gvisor.dev/gvisor/pkg/waiter" +) + +const nicID tcpip.NICID = 1 + +type gvisorAdapter struct { + device dev.TunDevice + ipstack *stack.Stack + dnsserver *DNSServer + udpIn chan<- *inbound.PacketAdapter + + stackName string + autoRoute bool + linkCache *channel.Endpoint + wg sync.WaitGroup // wait for goroutines to stop + + writeHandle *channel.NotificationHandle +} + +// GvisorAdapter create GvisorAdapter +func NewAdapter(device dev.TunDevice, conf config.Tun, tunAddress string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) { + ipstack := stack.New(stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, + TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, + }) + + adapter := &gvisorAdapter{ + device: device, + ipstack: ipstack, + udpIn: udpIn, + stackName: conf.Stack, + autoRoute: conf.AutoRoute, + } + + linkEP, err := adapter.AsLinkEndpoint() + if err != nil { + return nil, fmt.Errorf("unable to create virtual endpoint: %v", err) + } + + if err := ipstack.CreateNIC(nicID, linkEP); err != nil { + return nil, fmt.Errorf("fail to create NIC in ipstack: %v", err) + } + + ipstack.SetPromiscuousMode(nicID, true) // Accept all the traffice from this NIC + ipstack.SetSpoofing(nicID, true) // Otherwise our TCP connection can not find the route backward + + // Add route for ipv4 & ipv6 + // So FindRoute will return correct route to tun NIC + subnet, _ := tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 4)), tcpip.AddressMask(strings.Repeat("\x00", 4))) + ipstack.AddRoute(tcpip.Route{Destination: subnet, Gateway: "", NIC: nicID}) + subnet, _ = tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 16)), tcpip.AddressMask(strings.Repeat("\x00", 16))) + ipstack.AddRoute(tcpip.Route{Destination: subnet, Gateway: "", NIC: nicID}) + + // TCP handler + // maximum number of half-open tcp connection set to 1024 + // receive buffer size set to 20k + tcpFwd := tcp.NewForwarder(ipstack, pool.RelayBufferSize, 1024, func(r *tcp.ForwarderRequest) { + var wq waiter.Queue + ep, err := r.CreateEndpoint(&wq) + if err != nil { + log.Warnln("Can't create TCP Endpoint in ipstack: %v", err) + r.Complete(true) + return + } + r.Complete(false) + + conn := gonet.NewTCPConn(&wq, ep) + + // if the endpoint is not in connected state, conn.RemoteAddr() will return nil + // this protection may be not enough, but will help us debug the panic + if conn.RemoteAddr() == nil { + log.Warnln("TCP endpoint is not connected, current state: %v", tcp.EndpointState(ep.State())) + conn.Close() + return + } + + target := getAddr(ep.Info().(*stack.TransportEndpointInfo).ID) + tcpIn <- inbound.NewSocket(target, conn, C.TUN) + }) + ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpFwd.HandlePacket) + + // UDP handler + ipstack.SetTransportProtocolHandler(udp.ProtocolNumber, adapter.udpHandlePacket) + + if resolver.DefaultResolver != nil { + err = adapter.ReCreateDNSServer(resolver.DefaultResolver.(*dns.Resolver), resolver.DefaultHostMapper.(*dns.ResolverEnhancer), conf.DNSListen) + if err != nil { + return nil, err + } + } + + return adapter, nil +} + +func (t *gvisorAdapter) Stack() string { + return t.stackName +} + +func (t *gvisorAdapter) AutoRoute() bool { + return t.autoRoute +} + +// Close close the TunAdapter +func (t *gvisorAdapter) Close() { + if t.dnsserver != nil { + t.dnsserver.Stop() + } + if t.ipstack != nil { + t.ipstack.Close() + } + if t.device != nil { + _ = t.device.Close() + } +} + +func (t *gvisorAdapter) udpHandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool { + // ref: gvisor pkg/tcpip/transport/udp/endpoint.go HandlePacket + hdr := header.UDP(pkt.TransportHeader().View()) + if int(hdr.Length()) > pkt.Data().Size()+header.UDPMinimumSize { + // Malformed packet. + t.ipstack.Stats().UDP.MalformedPacketsReceived.Increment() + return true + } + + target := getAddr(id) + + packet := &fakeConn{ + id: id, + pkt: pkt, + s: t.ipstack, + payload: pkt.Data().AsRange().ToOwnedView(), + } + + select { + case t.udpIn <- inbound.NewPacket(target, packet, C.TUN): + default: + } + + return true +} + +// Wait wait goroutines to exit +func (t *gvisorAdapter) Wait() { + t.wg.Wait() +} + +func (t *gvisorAdapter) AsLinkEndpoint() (result stack.LinkEndpoint, err error) { + if t.linkCache != nil { + return t.linkCache, nil + } + + mtu, err := t.device.MTU() + if err != nil { + return nil, errors.New("unable to get device mtu") + } + + linkEP := channel.New(512, uint32(mtu), "") + + // start Read loop. read ip packet from tun and write it to ipstack + t.wg.Add(1) + go func() { + for !t.device.IsClose() { + packet := make([]byte, mtu) + n, err := t.device.Read(packet) + if err != nil && !t.device.IsClose() { + log.Errorln("can not read from tun: %v", err) + continue + } + var p tcpip.NetworkProtocolNumber + switch header.IPVersion(packet) { + case header.IPv4Version: + p = header.IPv4ProtocolNumber + case header.IPv6Version: + p = header.IPv6ProtocolNumber + } + if linkEP.IsAttached() { + linkEP.InjectInbound(p, stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: buffer.View(packet[:n]).ToVectorisedView(), + })) + } else { + log.Debugln("received packet from tun when %s is not attached to any dispatcher.", t.device.Name()) + } + } + t.wg.Done() + t.Close() + log.Debugln("%v stop read loop", t.device.Name()) + }() + + // start write notification + t.writeHandle = linkEP.AddNotify(t) + t.linkCache = linkEP + return t.linkCache, nil +} + +// WriteNotify implements channel.Notification.WriteNotify. +func (t *gvisorAdapter) WriteNotify() { + packet, ok := t.linkCache.Read() + if ok { + var vv buffer.VectorisedView + // Append upper headers. + vv.AppendView(packet.Pkt.NetworkHeader().View()) + vv.AppendView(packet.Pkt.TransportHeader().View()) + // Append data payload. + vv.Append(packet.Pkt.Data().ExtractVV()) + + _, err := t.device.Write(vv.ToView()) + if err != nil && !t.device.IsClose() { + log.Errorln("can not write to tun: %v", err) + } + } +} + +func getAddr(id stack.TransportEndpointID) socks5.Addr { + ipv4 := id.LocalAddress.To4() + + // get the big-endian binary represent of port + port := make([]byte, 2) + binary.BigEndian.PutUint16(port, id.LocalPort) + + if ipv4 != "" { + addr := make([]byte, 1+net.IPv4len+2) + addr[0] = socks5.AtypIPv4 + copy(addr[1:1+net.IPv4len], []byte(ipv4)) + addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] + return addr + } else { + addr := make([]byte, 1+net.IPv6len+2) + addr[0] = socks5.AtypIPv6 + copy(addr[1:1+net.IPv6len], []byte(id.LocalAddress)) + addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1] + return addr + } +} diff --git a/listener/tun/ipstack/gvisor/tundns.go b/listener/tun/ipstack/gvisor/tundns.go new file mode 100644 index 00000000..1bb9e766 --- /dev/null +++ b/listener/tun/ipstack/gvisor/tundns.go @@ -0,0 +1,291 @@ +package gvisor + +import ( + "fmt" + "net" + + "github.com/Dreamacro/clash/dns" + "github.com/Dreamacro/clash/log" + D "github.com/miekg/dns" + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" + "gvisor.dev/gvisor/pkg/tcpip/buffer" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" + "gvisor.dev/gvisor/pkg/tcpip/ports" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" +) + +var ( + ipv4Zero = tcpip.Address(net.IPv4zero.To4()) + ipv6Zero = tcpip.Address(net.IPv6zero.To16()) +) + +// DNSServer is DNS Server listening on tun devcice +type DNSServer struct { + *dns.Server + resolver *dns.Resolver + + stack *stack.Stack + tcpListener net.Listener + udpEndpoint *dnsEndpoint + udpEndpointID *stack.TransportEndpointID + tcpip.NICID +} + +// dnsEndpoint is a TransportEndpoint that will register to stack +type dnsEndpoint struct { + stack.TransportEndpoint + stack *stack.Stack + uniqueID uint64 + server *dns.Server +} + +// Keep track of the source of DNS request +type dnsResponseWriter struct { + s *stack.Stack + pkt *stack.PacketBuffer // The request packet + id stack.TransportEndpointID +} + +func (e *dnsEndpoint) UniqueID() uint64 { + return e.uniqueID +} + +func (e *dnsEndpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) { + hdr := header.UDP(pkt.TransportHeader().View()) + if int(hdr.Length()) > pkt.Data().Size()+header.UDPMinimumSize { + // Malformed packet. + e.stack.Stats().UDP.MalformedPacketsReceived.Increment() + return + } + + // server DNS + var msg D.Msg + msg.Unpack(pkt.Data().AsRange().ToOwnedView()) + writer := dnsResponseWriter{s: e.stack, pkt: pkt, id: id} + go e.server.ServeDNS(&writer, &msg) +} + +func (e *dnsEndpoint) Close() { +} + +func (e *dnsEndpoint) Wait() { +} + +func (e *dnsEndpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketBuffer) { + log.Warnln("DNS endpoint get a transport error: %v", transErr) + log.Debugln("DNS endpoint transport error packet : %v", pkt) +} + +// Abort implements stack.TransportEndpoint.Abort. +func (e *dnsEndpoint) Abort() { + e.Close() +} + +func (w *dnsResponseWriter) LocalAddr() net.Addr { + return &net.UDPAddr{IP: net.IP(w.id.LocalAddress), Port: int(w.id.LocalPort)} +} + +func (w *dnsResponseWriter) RemoteAddr() net.Addr { + return &net.UDPAddr{IP: net.IP(w.id.RemoteAddress), Port: int(w.id.RemotePort)} +} + +func (w *dnsResponseWriter) WriteMsg(msg *D.Msg) error { + b, err := msg.Pack() + if err != nil { + return err + } + _, err = w.Write(b) + return err +} + +func (w *dnsResponseWriter) TsigStatus() error { + // Unsupported + return nil +} + +func (w *dnsResponseWriter) TsigTimersOnly(bool) { + // Unsupported +} + +func (w *dnsResponseWriter) Hijack() { + // Unsupported +} + +func (w *dnsResponseWriter) Write(b []byte) (int, error) { + v := buffer.NewView(len(b)) + copy(v, b) + data := v.ToVectorisedView() + // w.id.LocalAddress is the source ip of DNS response + r, _ := w.s.FindRoute(w.pkt.NICID, w.id.LocalAddress, w.id.RemoteAddress, w.pkt.NetworkProtocolNumber, false /* multicastLoop */) + return writeUDP(r, data, w.id.LocalPort, w.id.RemotePort) +} + +func (w *dnsResponseWriter) Close() error { + return nil +} + +// CreateDNSServer create a dns server on given netstack +func CreateDNSServer(s *stack.Stack, resolver *dns.Resolver, mapper *dns.ResolverEnhancer, ip net.IP, port int, nicID tcpip.NICID) (*DNSServer, error) { + var v4 bool + var err error + + address := tcpip.FullAddress{NIC: nicID, Port: uint16(port)} + var protocol tcpip.NetworkProtocolNumber + if ip.To4() != nil { + v4 = true + address.Addr = tcpip.Address(ip.To4()) + protocol = ipv4.ProtocolNumber + + } else { + v4 = false + address.Addr = tcpip.Address(ip.To16()) + protocol = ipv6.ProtocolNumber + } + protocolAddr := tcpip.ProtocolAddress{ + Protocol: protocol, + AddressWithPrefix: address.Addr.WithPrefix(), + } + // netstack will only reassemble IP fragments when its' dest ip address is registered in NIC.endpoints + if err := s.AddProtocolAddress(nicID, protocolAddr, stack.AddressProperties{}); err != nil { + log.Errorln("AddProtocolAddress(%d, %+v, {}): %s", nicID, protocolAddr, err) + } + + if address.Addr == ipv4Zero || address.Addr == ipv6Zero { + address.Addr = "" + } + + handler := dns.NewHandler(resolver, mapper) + serverIn := &dns.Server{} + serverIn.SetHandler(handler) + + // UDP DNS + id := &stack.TransportEndpointID{ + LocalAddress: address.Addr, + LocalPort: uint16(port), + RemotePort: 0, + RemoteAddress: "", + } + + // TransportEndpoint for DNS + endpoint := &dnsEndpoint{ + stack: s, + uniqueID: s.UniqueID(), + server: serverIn, + } + + if tcpiperr := s.RegisterTransportEndpoint( + []tcpip.NetworkProtocolNumber{ + ipv4.ProtocolNumber, + ipv6.ProtocolNumber, + }, + udp.ProtocolNumber, + *id, + endpoint, + ports.Flags{LoadBalanced: true}, // it's actually the SO_REUSEPORT. Not sure it take effect. + nicID); tcpiperr != nil { + log.Errorln("Unable to start UDP DNS on tun: %v", tcpiperr.String()) + } + + // TCP DNS + var tcpListener net.Listener + if v4 { + tcpListener, err = gonet.ListenTCP(s, address, ipv4.ProtocolNumber) + } else { + tcpListener, err = gonet.ListenTCP(s, address, ipv6.ProtocolNumber) + } + if err != nil { + return nil, fmt.Errorf("can not listen on tun: %v", err) + } + + server := &DNSServer{ + Server: serverIn, + resolver: resolver, + stack: s, + tcpListener: tcpListener, + udpEndpoint: endpoint, + udpEndpointID: id, + NICID: nicID, + } + server.SetHandler(handler) + server.Server.Server = &D.Server{Listener: tcpListener, Handler: server} + + go func() { + server.ActivateAndServe() + }() + + return server, err +} + +// Stop stop the DNS Server on tun +func (s *DNSServer) Stop() { + // shutdown TCP DNS Server + s.Server.Shutdown() + // remove TCP endpoint from stack + if s.Listener != nil { + s.Listener.Close() + } + // remove udp endpoint from stack + s.stack.UnregisterTransportEndpoint( + []tcpip.NetworkProtocolNumber{ + ipv4.ProtocolNumber, + ipv6.ProtocolNumber, + }, + udp.ProtocolNumber, + *s.udpEndpointID, + s.udpEndpoint, + ports.Flags{LoadBalanced: true}, // should match the RegisterTransportEndpoint + s.NICID) +} + +// DNSListen return the listening address of DNS Server +func (t *gvisorAdapter) DNSListen() string { + if t.dnsserver != nil { + id := t.dnsserver.udpEndpointID + return fmt.Sprintf("%s:%d", id.LocalAddress.String(), id.LocalPort) + } + return "" +} + +// Stop stop the DNS Server on tun +func (t *gvisorAdapter) ReCreateDNSServer(resolver *dns.Resolver, mapper *dns.ResolverEnhancer, addr string) error { + if addr == "" && t.dnsserver == nil { + return nil + } + + if addr == t.DNSListen() && t.dnsserver != nil && t.dnsserver.resolver == resolver { + return nil + } + + if t.dnsserver != nil { + t.dnsserver.Stop() + t.dnsserver = nil + log.Debugln("tun DNS server stoped") + } + + var err error + _, port, err := net.SplitHostPort(addr) + if port == "0" || port == "" || err != nil { + return nil + } + + if resolver == nil { + return fmt.Errorf("failed to create DNS server on tun: resolver not provided") + } + + udpAddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return err + } + + server, err := CreateDNSServer(t.ipstack, resolver, mapper, udpAddr.IP, udpAddr.Port, nicID) + if err != nil { + return err + } + t.dnsserver = server + log.Infoln("Tun DNS server listening at: %s, fake ip enabled: %v", addr, mapper.FakeIPEnabled()) + return nil +} diff --git a/listener/tun/ipstack/gvisor/utils.go b/listener/tun/ipstack/gvisor/utils.go new file mode 100644 index 00000000..44063f0b --- /dev/null +++ b/listener/tun/ipstack/gvisor/utils.go @@ -0,0 +1,108 @@ +package gvisor + +import ( + "fmt" + "net" + + "github.com/Dreamacro/clash/component/resolver" + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/buffer" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" +) + +type fakeConn struct { + id stack.TransportEndpointID // The endpoint of incomming packet, it's remote address is the source address it sent from + pkt *stack.PacketBuffer // The original packet comming from tun + s *stack.Stack + payload []byte + fakeip *bool +} + +func (c *fakeConn) Data() []byte { + return c.payload +} + +func (c *fakeConn) WriteBack(b []byte, addr net.Addr) (n int, err error) { + v := buffer.View(b) + data := v.ToVectorisedView() + + var localAddress tcpip.Address + var localPort uint16 + // if addr is not provided, write back use original dst Addr as src Addr + if c.FakeIP() || addr == nil { + localAddress = c.id.LocalAddress + localPort = c.id.LocalPort + } else { + udpaddr, _ := addr.(*net.UDPAddr) + localAddress = tcpip.Address(udpaddr.IP) + localPort = uint16(udpaddr.Port) + } + + r, _ := c.s.FindRoute(c.pkt.NICID, localAddress, c.id.RemoteAddress, c.pkt.NetworkProtocolNumber, false /* multicastLoop */) + return writeUDP(r, data, localPort, c.id.RemotePort) +} + +func (c *fakeConn) LocalAddr() net.Addr { + return &net.UDPAddr{IP: net.IP(c.id.RemoteAddress), Port: int(c.id.RemotePort)} +} + +func (c *fakeConn) Close() error { + return nil +} + +func (c *fakeConn) Drop() { +} + +func (c *fakeConn) FakeIP() bool { + if c.fakeip != nil { + return *c.fakeip + } + fakeip := resolver.IsFakeIP(net.IP(c.id.LocalAddress.To4())) + c.fakeip = &fakeip + return fakeip +} + +func writeUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16) (int, error) { + const protocol = udp.ProtocolNumber + // Allocate a buffer for the UDP header. + + pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()), + Data: data, + }) + + // Initialize the header. + udp := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize)) + + length := uint16(pkt.Size()) + udp.Encode(&header.UDPFields{ + SrcPort: localPort, + DstPort: remotePort, + Length: length, + }) + + // Set the checksum field unless TX checksum offload is enabled. + // On IPv4, UDP checksum is optional, and a zero value indicates the + // transmitter skipped the checksum generation (RFC768). + // On IPv6, UDP checksum is not optional (RFC2460 Section 8.1). + if r.RequiresTXTransportChecksum() { + xsum := r.PseudoHeaderChecksum(protocol, length) + for _, v := range data.Views() { + xsum = header.Checksum(v, xsum) + } + udp.SetChecksum(^udp.CalculateChecksum(xsum)) + } + + ttl := r.DefaultTTL() + + if err := r.WritePacket(stack.NetworkHeaderParams{Protocol: protocol, TTL: ttl, TOS: 0 /* default */}, pkt); err != nil { + r.Stats().UDP.PacketSendErrors.Increment() + return 0, fmt.Errorf("%v", err) + } + + // Track count of packets sent. + r.Stats().UDP.PacketsSent.Increment() + return data.Size(), nil +} diff --git a/listener/tun/ipstack/lwip/dns.go b/listener/tun/ipstack/lwip/dns.go new file mode 100644 index 00000000..6a314c08 --- /dev/null +++ b/listener/tun/ipstack/lwip/dns.go @@ -0,0 +1,87 @@ +package lwip + +import ( + "encoding/binary" + "io" + "net" + "time" + + "github.com/Dreamacro/clash/component/resolver" + D "github.com/Dreamacro/clash/listener/tun/ipstack/commons" + "github.com/Dreamacro/clash/log" + "github.com/yaling888/go-lwip" +) + +const defaultDnsReadTimeout = time.Second * 8 + +func shouldHijackDns(dnsIP net.IP, targetIp net.IP, targetPort int) bool { + if targetPort != 53 { + return false + } + + return dnsIP.Equal(net.IPv4zero) || dnsIP.Equal(targetIp) +} + +func hijackUDPDns(conn golwip.UDPConn, pkt []byte, addr *net.UDPAddr) { + go func() { + defer func(conn golwip.UDPConn) { + _ = conn.Close() + }(conn) + + answer, err := D.RelayDnsPacket(pkt) + if err != nil { + return + } + _, _ = conn.WriteFrom(answer, addr) + }() +} + +func hijackTCPDns(conn net.Conn) { + go func() { + defer func(conn net.Conn) { + _ = conn.Close() + }(conn) + + if err := conn.SetDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil { + return + } + + for { + var length uint16 + if binary.Read(conn, binary.BigEndian, &length) != nil { + return + } + + data := make([]byte, length) + + _, err := io.ReadFull(conn, data) + if err != nil { + return + } + + rb, err := D.RelayDnsPacket(data) + if err != nil { + continue + } + + if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil { + return + } + + if _, err = conn.Write(rb); err != nil { + return + } + } + }() +} + +type dnsHandler struct{} + +func newDnsHandler() golwip.DnsHandler { + return &dnsHandler{} +} + +func (d dnsHandler) ResolveIP(host string) (net.IP, error) { + log.Debugln("[TUN] lwip resolve ip for host: %s", host) + return resolver.ResolveIP(host) +} diff --git a/listener/tun/ipstack/lwip/tcp.go b/listener/tun/ipstack/lwip/tcp.go new file mode 100644 index 00000000..c62a6beb --- /dev/null +++ b/listener/tun/ipstack/lwip/tcp.go @@ -0,0 +1,61 @@ +package lwip + +import ( + "net" + "strconv" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/context" + "github.com/Dreamacro/clash/log" + "github.com/yaling888/go-lwip" +) + +type tcpHandler struct { + dnsIP net.IP + tcpIn chan<- C.ConnContext +} + +func newTCPHandler(dnsIP net.IP, tcpIn chan<- C.ConnContext) golwip.TCPConnHandler { + return &tcpHandler{dnsIP, tcpIn} +} + +func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error { + if shouldHijackDns(h.dnsIP, target.IP, target.Port) { + hijackTCPDns(conn) + log.Debugln("[TUN] hijack dns tcp: %s:%d", target.IP.String(), target.Port) + return nil + } + + if conn.RemoteAddr() == nil { + _ = conn.Close() + return nil + } + + src, _ := conn.LocalAddr().(*net.TCPAddr) + dst, _ := conn.RemoteAddr().(*net.TCPAddr) + + addrType := C.AtypIPv4 + if dst.IP.To4() == nil { + addrType = C.AtypIPv6 + } + + metadata := &C.Metadata{ + NetWork: C.TCP, + Type: C.TUN, + SrcIP: src.IP, + DstIP: dst.IP, + SrcPort: strconv.Itoa(src.Port), + DstPort: strconv.Itoa(dst.Port), + AddrType: addrType, + Host: "", + } + + go func(conn net.Conn, metadata *C.Metadata) { + //if c, ok := conn.(*net.TCPConn); ok { + // c.SetKeepAlive(true) + //} + h.tcpIn <- context.NewConnContext(conn, metadata) + }(conn, metadata) + + return nil +} diff --git a/listener/tun/ipstack/lwip/tun.go b/listener/tun/ipstack/lwip/tun.go new file mode 100644 index 00000000..9037be6e --- /dev/null +++ b/listener/tun/ipstack/lwip/tun.go @@ -0,0 +1,121 @@ +package lwip + +import ( + "io" + "net" + "sync" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/common/pool" + "github.com/Dreamacro/clash/config" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/tun/dev" + "github.com/Dreamacro/clash/listener/tun/ipstack" + "github.com/Dreamacro/clash/log" + "github.com/yaling888/go-lwip" +) + +type lwipAdapter struct { + device dev.TunDevice + lwipStack golwip.LWIPStack + lock sync.Mutex + mtu int + stackName string + dnsListen string + autoRoute bool +} + +func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) { + adapter := &lwipAdapter{ + device: device, + mtu: mtu, + stackName: conf.Stack, + dnsListen: conf.DNSListen, + autoRoute: conf.AutoRoute, + } + + adapter.lock.Lock() + defer adapter.lock.Unlock() + + dnsHost, _, err := net.SplitHostPort(conf.DNSListen) + if err != nil { + return nil, err + } + + dnsIP := net.ParseIP(dnsHost) + + // Register output function, write packets from lwip stack to tun device + golwip.RegisterOutputFn(func(data []byte) (int, error) { + return device.Write(data) + }) + + // Set custom buffer pool + golwip.SetPoolAllocator(newLWIPPool()) + + // Setup TCP/IP stack. + lwipStack, err := golwip.NewLWIPStack(mtu) + if err != nil { + return nil, err + } + adapter.lwipStack = lwipStack + + golwip.RegisterDnsHandler(newDnsHandler()) + golwip.RegisterTCPConnHandler(newTCPHandler(dnsIP, tcpIn)) + golwip.RegisterUDPConnHandler(newUDPHandler(dnsIP, udpIn)) + + // Copy packets from tun device to lwip stack, it's the loop. + go func(lwipStack golwip.LWIPStack, device dev.TunDevice, mtu int) { + _, err := io.CopyBuffer(lwipStack.(io.Writer), device, make([]byte, mtu)) + if err != nil { + log.Debugln("copying data failed: %v", err) + } + }(lwipStack, device, mtu) + + return adapter, nil +} + +func (l *lwipAdapter) Stack() string { + return l.stackName +} + +func (l *lwipAdapter) AutoRoute() bool { + return l.autoRoute +} + +func (l *lwipAdapter) DNSListen() string { + return l.dnsListen +} + +func (l *lwipAdapter) Close() { + l.lock.Lock() + defer l.lock.Unlock() + + l.stopLocked() +} + +func (l *lwipAdapter) stopLocked() { + if l.lwipStack != nil { + _ = l.lwipStack.Close() + } + + if l.device != nil { + _ = l.device.Close() + } + + l.lwipStack = nil + l.device = nil +} + +type lwipPool struct{} + +func (p lwipPool) Get(size int) []byte { + return pool.Get(size) +} + +func (p lwipPool) Put(buf []byte) error { + return pool.Put(buf) +} + +func newLWIPPool() golwip.LWIPPool { + return &lwipPool{} +} diff --git a/listener/tun/ipstack/lwip/udp.go b/listener/tun/ipstack/lwip/udp.go new file mode 100644 index 00000000..747796bf --- /dev/null +++ b/listener/tun/ipstack/lwip/udp.go @@ -0,0 +1,74 @@ +package lwip + +import ( + "io" + "net" + + "github.com/Dreamacro/clash/adapter/inbound" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/socks5" + "github.com/yaling888/go-lwip" +) + +type udpPacket struct { + source *net.UDPAddr + payload []byte + sender golwip.UDPConn +} + +func (u *udpPacket) Data() []byte { + return u.payload +} + +func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) { + _, ok := addr.(*net.UDPAddr) + if !ok { + return 0, io.ErrClosedPipe + } + + return u.sender.WriteFrom(b, u.source) +} + +func (u *udpPacket) Drop() { +} + +func (u *udpPacket) LocalAddr() net.Addr { + return u.source +} + +type udpHandler struct { + dnsIP net.IP + udpIn chan<- *inbound.PacketAdapter +} + +func newUDPHandler(dnsIP net.IP, udpIn chan<- *inbound.PacketAdapter) golwip.UDPConnHandler { + return &udpHandler{dnsIP, udpIn} +} + +func (h *udpHandler) Connect(golwip.UDPConn, *net.UDPAddr) error { + return nil +} + +func (h *udpHandler) ReceiveTo(conn golwip.UDPConn, data []byte, addr *net.UDPAddr) error { + if shouldHijackDns(h.dnsIP, addr.IP, addr.Port) { + hijackUDPDns(conn, data, addr) + log.Debugln("[TUN] hijack dns udp: %s:%d", addr.IP.String(), addr.Port) + return nil + } + + packet := &udpPacket{ + source: conn.LocalAddr(), + payload: data, + sender: conn, + } + + go func(addr *net.UDPAddr, packet *udpPacket) { + select { + case h.udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(addr), packet, C.TUN): + default: + } + }(addr, packet) + + return nil +} diff --git a/listener/tun/ipstack/stack_adapter.go b/listener/tun/ipstack/stack_adapter.go new file mode 100644 index 00000000..333c3ca5 --- /dev/null +++ b/listener/tun/ipstack/stack_adapter.go @@ -0,0 +1,9 @@ +package ipstack + +// TunAdapter hold the state of tun/tap interface +type TunAdapter interface { + Close() + Stack() string + DNSListen() string + AutoRoute() bool +} diff --git a/listener/tun/ipstack/system/dns.go b/listener/tun/ipstack/system/dns.go new file mode 100644 index 00000000..77159206 --- /dev/null +++ b/listener/tun/ipstack/system/dns.go @@ -0,0 +1,75 @@ +package system + +import ( + "encoding/binary" + "io" + "net" + "time" + + D "github.com/Dreamacro/clash/listener/tun/ipstack/commons" + "github.com/kr328/tun2socket/binding" + "github.com/kr328/tun2socket/redirect" +) + +const defaultDnsReadTimeout = time.Second * 30 + +func shouldHijackDns(dnsAddr binding.Address, targetAddr binding.Address) bool { + if targetAddr.Port != 53 { + return false + } + + return dnsAddr.IP.Equal(net.IPv4zero) || dnsAddr.IP.Equal(targetAddr.IP) +} + +func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) { + go func() { + answer, err := D.RelayDnsPacket(pkt) + if err != nil { + return + } + + _ = sender(answer, &binding.Endpoint{ + Source: ep.Target, + Target: ep.Source, + }) + }() +} + +func hijackTCPDns(conn net.Conn) { + go func() { + defer func(conn net.Conn) { + _ = conn.Close() + }(conn) + + if err := conn.SetReadDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil { + return + } + + for { + var length uint16 + if binary.Read(conn, binary.BigEndian, &length) != nil { + return + } + + data := make([]byte, length) + + _, err := io.ReadFull(conn, data) + if err != nil { + return + } + + rb, err := D.RelayDnsPacket(data) + if err != nil { + continue + } + + if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil { + return + } + + if _, err = conn.Write(rb); err != nil { + return + } + } + }() +} diff --git a/listener/tun/ipstack/system/log.go b/listener/tun/ipstack/system/log.go new file mode 100644 index 00000000..6546dc27 --- /dev/null +++ b/listener/tun/ipstack/system/log.go @@ -0,0 +1,21 @@ +package system + +import "github.com/Dreamacro/clash/log" + +type logger struct{} + +func (l *logger) D(format string, args ...interface{}) { + log.Debugln("[TUN] "+format, args...) +} + +func (l *logger) I(format string, args ...interface{}) { + log.Infoln("[TUN] "+format, args...) +} + +func (l *logger) W(format string, args ...interface{}) { + log.Warnln("[TUN] "+format, args...) +} + +func (l *logger) E(format string, args ...interface{}) { + log.Errorln("[TUN] "+format, args...) +} diff --git a/listener/tun/ipstack/system/tcp.go b/listener/tun/ipstack/system/tcp.go new file mode 100644 index 00000000..f52f7268 --- /dev/null +++ b/listener/tun/ipstack/system/tcp.go @@ -0,0 +1,46 @@ +package system + +import ( + "net" + "strconv" + + "github.com/kr328/tun2socket/binding" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/context" +) + +func handleTCP(conn net.Conn, endpoint *binding.Endpoint, tcpIn chan<- C.ConnContext) { + src := &net.TCPAddr{ + IP: endpoint.Source.IP, + Port: int(endpoint.Source.Port), + Zone: "", + } + + dst := &net.TCPAddr{ + IP: endpoint.Target.IP, + Port: int(endpoint.Target.Port), + Zone: "", + } + + addrType := C.AtypIPv4 + if dst.IP.To4() == nil { + addrType = C.AtypIPv6 + } + + metadata := &C.Metadata{ + NetWork: C.TCP, + Type: C.TUN, + SrcIP: src.IP, + DstIP: dst.IP, + SrcPort: strconv.Itoa(src.Port), + DstPort: strconv.Itoa(dst.Port), + AddrType: addrType, + Host: "", + } + + //if c, ok := conn.(*net.TCPConn); ok { + // c.SetKeepAlive(true) + //} + tcpIn <- context.NewConnContext(conn, metadata) +} diff --git a/listener/tun/ipstack/system/tun.go b/listener/tun/ipstack/system/tun.go new file mode 100644 index 00000000..fbfb8cb1 --- /dev/null +++ b/listener/tun/ipstack/system/tun.go @@ -0,0 +1,116 @@ +package system + +import ( + "net" + "strconv" + "sync" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/config" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/tun/dev" + "github.com/Dreamacro/clash/listener/tun/ipstack" + "github.com/Dreamacro/clash/log" + "github.com/kr328/tun2socket" + "github.com/kr328/tun2socket/binding" + "github.com/kr328/tun2socket/redirect" +) + +type systemAdapter struct { + device dev.TunDevice + tun *tun2socket.Tun2Socket + lock sync.Mutex + stackName string + dnsListen string + autoRoute bool +} + +func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, gateway, mirror string, onStop func(), tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) { + adapter := &systemAdapter{ + device: device, + stackName: conf.Stack, + dnsListen: conf.DNSListen, + autoRoute: conf.AutoRoute, + } + + adapter.lock.Lock() + defer adapter.lock.Unlock() + + dnsHost, dnsPort, err := net.SplitHostPort(conf.DNSListen) + if err != nil { + return nil, err + } + + dnsP, err := strconv.Atoi(dnsPort) + if err != nil { + return nil, err + } + + dnsAddr := binding.Address{ + IP: net.ParseIP(dnsHost), + Port: uint16(dnsP), + } + + t := tun2socket.NewTun2Socket(device, mtu, net.ParseIP(gateway), net.ParseIP(mirror)) + + t.SetAllocator(allocUDP) + t.SetClosedHandler(onStop) + t.SetLogger(&logger{}) + + t.SetTCPHandler(func(conn net.Conn, endpoint *binding.Endpoint) { + if shouldHijackDns(dnsAddr, endpoint.Target) { + hijackTCPDns(conn) + log.Debugln("[TUN] hijack dns tcp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port) + return + } + + handleTCP(conn, endpoint, tcpIn) + }) + t.SetUDPHandler(func(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) { + if shouldHijackDns(dnsAddr, endpoint.Target) { + hijackUDPDns(payload, endpoint, sender) + log.Debugln("[TUN] hijack dns udp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port) + return + } + + handleUDP(payload, endpoint, sender, udpIn) + }) + + t.Start() + + adapter.tun = t + + return adapter, nil +} + +func (t *systemAdapter) Stack() string { + return t.stackName +} + +func (t *systemAdapter) AutoRoute() bool { + return t.autoRoute +} + +func (t *systemAdapter) DNSListen() string { + return t.dnsListen +} + +func (t *systemAdapter) Close() { + t.lock.Lock() + defer t.lock.Unlock() + + t.stopLocked() +} + +func (t *systemAdapter) stopLocked() { + if t.tun != nil { + t.tun.Close() + } + + if t.device != nil { + _ = t.device.Close() + } + + t.tun = nil + t.device = nil +} diff --git a/listener/tun/ipstack/system/udp.go b/listener/tun/ipstack/system/udp.go new file mode 100644 index 00000000..afb99174 --- /dev/null +++ b/listener/tun/ipstack/system/udp.go @@ -0,0 +1,74 @@ +package system + +import ( + "io" + "net" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/common/pool" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" + "github.com/kr328/tun2socket/binding" + "github.com/kr328/tun2socket/redirect" +) + +type udpPacket struct { + source binding.Address + data []byte + send redirect.UDPSender +} + +func (u *udpPacket) Data() []byte { + return u.data +} + +func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) { + uAddr, ok := addr.(*net.UDPAddr) + if !ok { + return 0, io.ErrClosedPipe + } + + return len(b), u.send(b, &binding.Endpoint{ + Source: binding.Address{IP: uAddr.IP, Port: uint16(uAddr.Port)}, + Target: u.source, + }) +} + +func (u *udpPacket) Drop() { + recycleUDP(u.data) +} + +func (u *udpPacket) LocalAddr() net.Addr { + return &net.UDPAddr{ + IP: u.source.IP, + Port: int(u.source.Port), + Zone: "", + } +} + +func handleUDP(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender, udpIn chan<- *inbound.PacketAdapter) { + pkt := &udpPacket{ + source: endpoint.Source, + data: payload, + send: sender, + } + + rAddr := &net.UDPAddr{ + IP: endpoint.Target.IP, + Port: int(endpoint.Target.Port), + Zone: "", + } + + select { + case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.TUN): + default: + } +} + +func allocUDP(size int) []byte { + return pool.Get(size) +} + +func recycleUDP(payload []byte) { + _ = pool.Put(payload) +} diff --git a/listener/tun/tun_adapter.go b/listener/tun/tun_adapter.go new file mode 100644 index 00000000..9f1b69f6 --- /dev/null +++ b/listener/tun/tun_adapter.go @@ -0,0 +1,54 @@ +package tun + +import ( + "errors" + "fmt" + "strings" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/config" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/tun/dev" + "github.com/Dreamacro/clash/listener/tun/ipstack" + "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor" + "github.com/Dreamacro/clash/listener/tun/ipstack/lwip" + "github.com/Dreamacro/clash/listener/tun/ipstack/system" + "github.com/Dreamacro/clash/log" +) + +// New create TunAdapter +func New(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) { + tunAddress := "198.18.0.1" + autoRoute := conf.AutoRoute + stack := conf.Stack + var tunAdapter ipstack.TunAdapter + + device, err := dev.OpenTunDevice(tunAddress, autoRoute) + if err != nil { + return nil, fmt.Errorf("can't open tun: %v", err) + } + + mtu, err := device.MTU() + if err != nil { + _ = device.Close() + return nil, errors.New("unable to get device mtu") + } + + if strings.EqualFold(stack, "lwip") { + tunAdapter, err = lwip.NewAdapter(device, conf, mtu, tcpIn, udpIn) + } else if strings.EqualFold(stack, "system") { + tunAdapter, err = system.NewAdapter(device, conf, mtu, tunAddress, tunAddress, func() {}, tcpIn, udpIn) + } else if strings.EqualFold(stack, "gvisor") { + tunAdapter, err = gvisor.NewAdapter(device, conf, tunAddress, tcpIn, udpIn) + } else { + err = fmt.Errorf("can not support tun ip stack: %s, only support \"lwip\" \"system\" and \"gvisor\"", stack) + } + + if err != nil { + _ = device.Close() + return nil, err + } + + log.Infoln("Tun adapter listening at: %s(%s), mtu: %d, auto route: %v, ip stack: %s", device.Name(), tunAddress, mtu, autoRoute, stack) + return tunAdapter, nil +} diff --git a/log/log.go b/log/log.go index 73ff0b9e..f5c57768 100644 --- a/log/log.go +++ b/log/log.go @@ -97,3 +97,9 @@ func newLog(logLevel LogLevel, format string, v ...interface{}) *Event { Payload: fmt.Sprintf(format, v...), } } + +func PrintLog(logLevel LogLevel, format string, v ...interface{}) { + event := newLog(logLevel, format, v...) + logCh <- event + print(event) +} diff --git a/main.go b/main.go index 5d953ed6..d1376b8b 100644 --- a/main.go +++ b/main.go @@ -46,9 +46,9 @@ func init() { } func main() { - maxprocs.Set(maxprocs.Logger(func(string, ...interface{}) {})) + _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...interface{}) {})) if version { - fmt.Printf("Clash %s %s %s with %s %s\n", C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime) + fmt.Printf("Clash Plus Pro %s %s %s with %s %s\n", C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime) return } @@ -101,6 +101,12 @@ func main() { } sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) <-sigCh + + // clean up + log.Warnln("Clash clean up") + hub.CleanUp() + + log.Warnln("Clash shutting down") } diff --git a/rule/base.go b/rule/base.go index 0f2d9f29..96d8bfe5 100644 --- a/rule/base.go +++ b/rule/base.go @@ -2,6 +2,9 @@ package rules import ( "errors" + "net" + + C "github.com/Dreamacro/clash/constant" ) var ( @@ -18,3 +21,33 @@ func HasNoResolve(params []string) bool { } return false } + +func findNetwork(params []string) C.NetWork { + for _, p := range params { + if p == "tcp" { + return C.TCP + } else if p == "udp" { + return C.UDP + } + } + return C.ALLNet +} + +func findSourceIPs(params []string) []*net.IPNet { + var ips []*net.IPNet + for _, p := range params { + if p == noResolve || len(p) < 7 { + continue + } + _, ipnet, err := net.ParseCIDR(p) + if err != nil { + continue + } + ips = append(ips, ipnet) + } + + if len(ips) > 0 { + return ips + } + return nil +} diff --git a/rule/domain.go b/rule/domain.go index f23ab18d..42281b5d 100644 --- a/rule/domain.go +++ b/rule/domain.go @@ -7,8 +7,9 @@ import ( ) type Domain struct { - domain string - adapter string + domain string + adapter string + ruleExtra *C.RuleExtra } func (d *Domain) RuleType() C.RuleType { @@ -34,9 +35,14 @@ func (d *Domain) ShouldResolveIP() bool { return false } -func NewDomain(domain string, adapter string) *Domain { +func (d *Domain) RuleExtra() *C.RuleExtra { + return d.ruleExtra +} + +func NewDomain(domain string, adapter string, ruleExtra *C.RuleExtra) *Domain { return &Domain{ - domain: strings.ToLower(domain), - adapter: adapter, + domain: strings.ToLower(domain), + adapter: adapter, + ruleExtra: ruleExtra, } } diff --git a/rule/domain_keyword.go b/rule/domain_keyword.go index b3d6fbaa..933e2524 100644 --- a/rule/domain_keyword.go +++ b/rule/domain_keyword.go @@ -7,8 +7,9 @@ import ( ) type DomainKeyword struct { - keyword string - adapter string + keyword string + adapter string + ruleExtra *C.RuleExtra } func (dk *DomainKeyword) RuleType() C.RuleType { @@ -35,9 +36,14 @@ func (dk *DomainKeyword) ShouldResolveIP() bool { return false } -func NewDomainKeyword(keyword string, adapter string) *DomainKeyword { +func (dk *DomainKeyword) RuleExtra() *C.RuleExtra { + return dk.ruleExtra +} + +func NewDomainKeyword(keyword string, adapter string, ruleExtra *C.RuleExtra) *DomainKeyword { return &DomainKeyword{ - keyword: strings.ToLower(keyword), - adapter: adapter, + keyword: strings.ToLower(keyword), + adapter: adapter, + ruleExtra: ruleExtra, } } diff --git a/rule/domain_suffix.go b/rule/domain_suffix.go index a1f9f28f..0ea4cea6 100644 --- a/rule/domain_suffix.go +++ b/rule/domain_suffix.go @@ -7,8 +7,9 @@ import ( ) type DomainSuffix struct { - suffix string - adapter string + suffix string + adapter string + ruleExtra *C.RuleExtra } func (ds *DomainSuffix) RuleType() C.RuleType { @@ -35,9 +36,14 @@ func (ds *DomainSuffix) ShouldResolveIP() bool { return false } -func NewDomainSuffix(suffix string, adapter string) *DomainSuffix { +func (ds *DomainSuffix) RuleExtra() *C.RuleExtra { + return ds.ruleExtra +} + +func NewDomainSuffix(suffix string, adapter string, ruleExtra *C.RuleExtra) *DomainSuffix { return &DomainSuffix{ - suffix: strings.ToLower(suffix), - adapter: adapter, + suffix: strings.ToLower(suffix), + adapter: adapter, + ruleExtra: ruleExtra, } } diff --git a/rule/final.go b/rule/final.go index 91c0b9d9..532824b9 100644 --- a/rule/final.go +++ b/rule/final.go @@ -5,7 +5,8 @@ import ( ) type Match struct { - adapter string + adapter string + ruleExtra *C.RuleExtra } func (f *Match) RuleType() C.RuleType { @@ -28,8 +29,16 @@ func (f *Match) ShouldResolveIP() bool { return false } -func NewMatch(adapter string) *Match { +func (f *Match) RuleExtra() *C.RuleExtra { + return f.ruleExtra +} + +func NewMatch(adapter string, ruleExtra *C.RuleExtra) *Match { + if ruleExtra.SourceIPs == nil { + ruleExtra = nil + } return &Match{ - adapter: adapter, + adapter: adapter, + ruleExtra: ruleExtra, } } diff --git a/rule/geoip.go b/rule/geoip.go index 3b6fbe6b..1783b7b7 100644 --- a/rule/geoip.go +++ b/rule/geoip.go @@ -11,6 +11,7 @@ type GEOIP struct { country string adapter string noResolveIP bool + ruleExtra *C.RuleExtra } func (g *GEOIP) RuleType() C.RuleType { @@ -23,7 +24,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) bool { return false } - if strings.EqualFold(g.country, "LAN") { + if strings.EqualFold(g.country, "LAN") || C.TunBroadcastAddr.Equal(ip) { return ip.IsPrivate() } record, _ := mmdb.Instance().Country(ip) @@ -42,12 +43,21 @@ func (g *GEOIP) ShouldResolveIP() bool { return !g.noResolveIP } -func NewGEOIP(country string, adapter string, noResolveIP bool) *GEOIP { +func (g *GEOIP) RuleExtra() *C.RuleExtra { + return g.ruleExtra +} + +func (g *GEOIP) GetCountry() string { + return g.country +} + +func NewGEOIP(country string, adapter string, noResolveIP bool, ruleExtra *C.RuleExtra) (*GEOIP, error) { geoip := &GEOIP{ country: country, adapter: adapter, noResolveIP: noResolveIP, + ruleExtra: ruleExtra, } - return geoip + return geoip, nil } diff --git a/rule/geosite.go b/rule/geosite.go new file mode 100644 index 00000000..875320bd --- /dev/null +++ b/rule/geosite.go @@ -0,0 +1,70 @@ +package rules + +import ( + "fmt" + + "github.com/Dreamacro/clash/component/geodata" + "github.com/Dreamacro/clash/component/geodata/router" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + + _ "github.com/Dreamacro/clash/component/geodata/standard" +) + +type GEOSITE struct { + country string + adapter string + ruleExtra *C.RuleExtra + matcher *router.DomainMatcher +} + +func (gs *GEOSITE) RuleType() C.RuleType { + return C.GEOSITE +} + +func (gs *GEOSITE) Match(metadata *C.Metadata) bool { + if metadata.AddrType != C.AtypDomainName { + return false + } + + domain := metadata.Host + return gs.matcher.ApplyDomain(domain) +} + +func (gs *GEOSITE) Adapter() string { + return gs.adapter +} + +func (gs *GEOSITE) Payload() string { + return gs.country +} + +func (gs *GEOSITE) ShouldResolveIP() bool { + return false +} + +func (gs *GEOSITE) RuleExtra() *C.RuleExtra { + return gs.ruleExtra +} + +func (gs *GEOSITE) GetDomainMatcher() *router.DomainMatcher { + return gs.matcher +} + +func NewGEOSITE(country string, adapter string, ruleExtra *C.RuleExtra) (*GEOSITE, error) { + matcher, recordsCount, err := geodata.LoadGeoSiteMatcher(country) + if err != nil { + return nil, fmt.Errorf("load GeoSite data error, %s", err.Error()) + } + + log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, recordsCount) + + geoSite := &GEOSITE{ + country: country, + adapter: adapter, + ruleExtra: ruleExtra, + matcher: matcher, + } + + return geoSite, nil +} diff --git a/rule/ipcidr.go b/rule/ipcidr.go index ff3b83c4..8910110f 100644 --- a/rule/ipcidr.go +++ b/rule/ipcidr.go @@ -23,6 +23,7 @@ func WithIPCIDRNoResolve(noResolve bool) IPCIDROption { type IPCIDR struct { ipnet *net.IPNet adapter string + ruleExtra *C.RuleExtra isSourceIP bool noResolveIP bool } @@ -54,15 +55,20 @@ func (i *IPCIDR) ShouldResolveIP() bool { return !i.noResolveIP } -func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) { +func (i *IPCIDR) RuleExtra() *C.RuleExtra { + return i.ruleExtra +} + +func NewIPCIDR(s string, adapter string, ruleExtra *C.RuleExtra, opts ...IPCIDROption) (*IPCIDR, error) { _, ipnet, err := net.ParseCIDR(s) if err != nil { return nil, errPayload } ipcidr := &IPCIDR{ - ipnet: ipnet, - adapter: adapter, + ipnet: ipnet, + adapter: adapter, + ruleExtra: ruleExtra, } for _, o := range opts { diff --git a/rule/parser.go b/rule/parser.go index 6e78b8fa..60bffefb 100644 --- a/rule/parser.go +++ b/rule/parser.go @@ -12,29 +12,38 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) { parsed C.Rule ) + ruleExtra := &C.RuleExtra{ + Network: findNetwork(params), + SourceIPs: findSourceIPs(params), + } + switch tp { case "DOMAIN": - parsed = NewDomain(payload, target) + parsed = NewDomain(payload, target, ruleExtra) case "DOMAIN-SUFFIX": - parsed = NewDomainSuffix(payload, target) + parsed = NewDomainSuffix(payload, target, ruleExtra) case "DOMAIN-KEYWORD": - parsed = NewDomainKeyword(payload, target) + parsed = NewDomainKeyword(payload, target, ruleExtra) + case "GEOSITE": + parsed, parseErr = NewGEOSITE(payload, target, ruleExtra) case "GEOIP": noResolve := HasNoResolve(params) - parsed = NewGEOIP(payload, target, noResolve) + parsed, parseErr = NewGEOIP(payload, target, noResolve, ruleExtra) case "IP-CIDR", "IP-CIDR6": noResolve := HasNoResolve(params) - parsed, parseErr = NewIPCIDR(payload, target, WithIPCIDRNoResolve(noResolve)) + parsed, parseErr = NewIPCIDR(payload, target, ruleExtra, WithIPCIDRNoResolve(noResolve)) case "SRC-IP-CIDR": - parsed, parseErr = NewIPCIDR(payload, target, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true)) + parsed, parseErr = NewIPCIDR(payload, target, ruleExtra, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true)) case "SRC-PORT": - parsed, parseErr = NewPort(payload, target, true) + parsed, parseErr = NewPort(payload, target, true, ruleExtra) case "DST-PORT": - parsed, parseErr = NewPort(payload, target, false) + parsed, parseErr = NewPort(payload, target, false, ruleExtra) case "PROCESS-NAME": - parsed, parseErr = NewProcess(payload, target) + parsed, parseErr = NewProcess(payload, target, ruleExtra) + case "SCRIPT": + parsed, parseErr = NewScript(payload, target) case "MATCH": - parsed = NewMatch(target) + parsed = NewMatch(target, ruleExtra) default: parseErr = fmt.Errorf("unsupported rule type %s", tp) } diff --git a/rule/port.go b/rule/port.go index b6e8abb1..e978e28d 100644 --- a/rule/port.go +++ b/rule/port.go @@ -1,15 +1,24 @@ package rules import ( + "fmt" "strconv" + "strings" C "github.com/Dreamacro/clash/constant" ) +type portReal struct { + portStart int + portEnd int +} + type Port struct { - adapter string - port string - isSource bool + adapter string + port string + isSource bool + portList []portReal + ruleExtra *C.RuleExtra } func (p *Port) RuleType() C.RuleType { @@ -21,9 +30,9 @@ func (p *Port) RuleType() C.RuleType { func (p *Port) Match(metadata *C.Metadata) bool { if p.isSource { - return metadata.SrcPort == p.port + return p.matchPortReal(metadata.SrcPort) } - return metadata.DstPort == p.port + return p.matchPortReal(metadata.DstPort) } func (p *Port) Adapter() string { @@ -38,14 +47,79 @@ func (p *Port) ShouldResolveIP() bool { return false } -func NewPort(port string, adapter string, isSource bool) (*Port, error) { - _, err := strconv.ParseUint(port, 10, 16) +func (p *Port) RuleExtra() *C.RuleExtra { + return p.ruleExtra +} + +func (p *Port) matchPortReal(portRef string) bool { + port, err := strconv.Atoi(portRef) if err != nil { + return false + } + + var rs bool + for _, pr := range p.portList { + if pr.portEnd == -1 { + rs = port == pr.portStart + } else { + rs = port >= pr.portStart && port <= pr.portEnd + } + if rs { + return true + } + } + return false +} + +func NewPort(port string, adapter string, isSource bool, ruleExtra *C.RuleExtra) (*Port, error) { + ports := strings.Split(port, "/") + if len(ports) > 28 { + return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error()) + } + + var portList []portReal + for _, p := range ports { + if p == "" { + continue + } + + subPorts := strings.Split(p, "-") + subPortsLen := len(subPorts) + if subPortsLen > 2 { + return nil, errPayload + } + + portStart, err := strconv.Atoi(strings.Trim(subPorts[0], "[ ]")) + if err != nil || portStart < 0 || portStart > 65535 { + return nil, errPayload + } + + if subPortsLen == 1 { + portList = append(portList, portReal{portStart, -1}) + } else if subPortsLen == 2 { + portEnd, err1 := strconv.Atoi(strings.Trim(subPorts[1], "[ ]")) + if err1 != nil || portEnd < 0 || portEnd > 65535 { + return nil, errPayload + } + + shouldReverse := portStart > portEnd + if shouldReverse { + portList = append(portList, portReal{portEnd, portStart}) + } else { + portList = append(portList, portReal{portStart, portEnd}) + } + } + } + + if len(portList) == 0 { return nil, errPayload } + return &Port{ - adapter: adapter, - port: port, - isSource: isSource, + adapter: adapter, + port: port, + isSource: isSource, + portList: portList, + ruleExtra: ruleExtra, }, nil } diff --git a/rule/process.go b/rule/process.go index 638ec723..b60c46bb 100644 --- a/rule/process.go +++ b/rule/process.go @@ -14,8 +14,9 @@ import ( var processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64)) type Process struct { - adapter string - process string + adapter string + process string + ruleExtra *C.RuleExtra } func (ps *Process) RuleType() C.RuleType { @@ -23,6 +24,15 @@ func (ps *Process) RuleType() C.RuleType { } func (ps *Process) Match(metadata *C.Metadata) bool { + if metadata.Process != "" { + return strings.EqualFold(metadata.Process, ps.process) + } + + // ignore match in proxy type "tproxy" + if metadata.Type == C.TPROXY { + return false + } + key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort) cached, hit := processCache.Get(key) if !hit { @@ -42,7 +52,9 @@ func (ps *Process) Match(metadata *C.Metadata) bool { cached = name } - return strings.EqualFold(cached.(string), ps.process) + metadata.Process = cached.(string) + + return strings.EqualFold(metadata.Process, ps.process) } func (ps *Process) Adapter() string { @@ -57,9 +69,14 @@ func (ps *Process) ShouldResolveIP() bool { return false } -func NewProcess(process string, adapter string) (*Process, error) { +func (ps *Process) RuleExtra() *C.RuleExtra { + return ps.ruleExtra +} + +func NewProcess(process string, adapter string, ruleExtra *C.RuleExtra) (*Process, error) { return &Process{ - adapter: adapter, - process: process, + adapter: adapter, + process: process, + ruleExtra: ruleExtra, }, nil } diff --git a/rule/script.go b/rule/script.go new file mode 100644 index 00000000..b1ccb5fb --- /dev/null +++ b/rule/script.go @@ -0,0 +1,73 @@ +package rules + +import ( + "fmt" + "runtime" + "strings" + + S "github.com/Dreamacro/clash/component/script" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" +) + +type Script struct { + shortcut string + adapter string + shortcutFunction *S.PyObject +} + +func (s *Script) RuleType() C.RuleType { + return C.Script +} + +func (s *Script) Match(metadata *C.Metadata) bool { + rs, err := S.CallPyShortcut(s.shortcutFunction, metadata) + if err != nil { + log.Errorln("[Script] match rule error: %s", err.Error()) + return false + } + + return rs +} + +func (s *Script) Adapter() string { + return s.adapter +} + +func (s *Script) Payload() string { + return s.shortcut +} + +func (s *Script) ShouldResolveIP() bool { + return false +} + +func (s *Script) RuleExtra() *C.RuleExtra { + return nil +} + +func NewScript(shortcut string, adapter string) (*Script, error) { + shortcut = strings.ToLower(shortcut) + if !S.Py_IsInitialized() { + return nil, fmt.Errorf("load script shortcut [%s] failure, can't find any shortcuts in the config file", shortcut) + } + + shortcutFunction, err := S.LoadShortcutFunction(shortcut) + if err != nil { + return nil, fmt.Errorf("can't find script shortcut [%s] in the config file", shortcut) + } + + obj := &Script{ + shortcut: shortcut, + adapter: adapter, + shortcutFunction: shortcutFunction, + } + + runtime.SetFinalizer(obj, func(s *Script) { + s.shortcutFunction.Clear() + }) + + log.Infoln("Start initial script shortcut rule %s => %s", shortcut, adapter) + + return obj, nil +} diff --git a/test/config/vless-tls.json b/test/config/vless-tls.json new file mode 100644 index 00000000..ea20ec41 --- /dev/null +++ b/test/config/vless-tls.json @@ -0,0 +1,39 @@ +{ + "inbounds": [ + { + "port": 10002, + "listen": "0.0.0.0", + "protocol": "vless", + "settings": { + "clients": [ + { + "id": "b831381d-6324-4d53-ad4f-8cda48b30811", + "level": 0, + "email": "love@example.com" + } + ], + "decryption": "none" + }, + "streamSettings": { + "network": "tcp", + "security": "tls", + "tlsSettings": { + "certificates": [ + { + "certificateFile": "/etc/ssl/v2ray/fullchain.pem", + "keyFile": "/etc/ssl/v2ray/privkey.pem" + } + ] + } + } + } + ], + "outbounds": [ + { + "protocol": "freedom" + } + ], + "log": { + "loglevel": "debug" + } +} \ No newline at end of file diff --git a/test/config/vless-xtls.json b/test/config/vless-xtls.json new file mode 100644 index 00000000..b4381d61 --- /dev/null +++ b/test/config/vless-xtls.json @@ -0,0 +1,40 @@ +{ + "inbounds": [ + { + "port": 10002, + "listen": "0.0.0.0", + "protocol": "vless", + "settings": { + "clients": [ + { + "id": "b831381d-6324-4d53-ad4f-8cda48b30811", + "flow": "xtls-rprx-direct", + "level": 0, + "email": "love@example.com" + } + ], + "decryption": "none" + }, + "streamSettings": { + "network": "tcp", + "security": "xtls", + "xtlsSettings": { + "certificates": [ + { + "certificateFile": "/etc/ssl/v2ray/fullchain.pem", + "keyFile": "/etc/ssl/v2ray/privkey.pem" + } + ] + } + } + } + ], + "outbounds": [ + { + "protocol": "freedom" + } + ], + "log": { + "loglevel": "debug" + } +} \ No newline at end of file diff --git a/test/go.mod b/test/go.mod index a43e4f5c..2d2204e2 100644 --- a/test/go.mod +++ b/test/go.mod @@ -22,10 +22,12 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/gofrs/uuid v4.1.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/btree v1.0.1 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/insomniacslk/dhcp v0.0.0-20211026125128-ad197bcd36fd // indirect + github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -36,16 +38,20 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect + github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 // indirect + github.com/yaling888/go-lwip v0.0.0-20211103185822-c9d650538091 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect - google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect + golang.zx2c4.com/wireguard/windows v0.5.1 // indirect + google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect google.golang.org/grpc v1.42.0 // indirect - google.golang.org/protobuf v1.26.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gvisor.dev/gvisor v0.0.0-20211104052249-2de3450f76d6 // indirect ) diff --git a/test/go.sum b/test/go.sum index 061bb3a4..0584ce90 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,4 +1,5 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -10,17 +11,35 @@ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6T cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.88.0/go.mod h1:dnKwfYbP9hQhefiUvpbcAyoGSHUrOxR20JVElLiUvEY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= @@ -28,13 +47,20 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -62,7 +88,9 @@ github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5 github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -74,6 +102,7 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/bazelbuild/rules_go v0.27.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -89,6 +118,7 @@ github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7 github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -104,8 +134,10 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -134,6 +166,7 @@ github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go. github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -151,6 +184,7 @@ github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cE github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= @@ -217,6 +251,7 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1S github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -229,8 +264,10 @@ github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TT github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.10+incompatible h1:GKkP0T7U4ks6X3lmmHKC2QDprnpRJor2Z5a8m62R9ZM= github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= @@ -244,15 +281,20 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -277,11 +319,15 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -290,10 +336,12 @@ github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblf github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v4.1.0+incompatible h1:sIa2eCvUTwgjbqXrPLfNwUf9S3i3mpH1O1atV+iL/Wk= github.com/gofrs/uuid v4.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -312,6 +360,11 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -326,36 +379,63 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github/v35 v35.1.0/go.mod h1:s0515YVTI+IMrDoy9Y4pHt9ShGpzHvHO8rZ7L7acgvs= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210715191844-86eeefc3e471/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -376,12 +456,15 @@ github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FK github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -398,6 +481,7 @@ github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+ github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -420,14 +504,21 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 h1:dkEFEnGUg2z/FAPywWr4yfR/sWDQK76qn3J4Y5H2hJs= +github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99/go.mod h1:FWfSixjrLgtK+dHkDoN6lHMNhvER24gnjUZd/wt8Z9o= +github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= @@ -457,8 +548,10 @@ github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQB github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= @@ -473,12 +566,14 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= @@ -495,6 +590,7 @@ github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5X github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc90/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -511,6 +607,7 @@ github.com/oschwald/geoip2-golang v1.5.0 h1:igg2yQIrrcRccB1ytFXqBfOHCjXWIoMv85lV github.com/oschwald/geoip2-golang v1.5.0/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s= github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -519,6 +616,7 @@ github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= @@ -591,6 +689,7 @@ github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -605,6 +704,7 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -613,20 +713,31 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 h1:QHESTXtfgc1ABV+ArlbPVqUx9Ht5I0dDkYhxYoXFxNo= +github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs= +github.com/yaling888/go-lwip v0.0.0-20211103185822-c9d650538091 h1:uOaYhg8ue1gAzV7KNAz1uc/qvjEDRtl16nvTPYiaphM= +github.com/yaling888/go-lwip v0.0.0-20211103185822-c9d650538091/go.mod h1:Y+f95PkWh183q1oDJxdlxTHa2mpdHG5zvBhV0TUhhSY= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= @@ -641,23 +752,30 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -687,6 +805,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -695,6 +815,10 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -713,6 +837,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -725,14 +850,27 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211105192438-b53810dc28af h1:SMeNJG/vclJ5wyBBd4xupMsSJIHTd1coW9g7q6KOjmY= golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -740,22 +878,34 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -775,6 +925,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -800,41 +951,66 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 h1:G2DDmludOQZoWbpCr7OKDxnl478ZBGMcOhrv+ooX/Q4= golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0= +golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -844,6 +1020,7 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2M golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -876,14 +1053,38 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard/windows v0.5.1 h1:OnYw96PF+CsIMrqWo5QP3Q59q5hY1rFErk/yN3cS+JQ= +golang.zx2c4.com/wireguard/windows v0.5.1/go.mod h1:EApyTk/ZNrkbZjurHL1nleDYnsPpJYBO7LZEBCyDAHk= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -894,12 +1095,29 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.52.0/go.mod h1:Him/adpjt0sxtkWViy0b6xyKW/SD71CwdJ7HqJo7SrU= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -920,11 +1138,38 @@ google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210721163202-f1cecdd8b78a/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f h1:YORWxaStkWBnWgELOHTmDrqNlFXuVGEbhwbB5iK94bQ= +google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -937,12 +1182,25 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -954,8 +1212,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -991,21 +1250,29 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +gvisor.dev/gvisor v0.0.0-20211104052249-2de3450f76d6 h1:ZqN8yQG1UONNe/u1LJvvTg80BDevYvaRgmWPBlCT+0g= +gvisor.dev/gvisor v0.0.0-20211104052249-2de3450f76d6/go.mod h1:btyTBPTxT8AFMvW7yctFJ2nPCEDWZLpmKQEZ0gG+bbQ= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= +k8s.io/api v0.16.13/go.mod h1:QWu8UWSTiuQZMMeYjwLs6ILu5O74qKSJ0c+4vrchDxs= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apimachinery v0.16.13/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ= +k8s.io/apimachinery v0.16.14-rc.0/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.16.13/go.mod h1:UKvVT4cajC2iN7DCjLgT0KVY/cbY6DGdUCyRiIfws5M= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= @@ -1016,17 +1283,24 @@ k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/test/vless_test.go b/test/vless_test.go new file mode 100644 index 00000000..8bce51d6 --- /dev/null +++ b/test/vless_test.go @@ -0,0 +1,91 @@ +package main + +import ( + "fmt" + "testing" + "time" + + "github.com/Dreamacro/clash/adapter/outbound" + C "github.com/Dreamacro/clash/constant" + + "github.com/docker/docker/api/types/container" + "github.com/stretchr/testify/assert" +) + +func TestClash_VlessTLS(t *testing.T) { + cfg := &container.Config{ + Image: ImageVmess, + ExposedPorts: defaultExposedPorts, + } + hostCfg := &container.HostConfig{ + PortBindings: defaultPortBindings, + Binds: []string{ + fmt.Sprintf("%s:/etc/v2ray/config.json", C.Path.Resolve("vless-tls.json")), + fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")), + fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")), + }, + } + + id, err := startContainer(cfg, hostCfg, "vless-tls") + if err != nil { + assert.FailNow(t, err.Error()) + } + defer cleanContainer(id) + + proxy, err := outbound.NewVless(outbound.VlessOption{ + Name: "vless", + Server: localIP.String(), + Port: 10002, + UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", + TLS: true, + SkipCertVerify: true, + ServerName: "example.org", + UDP: false, + }) + if err != nil { + assert.FailNow(t, err.Error()) + } + + time.Sleep(waitTime) + testSuit(t, proxy) +} + +func TestClash_VlessXTLS(t *testing.T) { + cfg := &container.Config{ + Image: ImageXray, + ExposedPorts: defaultExposedPorts, + } + hostCfg := &container.HostConfig{ + PortBindings: defaultPortBindings, + Binds: []string{ + fmt.Sprintf("%s:/etc/xray/config.json", C.Path.Resolve("vless-xtls.json")), + fmt.Sprintf("%s:/etc/ssl/v2ray/fullchain.pem", C.Path.Resolve("example.org.pem")), + fmt.Sprintf("%s:/etc/ssl/v2ray/privkey.pem", C.Path.Resolve("example.org-key.pem")), + }, + } + + id, err := startContainer(cfg, hostCfg, "vless-xtls") + if err != nil { + assert.FailNow(t, err.Error()) + } + defer cleanContainer(id) + + proxy, err := outbound.NewVless(outbound.VlessOption{ + Name: "vless", + Server: localIP.String(), + Port: 10002, + UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", + TLS: true, + Flow: "xtls-rprx-direct", + //FlowShow: true, + SkipCertVerify: true, + ServerName: "example.org", + UDP: false, + }) + if err != nil { + assert.FailNow(t, err.Error()) + } + + time.Sleep(waitTime) + testSuit(t, proxy) +} diff --git a/transport/gun/gun_xtls.go b/transport/gun/gun_xtls.go new file mode 100644 index 00000000..d97dd7bf --- /dev/null +++ b/transport/gun/gun_xtls.go @@ -0,0 +1,56 @@ +// Modified from: https://github.com/Qv2ray/gun-lite +// License: MIT + +package gun + +import ( + "crypto/tls" + "fmt" + "net" + + xtls "github.com/xtls/go" + "golang.org/x/net/http2" +) + +func NewHTTP2XTLSClient(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport { + dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) { + pconn, err := dialFn(network, addr) + if err != nil { + return nil, err + } + + xtlsConfig := &xtls.Config{ + InsecureSkipVerify: cfg.InsecureSkipVerify, + ServerName: cfg.ServerName, + } + + cn := xtls.Client(pconn, xtlsConfig) + if err := cn.Handshake(); err != nil { + pconn.Close() + return nil, err + } + state := cn.ConnectionState() + if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { + cn.Close() + return nil, fmt.Errorf("http2: unexpected ALPN protocol %s, want %s", p, http2.NextProtoTLS) + } + return cn, nil + } + + return &http2.Transport{ + DialTLS: dialFunc, + TLSClientConfig: tlsConfig, + AllowHTTP: false, + DisableCompression: true, + PingTimeout: 0, + } +} + +func StreamGunWithXTLSConn(conn net.Conn, tlsConfig *tls.Config, cfg *Config) (net.Conn, error) { + dialFn := func(network, addr string) (net.Conn, error) { + return conn, nil + } + + transport := NewHTTP2XTLSClient(dialFn, tlsConfig) + return StreamGunWithTransport(transport, cfg) +} diff --git a/transport/ssr/protocol/auth_aes128_sha1.go b/transport/ssr/protocol/auth_aes128_sha1.go index 8ce57d28..d31fb9bf 100644 --- a/transport/ssr/protocol/auth_aes128_sha1.go +++ b/transport/ssr/protocol/auth_aes128_sha1.go @@ -154,7 +154,7 @@ func (a *authAES128) Encode(buf *bytes.Buffer, b []byte) error { } func (a *authAES128) DecodePacket(b []byte) ([]byte, error) { - if !bytes.Equal(a.hmac(a.userKey, b[:len(b)-4])[:4], b[len(b)-4:]) { + if !bytes.Equal(a.hmac(a.Key, b[:len(b)-4])[:4], b[len(b)-4:]) { return nil, errAuthAES128ChksumError } return b[:len(b)-4], nil diff --git a/tunnel/mode.go b/tunnel/mode.go index 69660721..bdf646f0 100644 --- a/tunnel/mode.go +++ b/tunnel/mode.go @@ -12,12 +12,14 @@ type TunnelMode int var ModeMapping = map[string]TunnelMode{ Global.String(): Global, Rule.String(): Rule, + Script.String(): Script, Direct.String(): Direct, } const ( Global TunnelMode = iota Rule + Script Direct ) @@ -61,6 +63,8 @@ func (m TunnelMode) String() string { return "global" case Rule: return "rule" + case Script: + return "script" case Direct: return "direct" default: diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index 1f5f1f9c..9f231547 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -77,6 +77,9 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R if rule != nil { t.trackerInfo.Rule = rule.RuleType().String() t.trackerInfo.RulePayload = rule.Payload() + //if rule.RuleType() == C.GEOSITE || rule.RuleType() == C.GEOIP { + // t.trackerInfo.Rule = t.trackerInfo.Rule + " (" + rule.Payload() + ")" + //} } manager.Join(t) @@ -134,6 +137,9 @@ func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, ru if rule != nil { ut.trackerInfo.Rule = rule.RuleType().String() ut.trackerInfo.RulePayload = rule.Payload() + //if rule.RuleType() == C.GEOSITE || rule.RuleType() == C.GEOIP { + // ut.trackerInfo.Rule = ut.trackerInfo.Rule + " (" + rule.Payload() + ")" + //} } manager.Join(ut) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index f00a7bd0..73b2b499 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -11,10 +11,12 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/nat" "github.com/Dreamacro/clash/component/resolver" + S "github.com/Dreamacro/clash/component/script" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" icontext "github.com/Dreamacro/clash/context" "github.com/Dreamacro/clash/log" + R "github.com/Dreamacro/clash/rule" "github.com/Dreamacro/clash/tunnel/statistic" ) @@ -32,6 +34,8 @@ var ( // default timeout for UDP session udpTimeout = 60 * time.Second + + preProcessCacheFinder, _ = R.NewProcess("", "", nil) ) func init() { @@ -141,7 +145,7 @@ func preHandleMetadata(metadata *C.Metadata) error { // redir-host should lookup the hosts metadata.DstIP = node.Data.(net.IP) } - } else if resolver.IsFakeIP(metadata.DstIP) { + } else if resolver.IsFakeIP(metadata.DstIP) && !C.TunBroadcastAddr.Equal(metadata.DstIP) { return fmt.Errorf("fake DNS record %s missing", metadata.DstIP) } } @@ -155,6 +159,8 @@ func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, r proxy = proxies["DIRECT"] case Global: proxy = proxies["GLOBAL"] + case Script: + proxy, err = matchScript(metadata) // Rule default: proxy, rule, err = match(metadata) @@ -235,13 +241,15 @@ func handleUDPConn(packet *inbound.PacketAdapter) { switch true { case rule != nil: - log.Infoln("[UDP] %s --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), rawPc.Chains().String()) + log.Infoln("[UDP] %s(%s) --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), rawPc.Chains().String()) + case mode == Script: + log.Infoln("[UDP] %s --> %s using SCRIPT %s", metadata.SourceAddress(), metadata.RemoteAddress(), rawPc.Chains().String()) case mode == Global: - log.Infoln("[UDP] %s --> %s using GLOBAL", metadata.SourceAddress(), metadata.RemoteAddress()) + log.Infoln("[UDP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) case mode == Direct: - log.Infoln("[UDP] %s --> %s using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) + log.Infoln("[UDP] %s(%s) --> %s using DIRECT", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) default: - log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) + log.Infoln("[UDP] %s(%s) --> %s doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) } go handleUDPToLocal(packet.UDPPacket, pc, key, fAddr) @@ -287,13 +295,15 @@ func handleTCPConn(connCtx C.ConnContext) { switch true { case rule != nil: - log.Infoln("[TCP] %s --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String()) + log.Infoln("[TCP] %s(%s) --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String()) + case mode == Script: + log.Infoln("[TCP] %s(%s) --> %s using SCRIPT %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), remoteConn.Chains().String()) case mode == Global: - log.Infoln("[TCP] %s --> %s using GLOBAL", metadata.SourceAddress(), metadata.RemoteAddress()) + log.Infoln("[TCP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) case mode == Direct: - log.Infoln("[TCP] %s --> %s using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) + log.Infoln("[TCP] %s(%s) --> %s using DIRECT", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) default: - log.Infoln("[TCP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) + log.Infoln("[TCP] %s(%s) --> %s doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress()) } handleSocket(connCtx, remoteConn) @@ -315,6 +325,9 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { resolved = true } + // preset process name and cache it + preProcessCacheFinder.Match(metadata) + for _, rule := range rules { if !resolved && shouldResolveIP(rule, metadata) { ip, err := resolver.ResolveIP(metadata.Host) @@ -337,9 +350,45 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { log.Debugln("%s UDP is not supported", adapter.Name()) continue } + + extra := rule.RuleExtra() + if extra != nil { + if extra.NotMatchNetwork(metadata.NetWork) { + continue + } + + if extra.NotMatchSourceIP(metadata.SrcIP) { + continue + } + } + return adapter, rule, nil } } - return proxies["DIRECT"], nil, nil + return proxies["REJECT"], nil, nil +} + +func matchScript(metadata *C.Metadata) (C.Proxy, error) { + configMux.RLock() + defer configMux.RUnlock() + + if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { + ip := node.Data.(net.IP) + metadata.DstIP = ip + } + + // preset process name and cache it + preProcessCacheFinder.Match(metadata) + + adapter, err := S.CallPyMainFunction(metadata) + if err != nil { + return nil, err + } + + if _, ok := proxies[adapter]; !ok { + return nil, fmt.Errorf("proxy [%s] not found by script", adapter) + } + + return proxies[adapter], nil }