[
  {
    "path": ".config/bypass.txt",
    "content": "# period for live reloading\nreload      10s\n\n# matcher reversed\n reverse     true\n\n*.example.com\n\n# this will match example.org and *.example.org\n.example.org\n\n# From IANA IPv4 Special-Purpose Address Registry\n# http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml\n\n0.0.0.0/8           # RFC1122: \"This host on this network\"\n10.0.0.0/8          # RFC1918: Private-Use\n100.64.0.0/10       # RFC6598: Shared Address Space\n127.0.0.0/8         # RFC1122: Loopback\n169.254.0.0/16      # RFC3927: Link Local\n172.16.0.0/12       # RFC1918: Private-Use\n192.0.0.0/24        # RFC6890: IETF Protocol Assignments\n192.0.2.0/24        # RFC5737: Documentation (TEST-NET-1)\n192.88.99.0/24      # RFC3068: 6to4 Relay Anycast\n192.168.0.0/16      # RFC1918: Private-Use\n198.18.0.0/15       # RFC2544: Benchmarking\n198.51.100.0/24     # RFC5737: Documentation (TEST-NET-2)\n203.0.113.0/24      # RFC5737: Documentation (TEST-NET-3)\n240.0.0.0/4         # RFC1112: Reserved\n255.255.255.255/32  # RFC0919: Limited Broadcast\n\n# From IANA Multicast Address Space Registry\n# http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml\n\n224.0.0.0/4         # RFC5771: Multicast/Reserved\n"
  },
  {
    "path": ".config/dns.txt",
    "content": "# resolver timeout, default 30s.\ntimeout     10s\n\n# resolver cache TTL, \n# minus value means that cache is disabled,\n# default to the TTL in DNS server response.\n# ttl         300s\n\n# period for live reloading\nreload      10s\n\n# ip[:port]  [protocol]  [hostname]\n\nhttps://1.0.0.1/dns-query\n1.1.1.1:853 tls     cloudflare-dns.com\n8.8.8.8\n8.8.8.8     tcp\n1.1.1.1     udp\n1.1.1.1:53  tcp"
  },
  {
    "path": ".config/gost.json",
    "content": "{\n    \"Retries\": 1,\n    \"Debug\": false,\n    \"ServeNodes\": [\n        \":12345\"\n    ],\n    \"ChainNodes\": [\n        \"http://:8080\"\n    ],\n\n    \"Routes\": [\n        {\n            \"Retries\": 1,\n            \"ServeNodes\": [\n                \"ws://:1443\"\n            ],\n            \"ChainNodes\": [\n                \"socks://:192.168.1.1:1080\"\n            ]\n        },\n        {\n            \"Retries\": 3,\n            \"ServeNodes\": [\n                \"quic://:443\"\n            ]\n        }\n    ]\n}"
  },
  {
    "path": ".config/hosts.txt",
    "content": "# period for live reloading\nreload      10s\n\n# The following lines are desirable for IPv4 capable hosts\n127.0.0.1       localhost\n\n# 127.0.1.1 is often used for the FQDN of the machine\n127.0.1.1       thishost.mydomain.org  thishost\n192.168.1.10    foo.mydomain.org       foo\n192.168.1.13    bar.mydomain.org       bar\n146.82.138.7    master.debian.org      master\n209.237.226.90  www.opensource.org\n\n# The following lines are desirable for IPv6 capable hosts\n::1             localhost ip6-localhost ip6-loopback\nff02::1         ip6-allnodes\nff02::2         ip6-allrouters\n"
  },
  {
    "path": ".config/kcp.json",
    "content": "{\n\t\"key\": \"it's a secrect\",\n\t\"crypt\": \"aes\",\n\t\"mode\": \"fast\",\n\t\"mtu\" : 1350,\n\t\"sndwnd\": 1024,\n\t\"rcvwnd\": 1024,\n\t\"datashard\": 10,\n\t\"parityshard\": 3,\n\t\"dscp\": 0,\n\t\"nocomp\": false,\n\t\"acknodelay\": false,\n\t\"nodelay\": 0,\n\t\"interval\": 40,\n\t\"resend\": 0,\n\t\"nc\": 0,\n\t\"sockbuf\": 4194304,\n\t\"keepalive\": 10,\n\t\"snmplog\": \"\",\n\t\"snmpperiod\": 60\n}"
  },
  {
    "path": ".config/peer.txt",
    "content": "# strategy for node selecting\nstrategy        random\n\nmax_fails       1\n\nfail_timeout    30s\n\n# period for live reloading\nreload          10s\n\n# peers\npeer    http://:18080\npeer    socks://:11080\npeer    ss://chacha20:123456@:18338"
  },
  {
    "path": ".config/probe_resist.txt",
    "content": "Hello World!"
  },
  {
    "path": ".config/secrets.txt",
    "content": "# period for live reloading\nreload          3s\n\n# username password\n\n$test.admin$ $123456$\n@test.admin@ @123456@\ntest.admin# #123456#\ntest.admin\\admin 123456\ntest001 123456\ntest002 12345678"
  },
  {
    "path": ".dockerignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\nrelease\ndebian\ndocs\n\n*.exe\n*.test\n\n*.bak\n\n.git\n.gitignore\nLICENSE\nVERSION\nREADME.md\nChangelog.md\nMakefile\ndocker-compose.yml"
  },
  {
    "path": ".github/workflows/buildx.yml",
    "content": "# ref: https://docs.docker.com/ci-cd/github-actions/\n# https://blog.oddbit.com/post/2020-09-25-building-multi-architecture-im/\n\nname: docker\n\non: \n  push:\n    branches:\n    - master\n    tags:\n    - 'v*'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Prepare\n      id: prepare\n      run: |\n        DOCKER_IMAGE=${{ secrets.DOCKER_IMAGE }}\n        VERSION=latest\n\n        # If this is git tag, use the tag name as a docker tag\n        if [[ $GITHUB_REF == refs/tags/* ]]; then\n          VERSION=${GITHUB_REF#refs/tags/v}\n        fi\n        TAGS=\"${DOCKER_IMAGE}:${VERSION}\"\n\n        # If the VERSION looks like a version number, assume that\n        # this is the most recent version of the image and also\n        # tag it 'latest'.\n        if [[ $VERSION =~ ^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$ ]]; then\n          MAJOR_VERSION=`echo $VERSION | awk '{split($0,a,\".\"); print a[1]}'`\n          MINOR_VERSION=`echo $VERSION | awk '{split($0,a,\".\"); print a[2]}'`\n          TAGS=\"$TAGS,${DOCKER_IMAGE}:${MAJOR_VERSION},${DOCKER_IMAGE}:${MAJOR_VERSION}.${MINOR_VERSION},${DOCKER_IMAGE}:latest\"\n        fi\n\n        # Set output parameters.\n        echo \"tags=${TAGS}\" >> $GITHUB_OUTPUT\n        echo \"docker_image=${DOCKER_IMAGE}\" >> $GITHUB_OUTPUT\n        echo \"docker_platforms=linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/s390x,linux/riscv64\" >> $GITHUB_OUTPUT\n\n    - name: Set up QEMU\n      uses: docker/setup-qemu-action@v3\n\n    - name: Set up Docker Buildx\n      id: buildx\n      uses: docker/setup-buildx-action@v3\n\n    - name: Environment\n      run: |\n        echo home=$HOME\n        echo git_ref=$GITHUB_REF\n        echo git_sha=$GITHUB_SHA\n        echo image=${{ steps.prepare.outputs.docker_image }}\n        echo tags=${{ steps.prepare.outputs.tags }}\n        echo platforms=${{ steps.prepare.outputs.docker_platforms }}\n        echo avail_platforms=${{ steps.buildx.outputs.platforms }}\n\n    - name: Login to DockerHub\n      if: github.event_name != 'pull_request'\n      uses: docker/login-action@v3\n      with:\n        username: ${{ secrets.DOCKER_USERNAME }}\n        password: ${{ secrets.DOCKER_PASSWORD }}\n\n    - name: Buildx and push\n      uses: docker/build-push-action@v6\n      with:\n        platforms: ${{ steps.prepare.outputs.docker_platforms }}\n        push: true\n        tags: ${{ steps.prepare.outputs.tags }}"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: goreleaser\n\non:\n  push:\n    # run only against tags\n    tags:\n      - 'v*'\n\npermissions:\n  contents: write\n  # packages: write\n  # issues: write\n\njobs:\n  goreleaser:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n      - run: git fetch --force --tags\n      - uses: actions/setup-go@v3\n        with:\n          go-version: '1.22'\n          cache: true\n      # More assembly might be required: Docker logins, GPG, etc. It all depends\n      # on your needs.\n      - uses: goreleaser/goreleaser-action@v4\n        with:\n          # either 'goreleaser' (default) or 'goreleaser-pro':\n          distribution: goreleaser\n          version: latest\n          args: release --clean\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          # Your GoReleaser Pro key, if you are using the 'goreleaser-pro'\n          # distribution:\n          # GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\nrelease\ndebian\nbin\ndist/\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.swp\n*.swo\n\n*.exe\n*.test\n\n*.bak\n\n.vscode/\ncmd/gost/gost\n"
  },
  {
    "path": ".goreleaser.yaml",
    "content": "# This is an example .goreleaser.yml file with some sensible defaults.\n# Make sure to check the documentation at https://goreleaser.com\nbefore:\n  hooks:\n    # You may remove this if you don't use go modules.\n    - go mod tidy\n    # you may remove this if you don't need go generate\n    # - go generate ./...\nbuilds:\n  - env:\n      - CGO_ENABLED=0\n    main: ./cmd/gost\n    targets:\n      - darwin_amd64\n      - darwin_arm64\n      - linux_386\n      - linux_amd64\n      - linux_amd64_v3\n      - linux_arm_5\n      - linux_arm_6\n      - linux_arm_7\n      - linux_arm64\n      - linux_mips_softfloat\n      - linux_mips_hardfloat\n      - linux_mipsle_softfloat\n      - linux_mipsle_hardfloat\n      - linux_mips64\n      - linux_mips64le\n      - linux_s390x\n      - linux_riscv64\n      - freebsd_386\n      - freebsd_amd64\n      - windows_386\n      - windows_amd64\n      - windows_amd64_v3\n      - windows_arm64\n\narchives:\n  - format: tar.gz\n    # use zip for windows archives\n    format_overrides:\n    - goos: windows\n      format: zip\nchecksum:\n  name_template: 'checksums.txt'\nsnapshot:\n  name_template: \"{{ incpatch .Version }}-next\"\nchangelog:\n  sort: asc\n  filters:\n    exclude:\n      - '^docs:'\n      - '^test:'\n\n# The lines beneath this are called `modelines`. See `:help modeline`\n# Feel free to remove those if you don't want/use them.\n# yaml-language-server: $schema=https://goreleaser.com/static/schema.json\n# vim: set ts=2 sw=2 tw=0 fo=cnqoj\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: go\nsudo: false\ngo:\n  - 1.x\n\ninstall: true\nscript:\n  - go test -race -v -coverprofile=coverage.txt -covermode=atomic\n  - cd cmd/gost && go build\n\nafter_success:\n  - bash <(curl -s https://codecov.io/bash)\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx\n\nFROM --platform=$BUILDPLATFORM golang:1.23-alpine3.20 AS builder\n\nCOPY --from=xx / /\n\nARG TARGETPLATFORM\n\nRUN xx-info env\n\nENV CGO_ENABLED=0\n\nENV XX_VERIFY_STATIC=1\n\nWORKDIR /app\n\nCOPY . .\n\nRUN cd cmd/gost && \\\n    xx-go build && \\\n    xx-verify gost\n\nFROM alpine:3.20\n\n# add iptables for tun/tap\nRUN apk add --no-cache iptables\n\nWORKDIR /bin/\n\nCOPY --from=builder /app/cmd/gost/gost .\n\nENTRYPOINT [\"/bin/gost\"]"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016 ginuerzh\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "NAME=gost\nBINDIR=bin\nVERSION=$(shell cat gost.go | grep 'Version =' | sed 's/.*\\\"\\(.*\\)\\\".*/\\1/g')\nGOBUILD=CGO_ENABLED=0 go build --ldflags=\"-s -w\" -v -x -a\nGOFILES=cmd/gost/*.go\n\nPLATFORM_LIST = \\\n\tdarwin-amd64 \\\n\tdarwin-arm64 \\\n\tlinux-386 \\\n\tlinux-amd64 \\\n\tlinux-armv5 \\\n\tlinux-armv6 \\\n\tlinux-armv7 \\\n\tlinux-armv8 \\\n\tlinux-mips-softfloat \\\n\tlinux-mips-hardfloat \\\n\tlinux-mipsle-softfloat \\\n\tlinux-mipsle-hardfloat \\\n\tlinux-mips64 \\\n\tlinux-mips64le \\\n\tlinux-s390x \\\n\tlinux-riscv64 \\\n\tfreebsd-386 \\\n\tfreebsd-amd64\n\nWINDOWS_ARCH_LIST = \\\n\twindows-386 \\\n\twindows-amd64 \\\n\twindows-arm64\n\nall: linux-amd64 darwin-amd64 windows-amd64 # Most used\n\ndarwin-amd64:\n\tGOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\ndarwin-arm64:\n\tGOARCH=arm64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-386:\n\tGOARCH=386 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-amd64:\n\tGOARCH=amd64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-armv5:\n\tGOARCH=arm GOOS=linux GOARM=5 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-armv6:\n\tGOARCH=arm GOOS=linux GOARM=6 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-armv7:\n\tGOARCH=arm GOOS=linux GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-armv8:\n\tGOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-mips-softfloat:\n\tGOARCH=mips GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-mips-hardfloat:\n\tGOARCH=mips GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-mipsle-softfloat:\n\tGOARCH=mipsle GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-mipsle-hardfloat:\n\tGOARCH=mipsle GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-mips64:\n\tGOARCH=mips64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-mips64le:\n\tGOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-s390x:\n\tGOARCH=s390x GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nlinux-riscv64:\n\tGOARCH=riscv64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nfreebsd-386:\n\tGOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nfreebsd-amd64:\n\tGOARCH=amd64 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)\n\nwindows-386:\n\tGOARCH=386 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe $(GOFILES)\n\nwindows-amd64:\n\tGOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe $(GOFILES)\n\nwindows-arm64:\n\tGOARCH=arm64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe $(GOFILES)\n\ngz_releases=$(addsuffix .gz, $(PLATFORM_LIST))\nzip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST))\n\n$(gz_releases): %.gz : %\n\tchmod +x $(BINDIR)/$(NAME)-$(basename $@)\n\tgzip -f -S -$(VERSION).gz $(BINDIR)/$(NAME)-$(basename $@)\n\n$(zip_releases): %.zip : %\n\tzip -m -j $(BINDIR)/$(NAME)-$(basename $@)-$(VERSION).zip $(BINDIR)/$(NAME)-$(basename $@).exe\n\nall-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST)\n\nreleases: $(gz_releases) $(zip_releases)\nclean:\n\trm $(BINDIR)/*\n"
  },
  {
    "path": "README.md",
    "content": "GO Simple Tunnel\n======\n\n### GO语言实现的安全隧道\n\n[![GoDoc](https://godoc.org/github.com/ginuerzh/gost?status.svg)](https://godoc.org/github.com/ginuerzh/gost)\n[![Go Report Card](https://goreportcard.com/badge/github.com/ginuerzh/gost)](https://goreportcard.com/report/github.com/ginuerzh/gost)\n[![codecov](https://codecov.io/gh/ginuerzh/gost/branch/master/graphs/badge.svg)](https://codecov.io/gh/ginuerzh/gost/branch/master)\n[![GitHub release](https://img.shields.io/github/release/ginuerzh/gost.svg)](https://github.com/ginuerzh/gost/releases/latest)\n[![Docker](https://img.shields.io/docker/pulls/ginuerzh/gost.svg)](https://hub.docker.com/r/ginuerzh/gost/)\n[![gost](https://snapcraft.io/gost/badge.svg)](https://snapcraft.io/gost)\n \n[English README](README_en.md)\n\n特性\n------\n\n* 多端口监听\n* 可设置转发代理，支持多级转发(代理链)\n* 支持标准HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5代理协议\n* Web代理支持[探测防御](https://v2.gost.run/probe_resist/)\n* [支持多种隧道类型](https://v2.gost.run/configuration/)\n* [SOCKS5代理支持TLS协商加密](https://v2.gost.run/socks/)\n* [Tunnel UDP over TCP](https://v2.gost.run/socks/)\n* [TCP/UDP透明代理](https://v2.gost.run/redirect/)\n* [本地/远程TCP/UDP端口转发](https://v2.gost.run/port-forwarding/)\n* [支持Shadowsocks(TCP/UDP)协议](https://v2.gost.run/ss/)\n* [支持SNI代理](https://v2.gost.run/sni/)\n* [权限控制](https://v2.gost.run/permission/)\n* [负载均衡](https://v2.gost.run/load-balancing/)\n* [路由控制](https://v2.gost.run/bypass/)\n* DNS[解析](https://v2.gost.run/resolver/)和[代理](https://v2.gost.run/dns/)\n* [TUN/TAP设备](https://v2.gost.run/tuntap/)\n\nWiki站点: [v2.gost.run](https://v2.gost.run)\n\nTelegram讨论群: <https://t.me/gogost>\n\nGoogle讨论组: <https://groups.google.com/d/forum/go-gost>\n\nGOST v3 <https://gost.run>\n\n安装\n------\n\n#### 二进制文件\n\n<https://github.com/ginuerzh/gost/releases>\n\n#### 源码编译\n\n```bash\ngit clone https://github.com/ginuerzh/gost.git\ncd gost/cmd/gost\ngo build\n```\n\n#### Docker\n\n```bash\ndocker run --rm ginuerzh/gost -V\n```\n\n#### Homebrew\n\n```bash\nbrew install gost\n```\n\n#### Ubuntu商店\n\n\n```bash\nsudo snap install core\nsudo snap install gost\n```\n\n快速上手\n------\n\n#### 不设置转发代理\n\n<img src=\"https://ginuerzh.github.io/images/gost_01.png\" />\n\n* 作为标准HTTP/SOCKS5代理\n\n```bash\ngost -L=:8080\n```\n\n* 设置代理认证信息\n\n```bash\ngost -L=admin:123456@localhost:8080\n```\n\n* 多端口监听\n\n```bash\ngost -L=http2://:443 -L=socks5://:1080 -L=ss://aes-128-cfb:123456@:8338\n```\n\n#### 设置转发代理\n\n<img src=\"https://ginuerzh.github.io/images/gost_02.png\" />\n\n```bash\ngost -L=:8080 -F=192.168.1.1:8081\n```\n\n* 转发代理认证\n\n```bash\ngost -L=:8080 -F=http://admin:123456@192.168.1.1:8081\n```\n\n#### 设置多级转发代理(代理链)\n\n<img src=\"https://ginuerzh.github.io/images/gost_03.png\" />\n\n```bash\ngost -L=:8080 -F=quic://192.168.1.1:6121 -F=socks5+wss://192.168.1.2:1080 -F=http2://192.168.1.3:443 ... -F=a.b.c.d:NNNN\n```\n\ngost按照-F设置的顺序通过代理链将请求最终转发给a.b.c.d:NNNN处理，每一个转发代理可以是任意HTTP/HTTPS/HTTP2/SOCKS4/SOCKS5/Shadowsocks类型代理。\n\n#### 本地端口转发(TCP)\n\n```bash\ngost -L=tcp://:2222/192.168.1.1:22 [-F=...]\n```\n\n将本地TCP端口2222上的数据(通过代理链)转发到192.168.1.1:22上。当代理链末端(最后一个-F参数)为SSH转发通道类型时，gost会直接使用SSH的本地端口转发功能:\n\n```bash\ngost -L=tcp://:2222/192.168.1.1:22 -F forward+ssh://:2222\n```\n\n#### 本地端口转发(UDP)\n\n```bash\ngost -L=udp://:5353/192.168.1.1:53?ttl=60 [-F=...]\n```\n\n将本地UDP端口5353上的数据(通过代理链)转发到192.168.1.1:53上。\n每条转发通道都有超时时间，当超过此时间，且在此时间段内无任何数据交互，则此通道将关闭。可以通过`ttl`参数来设置超时时间，默认值为60秒。\n\n**注:** 转发UDP数据时，如果有代理链，则代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理，gost会使用UDP over TCP方式进行转发。\n\n#### 远程端口转发(TCP)\n\n```bash\ngost -L=rtcp://:2222/192.168.1.1:22 [-F=... -F=socks5://172.24.10.1:1080]\n```\n将172.24.10.1:2222上的数据(通过代理链)转发到192.168.1.1:22上。当代理链末端(最后一个-F参数)为SSH转发通道类型时，gost会直接使用SSH的远程端口转发功能:\n\n```bash\ngost -L=rtcp://:2222/192.168.1.1:22 -F forward+ssh://:2222\n```\n\n#### 远程端口转发(UDP)\n\n```bash\ngost -L=rudp://:5353/192.168.1.1:53?ttl=60 [-F=... -F=socks5://172.24.10.1:1080]\n```\n将172.24.10.1:5353上的数据(通过代理链)转发到192.168.1.1:53上。\n每条转发通道都有超时时间，当超过此时间，且在此时间段内无任何数据交互，则此通道将关闭。可以通过`ttl`参数来设置超时时间，默认值为60秒。\n\n**注:** 转发UDP数据时，如果有代理链，则代理链的末端(最后一个-F参数)必须是GOST SOCKS5类型代理，gost会使用UDP-over-TCP方式进行转发。\n\n#### HTTP2\n\ngost的HTTP2支持两种模式：\n* 作为标准的HTTP2代理，并向下兼容HTTPS代理。\n* 作为通道传输其他协议。\n\n##### 代理模式\n服务端:\n```bash\ngost -L=http2://:443\n```\n客户端:\n```bash\ngost -L=:8080 -F=http2://server_ip:443\n```\n\n##### 通道模式\n服务端:\n```bash\ngost -L=h2://:443\n```\n客户端:\n```bash\ngost -L=:8080 -F=h2://server_ip:443\n```\n\n#### QUIC\ngost对QUIC的支持是基于[quic-go](https://github.com/quic-go/quic-go)库。\n\n服务端:\n```bash\ngost -L=quic://:6121\n```\n\n客户端:\n```bash\ngost -L=:8080 -F=quic://server_ip:6121\n```\n\n**注：** QUIC模式只能作为代理链的第一个节点。\n\n#### KCP\ngost对KCP的支持是基于[kcp-go](https://github.com/xtaci/kcp-go)和[kcptun](https://github.com/xtaci/kcptun)库。\n\n服务端:\n```bash\ngost -L=kcp://:8388\n```\n\n客户端:\n```bash\ngost -L=:8080 -F=kcp://server_ip:8388\n```\n\ngost会自动加载当前工作目录中的kcp.json(如果存在)配置文件，或者可以手动通过参数指定配置文件路径：\n```bash\ngost -L=kcp://:8388?c=/path/to/conf/file\n```\n\n**注：** KCP模式只能作为代理链的第一个节点。\n\n#### SSH\n\ngost的SSH支持两种模式：\n* 作为转发通道，配合本地/远程TCP端口转发使用。\n* 作为通道传输其他协议。\n\n##### 转发模式\n服务端:\n```bash\ngost -L=forward+ssh://:2222\n```\n客户端:\n```bash\ngost -L=rtcp://:1222/:22 -F=forward+ssh://server_ip:2222\n```\n\n##### 通道模式\n服务端:\n```bash\ngost -L=ssh://:2222\n```\n客户端:\n```bash\ngost -L=:8080 -F=ssh://server_ip:2222?ping=60\n```\n\n可以通过`ping`参数设置心跳包发送周期，单位为秒。默认不发送心跳包。\n\n\n#### 透明代理\n基于iptables的透明代理。\n\n```bash\ngost -L=redirect://:12345 -F=http2://server_ip:443\n```\n\n#### obfs4\n此功能由[@isofew](https://github.com/isofew)贡献。\n\n服务端:\n```bash\ngost -L=obfs4://:443\n```\n\n当服务端运行后会在控制台打印出连接地址供客户端使用:\n```\nobfs4://:443/?cert=4UbQjIfjJEQHPOs8vs5sagrSXx1gfrDCGdVh2hpIPSKH0nklv1e4f29r7jb91VIrq4q5Jw&iat-mode=0\n```\n\n客户端:\n```\ngost -L=:8888 -F='obfs4://server_ip:443?cert=4UbQjIfjJEQHPOs8vs5sagrSXx1gfrDCGdVh2hpIPSKH0nklv1e4f29r7jb91VIrq4q5Jw&iat-mode=0'\n```\n\n加密机制\n------\n\n#### HTTP\n\n对于HTTP可以使用TLS加密整个通讯过程，即HTTPS代理：\n\n服务端:\n\n```bash\ngost -L=https://:443\n```\n客户端:\n\n```bash\ngost -L=:8080 -F=http+tls://server_ip:443\n```\n\n#### HTTP2\n\ngost的HTTP2代理模式仅支持使用TLS加密的HTTP2协议，不支持明文HTTP2传输。\n\ngost的HTTP2通道模式支持加密(h2)和明文(h2c)两种模式。\n\n#### SOCKS5\n\ngost支持标准SOCKS5协议的no-auth(0x00)和user/pass(0x02)方法，并在此基础上扩展了两个：tls(0x80)和tls-auth(0x82)，用于数据加密。\n\n服务端:\n\n```bash\ngost -L=socks5://:1080\n```\n\n客户端:\n\n```bash\ngost -L=:8080 -F=socks5://server_ip:1080\n```\n\n如果两端都是gost(如上)则数据传输会被加密(协商使用tls或tls-auth方法)，否则使用标准SOCKS5进行通讯(no-auth或user/pass方法)。\n\n#### Shadowsocks\ngost对shadowsocks的支持是基于[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)库。\n\n服务端:\n\n```bash\ngost -L=ss://chacha20:123456@:8338\n```\n客户端:\n\n```bash\ngost -L=:8080 -F=ss://chacha20:123456@server_ip:8338\n```\n\n##### Shadowsocks UDP relay\n\n目前仅服务端支持UDP Relay。\n\n服务端:\n\n```bash\ngost -L=ssu://chacha20:123456@:8338\n```\n\n#### TLS\ngost内置了TLS证书，如果需要使用其他TLS证书，有两种方法：\n* 在gost运行目录放置cert.pem(公钥)和key.pem(私钥)两个文件即可，gost会自动加载运行目录下的cert.pem和key.pem文件。\n* 使用参数指定证书文件路径：\n```bash\ngost -L=\"http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file\"\n```\n\n对于客户端可以通过`secure`参数开启服务器证书和域名校验:\n```bash\ngost -L=:8080 -F=\"http2://server_domain_name:443?secure=true\"\n```\n\n对于客户端可以指定CA证书进行[证书锁定](https://en.wikipedia.org/wiki/Transport_Layer_Security#Certificate_pinning)(Certificate Pinning):\n```bash\ngost -L=:8080 -F=\"http2://:443?ca=ca.pem\"\n```\n证书锁定功能由[@sheerun](https://github.com/sheerun)贡献\n"
  },
  {
    "path": "README_en.md",
    "content": "gost - GO Simple Tunnel\n======\n \n### A simple security tunnel written in Golang\n\n[![GoDoc](https://godoc.org/github.com/ginuerzh/gost?status.svg)](https://godoc.org/github.com/ginuerzh/gost)\n[![Go Report Card](https://goreportcard.com/badge/github.com/ginuerzh/gost)](https://goreportcard.com/report/github.com/ginuerzh/gost)\n[![codecov](https://codecov.io/gh/ginuerzh/gost/branch/master/graphs/badge.svg)](https://codecov.io/gh/ginuerzh/gost/branch/master)\n[![GitHub release](https://img.shields.io/github/release/ginuerzh/gost.svg)](https://github.com/ginuerzh/gost/releases/latest)\n[![Docker](https://img.shields.io/docker/pulls/ginuerzh/gost.svg)](https://hub.docker.com/r/ginuerzh/gost/)\n[![gost](https://snapcraft.io/gost/badge.svg)](https://snapcraft.io/gost)\n \nFeatures\n------\n* Listening on multiple ports\n* Multi-level forward proxy - proxy chain\n* Standard HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5 proxy protocols support\n* [Probing resistance](https://v2.gost.run/en/probe_resist/) support for web proxy\n* [Support multiple tunnel types](https://v2.gost.run/en/configuration/)\n* [TLS encryption via negotiation support for SOCKS5 proxy](https://v2.gost.run/en/socks/)\n* [Tunnel UDP over TCP](https://v2.gost.run/en/socks/)\n* [TCP/UDP Transparent proxy](https://v2.gost.run/en/redirect/)\n* [Local/remote TCP/UDP port forwarding](https://v2.gost.run/en/port-forwarding/)\n* [Shadowsocks protocol](https://v2.gost.run/en/ss/)\n* [SNI proxy](https://v2.gost.run/en/sni/)\n* [Permission control](https://v2.gost.run/en/permission/)\n* [Load balancing](https://v2.gost.run/en/load-balancing/)\n* [Routing control](https://v2.gost.run/en/bypass/)\n* DNS [resolver](https://v2.gost.run/resolver/) and [proxy](https://v2.gost.run/dns/)\n* [TUN/TAP device](https://v2.gost.run/en/tuntap/)\n\nWiki: [v2.gost.run](https://v2.gost.run/en/)\n\nTelegram group: <https://t.me/gogost>\n\nGoogle group: <https://groups.google.com/d/forum/go-gost>\n\nGOST v3: <https://gost.run>\n\nInstallation\n------\n\n#### Binary files\n\n<https://github.com/ginuerzh/gost/releases>\n\n#### From source\n\n```bash\ngit clone https://github.com/ginuerzh/gost.git\ncd gost/cmd/gost\ngo build\n```\n\n#### Docker\n\n```bash\ndocker run --rm ginuerzh/gost -V\n```\n\n#### Homebrew\n\n```bash\nbrew install gost\n```\n\n#### Ubuntu store\n\n```bash\nsudo snap install core\nsudo snap install gost\n```\n\nGetting started\n------\n\n#### No forward proxy\n\n<img src=\"https://ginuerzh.github.io/images/gost_01.png\" />\n\n* Standard HTTP/SOCKS5 proxy\n\n```bash\ngost -L=:8080\n```\n\n* Proxy authentication\n\n```bash\ngost -L=admin:123456@localhost:8080\n```\n\n* Multiple sets of authentication information\n\n```bash\ngost -L=localhost:8080?secrets=secrets.txt\n```\n\nThe secrets parameter allows you to set multiple authentication information for HTTP/SOCKS5 proxies, the format is:\n\n```plain\n# username password\n\ntest001 123456\ntest002 12345678\n```\n\n* Listen on multiple ports\n\n```bash\ngost -L=http2://:443 -L=socks5://:1080 -L=ss://aes-128-cfb:123456@:8338\n```\n\n#### Forward proxy\n\n<img src=\"https://ginuerzh.github.io/images/gost_02.png\" />\n\n```bash\ngost -L=:8080 -F=192.168.1.1:8081\n```\n\n* Forward proxy authentication\n\n```bash\ngost -L=:8080 -F=http://admin:123456@192.168.1.1:8081\n```\n\n#### Multi-level forward proxy\n\n<img src=\"https://ginuerzh.github.io/images/gost_03.png\" />\n\n```bash\ngost -L=:8080 -F=quic://192.168.1.1:6121 -F=socks5+wss://192.168.1.2:1080 -F=http2://192.168.1.3:443 ... -F=a.b.c.d:NNNN\n```\n\nGost forwards the request to a.b.c.d:NNNN through the proxy chain in the order set by -F, \neach forward proxy can be any HTTP/HTTPS/HTTP2/SOCKS4/SOCKS5/Shadowsocks type.\n\n#### Local TCP port forwarding\n\n```bash\ngost -L=tcp://:2222/192.168.1.1:22 [-F=...]\n```\n\nThe data on the local TCP port 2222 is forwarded to 192.168.1.1:22 (through the proxy chain). If the last node of the chain (the last -F parameter) is a SSH forwad tunnel, then gost will use the local port forwarding function of SSH directly:\n\n```bash\ngost -L=tcp://:2222/192.168.1.1:22 -F forward+ssh://:2222\n```\n\n#### Local UDP port forwarding\n\n```bash\ngost -L=udp://:5353/192.168.1.1:53?ttl=60 [-F=...]\n```\n\nThe data on the local UDP port 5353 is forwarded to 192.168.1.1:53 (through the proxy chain). \nEach forwarding channel has a timeout period. When this time is exceeded and there is no data interaction during this time period, the channel will be closed. The timeout value can be set by the `ttl` parameter. The default value is 60 seconds.\n\n**NOTE:** When forwarding UDP data, if there is a proxy chain, the end of the chain (the last -F parameter) must be gost SOCKS5 proxy, gost will use UDP-over-TCP to forward data.\n\n#### Remote TCP port forwarding\n\n```bash\ngost -L=rtcp://:2222/192.168.1.1:22 [-F=... -F=socks5://172.24.10.1:1080]\n```\n\nThe data on 172.24.10.1:2222 is forwarded to 192.168.1.1:22 (through the proxy chain). If the last node of the chain (the last -F parameter) is a SSH tunnel, then gost will use the remote port forwarding function of SSH directly:\n\n```bash\ngost -L=rtcp://:2222/192.168.1.1:22 -F forward+ssh://:2222\n```\n\n#### Remote UDP port forwarding\n\n```bash\ngost -L=rudp://:5353/192.168.1.1:53?ttl=60 [-F=... -F=socks5://172.24.10.1:1080]\n```\n\nThe data on 172.24.10.1:5353 is forwarded to 192.168.1.1:53 (through the proxy chain).\nEach forwarding channel has a timeout period. When this time is exceeded and there is no data interaction during this time period, the channel will be closed. The timeout value can be set by the `ttl` parameter. The default value is 60 seconds.\n\n**NOTE:** When forwarding UDP data, if there is a proxy chain, the end of the chain (the last -F parameter) must be gost SOCKS5 proxy, gost will use UDP-over-TCP to forward data.\n\n#### HTTP2\n\nGost HTTP2 supports two modes:\n\n* As a standard HTTP2 proxy, and backwards-compatible with the HTTPS proxy.\n\n* As a transport tunnel.\n\n##### Standard proxy\n\nServer:\n\n```bash\ngost -L=http2://:443\n```\n\nClient:\n\n```bash\ngost -L=:8080 -F=http2://server_ip:443?ping=30\n```\n\n##### Tunnel \n\nServer:\n\n```bash\ngost -L=h2://:443\n```\n\nClient:\n\n```bash\ngost -L=:8080 -F=h2://server_ip:443\n```\n\n#### QUIC\n\nSupport for QUIC is based on library [quic-go](https://github.com/quic-go/quic-go).\n\nServer:\n\n```bash\ngost -L=quic://:6121\n```\n\nClient:\n\n```bash\ngost -L=:8080 -F=quic://server_ip:6121\n```\n\n**NOTE:** QUIC node can only be used as the first node of the proxy chain.\n\n#### KCP\nSupport for KCP is based on libraries [kcp-go](https://github.com/xtaci/kcp-go) and [kcptun](https://github.com/xtaci/kcptun).\n\nServer:\n\n```bash\ngost -L=kcp://:8388\n```\n\nClient:\n\n```bash\ngost -L=:8080 -F=kcp://server_ip:8388\n```\n\nGost will automatically load kcp.json configuration file from current working directory if exists, \nor you can use the parameter to specify the path to the file.\n\n```bash\ngost -L=kcp://:8388?c=/path/to/conf/file\n```\n\n**NOTE:** KCP node can only be used as the first node of the proxy chain.\n\n#### SSH\n\nGost SSH supports two modes:\n\n* As a forward tunnel, used by local/remote TCP port forwarding.\n\n* As a transport tunnel.\n\n\n##### Forward tunnel\n\nServer:\n\n```bash\ngost -L=forward+ssh://:2222\n```\n\nClient:\n\n```bash\ngost -L=rtcp://:1222/:22 -F=forward+ssh://server_ip:2222\n```\n\n##### Transport tunnel\nServer:\n\n```bash\ngost -L=ssh://:2222\n```\nClient:\n\n```bash\ngost -L=:8080 -F=ssh://server_ip:2222?ping=60\n```\n\nThe client supports the ping parameter to enable heartbeat detection (which is disabled by default). Parameter value represents heartbeat interval seconds.\n\n#### Transparent proxy\nIptables-based transparent proxy\n\n```bash\ngost -L=redirect://:12345 -F=http2://server_ip:443\n```\n\n\n#### obfs4\nContributed by [@isofew](https://github.com/isofew).\n\nServer:\n\n```bash\ngost -L=obfs4://:443\n```\n\nWhen the server is running normally, the console prints out the connection address for the client to use:\n\n```bash\nobfs4://:443/?cert=4UbQjIfjJEQHPOs8vs5sagrSXx1gfrDCGdVh2hpIPSKH0nklv1e4f29r7jb91VIrq4q5Jw&iat-mode=0\n```\n\nClient:\n\n```bash\ngost -L=:8888 -F='obfs4://server_ip:443?cert=4UbQjIfjJEQHPOs8vs5sagrSXx1gfrDCGdVh2hpIPSKH0nklv1e4f29r7jb91VIrq4q5Jw&iat-mode=0'\n```\n\nEncryption Mechanism\n------\n\n#### HTTP\n\nFor HTTP, you can use TLS to encrypt the entire communication process, the HTTPS proxy:\n\nServer:\n\n```bash\ngost -L=http+tls://:443\n```\n\nClient:\n\n```bash\ngost -L=:8080 -F=http+tls://server_ip:443\n```\n\n#### HTTP2\n\nGost HTTP2 proxy mode only supports the use of TLS encrypted HTTP2 protocol, does not support plaintext HTTP2.\n\nGost HTTP2 tunnel mode supports both encryption (h2) and plaintext (h2c) modes.\n\n#### SOCKS5\n\nGost supports the standard SOCKS5 protocol methods: no-auth (0x00) and user/pass (0x02), \nand extends two methods for data encryption: tls(0x80) and tls-auth(0x82).\n\nServer:\n\n```bash\ngost -L=socks://:1080\n```\n\nClient:\n\n```bash\ngost -L=:8080 -F=socks://server_ip:1080\n```\n\nIf both ends are gosts (as example above), the data transfer will be encrypted (using tls or tls-auth). \nOtherwise, use standard SOCKS5 for communication (no-auth or user/pass).\n\n#### Shadowsocks\nSupport for shadowsocks is based on library [shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go).\n\nServer:\n\n```bash\ngost -L=ss://aes-128-cfb:123456@:8338\n```\n\nClient:\n\n```bash\ngost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338\n```\n\n##### Shadowsocks UDP relay\n\nCurrently, only the server supports UDP Relay.\n\nServer:\n\n```bash\ngost -L=ssu://aes-128-cfb:123456@:8338\n```\n\n#### TLS\nThere is built-in TLS certificate in gost, if you need to use other TLS certificate, there are two ways:\n\n* Place two files cert.pem (public key) and key.pem (private key) in the current working directory, gost will automatically load them.\n\n* Use the parameter to specify the path to the certificate file:\n\n```bash\ngost -L=\"http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file\"\n```\n\nClient can specify `secure` parameter to perform server's certificate chain and host name verification:\n\n```bash\ngost -L=:8080 -F=\"http2://server_domain_name:443?secure=true\"\n```\n\nClient can specify a CA certificate to allow for [Certificate Pinning](https://en.wikipedia.org/wiki/Transport_Layer_Security#Certificate_pinning):\n\n```bash\ngost -L=:8080 -F=\"http2://:443?ca=ca.pem\"\n```\n\nCertificate Pinning is contributed by [@sheerun](https://github.com/sheerun).\n"
  },
  {
    "path": "auth.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Authenticator is an interface for user authentication.\ntype Authenticator interface {\n\tAuthenticate(user, password string) bool\n}\n\n// LocalAuthenticator is an Authenticator that authenticates client by local key-value pairs.\ntype LocalAuthenticator struct {\n\tkvs     map[string]string\n\tperiod  time.Duration\n\tstopped chan struct{}\n\tmux     sync.RWMutex\n}\n\n// NewLocalAuthenticator creates an Authenticator that authenticates client by local infos.\nfunc NewLocalAuthenticator(kvs map[string]string) *LocalAuthenticator {\n\treturn &LocalAuthenticator{\n\t\tkvs:     kvs,\n\t\tstopped: make(chan struct{}),\n\t}\n}\n\n// Authenticate checks the validity of the provided user-password pair.\nfunc (au *LocalAuthenticator) Authenticate(user, password string) bool {\n\tif au == nil {\n\t\treturn true\n\t}\n\n\tau.mux.RLock()\n\tdefer au.mux.RUnlock()\n\n\tif len(au.kvs) == 0 {\n\t\treturn true\n\t}\n\n\tv, ok := au.kvs[user]\n\treturn ok && (v == \"\" || password == v)\n}\n\n// Add adds a key-value pair to the Authenticator.\nfunc (au *LocalAuthenticator) Add(k, v string) {\n\tau.mux.Lock()\n\tdefer au.mux.Unlock()\n\tif au.kvs == nil {\n\t\tau.kvs = make(map[string]string)\n\t}\n\tau.kvs[k] = v\n}\n\n// Reload parses config from r, then live reloads the Authenticator.\nfunc (au *LocalAuthenticator) Reload(r io.Reader) error {\n\tvar period time.Duration\n\tkvs := make(map[string]string)\n\n\tif r == nil || au.Stopped() {\n\t\treturn nil\n\t}\n\n\t// splitLine splits a line text by white space.\n\t// A line started with '#' will be ignored, otherwise it is valid.\n\tsplit := func(line string) []string {\n\t\tif line == \"\" {\n\t\t\treturn nil\n\t\t}\n\t\tline = strings.Replace(line, \"\\t\", \" \", -1)\n\t\tline = strings.TrimSpace(line)\n\n\t\tif strings.IndexByte(line, '#') == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tvar ss []string\n\t\tfor _, s := range strings.Split(line, \" \") {\n\t\t\tif s = strings.TrimSpace(s); s != \"\" {\n\t\t\t\tss = append(ss, s)\n\t\t\t}\n\t\t}\n\t\treturn ss\n\t}\n\n\tscanner := bufio.NewScanner(r)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tss := split(line)\n\t\tif len(ss) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch ss[0] {\n\t\tcase \"reload\": // reload option\n\t\t\tif len(ss) > 1 {\n\t\t\t\tperiod, _ = time.ParseDuration(ss[1])\n\t\t\t}\n\t\tdefault:\n\t\t\tvar k, v string\n\t\t\tk = ss[0]\n\t\t\tif len(ss) > 1 {\n\t\t\t\tv = ss[1]\n\t\t\t}\n\t\t\tkvs[k] = v\n\t\t}\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\n\tau.mux.Lock()\n\tdefer au.mux.Unlock()\n\n\tau.period = period\n\tau.kvs = kvs\n\n\treturn nil\n}\n\n// Period returns the reload period.\nfunc (au *LocalAuthenticator) Period() time.Duration {\n\tif au.Stopped() {\n\t\treturn -1\n\t}\n\n\tau.mux.RLock()\n\tdefer au.mux.RUnlock()\n\n\treturn au.period\n}\n\n// Stop stops reloading.\nfunc (au *LocalAuthenticator) Stop() {\n\tselect {\n\tcase <-au.stopped:\n\tdefault:\n\t\tclose(au.stopped)\n\t}\n}\n\n// Stopped checks whether the reloader is stopped.\nfunc (au *LocalAuthenticator) Stopped() bool {\n\tselect {\n\tcase <-au.stopped:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "auth_test.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar localAuthenticatorTests = []struct {\n\tclientUser  *url.Userinfo\n\tserverUsers []*url.Userinfo\n\tvalid       bool\n}{\n\t{nil, nil, true},\n\t{nil, []*url.Userinfo{url.User(\"admin\")}, false},\n\t{nil, []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, false},\n\t{nil, []*url.Userinfo{url.UserPassword(\"admin\", \"123456\")}, false},\n\n\t{url.User(\"admin\"), nil, true},\n\t{url.User(\"admin\"), []*url.Userinfo{url.User(\"admin\")}, true},\n\t{url.User(\"admin\"), []*url.Userinfo{url.User(\"test\")}, false},\n\t{url.User(\"admin\"), []*url.Userinfo{url.UserPassword(\"test\", \"123456\")}, false},\n\t{url.User(\"admin\"), []*url.Userinfo{url.UserPassword(\"admin\", \"123456\")}, false},\n\t{url.User(\"admin\"), []*url.Userinfo{url.UserPassword(\"admin\", \"\")}, true},\n\t{url.User(\"admin\"), []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, false},\n\n\t{url.UserPassword(\"\", \"\"), nil, true},\n\t{url.UserPassword(\"\", \"123456\"), nil, true},\n\t{url.UserPassword(\"\", \"123456\"), []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, true},\n\t{url.UserPassword(\"\", \"123456\"), []*url.Userinfo{url.UserPassword(\"admin\", \"\")}, false},\n\t{url.UserPassword(\"\", \"123456\"), []*url.Userinfo{url.UserPassword(\"admin\", \"123456\")}, false},\n\n\t{url.UserPassword(\"admin\", \"123456\"), nil, true},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.User(\"admin\")}, true},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.User(\"test\")}, false},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"admin\", \"\")}, true},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, false},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"admin\", \"123\")}, false},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"test\", \"123456\")}, false},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"admin\", \"123456\")}, true},\n\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{\n\t\turl.UserPassword(\"test\", \"123\"),\n\t\turl.UserPassword(\"admin\", \"123456\"),\n\t}, true},\n}\n\nfunc TestLocalAuthenticator(t *testing.T) {\n\tfor i, tc := range localAuthenticatorTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\tau := NewLocalAuthenticator(nil)\n\t\t\tfor _, u := range tc.serverUsers {\n\t\t\t\tif u != nil {\n\t\t\t\t\tp, _ := u.Password()\n\t\t\t\t\tau.Add(u.Username(), p)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar u, p string\n\t\t\tif tc.clientUser != nil {\n\t\t\t\tu = tc.clientUser.Username()\n\t\t\t\tp, _ = tc.clientUser.Password()\n\t\t\t}\n\t\t\tif au.Authenticate(u, p) != tc.valid {\n\t\t\t\tt.Error(\"authenticate result should be\", tc.valid)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar localAuthenticatorReloadTests = []struct {\n\tr       io.Reader\n\tperiod  time.Duration\n\tkvs     map[string]string\n\tstopped bool\n}{\n\t{\n\t\tr:      nil,\n\t\tperiod: 0,\n\t\tkvs:    nil,\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"\"),\n\t\tperiod: 0,\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"reload 10s\"),\n\t\tperiod: 10 * time.Second,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"# reload 10s\\n\"),\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"reload 10s\\n#admin\"),\n\t\tperiod: 10 * time.Second,\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"reload 10s\\nadmin\"),\n\t\tperiod: 10 * time.Second,\n\t\tkvs: map[string]string{\n\t\t\t\"admin\": \"\",\n\t\t},\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"# reload 10s\\nadmin\"),\n\t\tkvs: map[string]string{\n\t\t\t\"admin\": \"\",\n\t\t},\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"# reload 10s\\nadmin #123456\"),\n\t\tkvs: map[string]string{\n\t\t\t\"admin\": \"#123456\",\n\t\t},\n\t\tstopped: true,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"admin \\t #123456\\n\\n\\ntest \\t 123456\"),\n\t\tkvs: map[string]string{\n\t\t\t\"admin\": \"#123456\",\n\t\t\t\"test\":  \"123456\",\n\t\t},\n\t\tstopped: true,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(`\n\t\t$test.admin$ $123456$\n\t\t@test.admin@ @123456@\n\t\ttest.admin# #123456#\n\t\ttest.admin\\admin 123456\n\t\t`),\n\t\tkvs: map[string]string{\n\t\t\t\"$test.admin$\":      \"$123456$\",\n\t\t\t\"@test.admin@\":      \"@123456@\",\n\t\t\t\"test.admin#\":       \"#123456#\",\n\t\t\t\"test.admin\\\\admin\": \"123456\",\n\t\t},\n\t\tstopped: true,\n\t},\n}\n\nfunc TestLocalAuthenticatorReload(t *testing.T) {\n\tisEquals := func(a, b map[string]string) bool {\n\t\tif len(a) == 0 && len(b) == 0 {\n\t\t\treturn true\n\t\t}\n\t\tif len(a) != len(b) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor k, v := range a {\n\t\t\tif b[k] != v {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tfor i, tc := range localAuthenticatorReloadTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\tau := NewLocalAuthenticator(nil)\n\n\t\t\tif err := au.Reload(tc.r); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tif au.Period() != tc.period {\n\t\t\t\tt.Errorf(\"#%d test failed: period value should be %v, got %v\",\n\t\t\t\t\ti, tc.period, au.Period())\n\t\t\t}\n\t\t\tif !isEquals(au.kvs, tc.kvs) {\n\t\t\t\tt.Errorf(\"#%d test failed: %v, %s\", i, au.kvs, tc.kvs)\n\t\t\t}\n\n\t\t\tif tc.stopped {\n\t\t\t\tau.Stop()\n\t\t\t\tif au.Period() >= 0 {\n\t\t\t\t\tt.Errorf(\"period of the stopped reloader should be minus value\")\n\t\t\t\t}\n\t\t\t\tau.Stop()\n\t\t\t}\n\t\t\tif au.Stopped() != tc.stopped {\n\t\t\t\tt.Errorf(\"#%d test failed: stopped value should be %v, got %v\",\n\t\t\t\t\ti, tc.stopped, au.Stopped())\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bypass.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\tglob \"github.com/gobwas/glob\"\n)\n\n// Matcher is a generic pattern matcher,\n// it gives the match result of the given pattern for specific v.\ntype Matcher interface {\n\tMatch(v string) bool\n\tString() string\n}\n\n// NewMatcher creates a Matcher for the given pattern.\n// The acutal Matcher depends on the pattern:\n// IP Matcher if pattern is a valid IP address.\n// CIDR Matcher if pattern is a valid CIDR address.\n// Domain Matcher if both of the above are not.\nfunc NewMatcher(pattern string) Matcher {\n\tif pattern == \"\" {\n\t\treturn nil\n\t}\n\tif ip := net.ParseIP(pattern); ip != nil {\n\t\treturn IPMatcher(ip)\n\t}\n\tif _, inet, err := net.ParseCIDR(pattern); err == nil {\n\t\treturn CIDRMatcher(inet)\n\t}\n\treturn DomainMatcher(pattern)\n}\n\ntype ipMatcher struct {\n\tip net.IP\n}\n\n// IPMatcher creates a Matcher for a specific IP address.\nfunc IPMatcher(ip net.IP) Matcher {\n\treturn &ipMatcher{\n\t\tip: ip,\n\t}\n}\n\nfunc (m *ipMatcher) Match(ip string) bool {\n\tif m == nil {\n\t\treturn false\n\t}\n\treturn m.ip.Equal(net.ParseIP(ip))\n}\n\nfunc (m *ipMatcher) String() string {\n\treturn \"ip \" + m.ip.String()\n}\n\ntype cidrMatcher struct {\n\tipNet *net.IPNet\n}\n\n// CIDRMatcher creates a Matcher for a specific CIDR notation IP address.\nfunc CIDRMatcher(inet *net.IPNet) Matcher {\n\treturn &cidrMatcher{\n\t\tipNet: inet,\n\t}\n}\n\nfunc (m *cidrMatcher) Match(ip string) bool {\n\tif m == nil || m.ipNet == nil {\n\t\treturn false\n\t}\n\treturn m.ipNet.Contains(net.ParseIP(ip))\n}\n\nfunc (m *cidrMatcher) String() string {\n\treturn \"cidr \" + m.ipNet.String()\n}\n\ntype domainMatcher struct {\n\tpattern string\n\tglob    glob.Glob\n}\n\n// DomainMatcher creates a Matcher for a specific domain pattern,\n// the pattern can be a plain domain such as 'example.com',\n// a wildcard such as '*.exmaple.com' or a special wildcard '.example.com'.\nfunc DomainMatcher(pattern string) Matcher {\n\tp := pattern\n\tif strings.HasPrefix(pattern, \".\") {\n\t\tp = pattern[1:] // trim the prefix '.'\n\t\tpattern = \"*\" + p\n\t}\n\treturn &domainMatcher{\n\t\tpattern: p,\n\t\tglob:    glob.MustCompile(pattern),\n\t}\n}\n\nfunc (m *domainMatcher) Match(domain string) bool {\n\tif m == nil || m.glob == nil {\n\t\treturn false\n\t}\n\n\tif domain == m.pattern {\n\t\treturn true\n\t}\n\treturn m.glob.Match(domain)\n}\n\nfunc (m *domainMatcher) String() string {\n\treturn \"domain \" + m.pattern\n}\n\n// Bypass is a filter for address (IP or domain).\n// It contains a list of matchers.\ntype Bypass struct {\n\tmatchers []Matcher\n\tperiod   time.Duration // the period for live reloading\n\treversed bool\n\tstopped  chan struct{}\n\tmux      sync.RWMutex\n}\n\n// NewBypass creates and initializes a new Bypass using matchers as its match rules.\n// The rules will be reversed if the reversed is true.\nfunc NewBypass(reversed bool, matchers ...Matcher) *Bypass {\n\treturn &Bypass{\n\t\tmatchers: matchers,\n\t\treversed: reversed,\n\t\tstopped:  make(chan struct{}),\n\t}\n}\n\n// NewBypassPatterns creates and initializes a new Bypass using matcher patterns as its match rules.\n// The rules will be reversed if the reverse is true.\nfunc NewBypassPatterns(reversed bool, patterns ...string) *Bypass {\n\tvar matchers []Matcher\n\tfor _, pattern := range patterns {\n\t\tif m := NewMatcher(pattern); m != nil {\n\t\t\tmatchers = append(matchers, m)\n\t\t}\n\t}\n\tbp := NewBypass(reversed)\n\tbp.AddMatchers(matchers...)\n\treturn bp\n}\n\n// Contains reports whether the bypass includes addr.\nfunc (bp *Bypass) Contains(addr string) bool {\n\tif bp == nil || addr == \"\" {\n\t\treturn false\n\t}\n\n\t// try to strip the port\n\tif host, port, _ := net.SplitHostPort(addr); host != \"\" && port != \"\" {\n\t\tif p, _ := strconv.Atoi(port); p > 0 { // port is valid\n\t\t\taddr = host\n\t\t}\n\t}\n\n\tbp.mux.RLock()\n\tdefer bp.mux.RUnlock()\n\n\tif len(bp.matchers) == 0 {\n\t\treturn false\n\t}\n\n\tvar matched bool\n\tfor _, matcher := range bp.matchers {\n\t\tif matcher == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif matcher.Match(addr) {\n\t\t\tmatched = true\n\t\t\tbreak\n\t\t}\n\t}\n\treturn !bp.reversed && matched ||\n\t\tbp.reversed && !matched\n}\n\n// AddMatchers appends matchers to the bypass matcher list.\nfunc (bp *Bypass) AddMatchers(matchers ...Matcher) {\n\tbp.mux.Lock()\n\tdefer bp.mux.Unlock()\n\n\tbp.matchers = append(bp.matchers, matchers...)\n}\n\n// Matchers return the bypass matcher list.\nfunc (bp *Bypass) Matchers() []Matcher {\n\tbp.mux.RLock()\n\tdefer bp.mux.RUnlock()\n\n\treturn bp.matchers\n}\n\n// Reversed reports whether the rules of the bypass are reversed.\nfunc (bp *Bypass) Reversed() bool {\n\tbp.mux.RLock()\n\tdefer bp.mux.RUnlock()\n\n\treturn bp.reversed\n}\n\n// Reload parses config from r, then live reloads the bypass.\nfunc (bp *Bypass) Reload(r io.Reader) error {\n\tvar matchers []Matcher\n\tvar period time.Duration\n\tvar reversed bool\n\n\tif r == nil || bp.Stopped() {\n\t\treturn nil\n\t}\n\n\tscanner := bufio.NewScanner(r)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tss := splitLine(line)\n\t\tif len(ss) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tswitch ss[0] {\n\t\tcase \"reload\": // reload option\n\t\t\tif len(ss) > 1 {\n\t\t\t\tperiod, _ = time.ParseDuration(ss[1])\n\t\t\t}\n\t\tcase \"reverse\": // reverse option\n\t\t\tif len(ss) > 1 {\n\t\t\t\treversed, _ = strconv.ParseBool(ss[1])\n\t\t\t}\n\t\tdefault:\n\t\t\tmatchers = append(matchers, NewMatcher(ss[0]))\n\t\t}\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\n\tbp.mux.Lock()\n\tdefer bp.mux.Unlock()\n\n\tbp.matchers = matchers\n\tbp.period = period\n\tbp.reversed = reversed\n\n\treturn nil\n}\n\n// Period returns the reload period.\nfunc (bp *Bypass) Period() time.Duration {\n\tif bp.Stopped() {\n\t\treturn -1\n\t}\n\n\tbp.mux.RLock()\n\tdefer bp.mux.RUnlock()\n\n\treturn bp.period\n}\n\n// Stop stops reloading.\nfunc (bp *Bypass) Stop() {\n\tselect {\n\tcase <-bp.stopped:\n\tdefault:\n\t\tclose(bp.stopped)\n\t}\n}\n\n// Stopped checks whether the reloader is stopped.\nfunc (bp *Bypass) Stopped() bool {\n\tselect {\n\tcase <-bp.stopped:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (bp *Bypass) String() string {\n\tb := &bytes.Buffer{}\n\tfmt.Fprintf(b, \"reversed: %v\\n\", bp.Reversed())\n\tfmt.Fprintf(b, \"reload: %v\\n\", bp.Period())\n\tfor _, m := range bp.Matchers() {\n\t\tb.WriteString(m.String())\n\t\tb.WriteByte('\\n')\n\t}\n\treturn b.String()\n}\n"
  },
  {
    "path": "bypass_test.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar bypassContainTests = []struct {\n\tpatterns []string\n\treversed bool\n\taddr     string\n\tbypassed bool\n}{\n\t// empty pattern\n\t{[]string{\"\"}, false, \"\", false},\n\t{[]string{\"\"}, false, \"192.168.1.1\", false},\n\t{[]string{\"\"}, true, \"\", false},\n\t{[]string{\"\"}, true, \"192.168.1.1\", false},\n\n\t// IP address\n\t{[]string{\"192.168.1.1\"}, false, \"192.168.1.1\", true},\n\t{[]string{\"192.168.1.1\"}, true, \"192.168.1.1\", false},\n\t{[]string{\"192.168.1.1\"}, false, \"192.168.1.2\", false},\n\t{[]string{\"192.168.1.1\"}, true, \"192.168.1.2\", true},\n\t{[]string{\"0.0.0.0\"}, false, \"0.0.0.0\", true},\n\t{[]string{\"0.0.0.0\"}, true, \"0.0.0.0\", false},\n\n\t// CIDR address\n\t{[]string{\"192.168.1.0/0\"}, false, \"1.2.3.4\", true},\n\t{[]string{\"192.168.1.0/0\"}, true, \"1.2.3.4\", false},\n\t{[]string{\"192.168.1.0/8\"}, false, \"192.1.0.255\", true},\n\t{[]string{\"192.168.1.0/8\"}, true, \"192.1.0.255\", false},\n\t{[]string{\"192.168.1.0/8\"}, false, \"191.1.0.255\", false},\n\t{[]string{\"192.168.1.0/8\"}, true, \"191.1.0.255\", true},\n\t{[]string{\"192.168.1.0/16\"}, false, \"192.168.0.255\", true},\n\t{[]string{\"192.168.1.0/16\"}, true, \"192.168.0.255\", false},\n\t{[]string{\"192.168.1.0/16\"}, false, \"192.0.1.255\", false},\n\t{[]string{\"192.168.1.0/16\"}, true, \"192.0.0.255\", true},\n\t{[]string{\"192.168.1.0/24\"}, false, \"192.168.1.255\", true},\n\t{[]string{\"192.168.1.0/24\"}, true, \"192.168.1.255\", false},\n\t{[]string{\"192.168.1.0/24\"}, false, \"192.168.0.255\", false},\n\t{[]string{\"192.168.1.0/24\"}, true, \"192.168.0.255\", true},\n\t{[]string{\"192.168.1.1/32\"}, false, \"192.168.1.1\", true},\n\t{[]string{\"192.168.1.1/32\"}, true, \"192.168.1.1\", false},\n\t{[]string{\"192.168.1.1/32\"}, false, \"192.168.1.2\", false},\n\t{[]string{\"192.168.1.1/32\"}, true, \"192.168.1.2\", true},\n\n\t// plain domain\n\t{[]string{\"www.example.com\"}, false, \"www.example.com\", true},\n\t{[]string{\"www.example.com\"}, true, \"www.example.com\", false},\n\t{[]string{\"http://www.example.com\"}, false, \"http://www.example.com\", true},\n\t{[]string{\"http://www.example.com\"}, true, \"http://www.example.com\", false},\n\t{[]string{\"http://www.example.com\"}, false, \"http://example.com\", false},\n\t{[]string{\"http://www.example.com\"}, true, \"http://example.com\", true},\n\t{[]string{\"www.example.com\"}, false, \"example.com\", false},\n\t{[]string{\"www.example.com\"}, true, \"example.com\", true},\n\n\t// host:port\n\t{[]string{\"192.168.1.1\"}, false, \"192.168.1.1:80\", true},\n\t{[]string{\"192.168.1.1\"}, true, \"192.168.1.1:80\", false},\n\t{[]string{\"192.168.1.1:80\"}, false, \"192.168.1.1\", false},\n\t{[]string{\"192.168.1.1:80\"}, true, \"192.168.1.1\", true},\n\t{[]string{\"192.168.1.1:80\"}, false, \"192.168.1.1:80\", false},\n\t{[]string{\"192.168.1.1:80\"}, true, \"192.168.1.1:80\", true},\n\t{[]string{\"192.168.1.1:80\"}, false, \"192.168.1.1:8080\", false},\n\t{[]string{\"192.168.1.1:80\"}, true, \"192.168.1.1:8080\", true},\n\n\t{[]string{\"example.com\"}, false, \"example.com:80\", true},\n\t{[]string{\"example.com\"}, true, \"example.com:80\", false},\n\t{[]string{\"example.com:80\"}, false, \"example.com\", false},\n\t{[]string{\"example.com:80\"}, true, \"example.com\", true},\n\t{[]string{\"example.com:80\"}, false, \"example.com:80\", false},\n\t{[]string{\"example.com:80\"}, true, \"example.com:80\", true},\n\t{[]string{\"example.com:80\"}, false, \"example.com:8080\", false},\n\t{[]string{\"example.com:80\"}, true, \"example.com:8080\", true},\n\n\t// domain wildcard\n\n\t{[]string{\"*\"}, false, \"\", false},\n\t{[]string{\"*\"}, false, \"192.168.1.1\", true},\n\t{[]string{\"*\"}, false, \"192.168.0.0/16\", true},\n\t{[]string{\"*\"}, false, \"http://example.com\", true},\n\t{[]string{\"*\"}, false, \"example.com:80\", true},\n\t{[]string{\"*\"}, true, \"\", false},\n\t{[]string{\"*\"}, true, \"192.168.1.1\", false},\n\t{[]string{\"*\"}, true, \"192.168.0.0/16\", false},\n\t{[]string{\"*\"}, true, \"http://example.com\", false},\n\t{[]string{\"*\"}, true, \"example.com:80\", false},\n\n\t// sub-domain\n\t{[]string{\"*.example.com\"}, false, \"example.com\", false},\n\t{[]string{\"*.example.com\"}, false, \"http://example.com\", false},\n\t{[]string{\"*.example.com\"}, false, \"www.example.com\", true},\n\t{[]string{\"*.example.com\"}, false, \"http://www.example.com\", true},\n\t{[]string{\"*.example.com\"}, false, \"abc.def.example.com\", true},\n\n\t{[]string{\"*.*.example.com\"}, false, \"example.com\", false},\n\t{[]string{\"*.*.example.com\"}, false, \"www.example.com\", false},\n\t{[]string{\"*.*.example.com\"}, false, \"abc.def.example.com\", true},\n\t{[]string{\"*.*.example.com\"}, false, \"abc.def.ghi.example.com\", true},\n\n\t{[]string{\"**.example.com\"}, false, \"example.com\", false},\n\t{[]string{\"**.example.com\"}, false, \"www.example.com\", true},\n\t{[]string{\"**.example.com\"}, false, \"abc.def.ghi.example.com\", true},\n\n\t// prefix wildcard\n\t{[]string{\"*example.com\"}, false, \"example.com\", true},\n\t{[]string{\"*example.com\"}, false, \"www.example.com\", true},\n\t{[]string{\"*example.com\"}, false, \"abc.defexample.com\", true},\n\t{[]string{\"*example.com\"}, false, \"abc.def-example.com\", true},\n\t{[]string{\"*example.com\"}, false, \"abc.def.example.com\", true},\n\t{[]string{\"*example.com\"}, false, \"http://www.example.com\", true},\n\t{[]string{\"*example.com\"}, false, \"e-xample.com\", false},\n\n\t{[]string{\"http://*.example.com\"}, false, \"example.com\", false},\n\t{[]string{\"http://*.example.com\"}, false, \"http://example.com\", false},\n\t{[]string{\"http://*.example.com\"}, false, \"http://www.example.com\", true},\n\t{[]string{\"http://*.example.com\"}, false, \"https://www.example.com\", false},\n\t{[]string{\"http://*.example.com\"}, false, \"http://abc.def.example.com\", true},\n\n\t{[]string{\"www.*.com\"}, false, \"www.example.com\", true},\n\t{[]string{\"www.*.com\"}, false, \"www.abc.def.com\", true},\n\n\t{[]string{\"www.*.*.com\"}, false, \"www.example.com\", false},\n\t{[]string{\"www.*.*.com\"}, false, \"www.abc.def.com\", true},\n\t{[]string{\"www.*.*.com\"}, false, \"www.abc.def.ghi.com\", true},\n\n\t{[]string{\"www.*example*.com\"}, false, \"www.example.com\", true},\n\t{[]string{\"www.*example*.com\"}, false, \"www.abc.example.def.com\", true},\n\t{[]string{\"www.*example*.com\"}, false, \"www.e-xample.com\", false},\n\n\t{[]string{\"www.example.*\"}, false, \"www.example.com\", true},\n\t{[]string{\"www.example.*\"}, false, \"www.example.io\", true},\n\t{[]string{\"www.example.*\"}, false, \"www.example.com.cn\", true},\n\n\t{[]string{\".example.com\"}, false, \"www.example.com\", true},\n\t{[]string{\".example.com\"}, false, \"example.com\", true},\n\t{[]string{\".example.com\"}, false, \"www.example.com.cn\", false},\n\n\t{[]string{\"example.com*\"}, false, \"example.com\", true},\n\t{[]string{\"example.com:*\"}, false, \"example.com\", false},\n\t{[]string{\"example.com:*\"}, false, \"example.com:80\", false},\n\t{[]string{\"example.com:*\"}, false, \"example.com:8080\", false},\n\t{[]string{\"example.com:*\"}, false, \"example.com:http\", true},\n\t{[]string{\"example.com:*\"}, false, \"http://example.com:80\", false},\n\n\t{[]string{\"*example.com*\"}, false, \"example.com:80\", true},\n\t{[]string{\"*example.com:*\"}, false, \"example.com:80\", false},\n\n\t{[]string{\".example.com:*\"}, false, \"www.example.com\", false},\n\t{[]string{\".example.com:*\"}, false, \"http://www.example.com\", false},\n\t{[]string{\".example.com:*\"}, false, \"example.com:80\", false},\n\t{[]string{\".example.com:*\"}, false, \"www.example.com:8080\", false},\n\t{[]string{\".example.com:*\"}, false, \"http://www.example.com:80\", true},\n}\n\nfunc TestBypassContains(t *testing.T) {\n\tfor i, tc := range bypassContainTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\tbp := NewBypassPatterns(tc.reversed, tc.patterns...)\n\t\t\tif bp.Contains(tc.addr) != tc.bypassed {\n\t\t\t\tt.Errorf(\"#%d test failed: %v, %s\", i, tc.patterns, tc.addr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar bypassReloadTests = []struct {\n\tr io.Reader\n\n\treversed bool\n\tperiod   time.Duration\n\n\taddr     string\n\tbypassed bool\n\tstopped  bool\n}{\n\t{\n\t\tr:        nil,\n\t\treversed: false,\n\t\tperiod:   0,\n\t\taddr:     \"192.168.1.1\",\n\t\tbypassed: false,\n\t\tstopped:  false,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"\"),\n\t\treversed: false,\n\t\tperiod:   0,\n\t\taddr:     \"192.168.1.1\",\n\t\tbypassed: false,\n\t\tstopped:  false,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"reverse true\\nreload 10s\"),\n\t\treversed: true,\n\t\tperiod:   10 * time.Second,\n\t\taddr:     \"192.168.1.1\",\n\t\tbypassed: false,\n\t\tstopped:  false,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"reverse false\\nreload 10s\\n192.168.1.1\"),\n\t\treversed: false,\n\t\tperiod:   10 * time.Second,\n\t\taddr:     \"192.168.1.1\",\n\t\tbypassed: true,\n\t\tstopped:  false,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"#reverse true\\n#reload 10s\\n192.168.0.0/16\"),\n\t\treversed: false,\n\t\tperiod:   0,\n\t\taddr:     \"192.168.10.2\",\n\t\tbypassed: true,\n\t\tstopped:  true,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"#reverse true\\n#reload 10s\\n192.168.1.0/24 #comment\"),\n\t\treversed: false,\n\t\tperiod:   0,\n\t\taddr:     \"192.168.10.2\",\n\t\tbypassed: false,\n\t\tstopped:  true,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"reverse false\\nreload 10s\\n192.168.1.1\\n#example.com\"),\n\t\treversed: false,\n\t\tperiod:   10 * time.Second,\n\t\taddr:     \"example.com\",\n\t\tbypassed: false,\n\t\tstopped:  false,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"#reverse true\\n#reload 10s\\n192.168.1.1\\n#example.com\"),\n\t\treversed: false,\n\t\tperiod:   0,\n\t\taddr:     \"192.168.1.1\",\n\t\tbypassed: true,\n\t\tstopped:  true,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"#reverse true\\n#reload 10s\\nexample.com #comment\"),\n\t\treversed: false,\n\t\tperiod:   0,\n\t\taddr:     \"example.com\",\n\t\tbypassed: true,\n\t\tstopped:  true,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"#reverse true\\n#reload 10s\\n.example.com\"),\n\t\treversed: false,\n\t\tperiod:   0,\n\t\taddr:     \"example.com\",\n\t\tbypassed: true,\n\t\tstopped:  true,\n\t},\n\t{\n\t\tr:        bytes.NewBufferString(\"#reverse true\\n#reload 10s\\n*.example.com\"),\n\t\treversed: false,\n\t\tperiod:   0,\n\t\taddr:     \"example.com\",\n\t\tbypassed: false,\n\t\tstopped:  true,\n\t},\n}\n\nfunc TestByapssReload(t *testing.T) {\n\tfor i, tc := range bypassReloadTests {\n\t\tbp := NewBypass(false)\n\t\tif err := bp.Reload(tc.r); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tt.Log(bp.String())\n\n\t\tif bp.Reversed() != tc.reversed {\n\t\t\tt.Errorf(\"#%d test failed: reversed value should be %v, got %v\",\n\t\t\t\ti, tc.reversed, bp.reversed)\n\t\t}\n\t\tif bp.Period() != tc.period {\n\t\t\tt.Errorf(\"#%d test failed: period value should be %v, got %v\",\n\t\t\t\ti, tc.period, bp.Period())\n\t\t}\n\t\tif bp.Contains(tc.addr) != tc.bypassed {\n\t\t\tt.Errorf(\"#%d test failed: %v, %s\", i, bp.reversed, tc.addr)\n\t\t}\n\t\tif tc.stopped {\n\t\t\tbp.Stop()\n\t\t\tif bp.Period() >= 0 {\n\t\t\t\tt.Errorf(\"period of the stopped reloader should be minus value\")\n\t\t\t}\n\t\t\tbp.Stop()\n\t\t}\n\t\tif bp.Stopped() != tc.stopped {\n\t\t\tt.Errorf(\"#%d test failed: stopped value should be %v, got %v\",\n\t\t\t\ti, tc.stopped, bp.Stopped())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "chain.go",
    "content": "package gost\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\nvar (\n\t// ErrEmptyChain is an error that implies the chain is empty.\n\tErrEmptyChain = errors.New(\"empty chain\")\n)\n\n// Chain is a proxy chain that holds a list of proxy node groups.\ntype Chain struct {\n\tisRoute    bool\n\tRetries    int\n\tMark       int\n\tInterface  string\n\tnodeGroups []*NodeGroup\n\troute      []Node // nodes in the selected route\n}\n\n// NewChain creates a proxy chain with a list of proxy nodes.\n// It creates the node groups automatically, one group per node.\nfunc NewChain(nodes ...Node) *Chain {\n\tchain := &Chain{}\n\tfor _, node := range nodes {\n\t\tchain.nodeGroups = append(chain.nodeGroups, NewNodeGroup(node))\n\t}\n\treturn chain\n}\n\n// newRoute creates a chain route.\n// a chain route is the final route after node selection.\nfunc (c *Chain) newRoute(nodes ...Node) *Chain {\n\troute := NewChain(nodes...)\n\troute.isRoute = true\n\tif !c.IsEmpty() {\n\t\troute.Interface = c.Interface\n\t\troute.Mark = c.Mark\n\t}\n\treturn route\n}\n\n// Nodes returns the proxy nodes that the chain holds.\n// The first node in each group will be returned.\nfunc (c *Chain) Nodes() (nodes []Node) {\n\tfor _, group := range c.nodeGroups {\n\t\tif ns := group.Nodes(); len(ns) > 0 {\n\t\t\tnodes = append(nodes, ns[0])\n\t\t}\n\t}\n\treturn\n}\n\n// NodeGroups returns the list of node group.\nfunc (c *Chain) NodeGroups() []*NodeGroup {\n\treturn c.nodeGroups\n}\n\n// LastNode returns the last node of the node list.\n// If the chain is empty, an empty node will be returned.\n// If the last node is a node group, the first node in the group will be returned.\nfunc (c *Chain) LastNode() Node {\n\tif c.IsEmpty() {\n\t\treturn Node{}\n\t}\n\tgroup := c.nodeGroups[len(c.nodeGroups)-1]\n\treturn group.GetNode(0)\n}\n\n// LastNodeGroup returns the last group of the group list.\nfunc (c *Chain) LastNodeGroup() *NodeGroup {\n\tif c.IsEmpty() {\n\t\treturn nil\n\t}\n\treturn c.nodeGroups[len(c.nodeGroups)-1]\n}\n\n// AddNode appends the node(s) to the chain.\nfunc (c *Chain) AddNode(nodes ...Node) {\n\tif c == nil {\n\t\treturn\n\t}\n\tfor _, node := range nodes {\n\t\tc.nodeGroups = append(c.nodeGroups, NewNodeGroup(node))\n\t}\n}\n\n// AddNodeGroup appends the group(s) to the chain.\nfunc (c *Chain) AddNodeGroup(groups ...*NodeGroup) {\n\tif c == nil {\n\t\treturn\n\t}\n\tfor _, group := range groups {\n\t\tc.nodeGroups = append(c.nodeGroups, group)\n\t}\n}\n\n// IsEmpty checks if the chain is empty.\n// An empty chain means that there is no proxy node or node group in the chain.\nfunc (c *Chain) IsEmpty() bool {\n\treturn c == nil || len(c.nodeGroups) == 0\n}\n\n// Dial connects to the target TCP address addr through the chain.\n// Deprecated: use DialContext instead.\nfunc (c *Chain) Dial(address string, opts ...ChainOption) (conn net.Conn, err error) {\n\treturn c.DialContext(context.Background(), \"tcp\", address, opts...)\n}\n\n// DialContext connects to the address on the named network using the provided context.\nfunc (c *Chain) DialContext(ctx context.Context, network, address string, opts ...ChainOption) (conn net.Conn, err error) {\n\toptions := &ChainOptions{}\n\tfor _, opt := range opts {\n\t\topt(options)\n\t}\n\n\tretries := 1\n\tif c != nil && c.Retries > 0 {\n\t\tretries = c.Retries\n\t}\n\tif options.Retries > 0 {\n\t\tretries = options.Retries\n\t}\n\n\tfor i := 0; i < retries; i++ {\n\t\tconn, err = c.dialWithOptions(ctx, network, address, options)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\nfunc (c *Chain) dialWithOptions(ctx context.Context, network, address string, options *ChainOptions) (net.Conn, error) {\n\tif options == nil {\n\t\toptions = &ChainOptions{}\n\t}\n\tif c == nil {\n\t\tc = &Chain{}\n\t}\n\troute, err := c.selectRouteFor(address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tipAddr := address\n\tif address != \"\" {\n\t\tipAddr = c.resolve(address, options.Resolver, options.Hosts)\n\t\tif ipAddr == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"resolver: domain %s does not exists\", address)\n\t\t}\n\t}\n\n\ttimeout := options.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = DialTimeout\n\t}\n\n\tvar controlFunction func(_ string, _ string, c syscall.RawConn) error = nil\n\tif c.Mark > 0 {\n\t\tcontrolFunction = func(_, _ string, cc syscall.RawConn) error {\n\t\t\treturn cc.Control(func(fd uintptr) {\n\t\t\t\tex := setSocketMark(int(fd), c.Mark)\n\t\t\t\tif ex != nil {\n\t\t\t\t\tlog.Logf(\"net dialer set mark %d error: %s\", c.Mark, ex)\n\t\t\t\t} else {\n\t\t\t\t\t// log.Logf(\"net dialer set mark %d success\", options.Mark)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\tif c.Interface != \"\" {\n\t\tcontrolFunction = func(_, _ string, cc syscall.RawConn) error {\n\t\t\treturn cc.Control(func(fd uintptr) {\n\t\t\t\terr := setSocketInterface(int(fd), c.Interface)\n\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Logf(\"net dialer set interface %s error: %s\", c.Interface, err)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t}\n\n\tif route.IsEmpty() {\n\t\tswitch network {\n\t\tcase \"udp\", \"udp4\", \"udp6\":\n\t\t\tif address == \"\" {\n\t\t\t\treturn net.ListenUDP(network, nil)\n\t\t\t}\n\t\tdefault:\n\t\t}\n\t\td := &net.Dialer{\n\t\t\tTimeout: timeout,\n\t\t\tControl: controlFunction,\n\t\t\t// LocalAddr: laddr, // TODO: optional local address\n\t\t}\n\t\treturn d.DialContext(ctx, network, ipAddr)\n\t}\n\n\tconn, err := route.getConn(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcOpts := append([]ConnectOption{AddrConnectOption(address)}, route.LastNode().ConnectOptions...)\n\tcc, err := route.LastNode().Client.ConnectContext(ctx, conn, network, ipAddr, cOpts...)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\treturn cc, nil\n}\n\nfunc (*Chain) resolve(addr string, resolver Resolver, hosts *Hosts) string {\n\thost, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn addr\n\t}\n\n\tif ip := hosts.Lookup(host); ip != nil {\n\t\treturn net.JoinHostPort(ip.String(), port)\n\t}\n\tif resolver != nil {\n\t\tips, err := resolver.Resolve(host)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[resolver] %s: %v\", host, err)\n\t\t}\n\t\tif len(ips) == 0 {\n\t\t\tlog.Logf(\"[resolver] %s: domain does not exists\", host)\n\t\t\treturn \"\"\n\t\t}\n\t\treturn net.JoinHostPort(ips[0].String(), port)\n\t}\n\treturn addr\n}\n\n// Conn obtains a handshaked connection to the last node of the chain.\nfunc (c *Chain) Conn(opts ...ChainOption) (conn net.Conn, err error) {\n\toptions := &ChainOptions{}\n\tfor _, opt := range opts {\n\t\topt(options)\n\t}\n\n\tctx := context.Background()\n\n\tretries := 1\n\tif c != nil && c.Retries > 0 {\n\t\tretries = c.Retries\n\t}\n\tif options.Retries > 0 {\n\t\tretries = options.Retries\n\t}\n\n\tfor i := 0; i < retries; i++ {\n\t\tvar route *Chain\n\t\troute, err = c.selectRoute()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tconn, err = route.getConn(ctx)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n\n// getConn obtains a connection to the last node of the chain.\nfunc (c *Chain) getConn(ctx context.Context) (conn net.Conn, err error) {\n\tif c.IsEmpty() {\n\t\terr = ErrEmptyChain\n\t\treturn\n\t}\n\tnodes := c.Nodes()\n\tnode := nodes[0]\n\n\tcc, err := node.Client.Dial(node.Addr, node.DialOptions...)\n\tif err != nil {\n\t\tnode.MarkDead()\n\t\treturn\n\t}\n\n\tcn, err := node.Client.Handshake(cc, node.HandshakeOptions...)\n\tif err != nil {\n\t\tcc.Close()\n\t\tnode.MarkDead()\n\t\treturn\n\t}\n\tnode.ResetDead()\n\n\tpreNode := node\n\tfor _, node := range nodes[1:] {\n\t\tvar cc net.Conn\n\t\tcc, err = preNode.Client.ConnectContext(ctx, cn, \"tcp\", node.Addr, preNode.ConnectOptions...)\n\t\tif err != nil {\n\t\t\tcn.Close()\n\t\t\tnode.MarkDead()\n\t\t\treturn\n\t\t}\n\t\tcc, err = node.Client.Handshake(cc, node.HandshakeOptions...)\n\t\tif err != nil {\n\t\t\tcn.Close()\n\t\t\tnode.MarkDead()\n\t\t\treturn\n\t\t}\n\t\tnode.ResetDead()\n\n\t\tcn = cc\n\t\tpreNode = node\n\t}\n\n\tconn = cn\n\treturn\n}\n\nfunc (c *Chain) selectRoute() (route *Chain, err error) {\n\treturn c.selectRouteFor(\"\")\n}\n\n// selectRouteFor selects route with bypass testing.\nfunc (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {\n\tif c.IsEmpty() {\n\t\treturn c.newRoute(), nil\n\t}\n\tif c.isRoute {\n\t\treturn c, nil\n\t}\n\n\troute = c.newRoute()\n\tvar nl []Node\n\n\tfor _, group := range c.nodeGroups {\n\t\tvar node Node\n\t\tnode, err = group.Next()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif node.Bypass.Contains(addr) {\n\t\t\tbreak\n\t\t}\n\n\t\tif node.Client.Transporter.Multiplex() {\n\t\t\tnode.DialOptions = append(node.DialOptions,\n\t\t\t\tChainDialOption(route),\n\t\t\t)\n\t\t\troute = c.newRoute() // cutoff the chain for multiplex node.\n\t\t}\n\n\t\troute.AddNode(node)\n\t\tnl = append(nl, node)\n\t}\n\n\troute.route = nl\n\n\treturn\n}\n\n// ChainOptions holds options for Chain.\ntype ChainOptions struct {\n\tRetries  int\n\tTimeout  time.Duration\n\tHosts    *Hosts\n\tResolver Resolver\n\tMark     int\n}\n\n// ChainOption allows a common way to set chain options.\ntype ChainOption func(opts *ChainOptions)\n\n// RetryChainOption specifies the times of retry used by Chain.Dial.\nfunc RetryChainOption(retries int) ChainOption {\n\treturn func(opts *ChainOptions) {\n\t\topts.Retries = retries\n\t}\n}\n\n// TimeoutChainOption specifies the timeout used by Chain.Dial.\nfunc TimeoutChainOption(timeout time.Duration) ChainOption {\n\treturn func(opts *ChainOptions) {\n\t\topts.Timeout = timeout\n\t}\n}\n\n// HostsChainOption specifies the hosts used by Chain.Dial.\nfunc HostsChainOption(hosts *Hosts) ChainOption {\n\treturn func(opts *ChainOptions) {\n\t\topts.Hosts = hosts\n\t}\n}\n\n// ResolverChainOption specifies the Resolver used by Chain.Dial.\nfunc ResolverChainOption(resolver Resolver) ChainOption {\n\treturn func(opts *ChainOptions) {\n\t\topts.Resolver = resolver\n\t}\n}\n"
  },
  {
    "path": "client.go",
    "content": "package gost\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"net\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/go-gost/gosocks5\"\n)\n\n// Client is a proxy client.\n// A client is divided into two layers: connector and transporter.\n// Connector is responsible for connecting to the destination address through this proxy.\n// Transporter performs a handshake with this proxy.\ntype Client struct {\n\tConnector\n\tTransporter\n}\n\n// DefaultClient is a standard HTTP proxy client.\nvar DefaultClient = &Client{Connector: HTTPConnector(nil), Transporter: TCPTransporter()}\n\n// Dial connects to the address addr via the DefaultClient.\nfunc Dial(addr string, options ...DialOption) (net.Conn, error) {\n\treturn DefaultClient.Dial(addr, options...)\n}\n\n// Handshake performs a handshake via the DefaultClient.\nfunc Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\treturn DefaultClient.Handshake(conn, options...)\n}\n\n// Connect connects to the address addr via the DefaultClient.\nfunc Connect(conn net.Conn, addr string) (net.Conn, error) {\n\treturn DefaultClient.Connect(conn, addr)\n}\n\n// Connector is responsible for connecting to the destination address.\ntype Connector interface {\n\t// Deprecated: use ConnectContext instead.\n\tConnect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error)\n\tConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error)\n}\n\ntype autoConnector struct {\n\tUser *url.Userinfo\n}\n\n// AutoConnector is a Connector.\nfunc AutoConnector(user *url.Userinfo) Connector {\n\treturn &autoConnector{\n\t\tUser: user,\n\t}\n}\n\nfunc (c *autoConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *autoConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tvar cnr Connector\n\tswitch network {\n\tcase \"tcp\", \"tcp4\", \"tcp6\":\n\t\tcnr = &httpConnector{User: c.User}\n\tdefault:\n\t\tcnr = &socks5UDPTunConnector{User: c.User}\n\t}\n\n\treturn cnr.ConnectContext(ctx, conn, network, address, options...)\n}\n\n// Transporter is responsible for handshaking with the proxy server.\ntype Transporter interface {\n\tDial(addr string, options ...DialOption) (net.Conn, error)\n\tHandshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error)\n\t// Indicate that the Transporter supports multiplex\n\tMultiplex() bool\n}\n\n// DialOptions describes the options for Transporter.Dial.\ntype DialOptions struct {\n\tTimeout time.Duration\n\tChain   *Chain\n\tHost    string\n}\n\n// DialOption allows a common way to set DialOptions.\ntype DialOption func(opts *DialOptions)\n\n// TimeoutDialOption specifies the timeout used by Transporter.Dial\nfunc TimeoutDialOption(timeout time.Duration) DialOption {\n\treturn func(opts *DialOptions) {\n\t\topts.Timeout = timeout\n\t}\n}\n\n// ChainDialOption specifies a chain used by Transporter.Dial\nfunc ChainDialOption(chain *Chain) DialOption {\n\treturn func(opts *DialOptions) {\n\t\topts.Chain = chain\n\t}\n}\n\n// HostDialOption specifies the host used by Transporter.Dial\nfunc HostDialOption(host string) DialOption {\n\treturn func(opts *DialOptions) {\n\t\topts.Host = host\n\t}\n}\n\n// HandshakeOptions describes the options for handshake.\ntype HandshakeOptions struct {\n\tAddr       string\n\tHost       string\n\tUser       *url.Userinfo\n\tTimeout    time.Duration\n\tInterval   time.Duration\n\tRetry      int\n\tTLSConfig  *tls.Config\n\tWSOptions  *WSOptions\n\tKCPConfig  *KCPConfig\n\tQUICConfig *QUICConfig\n\tSSHConfig  *SSHConfig\n}\n\n// HandshakeOption allows a common way to set HandshakeOptions.\ntype HandshakeOption func(opts *HandshakeOptions)\n\n// AddrHandshakeOption specifies the server address\nfunc AddrHandshakeOption(addr string) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.Addr = addr\n\t}\n}\n\n// HostHandshakeOption specifies the hostname\nfunc HostHandshakeOption(host string) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.Host = host\n\t}\n}\n\n// UserHandshakeOption specifies the user used by Transporter.Handshake\nfunc UserHandshakeOption(user *url.Userinfo) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.User = user\n\t}\n}\n\n// TimeoutHandshakeOption specifies the timeout used by Transporter.Handshake\nfunc TimeoutHandshakeOption(timeout time.Duration) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.Timeout = timeout\n\t}\n}\n\n// IntervalHandshakeOption specifies the interval time used by Transporter.Handshake\nfunc IntervalHandshakeOption(interval time.Duration) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.Interval = interval\n\t}\n}\n\n// RetryHandshakeOption specifies the times of retry used by Transporter.Handshake\nfunc RetryHandshakeOption(retry int) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.Retry = retry\n\t}\n}\n\n// TLSConfigHandshakeOption specifies the TLS config used by Transporter.Handshake\nfunc TLSConfigHandshakeOption(config *tls.Config) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.TLSConfig = config\n\t}\n}\n\n// WSOptionsHandshakeOption specifies the websocket options used by websocket handshake\nfunc WSOptionsHandshakeOption(options *WSOptions) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.WSOptions = options\n\t}\n}\n\n// KCPConfigHandshakeOption specifies the KCP config used by KCP handshake\nfunc KCPConfigHandshakeOption(config *KCPConfig) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.KCPConfig = config\n\t}\n}\n\n// QUICConfigHandshakeOption specifies the QUIC config used by QUIC handshake\nfunc QUICConfigHandshakeOption(config *QUICConfig) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.QUICConfig = config\n\t}\n}\n\n// SSHConfigHandshakeOption specifies the ssh config used by SSH client handshake.\nfunc SSHConfigHandshakeOption(config *SSHConfig) HandshakeOption {\n\treturn func(opts *HandshakeOptions) {\n\t\topts.SSHConfig = config\n\t}\n}\n\n// ConnectOptions describes the options for Connector.Connect.\ntype ConnectOptions struct {\n\tAddr      string\n\tTimeout   time.Duration\n\tUser      *url.Userinfo\n\tSelector  gosocks5.Selector\n\tUserAgent string\n\tNoTLS     bool\n\tNoDelay   bool\n}\n\n// ConnectOption allows a common way to set ConnectOptions.\ntype ConnectOption func(opts *ConnectOptions)\n\n// AddrConnectOption specifies the corresponding address of the target.\nfunc AddrConnectOption(addr string) ConnectOption {\n\treturn func(opts *ConnectOptions) {\n\t\topts.Addr = addr\n\t}\n}\n\n// TimeoutConnectOption specifies the timeout for connecting to target.\nfunc TimeoutConnectOption(timeout time.Duration) ConnectOption {\n\treturn func(opts *ConnectOptions) {\n\t\topts.Timeout = timeout\n\t}\n}\n\n// UserConnectOption specifies the user info for authentication.\nfunc UserConnectOption(user *url.Userinfo) ConnectOption {\n\treturn func(opts *ConnectOptions) {\n\t\topts.User = user\n\t}\n}\n\n// SelectorConnectOption specifies the SOCKS5 client selector.\nfunc SelectorConnectOption(s gosocks5.Selector) ConnectOption {\n\treturn func(opts *ConnectOptions) {\n\t\topts.Selector = s\n\t}\n}\n\n// UserAgentConnectOption specifies the HTTP user-agent header.\nfunc UserAgentConnectOption(ua string) ConnectOption {\n\treturn func(opts *ConnectOptions) {\n\t\topts.UserAgent = ua\n\t}\n}\n\n// NoTLSConnectOption specifies the SOCKS5 method without TLS.\nfunc NoTLSConnectOption(b bool) ConnectOption {\n\treturn func(opts *ConnectOptions) {\n\t\topts.NoTLS = b\n\t}\n}\n\n// NoDelayConnectOption specifies the NoDelay option for ss.Connect.\nfunc NoDelayConnectOption(b bool) ConnectOption {\n\treturn func(opts *ConnectOptions) {\n\t\topts.NoDelay = b\n\t}\n}\n"
  },
  {
    "path": "cmd/gost/.ssl/README.md",
    "content": "[//]: <> (https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309)\n\n# Create Root CA (Done once)\n\n## Create Root Key\n\n**Attention:** this is the key used to sign the certificate requests, anyone holding this can sign certificates on your behalf. So keep it in a safe place!\n\n```bash\nopenssl genrsa -des3 -out rootCA.key 4096\n```\n\nIf you want a non password protected key just remove the `-des3` option\n\n\n## Create and self sign the Root Certificate\n\n```bash\nopenssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt\n```\n\nHere we used our root key to create the root certificate that needs to be distributed in all the computers that have to trust us.\n\n\n# Create a certificate (Done for each server)\n\nThis procedure needs to be followed for each server/appliance that needs a trusted certificate from our CA\n\n## Create the certificate key\n\n```\nopenssl genrsa -out mydomain.com.key 2048\n```\n\n## Create the signing request\n\n**Important:** Please mind that while creating the signign request is important to specify the `Common Name` providing the IP address or URL for the service, otherwise the certificate\ncannot be verified\n\n```\nopenssl req -new -key mydomain.com.key -out mydomain.com.csr\n```\n\n## Generate the certificate using the `mydomain` csr and key along with the CA Root key\n\n```\nopenssl x509 -req -in mydomain.com.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out mydomain.com.crt -days 500 -sha256\n```\n"
  },
  {
    "path": "cmd/gost/.ssl/localhost.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDvjCCAaYCCQC0XjV3wljvnjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls\nb2NhbGhvc3QwHhcNMTgwNzA4MDQ1MzIyWhcNMTkxMTIwMDQ1MzIyWjAuMQswCQYD\nVQQGEwJDTjELMAkGA1UEBwwCU0gxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJ\nKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOKgzWil/KjyRy2Axb3XlLB1nMwLFJC\npC6r8yb+1Kq/ldZghJZvymuFVjn+bihvJqvZiOv4KRtnM8gD55AhaQp6Ese5M9b+\n47HLB//SkfJsQREsmnrHfHxjmUQjhMy7jrpcf9OnDOXQ5zk3v6AWEIqMtAiZ99ku\nAQvyJJ07+VpwZrMuzbSGfFBCKEbbqP7yKHjSUm3QDTpTiK4AnBmzlVeThUIA68oa\nXZKQVXX/8U2i6H4eq5eNpyUsKSnnuK+cryHpAIK4vNMzw96vATTfEmuWASEzkHhW\n3KtfXE0CIH0GsK5zueGDo9ygnO7hjtx60SWynlGf6c6edxPwNvEmTZcCAwEAATAN\nBgkqhkiG9w0BAQsFAAOCAgEApLkdhnDzErgBljY6qRaR0JlouTpqJXwi5BRi7F1P\nbx5ukrZAVSOsZ7ncEkZuxkIX+ktBVFBL8twkvMEl+sMQ24R+F+TrlHWN2xPR/pez\n9V19hq26yMIlYLqSq3KZ0W9ZlT2ge+3sTvY+gAJhZ6nOz9WGRJ1mi+pN/ok678QX\nKdOJXcePzYr5iKqMq/5cJ2sA1xYwVl+0xrvfRVTFkp4yR6wzGODtjquB+scZ9S64\nGWnFTjHAJvUKYxpeoLAt9lZHsESDqGq7hA4z1uVjhNEDJHKnXW4OhXxMB8Gk2hY8\n3k4zbnKsouNNW0a7jijCMpXOem/vgQF4GK5ecp0S+Ml/AunsPoi6rGgOCX8XXmti\n6DfQhsxxgn1co/JKNxhgsnQftXFwKivh73JFctSh+bMLsewfXsvq+b0K3EuuV9bV\nEttVCgbUaCDYdA6IDkqD2PRx9tsotne76r+cX+ah+NjnA6XN+XY2bJgV1UaiKTrP\nmoNHglw+xoUqOJ7FlGJcVC7uIFPhMviNkpSZh6WxX+OSS4fPO25kxxNpldql6I+3\nxb5XEHLpPCEI4PyK0rYnsjk764Loqff8YBMFRQSXIUz9ot5SgGs/FY1vsQap5OeD\nHw2usWhCvkSzr7kiXI+30BvJKK2r9GOAM7mtO9dfkM9MMKKnMzd+O2XE4r6PNLrg\nRds=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "cmd/gost/.ssl/localhost.csr",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIICczCCAVsCAQAwLjELMAkGA1UEBhMCQ04xCzAJBgNVBAcMAlNIMRIwEAYDVQQD\nDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDioM1\nopfyo8kctgMW915SwdZzMCxSQqQuq/Mm/tSqv5XWYISWb8prhVY5/m4obyar2Yjr\n+CkbZzPIA+eQIWkKehLHuTPW/uOxywf/0pHybEERLJp6x3x8Y5lEI4TMu466XH/T\npwzl0Oc5N7+gFhCKjLQImffZLgEL8iSdO/lacGazLs20hnxQQihG26j+8ih40lJt\n0A06U4iuAJwZs5VXk4VCAOvKGl2SkFV1//FNouh+HquXjaclLCkp57ivnK8h6QCC\nuLzTM8PerwE03xJrlgEhM5B4VtyrX1xNAiB9BrCuc7nhg6PcoJzu4Y7cetElsp5R\nn+nOnncT8DbxJk2XAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAr+AkYRAPulBU\nB5HR3pAreYrf3Y2fvGLSNo4hvsJkmXJxDgMZnGsjVzW1IZLF8szn4v050y6Qm/Ne\nqupabYP5zpj0vKkACYGJ2zadnowmwlTzwlxEOv27uQykC/IuRcjdloAD7ZwhNwmO\ndLNjdiXn63GUeSL/JK0UHyXTqvpmiHq+6TAOdl3vmsRFCQDChRtViK2fwSeX2y87\nhLicSVQyNOe0gUx7IvE9B2QPNhdzaMVPYeN8I/cayNeUKhiWxEGKhwPAaievuSXJ\nfUsz11XYBYW+kjFsTqkV1OjkG0mxvwaiq5W3CRx8365w71IMdKV5t5xhc0n0TXp7\ncT27XN7cdw==\n-----END CERTIFICATE REQUEST-----\n"
  },
  {
    "path": "cmd/gost/.ssl/localhost.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAw4qDNaKX8qPJHLYDFvdeUsHWczAsUkKkLqvzJv7Uqr+V1mCE\nlm/Ka4VWOf5uKG8mq9mI6/gpG2czyAPnkCFpCnoSx7kz1v7jscsH/9KR8mxBESya\nesd8fGOZRCOEzLuOulx/06cM5dDnOTe/oBYQioy0CJn32S4BC/IknTv5WnBmsy7N\ntIZ8UEIoRtuo/vIoeNJSbdANOlOIrgCcGbOVV5OFQgDryhpdkpBVdf/xTaLofh6r\nl42nJSwpKee4r5yvIekAgri80zPD3q8BNN8Sa5YBITOQeFbcq19cTQIgfQawrnO5\n4YOj3KCc7uGO3HrRJbKeUZ/pzp53E/A28SZNlwIDAQABAoIBAGx1pMeYMw3L2R5K\nurX/aVsf1xI3My5Bdo3IpGsJx+4ZrEOnb4N96FnxMF2kiXd2B44kb/TqxepEOQ2F\nVOi2D2xXP5l2WZGz+ZnBUuOL6ZX8g67B/cGCasMX/4gy51Mj6UvnSKOeMeI7GDW9\nfVWPR4eB+c4XkMju4ne8zKBGBs4pN4KoxTWSnZSM4p+q/Jb/DMa+kVhFfRjkqfWc\nvCpDgHs1uMcHvPBNYO9flDaC2Jgk4cvV9mX0TolXAvaNo8aN0joM7WH3fvw7NCD9\nLCkqCmpjOxJIqJQT1twIkSy42q7VaFi7ApyIaMfXlmnj4UTlVTe5bBO+2AgwLYtC\ncKgDMjECgYEA8JPm3Pc80EsYB6d4qp/Qmy2VrnlaxZwvaRwh63Pssqthg4SZkIp5\nyjXOT4MDlJdrEzMtATRZUXTCRxGFSs0tolNY2KQ2WvYRhISlN8UBkGuMEkRGLuct\np++qpPcSZJcox25kT82CKin1nQYb48k33JAOMUOWIBJO56G35sfPj28CgYEA0BOE\npa+FYj/WxZS79YT1ZbsajeuUKlNUtAIxKJ2cKSQyfFuPM+xZG3H+iroRfR8HCVai\n2+Oz9/TlxZOPR7+P+2fpS8W2tT+Qkmiyz8QJAULd+Irw5XIdatkpVm343XxMx3Pa\n2qtBmgj6RINvsWTWRotMqhcuDRirxqm1IIQhkFkCgYBLNmIhyOXpVODRW8k8xrQI\nH6tBHc2EJD0qRlJQczCX9z6ISIdeCfzjfAjhENuos+IU4ZX7X2thLPikEVUzuou+\nyQHo0QXxUCbP4Exq8Bt6FDV5bIDonvvGGgamhlvouN1V5CxWSrCcD/wquEM15q2h\nNiRJwJCJvE+Q2R1OeD9q3wKBgFWDkAJf7luAjQ3KoKy4pfnXOYSWCuCSOr94Hyfo\nDmPCIpWFM4dNXRmwccIl0kYv2D54QppILJB9L2lRyZLdIZlbDUA802gN5aamLMbC\ndEj2aC9bOsGxcnGVKi4BKEQub4eRD6LKuz1I70H1GpQ3MvDvEuTcfeqX9xDAclYY\nt4qRAoGBAI6YSTs97DUe7Zwk7q+S3PBU5uct4Dwtmy2XWZdgHwl5aP8apSLciL5Y\nPMkpcTMzkuC+QFaPZ8wFcI7GLg0bOs91hkrqscDKEg4nGB9fJkU82iOQZNL4Hv1u\nwO7uIGa2kcpNtQOLNO88y45WFyrn5a+T6VhDmIuc+F+TU1ZzdYdH\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "cmd/gost/.ssl/rootCA.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEpDCCAowCCQDwV08QFUCcSzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls\nb2NhbGhvc3QwHhcNMTgwNzA4MDQ1MzE3WhcNMjEwNDI3MDQ1MzE3WjAUMRIwEAYD\nVQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDE\n+71yX9vQY3l52C31e04ACvm2oNMLSCrLOXGewOTFpv9yXjinMC0Ab6Xa4yB4MPtd\nujWDSEq9gkKCkILoalVX7R4gtLDN+EdoVadBw/WHbrGB4sHnFOWwpUbjiiwwPSU5\nqXjcqIqR2sA1BgoAv6c1qHq7V4bgbEjtGL71KkDoQZSIgNyJlXe1mcUKqmqeAnro\n0WfwrNXyYt66L3PmCy9MIpWRxf9sa8PDsgT3SQEN4BHb70Z8hNj6RXfVGDZcpfcI\niwbrL//YnK7waRKaKD4LoLOodE0cx3fowSWYvlwUAoYx8wFKOcdtM1zaUp+QDAug\nxT5g5ghU150XuqhDU6Wq1An8dLgcDU1D4cxhLk/W8OXtIk4k7yny6eUJi2zHziMm\n8jfHd3M6SwohUrE3LsQI5gpvu4sAVFLMkRxaWZ95XhsVMmIsE/L5FHwDfqid0dvx\nbafOKT+fI3N3BaUPJlVHCNqSzSZIW59+ufnDwBV7SmJj4KMlvixEU+EFfPFdGiCA\nLr0dSG5+Scx1aClaMUeVccCljp2f99IEa9wI+xwMPDStkOmnhVuqG1aEogggQZkD\n/5yh04wrn8EwYCAiasNNUXTV7AoqIt2bgeFbGo2Qr7LdsYuUmaWEzTm0KsHogkkg\nIbd3RPBLDr/WfWI13oHMdsz8jjbXG/D1AhrcdozYDQIDAQABMA0GCSqGSIb3DQEB\nCwUAA4ICAQC1EeQqn2AwrS+UVc5fKRpHzV9ZMiDpxFMRLsDWBP5kNr1nSA72yYcR\nWgxvdqG/rGRds6lvRbvIaWD0zeujPkR3iCpb1V5oRXQ6lWOlY44pZEwCdnDd2M8I\nyQ7BLZCHHmlCN7a51n2o0D78HeILIeeTCQlKFDc5r51qrZbZR5DZmrp9jaZ+3eCg\nLQ3Onfj0WEmQFuMFGQrbJ2oaCC1GvuZWEbRh+lrxjRKOyCaRQFTY4Efe8tIwMm6J\n1iyMtqK7BxminQCfizQrstB67wMljydYeUf+wwbgkiKGYc9VGopckrO3lntzKycu\n9l0BmlZYkmCFt3cv23BcqAbcLdyLXh3yASwVMXaLZ4iVSaslRm4uX+gbKFCBABLa\nvqu7JQHfAPOeYj7zCrN12EHejPxdCjSImBeAbe56vax4uFGAodXxDGcepRItSzax\nqPKJd8U/8e3JDn+wmZNKwD9UGLZPbiuYOg7X+EWhjki0J6ZjgLc8dMleeD2rO+j2\nP/Wgv1gMr6J1svUlqkNf1Ng9eSbl/nMhuOBVOGcPnK7+wCLxM7ByaR0QgeH6/9VO\n4urq53/vspBC679BHsZx3gIhcg4VefmOM2cZnTRM4izPstq1JBQkbuvz+5XuT7Yj\n5Fk1/xkapCUifntKYSoslkkbNHRYxAInqkc0txn3qNBI8GAQFksz5g==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "cmd/gost/.ssl/rootCA.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKAIBAAKCAgEAxPu9cl/b0GN5edgt9XtOAAr5tqDTC0gqyzlxnsDkxab/cl44\npzAtAG+l2uMgeDD7Xbo1g0hKvYJCgpCC6GpVV+0eILSwzfhHaFWnQcP1h26xgeLB\n5xTlsKVG44osMD0lOal43KiKkdrANQYKAL+nNah6u1eG4GxI7Ri+9SpA6EGUiIDc\niZV3tZnFCqpqngJ66NFn8KzV8mLeui9z5gsvTCKVkcX/bGvDw7IE90kBDeAR2+9G\nfITY+kV31Rg2XKX3CIsG6y//2Jyu8GkSmig+C6CzqHRNHMd36MElmL5cFAKGMfMB\nSjnHbTNc2lKfkAwLoMU+YOYIVNedF7qoQ1OlqtQJ/HS4HA1NQ+HMYS5P1vDl7SJO\nJO8p8unlCYtsx84jJvI3x3dzOksKIVKxNy7ECOYKb7uLAFRSzJEcWlmfeV4bFTJi\nLBPy+RR8A36ondHb8W2nzik/nyNzdwWlDyZVRwjaks0mSFuffrn5w8AVe0piY+Cj\nJb4sRFPhBXzxXRoggC69HUhufknMdWgpWjFHlXHApY6dn/fSBGvcCPscDDw0rZDp\np4VbqhtWhKIIIEGZA/+codOMK5/BMGAgImrDTVF01ewKKiLdm4HhWxqNkK+y3bGL\nlJmlhM05tCrB6IJJICG3d0TwSw6/1n1iNd6BzHbM/I421xvw9QIa3HaM2A0CAwEA\nAQKCAgBXbeSH/0PxGjWwfuLnMfNM0ZJEHN2PBFj6GmTzsWnY0GZQvMEoc5mFuAhF\nPsoKjrMCxsM5obyKoGYkzT9NKOT4QaY9nfVbdfc7t8ikx/USR29B1wN5LS1FWhY8\np/c08e6zySR7y9K1KgJlhmiqLGZqynyu6gpTUbyMf49CAZ8Ndw4WCBvadRzM3ZM3\nSKxJtZAYBdm8WPocuwVgXe9zC0PS5wa7zMWxuaMKGNlbaGuvXOSQWYNPgSdM7chi\nLHz0YjVi9VH80TEdU23SBtDa20Gup4UWH4iaXW47QH8PbG4x82zcfp7z8vEw5rsv\nq7xmkvIWSXWGTJMmFQ0EmzRTray5fj38Oo2ZtwHvkJQ1WkiFNFiWFZXnoS3z6h5q\n1lX6ZUhCoobUJRRlDYCwNDV6dMYKXK2NNeD1MPvzUoUIpoQnoxnNF+VYMMENax3e\nYuEiT6xbBXzB/WE0bFVAtSPzf1vPVw+8MP5BhaH3lQb6XA89FiEZg+u95rNpf2SA\ngFWvz0VZsGab+LwYhbYdicmKPRH+2Pzpt5MdWt8jyo066Lv0NP5xpH9IUv/u2RX7\nYcw0Bu1HWKLoEzovoH6OEa0n1A7H+PNOhABzrLvbU8GMp4kEQpXACxR43KruxE7S\nQgotUAb7teCP54yEHTVAe06YIaq4JPk5xqnmMVvaeuy5rvssAQKCAQEA5d4NUONV\n/An+bAf31HicZfRH6Pf1N3JUdjYz2l1y60Pf7dzlDI2fjOWlccp24+efXLM1sMeK\nGXQZsAnYJevZktQxodM67CsgEFgdGhH2s5Ey3Dp5bt3uS/SaFv4sT1x9awYdYtKp\n6fGovjB1Qp/eMuZNJVl9RwegFVzzrrSMxucNzUuL4v4L911ypR4wz4s1ptqI31/U\n56B1VRKjwZntqJaNO2Plt/yY0s+ganhzdKBynoOKzTpYHCqZhHdqvqfc1qC0W1xI\nE/b3Nf0J+GqjjT7JDbWgqNty5ipDfCdIeems96U1Gu9oeKGDzvCgtImZNFMyHzLM\nMhO0v6GA6zkuVQKCAQEA22Cls2AAUuugi2tdZR/krHokrUMPaKvi9RIQpqHpoKqL\nE3rKX9aWyIMhktch7VsnDF52R8CMUhgc7PfL4wsWA5cCq26x4E57aJUH5mxc5va5\nn5Sxb3C99Ytr6GpCkG4Y3pzO93ihgfuW+mQREYLpFYd/c/1SH/e4Wx5nGKx6YocY\n6b/AbxWcRMlihC2gK7JFgSZMqaL+wn68oKJ7j8RUN+ykZEzBXBWL2l4oKWm+qBDx\npOFSQODeQ0CQhPWovD4dVNmyrh5TcDUlJQ3+iU2hRkXe13mLTkGpSM53kwkrfVn+\n4SmVLEm5YhNcHG14A1yDqs6SY//8l5xfUeZyrPBK2QKCAQEAqIsRLm8SG9RkFWge\nQk8RNfxQQbSVu0r8PRTvHjyIx5Ij/e+KjpLFGvVDQtUWKXMquTi5tF4KlzE2qIn/\nT4bIKE2n+qS7vnC8eN9yryvevLlJFotVgIH/ePfnh9ZkPOhvGWsJXu1iIqPLe3Bi\nejBoJuAQTsN4BP3FVgSqtD20Px8pUo8DCbQGqCB/sCwb1AGZnDb+RvKoVBGmFnOt\nWIX56TRCZ/qOdEIk9+W/FHIvDaObhziiLGqMMlLV73fz78l7Nm/s7lQSkXjyuEZJ\n6jiepTEVEBVNsKH/dF4mz0CqdqFs7sPW1WIXMuQSlkh/PQDrMZ+Sz6daa5lhXWUY\n9uAdZQKCAQBrDzRuYIhn7yPPRlsy0ai4X3dsstBfRZsh/Gnx2Ax64x+yJveCY+f7\n/LqyvZiKDDT3PVY92ALiwW/EWX2/1JYutFCSNxhJniNtu2U6l2GTOY8HCPq6puud\nXCgSKWFIuOIcKax7avxuwchBc/o8cIWtgw25HkQo46ytkx2/FdU4JjQLRw/zZjl3\n/Eu+s8F58asnxvgcxTXM1yrYvdLNK4PqMutbI3YtqToyHEc/RqLLxFEZJPkOPm9Z\npLWinXx2OV35HbCsdpJDrTvuZHD2stLkx45j26YXT8X8iP4j3JLDvtq7KZ7qGSSG\nb2pBWU77XPfIsL0SXkf3+VEvV+ZY7X+pAoIBABV6Mu5Yr4UxI+ZgsaKgcK8aKoyD\n5GDshxkxs8R3K1i7eCF0mEjxkV25r11KX10qFvG+hPJqizKQDBOCQC2w3noqz42p\nQVUeBNXpDVGoImD1/4DqUvQMivTwHWS+wSAi/wYAODJ6/bWP5Kil/7iDOwCPp0WD\nmLd0ujjwkOw3Xksn2Gd01pXeiT4FZpkYnyh5ddWGf1TihRFATW5+vpi6t+6KX3LR\nhwd9zi6soSwju/n986NUfGfeewBb6/fnh6hM/vfS2a0Blvk/7yM1k2P0uN+TzLYf\nskhRay10UoMwtXak+q/DBzrrAbW3EwuIdV66H4dx1AV5NMq6kAAtfDXc728=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "cmd/gost/.ssl/rootCA.srl",
    "content": "B45E3577C258EF9E\n"
  },
  {
    "path": "cmd/gost/cfg.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nvar (\n\trouters []router\n)\n\ntype baseConfig struct {\n\troute\n\tRoutes []route\n\tDebug  bool\n}\n\nfunc parseBaseConfig(s string) (*baseConfig, error) {\n\tfile, err := os.Open(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\n\tif err := json.NewDecoder(file).Decode(baseCfg); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn baseCfg, nil\n}\n\nvar (\n\tdefaultCertFile = \"cert.pem\"\n\tdefaultKeyFile  = \"key.pem\"\n)\n\n// Load the certificate from cert & key files and optional client CA file,\n// will use the default certificate if the provided info are invalid.\nfunc tlsConfig(certFile, keyFile, caFile string) (*tls.Config, error) {\n\tif certFile == \"\" || keyFile == \"\" {\n\t\tcertFile, keyFile = defaultCertFile, defaultKeyFile\n\t}\n\n\tcert, err := tls.LoadX509KeyPair(certFile, keyFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcfg := &tls.Config{Certificates: []tls.Certificate{cert}}\n\n\tpool, err := loadCA(caFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif pool != nil {\n\t\tcfg.ClientCAs = pool\n\t\tcfg.ClientAuth = tls.RequireAndVerifyClientCert\n\t}\n\n\treturn cfg, nil\n}\n\nfunc loadCA(caFile string) (cp *x509.CertPool, err error) {\n\tif caFile == \"\" {\n\t\treturn\n\t}\n\tcp = x509.NewCertPool()\n\tdata, err := os.ReadFile(caFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !cp.AppendCertsFromPEM(data) {\n\t\treturn nil, fmt.Errorf(\"loadCA %s: AppendCertsFromPEM failed\", caFile)\n\t}\n\treturn\n}\n\nfunc parseKCPConfig(configFile string) (*gost.KCPConfig, error) {\n\tif configFile == \"\" {\n\t\treturn nil, nil\n\t}\n\tfile, err := os.Open(configFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer file.Close()\n\n\tconfig := &gost.KCPConfig{}\n\tif err = json.NewDecoder(file).Decode(config); err != nil {\n\t\treturn nil, err\n\t}\n\treturn config, nil\n}\n\nfunc parseUsers(authFile string) (users []*url.Userinfo, err error) {\n\tif authFile == \"\" {\n\t\treturn\n\t}\n\n\tfile, err := os.Open(authFile)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer file.Close()\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tline := strings.TrimSpace(scanner.Text())\n\t\tif line == \"\" || strings.HasPrefix(line, \"#\") {\n\t\t\tcontinue\n\t\t}\n\n\t\ts := strings.SplitN(line, \" \", 2)\n\t\tif len(s) == 1 {\n\t\t\tusers = append(users, url.User(strings.TrimSpace(s[0])))\n\t\t} else if len(s) == 2 {\n\t\t\tusers = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1])))\n\t\t}\n\t}\n\n\terr = scanner.Err()\n\treturn\n}\n\nfunc parseAuthenticator(s string) (gost.Authenticator, error) {\n\tif s == \"\" {\n\t\treturn nil, nil\n\t}\n\tf, err := os.Open(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer f.Close()\n\n\tau := gost.NewLocalAuthenticator(nil)\n\tau.Reload(f)\n\n\tgo gost.PeriodReload(au, s)\n\n\treturn au, nil\n}\n\nfunc parseIP(s string, port string) (ips []string) {\n\tif s == \"\" {\n\t\treturn\n\t}\n\tif port == \"\" {\n\t\tport = \"8080\" // default port\n\t}\n\n\taddrFn := func(s, port string) string {\n\t\tc := strings.Count(s, \":\")\n\t\tif c == 0 || //ipv4 or domain\n\t\t\ts[len(s)-1] == ']' { //[ipv6]\n\t\t\treturn s + \":\" + port\n\t\t}\n\t\tif c > 1 && s[0] != '[' { // ipv6\n\t\t\treturn \"[\" + s + \"]:\" + port\n\t\t}\n\t\treturn s //ipv4:port or [ipv6]:port\n\t}\n\n\tfile, err := os.Open(s)\n\tif err != nil {\n\t\tss := strings.Split(s, \",\")\n\t\tfor _, s := range ss {\n\t\t\ts = strings.TrimSpace(s)\n\t\t\tif s != \"\" {\n\t\t\t\tips = append(ips, addrFn(s, port))\n\t\t\t}\n\n\t\t}\n\t\treturn\n\t}\n\tdefer file.Close()\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tline := strings.TrimSpace(scanner.Text())\n\t\tif line == \"\" || strings.HasPrefix(line, \"#\") {\n\t\t\tcontinue\n\t\t}\n\t\tips = append(ips, addrFn(line, port))\n\t}\n\treturn\n}\n\nfunc parseBypass(s string) *gost.Bypass {\n\tif s == \"\" {\n\t\treturn nil\n\t}\n\tvar matchers []gost.Matcher\n\tvar reversed bool\n\tif strings.HasPrefix(s, \"~\") {\n\t\treversed = true\n\t\ts = strings.TrimLeft(s, \"~\")\n\t}\n\n\tf, err := os.Open(s)\n\tif err != nil {\n\t\tfor _, s := range strings.Split(s, \",\") {\n\t\t\ts = strings.TrimSpace(s)\n\t\t\tif s == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmatchers = append(matchers, gost.NewMatcher(s))\n\t\t}\n\t\treturn gost.NewBypass(reversed, matchers...)\n\t}\n\tdefer f.Close()\n\n\tbp := gost.NewBypass(reversed)\n\tbp.Reload(f)\n\tgo gost.PeriodReload(bp, s)\n\n\treturn bp\n}\n\nfunc parseResolver(cfg string) gost.Resolver {\n\tif cfg == \"\" {\n\t\treturn nil\n\t}\n\tvar nss []gost.NameServer\n\n\tf, err := os.Open(cfg)\n\tif err != nil {\n\t\tfor _, s := range strings.Split(cfg, \",\") {\n\t\t\ts = strings.TrimSpace(s)\n\t\t\tif s == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif strings.HasPrefix(s, \"https\") {\n\t\t\t\tp := \"https\"\n\t\t\t\tu, _ := url.Parse(s)\n\t\t\t\tif u == nil || u.Scheme == \"\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif u.Scheme == \"https-chain\" {\n\t\t\t\t\tp = u.Scheme\n\t\t\t\t}\n\t\t\t\tns := gost.NameServer{\n\t\t\t\t\tAddr:     s,\n\t\t\t\t\tProtocol: p,\n\t\t\t\t}\n\t\t\t\tnss = append(nss, ns)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tss := strings.Split(s, \"/\")\n\t\t\tif len(ss) == 1 {\n\t\t\t\tns := gost.NameServer{\n\t\t\t\t\tAddr: ss[0],\n\t\t\t\t}\n\t\t\t\tnss = append(nss, ns)\n\t\t\t}\n\t\t\tif len(ss) == 2 {\n\t\t\t\tns := gost.NameServer{\n\t\t\t\t\tAddr:     ss[0],\n\t\t\t\t\tProtocol: ss[1],\n\t\t\t\t}\n\t\t\t\tnss = append(nss, ns)\n\t\t\t}\n\t\t}\n\t\treturn gost.NewResolver(0, nss...)\n\t}\n\tdefer f.Close()\n\n\tresolver := gost.NewResolver(0)\n\tresolver.Reload(f)\n\n\tgo gost.PeriodReload(resolver, cfg)\n\n\treturn resolver\n}\n\nfunc parseHosts(s string) *gost.Hosts {\n\tf, err := os.Open(s)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tdefer f.Close()\n\n\thosts := gost.NewHosts()\n\thosts.Reload(f)\n\n\tgo gost.PeriodReload(hosts, s)\n\n\treturn hosts\n}\n\nfunc parseIPRoutes(s string) (routes []gost.IPRoute) {\n\tif s == \"\" {\n\t\treturn\n\t}\n\n\tfile, err := os.Open(s)\n\tif err != nil {\n\t\tss := strings.Split(s, \",\")\n\t\tfor _, s := range ss {\n\t\t\tif _, inet, _ := net.ParseCIDR(strings.TrimSpace(s)); inet != nil {\n\t\t\t\troutes = append(routes, gost.IPRoute{Dest: inet})\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tdefer file.Close()\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tline := strings.Replace(scanner.Text(), \"\\t\", \" \", -1)\n\t\tline = strings.TrimSpace(line)\n\t\tif line == \"\" || strings.HasPrefix(line, \"#\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar route gost.IPRoute\n\t\tvar ss []string\n\t\tfor _, s := range strings.Split(line, \" \") {\n\t\t\tif s = strings.TrimSpace(s); s != \"\" {\n\t\t\t\tss = append(ss, s)\n\t\t\t}\n\t\t}\n\t\tif len(ss) > 0 && ss[0] != \"\" {\n\t\t\t_, route.Dest, _ = net.ParseCIDR(strings.TrimSpace(ss[0]))\n\t\t\tif route.Dest == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tif len(ss) > 1 && ss[1] != \"\" {\n\t\t\troute.Gateway = net.ParseIP(ss[1])\n\t\t}\n\t\troutes = append(routes, route)\n\t}\n\treturn routes\n}\n"
  },
  {
    "path": "cmd/gost/main.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"runtime\"\n\n\t_ \"net/http/pprof\"\n\n\t\"github.com/ginuerzh/gost\"\n\t\"github.com/go-log/log\"\n)\n\nvar (\n\tconfigureFile string\n\tbaseCfg       = &baseConfig{}\n\tpprofAddr     string\n\tpprofEnabled  = os.Getenv(\"PROFILING\") != \"\"\n)\n\nfunc init() {\n\tgost.SetLogger(&gost.LogLogger{})\n\n\tvar (\n\t\tprintVersion bool\n\t)\n\n\tflag.Var(&baseCfg.route.ChainNodes, \"F\", \"forward address, can make a forward chain\")\n\tflag.Var(&baseCfg.route.ServeNodes, \"L\", \"listen address, can listen on multiple ports (required)\")\n\tflag.IntVar(&baseCfg.route.Mark, \"M\", 0, \"Specify out connection mark\")\n\tflag.StringVar(&configureFile, \"C\", \"\", \"configure file\")\n\tflag.StringVar(&baseCfg.route.Interface, \"I\", \"\", \"Interface to bind\")\n\tflag.BoolVar(&baseCfg.Debug, \"D\", false, \"enable debug log\")\n\tflag.BoolVar(&printVersion, \"V\", false, \"print version\")\n\tif pprofEnabled {\n\t\tflag.StringVar(&pprofAddr, \"P\", \":6060\", \"profiling HTTP server address\")\n\t}\n\tflag.Parse()\n\n\tif printVersion {\n\t\tfmt.Fprintf(os.Stdout, \"gost %s (%s %s/%s)\\n\",\n\t\t\tgost.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)\n\t\tos.Exit(0)\n\t}\n\n\tif configureFile != \"\" {\n\t\t_, err := parseBaseConfig(configureFile)\n\t\tif err != nil {\n\t\t\tlog.Log(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\tif flag.NFlag() == 0 {\n\t\tflag.PrintDefaults()\n\t\tos.Exit(0)\n\t}\n}\n\nfunc main() {\n\tif pprofEnabled {\n\t\tgo func() {\n\t\t\tlog.Log(\"profiling server on\", pprofAddr)\n\t\t\tlog.Log(http.ListenAndServe(pprofAddr, nil))\n\t\t}()\n\t}\n\n\t// NOTE: as of 2.6, you can use custom cert/key files to initialize the default certificate.\n\ttlsConfig, err := tlsConfig(defaultCertFile, defaultKeyFile, \"\")\n\tif err != nil {\n\t\t// generate random self-signed certificate.\n\t\tcert, err := gost.GenCertificate()\n\t\tif err != nil {\n\t\t\tlog.Log(err)\n\t\t\tos.Exit(1)\n\t\t}\n\t\ttlsConfig = &tls.Config{\n\t\t\tCertificates: []tls.Certificate{cert},\n\t\t}\n\t} else {\n\t\tlog.Log(\"load TLS certificate files OK\")\n\t}\n\n\tgost.DefaultTLSConfig = tlsConfig\n\n\tif err := start(); err != nil {\n\t\tlog.Log(err)\n\t\tos.Exit(1)\n\t}\n\n\tselect {}\n}\n\nfunc start() error {\n\tgost.Debug = baseCfg.Debug\n\n\tvar routers []router\n\trts, err := baseCfg.route.GenRouters()\n\tif err != nil {\n\t\treturn err\n\t}\n\trouters = append(routers, rts...)\n\n\tfor _, route := range baseCfg.Routes {\n\t\trts, err := route.GenRouters()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trouters = append(routers, rts...)\n\t}\n\n\tif len(routers) == 0 {\n\t\treturn errors.New(\"invalid config\")\n\t}\n\tfor i := range routers {\n\t\tgo routers[i].Serve()\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "cmd/gost/peer.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\ntype peerConfig struct {\n\tStrategy     string `json:\"strategy\"`\n\tMaxFails     int    `json:\"max_fails\"`\n\tFastestCount int    `json:\"fastest_count\"` // topN fastest node count\n\tFailTimeout  time.Duration\n\tperiod       time.Duration // the period for live reloading\n\n\tNodes     []string `json:\"nodes\"`\n\tgroup     *gost.NodeGroup\n\tbaseNodes []gost.Node\n\tstopped   chan struct{}\n}\n\nfunc newPeerConfig() *peerConfig {\n\treturn &peerConfig{\n\t\tstopped: make(chan struct{}),\n\t}\n}\n\nfunc (cfg *peerConfig) Validate() {\n}\n\nfunc (cfg *peerConfig) Reload(r io.Reader) error {\n\tif cfg.Stopped() {\n\t\treturn nil\n\t}\n\n\tif err := cfg.parse(r); err != nil {\n\t\treturn err\n\t}\n\tcfg.Validate()\n\n\tgroup := cfg.group\n\tgroup.SetSelector(\n\t\tnil,\n\t\tgost.WithFilter(\n\t\t\t&gost.FailFilter{\n\t\t\t\tMaxFails:    cfg.MaxFails,\n\t\t\t\tFailTimeout: cfg.FailTimeout,\n\t\t\t},\n\t\t\t&gost.InvalidFilter{},\n\t\t\tgost.NewFastestFilter(0, cfg.FastestCount),\n\t\t),\n\t\tgost.WithStrategy(gost.NewStrategy(cfg.Strategy)),\n\t)\n\n\tgNodes := cfg.baseNodes\n\tnid := len(gNodes) + 1\n\tfor _, s := range cfg.Nodes {\n\t\tnodes, err := parseChainNode(s)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor i := range nodes {\n\t\t\tnodes[i].ID = nid\n\t\t\tnid++\n\t\t}\n\n\t\tgNodes = append(gNodes, nodes...)\n\t}\n\n\tnodes := group.SetNodes(gNodes...)\n\tfor _, node := range nodes[len(cfg.baseNodes):] {\n\t\tif node.Bypass != nil {\n\t\t\tnode.Bypass.Stop() // clear the old nodes\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (cfg *peerConfig) parse(r io.Reader) error {\n\tdata, err := io.ReadAll(r)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// compatible with JSON format\n\tif err := json.NewDecoder(bytes.NewReader(data)).Decode(cfg); err == nil {\n\t\treturn nil\n\t}\n\n\tsplit := func(line string) []string {\n\t\tif line == \"\" {\n\t\t\treturn nil\n\t\t}\n\t\tif n := strings.IndexByte(line, '#'); n >= 0 {\n\t\t\tline = line[:n]\n\t\t}\n\t\tline = strings.Replace(line, \"\\t\", \" \", -1)\n\t\tline = strings.TrimSpace(line)\n\n\t\tvar ss []string\n\t\tfor _, s := range strings.Split(line, \" \") {\n\t\t\tif s = strings.TrimSpace(s); s != \"\" {\n\t\t\t\tss = append(ss, s)\n\t\t\t}\n\t\t}\n\t\treturn ss\n\t}\n\n\tcfg.Nodes = nil\n\tscanner := bufio.NewScanner(bytes.NewReader(data))\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tss := split(line)\n\t\tif len(ss) < 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch ss[0] {\n\t\tcase \"strategy\":\n\t\t\tcfg.Strategy = ss[1]\n\t\tcase \"max_fails\":\n\t\t\tcfg.MaxFails, _ = strconv.Atoi(ss[1])\n\t\tcase \"fastest_count\":\n\t\t\tcfg.FastestCount, _ = strconv.Atoi(ss[1])\n\t\tcase \"fail_timeout\":\n\t\t\tcfg.FailTimeout, _ = time.ParseDuration(ss[1])\n\t\tcase \"reload\":\n\t\t\tcfg.period, _ = time.ParseDuration(ss[1])\n\t\tcase \"peer\":\n\t\t\tcfg.Nodes = append(cfg.Nodes, ss[1])\n\t\t}\n\t}\n\n\treturn scanner.Err()\n}\n\nfunc (cfg *peerConfig) Period() time.Duration {\n\tif cfg.Stopped() {\n\t\treturn -1\n\t}\n\treturn cfg.period\n}\n\n// Stop stops reloading.\nfunc (cfg *peerConfig) Stop() {\n\tselect {\n\tcase <-cfg.stopped:\n\tdefault:\n\t\tclose(cfg.stopped)\n\t}\n}\n\n// Stopped checks whether the reloader is stopped.\nfunc (cfg *peerConfig) Stopped() bool {\n\tselect {\n\tcase <-cfg.stopped:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "cmd/gost/route.go",
    "content": "package main\n\nimport (\n\t\"crypto/sha256\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ginuerzh/gost\"\n\t\"github.com/go-log/log\"\n)\n\ntype stringList []string\n\nfunc (l *stringList) String() string {\n\treturn fmt.Sprintf(\"%s\", *l)\n}\nfunc (l *stringList) Set(value string) error {\n\t*l = append(*l, value)\n\treturn nil\n}\n\ntype route struct {\n\tServeNodes stringList\n\tChainNodes stringList\n\tRetries    int\n\tMark       int\n\tInterface  string\n}\n\nfunc (r *route) parseChain() (*gost.Chain, error) {\n\tchain := gost.NewChain()\n\tchain.Retries = r.Retries\n\tchain.Mark = r.Mark\n\tchain.Interface = r.Interface\n\tgid := 1 // group ID\n\n\tfor _, ns := range r.ChainNodes {\n\t\tngroup := gost.NewNodeGroup()\n\t\tngroup.ID = gid\n\t\tgid++\n\n\t\t// parse the base nodes\n\t\tnodes, err := parseChainNode(ns)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tnid := 1 // node ID\n\t\tfor i := range nodes {\n\t\t\tnodes[i].ID = nid\n\t\t\tnid++\n\t\t}\n\t\tngroup.AddNode(nodes...)\n\n\t\tngroup.SetSelector(nil,\n\t\t\tgost.WithFilter(\n\t\t\t\t&gost.FailFilter{\n\t\t\t\t\tMaxFails:    nodes[0].GetInt(\"max_fails\"),\n\t\t\t\t\tFailTimeout: nodes[0].GetDuration(\"fail_timeout\"),\n\t\t\t\t},\n\t\t\t\t&gost.InvalidFilter{},\n\t\t\t\tgost.NewFastestFilter(0, nodes[0].GetInt(\"fastest_count\")),\n\t\t\t),\n\t\t\tgost.WithStrategy(gost.NewStrategy(nodes[0].Get(\"strategy\"))),\n\t\t)\n\n\t\tif cfg := nodes[0].Get(\"peer\"); cfg != \"\" {\n\t\t\tf, err := os.Open(cfg)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tpeerCfg := newPeerConfig()\n\t\t\tpeerCfg.group = ngroup\n\t\t\tpeerCfg.baseNodes = nodes\n\t\t\tpeerCfg.Reload(f)\n\t\t\tf.Close()\n\n\t\t\tgo gost.PeriodReload(peerCfg, cfg)\n\t\t}\n\n\t\tchain.AddNodeGroup(ngroup)\n\t}\n\n\treturn chain, nil\n}\n\nfunc parseChainNode(ns string) (nodes []gost.Node, err error) {\n\tnode, err := gost.ParseNode(ns)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif auth := node.Get(\"auth\"); auth != \"\" && node.User == nil {\n\t\tc, err := base64.StdEncoding.DecodeString(auth)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcs := string(c)\n\t\ts := strings.IndexByte(cs, ':')\n\t\tif s < 0 {\n\t\t\tnode.User = url.User(cs)\n\t\t} else {\n\t\t\tnode.User = url.UserPassword(cs[:s], cs[s+1:])\n\t\t}\n\t}\n\tif node.User == nil {\n\t\tusers, err := parseUsers(node.Get(\"secrets\"))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(users) > 0 {\n\t\t\tnode.User = users[0]\n\t\t}\n\t}\n\n\tserverName, sport, _ := net.SplitHostPort(node.Addr)\n\tif serverName == \"\" {\n\t\tserverName = \"localhost\" // default server name\n\t}\n\n\trootCAs, err := loadCA(node.Get(\"ca\"))\n\tif err != nil {\n\t\treturn\n\t}\n\ttlsCfg := &tls.Config{\n\t\tServerName:         serverName,\n\t\tInsecureSkipVerify: !node.GetBool(\"secure\"),\n\t\tRootCAs:            rootCAs,\n\t}\n\n\t// If the argument `ca` is given, but not open `secure`, we verify the\n\t// certificate manually.\n\tif rootCAs != nil && !node.GetBool(\"secure\") {\n\t\ttlsCfg.VerifyConnection = func(state tls.ConnectionState) error {\n\t\t\topts := x509.VerifyOptions{\n\t\t\t\tRoots:         rootCAs,\n\t\t\t\tCurrentTime:   time.Now(),\n\t\t\t\tDNSName:       \"\",\n\t\t\t\tIntermediates: x509.NewCertPool(),\n\t\t\t}\n\n\t\t\tcerts := state.PeerCertificates\n\t\t\tfor i, cert := range certs {\n\t\t\t\tif i == 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\topts.Intermediates.AddCert(cert)\n\t\t\t}\n\n\t\t\t_, err = certs[0].Verify(opts)\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif cert, err := tls.LoadX509KeyPair(node.Get(\"cert\"), node.Get(\"key\")); err == nil {\n\t\ttlsCfg.Certificates = []tls.Certificate{cert}\n\t}\n\n\twsOpts := &gost.WSOptions{}\n\twsOpts.EnableCompression = node.GetBool(\"compression\")\n\twsOpts.ReadBufferSize = node.GetInt(\"rbuf\")\n\twsOpts.WriteBufferSize = node.GetInt(\"wbuf\")\n\twsOpts.UserAgent = node.Get(\"agent\")\n\twsOpts.Path = node.Get(\"path\")\n\n\ttimeout := node.GetDuration(\"timeout\")\n\n\tvar tr gost.Transporter\n\tswitch node.Transport {\n\tcase \"tls\":\n\t\ttr = gost.TLSTransporter()\n\tcase \"mtls\":\n\t\ttr = gost.MTLSTransporter()\n\tcase \"ws\":\n\t\ttr = gost.WSTransporter(wsOpts)\n\tcase \"mws\":\n\t\ttr = gost.MWSTransporter(wsOpts)\n\tcase \"wss\":\n\t\ttr = gost.WSSTransporter(wsOpts)\n\tcase \"mwss\":\n\t\ttr = gost.MWSSTransporter(wsOpts)\n\tcase \"kcp\":\n\t\tconfig, err := parseKCPConfig(node.Get(\"c\"))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif config == nil {\n\t\t\tconf := gost.DefaultKCPConfig\n\t\t\tif node.GetBool(\"tcp\") {\n\t\t\t\tconf.TCP = true\n\t\t\t}\n\t\t\tconfig = &conf\n\t\t}\n\t\ttr = gost.KCPTransporter(config)\n\tcase \"ssh\":\n\t\tif node.Protocol == \"direct\" || node.Protocol == \"remote\" {\n\t\t\ttr = gost.SSHForwardTransporter()\n\t\t} else {\n\t\t\ttr = gost.SSHTunnelTransporter()\n\t\t}\n\tcase \"quic\":\n\t\tconfig := &gost.QUICConfig{\n\t\t\tTLSConfig:   tlsCfg,\n\t\t\tKeepAlive:   node.GetBool(\"keepalive\"),\n\t\t\tTimeout:     timeout,\n\t\t\tIdleTimeout: node.GetDuration(\"idle\"),\n\t\t}\n\t\tif config.KeepAlive {\n\t\t\tconfig.KeepAlivePeriod = node.GetDuration(\"ttl\")\n\t\t\tif config.KeepAlivePeriod == 0 {\n\t\t\t\tconfig.KeepAlivePeriod = 10 * time.Second\n\t\t\t}\n\t\t}\n\n\t\tif cipher := node.Get(\"cipher\"); cipher != \"\" {\n\t\t\tsum := sha256.Sum256([]byte(cipher))\n\t\t\tconfig.Key = sum[:]\n\t\t}\n\n\t\ttr = gost.QUICTransporter(config)\n\tcase \"http2\":\n\t\ttr = gost.HTTP2Transporter(tlsCfg)\n\tcase \"h2\":\n\t\ttr = gost.H2Transporter(tlsCfg, node.Get(\"path\"))\n\tcase \"h2c\":\n\t\ttr = gost.H2CTransporter(node.Get(\"path\"))\n\tcase \"obfs4\":\n\t\ttr = gost.Obfs4Transporter()\n\tcase \"ohttp\":\n\t\ttr = gost.ObfsHTTPTransporter()\n\tcase \"otls\":\n\t\ttr = gost.ObfsTLSTransporter()\n\tcase \"ftcp\":\n\t\ttr = gost.FakeTCPTransporter()\n\tcase \"udp\":\n\t\ttr = gost.UDPTransporter()\n\tcase \"vsock\":\n\t\ttr = gost.VSOCKTransporter()\n\tdefault:\n\t\ttr = gost.TCPTransporter()\n\t}\n\n\tvar connector gost.Connector\n\tswitch node.Protocol {\n\tcase \"http2\":\n\t\tconnector = gost.HTTP2Connector(node.User)\n\tcase \"socks\", \"socks5\":\n\t\tconnector = gost.SOCKS5Connector(node.User)\n\tcase \"socks4\":\n\t\tconnector = gost.SOCKS4Connector()\n\tcase \"socks4a\":\n\t\tconnector = gost.SOCKS4AConnector()\n\tcase \"ss\":\n\t\tconnector = gost.ShadowConnector(node.User)\n\tcase \"ssu\":\n\t\tconnector = gost.ShadowUDPConnector(node.User)\n\tcase \"direct\":\n\t\tconnector = gost.SSHDirectForwardConnector()\n\tcase \"remote\":\n\t\tconnector = gost.SSHRemoteForwardConnector()\n\tcase \"forward\":\n\t\tconnector = gost.ForwardConnector()\n\tcase \"sni\":\n\t\tconnector = gost.SNIConnector(node.Get(\"host\"))\n\tcase \"http\":\n\t\tconnector = gost.HTTPConnector(node.User)\n\tcase \"relay\":\n\t\tconnector = gost.RelayConnector(node.User)\n\tdefault:\n\t\tconnector = gost.AutoConnector(node.User)\n\t}\n\n\thost := node.Get(\"host\")\n\tif host == \"\" {\n\t\thost = node.Host\n\t}\n\n\tnode.DialOptions = append(node.DialOptions,\n\t\tgost.TimeoutDialOption(timeout),\n\t\tgost.HostDialOption(host),\n\t)\n\n\tnode.ConnectOptions = []gost.ConnectOption{\n\t\tgost.UserAgentConnectOption(node.Get(\"agent\")),\n\t\tgost.NoTLSConnectOption(node.GetBool(\"notls\")),\n\t\tgost.NoDelayConnectOption(node.GetBool(\"nodelay\")),\n\t}\n\n\tsshConfig := &gost.SSHConfig{}\n\tif s := node.Get(\"ssh_key\"); s != \"\" {\n\t\tkey, err := gost.ParseSSHKeyFile(s)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsshConfig.Key = key\n\t}\n\thandshakeOptions := []gost.HandshakeOption{\n\t\tgost.AddrHandshakeOption(node.Addr),\n\t\tgost.HostHandshakeOption(host),\n\t\tgost.UserHandshakeOption(node.User),\n\t\tgost.TLSConfigHandshakeOption(tlsCfg),\n\t\tgost.IntervalHandshakeOption(node.GetDuration(\"ping\")),\n\t\tgost.TimeoutHandshakeOption(timeout),\n\t\tgost.RetryHandshakeOption(node.GetInt(\"retry\")),\n\t\tgost.SSHConfigHandshakeOption(sshConfig),\n\t}\n\n\tnode.Client = &gost.Client{\n\t\tConnector:   connector,\n\t\tTransporter: tr,\n\t}\n\n\tnode.Bypass = parseBypass(node.Get(\"bypass\"))\n\n\tips := parseIP(node.Get(\"ip\"), sport)\n\tfor _, ip := range ips {\n\t\tnd := node.Clone()\n\t\tnd.Addr = ip\n\t\t// override the default node address\n\t\tnd.HandshakeOptions = append(handshakeOptions, gost.AddrHandshakeOption(ip))\n\t\t// One node per IP\n\t\tnodes = append(nodes, nd)\n\t}\n\tif len(ips) == 0 {\n\t\tnode.HandshakeOptions = handshakeOptions\n\t\tnodes = []gost.Node{node}\n\t}\n\n\tif node.Transport == \"obfs4\" {\n\t\tfor i := range nodes {\n\t\t\tif err := gost.Obfs4Init(nodes[i], false); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (r *route) GenRouters() ([]router, error) {\n\tchain, err := r.parseChain()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar rts []router\n\n\tfor _, ns := range r.ServeNodes {\n\t\tnode, err := gost.ParseNode(ns)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif auth := node.Get(\"auth\"); auth != \"\" && node.User == nil {\n\t\t\tc, err := base64.StdEncoding.DecodeString(auth)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcs := string(c)\n\t\t\ts := strings.IndexByte(cs, ':')\n\t\t\tif s < 0 {\n\t\t\t\tnode.User = url.User(cs)\n\t\t\t} else {\n\t\t\t\tnode.User = url.UserPassword(cs[:s], cs[s+1:])\n\t\t\t}\n\t\t}\n\t\tauthenticator, err := parseAuthenticator(node.Get(\"secrets\"))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif authenticator == nil && node.User != nil {\n\t\t\tkvs := make(map[string]string)\n\t\t\tkvs[node.User.Username()], _ = node.User.Password()\n\t\t\tauthenticator = gost.NewLocalAuthenticator(kvs)\n\t\t}\n\t\tif node.User == nil {\n\t\t\tif users, _ := parseUsers(node.Get(\"secrets\")); len(users) > 0 {\n\t\t\t\tnode.User = users[0]\n\t\t\t}\n\t\t}\n\t\tcertFile, keyFile := node.Get(\"cert\"), node.Get(\"key\")\n\t\ttlsCfg, err := tlsConfig(certFile, keyFile, node.Get(\"ca\"))\n\t\tif err != nil && certFile != \"\" && keyFile != \"\" {\n\t\t\treturn nil, err\n\t\t}\n\n\t\twsOpts := &gost.WSOptions{}\n\t\twsOpts.EnableCompression = node.GetBool(\"compression\")\n\t\twsOpts.ReadBufferSize = node.GetInt(\"rbuf\")\n\t\twsOpts.WriteBufferSize = node.GetInt(\"wbuf\")\n\t\twsOpts.Path = node.Get(\"path\")\n\n\t\tttl := node.GetDuration(\"ttl\")\n\t\ttimeout := node.GetDuration(\"timeout\")\n\n\t\ttunRoutes := parseIPRoutes(node.Get(\"route\"))\n\t\tgw := net.ParseIP(node.Get(\"gw\")) // default gateway\n\t\tfor i := range tunRoutes {\n\t\t\tif tunRoutes[i].Gateway == nil {\n\t\t\t\ttunRoutes[i].Gateway = gw\n\t\t\t}\n\t\t}\n\n\t\tvar ln gost.Listener\n\t\tswitch node.Transport {\n\t\tcase \"tls\":\n\t\t\tln, err = gost.TLSListener(node.Addr, tlsCfg)\n\t\tcase \"mtls\":\n\t\t\tln, err = gost.MTLSListener(node.Addr, tlsCfg)\n\t\tcase \"ws\":\n\t\t\tln, err = gost.WSListener(node.Addr, wsOpts)\n\t\tcase \"mws\":\n\t\t\tln, err = gost.MWSListener(node.Addr, wsOpts)\n\t\tcase \"wss\":\n\t\t\tln, err = gost.WSSListener(node.Addr, tlsCfg, wsOpts)\n\t\tcase \"mwss\":\n\t\t\tln, err = gost.MWSSListener(node.Addr, tlsCfg, wsOpts)\n\t\tcase \"kcp\":\n\t\t\tconfig, er := parseKCPConfig(node.Get(\"c\"))\n\t\t\tif er != nil {\n\t\t\t\treturn nil, er\n\t\t\t}\n\t\t\tif config == nil {\n\t\t\t\tconf := gost.DefaultKCPConfig\n\t\t\t\tif node.GetBool(\"tcp\") {\n\t\t\t\t\tconf.TCP = true\n\t\t\t\t}\n\t\t\t\tconfig = &conf\n\t\t\t}\n\t\t\tln, err = gost.KCPListener(node.Addr, config)\n\t\tcase \"ssh\":\n\t\t\tconfig := &gost.SSHConfig{\n\t\t\t\tAuthenticator: authenticator,\n\t\t\t\tTLSConfig:     tlsCfg,\n\t\t\t}\n\t\t\tif s := node.Get(\"ssh_key\"); s != \"\" {\n\t\t\t\tkey, err := gost.ParseSSHKeyFile(s)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tconfig.Key = key\n\t\t\t}\n\t\t\tif s := node.Get(\"ssh_authorized_keys\"); s != \"\" {\n\t\t\t\tkeys, err := gost.ParseSSHAuthorizedKeysFile(s)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tconfig.AuthorizedKeys = keys\n\t\t\t}\n\t\t\tif node.Protocol == \"forward\" {\n\t\t\t\tln, err = gost.TCPListener(node.Addr)\n\t\t\t} else {\n\t\t\t\tln, err = gost.SSHTunnelListener(node.Addr, config)\n\t\t\t}\n\t\tcase \"quic\":\n\t\t\tconfig := &gost.QUICConfig{\n\t\t\t\tTLSConfig:   tlsCfg,\n\t\t\t\tKeepAlive:   node.GetBool(\"keepalive\"),\n\t\t\t\tTimeout:     timeout,\n\t\t\t\tIdleTimeout: node.GetDuration(\"idle\"),\n\t\t\t}\n\t\t\tif config.KeepAlive {\n\t\t\t\tconfig.KeepAlivePeriod = node.GetDuration(\"ttl\")\n\t\t\t\tif config.KeepAlivePeriod == 0 {\n\t\t\t\t\tconfig.KeepAlivePeriod = 10 * time.Second\n\t\t\t\t}\n\t\t\t}\n\t\t\tif cipher := node.Get(\"cipher\"); cipher != \"\" {\n\t\t\t\tsum := sha256.Sum256([]byte(cipher))\n\t\t\t\tconfig.Key = sum[:]\n\t\t\t}\n\n\t\t\tln, err = gost.QUICListener(node.Addr, config)\n\t\tcase \"http2\":\n\t\t\tln, err = gost.HTTP2Listener(node.Addr, tlsCfg)\n\t\tcase \"h2\":\n\t\t\tln, err = gost.H2Listener(node.Addr, tlsCfg, node.Get(\"path\"))\n\t\tcase \"h2c\":\n\t\t\tln, err = gost.H2CListener(node.Addr, node.Get(\"path\"))\n\t\tcase \"tcp\":\n\t\t\t// Directly use SSH port forwarding if the last chain node is forward+ssh\n\t\t\tif chain.LastNode().Protocol == \"forward\" && chain.LastNode().Transport == \"ssh\" {\n\t\t\t\tchain.Nodes()[len(chain.Nodes())-1].Client.Connector = gost.SSHDirectForwardConnector()\n\t\t\t\tchain.Nodes()[len(chain.Nodes())-1].Client.Transporter = gost.SSHForwardTransporter()\n\t\t\t}\n\t\t\tln, err = gost.TCPListener(node.Addr)\n\t\tcase \"vsock\":\n\t\t\tln, err = gost.VSOCKListener(node.Addr)\n\t\tcase \"udp\":\n\t\t\tln, err = gost.UDPListener(node.Addr, &gost.UDPListenConfig{\n\t\t\t\tTTL:       ttl,\n\t\t\t\tBacklog:   node.GetInt(\"backlog\"),\n\t\t\t\tQueueSize: node.GetInt(\"queue\"),\n\t\t\t})\n\t\tcase \"rtcp\":\n\t\t\t// Directly use SSH port forwarding if the last chain node is forward+ssh\n\t\t\tif chain.LastNode().Protocol == \"forward\" && chain.LastNode().Transport == \"ssh\" {\n\t\t\t\tchain.Nodes()[len(chain.Nodes())-1].Client.Connector = gost.SSHRemoteForwardConnector()\n\t\t\t\tchain.Nodes()[len(chain.Nodes())-1].Client.Transporter = gost.SSHForwardTransporter()\n\t\t\t}\n\t\t\tln, err = gost.TCPRemoteForwardListener(node.Addr, chain)\n\t\tcase \"rudp\":\n\t\t\tln, err = gost.UDPRemoteForwardListener(node.Addr,\n\t\t\t\tchain,\n\t\t\t\t&gost.UDPListenConfig{\n\t\t\t\t\tTTL:       ttl,\n\t\t\t\t\tBacklog:   node.GetInt(\"backlog\"),\n\t\t\t\t\tQueueSize: node.GetInt(\"queue\"),\n\t\t\t\t})\n\t\tcase \"obfs4\":\n\t\t\tif err = gost.Obfs4Init(node, true); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tln, err = gost.Obfs4Listener(node.Addr)\n\t\tcase \"ohttp\":\n\t\t\tln, err = gost.ObfsHTTPListener(node.Addr)\n\t\tcase \"otls\":\n\t\t\tln, err = gost.ObfsTLSListener(node.Addr)\n\t\tcase \"tun\":\n\t\t\tcfg := gost.TunConfig{\n\t\t\t\tName:    node.Get(\"name\"),\n\t\t\t\tAddr:    node.Get(\"net\"),\n\t\t\t\tPeer:    node.Get(\"peer\"),\n\t\t\t\tMTU:     node.GetInt(\"mtu\"),\n\t\t\t\tRoutes:  tunRoutes,\n\t\t\t\tGateway: node.Get(\"gw\"),\n\t\t\t}\n\t\t\tln, err = gost.TunListener(cfg)\n\t\tcase \"tap\":\n\t\t\tcfg := gost.TapConfig{\n\t\t\t\tName:    node.Get(\"name\"),\n\t\t\t\tAddr:    node.Get(\"net\"),\n\t\t\t\tMTU:     node.GetInt(\"mtu\"),\n\t\t\t\tRoutes:  strings.Split(node.Get(\"route\"), \",\"),\n\t\t\t\tGateway: node.Get(\"gw\"),\n\t\t\t}\n\t\t\tln, err = gost.TapListener(cfg)\n\t\tcase \"ftcp\":\n\t\t\tln, err = gost.FakeTCPListener(\n\t\t\t\tnode.Addr,\n\t\t\t\t&gost.FakeTCPListenConfig{\n\t\t\t\t\tTTL:       ttl,\n\t\t\t\t\tBacklog:   node.GetInt(\"backlog\"),\n\t\t\t\t\tQueueSize: node.GetInt(\"queue\"),\n\t\t\t\t},\n\t\t\t)\n\t\tcase \"dns\":\n\t\t\tln, err = gost.DNSListener(\n\t\t\t\tnode.Addr,\n\t\t\t\t&gost.DNSOptions{\n\t\t\t\t\tMode:      node.Get(\"mode\"),\n\t\t\t\t\tTLSConfig: tlsCfg,\n\t\t\t\t},\n\t\t\t)\n\t\tcase \"redu\", \"redirectu\":\n\t\t\tln, err = gost.UDPRedirectListener(node.Addr, &gost.UDPListenConfig{\n\t\t\t\tTTL:       ttl,\n\t\t\t\tBacklog:   node.GetInt(\"backlog\"),\n\t\t\t\tQueueSize: node.GetInt(\"queue\"),\n\t\t\t})\n\t\tdefault:\n\t\t\tln, err = gost.TCPListener(node.Addr)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar handler gost.Handler\n\t\tswitch node.Protocol {\n\t\tcase \"http2\":\n\t\t\thandler = gost.HTTP2Handler()\n\t\tcase \"socks\", \"socks5\":\n\t\t\thandler = gost.SOCKS5Handler()\n\t\tcase \"socks4\", \"socks4a\":\n\t\t\thandler = gost.SOCKS4Handler()\n\t\tcase \"ss\":\n\t\t\thandler = gost.ShadowHandler()\n\t\tcase \"http\":\n\t\t\thandler = gost.HTTPHandler()\n\t\tcase \"tcp\":\n\t\t\thandler = gost.TCPDirectForwardHandler(node.Remote)\n\t\tcase \"rtcp\":\n\t\t\thandler = gost.TCPRemoteForwardHandler(node.Remote)\n\t\tcase \"udp\":\n\t\t\thandler = gost.UDPDirectForwardHandler(node.Remote)\n\t\tcase \"rudp\":\n\t\t\thandler = gost.UDPRemoteForwardHandler(node.Remote)\n\t\tcase \"forward\":\n\t\t\thandler = gost.SSHForwardHandler()\n\t\tcase \"red\", \"redirect\":\n\t\t\thandler = gost.TCPRedirectHandler()\n\t\tcase \"redu\", \"redirectu\":\n\t\t\thandler = gost.UDPRedirectHandler()\n\t\tcase \"ssu\":\n\t\t\thandler = gost.ShadowUDPHandler()\n\t\tcase \"sni\":\n\t\t\thandler = gost.SNIHandler()\n\t\tcase \"tun\":\n\t\t\thandler = gost.TunHandler()\n\t\tcase \"tap\":\n\t\t\thandler = gost.TapHandler()\n\t\tcase \"dns\":\n\t\t\thandler = gost.DNSHandler(node.Remote)\n\t\tcase \"relay\":\n\t\t\thandler = gost.RelayHandler(node.Remote)\n\t\tdefault:\n\t\t\t// start from 2.5, if remote is not empty, then we assume that it is a forward tunnel.\n\t\t\tif node.Remote != \"\" {\n\t\t\t\thandler = gost.TCPDirectForwardHandler(node.Remote)\n\t\t\t} else {\n\t\t\t\thandler = gost.AutoHandler()\n\t\t\t}\n\t\t}\n\n\t\tvar whitelist, blacklist *gost.Permissions\n\t\tif node.Values.Get(\"whitelist\") != \"\" {\n\t\t\tif whitelist, err = gost.ParsePermissions(node.Get(\"whitelist\")); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tif node.Values.Get(\"blacklist\") != \"\" {\n\t\t\tif blacklist, err = gost.ParsePermissions(node.Get(\"blacklist\")); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\tnode.Bypass = parseBypass(node.Get(\"bypass\"))\n\t\thosts := parseHosts(node.Get(\"hosts\"))\n\t\tips := parseIP(node.Get(\"ip\"), \"\")\n\n\t\tresolver := parseResolver(node.Get(\"dns\"))\n\t\tif resolver != nil {\n\t\t\tresolver.Init(\n\t\t\t\tgost.ChainResolverOption(chain),\n\t\t\t\tgost.TimeoutResolverOption(timeout),\n\t\t\t\tgost.TTLResolverOption(ttl),\n\t\t\t\tgost.PreferResolverOption(node.Get(\"prefer\")),\n\t\t\t\tgost.SrcIPResolverOption(net.ParseIP(node.Get(\"ip\"))),\n\t\t\t)\n\t\t}\n\n\t\thandler.Init(\n\t\t\tgost.AddrHandlerOption(ln.Addr().String()),\n\t\t\tgost.ChainHandlerOption(chain),\n\t\t\tgost.UsersHandlerOption(node.User),\n\t\t\tgost.AuthenticatorHandlerOption(authenticator),\n\t\t\tgost.TLSConfigHandlerOption(tlsCfg),\n\t\t\tgost.WhitelistHandlerOption(whitelist),\n\t\t\tgost.BlacklistHandlerOption(blacklist),\n\t\t\tgost.StrategyHandlerOption(gost.NewStrategy(node.Get(\"strategy\"))),\n\t\t\tgost.MaxFailsHandlerOption(node.GetInt(\"max_fails\")),\n\t\t\tgost.FailTimeoutHandlerOption(node.GetDuration(\"fail_timeout\")),\n\t\t\tgost.BypassHandlerOption(node.Bypass),\n\t\t\tgost.ResolverHandlerOption(resolver),\n\t\t\tgost.HostsHandlerOption(hosts),\n\t\t\tgost.RetryHandlerOption(node.GetInt(\"retry\")), // override the global retry option.\n\t\t\tgost.TimeoutHandlerOption(timeout),\n\t\t\tgost.ProbeResistHandlerOption(node.Get(\"probe_resist\")),\n\t\t\tgost.KnockingHandlerOption(node.Get(\"knock\")),\n\t\t\tgost.NodeHandlerOption(node),\n\t\t\tgost.IPsHandlerOption(ips),\n\t\t\tgost.TCPModeHandlerOption(node.GetBool(\"tcp\")),\n\t\t\tgost.IPRoutesHandlerOption(tunRoutes...),\n\t\t\tgost.ProxyAgentHandlerOption(node.Get(\"proxyAgent\")),\n\t\t\tgost.HTTPTunnelHandlerOption(node.GetBool(\"httpTunnel\")),\n\t\t)\n\n\t\trt := router{\n\t\t\tnode:     node,\n\t\t\tserver:   &gost.Server{Listener: ln},\n\t\t\thandler:  handler,\n\t\t\tchain:    chain,\n\t\t\tresolver: resolver,\n\t\t\thosts:    hosts,\n\t\t}\n\t\trts = append(rts, rt)\n\t}\n\n\treturn rts, nil\n}\n\ntype router struct {\n\tnode     gost.Node\n\tserver   *gost.Server\n\thandler  gost.Handler\n\tchain    *gost.Chain\n\tresolver gost.Resolver\n\thosts    *gost.Hosts\n}\n\nfunc (r *router) Serve() error {\n\tlog.Logf(\"%s on %s\", r.node.String(), r.server.Addr())\n\treturn r.server.Serve(r.handler)\n}\n\nfunc (r *router) Close() error {\n\tif r == nil || r.server == nil {\n\t\treturn nil\n\t}\n\treturn r.server.Close()\n}\n"
  },
  {
    "path": "common_test.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\nfunc init() {\n\tSetLogger(&NopLogger{})\n\t// SetLogger(&LogLogger{})\n\tDebug = true\n\tDialTimeout = 1000 * time.Millisecond\n\tHandshakeTimeout = 1000 * time.Millisecond\n\tConnectTimeout = 1000 * time.Millisecond\n\n\tcert, err := GenCertificate()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tDefaultTLSConfig = &tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n}\n\nvar (\n\thttpTestHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tdata, _ := io.ReadAll(r.Body)\n\t\tif len(data) == 0 {\n\t\t\tdata = []byte(\"Hello World!\")\n\t\t}\n\t\tio.Copy(w, bytes.NewReader(data))\n\t})\n\n\tudpTestHandler = udpHandlerFunc(func(w io.Writer, r *udpRequest) {\n\t\tio.Copy(w, r.Body)\n\t})\n)\n\n// proxyConn obtains a connection to the proxy server.\nfunc proxyConn(client *Client, server *Server) (net.Conn, error) {\n\tconn, err := client.Dial(server.Addr().String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcc, err := client.Handshake(conn, AddrHandshakeOption(server.Addr().String()))\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\treturn cc, nil\n}\n\n// httpRoundtrip does a HTTP request-response roundtrip, and checks the data received.\nfunc httpRoundtrip(conn net.Conn, targetURL string, data []byte) (err error) {\n\treq, err := http.NewRequest(\n\t\thttp.MethodGet,\n\t\ttargetURL,\n\t\tbytes.NewReader(data),\n\t)\n\tif err != nil {\n\t\treturn\n\t}\n\tif err = req.Write(conn); err != nil {\n\t\treturn\n\t}\n\tresp, err := http.ReadResponse(bufio.NewReader(conn), req)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn errors.New(resp.Status)\n\t}\n\n\trecv, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif !bytes.Equal(data, recv) {\n\t\treturn fmt.Errorf(\"data not equal\")\n\t}\n\treturn\n}\n\nfunc udpRoundtrip(logger log.Logger, client *Client, server *Server, host string, data []byte) (err error) {\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer conn.Close()\n\n\tconn, err = client.Connect(conn, host)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn.SetDeadline(time.Now().Add(1 * time.Second))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tif _, err = conn.Write(data); err != nil {\n\t\tlogger.Logf(\"write to %s via %s: %s\", host, server.Addr(), err)\n\t\treturn\n\t}\n\n\trecv := make([]byte, len(data))\n\tif _, err = conn.Read(recv); err != nil {\n\t\tlogger.Logf(\"read from %s via %s: %s\", host, server.Addr(), err)\n\t\treturn\n\t}\n\n\tif !bytes.Equal(data, recv) {\n\t\treturn fmt.Errorf(\"data not equal\")\n\t}\n\n\treturn\n}\n\nfunc proxyRoundtrip(client *Client, server *Server, targetURL string, data []byte) (err error) {\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer conn.Close()\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn, err = client.Connect(conn, u.Host)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn.SetDeadline(time.Now().Add(1000 * time.Millisecond))\n\tdefer conn.SetDeadline(time.Time{})\n\n\treturn httpRoundtrip(conn, targetURL, data)\n}\n\ntype udpRequest struct {\n\tBody       io.Reader\n\tRemoteAddr string\n}\n\ntype udpResponseWriter struct {\n\tconn net.PacketConn\n\taddr net.Addr\n}\n\nfunc (w *udpResponseWriter) Write(p []byte) (int, error) {\n\treturn w.conn.WriteTo(p, w.addr)\n}\n\ntype udpHandlerFunc func(w io.Writer, r *udpRequest)\n\n// udpTestServer is a UDP server for test.\ntype udpTestServer struct {\n\tln        net.PacketConn\n\thandler   udpHandlerFunc\n\twg        sync.WaitGroup\n\tmu        sync.Mutex // guards closed and conns\n\tclosed    bool\n\tstartChan chan struct{}\n\texitChan  chan struct{}\n}\n\nfunc newUDPTestServer(handler udpHandlerFunc) *udpTestServer {\n\tladdr, _ := net.ResolveUDPAddr(\"udp\", \"127.0.0.1:0\")\n\tln, err := net.ListenUDP(\"udp\", laddr)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"udptest: failed to listen on a port: %v\", err))\n\t}\n\n\treturn &udpTestServer{\n\t\tln:        ln,\n\t\thandler:   handler,\n\t\tstartChan: make(chan struct{}),\n\t\texitChan:  make(chan struct{}),\n\t}\n}\n\nfunc (s *udpTestServer) Start() {\n\tgo s.serve()\n\t<-s.startChan\n}\n\nfunc (s *udpTestServer) serve() {\n\tselect {\n\tcase <-s.startChan:\n\t\treturn\n\tdefault:\n\t\tclose(s.startChan)\n\t}\n\n\tfor {\n\t\tdata := make([]byte, 32*1024)\n\t\tn, raddr, err := s.ln.ReadFrom(data)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tif s.handler != nil {\n\t\t\ts.wg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer s.wg.Done()\n\t\t\t\tw := &udpResponseWriter{\n\t\t\t\t\tconn: s.ln,\n\t\t\t\t\taddr: raddr,\n\t\t\t\t}\n\t\t\t\tr := &udpRequest{\n\t\t\t\t\tBody:       bytes.NewReader(data[:n]),\n\t\t\t\t\tRemoteAddr: raddr.String(),\n\t\t\t\t}\n\t\t\t\ts.handler(w, r)\n\t\t\t}()\n\t\t}\n\t}\n\n\t// signal the listener has been exited.\n\tclose(s.exitChan)\n}\n\nfunc (s *udpTestServer) Addr() string {\n\treturn s.ln.LocalAddr().String()\n}\n\nfunc (s *udpTestServer) Close() error {\n\ts.mu.Lock()\n\n\tif s.closed {\n\t\ts.mu.Unlock()\n\t\treturn nil\n\t}\n\n\terr := s.ln.Close()\n\ts.closed = true\n\ts.mu.Unlock()\n\n\t<-s.exitChan\n\n\ts.wg.Wait()\n\n\treturn err\n}\n"
  },
  {
    "path": "dns.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/miekg/dns\"\n)\n\nvar (\n\tdefaultResolver Resolver\n)\n\nfunc init() {\n\tdefaultResolver = NewResolver(\n\t\tDefaultResolverTimeout,\n\t\tNameServer{\n\t\t\tAddr:     \"127.0.0.1:53\",\n\t\t\tProtocol: \"udp\",\n\t\t})\n\tdefaultResolver.Init()\n}\n\ntype dnsHandler struct {\n\toptions *HandlerOptions\n}\n\n// DNSHandler creates a Handler for DNS server.\nfunc DNSHandler(raddr string, opts ...HandlerOption) Handler {\n\th := &dnsHandler{}\n\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\treturn h\n}\n\nfunc (h *dnsHandler) Init(opts ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *dnsHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tb := mPool.Get().([]byte)\n\tdefer mPool.Put(b)\n\n\tn, err := conn.Read(b)\n\tif err != nil {\n\t\tlog.Logf(\"[dns] %s - %s: %v\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t}\n\n\tmq := &dns.Msg{}\n\tif err = mq.Unpack(b[:n]); err != nil {\n\t\tlog.Logf(\"[dns] %s - %s request unpack: %v\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tlog.Logf(\"[dns] %s -> %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), h.dumpMsgHeader(mq))\n\tif Debug {\n\t\tlog.Logf(\"[dns] %s >>> %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), mq.String())\n\t}\n\n\tstart := time.Now()\n\n\tresolver := h.options.Resolver\n\tif resolver == nil {\n\t\tresolver = defaultResolver\n\t}\n\treply, err := resolver.Exchange(context.Background(), b[:n])\n\tif err != nil {\n\t\tlog.Logf(\"[dns] %s - %s exchange: %v\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\n\trtt := time.Since(start)\n\n\tmr := &dns.Msg{}\n\tif err = mr.Unpack(reply); err != nil {\n\t\tlog.Logf(\"[dns] %s - %s reply unpack: %v\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tlog.Logf(\"[dns] %s <- %s: %s [%s]\",\n\t\tconn.RemoteAddr(), conn.LocalAddr(), h.dumpMsgHeader(mr), rtt)\n\tif Debug {\n\t\tlog.Logf(\"[dns] %s <<< %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), mr.String())\n\t}\n\n\tif _, err = conn.Write(reply); err != nil {\n\t\tlog.Logf(\"[dns] %s - %s reply unpack: %v\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t}\n}\n\nfunc (h *dnsHandler) dumpMsgHeader(m *dns.Msg) string {\n\tbuf := new(bytes.Buffer)\n\tbuf.WriteString(m.MsgHdr.String() + \" \")\n\tbuf.WriteString(\"QUERY: \" + strconv.Itoa(len(m.Question)) + \", \")\n\tbuf.WriteString(\"ANSWER: \" + strconv.Itoa(len(m.Answer)) + \", \")\n\tbuf.WriteString(\"AUTHORITY: \" + strconv.Itoa(len(m.Ns)) + \", \")\n\tbuf.WriteString(\"ADDITIONAL: \" + strconv.Itoa(len(m.Extra)))\n\treturn buf.String()\n}\n\n// DNSOptions is options for DNS Listener.\ntype DNSOptions struct {\n\tMode         string\n\tUDPSize      int\n\tReadTimeout  time.Duration\n\tWriteTimeout time.Duration\n\tTLSConfig    *tls.Config\n}\n\ntype dnsListener struct {\n\taddr     net.Addr\n\tserver   dnsServer\n\tconnChan chan net.Conn\n\terrc     chan error\n}\n\n// DNSListener creates a Listener for DNS proxy server.\nfunc DNSListener(addr string, options *DNSOptions) (Listener, error) {\n\tif options == nil {\n\t\toptions = &DNSOptions{}\n\t}\n\n\ttlsConfig := options.TLSConfig\n\tif tlsConfig == nil {\n\t\ttlsConfig = DefaultTLSConfig\n\t}\n\n\tln := &dnsListener{\n\t\tconnChan: make(chan net.Conn, 128),\n\t\terrc:     make(chan error, 1),\n\t}\n\n\tvar srv dnsServer\n\tvar err error\n\tswitch strings.ToLower(options.Mode) {\n\tcase \"tcp\":\n\t\tsrv = &dns.Server{\n\t\t\tNet:          \"tcp\",\n\t\t\tAddr:         addr,\n\t\t\tHandler:      ln,\n\t\t\tReadTimeout:  options.ReadTimeout,\n\t\t\tWriteTimeout: options.WriteTimeout,\n\t\t}\n\tcase \"tls\":\n\t\tsrv = &dns.Server{\n\t\t\tNet:          \"tcp-tls\",\n\t\t\tAddr:         addr,\n\t\t\tHandler:      ln,\n\t\t\tTLSConfig:    tlsConfig,\n\t\t\tReadTimeout:  options.ReadTimeout,\n\t\t\tWriteTimeout: options.WriteTimeout,\n\t\t}\n\tcase \"https\":\n\t\tsrv = &dohServer{\n\t\t\taddr:      addr,\n\t\t\ttlsConfig: tlsConfig,\n\t\t\tserver: &http.Server{\n\t\t\t\tHandler:      ln,\n\t\t\t\tReadTimeout:  options.ReadTimeout,\n\t\t\t\tWriteTimeout: options.WriteTimeout,\n\t\t\t},\n\t\t}\n\n\tdefault:\n\t\tln.addr, err = net.ResolveTCPAddr(\"tcp\", addr)\n\t\tsrv = &dns.Server{\n\t\t\tNet:          \"udp\",\n\t\t\tAddr:         addr,\n\t\t\tHandler:      ln,\n\t\t\tUDPSize:      options.UDPSize,\n\t\t\tReadTimeout:  options.ReadTimeout,\n\t\t\tWriteTimeout: options.WriteTimeout,\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif ln.addr == nil {\n\t\tln.addr, err = net.ResolveTCPAddr(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tln.server = srv\n\n\tgo func() {\n\t\tif err := ln.server.ListenAndServe(); err != nil {\n\t\t\tln.errc <- err\n\t\t\treturn\n\t\t}\n\t}()\n\n\tselect {\n\tcase err := <-ln.errc:\n\t\treturn nil, err\n\tdefault:\n\t}\n\n\treturn ln, nil\n}\n\nfunc (l *dnsListener) serve(w dnsResponseWriter, mq []byte) (err error) {\n\tconn := newDNSServerConn(l.addr, w.RemoteAddr())\n\tconn.mq <- mq\n\n\tselect {\n\tcase l.connChan <- conn:\n\tdefault:\n\t\treturn errors.New(\"connection queue is full\")\n\t}\n\n\tselect {\n\tcase mr := <-conn.mr:\n\t\t_, err = w.Write(mr)\n\tcase <-conn.cclose:\n\t\terr = io.EOF\n\t}\n\treturn\n}\n\nfunc (l *dnsListener) ServeDNS(w dns.ResponseWriter, m *dns.Msg) {\n\tb, err := m.Pack()\n\tif err != nil {\n\t\tlog.Logf(\"[dns] %s: %v\", l.addr, err)\n\t\treturn\n\t}\n\tif err := l.serve(w, b); err != nil {\n\t\tlog.Logf(\"[dns] %s: %v\", l.addr, err)\n\t}\n}\n\n// Based on https://github.com/semihalev/sdns\nfunc (l *dnsListener) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tvar buf []byte\n\tvar err error\n\tswitch r.Method {\n\tcase http.MethodGet:\n\t\tbuf, err = base64.RawURLEncoding.DecodeString(r.URL.Query().Get(\"dns\"))\n\t\tif len(buf) == 0 || err != nil {\n\t\t\thttp.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\tcase http.MethodPost:\n\t\tif r.Header.Get(\"Content-Type\") != \"application/dns-message\" {\n\t\t\thttp.Error(w, http.StatusText(http.StatusUnsupportedMediaType), http.StatusUnsupportedMediaType)\n\t\t\treturn\n\t\t}\n\n\t\tbuf, err = io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\thttp.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\tdefault:\n\t\thttp.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\n\tmq := &dns.Msg{}\n\tif err := mq.Unpack(buf); err != nil {\n\t\thttp.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Server\", \"SDNS\")\n\tw.Header().Set(\"Content-Type\", \"application/dns-message\")\n\n\traddr, _ := net.ResolveTCPAddr(\"tcp\", r.RemoteAddr)\n\tif err := l.serve(newDoHResponseWriter(raddr, w), buf); err != nil {\n\t\tlog.Logf(\"[dns] %s: %v\", l.addr, err)\n\t\thttp.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)\n\t}\n}\n\nfunc (l *dnsListener) Accept() (conn net.Conn, err error) {\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err = <-l.errc:\n\t}\n\treturn\n}\n\nfunc (l *dnsListener) Close() error {\n\treturn l.server.Shutdown()\n}\n\nfunc (l *dnsListener) Addr() net.Addr {\n\treturn l.addr\n}\n\ntype dnsServer interface {\n\tListenAndServe() error\n\tShutdown() error\n}\n\ntype dohServer struct {\n\taddr      string\n\ttlsConfig *tls.Config\n\tserver    *http.Server\n}\n\nfunc (s *dohServer) ListenAndServe() error {\n\tln, err := net.Listen(\"tcp\", s.addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, s.tlsConfig)\n\treturn s.server.Serve(ln)\n}\n\nfunc (s *dohServer) Shutdown() error {\n\treturn s.server.Shutdown(context.Background())\n}\n\ntype dnsServerConn struct {\n\tmq           chan []byte\n\tmr           chan []byte\n\tcclose       chan struct{}\n\tladdr, raddr net.Addr\n}\n\nfunc newDNSServerConn(laddr, raddr net.Addr) *dnsServerConn {\n\treturn &dnsServerConn{\n\t\tmq:     make(chan []byte, 1),\n\t\tmr:     make(chan []byte, 1),\n\t\tladdr:  laddr,\n\t\traddr:  raddr,\n\t\tcclose: make(chan struct{}),\n\t}\n}\n\nfunc (c *dnsServerConn) Read(b []byte) (n int, err error) {\n\tselect {\n\tcase mb := <-c.mq:\n\t\tn = copy(b, mb)\n\tcase <-c.cclose:\n\t\terr = errors.New(\"connection is closed\")\n\t}\n\treturn\n}\n\nfunc (c *dnsServerConn) Write(b []byte) (n int, err error) {\n\tselect {\n\tcase c.mr <- b:\n\t\tn = len(b)\n\tcase <-c.cclose:\n\t\terr = errors.New(\"broken pipe\")\n\t}\n\n\treturn\n}\n\nfunc (c *dnsServerConn) Close() error {\n\tselect {\n\tcase <-c.cclose:\n\tdefault:\n\t\tclose(c.cclose)\n\t}\n\treturn nil\n}\n\nfunc (c *dnsServerConn) LocalAddr() net.Addr {\n\treturn c.laddr\n}\n\nfunc (c *dnsServerConn) RemoteAddr() net.Addr {\n\treturn c.raddr\n}\n\nfunc (c *dnsServerConn) SetDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"dns\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *dnsServerConn) SetReadDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"dns\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *dnsServerConn) SetWriteDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"dns\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\ntype dnsResponseWriter interface {\n\tio.Writer\n\tRemoteAddr() net.Addr\n}\n\ntype dohResponseWriter struct {\n\traddr net.Addr\n\thttp.ResponseWriter\n}\n\nfunc newDoHResponseWriter(raddr net.Addr, w http.ResponseWriter) dnsResponseWriter {\n\treturn &dohResponseWriter{\n\t\traddr:          raddr,\n\t\tResponseWriter: w,\n\t}\n}\n\nfunc (w *dohResponseWriter) RemoteAddr() net.Addr {\n\treturn w.raddr\n}\n"
  },
  {
    "path": "examples/bench/cli.go",
    "content": "package main\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/ginuerzh/gost\"\n\t\"golang.org/x/net/http2\"\n)\n\nvar (\n\trequests, concurrency int\n\tquiet                 bool\n\tswg, ewg              sync.WaitGroup\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.IntVar(&requests, \"n\", 1, \"Number of requests to perform\")\n\tflag.IntVar(&concurrency, \"c\", 1, \"Number of multiple requests to make at a time\")\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&http2.VerboseLogs, \"v\", false, \"HTTP2 verbose logs\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\n\nfunc main() {\n\tchain := gost.NewChain(\n\n\t\t/*\n\t\t\t// http+tcp\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:18080\",\n\t\t\t\tClient: gost.NewClient(\n\t\t\t\t\tgost.HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tgost.TCPTransporter(),\n\t\t\t\t),\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// socks5+tcp\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:11080\",\n\t\t\t\tClient: gost.NewClient(\n\t\t\t\t\tgost.SOCKS5Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tgost.TCPTransporter(),\n\t\t\t\t),\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// ss+tcp\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:18338\",\n\t\t\t\tClient: gost.NewClient(\n\t\t\t\t\tgost.ShadowConnector(url.UserPassword(\"chacha20\", \"123456\")),\n\t\t\t\t\tgost.TCPTransporter(),\n\t\t\t\t),\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// http+ws\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:18000\",\n\t\t\t\tClient: gost.NewClient(\n\t\t\t\t\tgost.HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tgost.WSTransporter(nil),\n\t\t\t\t),\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// http+wss\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:18443\",\n\t\t\t\tClient: gost.NewClient(\n\t\t\t\t\tgost.HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tgost.WSSTransporter(nil),\n\t\t\t\t),\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// http+tls\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:11443\",\n\t\t\t\tClient: gost.NewClient(\n\t\t\t\t\tgost.HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tgost.TLSTransporter(),\n\t\t\t\t),\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// http2\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:1443\",\n\t\t\t\tClient: &gost.Client{\n\t\t\t\t\tConnector:   gost.HTTP2Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tTransporter: gost.HTTP2Transporter(nil),\n\t\t\t\t},\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// http+kcp\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:18388\",\n\t\t\t\tClient: gost.NewClient(\n\t\t\t\t\tgost.HTTPConnector(nil),\n\t\t\t\t\tgost.KCPTransporter(nil),\n\t\t\t\t),\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// http+ssh\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"127.0.0.1:12222\",\n\t\t\t\tClient: gost.NewClient(\n\t\t\t\t\tgost.HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tgost.SSHTunnelTransporter(),\n\t\t\t\t),\n\t\t\t},\n\t\t*/\n\n\t\t/*\n\t\t\t// http+quic\n\t\t\tgost.Node{\n\t\t\t\tAddr: \"localhost:6121\",\n\t\t\t\tClient: &gost.Client{\n\t\t\t\t\tConnector:   gost.HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tTransporter: gost.QUICTransporter(nil),\n\t\t\t\t},\n\t\t\t},\n\t\t*/\n\t\t// socks5+h2\n\t\tgost.Node{\n\t\t\tAddr: \"localhost:8443\",\n\t\t\tClient: &gost.Client{\n\t\t\t\t// Connector: gost.HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\tConnector: gost.SOCKS5Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t// Transporter: gost.H2CTransporter(), // HTTP2 h2c mode\n\t\t\t\tTransporter: gost.H2Transporter(nil), // HTTP2 h2\n\t\t\t},\n\t\t},\n\t)\n\n\ttotal := 0\n\tfor total < requests {\n\t\tif total+concurrency > requests {\n\t\t\tconcurrency = requests - total\n\t\t}\n\t\tstartChan := make(chan struct{})\n\t\tfor i := 0; i < concurrency; i++ {\n\t\t\tswg.Add(1)\n\t\t\tewg.Add(1)\n\t\t\tgo request(chain, startChan)\n\t\t}\n\n\t\tstart := time.Now()\n\t\tswg.Wait()       // wait for workers ready\n\t\tclose(startChan) // start signal\n\t\tewg.Wait()       // wait for workers done\n\n\t\tduration := time.Since(start)\n\t\ttotal += concurrency\n\t\tlog.Printf(\"%d/%d/%d requests done (%v/%v)\", total, requests, concurrency, duration, duration/time.Duration(concurrency))\n\t}\n}\n\nfunc request(chain *gost.Chain, start <-chan struct{}) {\n\tdefer ewg.Done()\n\n\tswg.Done()\n\t<-start\n\n\tconn, err := chain.Dial(\"localhost:18888\")\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\tdefer conn.Close()\n\t//conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})\n\treq, err := http.NewRequest(http.MethodGet, \"http://localhost:18888\", nil)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\tif err := req.Write(conn); err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\tresp, err := http.ReadResponse(bufio.NewReader(conn), req)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\tif gost.Debug {\n\t\trb, _ := httputil.DumpRequest(req, true)\n\t\tlog.Println(string(rb))\n\t\trb, _ = httputil.DumpResponse(resp, true)\n\t\tlog.Println(string(rb))\n\t}\n}\n"
  },
  {
    "path": "examples/bench/srv.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/ginuerzh/gost\"\n\t\"golang.org/x/net/http2\"\n)\n\nvar (\n\tquiet bool\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\tflag.BoolVar(&http2.VerboseLogs, \"v\", false, \"HTTP2 verbose logs\")\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\n\nfunc main() {\n\tgo httpServer()\n\tgo socks5Server()\n\tgo tlsServer()\n\tgo shadowServer()\n\tgo wsServer()\n\tgo wssServer()\n\tgo kcpServer()\n\tgo tcpForwardServer()\n\tgo tcpRemoteForwardServer()\n\t// go rudpForwardServer()\n\t// go tcpRedirectServer()\n\tgo sshTunnelServer()\n\tgo http2Server()\n\tgo http2TunnelServer()\n\tgo quicServer()\n\tgo shadowUDPServer()\n\tgo testServer()\n\tselect {}\n}\n\nfunc httpServer() {\n\tln, err := gost.TCPListener(\":18080\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.HTTPHandler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc socks5Server() {\n\tln, err := gost.TCPListener(\":11080\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.SOCKS5Handler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\tgost.TLSConfigHandlerOption(tlsConfig()),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc shadowServer() {\n\tln, err := gost.TCPListener(\":18338\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.ShadowHandler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"chacha20\", \"123456\")),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc tlsServer() {\n\tln, err := gost.TLSListener(\":11443\", tlsConfig())\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.HTTPHandler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc wsServer() {\n\tln, err := gost.WSListener(\":18000\", nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.HTTPHandler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc wssServer() {\n\tln, err := gost.WSSListener(\":18443\", tlsConfig(), nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.HTTPHandler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc kcpServer() {\n\tln, err := gost.KCPListener(\":18388\", nil)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.HTTPHandler()\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc tcpForwardServer() {\n\tln, err := gost.TCPListener(\":2222\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.TCPDirectForwardHandler(\"localhost:22\")\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc tcpRemoteForwardServer() {\n\tln, err := gost.TCPRemoteForwardListener(\n\t\t\":1222\",\n\t\t/*\n\t\t\tgost.NewChain(\n\t\t\t\tgost.Node{\n\t\t\t\t\tProtocol:  \"socks5\",\n\t\t\t\t\tTransport: \"tcp\",\n\t\t\t\t\tAddr:      \"localhost:12345\",\n\t\t\t\t\tUser:      url.UserPassword(\"admin\", \"123456\"),\n\t\t\t\t\tClient: &gost.Client{\n\t\t\t\t\t\tConnector:   gost.SOCKS5Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\t\tTransporter: gost.TCPTransporter(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t),\n\t\t*/\n\t\tnil,\n\t)\n\tif err != nil {\n\t\tlog.Fatal()\n\t}\n\th := gost.TCPRemoteForwardHandler(\n\t\t\":22\",\n\t\t//gost.AddrHandlerOption(\"127.0.0.1:22\"),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc rudpForwardServer() {\n\tln, err := gost.UDPRemoteForwardListener(\n\t\t\":10053\",\n\t\tgost.NewChain(\n\t\t\tgost.Node{\n\t\t\t\tProtocol:  \"socks5\",\n\t\t\t\tTransport: \"tcp\",\n\t\t\t\tAddr:      \"localhost:12345\",\n\t\t\t\tUser:      url.UserPassword(\"admin\", \"123456\"),\n\t\t\t\tClient: &gost.Client{\n\t\t\t\t\tConnector:   gost.SOCKS5Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tTransporter: gost.TCPTransporter(),\n\t\t\t\t},\n\t\t\t},\n\t\t),\n\t\t30*time.Second,\n\t)\n\tif err != nil {\n\t\tlog.Fatal()\n\t}\n\th := gost.UDPRemoteForwardHandler(\"localhost:53\")\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc tcpRedirectServer() {\n\tln, err := gost.TCPListener(\":8008\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.TCPRedirectHandler()\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc sshTunnelServer() {\n\tln, err := gost.SSHTunnelListener(\":12222\", &gost.SSHConfig{TLSConfig: tlsConfig()})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.HTTPHandler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc http2Server() {\n\t// http2.VerboseLogs = true\n\n\tln, err := gost.HTTP2Listener(\":1443\", tlsConfig())\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.HTTP2Handler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc http2TunnelServer() {\n\tln, err := gost.H2Listener(\":8443\", tlsConfig()) // HTTP2 h2 mode\n\t// ln, err := gost.H2CListener(\":8443\") // HTTP2 h2c mode\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t// h := gost.HTTPHandler(\n\t// \tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t// )\n\th := gost.SOCKS5Handler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\tgost.TLSConfigHandlerOption(tlsConfig()),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc quicServer() {\n\tln, err := gost.QUICListener(\"localhost:6121\", &gost.QUICConfig{TLSConfig: tlsConfig()})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.HTTPHandler(\n\t\tgost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nfunc shadowUDPServer() {\n\tln, err := gost.ShadowUDPListener(\":18338\", url.UserPassword(\"chacha20\", \"123456\"), 30*time.Second)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.ShadowUDPdHandler(\n\t/*\n\t\tgost.ChainHandlerOption(gost.NewChain(\n\t\t\tgost.Node{\n\t\t\t\tProtocol:  \"socks5\",\n\t\t\t\tTransport: \"tcp\",\n\t\t\t\tAddr:      \"localhost:11080\",\n\t\t\t\tUser:      url.UserPassword(\"admin\", \"123456\"),\n\t\t\t\tClient: &gost.Client{\n\t\t\t\t\tConnector:   gost.SOCKS5Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\t\t\t\tTransporter: gost.TCPTransporter(),\n\t\t\t\t},\n\t\t\t},\n\t\t)),\n\t*/\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nvar (\n\trawCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5\nMDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N\n0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw\nhLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4\n8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv\n482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR\nLIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF\noDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC\nCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN\nl/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS\ncBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w\nemcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj\nb3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57\nlNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==\n-----END CERTIFICATE-----`)\n\trawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N\nZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo\nN39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv\nGYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh\nJnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg\nIQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n\nIShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H\nr+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe\nyE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru\nkcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk\nTS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU\nk8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o\n/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK\nHgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg\nHcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY\nCFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d\nJI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr\npJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt\n/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD\nxJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL\nvx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX\n1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt\n7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4\nfqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw\ncfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=\n-----END RSA PRIVATE KEY-----`)\n)\n\nfunc tlsConfig() *tls.Config {\n\tcert, err := tls.X509KeyPair(rawCert, rawKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &tls.Config{\n\t\tCertificates:             []tls.Certificate{cert},\n\t\tPreferServerCipherSuites: true,\n\t}\n}\n\nfunc testServer() {\n\ts := &http.Server{\n\t\tAddr: \":18888\",\n\t\tHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\tfmt.Fprintln(w, \"abcdefghijklmnopqrstuvwxyz\")\n\t\t}),\n\t}\n\tlog.Fatal(s.ListenAndServe())\n}\n"
  },
  {
    "path": "examples/forward/direct/client.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nfunc main() {\n\ttcpForward()\n}\n\nfunc tcpForward() {\n\tchain := gost.NewChain(\n\t\tgost.Node{\n\t\t\tAddr: \"localhost:11222\",\n\t\t\tClient: &gost.Client{\n\t\t\t\tConnector:   gost.SSHDirectForwardConnector(),\n\t\t\t\tTransporter: gost.SSHForwardTransporter(),\n\t\t\t},\n\t\t},\n\t)\n\n\tln, err := gost.TCPListener(\":11800\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.TCPDirectForwardHandler(\n\t\t\"localhost:22\",\n\t\tgost.ChainHandlerOption(chain),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n"
  },
  {
    "path": "examples/forward/direct/server.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"log\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nfunc main() {\n\tsshForwardServer()\n}\n\nfunc sshForwardServer() {\n\tln, err := gost.TCPListener(\":11222\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.SSHForwardHandler(\n\t\tgost.AddrHandlerOption(\":11222\"),\n\t\t// gost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\tgost.TLSConfigHandlerOption(tlsConfig()),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nvar (\n\trawCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5\nMDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N\n0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw\nhLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4\n8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv\n482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR\nLIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF\noDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC\nCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN\nl/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS\ncBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w\nemcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj\nb3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57\nlNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==\n-----END CERTIFICATE-----`)\n\trawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N\nZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo\nN39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv\nGYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh\nJnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg\nIQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n\nIShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H\nr+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe\nyE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru\nkcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk\nTS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU\nk8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o\n/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK\nHgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg\nHcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY\nCFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d\nJI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr\npJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt\n/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD\nxJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL\nvx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX\n1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt\n7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4\nfqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw\ncfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=\n-----END RSA PRIVATE KEY-----`)\n)\n\nfunc tlsConfig() *tls.Config {\n\tcert, err := tls.X509KeyPair(rawCert, rawKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &tls.Config{Certificates: []tls.Certificate{cert}}\n}\n"
  },
  {
    "path": "examples/forward/remote/client.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nfunc main() {\n\tsshRemoteForward()\n}\n\nfunc sshRemoteForward() {\n\tchain := gost.NewChain(\n\t\tgost.Node{\n\t\t\tProtocol:  \"forward\",\n\t\t\tTransport: \"ssh\",\n\t\t\tAddr:      \"localhost:11222\",\n\t\t\tClient: &gost.Client{\n\t\t\t\tConnector:   gost.SSHRemoteForwardConnector(),\n\t\t\t\tTransporter: gost.SSHForwardTransporter(),\n\t\t\t},\n\t\t},\n\t)\n\n\tln, err := gost.TCPRemoteForwardListener(\":11800\", chain)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.TCPRemoteForwardHandler(\n\t\t\"localhost:10000\",\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n"
  },
  {
    "path": "examples/forward/remote/server.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"log\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nfunc main() {\n\tsshRemoteForwardServer()\n}\n\nfunc sshRemoteForwardServer() {\n\tln, err := gost.TCPListener(\":11222\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.SSHForwardHandler(\n\t\tgost.AddrHandlerOption(\":11222\"),\n\t\t// gost.UsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\tgost.TLSConfigHandlerOption(tlsConfig()),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nvar (\n\trawCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5\nMDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N\n0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw\nhLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4\n8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv\n482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR\nLIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF\noDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC\nCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN\nl/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS\ncBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w\nemcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj\nb3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57\nlNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==\n-----END CERTIFICATE-----`)\n\trawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N\nZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo\nN39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv\nGYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh\nJnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg\nIQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n\nIShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H\nr+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe\nyE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru\nkcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk\nTS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU\nk8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o\n/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK\nHgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg\nHcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY\nCFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d\nJI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr\npJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt\n/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD\nxJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL\nvx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX\n1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt\n7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4\nfqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw\ncfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=\n-----END RSA PRIVATE KEY-----`)\n)\n\nfunc tlsConfig() *tls.Config {\n\tcert, err := tls.X509KeyPair(rawCert, rawKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &tls.Config{Certificates: []tls.Certificate{cert}}\n}\n"
  },
  {
    "path": "examples/forward/udp/cli.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"net\"\n\t\"time\"\n)\n\nvar (\n\tconcurrency int\n\tsaddr       string\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&saddr, \"S\", \":18080\", \"server address\")\n\tflag.IntVar(&concurrency, \"c\", 1, \"Number of multiple echo to make at a time\")\n\tflag.Parse()\n}\n\nfunc main() {\n\tfor i := 0; i < concurrency; i++ {\n\t\tgo udpEchoLoop()\n\t}\n\tselect {}\n}\n\nfunc udpEchoLoop() {\n\taddr, err := net.ResolveUDPAddr(\"udp\", saddr)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tconn, err := net.DialUDP(\"udp\", nil, addr)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tmsg := []byte(`abcdefghijklmnopqrstuvwxyz`)\n\tfor {\n\t\tif _, err := conn.Write(msg); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tb := make([]byte, 1024)\n\t\t_, err := conn.Read(b)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\t// log.Println(string(b[:n]))\n\t\ttime.Sleep(100 * time.Millisecond)\n\t}\n}\n"
  },
  {
    "path": "examples/forward/udp/direct.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nvar (\n\tladdr, faddr string\n\tquiet        bool\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&laddr, \"L\", \":18080\", \"listen address\")\n\tflag.StringVar(&faddr, \"F\", \":8080\", \"forward address\")\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\nfunc main() {\n\tudpDirectForwardServer()\n}\n\nfunc udpDirectForwardServer() {\n\tln, err := gost.UDPDirectForwardListener(laddr, time.Second*30)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.UDPDirectForwardHandler(\n\t\tfaddr,\n\t/*\n\t\tgost.ChainHandlerOption(gost.NewChain(gost.Node{\n\t\t\tProtocol:  \"socks5\",\n\t\t\tTransport: \"tcp\",\n\t\t\tAddr:      \":11080\",\n\t\t\tUser:      url.UserPassword(\"admin\", \"123456\"),\n\t\t\tClient: &gost.Client{\n\t\t\t\tConnector: gost.SOCKS5Connector(\n\t\t\t\t\turl.UserPassword(\"admin\", \"123456\"),\n\t\t\t\t),\n\t\t\t\tTransporter: gost.TCPTransporter(),\n\t\t\t},\n\t\t})),\n\t*/\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n"
  },
  {
    "path": "examples/forward/udp/remote.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nvar (\n\tladdr, faddr string\n\tquiet        bool\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&laddr, \"L\", \":18080\", \"listen address\")\n\tflag.StringVar(&faddr, \"F\", \":8080\", \"forward address\")\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\nfunc main() {\n\tudpRemoteForwardServer()\n}\n\nfunc udpRemoteForwardServer() {\n\tln, err := gost.UDPRemoteForwardListener(\n\t\tladdr,\n\t\t/*\n\t\t\tgost.NewChain(gost.Node{\n\t\t\t\tProtocol:  \"socks5\",\n\t\t\t\tTransport: \"tcp\",\n\t\t\t\tAddr:      \":11080\",\n\t\t\t\tUser:      url.UserPassword(\"admin\", \"123456\"),\n\t\t\t\tClient: &gost.Client{\n\t\t\t\t\tConnector: gost.SOCKS5Connector(\n\t\t\t\t\t\turl.UserPassword(\"admin\", \"123456\"),\n\t\t\t\t\t),\n\t\t\t\t\tTransporter: gost.TCPTransporter(),\n\t\t\t\t},\n\t\t\t}),\n\t\t*/\n\t\tnil,\n\t\ttime.Second*30)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.UDPRemoteForwardHandler(\n\t\tfaddr,\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n"
  },
  {
    "path": "examples/forward/udp/srv.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"net\"\n)\n\nvar (\n\tladdr string\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&laddr, \"L\", \":8080\", \"listen address\")\n\tflag.Parse()\n}\nfunc main() {\n\tudpEchoServer()\n}\n\nfunc udpEchoServer() {\n\taddr, err := net.ResolveUDPAddr(\"udp\", laddr)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tconn, err := net.ListenUDP(\"udp\", addr)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfor {\n\t\tb := make([]byte, 1024)\n\t\tn, raddr, err := conn.ReadFromUDP(b)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tif _, err = conn.WriteToUDP(b[:n], raddr); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "examples/http2/http2.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"flag\"\n\t\"log\"\n\t\"net/url\"\n\n\t\"golang.org/x/net/http2\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nvar (\n\tquiet             bool\n\tkeyFile, certFile string\n\tladdr             string\n\tuser, passwd      string\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&laddr, \"L\", \":1443\", \"listen address\")\n\tflag.StringVar(&user, \"u\", \"\", \"username\")\n\tflag.StringVar(&passwd, \"p\", \"\", \"password\")\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\tflag.BoolVar(&http2.VerboseLogs, \"v\", false, \"HTTP2 verbose log\")\n\tflag.StringVar(&keyFile, \"key\", \"key.pem\", \"TLS key file\")\n\tflag.StringVar(&certFile, \"cert\", \"cert.pem\", \"TLS cert file\")\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\n\nfunc main() {\n\thttp2Server()\n}\n\nfunc http2Server() {\n\tcert, er := tls.LoadX509KeyPair(certFile, keyFile)\n\tif er != nil {\n\t\tlog.Println(er)\n\t\tcert, er = tls.X509KeyPair(rawCert, rawKey)\n\t\tif er != nil {\n\t\t\tpanic(er)\n\t\t}\n\t}\n\n\tln, err := gost.HTTP2Listener(laddr, &tls.Config{Certificates: []tls.Certificate{cert}})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tvar users []*url.Userinfo\n\tif user != \"\" || passwd != \"\" {\n\t\tusers = append(users, url.UserPassword(user, passwd))\n\t}\n\n\th := gost.HTTP2Handler(\n\t\tgost.UsersHandlerOption(users...),\n\t\tgost.AddrHandlerOption(laddr),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nvar (\n\trawCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5\nMDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N\n0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw\nhLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4\n8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv\n482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR\nLIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF\noDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC\nCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN\nl/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS\ncBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w\nemcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj\nb3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57\nlNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==\n-----END CERTIFICATE-----`)\n\trawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N\nZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo\nN39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv\nGYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh\nJnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg\nIQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n\nIShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H\nr+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe\nyE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru\nkcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk\nTS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU\nk8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o\n/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK\nHgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg\nHcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY\nCFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d\nJI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr\npJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt\n/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD\nxJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL\nvx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX\n1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt\n7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4\nfqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw\ncfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=\n-----END RSA PRIVATE KEY-----`)\n)\n\nfunc tlsConfig() *tls.Config {\n\tcert, err := tls.X509KeyPair(rawCert, rawKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &tls.Config{Certificates: []tls.Certificate{cert}}\n}\n"
  },
  {
    "path": "examples/quic/quicc.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"flag\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nvar (\n\tladdr, faddr string\n\tquiet        bool\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&laddr, \"L\", \":18080\", \"listen address\")\n\tflag.StringVar(&faddr, \"F\", \"localhost:6121\", \"forward address\")\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\n\nfunc main() {\n\tchain := gost.NewChain(\n\t\tgost.Node{\n\t\t\tProtocol:  \"socks5\",\n\t\t\tTransport: \"quic\",\n\t\t\tAddr:      faddr,\n\t\t\tClient: &gost.Client{\n\t\t\t\tConnector:   gost.SOCKS5Connector(nil),\n\t\t\t\tTransporter: gost.QUICTransporter(&gost.QUICConfig{Timeout: 30 * time.Second, KeepAlive: true}),\n\t\t\t},\n\t\t},\n\t)\n\n\tln, err := gost.TCPListener(laddr)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.SOCKS5Handler(\n\t\tgost.ChainHandlerOption(chain),\n\t\tgost.TLSConfigHandlerOption(tlsConfig()),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nvar (\n\trawCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5\nMDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N\n0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw\nhLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4\n8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv\n482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR\nLIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF\noDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC\nCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN\nl/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS\ncBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w\nemcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj\nb3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57\nlNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==\n-----END CERTIFICATE-----`)\n\trawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N\nZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo\nN39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv\nGYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh\nJnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg\nIQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n\nIShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H\nr+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe\nyE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru\nkcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk\nTS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU\nk8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o\n/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK\nHgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg\nHcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY\nCFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d\nJI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr\npJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt\n/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD\nxJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL\nvx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX\n1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt\n7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4\nfqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw\ncfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=\n-----END RSA PRIVATE KEY-----`)\n)\n\nfunc tlsConfig() *tls.Config {\n\tcert, err := tls.X509KeyPair(rawCert, rawKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &tls.Config{Certificates: []tls.Certificate{cert}}\n}\n"
  },
  {
    "path": "examples/quic/quics.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"flag\"\n\t\"log\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nvar (\n\tladdr string\n\tquiet bool\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&laddr, \"L\", \":6121\", \"listen address\")\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\n\nfunc main() {\n\tquicServer()\n}\n\nfunc quicServer() {\n\tln, err := gost.QUICListener(laddr, &gost.QUICConfig{TLSConfig: tlsConfig()})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig()))\n\tlog.Println(\"server listen on\", laddr)\n\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nvar (\n\trawCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5\nMDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N\n0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw\nhLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4\n8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv\n482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR\nLIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF\noDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC\nCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN\nl/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS\ncBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w\nemcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj\nb3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57\nlNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==\n-----END CERTIFICATE-----`)\n\trawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N\nZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo\nN39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv\nGYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh\nJnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg\nIQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n\nIShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H\nr+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe\nyE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru\nkcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk\nTS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU\nk8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o\n/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK\nHgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg\nHcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY\nCFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d\nJI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr\npJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt\n/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD\nxJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL\nvx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX\n1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt\n7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4\nfqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw\ncfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=\n-----END RSA PRIVATE KEY-----`)\n)\n\nfunc tlsConfig() *tls.Config {\n\tcert, err := tls.X509KeyPair(rawCert, rawKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &tls.Config{Certificates: []tls.Certificate{cert}}\n}\n"
  },
  {
    "path": "examples/ssh/sshc.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"flag\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nvar (\n\tladdr, faddr string\n\tquiet        bool\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&laddr, \"L\", \":18080\", \"listen address\")\n\tflag.StringVar(&faddr, \"F\", \":12222\", \"forward address\")\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\n\nfunc main() {\n\tchain := gost.NewChain(\n\t\tgost.Node{\n\t\t\tProtocol:  \"socks5\",\n\t\t\tTransport: \"ssh\",\n\t\t\tAddr:      faddr,\n\t\t\tHandshakeOptions: []gost.HandshakeOption{\n\t\t\t\tgost.IntervalHandshakeOption(30 * time.Second),\n\t\t\t},\n\t\t\tClient: &gost.Client{\n\t\t\t\tConnector:   gost.SOCKS5Connector(nil),\n\t\t\t\tTransporter: gost.SSHTunnelTransporter(),\n\t\t\t},\n\t\t},\n\t)\n\n\tln, err := gost.TCPListener(laddr)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.SOCKS5Handler(\n\t\tgost.ChainHandlerOption(chain),\n\t\tgost.TLSConfigHandlerOption(tlsConfig()),\n\t)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nvar (\n\trawCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5\nMDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N\n0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw\nhLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4\n8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv\n482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR\nLIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF\noDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC\nCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN\nl/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS\ncBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w\nemcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj\nb3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57\nlNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==\n-----END CERTIFICATE-----`)\n\trawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N\nZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo\nN39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv\nGYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh\nJnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg\nIQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n\nIShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H\nr+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe\nyE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru\nkcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk\nTS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU\nk8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o\n/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK\nHgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg\nHcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY\nCFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d\nJI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr\npJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt\n/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD\nxJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL\nvx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX\n1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt\n7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4\nfqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw\ncfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=\n-----END RSA PRIVATE KEY-----`)\n)\n\nfunc tlsConfig() *tls.Config {\n\tcert, err := tls.X509KeyPair(rawCert, rawKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &tls.Config{Certificates: []tls.Certificate{cert}}\n}\n"
  },
  {
    "path": "examples/ssh/sshd.go",
    "content": "package main\n\nimport (\n\t\"crypto/tls\"\n\t\"flag\"\n\t\"log\"\n\n\t\"github.com/ginuerzh/gost\"\n)\n\nvar (\n\tladdr string\n\tquiet bool\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n\n\tflag.StringVar(&laddr, \"L\", \":12222\", \"listen address\")\n\tflag.BoolVar(&quiet, \"q\", false, \"quiet mode\")\n\tflag.BoolVar(&gost.Debug, \"d\", false, \"debug mode\")\n\n\tflag.Parse()\n\n\tif quiet {\n\t\tgost.SetLogger(&gost.NopLogger{})\n\t}\n}\n\nfunc main() {\n\tsshTunnelServer()\n}\n\nfunc sshTunnelServer() {\n\tln, err := gost.SSHTunnelListener(laddr, &gost.SSHConfig{TLSConfig: tlsConfig()})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\th := gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig()))\n\tlog.Println(\"server listen on\", laddr)\n\ts := &gost.Server{ln}\n\tlog.Fatal(s.Serve(h))\n}\n\nvar (\n\trawCert = []byte(`-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw\nEjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5\nMDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N\n0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw\nhLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4\n8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv\n482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR\nLIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF\noDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC\nCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN\nl/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS\ncBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w\nemcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj\nb3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57\nlNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg==\n-----END CERTIFICATE-----`)\n\trawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N\nZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo\nN39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv\nGYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh\nJnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg\nIQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n\nIShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H\nr+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe\nyE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru\nkcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk\nTS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU\nk8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o\n/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK\nHgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg\nHcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY\nCFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d\nJI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr\npJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt\n/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD\nxJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL\nvx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX\n1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt\n7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4\nfqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw\ncfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI=\n-----END RSA PRIVATE KEY-----`)\n)\n\nfunc tlsConfig() *tls.Config {\n\tcert, err := tls.X509KeyPair(rawCert, rawKey)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn &tls.Config{Certificates: []tls.Certificate{cert}}\n}\n"
  },
  {
    "path": "examples/ssu/ssu.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"log\"\n\t\"net\"\n\t\"strconv\"\n\n\t\"github.com/go-gost/gosocks5\"\n\tss \"github.com/shadowsocks/shadowsocks-go/shadowsocks\"\n)\n\nfunc main() {\n\tssuClient()\n}\n\nfunc ssuClient() {\n\taddr, err := net.ResolveUDPAddr(\"udp\", \":18338\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tladdr, _ := net.ResolveUDPAddr(\"udp\", \":10800\")\n\tconn, err := net.ListenUDP(\"udp\", laddr)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tcp, err := ss.NewCipher(\"chacha20\", \"123456\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tcc := ss.NewSecurePacketConn(conn, cp, false)\n\n\traddr, _ := net.ResolveUDPAddr(\"udp\", \":8080\")\n\tmsg := []byte(`abcdefghijklmnopqrstuvwxyz`)\n\tdgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), msg)\n\tbuf := bytes.Buffer{}\n\tdgram.Write(&buf)\n\tfor {\n\t\tlog.Printf(\"%# x\", buf.Bytes()[3:])\n\t\tif _, err := cc.WriteTo(buf.Bytes()[3:], addr); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tb := make([]byte, 1024)\n\t\tn, adr, err := cc.ReadFrom(b)\n\t\tif err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t\tlog.Printf(\"%s: %# x\", adr, b[:n])\n\t}\n}\n\nfunc toSocksAddr(addr net.Addr) *gosocks5.Addr {\n\thost := \"0.0.0.0\"\n\tport := 0\n\tif addr != nil {\n\t\th, p, _ := net.SplitHostPort(addr.String())\n\t\thost = h\n\t\tport, _ = strconv.Atoi(p)\n\t}\n\treturn &gosocks5.Addr{\n\t\tType: gosocks5.AddrIPv4,\n\t\tHost: host,\n\t\tPort: uint16(port),\n\t}\n}\n"
  },
  {
    "path": "forward.go",
    "content": "package gost\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"fmt\"\n\n\t\"github.com/go-gost/gosocks5\"\n\t\"github.com/go-log/log\"\n\tsmux \"github.com/xtaci/smux\"\n)\n\ntype forwardConnector struct {\n}\n\n// ForwardConnector creates a Connector for data forward client.\nfunc ForwardConnector() Connector {\n\treturn &forwardConnector{}\n}\n\nfunc (c *forwardConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *forwardConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn conn, nil\n}\n\ntype baseForwardHandler struct {\n\traddr   string\n\tgroup   *NodeGroup\n\toptions *HandlerOptions\n}\n\nfunc (h *baseForwardHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n\n\th.group = NewNodeGroup() // reset node group\n\n\th.group.SetSelector(&defaultSelector{},\n\t\tWithStrategy(h.options.Strategy),\n\t\tWithFilter(&FailFilter{\n\t\t\tMaxFails:    h.options.MaxFails,\n\t\t\tFailTimeout: h.options.FailTimeout,\n\t\t}),\n\t)\n\n\tn := 1\n\taddrs := append(strings.Split(h.raddr, \",\"), h.options.IPs...)\n\tfor _, addr := range addrs {\n\t\tif addr == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\t// We treat the remote target server as a node, so we can put them in a group,\n\t\t// and perform the node selection for load balancing.\n\t\th.group.AddNode(Node{\n\t\t\tID:     n,\n\t\t\tAddr:   addr,\n\t\t\tHost:   addr,\n\t\t\tmarker: &failMarker{},\n\t\t})\n\n\t\tn++\n\t}\n}\n\ntype tcpDirectForwardHandler struct {\n\t*baseForwardHandler\n}\n\n// TCPDirectForwardHandler creates a server Handler for TCP port forwarding server.\n// The raddr is the remote address that the server will forward to.\n// NOTE: as of 2.6, remote address can be a comma-separated address list.\nfunc TCPDirectForwardHandler(raddr string, opts ...HandlerOption) Handler {\n\th := &tcpDirectForwardHandler{\n\t\tbaseForwardHandler: &baseForwardHandler{\n\t\t\traddr:   raddr,\n\t\t\tgroup:   NewNodeGroup(),\n\t\t\toptions: &HandlerOptions{},\n\t\t},\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\n\treturn h\n}\n\nfunc (h *tcpDirectForwardHandler) Init(options ...HandlerOption) {\n\th.baseForwardHandler.Init(options...)\n}\n\nfunc (h *tcpDirectForwardHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tlog.Logf(\"[tcp] %s - %s\", conn.RemoteAddr(), conn.LocalAddr())\n\n\tretries := 1\n\tif h.options.Chain != nil && h.options.Chain.Retries > 0 {\n\t\tretries = h.options.Chain.Retries\n\t}\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tvar cc net.Conn\n\tvar node Node\n\tvar err error\n\tfor i := 0; i < retries; i++ {\n\t\tif len(h.group.Nodes()) > 0 {\n\t\t\tnode, err = h.group.Next()\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[tcp] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tcc, err = h.options.Chain.Dial(node.Addr,\n\t\t\tRetryChainOption(h.options.Retries),\n\t\t\tTimeoutChainOption(h.options.Timeout),\n\t\t\tResolverChainOption(h.options.Resolver),\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[tcp] %s -> %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\tnode.MarkDead()\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\n\tnode.ResetDead()\n\tdefer cc.Close()\n\n\taddr := node.Addr\n\tif addr == \"\" {\n\t\taddr = conn.LocalAddr().String()\n\t}\n\tlog.Logf(\"[tcp] %s <-> %s\", conn.RemoteAddr(), addr)\n\ttransport(conn, cc)\n\tlog.Logf(\"[tcp] %s >-< %s\", conn.RemoteAddr(), addr)\n}\n\ntype udpDirectForwardHandler struct {\n\t*baseForwardHandler\n}\n\n// UDPDirectForwardHandler creates a server Handler for UDP port forwarding server.\n// The raddr is the remote address that the server will forward to.\n// NOTE: as of 2.6, remote address can be a comma-separated address list.\nfunc UDPDirectForwardHandler(raddr string, opts ...HandlerOption) Handler {\n\th := &udpDirectForwardHandler{\n\t\tbaseForwardHandler: &baseForwardHandler{\n\t\t\traddr:   raddr,\n\t\t\tgroup:   NewNodeGroup(),\n\t\t\toptions: &HandlerOptions{},\n\t\t},\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\n\treturn h\n}\n\nfunc (h *udpDirectForwardHandler) Init(options ...HandlerOption) {\n\th.baseForwardHandler.Init(options...)\n}\n\nfunc (h *udpDirectForwardHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tlog.Logf(\"[udp] %s - %s\", conn.RemoteAddr(), conn.LocalAddr())\n\n\tvar node Node\n\tvar err error\n\tif len(h.group.Nodes()) > 0 {\n\t\tnode, err = h.group.Next()\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[udp] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tcc, err := h.options.Chain.DialContext(\n\t\tcontext.Background(),\n\t\t\"udp\",\n\t\tnode.Addr,\n\t\tResolverChainOption(h.options.Resolver),\n\t)\n\tif err != nil {\n\t\tnode.MarkDead()\n\t\tlog.Logf(\"[udp] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tdefer cc.Close()\n\tnode.ResetDead()\n\n\taddr := node.Addr\n\tif addr == \"\" {\n\t\taddr = conn.LocalAddr().String()\n\t}\n\tlog.Logf(\"[udp] %s <-> %s\", conn.RemoteAddr(), addr)\n\ttransport(conn, cc)\n\tlog.Logf(\"[udp] %s >-< %s\", conn.RemoteAddr(), addr)\n}\n\ntype tcpRemoteForwardHandler struct {\n\t*baseForwardHandler\n}\n\n// TCPRemoteForwardHandler creates a server Handler for TCP remote port forwarding server.\n// The raddr is the remote address that the server will forward to.\n// NOTE: as of 2.6, remote address can be a comma-separated address list.\nfunc TCPRemoteForwardHandler(raddr string, opts ...HandlerOption) Handler {\n\th := &tcpRemoteForwardHandler{\n\t\tbaseForwardHandler: &baseForwardHandler{\n\t\t\traddr:   raddr,\n\t\t\tgroup:   NewNodeGroup(),\n\t\t\toptions: &HandlerOptions{},\n\t\t},\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\n\treturn h\n}\n\nfunc (h *tcpRemoteForwardHandler) Init(options ...HandlerOption) {\n\th.baseForwardHandler.Init(options...)\n}\n\nfunc (h *tcpRemoteForwardHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tretries := 1\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tvar cc net.Conn\n\tvar node Node\n\tvar err error\n\tfor i := 0; i < retries; i++ {\n\t\tif len(h.group.Nodes()) > 0 {\n\t\t\tnode, err = h.group.Next()\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[rtcp] %s - %s : %s\", conn.LocalAddr(), h.raddr, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tcc, err = net.DialTimeout(\"tcp\", node.Addr, h.options.Timeout)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[rtcp] %s -> %s : %s\", conn.LocalAddr(), node.Addr, err)\n\t\t\tnode.MarkDead()\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\n\tdefer cc.Close()\n\tnode.ResetDead()\n\n\tlog.Logf(\"[rtcp] %s <-> %s\", conn.LocalAddr(), node.Addr)\n\ttransport(cc, conn)\n\tlog.Logf(\"[rtcp] %s >-< %s\", conn.LocalAddr(), node.Addr)\n}\n\ntype udpRemoteForwardHandler struct {\n\t*baseForwardHandler\n}\n\n// UDPRemoteForwardHandler creates a server Handler for UDP remote port forwarding server.\n// The raddr is the remote address that the server will forward to.\n// NOTE: as of 2.6, remote address can be a comma-separated address list.\nfunc UDPRemoteForwardHandler(raddr string, opts ...HandlerOption) Handler {\n\th := &udpRemoteForwardHandler{\n\t\tbaseForwardHandler: &baseForwardHandler{\n\t\t\traddr:   raddr,\n\t\t\tgroup:   NewNodeGroup(),\n\t\t\toptions: &HandlerOptions{},\n\t\t},\n\t}\n\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\n\treturn h\n}\n\nfunc (h *udpRemoteForwardHandler) Init(options ...HandlerOption) {\n\th.baseForwardHandler.Init(options...)\n}\n\nfunc (h *udpRemoteForwardHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tvar node Node\n\tvar err error\n\tif len(h.group.Nodes()) > 0 {\n\t\tnode, err = h.group.Next()\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[rudp] %s - %s : %s\", conn.RemoteAddr(), h.raddr, err)\n\t\t\treturn\n\t\t}\n\t}\n\n\traddr, err := net.ResolveUDPAddr(\"udp\", node.Addr)\n\tif err != nil {\n\t\tnode.MarkDead()\n\t\tlog.Logf(\"[rudp] %s - %s : %s\", conn.RemoteAddr(), node.Addr, err)\n\t\treturn\n\t}\n\tcc, err := net.DialUDP(\"udp\", nil, raddr)\n\tif err != nil {\n\t\tnode.MarkDead()\n\t\tlog.Logf(\"[rudp] %s - %s : %s\", conn.RemoteAddr(), node.Addr, err)\n\t\treturn\n\t}\n\tdefer cc.Close()\n\tnode.ResetDead()\n\n\tlog.Logf(\"[rudp] %s <-> %s\", conn.RemoteAddr(), node.Addr)\n\ttransport(conn, cc)\n\tlog.Logf(\"[rudp] %s >-< %s\", conn.RemoteAddr(), node.Addr)\n}\n\ntype tcpRemoteForwardListener struct {\n\taddr       net.Addr\n\tchain      *Chain\n\tconnChan   chan net.Conn\n\tln         net.Listener\n\tsession    *muxSession\n\tsessionMux sync.Mutex\n\tclosed     chan struct{}\n\tcloseMux   sync.Mutex\n}\n\n// TCPRemoteForwardListener creates a Listener for TCP remote port forwarding server.\nfunc TCPRemoteForwardListener(addr string, chain *Chain) (Listener, error) {\n\tladdr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tln := &tcpRemoteForwardListener{\n\t\taddr:     laddr,\n\t\tchain:    chain,\n\t\tconnChan: make(chan net.Conn, 1024),\n\t\tclosed:   make(chan struct{}),\n\t}\n\n\tif !ln.isChainValid() {\n\t\tln.ln, err = net.Listen(\"tcp\", ln.addr.String())\n\t\treturn ln, err\n\t}\n\n\tgo ln.listenLoop()\n\n\treturn ln, err\n}\n\nfunc (l *tcpRemoteForwardListener) isChainValid() bool {\n\tif l.chain.IsEmpty() {\n\t\treturn false\n\t}\n\n\tlastNode := l.chain.LastNode()\n\tif (lastNode.Protocol == \"forward\" && lastNode.Transport == \"ssh\") ||\n\t\tlastNode.Protocol == \"socks5\" || lastNode.Protocol == \"\" {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (l *tcpRemoteForwardListener) listenLoop() {\n\tvar tempDelay time.Duration\n\n\tfor {\n\t\tconn, err := l.accept()\n\n\t\tselect {\n\t\tcase <-l.closed:\n\t\t\tif conn != nil {\n\t\t\t\tconn.Close()\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif tempDelay == 0 {\n\t\t\t\ttempDelay = 1000 * time.Millisecond\n\t\t\t} else {\n\t\t\t\ttempDelay *= 2\n\t\t\t}\n\t\t\tif max := 6 * time.Second; tempDelay > max {\n\t\t\t\ttempDelay = max\n\t\t\t}\n\t\t\tlog.Logf(\"[rtcp] accept error: %v; retrying in %v\", err, tempDelay)\n\t\t\ttime.Sleep(tempDelay)\n\t\t\tcontinue\n\t\t}\n\n\t\ttempDelay = 0\n\n\t\tselect {\n\t\tcase l.connChan <- conn:\n\t\tdefault:\n\t\t\tconn.Close()\n\t\t\tlog.Logf(\"[rtcp] %s - %s: connection queue is full\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t}\n\t}\n}\n\nfunc (l *tcpRemoteForwardListener) Accept() (conn net.Conn, err error) {\n\tif l.ln != nil {\n\t\treturn l.ln.Accept()\n\t}\n\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase <-l.closed:\n\t\terr = errors.New(\"closed\")\n\t}\n\n\treturn\n}\n\nfunc (l *tcpRemoteForwardListener) accept() (conn net.Conn, err error) {\n\tlastNode := l.chain.LastNode()\n\tif lastNode.Protocol == \"forward\" && lastNode.Transport == \"ssh\" {\n\t\treturn l.chain.Dial(l.addr.String())\n\t}\n\n\tif l.isChainValid() {\n\t\tif lastNode.GetBool(\"mbind\") {\n\t\t\treturn l.muxAccept() // multiplexing support for binding.\n\t\t}\n\n\t\tcc, er := l.chain.Conn()\n\t\tif er != nil {\n\t\t\treturn nil, er\n\t\t}\n\t\tconn, err = l.waitConnectSOCKS5(cc)\n\t\tif err != nil {\n\t\t\tcc.Close()\n\t\t}\n\t}\n\treturn\n}\n\nfunc (l *tcpRemoteForwardListener) muxAccept() (conn net.Conn, err error) {\n\tsession, err := l.getSession()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcc, err := session.Accept()\n\tif err != nil {\n\t\tsession.Close()\n\t\treturn nil, err\n\t}\n\n\treturn cc, nil\n}\n\nfunc (l *tcpRemoteForwardListener) getSession() (s *muxSession, err error) {\n\tl.sessionMux.Lock()\n\tdefer l.sessionMux.Unlock()\n\n\tif l.session != nil && !l.session.IsClosed() {\n\t\treturn l.session, nil\n\t}\n\n\tconn, err := l.chain.Conn()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tif err != nil {\n\t\t\tc.Close()\n\t\t}\n\t}(conn)\n\n\tconn.SetDeadline(time.Now().Add(HandshakeTimeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tconn, err = socks5Handshake(conn, userSocks5HandshakeOption(l.chain.LastNode().User))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq := gosocks5.NewRequest(CmdMuxBind, toSocksAddr(l.addr))\n\tif err := req.Write(conn); err != nil {\n\t\tlog.Log(\"[rtcp] SOCKS5 BIND request: \", err)\n\t\treturn nil, err\n\t}\n\n\trep, err := gosocks5.ReadReply(conn)\n\tif err != nil {\n\t\tlog.Log(\"[rtcp] SOCKS5 BIND reply: \", err)\n\t\treturn nil, err\n\t}\n\tif rep.Rep != gosocks5.Succeeded {\n\t\tlog.Logf(\"[rtcp] bind on %s failure\", l.addr)\n\t\treturn nil, fmt.Errorf(\"Bind on %s failure\", l.addr.String())\n\t}\n\tlog.Logf(\"[rtcp] BIND ON %s OK\", rep.Addr)\n\n\t// Upgrade connection to multiplex stream.\n\tsession, err := smux.Server(conn, smux.DefaultConfig())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl.session = &muxSession{\n\t\tconn:    conn,\n\t\tsession: session,\n\t}\n\n\treturn l.session, nil\n}\n\nfunc (l *tcpRemoteForwardListener) waitConnectSOCKS5(conn net.Conn) (net.Conn, error) {\n\tconn, err := socks5Handshake(conn, userSocks5HandshakeOption(l.chain.LastNode().User))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq := gosocks5.NewRequest(gosocks5.CmdBind, toSocksAddr(l.addr))\n\tif err := req.Write(conn); err != nil {\n\t\tlog.Log(\"[rtcp] SOCKS5 BIND request: \", err)\n\t\treturn nil, err\n\t}\n\n\t// first reply, bind status\n\tconn.SetReadDeadline(time.Now().Add(ReadTimeout))\n\trep, err := gosocks5.ReadReply(conn)\n\tif err != nil {\n\t\tlog.Log(\"[rtcp] SOCKS5 BIND reply: \", err)\n\t\treturn nil, err\n\t}\n\tconn.SetReadDeadline(time.Time{})\n\tif rep.Rep != gosocks5.Succeeded {\n\t\tlog.Logf(\"[rtcp] bind on %s failure\", l.addr)\n\t\treturn nil, fmt.Errorf(\"Bind on %s failure\", l.addr.String())\n\t}\n\tlog.Logf(\"[rtcp] BIND ON %s OK\", rep.Addr)\n\n\t// second reply, peer connected\n\trep, err = gosocks5.ReadReply(conn)\n\tif err != nil {\n\t\tlog.Log(\"[rtcp]\", err)\n\t\treturn nil, err\n\t}\n\tif rep.Rep != gosocks5.Succeeded {\n\t\tlog.Logf(\"[rtcp] peer connect failure: %d\", rep.Rep)\n\t\treturn nil, errors.New(\"peer connect failure\")\n\t}\n\n\tlog.Logf(\"[rtcp] PEER %s CONNECTED\", rep.Addr)\n\treturn conn, nil\n}\n\nfunc (l *tcpRemoteForwardListener) Addr() net.Addr {\n\tif l.ln != nil {\n\t\treturn l.ln.Addr()\n\t}\n\treturn l.addr\n}\n\nfunc (l *tcpRemoteForwardListener) Close() error {\n\tif l.ln != nil {\n\t\treturn l.ln.Close()\n\t}\n\n\tl.closeMux.Lock()\n\tdefer l.closeMux.Unlock()\n\n\tselect {\n\tcase <-l.closed:\n\t\treturn nil\n\tdefault:\n\t\tclose(l.closed)\n\t}\n\treturn nil\n}\n\ntype udpRemoteForwardListener struct {\n\taddr     net.Addr\n\tchain    *Chain\n\tconnMap  *udpConnMap\n\tconnChan chan net.Conn\n\tln       *net.UDPConn\n\tttl      time.Duration\n\tclosed   chan struct{}\n\tready    chan struct{}\n\tonce     sync.Once\n\tcloseMux sync.Mutex\n\tconfig   *UDPListenConfig\n}\n\n// UDPRemoteForwardListener creates a Listener for UDP remote port forwarding server.\nfunc UDPRemoteForwardListener(addr string, chain *Chain, cfg *UDPListenConfig) (Listener, error) {\n\tladdr, err := net.ResolveUDPAddr(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cfg == nil {\n\t\tcfg = &UDPListenConfig{}\n\t}\n\n\tbacklog := cfg.Backlog\n\tif backlog <= 0 {\n\t\tbacklog = defaultBacklog\n\t}\n\n\tln := &udpRemoteForwardListener{\n\t\taddr:     laddr,\n\t\tchain:    chain,\n\t\tconnMap:  new(udpConnMap),\n\t\tconnChan: make(chan net.Conn, backlog),\n\t\tready:    make(chan struct{}),\n\t\tclosed:   make(chan struct{}),\n\t\tconfig:   cfg,\n\t}\n\n\tgo ln.listenLoop()\n\n\t<-ln.ready\n\n\treturn ln, err\n}\n\nfunc (l *udpRemoteForwardListener) isChainValid() bool {\n\tif l.chain.IsEmpty() {\n\t\treturn false\n\t}\n\n\tlastNode := l.chain.LastNode()\n\treturn lastNode.Protocol == \"socks5\" || lastNode.Protocol == \"\"\n}\n\nfunc (l *udpRemoteForwardListener) listenLoop() {\n\tfor {\n\t\tconn, err := l.connect()\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[rudp] %s : %s\", l.Addr(), err)\n\t\t\treturn\n\t\t}\n\n\t\tl.once.Do(func() {\n\t\t\tclose(l.ready)\n\t\t})\n\n\t\tfunc() {\n\t\t\tdefer conn.Close()\n\n\t\t\tfor {\n\t\t\t\tb := make([]byte, mediumBufferSize)\n\t\t\t\tn, raddr, err := conn.ReadFrom(b)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Logf(\"[rudp] %s : %s\", l.Addr(), err)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tuc, ok := l.connMap.Get(raddr.String())\n\t\t\t\tif !ok {\n\t\t\t\t\tuc = newUDPServerConn(conn, raddr, &udpServerConnConfig{\n\t\t\t\t\t\tttl:   l.config.TTL,\n\t\t\t\t\t\tqsize: l.config.QueueSize,\n\t\t\t\t\t\tonClose: func() {\n\t\t\t\t\t\t\tl.connMap.Delete(raddr.String())\n\t\t\t\t\t\t\tlog.Logf(\"[rudp] %s closed (%d)\", raddr, l.connMap.Size())\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\tselect {\n\t\t\t\t\tcase l.connChan <- uc:\n\t\t\t\t\t\tl.connMap.Set(raddr.String(), uc)\n\t\t\t\t\t\tlog.Logf(\"[rudp] %s -> %s (%d)\", raddr, l.Addr(), l.connMap.Size())\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tuc.Close()\n\t\t\t\t\t\tlog.Logf(\"[rudp] %s - %s: connection queue is full (%d)\",\n\t\t\t\t\t\t\traddr, l.Addr(), cap(l.connChan))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tselect {\n\t\t\t\tcase uc.rChan <- b[:n]:\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tlog.Logf(\"[rudp] %s >>> %s : length %d\", raddr, l.Addr(), n)\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tlog.Logf(\"[rudp] %s -> %s : recv queue is full\", raddr, l.Addr(), cap(uc.rChan))\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n}\n\nfunc (l *udpRemoteForwardListener) connect() (conn net.PacketConn, err error) {\n\tvar tempDelay time.Duration\n\n\tfor {\n\t\tselect {\n\t\tcase <-l.closed:\n\t\t\treturn nil, errors.New(\"closed\")\n\t\tdefault:\n\t\t}\n\n\t\tif l.isChainValid() {\n\t\t\tvar cc net.Conn\n\t\t\tcc, err = getSocks5UDPTunnel(l.chain, l.addr)\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[rudp] %s : %s\", l.Addr(), err)\n\t\t\t} else {\n\t\t\t\tconn = cc.(net.PacketConn)\n\t\t\t}\n\t\t} else {\n\t\t\tvar uc *net.UDPConn\n\t\t\tuc, err = net.ListenUDP(\"udp\", l.addr.(*net.UDPAddr))\n\t\t\tif err == nil {\n\t\t\t\tl.addr = uc.LocalAddr()\n\t\t\t\tconn = uc\n\t\t\t}\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif tempDelay == 0 {\n\t\t\t\ttempDelay = 1000 * time.Millisecond\n\t\t\t} else {\n\t\t\t\ttempDelay *= 2\n\t\t\t}\n\t\t\tif max := 6 * time.Second; tempDelay > max {\n\t\t\t\ttempDelay = max\n\t\t\t}\n\t\t\tlog.Logf(\"[rudp] Accept error: %v; retrying in %v\", err, tempDelay)\n\t\t\ttime.Sleep(tempDelay)\n\t\t\tcontinue\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc (l *udpRemoteForwardListener) Accept() (conn net.Conn, err error) {\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase <-l.closed:\n\t\terr = errors.New(\"accpet on closed listener\")\n\t}\n\treturn\n}\n\nfunc (l *udpRemoteForwardListener) Addr() net.Addr {\n\treturn l.addr\n}\n\nfunc (l *udpRemoteForwardListener) Close() error {\n\tl.closeMux.Lock()\n\tdefer l.closeMux.Unlock()\n\n\tselect {\n\tcase <-l.closed:\n\t\treturn nil\n\tdefault:\n\t\tl.connMap.Range(func(k interface{}, v *udpServerConn) bool {\n\t\t\tv.Close()\n\t\t\treturn true\n\t\t})\n\t\tclose(l.closed)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "forward_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc tcpDirectForwardRoundtrip(targetURL string, data []byte) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\th := TCPDirectForwardHandler(u.Host)\n\th.Init()\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  h,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestTCPDirectForward(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := tcpDirectForwardRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc BenchmarkTCPDirectForward(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tu, err := url.Parse(httpSrv.URL)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\th := TCPDirectForwardHandler(u.Host)\n\th.Init()\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  h,\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkTCPDirectForwardParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tu, err := url.Parse(httpSrv.URL)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\th := TCPDirectForwardHandler(u.Host)\n\th.Init()\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  h,\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc udpDirectForwardRoundtrip(t *testing.T, host string, data []byte) error {\n\tln, err := UDPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: UDPTransporter(),\n\t}\n\n\th := UDPDirectForwardHandler(host)\n\th.Init()\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  h,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn udpRoundtrip(t, client, server, host, data)\n}\n\nfunc TestUDPDirectForward(t *testing.T) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\terr := udpDirectForwardRoundtrip(t, udpSrv.Addr(), sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc BenchmarkUDPDirectForward(b *testing.B) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := UDPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: UDPTransporter(),\n\t}\n\n\th := UDPDirectForwardHandler(udpSrv.Addr())\n\th.Init()\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  h,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := udpRoundtrip(b, client, server, udpSrv.Addr(), sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkUDPDirectForwardParallel(b *testing.B) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := UDPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: UDPTransporter(),\n\t}\n\n\th := UDPDirectForwardHandler(udpSrv.Addr())\n\th.Init()\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  h,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := udpRoundtrip(b, client, server, udpSrv.Addr(), sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc tcpRemoteForwardRoundtrip(t *testing.T, targetURL string, data []byte) error {\n\tln, err := TCPRemoteForwardListener(\"localhost:0\", nil) // listening on localhost\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\th := TCPRemoteForwardHandler(u.Host) // forward to u.Host\n\th.Init()\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  h,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestTCPRemoteForward(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := tcpRemoteForwardRoundtrip(t, httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc udpRemoteForwardRoundtrip(t *testing.T, host string, data []byte) error {\n\tln, err := UDPRemoteForwardListener(\"localhost:0\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: UDPTransporter(),\n\t}\n\n\th := UDPRemoteForwardHandler(host)\n\th.Init()\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  h,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn udpRoundtrip(t, client, server, host, data)\n}\n\nfunc TestUDPRemoteForward(t *testing.T) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := udpRemoteForwardRoundtrip(t, udpSrv.Addr(), sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n"
  },
  {
    "path": "ftcp.go",
    "content": "package gost\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/xtaci/tcpraw\"\n)\n\ntype fakeTCPTransporter struct{}\n\n// FakeTCPTransporter creates a Transporter that is used by fake tcp client.\nfunc FakeTCPTransporter() Transporter {\n\treturn &fakeTCPTransporter{}\n}\n\nfunc (tr *fakeTCPTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\traddr, er := net.ResolveTCPAddr(\"tcp\", addr)\n\tif er != nil {\n\t\treturn nil, er\n\t}\n\tc, err := tcpraw.Dial(\"tcp\", addr)\n\tif err != nil {\n\t\treturn\n\t}\n\tconn = &fakeTCPConn{\n\t\traddr:      raddr,\n\t\tPacketConn: c,\n\t}\n\treturn conn, nil\n}\n\nfunc (tr *fakeTCPTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\treturn conn, nil\n}\n\nfunc (tr *fakeTCPTransporter) Multiplex() bool {\n\treturn false\n}\n\n// FakeTCPListenConfig is config for fake TCP Listener.\ntype FakeTCPListenConfig struct {\n\tTTL       time.Duration\n\tBacklog   int\n\tQueueSize int\n}\n\ntype fakeTCPListener struct {\n\tln       net.PacketConn\n\tconnChan chan net.Conn\n\terrChan  chan error\n\tconnMap  udpConnMap\n\tconfig   *FakeTCPListenConfig\n}\n\n// FakeTCPListener creates a Listener for fake TCP server.\nfunc FakeTCPListener(addr string, cfg *FakeTCPListenConfig) (Listener, error) {\n\tln, err := tcpraw.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cfg == nil {\n\t\tcfg = &FakeTCPListenConfig{}\n\t}\n\n\tbacklog := cfg.Backlog\n\tif backlog <= 0 {\n\t\tbacklog = defaultBacklog\n\t}\n\n\tl := &fakeTCPListener{\n\t\tln:       ln,\n\t\tconnChan: make(chan net.Conn, backlog),\n\t\terrChan:  make(chan error, 1),\n\t\tconfig:   cfg,\n\t}\n\tgo l.listenLoop()\n\treturn l, nil\n}\n\nfunc (l *fakeTCPListener) listenLoop() {\n\tfor {\n\t\tb := make([]byte, mediumBufferSize)\n\t\tn, raddr, err := l.ln.ReadFrom(b)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[ftcp] peer -> %s : %s\", l.Addr(), err)\n\t\t\tl.Close()\n\t\t\tl.errChan <- err\n\t\t\tclose(l.errChan)\n\t\t\treturn\n\t\t}\n\n\t\tconn, ok := l.connMap.Get(raddr.String())\n\t\tif !ok {\n\t\t\tconn = newUDPServerConn(l.ln, raddr, &udpServerConnConfig{\n\t\t\t\tttl:   l.config.TTL,\n\t\t\t\tqsize: l.config.QueueSize,\n\t\t\t\tonClose: func() {\n\t\t\t\t\tl.connMap.Delete(raddr.String())\n\t\t\t\t\tlog.Logf(\"[ftcp] %s closed (%d)\", raddr, l.connMap.Size())\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tselect {\n\t\t\tcase l.connChan <- conn:\n\t\t\t\tl.connMap.Set(raddr.String(), conn)\n\t\t\t\tlog.Logf(\"[ftcp] %s -> %s (%d)\", raddr, l.Addr(), l.connMap.Size())\n\t\t\tdefault:\n\t\t\t\tconn.Close()\n\t\t\t\tlog.Logf(\"[ftcp] %s - %s: connection queue is full (%d)\", raddr, l.Addr(), cap(l.connChan))\n\t\t\t}\n\t\t}\n\n\t\tselect {\n\t\tcase conn.rChan <- b[:n]:\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[ftcp] %s >>> %s : length %d\", raddr, l.Addr(), n)\n\t\t\t}\n\t\tdefault:\n\t\t\tlog.Logf(\"[ftcp] %s -> %s : recv queue is full (%d)\", raddr, l.Addr(), cap(conn.rChan))\n\t\t}\n\t}\n}\n\nfunc (l *fakeTCPListener) Accept() (conn net.Conn, err error) {\n\tvar ok bool\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err, ok = <-l.errChan:\n\t\tif !ok {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\n\nfunc (l *fakeTCPListener) Addr() net.Addr {\n\treturn l.ln.LocalAddr()\n}\n\nfunc (l *fakeTCPListener) Close() error {\n\terr := l.ln.Close()\n\tl.connMap.Range(func(k interface{}, v *udpServerConn) bool {\n\t\tv.Close()\n\t\treturn true\n\t})\n\n\treturn err\n}\n\ntype fakeTCPConn struct {\n\traddr net.Addr\n\tnet.PacketConn\n}\n\nfunc (c *fakeTCPConn) Read(b []byte) (n int, err error) {\n\tn, _, err = c.ReadFrom(b)\n\treturn\n}\n\nfunc (c *fakeTCPConn) Write(b []byte) (n int, err error) {\n\treturn c.WriteTo(b, c.raddr)\n}\n\nfunc (c *fakeTCPConn) RemoteAddr() net.Addr {\n\treturn c.raddr\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/ginuerzh/gost\n\ngo 1.22\n\nreplace github.com/templexxx/cpu v0.0.7 => github.com/templexxx/cpu v0.0.10-0.20211111114238-98168dcec14a\n\nrequire (\n\tgit.torproject.org/pluggable-transports/goptlib.git v1.3.0\n\tgithub.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed\n\tgithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2\n\tgithub.com/go-gost/gosocks4 v0.0.1\n\tgithub.com/go-gost/gosocks5 v0.3.0\n\tgithub.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7\n\tgithub.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451\n\tgithub.com/go-log/log v0.2.0\n\tgithub.com/gobwas/glob v0.2.3\n\tgithub.com/gorilla/websocket v1.5.1\n\tgithub.com/klauspost/compress v1.17.6\n\tgithub.com/mdlayher/vsock v1.2.1\n\tgithub.com/miekg/dns v1.1.58\n\tgithub.com/quic-go/quic-go v0.45.0\n\tgithub.com/ryanuber/go-glob v1.0.0\n\tgithub.com/shadowsocks/go-shadowsocks2 v0.1.5\n\tgithub.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601\n\tgithub.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8\n\tgithub.com/xtaci/kcp-go/v5 v5.6.7\n\tgithub.com/xtaci/smux v1.5.24\n\tgithub.com/xtaci/tcpraw v1.2.25\n\tgitlab.com/yawning/obfs4.git v0.0.0-20220204003609-77af0cba934d\n\tgolang.org/x/crypto v0.24.0\n\tgolang.org/x/net v0.26.0\n)\n\nrequire (\n\tfilippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20 // indirect\n\tgithub.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect\n\tgithub.com/coreos/go-iptables v0.6.0 // indirect\n\tgithub.com/dchest/siphash v1.2.2 // indirect\n\tgithub.com/go-task/slim-sprig/v3 v3.0.0 // indirect\n\tgithub.com/google/gopacket v1.1.19 // indirect\n\tgithub.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.6 // indirect\n\tgithub.com/klauspost/reedsolomon v1.12.0 // indirect\n\tgithub.com/mdlayher/socket v0.4.1 // indirect\n\tgithub.com/onsi/ginkgo/v2 v2.19.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect\n\tgithub.com/templexxx/cpu v0.1.0 // indirect\n\tgithub.com/templexxx/xorsimd v0.4.2 // indirect\n\tgithub.com/tjfoc/gmsm v1.4.1 // indirect\n\tgithub.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect\n\tgitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb // indirect\n\tgo.uber.org/mock v0.4.0 // indirect\n\tgolang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect\n\tgolang.org/x/mod v0.18.0 // indirect\n\tgolang.org/x/sync v0.7.0 // indirect\n\tgolang.org/x/sys v0.21.0 // indirect\n\tgolang.org/x/text v0.16.0 // indirect\n\tgolang.org/x/tools v0.22.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\nfilippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20 h1:iJoUgXvhagsNMrJrvavw7vu1eG8+hm6jLOxlLFcoODw=\nfilippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=\ngit.torproject.org/pluggable-transports/goptlib.git v1.0.0/go.mod h1:YT4XMSkuEXbtqlydr9+OxqFAyspUv0Gr9qhM3B++o/Q=\ngit.torproject.org/pluggable-transports/goptlib.git v1.3.0 h1:G+iuRUblCCC2xnO+0ag1/4+aaM98D5mjWP1M0v9s8a0=\ngit.torproject.org/pluggable-transports/goptlib.git v1.3.0/go.mod h1:4PBMl1dg7/3vMWSoWb46eGWlrxkUyn/CAJmxhDLAlDs=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed h1:eqa6queieK8SvoszxCu0WwH7lSVeL4/N/f1JwOMw1G4=\ngithub.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed/go.mod h1:rA52xkgZwql9LRZXWb2arHEFP6qSR48KY2xOfWzEciQ=\ngithub.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=\ngithub.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=\ngithub.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=\ngithub.com/dchest/siphash v1.2.2 h1:9DFz8tQwl9pTVt5iok/9zKyzA1Q6bRGiF3HPiEEVr9I=\ngithub.com/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=\ngithub.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=\ngithub.com/go-gost/gosocks5 v0.3.0 h1:Hkmp9YDRBSCJd7xywW6dBPT6B9aQTkuWd+3WCheJiJA=\ngithub.com/go-gost/gosocks5 v0.3.0/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=\ngithub.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=\ngithub.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=\ngithub.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 h1:xj8gUZGYO3nb5+6Bjw9+tsFkA9sYynrOvDvvC4uDV2I=\ngithub.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=\ngithub.com/go-log/log v0.2.0 h1:z8i91GBudxD5L3RmF0KVpetCbcGWAV7q1Tw1eRwQM9Q=\ngithub.com/go-log/log v0.2.0/go.mod h1:xzCnwajcues/6w7lne3yK2QU7DBPW7kqbgPGG5AF65U=\ngithub.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=\ngithub.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=\ngithub.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=\ngithub.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=\ngithub.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=\ngithub.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=\ngithub.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=\ngithub.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=\ngithub.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=\ngithub.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno=\ngithub.com/klauspost/reedsolomon v1.12.0/go.mod h1:EPLZJeh4l27pUGC3aXOjheaoh1I9yut7xTURiW3LQ9Y=\ngithub.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=\ngithub.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=\ngithub.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=\ngithub.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=\ngithub.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=\ngithub.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=\ngithub.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=\ngithub.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=\ngithub.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=\ngithub.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/quic-go/quic-go v0.45.0 h1:OHmkQGM37luZITyTSu6ff03HP/2IrwDX1ZFiNEhSFUE=\ngithub.com/quic-go/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=\ngithub.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=\ngithub.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=\ngithub.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=\ngithub.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=\ngithub.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28=\ngithub.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM=\ngithub.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 h1:XU9hik0exChEmY92ALW4l9WnDodxLVS9yOSNh2SizaQ=\ngithub.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY=\ngithub.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=\ngithub.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=\ngithub.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=\ngithub.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=\ngithub.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=\ngithub.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=\ngithub.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=\ngithub.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=\ngithub.com/xtaci/kcp-go/v5 v5.6.7 h1:7+rnxNFIsjEwTXQk4cSZpXM4pO0hqtpwE1UFFoJBffA=\ngithub.com/xtaci/kcp-go/v5 v5.6.7/go.mod h1:oE9j2NVqAkuKO5o8ByKGch3vgVX3BNf8zqP8JiGq0bM=\ngithub.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=\ngithub.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=\ngithub.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=\ngithub.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=\ngithub.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=\ngithub.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=\ngitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb h1:qRSZHsODmAP5qDvb3YsO7Qnf3TRiVbGxNG/WYnlM4/o=\ngitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb/go.mod h1:gvdJuZuO/tPZyhEV8K3Hmoxv/DWud5L4qEQxfYjEUTo=\ngitlab.com/yawning/obfs4.git v0.0.0-20220204003609-77af0cba934d h1:tJ8F7ABaQ3p3wjxwXiWSktVDgjZEXkvaRawd2rIq5ws=\ngitlab.com/yawning/obfs4.git v0.0.0-20220204003609-77af0cba934d/go.mod h1:9GcM8QNU9/wXtEEH2q8bVOnPI7FtIF6VVLzZ1l6Hgf8=\ngo.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=\ngo.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=\ngolang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=\ngolang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=\ngolang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=\ngolang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=\ngolang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=\ngolang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=\ngolang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=\ngolang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=\ngolang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=\ngolang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=\ngolang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=\ngoogle.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\n"
  },
  {
    "path": "gost.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"io\"\n\t\"math/big\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\n// Version is the gost version.\nconst Version = \"2.12.0\"\n\n// Debug is a flag that enables the debug log.\nvar Debug bool\n\nvar (\n\ttinyBufferSize   = 512\n\tsmallBufferSize  = 2 * 1024  // 2KB small buffer\n\tmediumBufferSize = 8 * 1024  // 8KB medium buffer\n\tlargeBufferSize  = 32 * 1024 // 32KB large buffer\n)\n\nvar (\n\tsPool = sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn make([]byte, smallBufferSize)\n\t\t},\n\t}\n\tmPool = sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn make([]byte, mediumBufferSize)\n\t\t},\n\t}\n\tlPool = sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn make([]byte, largeBufferSize)\n\t\t},\n\t}\n)\n\nvar (\n\t// KeepAliveTime is the keep alive time period for TCP connection.\n\tKeepAliveTime = 180 * time.Second\n\t// DialTimeout is the timeout of dial.\n\tDialTimeout = 5 * time.Second\n\t// HandshakeTimeout is the timeout of handshake.\n\tHandshakeTimeout = 5 * time.Second\n\t// ConnectTimeout is the timeout for connect.\n\tConnectTimeout = 5 * time.Second\n\t// ReadTimeout is the timeout for reading.\n\tReadTimeout = 10 * time.Second\n\t// WriteTimeout is the timeout for writing.\n\tWriteTimeout = 10 * time.Second\n\t// PingTimeout is the timeout for pinging.\n\tPingTimeout = 30 * time.Second\n\t// PingRetries is the reties of ping.\n\tPingRetries = 1\n\t// default udp node TTL in second for udp port forwarding.\n\tdefaultTTL       = 60 * time.Second\n\tdefaultBacklog   = 128\n\tdefaultQueueSize = 128\n)\n\nvar (\n\t// DefaultTLSConfig is a default TLS config for internal use.\n\tDefaultTLSConfig *tls.Config\n\n\t// DefaultUserAgent is the default HTTP User-Agent header used by HTTP and websocket.\n\tDefaultUserAgent = \"Chrome/78.0.3904.106\"\n\n\tDefaultProxyAgent = \"gost/\" + Version\n\n\t// DefaultMTU is the default mtu for tun/tap device\n\tDefaultMTU = 1350\n)\n\n// SetLogger sets a new logger for internal log system.\nfunc SetLogger(logger log.Logger) {\n\tlog.DefaultLogger = logger\n}\n\n// GenCertificate generates a random TLS certificate.\nfunc GenCertificate() (cert tls.Certificate, err error) {\n\trawCert, rawKey, err := generateKeyPair()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn tls.X509KeyPair(rawCert, rawKey)\n}\n\nfunc generateKeyPair() (rawCert, rawKey []byte, err error) {\n\t// Create private key and self-signed certificate\n\t// Adapted from https://golang.org/src/crypto/tls/generate_cert.go\n\n\tpriv, err := rsa.GenerateKey(rand.Reader, 2048)\n\tif err != nil {\n\t\treturn\n\t}\n\tvalidFor := time.Hour * 24 * 365 * 10 // ten years\n\tnotBefore := time.Now()\n\tnotAfter := notBefore.Add(validFor)\n\tserialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)\n\tserialNumber, err := rand.Int(rand.Reader, serialNumberLimit)\n\ttemplate := x509.Certificate{\n\t\tSerialNumber: serialNumber,\n\t\tSubject: pkix.Name{\n\t\t\tOrganization: []string{\"gost\"},\n\t\t},\n\t\tNotBefore: notBefore,\n\t\tNotAfter:  notAfter,\n\n\t\tKeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,\n\t\tExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},\n\t\tBasicConstraintsValid: true,\n\t}\n\tderBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)\n\tif err != nil {\n\t\treturn\n\t}\n\n\trawCert = pem.EncodeToMemory(&pem.Block{Type: \"CERTIFICATE\", Bytes: derBytes})\n\trawKey = pem.EncodeToMemory(&pem.Block{Type: \"RSA PRIVATE KEY\", Bytes: x509.MarshalPKCS1PrivateKey(priv)})\n\n\treturn\n}\n\ntype readWriter struct {\n\tr io.Reader\n\tw io.Writer\n}\n\nfunc (rw *readWriter) Read(p []byte) (n int, err error) {\n\treturn rw.r.Read(p)\n}\n\nfunc (rw *readWriter) Write(p []byte) (n int, err error) {\n\treturn rw.w.Write(p)\n}\n\nvar nopClientConn = &nopConn{}\n\n// a nop connection implements net.Conn,\n// it does nothing.\ntype nopConn struct{}\n\nfunc (c *nopConn) Read(b []byte) (n int, err error) {\n\treturn 0, &net.OpError{Op: \"read\", Net: \"nop\", Source: nil, Addr: nil, Err: errors.New(\"read not supported\")}\n}\n\nfunc (c *nopConn) Write(b []byte) (n int, err error) {\n\treturn 0, &net.OpError{Op: \"write\", Net: \"nop\", Source: nil, Addr: nil, Err: errors.New(\"write not supported\")}\n}\n\nfunc (c *nopConn) Close() error {\n\treturn nil\n}\n\nfunc (c *nopConn) LocalAddr() net.Addr {\n\treturn nil\n}\n\nfunc (c *nopConn) RemoteAddr() net.Addr {\n\treturn nil\n}\n\nfunc (c *nopConn) SetDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"nop\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *nopConn) SetReadDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"nop\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *nopConn) SetWriteDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"nop\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\n// splitLine splits a line text by white space, mainly used by config parser.\nfunc splitLine(line string) []string {\n\tif line == \"\" {\n\t\treturn nil\n\t}\n\tif n := strings.IndexByte(line, '#'); n >= 0 {\n\t\tline = line[:n]\n\t}\n\tline = strings.Replace(line, \"\\t\", \" \", -1)\n\tline = strings.TrimSpace(line)\n\n\tvar ss []string\n\tfor _, s := range strings.Split(line, \" \") {\n\t\tif s = strings.TrimSpace(s); s != \"\" {\n\t\t\tss = append(ss, s)\n\t\t}\n\t}\n\treturn ss\n}\n\nfunc connStateCallback(conn net.Conn, cs http.ConnState) {\n\tswitch cs {\n\tcase http.StateNew:\n\t\tconn.SetReadDeadline(time.Now().Add(30 * time.Second))\n\tdefault:\n\t}\n}\n"
  },
  {
    "path": "handler.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"crypto/tls\"\n\t\"net\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/go-gost/gosocks4\"\n\t\"github.com/go-gost/gosocks5\"\n\t\"github.com/go-log/log\"\n)\n\n// Handler is a proxy server handler\ntype Handler interface {\n\tInit(options ...HandlerOption)\n\tHandle(net.Conn)\n}\n\n// HandlerOptions describes the options for Handler.\ntype HandlerOptions struct {\n\tAddr          string\n\tChain         *Chain\n\tUsers         []*url.Userinfo\n\tAuthenticator Authenticator\n\tTLSConfig     *tls.Config\n\tWhitelist     *Permissions\n\tBlacklist     *Permissions\n\tStrategy      Strategy\n\tMaxFails      int\n\tFailTimeout   time.Duration\n\tBypass        *Bypass\n\tRetries       int\n\tTimeout       time.Duration\n\tResolver      Resolver\n\tHosts         *Hosts\n\tProbeResist   string\n\tKnockingHost  string\n\tNode          Node\n\tHost          string\n\tIPs           []string\n\tTCPMode       bool\n\tIPRoutes      []IPRoute\n\tProxyAgent    string\n\tHTTPTunnel    bool\n}\n\n// HandlerOption allows a common way to set handler options.\ntype HandlerOption func(opts *HandlerOptions)\n\n// AddrHandlerOption sets the Addr option of HandlerOptions.\nfunc AddrHandlerOption(addr string) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Addr = addr\n\t}\n}\n\n// ChainHandlerOption sets the Chain option of HandlerOptions.\nfunc ChainHandlerOption(chain *Chain) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Chain = chain\n\t}\n}\n\n// UsersHandlerOption sets the Users option of HandlerOptions.\nfunc UsersHandlerOption(users ...*url.Userinfo) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Users = users\n\n\t\tkvs := make(map[string]string)\n\t\tfor _, u := range users {\n\t\t\tif u != nil {\n\t\t\t\tkvs[u.Username()], _ = u.Password()\n\t\t\t}\n\t\t}\n\t\tif len(kvs) > 0 {\n\t\t\topts.Authenticator = NewLocalAuthenticator(kvs)\n\t\t}\n\t}\n}\n\n// AuthenticatorHandlerOption sets the Authenticator option of HandlerOptions.\nfunc AuthenticatorHandlerOption(au Authenticator) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Authenticator = au\n\t}\n}\n\n// TLSConfigHandlerOption sets the TLSConfig option of HandlerOptions.\nfunc TLSConfigHandlerOption(config *tls.Config) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.TLSConfig = config\n\t}\n}\n\n// WhitelistHandlerOption sets the Whitelist option of HandlerOptions.\nfunc WhitelistHandlerOption(whitelist *Permissions) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Whitelist = whitelist\n\t}\n}\n\n// BlacklistHandlerOption sets the Blacklist option of HandlerOptions.\nfunc BlacklistHandlerOption(blacklist *Permissions) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Blacklist = blacklist\n\t}\n}\n\n// BypassHandlerOption sets the bypass option of HandlerOptions.\nfunc BypassHandlerOption(bypass *Bypass) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Bypass = bypass\n\t}\n}\n\n// StrategyHandlerOption sets the strategy option of HandlerOptions.\nfunc StrategyHandlerOption(strategy Strategy) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Strategy = strategy\n\t}\n}\n\n// MaxFailsHandlerOption sets the max_fails option of HandlerOptions.\nfunc MaxFailsHandlerOption(n int) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.MaxFails = n\n\t}\n}\n\n// FailTimeoutHandlerOption sets the fail_timeout option of HandlerOptions.\nfunc FailTimeoutHandlerOption(d time.Duration) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.FailTimeout = d\n\t}\n}\n\n// RetryHandlerOption sets the retry option of HandlerOptions.\nfunc RetryHandlerOption(retries int) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Retries = retries\n\t}\n}\n\n// TimeoutHandlerOption sets the timeout option of HandlerOptions.\nfunc TimeoutHandlerOption(timeout time.Duration) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Timeout = timeout\n\t}\n}\n\n// ResolverHandlerOption sets the resolver option of HandlerOptions.\nfunc ResolverHandlerOption(resolver Resolver) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Resolver = resolver\n\t}\n}\n\n// HostsHandlerOption sets the Hosts option of HandlerOptions.\nfunc HostsHandlerOption(hosts *Hosts) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Hosts = hosts\n\t}\n}\n\n// ProbeResistHandlerOption adds the probe resistance for HTTP proxy.\nfunc ProbeResistHandlerOption(pr string) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.ProbeResist = pr\n\t}\n}\n\n// KnockingHandlerOption adds the knocking host for probe resistance.\nfunc KnockingHandlerOption(host string) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.KnockingHost = host\n\t}\n}\n\n// NodeHandlerOption set the server node for server handler.\nfunc NodeHandlerOption(node Node) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Node = node\n\t}\n}\n\n// HostHandlerOption sets the target host for SNI proxy.\nfunc HostHandlerOption(host string) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.Host = host\n\t}\n}\n\n// IPsHandlerOption sets the ip list for port forward.\nfunc IPsHandlerOption(ips []string) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.IPs = ips\n\t}\n}\n\n// TCPModeHandlerOption sets the tcp mode for tun/tap device.\nfunc TCPModeHandlerOption(b bool) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.TCPMode = b\n\t}\n}\n\n// IPRoutesHandlerOption sets the IP routes for tun tunnel.\nfunc IPRoutesHandlerOption(routes ...IPRoute) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.IPRoutes = routes\n\t}\n}\n\n// ProxyAgentHandlerOption sets the proxy agent for http handler.\nfunc ProxyAgentHandlerOption(agent string) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.ProxyAgent = agent\n\t}\n}\n\n// HTTPTunnelHandlerOption sets the Tunnel mode for HTTP client used in HTTP handler.\nfunc HTTPTunnelHandlerOption(tunnelMode bool) HandlerOption {\n\treturn func(opts *HandlerOptions) {\n\t\topts.HTTPTunnel = tunnelMode\n\t}\n}\n\ntype autoHandler struct {\n\toptions *HandlerOptions\n}\n\n// AutoHandler creates a server Handler for auto proxy server.\nfunc AutoHandler(opts ...HandlerOption) Handler {\n\th := &autoHandler{}\n\th.Init(opts...)\n\treturn h\n}\n\nfunc (h *autoHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *autoHandler) Handle(conn net.Conn) {\n\tbr := bufio.NewReader(conn)\n\tb, err := br.Peek(1)\n\tif err != nil {\n\t\tlog.Logf(\"[auto] %s - %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\tconn.Close()\n\t\treturn\n\t}\n\n\tcc := &bufferdConn{Conn: conn, br: br}\n\tvar handler Handler\n\tswitch b[0] {\n\tcase gosocks4.Ver4:\n\t\t// SOCKS4(a) does not suppport authentication method,\n\t\t// so we ignore it when credentials are specified for security reason.\n\t\tif len(h.options.Users) > 0 {\n\t\t\tcc.Close()\n\t\t\treturn\n\t\t}\n\t\thandler = &socks4Handler{options: h.options}\n\tcase gosocks5.Ver5: // socks5\n\t\thandler = &socks5Handler{options: h.options}\n\tdefault: // http\n\t\thandler = &httpHandler{options: h.options}\n\t}\n\thandler.Init()\n\thandler.Handle(cc)\n}\n\ntype bufferdConn struct {\n\tnet.Conn\n\tbr *bufio.Reader\n}\n\nfunc (c *bufferdConn) Read(b []byte) (int, error) {\n\treturn c.br.Read(b)\n}\n"
  },
  {
    "path": "handler_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc autoHTTPProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: TCPTransporter(),\n\t}\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: AutoHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestAutoHTTPProxy(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := autoHTTPProxyRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc autoSocks5ProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  AutoHandler(UsersHandlerOption(serverInfo...)),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestAutoSOCKS5Proxy(t *testing.T) {\n\tcert, err := GenCertificate()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tDefaultTLSConfig = &tls.Config{\n\t\tCertificates: []tls.Certificate{cert},\n\t}\n\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := autoSocks5ProxyRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc autoSOCKS4ProxyRoundtrip(targetURL string, data []byte, options ...HandlerOption) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  AutoHandler(options...),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestAutoSOCKS4Proxy(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tif err := autoSOCKS4ProxyRoundtrip(httpSrv.URL, sendData); err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n\n\tif err := autoSOCKS4ProxyRoundtrip(httpSrv.URL, sendData,\n\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))); err == nil {\n\t\tt.Errorf(\"authentication required auto handler for SOCKS4 should failed\")\n\t}\n}\n\nfunc autoSocks4aProxyRoundtrip(targetURL string, data []byte, options ...HandlerOption) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  AutoHandler(options...),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestAutoSOCKS4AProxy(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tif err := autoSocks4aProxyRoundtrip(httpSrv.URL, sendData); err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n\n\tif err := autoSocks4aProxyRoundtrip(httpSrv.URL, sendData,\n\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))); err == nil {\n\t\tt.Errorf(\"authentication required auto handler for SOCKS4A should failed\")\n\t}\n}\n\nfunc autoSSProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo *url.Userinfo) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  AutoHandler(UsersHandlerOption(serverInfo)),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestAutoSSProxy(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssTests {\n\t\terr := autoSSProxyRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "hosts.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\n// Host is a static mapping from hostname to IP.\ntype Host struct {\n\tIP       net.IP\n\tHostname string\n\tAliases  []string\n}\n\n// NewHost creates a Host.\nfunc NewHost(ip net.IP, hostname string, aliases ...string) Host {\n\treturn Host{\n\t\tIP:       ip,\n\t\tHostname: hostname,\n\t\tAliases:  aliases,\n\t}\n}\n\n// Hosts is a static table lookup for hostnames.\n// For each host a single line should be present with the following information:\n// IP_address canonical_hostname [aliases...]\n// Fields of the entry are separated by any number of blanks and/or tab characters.\n// Text from a \"#\" character until the end of the line is a comment, and is ignored.\ntype Hosts struct {\n\thosts   []Host\n\tperiod  time.Duration\n\tstopped chan struct{}\n\tmux     sync.RWMutex\n}\n\n// NewHosts creates a Hosts with optional list of hosts.\nfunc NewHosts(hosts ...Host) *Hosts {\n\treturn &Hosts{\n\t\thosts:   hosts,\n\t\tstopped: make(chan struct{}),\n\t}\n}\n\n// AddHost adds host(s) to the host table.\nfunc (h *Hosts) AddHost(host ...Host) {\n\th.mux.Lock()\n\tdefer h.mux.Unlock()\n\n\th.hosts = append(h.hosts, host...)\n}\n\n// Lookup searches the IP address corresponds to the given host from the host table.\nfunc (h *Hosts) Lookup(host string) (ip net.IP) {\n\tif h == nil || host == \"\" {\n\t\treturn\n\t}\n\n\th.mux.RLock()\n\tdefer h.mux.RUnlock()\n\n\tfor _, h := range h.hosts {\n\t\tif h.Hostname == host {\n\t\t\tip = h.IP\n\t\t\tbreak\n\t\t}\n\t\tfor _, alias := range h.Aliases {\n\t\t\tif alias == host {\n\t\t\t\tip = h.IP\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\tif ip != nil && Debug {\n\t\tlog.Logf(\"[hosts] hit: %s %s\", host, ip.String())\n\t}\n\treturn\n}\n\n// Reload parses config from r, then live reloads the hosts.\nfunc (h *Hosts) Reload(r io.Reader) error {\n\tvar period time.Duration\n\tvar hosts []Host\n\n\tif r == nil || h.Stopped() {\n\t\treturn nil\n\t}\n\n\tscanner := bufio.NewScanner(r)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tss := splitLine(line)\n\t\tif len(ss) < 2 {\n\t\t\tcontinue // invalid lines are ignored\n\t\t}\n\n\t\tswitch ss[0] {\n\t\tcase \"reload\": // reload option\n\t\t\tperiod, _ = time.ParseDuration(ss[1])\n\t\tdefault:\n\t\t\tip := net.ParseIP(ss[0])\n\t\t\tif ip == nil {\n\t\t\t\tbreak // invalid IP addresses are ignored\n\t\t\t}\n\t\t\thost := Host{\n\t\t\t\tIP:       ip,\n\t\t\t\tHostname: ss[1],\n\t\t\t}\n\t\t\tif len(ss) > 2 {\n\t\t\t\thost.Aliases = ss[2:]\n\t\t\t}\n\t\t\thosts = append(hosts, host)\n\t\t}\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\n\th.mux.Lock()\n\th.period = period\n\th.hosts = hosts\n\th.mux.Unlock()\n\n\treturn nil\n}\n\n// Period returns the reload period\nfunc (h *Hosts) Period() time.Duration {\n\tif h.Stopped() {\n\t\treturn -1\n\t}\n\n\th.mux.RLock()\n\tdefer h.mux.RUnlock()\n\n\treturn h.period\n}\n\n// Stop stops reloading.\nfunc (h *Hosts) Stop() {\n\tselect {\n\tcase <-h.stopped:\n\tdefault:\n\t\tclose(h.stopped)\n\t}\n}\n\n// Stopped checks whether the reloader is stopped.\nfunc (h *Hosts) Stopped() bool {\n\tselect {\n\tcase <-h.stopped:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "hosts_test.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar hostsLookupTests = []struct {\n\thosts []Host\n\thost  string\n\tip    net.IP\n}{\n\t{nil, \"\", nil},\n\t{nil, \"example.com\", nil},\n\t{[]Host{}, \"\", nil},\n\t{[]Host{}, \"example.com\", nil},\n\t{[]Host{NewHost(nil, \"\")}, \"\", nil},\n\t{[]Host{NewHost(nil, \"example.com\")}, \"example.com\", nil},\n\t{[]Host{NewHost(net.IPv4(192, 168, 1, 1), \"\")}, \"\", nil},\n\t{[]Host{NewHost(net.IPv4(192, 168, 1, 1), \"example.com\")}, \"example.com\", net.IPv4(192, 168, 1, 1)},\n\t{[]Host{NewHost(net.IPv4(192, 168, 1, 1), \"example.com\")}, \"example\", nil},\n\t{[]Host{NewHost(net.IPv4(192, 168, 1, 1), \"example.com\", \"example\", \"examples\")}, \"example\", net.IPv4(192, 168, 1, 1)},\n\t{[]Host{NewHost(net.IPv4(192, 168, 1, 1), \"example.com\", \"example\", \"examples\")}, \"examples\", net.IPv4(192, 168, 1, 1)},\n}\n\nfunc TestHostsLookup(t *testing.T) {\n\tfor i, tc := range hostsLookupTests {\n\t\thosts := NewHosts()\n\t\thosts.AddHost(tc.hosts...)\n\t\tip := hosts.Lookup(tc.host)\n\t\tif !ip.Equal(tc.ip) {\n\t\t\tt.Errorf(\"#%d test failed: lookup should be %s, got %s\", i, tc.ip, ip)\n\t\t}\n\t}\n}\n\nvar HostsReloadTests = []struct {\n\tr       io.Reader\n\tperiod  time.Duration\n\thost    string\n\tip      net.IP\n\tstopped bool\n}{\n\t{\n\t\tr:      nil,\n\t\tperiod: 0,\n\t\thost:   \"\",\n\t\tip:     nil,\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"\"),\n\t\tperiod: 0,\n\t\thost:   \"example.com\",\n\t\tip:     nil,\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"reload 10s\"),\n\t\tperiod: 10 * time.Second,\n\t\thost:   \"example.com\",\n\t\tip:     nil,\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"#reload 10s\\ninvalid.ip.addr example.com\"),\n\t\tperiod: 0,\n\t\tip:     nil,\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"reload 10s\\n192.168.1.1\"),\n\t\tperiod: 10 * time.Second,\n\t\thost:   \"\",\n\t\tip:     nil,\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"#reload 10s\\n192.168.1.1 example.com\"),\n\t\tperiod: 0,\n\t\thost:   \"example.com\",\n\t\tip:     net.IPv4(192, 168, 1, 1),\n\t},\n\t{\n\t\tr:       bytes.NewBufferString(\"#reload 10s\\n#192.168.1.1 example.com\"),\n\t\tperiod:  0,\n\t\thost:    \"example.com\",\n\t\tip:      nil,\n\t\tstopped: true,\n\t},\n\t{\n\t\tr:       bytes.NewBufferString(\"#reload 10s\\n192.168.1.1 example.com example examples\"),\n\t\tperiod:  0,\n\t\thost:    \"example\",\n\t\tip:      net.IPv4(192, 168, 1, 1),\n\t\tstopped: true,\n\t},\n\t{\n\t\tr:       bytes.NewBufferString(\"#reload 10s\\n192.168.1.1 example.com example examples\"),\n\t\tperiod:  0,\n\t\thost:    \"examples\",\n\t\tip:      net.IPv4(192, 168, 1, 1),\n\t\tstopped: true,\n\t},\n}\n\nfunc TestHostsReload(t *testing.T) {\n\tfor i, tc := range HostsReloadTests {\n\t\thosts := NewHosts()\n\t\tif err := hosts.Reload(tc.r); err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif hosts.Period() != tc.period {\n\t\t\tt.Errorf(\"#%d test failed: period value should be %v, got %v\",\n\t\t\t\ti, tc.period, hosts.Period())\n\t\t}\n\t\tip := hosts.Lookup(tc.host)\n\t\tif !ip.Equal(tc.ip) {\n\t\t\tt.Errorf(\"#%d test failed: lookup should be %s, got %s\", i, tc.ip, ip)\n\t\t}\n\t\tif tc.stopped {\n\t\t\thosts.Stop()\n\t\t\tif hosts.Period() >= 0 {\n\t\t\t\tt.Errorf(\"period of the stopped reloader should be minus value\")\n\t\t\t}\n\t\t}\n\t\tif hosts.Stopped() != tc.stopped {\n\t\t\tt.Errorf(\"#%d test failed: stopped value should be %v, got %v\",\n\t\t\t\ti, tc.stopped, hosts.Stopped())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "http.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\ntype httpConnector struct {\n\tUser *url.Userinfo\n}\n\n// HTTPConnector creates a Connector for HTTP proxy client.\n// It accepts an optional auth info for HTTP Basic Authentication.\nfunc HTTPConnector(user *url.Userinfo) Connector {\n\treturn &httpConnector{User: user}\n}\n\nfunc (c *httpConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *httpConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\tua := opts.UserAgent\n\tif ua == \"\" {\n\t\tua = DefaultUserAgent\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\treq := &http.Request{\n\t\tMethod:     http.MethodConnect,\n\t\tURL:        &url.URL{Host: address},\n\t\tHost:       address,\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tHeader:     make(http.Header),\n\t}\n\treq.Header.Set(\"User-Agent\", ua)\n\treq.Header.Set(\"Proxy-Connection\", \"keep-alive\")\n\n\tuser := opts.User\n\tif user == nil {\n\t\tuser = c.User\n\t}\n\n\tif user != nil {\n\t\tu := user.Username()\n\t\tp, _ := user.Password()\n\t\treq.Header.Set(\"Proxy-Authorization\",\n\t\t\t\"Basic \"+base64.StdEncoding.EncodeToString([]byte(u+\":\"+p)))\n\t}\n\n\tif err := req.Write(conn); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(req, false)\n\t\tlog.Log(string(dump))\n\t}\n\n\tresp, err := http.ReadResponse(bufio.NewReader(conn), req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\tlog.Log(string(dump))\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"%s\", resp.Status)\n\t}\n\n\treturn conn, nil\n}\n\ntype httpHandler struct {\n\toptions *HandlerOptions\n}\n\n// HTTPHandler creates a server Handler for HTTP proxy server.\nfunc HTTPHandler(opts ...HandlerOption) Handler {\n\th := &httpHandler{}\n\th.Init(opts...)\n\treturn h\n}\n\nfunc (h *httpHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *httpHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\treq, err := http.ReadRequest(bufio.NewReader(conn))\n\tif err != nil {\n\t\tlog.Logf(\"[http] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tdefer req.Body.Close()\n\n\th.handleRequest(conn, req)\n}\n\nfunc (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {\n\tif req == nil {\n\t\treturn\n\t}\n\n\t// try to get the actual host.\n\tif v := req.Header.Get(\"Gost-Target\"); v != \"\" {\n\t\tif h, err := decodeServerName(v); err == nil {\n\t\t\treq.Host = h\n\t\t}\n\t}\n\n\thost := req.Host\n\tif _, port, _ := net.SplitHostPort(host); port == \"\" {\n\t\thost = net.JoinHostPort(host, \"80\")\n\t}\n\n\tu, _, _ := basicProxyAuth(req.Header.Get(\"Proxy-Authorization\"))\n\tif u != \"\" {\n\t\tu += \"@\"\n\t}\n\tlog.Logf(\"[http] %s%s -> %s -> %s\",\n\t\tu, conn.RemoteAddr(), h.options.Node.String(), host)\n\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(req, false)\n\t\tlog.Logf(\"[http] %s -> %s\\n%s\", conn.RemoteAddr(), conn.LocalAddr(), string(dump))\n\t}\n\n\treq.Header.Del(\"Gost-Target\")\n\n\tresp := &http.Response{\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tHeader:     http.Header{},\n\t}\n\n\tproxyAgent := DefaultProxyAgent\n\tif h.options.ProxyAgent != \"\" {\n\t\tproxyAgent = h.options.ProxyAgent\n\t}\n\tresp.Header.Add(\"Proxy-Agent\", proxyAgent)\n\n\tif !Can(\"tcp\", host, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[http] %s - %s : Unauthorized to tcp connect to %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), host)\n\t\tresp.StatusCode = http.StatusForbidden\n\n\t\tif Debug {\n\t\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\t\tlog.Logf(\"[http] %s <- %s\\n%s\", conn.RemoteAddr(), conn.LocalAddr(), string(dump))\n\t\t}\n\n\t\tresp.Write(conn)\n\t\treturn\n\t}\n\n\tif h.options.Bypass.Contains(host) {\n\t\tresp.StatusCode = http.StatusForbidden\n\n\t\tlog.Logf(\"[http] %s - %s bypass %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), host)\n\t\tif Debug {\n\t\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\t\tlog.Logf(\"[http] %s <- %s\\n%s\", conn.RemoteAddr(), conn.LocalAddr(), string(dump))\n\t\t}\n\n\t\tresp.Write(conn)\n\t\treturn\n\t}\n\n\tif !h.authenticate(conn, req, resp) {\n\t\treturn\n\t}\n\n\tif req.Method == \"PRI\" || (req.Method != http.MethodConnect && req.URL.Scheme != \"http\") {\n\t\tresp.StatusCode = http.StatusBadRequest\n\n\t\tif Debug {\n\t\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\t\tlog.Logf(\"[http] %s <- %s\\n%s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), string(dump))\n\t\t}\n\n\t\tresp.Write(conn)\n\t\treturn\n\t}\n\n\treq.Header.Del(\"Proxy-Authorization\")\n\n\tretries := 1\n\tif h.options.Chain != nil && h.options.Chain.Retries > 0 {\n\t\tretries = h.options.Chain.Retries\n\t}\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tvar err error\n\tvar cc net.Conn\n\tvar route *Chain\n\tfor i := 0; i < retries; i++ {\n\t\troute, err = h.options.Chain.selectRouteFor(host)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[http] %s -> %s : %s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf := bytes.Buffer{}\n\t\tfmt.Fprintf(&buf, \"%s -> %s -> \",\n\t\t\tconn.RemoteAddr(), h.options.Node.String())\n\t\tfor _, nd := range route.route {\n\t\t\tfmt.Fprintf(&buf, \"%d@%s -> \", nd.ID, nd.String())\n\t\t}\n\t\tfmt.Fprintf(&buf, \"%s\", host)\n\t\tlog.Log(\"[route]\", buf.String())\n\n\t\t// forward http request\n\t\tlastNode := route.LastNode()\n\t\tif req.Method != http.MethodConnect &&\n\t\t\tlastNode.Protocol == \"http\" &&\n\t\t\t!h.options.HTTPTunnel {\n\t\t\terr = h.forwardRequest(conn, req, route)\n\t\t\tif err == nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Logf(\"[http] %s -> %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\tcontinue\n\t\t}\n\n\t\tcc, err = route.Dial(host,\n\t\t\tTimeoutChainOption(h.options.Timeout),\n\t\t\tHostsChainOption(h.options.Hosts),\n\t\t\tResolverChainOption(h.options.Resolver),\n\t\t)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tlog.Logf(\"[http] %s -> %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t}\n\n\tif err != nil {\n\t\tresp.StatusCode = http.StatusServiceUnavailable\n\n\t\tif Debug {\n\t\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\t\tlog.Logf(\"[http] %s <- %s\\n%s\", conn.RemoteAddr(), conn.LocalAddr(), string(dump))\n\t\t}\n\n\t\tresp.Write(conn)\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tif req.Method != http.MethodConnect {\n\t\th.handleProxy(conn, cc, req)\n\t\treturn\n\t}\n\n\tb := []byte(\"HTTP/1.1 200 Connection established\\r\\n\" +\n\t\t\"Proxy-Agent: \" + proxyAgent + \"\\r\\n\\r\\n\")\n\tif Debug {\n\t\tlog.Logf(\"[http] %s <- %s\\n%s\", conn.RemoteAddr(), conn.LocalAddr(), string(b))\n\t}\n\tconn.Write(b)\n\n\tlog.Logf(\"[http] %s <-> %s\", conn.RemoteAddr(), host)\n\ttransport(conn, cc)\n\tlog.Logf(\"[http] %s >-< %s\", conn.RemoteAddr(), host)\n}\n\nfunc (h *httpHandler) handleProxy(rw, cc io.ReadWriter, req *http.Request) (err error) {\n\treq.Header.Del(\"Proxy-Connection\")\n\n\tif err = req.Write(cc); err != nil {\n\t\treturn err\n\t}\n\n\tch := make(chan error, 1)\n\n\tgo func() {\n\t\tch <- copyBuffer(rw, cc)\n\t}()\n\n\tfor {\n\t\terr := func() error {\n\t\t\treq, err := http.ReadRequest(bufio.NewReader(rw))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif Debug {\n\t\t\t\tdump, _ := httputil.DumpRequest(req, false)\n\t\t\t\tlog.Log(string(dump))\n\t\t\t}\n\n\t\t\treq.Header.Del(\"Proxy-Connection\")\n\n\t\t\tif err = req.Write(cc); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}()\n\t\tch <- err\n\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn <-ch\n}\n\nfunc (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response) (ok bool) {\n\tu, p, _ := basicProxyAuth(req.Header.Get(\"Proxy-Authorization\"))\n\tif Debug && (u != \"\" || p != \"\") {\n\t\tlog.Logf(\"[http] %s -> %s : Authorization '%s' '%s'\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), u, p)\n\t}\n\tif h.options.Authenticator == nil || h.options.Authenticator.Authenticate(u, p) {\n\t\treturn true\n\t}\n\n\t// probing resistance is enabled, and knocking host is mismatch.\n\tif ss := strings.SplitN(h.options.ProbeResist, \":\", 2); len(ss) == 2 &&\n\t\t(h.options.KnockingHost == \"\" || !strings.EqualFold(req.URL.Hostname(), h.options.KnockingHost)) {\n\t\tresp.StatusCode = http.StatusServiceUnavailable // default status code\n\n\t\tswitch ss[0] {\n\t\tcase \"code\":\n\t\t\tresp.StatusCode, _ = strconv.Atoi(ss[1])\n\t\tcase \"web\":\n\t\t\turl := ss[1]\n\t\t\tif !strings.HasPrefix(url, \"http\") {\n\t\t\t\turl = \"http://\" + url\n\t\t\t}\n\t\t\tif r, err := http.Get(url); err == nil {\n\t\t\t\tresp = r\n\t\t\t}\n\t\tcase \"host\":\n\t\t\tcc, err := net.Dial(\"tcp\", ss[1])\n\t\t\tif err == nil {\n\t\t\t\tdefer cc.Close()\n\n\t\t\t\treq.Write(cc)\n\t\t\t\tlog.Logf(\"[http] %s <-> %s : forward to %s\",\n\t\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), ss[1])\n\t\t\t\ttransport(conn, cc)\n\t\t\t\tlog.Logf(\"[http] %s >-< %s : forward to %s\",\n\t\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), ss[1])\n\t\t\t\treturn\n\t\t\t}\n\t\tcase \"file\":\n\t\t\tf, _ := os.Open(ss[1])\n\t\t\tif f != nil {\n\t\t\t\tresp.StatusCode = http.StatusOK\n\t\t\t\tif finfo, _ := f.Stat(); finfo != nil {\n\t\t\t\t\tresp.ContentLength = finfo.Size()\n\t\t\t\t}\n\t\t\t\tresp.Header.Set(\"Content-Type\", \"text/html\")\n\t\t\t\tresp.Body = f\n\t\t\t}\n\t\t}\n\t}\n\n\tif resp.StatusCode == 0 {\n\t\tlog.Logf(\"[http] %s <- %s : proxy authentication required\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr())\n\t\tresp.StatusCode = http.StatusProxyAuthRequired\n\t\tresp.Header.Add(\"Proxy-Authenticate\", \"Basic realm=\\\"gost\\\"\")\n\t\tif strings.ToLower(req.Header.Get(\"Proxy-Connection\")) == \"keep-alive\" {\n\t\t\t// XXX libcurl will keep sending auth request in same conn\n\t\t\t// which we don't supported yet.\n\t\t\tresp.Header.Add(\"Connection\", \"close\")\n\t\t\tresp.Header.Add(\"Proxy-Connection\", \"close\")\n\t\t}\n\t} else {\n\t\tresp.Header = http.Header{}\n\t\tresp.Header.Set(\"Server\", \"nginx/1.14.1\")\n\t\tresp.Header.Set(\"Date\", time.Now().UTC().Format(http.TimeFormat))\n\t\tif resp.StatusCode == http.StatusOK {\n\t\t\tresp.Header.Set(\"Connection\", \"keep-alive\")\n\t\t}\n\t}\n\n\tif Debug {\n\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\tlog.Logf(\"[http] %s <- %s\\n%s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), string(dump))\n\t}\n\n\tresp.Write(conn)\n\treturn\n}\n\nfunc (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Chain) error {\n\tif route.IsEmpty() {\n\t\treturn nil\n\t}\n\n\thost := req.Host\n\tvar userpass string\n\n\tif user := route.LastNode().User; user != nil {\n\t\tu := user.Username()\n\t\tp, _ := user.Password()\n\t\tuserpass = base64.StdEncoding.EncodeToString([]byte(u + \":\" + p))\n\t}\n\n\tcc, err := route.Conn()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cc.Close()\n\n\terrc := make(chan error, 1)\n\tgo func() {\n\t\terrc <- copyBuffer(conn, cc)\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\tif userpass != \"\" {\n\t\t\t\treq.Header.Set(\"Proxy-Authorization\", \"Basic \"+userpass)\n\t\t\t}\n\n\t\t\tcc.SetWriteDeadline(time.Now().Add(WriteTimeout))\n\t\t\tif !req.URL.IsAbs() {\n\t\t\t\treq.URL.Scheme = \"http\" // make sure that the URL is absolute\n\t\t\t}\n\t\t\terr := req.WriteProxy(cc)\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[http] %s -> %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcc.SetWriteDeadline(time.Time{})\n\n\t\t\treq, err = http.ReadRequest(bufio.NewReader(conn))\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif Debug {\n\t\t\t\tdump, _ := httputil.DumpRequest(req, false)\n\t\t\t\tlog.Logf(\"[http] %s -> %s\\n%s\",\n\t\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), string(dump))\n\t\t\t}\n\t\t}\n\t}()\n\n\tlog.Logf(\"[http] %s <-> %s\", conn.RemoteAddr(), host)\n\t<-errc\n\tlog.Logf(\"[http] %s >-< %s\", conn.RemoteAddr(), host)\n\n\treturn nil\n}\n\nfunc basicProxyAuth(proxyAuth string) (username, password string, ok bool) {\n\tif proxyAuth == \"\" {\n\t\treturn\n\t}\n\n\tif !strings.HasPrefix(proxyAuth, \"Basic \") {\n\t\treturn\n\t}\n\tc, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, \"Basic \"))\n\tif err != nil {\n\t\treturn\n\t}\n\tcs := string(c)\n\ts := strings.IndexByte(cs, ':')\n\tif s < 0 {\n\t\treturn\n\t}\n\n\treturn cs[:s], cs[s+1:], true\n}\n"
  },
  {
    "path": "http2.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\t\"golang.org/x/net/http2\"\n)\n\ntype http2Connector struct {\n\tUser *url.Userinfo\n}\n\n// HTTP2Connector creates a Connector for HTTP2 proxy client.\n// It accepts an optional auth info for HTTP Basic Authentication.\nfunc HTTP2Connector(user *url.Userinfo) Connector {\n\treturn &http2Connector{User: user}\n}\n\nfunc (c *http2Connector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *http2Connector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\tua := opts.UserAgent\n\tif ua == \"\" {\n\t\tua = DefaultUserAgent\n\t}\n\n\tcc, ok := conn.(*http2ClientConn)\n\tif !ok {\n\t\treturn nil, errors.New(\"wrong connection type\")\n\t}\n\n\tpr, pw := io.Pipe()\n\treq := &http.Request{\n\t\tMethod:        http.MethodConnect,\n\t\tURL:           &url.URL{Scheme: \"https\", Host: cc.addr},\n\t\tHeader:        make(http.Header),\n\t\tProto:         \"HTTP/2.0\",\n\t\tProtoMajor:    2,\n\t\tProtoMinor:    0,\n\t\tBody:          pr,\n\t\tHost:          address,\n\t\tContentLength: -1,\n\t}\n\treq.Header.Set(\"User-Agent\", ua)\n\n\tuser := opts.User\n\tif user == nil {\n\t\tuser = c.User\n\t}\n\n\tif user != nil {\n\t\tu := user.Username()\n\t\tp, _ := user.Password()\n\t\treq.Header.Set(\"Proxy-Authorization\",\n\t\t\t\"Basic \"+base64.StdEncoding.EncodeToString([]byte(u+\":\"+p)))\n\t}\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(req, false)\n\t\tlog.Log(\"[http2]\", string(dump))\n\t}\n\tresp, err := cc.client.Do(req)\n\tif err != nil {\n\t\tcc.Close()\n\t\treturn nil, err\n\t}\n\tif Debug {\n\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\tlog.Log(\"[http2]\", string(dump))\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tresp.Body.Close()\n\t\treturn nil, errors.New(resp.Status)\n\t}\n\thc := &http2Conn{\n\t\tr:      resp.Body,\n\t\tw:      pw,\n\t\tclosed: make(chan struct{}),\n\t}\n\n\thc.remoteAddr, _ = net.ResolveTCPAddr(\"tcp\", address)\n\thc.localAddr, _ = net.ResolveTCPAddr(\"tcp\", cc.addr)\n\n\treturn hc, nil\n}\n\ntype http2Transporter struct {\n\tclients     map[string]*http.Client\n\tclientMutex sync.Mutex\n\ttlsConfig   *tls.Config\n}\n\n// HTTP2Transporter creates a Transporter that is used by HTTP2 h2 proxy client.\nfunc HTTP2Transporter(config *tls.Config) Transporter {\n\tif config == nil {\n\t\tconfig = &tls.Config{InsecureSkipVerify: true}\n\t}\n\treturn &http2Transporter{\n\t\tclients:   make(map[string]*http.Client),\n\t\ttlsConfig: config,\n\t}\n}\n\nfunc (tr *http2Transporter) Dial(addr string, options ...DialOption) (net.Conn, error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttr.clientMutex.Lock()\n\tdefer tr.clientMutex.Unlock()\n\n\tclient, ok := tr.clients[addr]\n\tif !ok {\n\t\t// NOTE: There is no real connection to the HTTP2 server at this moment.\n\t\t// So we try to connect to the server to check the server health.\n\t\tconn, err := opts.Chain.Dial(addr)\n\t\tif err != nil {\n\t\t\tlog.Log(\"http2 dial:\", addr, err)\n\t\t\treturn nil, err\n\t\t}\n\t\tconn.Close()\n\n\t\ttimeout := opts.Timeout\n\t\tif timeout <= 0 {\n\t\t\ttimeout = DialTimeout\n\t\t}\n\t\ttransport := http2.Transport{\n\t\t\tTLSClientConfig: tr.tlsConfig,\n\t\t\tDialTLS: func(network, adr string, cfg *tls.Config) (net.Conn, error) {\n\t\t\t\tconn, err := opts.Chain.Dial(adr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\treturn wrapTLSClient(conn, cfg, timeout)\n\t\t\t},\n\t\t}\n\t\tclient = &http.Client{\n\t\t\tTransport: &transport,\n\t\t\t// Timeout:   timeout,\n\t\t}\n\t\ttr.clients[addr] = client\n\t}\n\n\treturn &http2ClientConn{\n\t\taddr:   addr,\n\t\tclient: client,\n\t\tonClose: func() {\n\t\t\ttr.clientMutex.Lock()\n\t\t\tdefer tr.clientMutex.Unlock()\n\t\t\tdelete(tr.clients, addr)\n\t\t},\n\t}, nil\n}\n\nfunc (tr *http2Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\treturn conn, nil\n}\n\nfunc (tr *http2Transporter) Multiplex() bool {\n\treturn true\n}\n\n// TODO: clean closed clients\ntype h2Transporter struct {\n\tclients     map[string]*http.Client\n\tclientMutex sync.Mutex\n\ttlsConfig   *tls.Config\n\tpath        string\n}\n\n// H2Transporter creates a Transporter that is used by HTTP2 h2 tunnel client.\nfunc H2Transporter(config *tls.Config, path string) Transporter {\n\tif config == nil {\n\t\tconfig = &tls.Config{InsecureSkipVerify: true}\n\t}\n\treturn &h2Transporter{\n\t\tclients:   make(map[string]*http.Client),\n\t\ttlsConfig: config,\n\t\tpath:      path,\n\t}\n}\n\n// H2CTransporter creates a Transporter that is used by HTTP2 h2c tunnel client.\nfunc H2CTransporter(path string) Transporter {\n\treturn &h2Transporter{\n\t\tclients: make(map[string]*http.Client),\n\t\tpath:    path,\n\t}\n}\n\nfunc (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttr.clientMutex.Lock()\n\tclient, ok := tr.clients[addr]\n\tif !ok {\n\t\ttimeout := opts.Timeout\n\t\tif timeout <= 0 {\n\t\t\ttimeout = DialTimeout\n\t\t}\n\n\t\ttransport := http2.Transport{\n\t\t\tTLSClientConfig: tr.tlsConfig,\n\t\t\tDialTLS: func(network, adr string, cfg *tls.Config) (net.Conn, error) {\n\t\t\t\tconn, err := opts.Chain.Dial(addr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tif tr.tlsConfig == nil {\n\t\t\t\t\treturn conn, nil\n\t\t\t\t}\n\t\t\t\treturn wrapTLSClient(conn, cfg, timeout)\n\t\t\t},\n\t\t}\n\t\tclient = &http.Client{\n\t\t\tTransport: &transport,\n\t\t\t// Timeout:   timeout,\n\t\t}\n\t\ttr.clients[addr] = client\n\t}\n\ttr.clientMutex.Unlock()\n\n\tpr, pw := io.Pipe()\n\treq := &http.Request{\n\t\tMethod:        http.MethodConnect,\n\t\tURL:           &url.URL{Scheme: \"https\", Host: opts.Host},\n\t\tHeader:        make(http.Header),\n\t\tProto:         \"HTTP/2.0\",\n\t\tProtoMajor:    2,\n\t\tProtoMinor:    0,\n\t\tBody:          pr,\n\t\tHost:          opts.Host,\n\t\tContentLength: -1,\n\t}\n\tif tr.path != \"\" {\n\t\treq.Method = http.MethodGet\n\t\treq.URL.Path = tr.path\n\t}\n\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(req, false)\n\t\tlog.Log(\"[http2]\", string(dump))\n\t}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif Debug {\n\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\tlog.Log(\"[http2]\", string(dump))\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tresp.Body.Close()\n\t\treturn nil, errors.New(resp.Status)\n\t}\n\tconn := &http2Conn{\n\t\tr:      resp.Body,\n\t\tw:      pw,\n\t\tclosed: make(chan struct{}),\n\t}\n\tconn.remoteAddr, _ = net.ResolveTCPAddr(\"tcp\", addr)\n\tconn.localAddr = &net.TCPAddr{IP: net.IPv4zero, Port: 0}\n\treturn conn, nil\n}\n\nfunc (tr *h2Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\treturn conn, nil\n}\n\nfunc (tr *h2Transporter) Multiplex() bool {\n\treturn true\n}\n\ntype http2Handler struct {\n\toptions *HandlerOptions\n}\n\n// HTTP2Handler creates a server Handler for HTTP2 proxy server.\nfunc HTTP2Handler(opts ...HandlerOption) Handler {\n\th := &http2Handler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *http2Handler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *http2Handler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\th2c, ok := conn.(*http2ServerConn)\n\tif !ok {\n\t\tlog.Log(\"[http2] wrong connection type\")\n\t\treturn\n\t}\n\n\th.roundTrip(h2c.w, h2c.r)\n}\n\nfunc (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {\n\thost := r.Header.Get(\"Gost-Target\")\n\tif host == \"\" {\n\t\thost = r.Host\n\t}\n\n\tif _, port, _ := net.SplitHostPort(host); port == \"\" {\n\t\thost = net.JoinHostPort(host, \"80\")\n\t}\n\n\tladdr := h.options.Addr\n\tu, _, _ := basicProxyAuth(r.Header.Get(\"Proxy-Authorization\"))\n\tif u != \"\" {\n\t\tu += \"@\"\n\t}\n\tlog.Logf(\"[http2] %s%s -> %s -> %s\",\n\t\tu, r.RemoteAddr, h.options.Node.String(), host)\n\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(r, false)\n\t\tlog.Logf(\"[http2] %s - %s\\n%s\", r.RemoteAddr, laddr, string(dump))\n\t}\n\n\tproxyAgent := DefaultProxyAgent\n\tif h.options.ProxyAgent != \"\" {\n\t\tproxyAgent = h.options.ProxyAgent\n\t}\n\tw.Header().Set(\"Proxy-Agent\", proxyAgent)\n\n\tif !Can(\"tcp\", host, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[http2] %s - %s : Unauthorized to tcp connect to %s\",\n\t\t\tr.RemoteAddr, laddr, host)\n\t\tw.WriteHeader(http.StatusForbidden)\n\t\treturn\n\t}\n\n\tif h.options.Bypass.Contains(host) {\n\t\tlog.Logf(\"[http2] %s - %s bypass %s\",\n\t\t\tr.RemoteAddr, laddr, host)\n\t\tw.WriteHeader(http.StatusForbidden)\n\t\treturn\n\t}\n\n\tresp := &http.Response{\n\t\tProtoMajor: 2,\n\t\tProtoMinor: 0,\n\t\tHeader:     http.Header{},\n\t\tBody:       io.NopCloser(bytes.NewReader([]byte{})),\n\t}\n\n\tif !h.authenticate(w, r, resp) {\n\t\treturn\n\t}\n\n\t// delete the proxy related headers.\n\tr.Header.Del(\"Proxy-Authorization\")\n\tr.Header.Del(\"Proxy-Connection\")\n\n\tretries := 1\n\tif h.options.Chain != nil && h.options.Chain.Retries > 0 {\n\t\tretries = h.options.Chain.Retries\n\t}\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tvar err error\n\tvar cc net.Conn\n\tvar route *Chain\n\tfor i := 0; i < retries; i++ {\n\t\troute, err = h.options.Chain.selectRouteFor(host)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[http2] %s -> %s : %s\",\n\t\t\t\tr.RemoteAddr, laddr, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf := bytes.Buffer{}\n\t\tfmt.Fprintf(&buf, \"%s -> %s -> \",\n\t\t\tr.RemoteAddr, h.options.Node.String())\n\t\tfor _, nd := range route.route {\n\t\t\tfmt.Fprintf(&buf, \"%d@%s -> \", nd.ID, nd.String())\n\t\t}\n\t\tfmt.Fprintf(&buf, \"%s\", host)\n\t\tlog.Log(\"[route]\", buf.String())\n\n\t\tcc, err = route.Dial(host,\n\t\t\tTimeoutChainOption(h.options.Timeout),\n\t\t\tHostsChainOption(h.options.Hosts),\n\t\t\tResolverChainOption(h.options.Resolver),\n\t\t)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tlog.Logf(\"[http2] %s -> %s : %s\", r.RemoteAddr, laddr, err)\n\t}\n\n\tif err != nil {\n\t\tw.WriteHeader(http.StatusServiceUnavailable)\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tif r.Method == http.MethodConnect {\n\t\tw.WriteHeader(http.StatusOK)\n\t\tif fw, ok := w.(http.Flusher); ok {\n\t\t\tfw.Flush()\n\t\t}\n\n\t\t// compatible with HTTP1.x\n\t\tif hj, ok := w.(http.Hijacker); ok && r.ProtoMajor == 1 {\n\t\t\t// we take over the underly connection\n\t\t\tconn, _, err := hj.Hijack()\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[http2] %s -> %s : %s\",\n\t\t\t\t\tr.RemoteAddr, laddr, err)\n\t\t\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer conn.Close()\n\n\t\t\tlog.Logf(\"[http2] %s <-> %s : downgrade to HTTP/1.1\", r.RemoteAddr, host)\n\t\t\ttransport(conn, cc)\n\t\t\tlog.Logf(\"[http2] %s >-< %s\", r.RemoteAddr, host)\n\t\t\treturn\n\t\t}\n\n\t\tlog.Logf(\"[http2] %s <-> %s\", r.RemoteAddr, host)\n\t\ttransport(&readWriter{r: r.Body, w: flushWriter{w}}, cc)\n\t\tlog.Logf(\"[http2] %s >-< %s\", r.RemoteAddr, host)\n\t\treturn\n\t}\n\n\tlog.Logf(\"[http2] %s <-> %s\", r.RemoteAddr, host)\n\tif err := h.forwardRequest(w, r, cc); err != nil {\n\t\tlog.Logf(\"[http2] %s - %s : %s\", r.RemoteAddr, host, err)\n\t}\n\tlog.Logf(\"[http2] %s >-< %s\", r.RemoteAddr, host)\n}\n\nfunc (h *http2Handler) authenticate(w http.ResponseWriter, r *http.Request, resp *http.Response) (ok bool) {\n\tladdr := h.options.Addr\n\tu, p, _ := basicProxyAuth(r.Header.Get(\"Proxy-Authorization\"))\n\tif Debug && (u != \"\" || p != \"\") {\n\t\tlog.Logf(\"[http2] %s - %s : Authorization '%s' '%s'\", r.RemoteAddr, laddr, u, p)\n\t}\n\tif h.options.Authenticator == nil || h.options.Authenticator.Authenticate(u, p) {\n\t\treturn true\n\t}\n\n\t// probing resistance is enabled, and knocking host is mismatch.\n\tif ss := strings.SplitN(h.options.ProbeResist, \":\", 2); len(ss) == 2 &&\n\t\t(h.options.KnockingHost == \"\" || !strings.EqualFold(r.URL.Hostname(), h.options.KnockingHost)) {\n\t\tresp.StatusCode = http.StatusServiceUnavailable // default status code\n\t\tw.Header().Del(\"Proxy-Agent\")\n\n\t\tswitch ss[0] {\n\t\tcase \"code\":\n\t\t\tresp.StatusCode, _ = strconv.Atoi(ss[1])\n\t\tcase \"web\":\n\t\t\turl := ss[1]\n\t\t\tif !strings.HasPrefix(url, \"http\") {\n\t\t\t\turl = \"http://\" + url\n\t\t\t}\n\t\t\tif r, err := http.Get(url); err == nil {\n\t\t\t\tresp = r\n\t\t\t}\n\t\tcase \"host\":\n\t\t\tcc, err := net.Dial(\"tcp\", ss[1])\n\t\t\tif err == nil {\n\t\t\t\tdefer cc.Close()\n\t\t\t\tlog.Logf(\"[http2] %s <-> %s : forward to %s\", r.RemoteAddr, laddr, ss[1])\n\t\t\t\tif err := h.forwardRequest(w, r, cc); err != nil {\n\t\t\t\t\tlog.Logf(\"[http2] %s - %s : %s\", r.RemoteAddr, laddr, err)\n\t\t\t\t}\n\t\t\t\tlog.Logf(\"[http2] %s >-< %s : forward to %s\", r.RemoteAddr, laddr, ss[1])\n\t\t\t\treturn\n\t\t\t}\n\t\tcase \"file\":\n\t\t\tf, _ := os.Open(ss[1])\n\t\t\tif f != nil {\n\t\t\t\tresp.StatusCode = http.StatusOK\n\t\t\t\tif finfo, _ := f.Stat(); finfo != nil {\n\t\t\t\t\tresp.ContentLength = finfo.Size()\n\t\t\t\t}\n\t\t\t\tresp.Body = f\n\t\t\t}\n\t\t}\n\t}\n\n\tif resp.StatusCode == 0 {\n\t\tlog.Logf(\"[http2] %s <- %s : proxy authentication required\", r.RemoteAddr, laddr)\n\t\tresp.StatusCode = http.StatusProxyAuthRequired\n\t\tresp.Header.Add(\"Proxy-Authenticate\", \"Basic realm=\\\"gost\\\"\")\n\t} else {\n\t\tresp.Header = http.Header{}\n\t\tresp.Header.Set(\"Server\", \"nginx/1.14.1\")\n\t\tresp.Header.Set(\"Date\", time.Now().UTC().Format(http.TimeFormat))\n\t\tif resp.ContentLength > 0 {\n\t\t\tresp.Header.Set(\"Content-Type\", \"text/html\")\n\t\t}\n\t\tif resp.StatusCode == http.StatusOK {\n\t\t\tresp.Header.Set(\"Connection\", \"keep-alive\")\n\t\t}\n\t}\n\n\tif Debug {\n\t\tdump, _ := httputil.DumpResponse(resp, false)\n\t\tlog.Logf(\"[http2] %s <- %s\\n%s\", r.RemoteAddr, laddr, string(dump))\n\t}\n\n\th.writeResponse(w, resp)\n\tresp.Body.Close()\n\n\treturn\n}\n\nfunc (h *http2Handler) forwardRequest(w http.ResponseWriter, r *http.Request, rw io.ReadWriter) (err error) {\n\tif err = r.Write(rw); err != nil {\n\t\treturn\n\t}\n\n\tresp, err := http.ReadResponse(bufio.NewReader(rw), r)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\treturn h.writeResponse(w, resp)\n}\n\nfunc (h *http2Handler) writeResponse(w http.ResponseWriter, resp *http.Response) error {\n\tfor k, v := range resp.Header {\n\t\tfor _, vv := range v {\n\t\t\tw.Header().Add(k, vv)\n\t\t}\n\t}\n\tw.WriteHeader(resp.StatusCode)\n\t_, err := io.Copy(flushWriter{w}, resp.Body)\n\treturn err\n}\n\ntype http2Listener struct {\n\tserver   *http.Server\n\tconnChan chan *http2ServerConn\n\taddr     net.Addr\n\terrChan  chan error\n}\n\n// HTTP2Listener creates a Listener for HTTP2 proxy server.\nfunc HTTP2Listener(addr string, config *tls.Config) (Listener, error) {\n\tl := &http2Listener{\n\t\tconnChan: make(chan *http2ServerConn, 1024),\n\t\terrChan:  make(chan error, 1),\n\t}\n\tif config == nil {\n\t\tconfig = DefaultTLSConfig\n\t}\n\tserver := &http.Server{\n\t\tAddr:      addr,\n\t\tHandler:   http.HandlerFunc(l.handleFunc),\n\t\tTLSConfig: config,\n\t}\n\tif err := http2.ConfigureServer(server, nil); err != nil {\n\t\treturn nil, err\n\t}\n\tl.server = server\n\n\tln, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl.addr = ln.Addr()\n\n\tln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)\n\tgo func() {\n\t\terr := server.Serve(ln)\n\t\tif err != nil {\n\t\t\tlog.Log(\"[http2]\", err)\n\t\t}\n\t}()\n\n\treturn l, nil\n}\n\nfunc (l *http2Listener) handleFunc(w http.ResponseWriter, r *http.Request) {\n\tconn := &http2ServerConn{\n\t\tr:      r,\n\t\tw:      w,\n\t\tclosed: make(chan struct{}),\n\t}\n\tselect {\n\tcase l.connChan <- conn:\n\tdefault:\n\t\tlog.Logf(\"[http2] %s - %s: connection queue is full\", r.RemoteAddr, l.server.Addr)\n\t\treturn\n\t}\n\n\t<-conn.closed\n}\n\nfunc (l *http2Listener) Accept() (conn net.Conn, err error) {\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err = <-l.errChan:\n\t\tif err == nil {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\n\nfunc (l *http2Listener) Addr() net.Addr {\n\treturn l.addr\n}\n\nfunc (l *http2Listener) Close() (err error) {\n\tselect {\n\tcase <-l.errChan:\n\tdefault:\n\t\terr = l.server.Close()\n\t\tl.errChan <- err\n\t\tclose(l.errChan)\n\t}\n\treturn nil\n}\n\ntype h2Listener struct {\n\tnet.Listener\n\tserver    *http2.Server\n\ttlsConfig *tls.Config\n\tpath      string\n\tconnChan  chan net.Conn\n\terrChan   chan error\n}\n\n// H2Listener creates a Listener for HTTP2 h2 tunnel server.\nfunc H2Listener(addr string, config *tls.Config, path string) (Listener, error) {\n\tln, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif config == nil {\n\t\tconfig = DefaultTLSConfig\n\t}\n\n\tl := &h2Listener{\n\t\tListener: tcpKeepAliveListener{ln.(*net.TCPListener)},\n\t\tserver: &http2.Server{\n\t\t\t// MaxConcurrentStreams:         1000,\n\t\t\tPermitProhibitedCipherSuites: true,\n\t\t\tIdleTimeout:                  5 * time.Minute,\n\t\t},\n\t\ttlsConfig: config,\n\t\tpath:      path,\n\t\tconnChan:  make(chan net.Conn, 1024),\n\t\terrChan:   make(chan error, 1),\n\t}\n\tgo l.listenLoop()\n\n\treturn l, nil\n}\n\n// H2CListener creates a Listener for HTTP2 h2c tunnel server.\nfunc H2CListener(addr string, path string) (Listener, error) {\n\tln, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl := &h2Listener{\n\t\tListener: tcpKeepAliveListener{ln.(*net.TCPListener)},\n\t\tserver:   &http2.Server{\n\t\t\t// MaxConcurrentStreams:         1000,\n\t\t},\n\t\tpath:     path,\n\t\tconnChan: make(chan net.Conn, 1024),\n\t\terrChan:  make(chan error, 1),\n\t}\n\tgo l.listenLoop()\n\n\treturn l, nil\n}\n\nfunc (l *h2Listener) listenLoop() {\n\tfor {\n\t\tconn, err := l.Listener.Accept()\n\t\tif err != nil {\n\t\t\tlog.Log(\"[http2] accept:\", err)\n\t\t\tl.errChan <- err\n\t\t\tclose(l.errChan)\n\t\t\treturn\n\t\t}\n\t\tgo l.handleLoop(conn)\n\t}\n}\n\nfunc (l *h2Listener) handleLoop(conn net.Conn) {\n\tif l.tlsConfig != nil {\n\t\tconn = tls.Server(conn, l.tlsConfig)\n\t}\n\n\tif tc, ok := conn.(*tls.Conn); ok {\n\t\t// NOTE: HTTP2 server will check the TLS version,\n\t\t// so we must ensure that the TLS connection is handshake completed.\n\t\tif err := tc.Handshake(); err != nil {\n\t\t\tlog.Logf(\"[http2] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\treturn\n\t\t}\n\t}\n\n\topt := http2.ServeConnOpts{\n\t\tHandler: http.HandlerFunc(l.handleFunc),\n\t}\n\tl.server.ServeConn(conn, &opt)\n}\n\nfunc (l *h2Listener) handleFunc(w http.ResponseWriter, r *http.Request) {\n\tlog.Logf(\"[http2] %s -> %s %s %s %s\",\n\t\tr.RemoteAddr, r.Host, r.Method, r.RequestURI, r.Proto)\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(r, false)\n\t\tlog.Log(\"[http2]\", string(dump))\n\t}\n\tw.Header().Set(\"Proxy-Agent\", \"gost/\"+Version)\n\tconn, err := l.upgrade(w, r)\n\tif err != nil {\n\t\tlog.Logf(\"[http2] %s - %s %s %s %s: %s\",\n\t\t\tr.RemoteAddr, r.Host, r.Method, r.RequestURI, r.Proto, err)\n\t\treturn\n\t}\n\tselect {\n\tcase l.connChan <- conn:\n\tdefault:\n\t\tconn.Close()\n\t\tlog.Logf(\"[http2] %s - %s: connection queue is full\", conn.RemoteAddr(), conn.LocalAddr())\n\t}\n\n\t<-conn.closed // NOTE: we need to wait for streaming end, or the connection will be closed\n}\n\nfunc (l *h2Listener) upgrade(w http.ResponseWriter, r *http.Request) (*http2Conn, error) {\n\tif l.path == \"\" && r.Method != http.MethodConnect {\n\t\tw.WriteHeader(http.StatusMethodNotAllowed)\n\t\treturn nil, errors.New(\"method not allowed\")\n\t}\n\n\tif l.path != \"\" && r.RequestURI != l.path {\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\treturn nil, errors.New(\"bad request\")\n\t}\n\n\tw.WriteHeader(http.StatusOK)\n\tif fw, ok := w.(http.Flusher); ok {\n\t\tfw.Flush() // write header to client\n\t}\n\n\tremoteAddr, _ := net.ResolveTCPAddr(\"tcp\", r.RemoteAddr)\n\tif remoteAddr == nil {\n\t\tremoteAddr = &net.TCPAddr{\n\t\t\tIP:   net.IPv4zero,\n\t\t\tPort: 0,\n\t\t}\n\t}\n\tconn := &http2Conn{\n\t\tr:          r.Body,\n\t\tw:          flushWriter{w},\n\t\tlocalAddr:  l.Listener.Addr(),\n\t\tremoteAddr: remoteAddr,\n\t\tclosed:     make(chan struct{}),\n\t}\n\treturn conn, nil\n}\n\nfunc (l *h2Listener) Accept() (conn net.Conn, err error) {\n\tvar ok bool\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err, ok = <-l.errChan:\n\t\tif !ok {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\n\n// HTTP2 connection, wrapped up just like a net.Conn\ntype http2Conn struct {\n\tr          io.Reader\n\tw          io.Writer\n\tremoteAddr net.Addr\n\tlocalAddr  net.Addr\n\tclosed     chan struct{}\n}\n\nfunc (c *http2Conn) Read(b []byte) (n int, err error) {\n\treturn c.r.Read(b)\n}\n\nfunc (c *http2Conn) Write(b []byte) (n int, err error) {\n\treturn c.w.Write(b)\n}\n\nfunc (c *http2Conn) Close() (err error) {\n\tselect {\n\tcase <-c.closed:\n\t\treturn\n\tdefault:\n\t\tclose(c.closed)\n\t}\n\tif rc, ok := c.r.(io.Closer); ok {\n\t\terr = rc.Close()\n\t}\n\tif w, ok := c.w.(io.Closer); ok {\n\t\terr = w.Close()\n\t}\n\treturn\n}\n\nfunc (c *http2Conn) LocalAddr() net.Addr {\n\treturn c.localAddr\n}\n\nfunc (c *http2Conn) RemoteAddr() net.Addr {\n\treturn c.remoteAddr\n}\n\nfunc (c *http2Conn) SetDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"http2\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *http2Conn) SetReadDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"http2\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *http2Conn) SetWriteDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"http2\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\n// a dummy HTTP2 server conn used by HTTP2 handler\ntype http2ServerConn struct {\n\tr      *http.Request\n\tw      http.ResponseWriter\n\tclosed chan struct{}\n}\n\nfunc (c *http2ServerConn) Read(b []byte) (n int, err error) {\n\treturn 0, &net.OpError{Op: \"read\", Net: \"http2\", Source: nil, Addr: nil, Err: errors.New(\"read not supported\")}\n}\n\nfunc (c *http2ServerConn) Write(b []byte) (n int, err error) {\n\treturn 0, &net.OpError{Op: \"write\", Net: \"http2\", Source: nil, Addr: nil, Err: errors.New(\"write not supported\")}\n}\n\nfunc (c *http2ServerConn) Close() error {\n\tselect {\n\tcase <-c.closed:\n\tdefault:\n\t\tclose(c.closed)\n\t}\n\treturn nil\n}\n\nfunc (c *http2ServerConn) LocalAddr() net.Addr {\n\taddr, _ := net.ResolveTCPAddr(\"tcp\", c.r.Host)\n\treturn addr\n}\n\nfunc (c *http2ServerConn) RemoteAddr() net.Addr {\n\taddr, _ := net.ResolveTCPAddr(\"tcp\", c.r.RemoteAddr)\n\treturn addr\n}\n\nfunc (c *http2ServerConn) SetDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"http2\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *http2ServerConn) SetReadDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"http2\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *http2ServerConn) SetWriteDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"http2\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\n// a dummy HTTP2 client conn used by HTTP2 client connector\ntype http2ClientConn struct {\n\tnopConn\n\taddr    string\n\tclient  *http.Client\n\tonClose func()\n}\n\nfunc (c *http2ClientConn) Close() error {\n\tif c.onClose != nil {\n\t\tc.onClose()\n\t}\n\treturn nil\n}\n\ntype flushWriter struct {\n\tw io.Writer\n}\n\nfunc (fw flushWriter) Write(p []byte) (n int, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tif s, ok := r.(string); ok {\n\t\t\t\terr = errors.New(s)\n\t\t\t\tlog.Log(\"[http2]\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = r.(error)\n\t\t}\n\t}()\n\n\tn, err = fw.w.Write(p)\n\tif err != nil {\n\t\t// log.Log(\"flush writer:\", err)\n\t\treturn\n\t}\n\tif f, ok := fw.w.(http.Flusher); ok {\n\t\tf.Flush()\n\t}\n\treturn\n}\n"
  },
  {
    "path": "http2_test.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc http2ProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\tln, err := HTTP2Listener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTP2Connector(clientInfo),\n\t\tTransporter: HTTP2Transporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTP2Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTP2ProxyAuth(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := http2ProxyRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTP2Proxy(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := HTTP2Listener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTP2Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: HTTP2Transporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTP2Handler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTP2ProxyParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := HTTP2Listener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTP2Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: HTTP2Transporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTP2Handler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc httpOverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := H2Listener(\"\", tlsConfig, \"/h2\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: H2Transporter(nil, \"/h2\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverH2(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverH2Roundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverH2(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := H2Listener(\"\", nil, \"/h2\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: H2Transporter(nil, \"/h2\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverH2Parallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := H2Listener(\"\", nil, \"/h2\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: H2Transporter(nil, \"/h2\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := H2Listener(\"\", tlsConfig, \"/h2\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: H2Transporter(nil, \"/h2\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverH2(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverH2Roundtrip(httpSrv.URL, sendData,\n\t\t\tnil,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error {\n\tln, err := H2Listener(\"\", tlsConfig, \"/h2\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: H2Transporter(nil, \"/h2\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverH2(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverH2Roundtrip(httpSrv.URL, sendData, nil)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error {\n\tln, err := H2Listener(\"\", tlsConfig, \"/h2\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: H2Transporter(nil, \"/h2\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverH2(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverH2Roundtrip(httpSrv.URL, sendData, nil)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := H2Listener(\"\", tlsConfig, \"/h2\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: H2Transporter(nil, \"/h2\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverH2(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverH2Roundtrip(httpSrv.URL, sendData,\n\t\t\tnil,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverH2Roundtrip(targetURL string, data []byte, host string) error {\n\tln, err := H2Listener(\"\", nil, \"/h2\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: H2Transporter(nil, \"/h2\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverH2(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverH2Roundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc h2ForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := H2Listener(\"\", nil, \"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: H2Transporter(nil, \"\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestH2ForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := h2ForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc httpOverH2CRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := H2CListener(\"\", \"/h2c\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: H2CTransporter(\"/h2c\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverH2C(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverH2CRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverH2C(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := H2CListener(\"\", \"/h2c\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: H2CTransporter(\"/h2c\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverH2CParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := H2CListener(\"\", \"/h2c\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: H2CTransporter(\"/h2c\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverH2CRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := H2CListener(\"\", \"/h2c\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: H2CTransporter(\"/h2c\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverH2C(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverH2CRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverH2CRoundtrip(targetURL string, data []byte) error {\n\tln, err := H2CListener(\"\", \"/h2c\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: H2CTransporter(\"/h2c\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverH2C(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverH2CRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverH2CRoundtrip(targetURL string, data []byte) error {\n\tln, err := H2CListener(\"\", \"/h2c\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: H2CTransporter(\"/h2c\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverH2C(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverH2CRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverH2CRoundtrip(targetURL string, data []byte,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := H2CListener(\"\", \"/h2c\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: H2CTransporter(\"/h2c\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverH2C(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverH2CRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverH2CRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := H2CListener(\"\", \"/h2c\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: H2CTransporter(\"/h2c\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverH2C(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverH2CRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc h2cForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := H2CListener(\"\", \"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: H2CTransporter(\"\"),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestH2CForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := h2cForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc TestHTTP2ProxyWithCodeProbeResist(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tln, err := HTTP2Listener(\"\", nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTP2Connector(nil),\n\t\tTransporter: HTTP2Transporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTP2Handler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t\tProbeResistHandlerOption(\"code:400\"),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\terr = proxyRoundtrip(client, server, httpSrv.URL, nil)\n\tif err == nil {\n\t\tt.Error(\"should failed with status code 400\")\n\t} else if err.Error() != \"400 Bad Request\" {\n\t\tt.Error(\"should failed with status code 400, got\", err.Error())\n\t}\n}\n\nfunc TestHTTP2ProxyWithWebProbeResist(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tln, err := HTTP2Listener(\"\", nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTP2Connector(nil),\n\t\tTransporter: HTTP2Transporter(nil),\n\t}\n\n\tu, err := url.Parse(httpSrv.URL)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTP2Handler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t\tProbeResistHandlerOption(\"web:\"+u.Host),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer conn.Close()\n\n\tconn, err = client.Connect(conn, \"github.com:443\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\trecv, _ := io.ReadAll(conn)\n\tif !bytes.Equal(recv, []byte(\"Hello World!\")) {\n\t\tt.Error(\"data not equal\")\n\t}\n}\n\nfunc TestHTTP2ProxyWithHostProbeResist(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := HTTP2Listener(\"\", nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTP2Connector(nil),\n\t\tTransporter: HTTP2Transporter(nil),\n\t}\n\n\tu, err := url.Parse(httpSrv.URL)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTP2Handler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t\tProbeResistHandlerOption(\"host:\"+u.Host),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer conn.Close()\n\n\tcc, ok := conn.(*http2ClientConn)\n\tif !ok {\n\t\tt.Error(\"wrong connection type\")\n\t}\n\n\treq := &http.Request{\n\t\tMethod:        http.MethodConnect,\n\t\tURL:           &url.URL{Scheme: \"https\", Host: cc.addr},\n\t\tHeader:        make(http.Header),\n\t\tProto:         \"HTTP/2.0\",\n\t\tProtoMajor:    2,\n\t\tProtoMinor:    0,\n\t\tBody:          io.NopCloser(bytes.NewReader(sendData)),\n\t\tHost:          \"github.com:443\",\n\t\tContentLength: int64(len(sendData)),\n\t}\n\n\tresp, err := cc.client.Do(req)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tt.Error(\"got non-200 status:\", resp.Status)\n\t}\n\n\trecv, _ := io.ReadAll(resp.Body)\n\tif !bytes.Equal(sendData, recv) {\n\t\tt.Error(\"data not equal\")\n\t}\n}\n\nfunc TestHTTP2ProxyWithFileProbeResist(t *testing.T) {\n\tln, err := HTTP2Listener(\"\", nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTP2Connector(nil),\n\t\tTransporter: HTTP2Transporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTP2Handler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t\tProbeResistHandlerOption(\"file:.config/probe_resist.txt\"),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer conn.Close()\n\n\tconn, err = client.Connect(conn, \"github.com:443\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\trecv, _ := io.ReadAll(conn)\n\tif !bytes.Equal(recv, []byte(\"Hello World!\")) {\n\t\tt.Error(\"data not equal\")\n\t}\n}\n\nfunc TestHTTP2ProxyWithBypass(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tu, err := url.Parse(httpSrv.URL)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tln, err := HTTP2Listener(\"\", nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTP2Connector(nil),\n\t\tTransporter: HTTP2Transporter(nil),\n\t}\n\n\thost := u.Host\n\tif h, _, _ := net.SplitHostPort(u.Host); h != \"\" {\n\t\thost = h\n\t}\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTP2Handler(\n\t\t\tBypassHandlerOption(NewBypassPatterns(false, host)),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tif err = proxyRoundtrip(client, server, httpSrv.URL, sendData); err == nil {\n\t\tt.Error(\"should failed\")\n\t}\n}\n"
  },
  {
    "path": "http_test.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nvar httpProxyTests = []struct {\n\tcliUser  *url.Userinfo\n\tsrvUsers []*url.Userinfo\n\terrStr   string\n}{\n\t{nil, nil, \"\"},\n\t{nil, []*url.Userinfo{url.User(\"admin\")}, \"407 Proxy Authentication Required\"},\n\t{nil, []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, \"407 Proxy Authentication Required\"},\n\t{url.User(\"admin\"), []*url.Userinfo{url.User(\"test\")}, \"407 Proxy Authentication Required\"},\n\t{url.User(\"admin\"), []*url.Userinfo{url.UserPassword(\"admin\", \"123456\")}, \"407 Proxy Authentication Required\"},\n\t{url.User(\"admin\"), []*url.Userinfo{url.User(\"admin\")}, \"\"},\n\t{url.User(\"admin\"), []*url.Userinfo{url.UserPassword(\"admin\", \"\")}, \"\"},\n\t{url.UserPassword(\"admin\", \"123456\"), nil, \"\"},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.User(\"admin\")}, \"\"},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, \"407 Proxy Authentication Required\"},\n\t{url.UserPassword(\"\", \"123456\"), []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, \"\"},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"admin\", \"123456\")}, \"\"},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"user\", \"pass\"), url.UserPassword(\"admin\", \"123456\")}, \"\"},\n}\n\nfunc httpProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPProxyAuth(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := httpProxyRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\t\tif err == nil {\n\t\t\t\tif tc.errStr != \"\" {\n\t\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif tc.errStr == \"\" {\n\t\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t\t}\n\t\t\t\tif err.Error() != tc.errStr {\n\t\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHTTPProxyWithInvalidRequest(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  HTTPHandler(),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tr, err := http.NewRequest(\"GET\", \"http://\"+ln.Addr().String(), bytes.NewReader(sendData))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tresp, err := http.DefaultClient.Do(r)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusBadRequest {\n\t\tt.Error(\"got status:\", resp.Status)\n\t}\n}\n\nfunc BenchmarkHTTPProxy(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPProxyParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestHTTPProxyWithCodeProbeResist(t *testing.T) {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t\tProbeResistHandlerOption(\"code:400\"),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tresp, err := http.Get(\"http://\" + ln.Addr().String())\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 400 {\n\t\tt.Error(\"should failed with status code 400, got\", resp.Status)\n\t}\n}\n\nfunc TestHTTPProxyWithWebProbeResist(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tu, err := url.Parse(httpSrv.URL)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t\tProbeResistHandlerOption(\"web:\"+u.Host),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tr, err := http.NewRequest(\"GET\", \"http://\"+ln.Addr().String(), nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tresp, err := http.DefaultClient.Do(r)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\tt.Error(\"got status:\", resp.Status)\n\t}\n\n\trecv, _ := io.ReadAll(resp.Body)\n\tif !bytes.Equal(recv, []byte(\"Hello World!\")) {\n\t\tt.Error(\"data not equal\")\n\t}\n}\n\nfunc TestHTTPProxyWithHostProbeResist(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tu, err := url.Parse(httpSrv.URL)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t\tProbeResistHandlerOption(\"host:\"+u.Host),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tr, err := http.NewRequest(\"GET\", \"http://\"+ln.Addr().String(), bytes.NewReader(sendData))\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tresp, err := http.DefaultClient.Do(r)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\tt.Error(\"got status:\", resp.Status)\n\t}\n\n\trecv, _ := io.ReadAll(resp.Body)\n\tif !bytes.Equal(sendData, recv) {\n\t\tt.Error(\"data not equal\")\n\t}\n}\n\nfunc TestHTTPProxyWithFileProbeResist(t *testing.T) {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t\tProbeResistHandlerOption(\"file:.config/probe_resist.txt\"),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tr, err := http.NewRequest(\"GET\", \"http://\"+ln.Addr().String(), nil)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tresp, err := http.DefaultClient.Do(r)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\tt.Error(\"got status:\", resp.Status)\n\t}\n\n\trecv, _ := io.ReadAll(resp.Body)\n\tif !bytes.Equal(recv, []byte(\"Hello World!\")) {\n\t\tt.Error(\"data not equal, got:\", string(recv))\n\t}\n}\n\nfunc TestHTTPProxyWithBypass(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tu, err := url.Parse(httpSrv.URL)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(nil),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\thost := u.Host\n\tif h, _, _ := net.SplitHostPort(u.Host); h != \"\" {\n\t\thost = h\n\t}\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tBypassHandlerOption(NewBypassPatterns(false, host)),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tif err = proxyRoundtrip(client, server, httpSrv.URL, sendData); err == nil {\n\t\tt.Error(\"should failed\")\n\t}\n}\n"
  },
  {
    "path": "kcp.go",
    "content": "package gost\n\nimport (\n\t\"crypto/sha1\"\n\t\"encoding/csv\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"golang.org/x/crypto/pbkdf2\"\n\n\t\"sync\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/klauspost/compress/snappy\"\n\t\"github.com/xtaci/kcp-go/v5\"\n\t\"github.com/xtaci/smux\"\n\t\"github.com/xtaci/tcpraw\"\n)\n\nvar (\n\t// KCPSalt is the default salt for KCP cipher.\n\tKCPSalt = \"kcp-go\"\n)\n\n// KCPConfig describes the config for KCP.\ntype KCPConfig struct {\n\tKey          string `json:\"key\"`\n\tCrypt        string `json:\"crypt\"`\n\tMode         string `json:\"mode\"`\n\tMTU          int    `json:\"mtu\"`\n\tSndWnd       int    `json:\"sndwnd\"`\n\tRcvWnd       int    `json:\"rcvwnd\"`\n\tDataShard    int    `json:\"datashard\"`\n\tParityShard  int    `json:\"parityshard\"`\n\tDSCP         int    `json:\"dscp\"`\n\tNoComp       bool   `json:\"nocomp\"`\n\tAckNodelay   bool   `json:\"acknodelay\"`\n\tNoDelay      int    `json:\"nodelay\"`\n\tInterval     int    `json:\"interval\"`\n\tResend       int    `json:\"resend\"`\n\tNoCongestion int    `json:\"nc\"`\n\tSockBuf      int    `json:\"sockbuf\"`\n\tSmuxBuf      int    `json:\"smuxbuf\"`\n\tStreamBuf    int    `json:\"streambuf\"`\n\tSmuxVer      int    `json:\"smuxver\"`\n\tKeepAlive    int    `json:\"keepalive\"`\n\tSnmpLog      string `json:\"snmplog\"`\n\tSnmpPeriod   int    `json:\"snmpperiod\"`\n\tSignal       bool   `json:\"signal\"` // Signal enables the signal SIGUSR1 feature.\n\tTCP          bool   `json:\"tcp\"`\n}\n\n// Init initializes the KCP config.\nfunc (c *KCPConfig) Init() {\n\tswitch c.Mode {\n\tcase \"normal\":\n\t\tc.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 40, 2, 1\n\tcase \"fast\":\n\t\tc.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 30, 2, 1\n\tcase \"fast2\":\n\t\tc.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 20, 2, 1\n\tcase \"fast3\":\n\t\tc.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 10, 2, 1\n\t}\n\tif c.SmuxVer <= 0 {\n\t\tc.SmuxVer = 1\n\t}\n\tif c.SmuxBuf <= 0 {\n\t\tc.SmuxBuf = c.SockBuf\n\t}\n\tif c.StreamBuf <= 0 {\n\t\tc.StreamBuf = c.SockBuf / 2\n\t}\n\tlog.Logf(\"%#v\", c)\n}\n\nvar (\n\t// DefaultKCPConfig is the default KCP config.\n\tDefaultKCPConfig = KCPConfig{\n\t\tKey:          \"it's a secrect\",\n\t\tCrypt:        \"aes\",\n\t\tMode:         \"fast\",\n\t\tMTU:          1350,\n\t\tSndWnd:       1024,\n\t\tRcvWnd:       1024,\n\t\tDataShard:    10,\n\t\tParityShard:  3,\n\t\tDSCP:         0,\n\t\tNoComp:       false,\n\t\tAckNodelay:   false,\n\t\tNoDelay:      0,\n\t\tInterval:     50,\n\t\tResend:       0,\n\t\tNoCongestion: 0,\n\t\tSockBuf:      4194304,\n\t\tSmuxVer:      1,\n\t\tSmuxBuf:      4194304,\n\t\tStreamBuf:    2097152,\n\t\tKeepAlive:    10,\n\t\tSnmpLog:      \"\",\n\t\tSnmpPeriod:   60,\n\t\tSignal:       false,\n\t\tTCP:          false,\n\t}\n)\n\ntype kcpTransporter struct {\n\tsessions     map[string]*muxSession\n\tsessionMutex sync.Mutex\n\tconfig       *KCPConfig\n}\n\n// KCPTransporter creates a Transporter that is used by KCP proxy client.\nfunc KCPTransporter(config *KCPConfig) Transporter {\n\tif config == nil {\n\t\tconfig = &KCPConfig{}\n\t\t*config = DefaultKCPConfig\n\t}\n\tconfig.Init()\n\n\tgo snmpLogger(config.SnmpLog, config.SnmpPeriod)\n\tif config.Signal {\n\t\tgo kcpSigHandler()\n\t}\n\n\treturn &kcpTransporter{\n\t\tconfig:   config,\n\t\tsessions: make(map[string]*muxSession),\n\t}\n}\n\nfunc (tr *kcpTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tsession, ok := tr.sessions[addr]\n\tif session != nil && session.session != nil && session.session.IsClosed() {\n\t\tsession.Close()\n\t\tdelete(tr.sessions, addr) // session is dead\n\t\tok = false\n\t}\n\tif !ok {\n\t\traddr, err := net.ResolveUDPAddr(\"udp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif tr.config.TCP {\n\t\t\tpc, err := tcpraw.Dial(\"tcp\", addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tconn = &fakeTCPConn{\n\t\t\t\traddr:      raddr,\n\t\t\t\tPacketConn: pc,\n\t\t\t}\n\t\t} else {\n\t\t\tconn, err = net.ListenUDP(\"udp\", nil)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tsession = &muxSession{conn: conn}\n\t\ttr.sessions[addr] = session\n\t}\n\treturn session.conn, nil\n}\n\nfunc (tr *kcpTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\tconfig := tr.config\n\tif opts.KCPConfig != nil {\n\t\tconfig = opts.KCPConfig\n\t}\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tsession, ok := tr.sessions[opts.Addr]\n\tif !ok || session.session == nil {\n\t\ts, err := tr.initSession(opts.Addr, conn, config)\n\t\tif err != nil {\n\t\t\tconn.Close()\n\t\t\tdelete(tr.sessions, opts.Addr)\n\t\t\treturn nil, err\n\t\t}\n\t\tsession = s\n\t\ttr.sessions[opts.Addr] = session\n\t}\n\tcc, err := session.GetConn()\n\tif err != nil {\n\t\tsession.Close()\n\t\tdelete(tr.sessions, opts.Addr)\n\t\treturn nil, err\n\t}\n\n\treturn cc, nil\n}\n\nfunc (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPConfig) (*muxSession, error) {\n\tpc, ok := conn.(net.PacketConn)\n\tif !ok {\n\t\treturn nil, errors.New(\"kcp: wrong connection type\")\n\t}\n\n\tkcpconn, err := kcp.NewConn(addr,\n\t\tblockCrypt(config.Key, config.Crypt, KCPSalt),\n\t\tconfig.DataShard, config.ParityShard, pc)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tkcpconn.SetStreamMode(true)\n\tkcpconn.SetWriteDelay(false)\n\tkcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)\n\tkcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)\n\tkcpconn.SetMtu(config.MTU)\n\tkcpconn.SetACKNoDelay(config.AckNodelay)\n\n\tif config.DSCP > 0 {\n\t\tif err := kcpconn.SetDSCP(config.DSCP); err != nil {\n\t\t\tlog.Log(\"[kcp]\", err)\n\t\t}\n\t}\n\tif err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {\n\t\tlog.Log(\"[kcp]\", err)\n\t}\n\tif err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {\n\t\tlog.Log(\"[kcp]\", err)\n\t}\n\n\t// stream multiplex\n\tsmuxConfig := smux.DefaultConfig()\n\tsmuxConfig.Version = config.SmuxVer\n\tsmuxConfig.MaxReceiveBuffer = config.SmuxBuf\n\tsmuxConfig.MaxStreamBuffer = config.StreamBuf\n\tsmuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second\n\tif err := smux.VerifyConfig(smuxConfig); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar cc net.Conn = kcpconn\n\tif !config.NoComp {\n\t\tcc = newCompStreamConn(kcpconn)\n\t}\n\tsession, err := smux.Client(cc, smuxConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &muxSession{conn: conn, session: session}, nil\n}\n\nfunc (tr *kcpTransporter) Multiplex() bool {\n\treturn true\n}\n\ntype kcpListener struct {\n\tconfig   *KCPConfig\n\tln       *kcp.Listener\n\tconnChan chan net.Conn\n\terrChan  chan error\n}\n\n// KCPListener creates a Listener for KCP proxy server.\nfunc KCPListener(addr string, config *KCPConfig) (Listener, error) {\n\tif config == nil {\n\t\tconfig = &KCPConfig{}\n\t\t*config = DefaultKCPConfig\n\t}\n\tconfig.Init()\n\n\tvar err error\n\tvar ln *kcp.Listener\n\tif config.TCP {\n\t\tvar conn net.PacketConn\n\t\tconn, err = tcpraw.Listen(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tln, err = kcp.ServeConn(\n\t\t\tblockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard, conn)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tln, err = kcp.ListenWithOptions(addr,\n\t\t\tblockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif config.DSCP > 0 {\n\t\tif err = ln.SetDSCP(config.DSCP); err != nil {\n\t\t\tlog.Log(\"[kcp]\", err)\n\t\t}\n\t}\n\tif err = ln.SetReadBuffer(config.SockBuf); err != nil {\n\t\tlog.Log(\"[kcp]\", err)\n\t}\n\tif err = ln.SetWriteBuffer(config.SockBuf); err != nil {\n\t\tlog.Log(\"[kcp]\", err)\n\t}\n\n\tgo snmpLogger(config.SnmpLog, config.SnmpPeriod)\n\tif config.Signal {\n\t\tgo kcpSigHandler()\n\t}\n\n\tl := &kcpListener{\n\t\tconfig:   config,\n\t\tln:       ln,\n\t\tconnChan: make(chan net.Conn, 1024),\n\t\terrChan:  make(chan error, 1),\n\t}\n\tgo l.listenLoop()\n\n\treturn l, nil\n}\n\nfunc (l *kcpListener) listenLoop() {\n\tfor {\n\t\tconn, err := l.ln.AcceptKCP()\n\t\tif err != nil {\n\t\t\tlog.Log(\"[kcp] accept:\", err)\n\t\t\tl.errChan <- err\n\t\t\tclose(l.errChan)\n\t\t\treturn\n\t\t}\n\t\tconn.SetStreamMode(true)\n\t\tconn.SetWriteDelay(false)\n\t\tconn.SetNoDelay(l.config.NoDelay, l.config.Interval, l.config.Resend, l.config.NoCongestion)\n\t\tconn.SetMtu(l.config.MTU)\n\t\tconn.SetWindowSize(l.config.SndWnd, l.config.RcvWnd)\n\t\tconn.SetACKNoDelay(l.config.AckNodelay)\n\t\tgo l.mux(conn)\n\t}\n}\n\nfunc (l *kcpListener) mux(conn net.Conn) {\n\tsmuxConfig := smux.DefaultConfig()\n\tsmuxConfig.Version = l.config.SmuxVer\n\tsmuxConfig.MaxReceiveBuffer = l.config.SmuxBuf\n\tsmuxConfig.MaxStreamBuffer = l.config.StreamBuf\n\tsmuxConfig.KeepAliveInterval = time.Duration(l.config.KeepAlive) * time.Second\n\n\tlog.Logf(\"[kcp] %s - %s\", conn.RemoteAddr(), l.Addr())\n\n\tif !l.config.NoComp {\n\t\tconn = newCompStreamConn(conn)\n\t}\n\n\tmux, err := smux.Server(conn, smuxConfig)\n\tif err != nil {\n\t\tlog.Log(\"[kcp]\", err)\n\t\treturn\n\t}\n\tdefer mux.Close()\n\n\tlog.Logf(\"[kcp] %s <-> %s\", conn.RemoteAddr(), l.Addr())\n\tdefer log.Logf(\"[kcp] %s >-< %s\", conn.RemoteAddr(), l.Addr())\n\n\tfor {\n\t\tstream, err := mux.AcceptStream()\n\t\tif err != nil {\n\t\t\tlog.Log(\"[kcp] accept stream:\", err)\n\t\t\treturn\n\t\t}\n\n\t\tcc := &muxStreamConn{Conn: conn, stream: stream}\n\t\tselect {\n\t\tcase l.connChan <- cc:\n\t\tdefault:\n\t\t\tcc.Close()\n\t\t\tlog.Logf(\"[kcp] %s - %s: connection queue is full\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t}\n\t}\n}\n\nfunc (l *kcpListener) Accept() (conn net.Conn, err error) {\n\tvar ok bool\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err, ok = <-l.errChan:\n\t\tif !ok {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\nfunc (l *kcpListener) Addr() net.Addr {\n\treturn l.ln.Addr()\n}\n\nfunc (l *kcpListener) Close() error {\n\treturn l.ln.Close()\n}\n\nfunc blockCrypt(key, crypt, salt string) (block kcp.BlockCrypt) {\n\tpass := pbkdf2.Key([]byte(key), []byte(salt), 4096, 32, sha1.New)\n\n\tswitch crypt {\n\tcase \"sm4\":\n\t\tblock, _ = kcp.NewSM4BlockCrypt(pass[:16])\n\tcase \"tea\":\n\t\tblock, _ = kcp.NewTEABlockCrypt(pass[:16])\n\tcase \"xor\":\n\t\tblock, _ = kcp.NewSimpleXORBlockCrypt(pass)\n\tcase \"none\":\n\t\tblock, _ = kcp.NewNoneBlockCrypt(pass)\n\tcase \"aes-128\":\n\t\tblock, _ = kcp.NewAESBlockCrypt(pass[:16])\n\tcase \"aes-192\":\n\t\tblock, _ = kcp.NewAESBlockCrypt(pass[:24])\n\tcase \"blowfish\":\n\t\tblock, _ = kcp.NewBlowfishBlockCrypt(pass)\n\tcase \"twofish\":\n\t\tblock, _ = kcp.NewTwofishBlockCrypt(pass)\n\tcase \"cast5\":\n\t\tblock, _ = kcp.NewCast5BlockCrypt(pass[:16])\n\tcase \"3des\":\n\t\tblock, _ = kcp.NewTripleDESBlockCrypt(pass[:24])\n\tcase \"xtea\":\n\t\tblock, _ = kcp.NewXTEABlockCrypt(pass[:16])\n\tcase \"salsa20\":\n\t\tblock, _ = kcp.NewSalsa20BlockCrypt(pass)\n\tcase \"aes\":\n\t\tfallthrough\n\tdefault: // aes\n\t\tblock, _ = kcp.NewAESBlockCrypt(pass)\n\t}\n\treturn\n}\n\nfunc snmpLogger(format string, interval int) {\n\tif format == \"\" || interval == 0 {\n\t\treturn\n\t}\n\tticker := time.NewTicker(time.Duration(interval) * time.Second)\n\tdefer ticker.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tf, err := os.OpenFile(time.Now().Format(format), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)\n\t\t\tif err != nil {\n\t\t\t\tlog.Log(\"[kcp]\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tw := csv.NewWriter(f)\n\t\t\t// write header in empty file\n\t\t\tif stat, err := f.Stat(); err == nil && stat.Size() == 0 {\n\t\t\t\tif err := w.Write(append([]string{\"Unix\"}, kcp.DefaultSnmp.Header()...)); err != nil {\n\t\t\t\t\tlog.Log(\"[kcp]\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err := w.Write(append([]string{fmt.Sprint(time.Now().Unix())}, kcp.DefaultSnmp.ToSlice()...)); err != nil {\n\t\t\t\tlog.Log(\"[kcp]\", err)\n\t\t\t}\n\t\t\tkcp.DefaultSnmp.Reset()\n\t\t\tw.Flush()\n\t\t\tf.Close()\n\t\t}\n\t}\n}\n\ntype compStreamConn struct {\n\tconn net.Conn\n\tw    *snappy.Writer\n\tr    *snappy.Reader\n}\n\nfunc newCompStreamConn(conn net.Conn) *compStreamConn {\n\tc := new(compStreamConn)\n\tc.conn = conn\n\tc.w = snappy.NewBufferedWriter(conn)\n\tc.r = snappy.NewReader(conn)\n\treturn c\n}\n\nfunc (c *compStreamConn) Read(b []byte) (n int, err error) {\n\treturn c.r.Read(b)\n}\n\nfunc (c *compStreamConn) Write(b []byte) (n int, err error) {\n\tif _, err = c.w.Write(b); err != nil {\n\t\treturn 0, err\n\t}\n\tif err = c.w.Flush(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(b), err\n}\n\nfunc (c *compStreamConn) Close() error {\n\treturn c.conn.Close()\n}\n\nfunc (c *compStreamConn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\nfunc (c *compStreamConn) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\nfunc (c *compStreamConn) SetDeadline(t time.Time) error {\n\treturn c.conn.SetDeadline(t)\n}\n\nfunc (c *compStreamConn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\nfunc (c *compStreamConn) SetWriteDeadline(t time.Time) error {\n\treturn c.conn.SetWriteDeadline(t)\n}\n"
  },
  {
    "path": "kcp_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc httpOverKCPRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := KCPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverKCP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverKCPRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverKCP(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := KCPListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tb.Log(ln.Addr())\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverKCPParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := KCPListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverKCPRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := KCPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverKCP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverKCPRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverKCPRoundtrip(targetURL string, data []byte) error {\n\tln, err := KCPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverKCP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverKCPRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverKCPRoundtrip(targetURL string, data []byte) error {\n\tln, err := KCPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverKCP(t *testing.T) {\n\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverKCPRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverKCPRoundtrip(targetURL string, data []byte,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := KCPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverKCP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverKCPRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverKCPRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := KCPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverKCP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverKCPRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc kcpForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := KCPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: KCPTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestKCPForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := kcpForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n"
  },
  {
    "path": "log.go",
    "content": "package gost\n\nimport (\n\t\"fmt\"\n\t\"log\"\n)\n\nfunc init() {\n\tlog.SetFlags(log.LstdFlags | log.Lshortfile)\n}\n\n// LogLogger uses the standard log package as the logger\ntype LogLogger struct {\n}\n\n// Log uses the standard log library log.Output\nfunc (l *LogLogger) Log(v ...interface{}) {\n\tlog.Output(3, fmt.Sprintln(v...))\n}\n\n// Logf uses the standard log library log.Output\nfunc (l *LogLogger) Logf(format string, v ...interface{}) {\n\tlog.Output(3, fmt.Sprintf(format, v...))\n}\n\n// NopLogger is a dummy logger that discards the log outputs\ntype NopLogger struct {\n}\n\n// Log does nothing\nfunc (l *NopLogger) Log(v ...interface{}) {\n}\n\n// Logf does nothing\nfunc (l *NopLogger) Logf(format string, v ...interface{}) {\n}\n"
  },
  {
    "path": "mux.go",
    "content": "package gost\n\nimport (\n\t\"net\"\n\n\tsmux \"github.com/xtaci/smux\"\n)\n\ntype muxStreamConn struct {\n\tnet.Conn\n\tstream *smux.Stream\n}\n\nfunc (c *muxStreamConn) Read(b []byte) (n int, err error) {\n\treturn c.stream.Read(b)\n}\n\nfunc (c *muxStreamConn) Write(b []byte) (n int, err error) {\n\treturn c.stream.Write(b)\n}\n\nfunc (c *muxStreamConn) Close() error {\n\treturn c.stream.Close()\n}\n\ntype muxSession struct {\n\tconn    net.Conn\n\tsession *smux.Session\n}\n\nfunc (session *muxSession) GetConn() (net.Conn, error) {\n\tstream, err := session.session.OpenStream()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &muxStreamConn{Conn: session.conn, stream: stream}, nil\n}\n\nfunc (session *muxSession) Accept() (net.Conn, error) {\n\tstream, err := session.session.AcceptStream()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &muxStreamConn{Conn: session.conn, stream: stream}, nil\n}\n\nfunc (session *muxSession) Close() error {\n\tif session.session == nil {\n\t\treturn nil\n\t}\n\treturn session.session.Close()\n}\n\nfunc (session *muxSession) IsClosed() bool {\n\tif session.session == nil {\n\t\treturn true\n\t}\n\treturn session.session.IsClosed()\n}\n\nfunc (session *muxSession) NumStreams() int {\n\treturn session.session.NumStreams()\n}\n"
  },
  {
    "path": "node.go",
    "content": "package gost\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar (\n\t// ErrInvalidNode is an error that implies the node is invalid.\n\tErrInvalidNode = errors.New(\"invalid node\")\n)\n\n// Node is a proxy node, mainly used to construct a proxy chain.\ntype Node struct {\n\tID               int\n\tAddr             string\n\tHost             string\n\tProtocol         string\n\tTransport        string\n\tRemote           string   // remote address, used by tcp/udp port forwarding\n\turl              *url.URL // raw url\n\tUser             *url.Userinfo\n\tValues           url.Values\n\tDialOptions      []DialOption\n\tHandshakeOptions []HandshakeOption\n\tConnectOptions   []ConnectOption\n\tClient           *Client\n\tmarker           *failMarker\n\tBypass           *Bypass\n}\n\n// ParseNode parses the node info.\n// The proxy node string pattern is [scheme://][user:pass@host]:port.\n// Scheme can be divided into two parts by character '+', such as: http+tls.\nfunc ParseNode(s string) (node Node, err error) {\n\ts = strings.TrimSpace(s)\n\tif s == \"\" {\n\t\treturn Node{}, ErrInvalidNode\n\t}\n\n\tif !strings.Contains(s, \"://\") {\n\t\ts = \"auto://\" + s\n\t}\n\tu, err := url.Parse(s)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tnode = Node{\n\t\tAddr:   u.Host,\n\t\tHost:   u.Host,\n\t\tRemote: strings.Trim(u.EscapedPath(), \"/\"),\n\t\tValues: u.Query(),\n\t\tUser:   u.User,\n\t\tmarker: &failMarker{},\n\t\turl:    u,\n\t}\n\n\tu.RawQuery = \"\"\n\tu.User = nil\n\n\tschemes := strings.Split(u.Scheme, \"+\")\n\tif len(schemes) == 1 {\n\t\tnode.Protocol = schemes[0]\n\t\tnode.Transport = schemes[0]\n\t}\n\tif len(schemes) == 2 {\n\t\tnode.Protocol = schemes[0]\n\t\tnode.Transport = schemes[1]\n\t}\n\n\tswitch node.Transport {\n\tcase \"https\":\n\t\tnode.Transport = \"tls\"\n\tcase \"tls\", \"mtls\":\n\tcase \"http2\", \"h2\", \"h2c\":\n\tcase \"ws\", \"mws\", \"wss\", \"mwss\":\n\tcase \"kcp\", \"ssh\", \"quic\":\n\tcase \"ssu\":\n\t\tnode.Transport = \"udp\"\n\tcase \"ohttp\", \"otls\", \"obfs4\": // obfs\n\tcase \"tcp\", \"udp\":\n\tcase \"rtcp\", \"rudp\": // rtcp and rudp are for remote port forwarding\n\tcase \"tun\", \"tap\": // tun/tap device\n\tcase \"ftcp\": // fake TCP\n\tcase \"dns\":\n\tcase \"redu\", \"redirectu\": // UDP tproxy\n\tcase \"vsock\":\n\tdefault:\n\t\tnode.Transport = \"tcp\"\n\t}\n\n\tswitch node.Protocol {\n\tcase \"http\", \"http2\":\n\tcase \"https\":\n\t\tnode.Protocol = \"http\"\n\tcase \"socks4\", \"socks4a\":\n\tcase \"socks\", \"socks5\":\n\t\tnode.Protocol = \"socks5\"\n\tcase \"ss\", \"ssu\":\n\tcase \"ss2\": // as of 2.10.1, ss2 is same as ss\n\t\tnode.Protocol = \"ss\"\n\tcase \"sni\":\n\tcase \"tcp\", \"udp\", \"rtcp\", \"rudp\": // port forwarding\n\tcase \"direct\", \"remote\", \"forward\": // forwarding\n\tcase \"red\", \"redirect\", \"redu\", \"redirectu\": // TCP,UDP transparent proxy\n\tcase \"tun\", \"tap\": // tun/tap device\n\tcase \"ftcp\": // fake TCP\n\tcase \"dns\", \"dot\", \"doh\":\n\tcase \"relay\":\n\tdefault:\n\t\tnode.Protocol = \"\"\n\t}\n\n\treturn\n}\n\n// MarkDead marks the node fail status.\nfunc (node *Node) MarkDead() {\n\tif node.marker == nil {\n\t\treturn\n\t}\n\tnode.marker.Mark()\n}\n\n// ResetDead resets the node fail status.\nfunc (node *Node) ResetDead() {\n\tif node.marker == nil {\n\t\treturn\n\t}\n\tnode.marker.Reset()\n}\n\n// Clone clones the node, it will prevent data race.\nfunc (node *Node) Clone() Node {\n\tnd := *node\n\tif node.marker != nil {\n\t\tnd.marker = node.marker.Clone()\n\t}\n\treturn nd\n}\n\n// Get returns node parameter specified by key.\nfunc (node *Node) Get(key string) string {\n\treturn node.Values.Get(key)\n}\n\n// GetBool converts node parameter value to bool.\nfunc (node *Node) GetBool(key string) bool {\n\tb, _ := strconv.ParseBool(node.Values.Get(key))\n\treturn b\n}\n\n// GetInt converts node parameter value to int.\nfunc (node *Node) GetInt(key string) int {\n\tn, _ := strconv.Atoi(node.Get(key))\n\treturn n\n}\n\n// GetDuration converts node parameter value to time.Duration.\nfunc (node *Node) GetDuration(key string) time.Duration {\n\td, err := time.ParseDuration(node.Get(key))\n\tif err != nil {\n\t\td = time.Duration(node.GetInt(key)) * time.Second\n\t}\n\treturn d\n}\n\nfunc (node Node) String() string {\n\tvar scheme string\n\tif node.url != nil {\n\t\tscheme = node.url.Scheme\n\t}\n\tif scheme == \"\" {\n\t\tscheme = fmt.Sprintf(\"%s+%s\", node.Protocol, node.Transport)\n\t}\n\treturn fmt.Sprintf(\"%s://%s\",\n\t\tscheme, node.Addr)\n}\n\n// NodeGroup is a group of nodes.\ntype NodeGroup struct {\n\tID              int\n\tnodes           []Node\n\tselectorOptions []SelectOption\n\tselector        NodeSelector\n\tmux             sync.RWMutex\n}\n\n// NewNodeGroup creates a node group\nfunc NewNodeGroup(nodes ...Node) *NodeGroup {\n\treturn &NodeGroup{\n\t\tnodes: nodes,\n\t}\n}\n\n// AddNode appends node or node list into group node.\nfunc (group *NodeGroup) AddNode(node ...Node) {\n\tif group == nil {\n\t\treturn\n\t}\n\tgroup.mux.Lock()\n\tdefer group.mux.Unlock()\n\n\tgroup.nodes = append(group.nodes, node...)\n}\n\n// SetNodes replaces the group nodes to the specified nodes,\n// and returns the previous nodes.\nfunc (group *NodeGroup) SetNodes(nodes ...Node) []Node {\n\tif group == nil {\n\t\treturn nil\n\t}\n\n\tgroup.mux.Lock()\n\tdefer group.mux.Unlock()\n\n\told := group.nodes\n\tgroup.nodes = nodes\n\treturn old\n}\n\n// SetSelector sets node selector with options for the group.\nfunc (group *NodeGroup) SetSelector(selector NodeSelector, opts ...SelectOption) {\n\tif group == nil {\n\t\treturn\n\t}\n\tgroup.mux.Lock()\n\tdefer group.mux.Unlock()\n\n\tgroup.selector = selector\n\tgroup.selectorOptions = opts\n}\n\n// Nodes returns the node list in the group\nfunc (group *NodeGroup) Nodes() []Node {\n\tif group == nil {\n\t\treturn nil\n\t}\n\n\tgroup.mux.RLock()\n\tdefer group.mux.RUnlock()\n\n\treturn group.nodes\n}\n\n// GetNode returns the node specified by index in the group.\nfunc (group *NodeGroup) GetNode(i int) Node {\n\tgroup.mux.RLock()\n\tdefer group.mux.RUnlock()\n\n\tif i < 0 || group == nil || len(group.nodes) <= i {\n\t\treturn Node{}\n\t}\n\treturn group.nodes[i]\n}\n\n// Next selects a node from group.\n// It also selects IP if the IP list exists.\nfunc (group *NodeGroup) Next() (node Node, err error) {\n\tif group == nil {\n\t\treturn\n\t}\n\n\tgroup.mux.RLock()\n\tdefer group.mux.RUnlock()\n\n\tselector := group.selector\n\tif selector == nil {\n\t\tselector = &defaultSelector{}\n\t}\n\n\t// select node from node group\n\tnode, err = selector.Select(group.nodes, group.selectorOptions...)\n\tif err != nil {\n\t\treturn\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "node_test.go",
    "content": "package gost\n\nimport \"testing\"\nimport \"net/url\"\n\nvar nodeTests = []struct {\n\tin       string\n\tout      Node\n\thasError bool\n}{\n\t{\"\", Node{}, true},\n\t{\"://\", Node{}, true},\n\t{\"localhost\", Node{Addr: \"localhost\", Transport: \"tcp\"}, false},\n\t{\":\", Node{Addr: \":\", Transport: \"tcp\"}, false},\n\t{\":8080\", Node{Addr: \":8080\", Transport: \"tcp\"}, false},\n\t{\"http://:8080\", Node{Addr: \":8080\", Protocol: \"http\", Transport: \"tcp\"}, false},\n\t{\"http://localhost:8080\", Node{Addr: \"localhost:8080\", Protocol: \"http\", Transport: \"tcp\"}, false},\n\t{\"http://admin:123456@:8080\", Node{Addr: \":8080\", Protocol: \"http\", Transport: \"tcp\", User: url.UserPassword(\"admin\", \"123456\")}, false},\n\t{\"http://admin@localhost:8080\", Node{Addr: \"localhost:8080\", Protocol: \"http\", Transport: \"tcp\", User: url.User(\"admin\")}, false},\n\t{\"http://:123456@localhost:8080\", Node{Addr: \"localhost:8080\", Protocol: \"http\", Transport: \"tcp\", User: url.UserPassword(\"\", \"123456\")}, false},\n\t{\"http://@localhost:8080\", Node{Addr: \"localhost:8080\", Protocol: \"http\", Transport: \"tcp\", User: url.User(\"\")}, false},\n\t{\"http://:@localhost:8080\", Node{Addr: \"localhost:8080\", Protocol: \"http\", Transport: \"tcp\", User: url.UserPassword(\"\", \"\")}, false},\n\t{\"https://:8080\", Node{Addr: \":8080\", Protocol: \"http\", Transport: \"tls\"}, false},\n\t{\"socks+tls://:8080\", Node{Addr: \":8080\", Protocol: \"socks5\", Transport: \"tls\"}, false},\n\t{\"tls://:8080\", Node{Addr: \":8080\", Transport: \"tls\"}, false},\n\t{\"tcp://:8080/:8081\", Node{Addr: \":8080\", Remote: \":8081\", Protocol: \"tcp\", Transport: \"tcp\"}, false},\n\t{\"udp://:8080/:8081\", Node{Addr: \":8080\", Remote: \":8081\", Protocol: \"udp\", Transport: \"udp\"}, false},\n\t{\"rtcp://:8080/:8081\", Node{Addr: \":8080\", Remote: \":8081\", Protocol: \"rtcp\", Transport: \"rtcp\"}, false},\n\t{\"rudp://:8080/:8081\", Node{Addr: \":8080\", Remote: \":8081\", Protocol: \"rudp\", Transport: \"rudp\"}, false},\n\t{\"redirect://:8080\", Node{Addr: \":8080\", Protocol: \"redirect\", Transport: \"tcp\"}, false},\n}\n\nfunc TestParseNode(t *testing.T) {\n\tfor _, test := range nodeTests {\n\t\tactual, err := ParseNode(test.in)\n\t\tif err != nil {\n\t\t\tif test.hasError {\n\t\t\t\t// t.Logf(\"ParseNode(%q) got expected error: %v\", test.in, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tt.Errorf(\"ParseNode(%q) got error: %v\", test.in, err)\n\t\t} else {\n\t\t\tif test.hasError {\n\t\t\t\tt.Errorf(\"ParseNode(%q) got %v, but should return error\", test.in, actual)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif actual.Addr != test.out.Addr || actual.Protocol != test.out.Protocol ||\n\t\t\t\tactual.Transport != test.out.Transport || actual.Remote != test.out.Remote {\n\t\t\t\tt.Errorf(\"ParseNode(%q) got %v, want %v\", test.in, actual, test.out)\n\t\t\t}\n\t\t\tif actual.User == nil {\n\t\t\t\tif test.out.User != nil {\n\t\t\t\t\tt.Errorf(\"ParseNode(%q) got %v, want %v\", test.in, actual, test.out)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif actual.User != nil {\n\t\t\t\tif test.out.User == nil {\n\t\t\t\t\tt.Errorf(\"ParseNode(%q) got %v, want %v\", test.in, actual, test.out)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif *actual.User != *test.out.User {\n\t\t\t\t\tt.Errorf(\"ParseNode(%q) got %v, want %v\", test.in, actual, test.out)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "obfs.go",
    "content": "// obfs4 connection wrappers\n\npackage gost\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"net/url\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\n\tpt \"git.torproject.org/pluggable-transports/goptlib.git\"\n\tdissector \"github.com/go-gost/tls-dissector\"\n\t\"gitlab.com/yawning/obfs4.git/transports/base\"\n\t\"gitlab.com/yawning/obfs4.git/transports/obfs4\"\n)\n\nconst (\n\tmaxTLSDataLen = 16384\n)\n\ntype obfsHTTPTransporter struct {\n\ttcpTransporter\n}\n\n// ObfsHTTPTransporter creates a Transporter that is used by HTTP obfuscating tunnel client.\nfunc ObfsHTTPTransporter() Transporter {\n\treturn &obfsHTTPTransporter{}\n}\n\nfunc (tr *obfsHTTPTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\treturn &obfsHTTPConn{Conn: conn, host: opts.Host}, nil\n}\n\ntype obfsHTTPListener struct {\n\tnet.Listener\n}\n\n// ObfsHTTPListener creates a Listener for HTTP obfuscating tunnel server.\nfunc ObfsHTTPListener(addr string) (Listener, error) {\n\tladdr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tln, err := net.ListenTCP(\"tcp\", laddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &obfsHTTPListener{Listener: tcpKeepAliveListener{ln}}, nil\n}\n\nfunc (l *obfsHTTPListener) Accept() (net.Conn, error) {\n\tconn, err := l.Listener.Accept()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &obfsHTTPConn{Conn: conn, isServer: true}, nil\n}\n\ntype obfsHTTPConn struct {\n\tnet.Conn\n\thost           string\n\trbuf           bytes.Buffer\n\twbuf           bytes.Buffer\n\tisServer       bool\n\theaderDrained  bool\n\thandshaked     bool\n\thandshakeMutex sync.Mutex\n}\n\nfunc (c *obfsHTTPConn) Handshake() (err error) {\n\tc.handshakeMutex.Lock()\n\tdefer c.handshakeMutex.Unlock()\n\n\tif c.handshaked {\n\t\treturn nil\n\t}\n\n\tif c.isServer {\n\t\terr = c.serverHandshake()\n\t} else {\n\t\terr = c.clientHandshake()\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\n\tc.handshaked = true\n\treturn nil\n}\n\nfunc (c *obfsHTTPConn) serverHandshake() (err error) {\n\tbr := bufio.NewReader(c.Conn)\n\tr, err := http.ReadRequest(br)\n\tif err != nil {\n\t\treturn\n\t}\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(r, false)\n\t\tlog.Logf(\"[ohttp] %s -> %s\\n%s\", c.RemoteAddr(), c.LocalAddr(), string(dump))\n\t}\n\n\tif r.ContentLength > 0 {\n\t\t_, err = io.Copy(&c.rbuf, r.Body)\n\t} else {\n\t\tvar b []byte\n\t\tb, err = br.Peek(br.Buffered())\n\t\tif len(b) > 0 {\n\t\t\t_, err = c.rbuf.Write(b)\n\t\t}\n\t}\n\tif err != nil {\n\t\tlog.Logf(\"[ohttp] %s -> %s : %v\", c.Conn.RemoteAddr(), c.Conn.LocalAddr(), err)\n\t\treturn\n\t}\n\n\tb := bytes.Buffer{}\n\n\tif r.Method != http.MethodGet || r.Header.Get(\"Upgrade\") != \"websocket\" {\n\t\tb.WriteString(\"HTTP/1.1 503 Service Unavailable\\r\\n\")\n\t\tb.WriteString(\"Content-Length: 0\\r\\n\")\n\t\tb.WriteString(\"Date: \" + time.Now().Format(time.RFC1123) + \"\\r\\n\")\n\t\tb.WriteString(\"\\r\\n\")\n\n\t\tif Debug {\n\t\t\tlog.Logf(\"[ohttp] %s <- %s\\n%s\", c.RemoteAddr(), c.LocalAddr(), b.String())\n\t\t}\n\n\t\tb.WriteTo(c.Conn)\n\t\treturn errors.New(\"bad request\")\n\t}\n\n\tb.WriteString(\"HTTP/1.1 101 Switching Protocols\\r\\n\")\n\tb.WriteString(\"Server: nginx/1.10.0\\r\\n\")\n\tb.WriteString(\"Date: \" + time.Now().Format(time.RFC1123) + \"\\r\\n\")\n\tb.WriteString(\"Connection: Upgrade\\r\\n\")\n\tb.WriteString(\"Upgrade: websocket\\r\\n\")\n\tb.WriteString(fmt.Sprintf(\"Sec-WebSocket-Accept: %s\\r\\n\", computeAcceptKey(r.Header.Get(\"Sec-WebSocket-Key\"))))\n\tb.WriteString(\"\\r\\n\")\n\n\tif Debug {\n\t\tlog.Logf(\"[ohttp] %s <- %s\\n%s\", c.RemoteAddr(), c.LocalAddr(), b.String())\n\t}\n\n\tif c.rbuf.Len() > 0 {\n\t\tc.wbuf = b // cache the response header if there are extra data in the request body.\n\t\treturn\n\t}\n\n\t_, err = b.WriteTo(c.Conn)\n\treturn\n}\n\nfunc (c *obfsHTTPConn) clientHandshake() (err error) {\n\tr := &http.Request{\n\t\tMethod:     http.MethodGet,\n\t\tProtoMajor: 1,\n\t\tProtoMinor: 1,\n\t\tURL:        &url.URL{Scheme: \"http\", Host: c.host},\n\t\tHeader:     make(http.Header),\n\t}\n\tr.Header.Set(\"User-Agent\", DefaultUserAgent)\n\tr.Header.Set(\"Connection\", \"Upgrade\")\n\tr.Header.Set(\"Upgrade\", \"websocket\")\n\tkey, _ := generateChallengeKey()\n\tr.Header.Set(\"Sec-WebSocket-Key\", key)\n\n\t// cache the request header\n\tif err = r.Write(&c.wbuf); err != nil {\n\t\treturn\n\t}\n\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(r, false)\n\t\tlog.Logf(\"[ohttp] %s -> %s\\n%s\", c.LocalAddr(), c.RemoteAddr(), string(dump))\n\t}\n\n\treturn nil\n}\n\nfunc (c *obfsHTTPConn) Read(b []byte) (n int, err error) {\n\tif err = c.Handshake(); err != nil {\n\t\treturn\n\t}\n\n\tif !c.isServer {\n\t\tif err = c.drainHeader(); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif c.rbuf.Len() > 0 {\n\t\treturn c.rbuf.Read(b)\n\t}\n\treturn c.Conn.Read(b)\n}\n\nfunc (c *obfsHTTPConn) drainHeader() (err error) {\n\tif c.headerDrained {\n\t\treturn\n\t}\n\tc.headerDrained = true\n\n\tbr := bufio.NewReader(c.Conn)\n\t// drain and discard the response header\n\tvar line string\n\tvar buf bytes.Buffer\n\tfor {\n\t\tline, err = br.ReadString('\\n')\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tbuf.WriteString(line)\n\t\tif line == \"\\r\\n\" {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif Debug {\n\t\tlog.Logf(\"[ohttp] %s <- %s\\n%s\", c.LocalAddr(), c.RemoteAddr(), buf.String())\n\t}\n\t// cache the extra data for next read.\n\tvar b []byte\n\tb, err = br.Peek(br.Buffered())\n\tif len(b) > 0 {\n\t\t_, err = c.rbuf.Write(b)\n\t}\n\treturn\n}\n\nfunc (c *obfsHTTPConn) Write(b []byte) (n int, err error) {\n\tif err = c.Handshake(); err != nil {\n\t\treturn\n\t}\n\tif c.wbuf.Len() > 0 {\n\t\tc.wbuf.Write(b) // append the data to the cached header\n\t\t_, err = c.wbuf.WriteTo(c.Conn)\n\t\tn = len(b) // exclude the header length\n\t\treturn\n\t}\n\treturn c.Conn.Write(b)\n}\n\ntype obfsTLSTransporter struct {\n\ttcpTransporter\n}\n\n// ObfsTLSTransporter creates a Transporter that is used by TLS obfuscating.\nfunc ObfsTLSTransporter() Transporter {\n\treturn &obfsTLSTransporter{}\n}\n\nfunc (tr *obfsTLSTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\treturn ClientObfsTLSConn(conn, opts.Host), nil\n}\n\ntype obfsTLSListener struct {\n\tnet.Listener\n}\n\n// ObfsTLSListener creates a Listener for TLS obfuscating server.\nfunc ObfsTLSListener(addr string) (Listener, error) {\n\tladdr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tln, err := net.ListenTCP(\"tcp\", laddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &obfsTLSListener{Listener: tcpKeepAliveListener{ln}}, nil\n}\n\nfunc (l *obfsTLSListener) Accept() (net.Conn, error) {\n\tconn, err := l.Listener.Accept()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ServerObfsTLSConn(conn, \"\"), nil\n}\n\nvar (\n\tcipherSuites = []uint16{\n\t\t0xc02c, 0xc030, 0x009f, 0xcca9, 0xcca8, 0xccaa, 0xc02b, 0xc02f,\n\t\t0x009e, 0xc024, 0xc028, 0x006b, 0xc023, 0xc027, 0x0067, 0xc00a,\n\t\t0xc014, 0x0039, 0xc009, 0xc013, 0x0033, 0x009d, 0x009c, 0x003d,\n\t\t0x003c, 0x0035, 0x002f, 0x00ff,\n\t}\n\n\tcompressionMethods = []uint8{0x00}\n\n\talgorithms = []uint16{\n\t\t0x0601, 0x0602, 0x0603, 0x0501, 0x0502, 0x0503, 0x0401, 0x0402,\n\t\t0x0403, 0x0301, 0x0302, 0x0303, 0x0201, 0x0202, 0x0203,\n\t}\n\n\ttlsRecordTypes   = []uint8{0x16, 0x14, 0x16, 0x17}\n\ttlsVersionMinors = []uint8{0x01, 0x03, 0x03, 0x03}\n\n\tErrBadType         = errors.New(\"bad type\")\n\tErrBadMajorVersion = errors.New(\"bad major version\")\n\tErrBadMinorVersion = errors.New(\"bad minor version\")\n\tErrMaxDataLen      = errors.New(\"bad tls data len\")\n)\n\nconst (\n\ttlsRecordStateType = iota\n\ttlsRecordStateVersion0\n\ttlsRecordStateVersion1\n\ttlsRecordStateLength0\n\ttlsRecordStateLength1\n\ttlsRecordStateData\n)\n\ntype obfsTLSParser struct {\n\tstep   uint8\n\tstate  uint8\n\tlength uint16\n}\n\ntype obfsTLSConn struct {\n\tnet.Conn\n\trbuf           bytes.Buffer\n\twbuf           bytes.Buffer\n\thost           string\n\tisServer       bool\n\thandshaked     chan struct{}\n\tparser         *obfsTLSParser\n\thandshakeMutex sync.Mutex\n}\n\nfunc (r *obfsTLSParser) Parse(b []byte) (int, error) {\n\ti := 0\n\tlast := 0\n\tlength := len(b)\n\n\tfor i < length {\n\t\tch := b[i]\n\t\tswitch r.state {\n\t\tcase tlsRecordStateType:\n\t\t\tif tlsRecordTypes[r.step] != ch {\n\t\t\t\treturn 0, ErrBadType\n\t\t\t}\n\t\t\tr.state = tlsRecordStateVersion0\n\t\t\ti++\n\t\tcase tlsRecordStateVersion0:\n\t\t\tif ch != 0x03 {\n\t\t\t\treturn 0, ErrBadMajorVersion\n\t\t\t}\n\t\t\tr.state = tlsRecordStateVersion1\n\t\t\ti++\n\t\tcase tlsRecordStateVersion1:\n\t\t\tif ch != tlsVersionMinors[r.step] {\n\t\t\t\treturn 0, ErrBadMinorVersion\n\t\t\t}\n\t\t\tr.state = tlsRecordStateLength0\n\t\t\ti++\n\t\tcase tlsRecordStateLength0:\n\t\t\tr.length = uint16(ch) << 8\n\t\t\tr.state = tlsRecordStateLength1\n\t\t\ti++\n\t\tcase tlsRecordStateLength1:\n\t\t\tr.length |= uint16(ch)\n\t\t\tif r.step == 0 {\n\t\t\t\tr.length = 91\n\t\t\t} else if r.step == 1 {\n\t\t\t\tr.length = 1\n\t\t\t} else if r.length > maxTLSDataLen {\n\t\t\t\treturn 0, ErrMaxDataLen\n\t\t\t}\n\t\t\tif r.length > 0 {\n\t\t\t\tr.state = tlsRecordStateData\n\t\t\t} else {\n\t\t\t\tr.state = tlsRecordStateType\n\t\t\t\tr.step++\n\t\t\t}\n\t\t\ti++\n\t\tcase tlsRecordStateData:\n\t\t\tleft := uint16(length - i)\n\t\t\tif left > r.length {\n\t\t\t\tleft = r.length\n\t\t\t}\n\t\t\tif r.step >= 2 {\n\t\t\t\tskip := i - last\n\t\t\t\tcopy(b[last:], b[i:length])\n\t\t\t\tlength -= int(skip)\n\t\t\t\tlast += int(left)\n\t\t\t\ti = last\n\t\t\t} else {\n\t\t\t\ti += int(left)\n\t\t\t}\n\t\t\tr.length -= left\n\t\t\tif r.length == 0 {\n\t\t\t\tif r.step < 3 {\n\t\t\t\t\tr.step++\n\t\t\t\t}\n\t\t\t\tr.state = tlsRecordStateType\n\t\t\t}\n\t\t}\n\t}\n\n\tif last == 0 {\n\t\treturn 0, nil\n\t} else if last < length {\n\t\tlength -= last\n\t}\n\n\treturn length, nil\n}\n\n// ClientObfsTLSConn creates a connection for obfs-tls client.\nfunc ClientObfsTLSConn(conn net.Conn, host string) net.Conn {\n\treturn &obfsTLSConn{\n\t\tConn:       conn,\n\t\thost:       host,\n\t\thandshaked: make(chan struct{}),\n\t\tparser:     &obfsTLSParser{},\n\t}\n}\n\n// ServerObfsTLSConn creates a connection for obfs-tls server.\nfunc ServerObfsTLSConn(conn net.Conn, host string) net.Conn {\n\treturn &obfsTLSConn{\n\t\tConn:       conn,\n\t\thost:       host,\n\t\tisServer:   true,\n\t\thandshaked: make(chan struct{}),\n\t}\n}\n\nfunc (c *obfsTLSConn) Handshaked() bool {\n\tselect {\n\tcase <-c.handshaked:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (c *obfsTLSConn) Handshake(payload []byte) (err error) {\n\tc.handshakeMutex.Lock()\n\tdefer c.handshakeMutex.Unlock()\n\n\tif c.Handshaked() {\n\t\treturn\n\t}\n\n\tif c.isServer {\n\t\terr = c.serverHandshake()\n\t} else {\n\t\terr = c.clientHandshake(payload)\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\n\tclose(c.handshaked)\n\treturn nil\n}\n\nfunc (c *obfsTLSConn) clientHandshake(payload []byte) error {\n\tclientMsg := &dissector.ClientHelloMsg{\n\t\tVersion:            tls.VersionTLS12,\n\t\tSessionID:          make([]byte, 32),\n\t\tCipherSuites:       cipherSuites,\n\t\tCompressionMethods: compressionMethods,\n\t\tExtensions: []dissector.Extension{\n\t\t\t&dissector.SessionTicketExtension{\n\t\t\t\tData: payload,\n\t\t\t},\n\t\t\t&dissector.ServerNameExtension{\n\t\t\t\tName: c.host,\n\t\t\t},\n\t\t\t&dissector.ECPointFormatsExtension{\n\t\t\t\tFormats: []uint8{0x01, 0x00, 0x02},\n\t\t\t},\n\t\t\t&dissector.SupportedGroupsExtension{\n\t\t\t\tGroups: []uint16{0x001d, 0x0017, 0x0019, 0x0018},\n\t\t\t},\n\t\t\t&dissector.SignatureAlgorithmsExtension{\n\t\t\t\tAlgorithms: algorithms,\n\t\t\t},\n\t\t\t&dissector.EncryptThenMacExtension{},\n\t\t\t&dissector.ExtendedMasterSecretExtension{},\n\t\t},\n\t}\n\tclientMsg.Random.Time = uint32(time.Now().Unix())\n\trand.Read(clientMsg.Random.Opaque[:])\n\trand.Read(clientMsg.SessionID)\n\tb, err := clientMsg.Encode()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trecord := &dissector.Record{\n\t\tType:    dissector.Handshake,\n\t\tVersion: tls.VersionTLS10,\n\t\tOpaque:  b,\n\t}\n\tif _, err := record.WriteTo(c.Conn); err != nil {\n\t\treturn err\n\t}\n\treturn err\n}\n\nfunc (c *obfsTLSConn) serverHandshake() error {\n\trecord := &dissector.Record{}\n\tif _, err := record.ReadFrom(c.Conn); err != nil {\n\t\tlog.Log(err)\n\t\treturn err\n\t}\n\tif record.Type != dissector.Handshake {\n\t\treturn dissector.ErrBadType\n\t}\n\n\tclientMsg := &dissector.ClientHelloMsg{}\n\tif err := clientMsg.Decode(record.Opaque); err != nil {\n\t\tlog.Log(err)\n\t\treturn err\n\t}\n\n\tfor _, ext := range clientMsg.Extensions {\n\t\tif ext.Type() == dissector.ExtSessionTicket {\n\t\t\tb, err := ext.Encode()\n\t\t\tif err != nil {\n\t\t\t\tlog.Log(err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tc.rbuf.Write(b)\n\t\t\tbreak\n\t\t}\n\t}\n\n\tserverMsg := &dissector.ServerHelloMsg{\n\t\tVersion:           tls.VersionTLS12,\n\t\tSessionID:         clientMsg.SessionID,\n\t\tCipherSuite:       0xcca8,\n\t\tCompressionMethod: 0x00,\n\t\tExtensions: []dissector.Extension{\n\t\t\t&dissector.RenegotiationInfoExtension{},\n\t\t\t&dissector.ExtendedMasterSecretExtension{},\n\t\t\t&dissector.ECPointFormatsExtension{\n\t\t\t\tFormats: []uint8{0x00},\n\t\t\t},\n\t\t},\n\t}\n\n\tserverMsg.Random.Time = uint32(time.Now().Unix())\n\trand.Read(serverMsg.Random.Opaque[:])\n\tb, err := serverMsg.Encode()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trecord = &dissector.Record{\n\t\tType:    dissector.Handshake,\n\t\tVersion: tls.VersionTLS10,\n\t\tOpaque:  b,\n\t}\n\n\tif _, err := record.WriteTo(&c.wbuf); err != nil {\n\t\treturn err\n\t}\n\n\trecord = &dissector.Record{\n\t\tType:    dissector.ChangeCipherSpec,\n\t\tVersion: tls.VersionTLS12,\n\t\tOpaque:  []byte{0x01},\n\t}\n\tif _, err := record.WriteTo(&c.wbuf); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *obfsTLSConn) Read(b []byte) (n int, err error) {\n\tif c.isServer { // NOTE: only Write performs the handshake operation on client side.\n\t\tif err = c.Handshake(nil); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tselect {\n\tcase <-c.handshaked:\n\t}\n\n\tif c.isServer {\n\t\tif c.rbuf.Len() > 0 {\n\t\t\treturn c.rbuf.Read(b)\n\t\t}\n\t\trecord := &dissector.Record{}\n\t\tif _, err = record.ReadFrom(c.Conn); err != nil {\n\t\t\treturn\n\t\t}\n\t\tn = copy(b, record.Opaque)\n\t\t_, err = c.rbuf.Write(record.Opaque[n:])\n\t} else {\n\t\tn, err = c.Conn.Read(b)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif n > 0 {\n\t\t\tn, err = c.parser.Parse(b[:n])\n\t\t}\n\t}\n\treturn\n}\n\nfunc (c *obfsTLSConn) Write(b []byte) (n int, err error) {\n\tn = len(b)\n\tif !c.Handshaked() {\n\t\tif err = c.Handshake(b); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif !c.isServer { // the data b has been sended during handshake phase.\n\t\t\treturn\n\t\t}\n\t}\n\n\tfor len(b) > 0 {\n\t\tdata := b\n\t\tif len(b) > maxTLSDataLen {\n\t\t\tdata = b[:maxTLSDataLen]\n\t\t\tb = b[maxTLSDataLen:]\n\t\t} else {\n\t\t\tb = b[:0]\n\t\t}\n\t\trecord := &dissector.Record{\n\t\t\tType:    dissector.AppData,\n\t\t\tVersion: tls.VersionTLS12,\n\t\t\tOpaque:  data,\n\t\t}\n\n\t\tif c.wbuf.Len() > 0 {\n\t\t\trecord.Type = dissector.Handshake\n\t\t\trecord.WriteTo(&c.wbuf)\n\t\t\t_, err = c.wbuf.WriteTo(c.Conn)\n\t\t\treturn\n\t\t}\n\n\t\tif _, err = record.WriteTo(c.Conn); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\ntype obfs4Context struct {\n\tcf    base.ClientFactory\n\tcargs interface{} // type obfs4ClientArgs\n\tsf    base.ServerFactory\n\tsargs *pt.Args\n}\n\nvar obfs4Map = make(map[string]obfs4Context)\n\n// Obfs4Init initializes the obfs client or server based on isServeNode\nfunc Obfs4Init(node Node, isServeNode bool) error {\n\tif _, ok := obfs4Map[node.Addr]; ok {\n\t\treturn fmt.Errorf(\"obfs4 context already inited\")\n\t}\n\n\tt := new(obfs4.Transport)\n\n\tstateDir := node.Values.Get(\"state-dir\")\n\tif stateDir == \"\" {\n\t\tstateDir = \".\"\n\t}\n\n\tptArgs := pt.Args(node.Values)\n\n\tif !isServeNode {\n\t\tcf, err := t.ClientFactory(stateDir)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tcargs, err := cf.ParseArgs(&ptArgs)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tobfs4Map[node.Addr] = obfs4Context{cf: cf, cargs: cargs}\n\t} else {\n\t\tsf, err := t.ServerFactory(stateDir, &ptArgs)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tsargs := sf.Args()\n\n\t\tobfs4Map[node.Addr] = obfs4Context{sf: sf, sargs: sargs}\n\n\t\tlog.Log(\"[obfs4] server inited:\", obfs4ServerURL(node))\n\t}\n\n\treturn nil\n}\n\nfunc obfs4GetContext(addr string) (obfs4Context, error) {\n\tctx, ok := obfs4Map[addr]\n\tif !ok {\n\t\treturn obfs4Context{}, fmt.Errorf(\"obfs4 context not inited\")\n\t}\n\treturn ctx, nil\n}\n\nfunc obfs4ServerURL(node Node) string {\n\tctx, err := obfs4GetContext(node.Addr)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\tvalues := (*url.Values)(ctx.sargs)\n\tquery := values.Encode()\n\treturn fmt.Sprintf(\n\t\t\"%s+%s://%s/?%s\", //obfs4-cert=%s&iat-mode=%s\",\n\t\tnode.Protocol,\n\t\tnode.Transport,\n\t\tnode.Addr,\n\t\tquery,\n\t)\n}\n\nfunc obfs4ClientConn(addr string, conn net.Conn) (net.Conn, error) {\n\tctx, err := obfs4GetContext(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpseudoDial := func(a, b string) (net.Conn, error) { return conn, nil }\n\treturn ctx.cf.Dial(\"tcp\", \"\", pseudoDial, ctx.cargs)\n}\n\nfunc obfs4ServerConn(addr string, conn net.Conn) (net.Conn, error) {\n\tctx, err := obfs4GetContext(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ctx.sf.WrapConn(conn)\n}\n\ntype obfs4Transporter struct {\n\ttcpTransporter\n}\n\n// Obfs4Transporter creates a Transporter that is used by obfs4 client.\nfunc Obfs4Transporter() Transporter {\n\treturn &obfs4Transporter{}\n}\n\nfunc (tr *obfs4Transporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\treturn obfs4ClientConn(opts.Addr, conn)\n}\n\ntype obfs4Listener struct {\n\taddr string\n\tnet.Listener\n}\n\n// Obfs4Listener creates a Listener for obfs4 server.\nfunc Obfs4Listener(addr string) (Listener, error) {\n\tln, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl := &obfs4Listener{\n\t\taddr:     addr,\n\t\tListener: tcpKeepAliveListener{ln.(*net.TCPListener)},\n\t}\n\treturn l, nil\n}\n\n// TempError satisfies the net.Error interface and presents itself\n// as temporary to make sure that it gets retried by the Accept loop\n// in server.go.\ntype TempError struct {\n\terror\n}\n\nfunc (e TempError) Timeout() bool   { return false }\nfunc (e TempError) Temporary() bool { return true }\n\nfunc (l *obfs4Listener) Accept() (net.Conn, error) {\n\tconn, err := l.Listener.Accept()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcc, err := obfs4ServerConn(l.addr, conn)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, TempError{err}\n\t}\n\treturn cc, nil\n}\n"
  },
  {
    "path": "obfs_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc httpOverObfsHTTPRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := ObfsHTTPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: ObfsHTTPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverObfsHTTP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := httpOverObfsHTTPRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\t\tif err == nil {\n\t\t\t\tif tc.errStr != \"\" {\n\t\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif tc.errStr == \"\" {\n\t\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t\t}\n\t\t\t\tif err.Error() != tc.errStr {\n\t\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkHTTPOverObfsHTTP(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := ObfsHTTPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\t// b.Log(ln.Addr())\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: ObfsHTTPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverObfsHTTPParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := ObfsHTTPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: ObfsHTTPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverObfsHTTPRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := ObfsHTTPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: ObfsHTTPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverObfsHTTP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverObfsHTTPRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverObfsHTTPRoundtrip(targetURL string, data []byte) error {\n\tln, err := ObfsHTTPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: ObfsHTTPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverObfsHTTP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverObfsHTTPRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverObfsHTTPRoundtrip(targetURL string, data []byte) error {\n\tln, err := ObfsHTTPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: ObfsHTTPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverObfsHTTP(t *testing.T) {\n\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverObfsHTTPRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverObfsHTTPRoundtrip(targetURL string, data []byte,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := ObfsHTTPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: ObfsHTTPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverObfsHTTP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverObfsHTTPRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverObfsHTTPRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := ObfsHTTPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: ObfsHTTPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverObfsHTTP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverObfsHTTPRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc httpOverObfs4Roundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := Obfs4Listener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: Obfs4Transporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc _TestHTTPOverObfs4(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := httpOverObfs4Roundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\t\tif err == nil {\n\t\t\t\tif tc.errStr != \"\" {\n\t\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif tc.errStr == \"\" {\n\t\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t\t}\n\t\t\t\tif err.Error() != tc.errStr {\n\t\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "permissions.go",
    "content": "package gost\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\n\tglob \"github.com/ryanuber/go-glob\"\n)\n\n// Permission is a rule for blacklist and whitelist.\ntype Permission struct {\n\tActions StringSet\n\tHosts   StringSet\n\tPorts   PortSet\n}\n\n// PortRange specifies the range of port, such as 1000-2000.\ntype PortRange struct {\n\tMin, Max int\n}\n\n// ParsePortRange parses the s to a PortRange.\n// The s may be a '*' means 0-65535.\nfunc ParsePortRange(s string) (*PortRange, error) {\n\tif s == \"*\" {\n\t\treturn &PortRange{Min: 0, Max: 65535}, nil\n\t}\n\n\tminmax := strings.Split(s, \"-\")\n\tswitch len(minmax) {\n\tcase 1:\n\t\tport, err := strconv.Atoi(s)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif port < 0 || port > 65535 {\n\t\t\treturn nil, fmt.Errorf(\"invalid port: %s\", s)\n\t\t}\n\t\treturn &PortRange{Min: port, Max: port}, nil\n\tcase 2:\n\t\tmin, err := strconv.Atoi(minmax[0])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tmax, err := strconv.Atoi(minmax[1])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\trealmin := maxint(0, minint(min, max))\n\t\trealmax := minint(65535, maxint(min, max))\n\n\t\treturn &PortRange{Min: realmin, Max: realmax}, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"invalid range: %s\", s)\n\t}\n}\n\n// Contains checks whether the value is within this range.\nfunc (ir *PortRange) Contains(value int) bool {\n\treturn value >= ir.Min && value <= ir.Max\n}\n\n// PortSet is a set of PortRange\ntype PortSet []PortRange\n\n// ParsePortSet parses the s to a PortSet.\n// The s shoud be a comma separated string.\nfunc ParsePortSet(s string) (*PortSet, error) {\n\tps := &PortSet{}\n\n\tif s == \"\" {\n\t\treturn nil, errors.New(\"must specify at least one port\")\n\t}\n\n\tranges := strings.Split(s, \",\")\n\n\tfor _, r := range ranges {\n\t\tportRange, err := ParsePortRange(r)\n\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t*ps = append(*ps, *portRange)\n\t}\n\n\treturn ps, nil\n}\n\n// Contains checks whether the value is within this port set.\nfunc (ps *PortSet) Contains(value int) bool {\n\tfor _, portRange := range *ps {\n\t\tif portRange.Contains(value) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// StringSet is a set of string.\ntype StringSet []string\n\n// ParseStringSet parses the s to a StringSet.\n// The s shoud be a comma separated string.\nfunc ParseStringSet(s string) (*StringSet, error) {\n\tss := &StringSet{}\n\tif s == \"\" {\n\t\treturn nil, errors.New(\"cannot be empty\")\n\t}\n\n\t*ss = strings.Split(s, \",\")\n\n\treturn ss, nil\n}\n\n// Contains checks whether the string subj within this StringSet.\nfunc (ss *StringSet) Contains(subj string) bool {\n\tfor _, s := range *ss {\n\t\tif glob.Glob(s, subj) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Permissions is a set of Permission.\ntype Permissions []Permission\n\n// ParsePermissions parses the s to a Permissions.\nfunc ParsePermissions(s string) (*Permissions, error) {\n\tps := &Permissions{}\n\n\tif s == \"\" {\n\t\treturn &Permissions{}, nil\n\t}\n\n\tperms := strings.Split(s, \" \")\n\n\tfor _, perm := range perms {\n\t\tparts := strings.Split(perm, \":\")\n\n\t\tswitch len(parts) {\n\t\tcase 3:\n\t\t\tactions, err := ParseStringSet(parts[0])\n\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"action list must look like connect,bind given: %s\", parts[0])\n\t\t\t}\n\n\t\t\thosts, err := ParseStringSet(parts[1])\n\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"hosts list must look like google.pl,*.google.com given: %s\", parts[1])\n\t\t\t}\n\n\t\t\tports, err := ParsePortSet(parts[2])\n\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"ports list must look like 80,8000-9000, given: %s\", parts[2])\n\t\t\t}\n\n\t\t\tpermission := Permission{Actions: *actions, Hosts: *hosts, Ports: *ports}\n\n\t\t\t*ps = append(*ps, permission)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"permission must have format [actions]:[hosts]:[ports] given: %s\", perm)\n\t\t}\n\t}\n\n\treturn ps, nil\n}\n\n// Can tests whether the given action and host:port is allowed by this Permissions.\nfunc (ps *Permissions) Can(action string, host string, port int) bool {\n\tfor _, p := range *ps {\n\t\tif p.Actions.Contains(action) && p.Hosts.Contains(host) && p.Ports.Contains(port) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc minint(x, y int) int {\n\tif x < y {\n\t\treturn x\n\t}\n\treturn y\n}\n\nfunc maxint(x, y int) int {\n\tif x > y {\n\t\treturn x\n\t}\n\treturn y\n}\n\n// Can tests whether the given action and address is allowed by the whitelist and blacklist.\nfunc Can(action string, addr string, whitelist, blacklist *Permissions) bool {\n\tif !strings.Contains(addr, \":\") {\n\t\taddr = addr + \":80\"\n\t}\n\thost, strport, err := net.SplitHostPort(addr)\n\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tport, err := strconv.Atoi(strport)\n\n\tif err != nil {\n\t\treturn false\n\t}\n\n\treturn (whitelist == nil || whitelist.Can(action, host, port)) &&\n\t\t(blacklist == nil || !blacklist.Can(action, host, port))\n}\n"
  },
  {
    "path": "permissions_test.go",
    "content": "package gost\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nvar portRangeTests = []struct {\n\tin  string\n\tout *PortRange\n}{\n\t{\"1\", &PortRange{Min: 1, Max: 1}},\n\t{\"1-3\", &PortRange{Min: 1, Max: 3}},\n\t{\"3-1\", &PortRange{Min: 1, Max: 3}},\n\t{\"0-100000\", &PortRange{Min: 0, Max: 65535}},\n\t{\"*\", &PortRange{Min: 0, Max: 65535}},\n}\n\nvar stringSetTests = []struct {\n\tin  string\n\tout *StringSet\n}{\n\t{\"*\", &StringSet{\"*\"}},\n\t{\"google.pl,google.com\", &StringSet{\"google.pl\", \"google.com\"}},\n}\n\nvar portSetTests = []struct {\n\tin  string\n\tout *PortSet\n}{\n\t{\"1,3\", &PortSet{PortRange{Min: 1, Max: 1}, PortRange{Min: 3, Max: 3}}},\n\t{\"1-3,7-5\", &PortSet{PortRange{Min: 1, Max: 3}, PortRange{Min: 5, Max: 7}}},\n\t{\"0-100000\", &PortSet{PortRange{Min: 0, Max: 65535}}},\n\t{\"*\", &PortSet{PortRange{Min: 0, Max: 65535}}},\n}\n\nvar permissionsTests = []struct {\n\tin  string\n\tout *Permissions\n}{\n\t{\"\", &Permissions{}},\n\t{\"*:*:*\", &Permissions{\n\t\tPermission{\n\t\t\tActions: StringSet{\"*\"},\n\t\t\tHosts:   StringSet{\"*\"},\n\t\t\tPorts:   PortSet{PortRange{Min: 0, Max: 65535}},\n\t\t},\n\t}},\n\t{\"bind:127.0.0.1,localhost:80,443,8000-8100 connect:*.google.pl:80,443\", &Permissions{\n\t\tPermission{\n\t\t\tActions: StringSet{\"bind\"},\n\t\t\tHosts:   StringSet{\"127.0.0.1\", \"localhost\"},\n\t\t\tPorts: PortSet{\n\t\t\t\tPortRange{Min: 80, Max: 80},\n\t\t\t\tPortRange{Min: 443, Max: 443},\n\t\t\t\tPortRange{Min: 8000, Max: 8100},\n\t\t\t},\n\t\t},\n\t\tPermission{\n\t\t\tActions: StringSet{\"connect\"},\n\t\t\tHosts:   StringSet{\"*.google.pl\"},\n\t\t\tPorts: PortSet{\n\t\t\t\tPortRange{Min: 80, Max: 80},\n\t\t\t\tPortRange{Min: 443, Max: 443},\n\t\t\t},\n\t\t},\n\t}},\n}\n\nfunc TestPortRangeParse(t *testing.T) {\n\tfor _, test := range portRangeTests {\n\t\tactual, err := ParsePortRange(test.in)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ParsePortRange(%q) returned error: %v\", test.in, err)\n\t\t} else if *actual != *test.out {\n\t\t\tt.Errorf(\"ParsePortRange(%q): got %v, want %v\", test.in, actual, test.out)\n\t\t}\n\t}\n}\n\nfunc TestPortRangeContains(t *testing.T) {\n\tactual, _ := ParsePortRange(\"5-10\")\n\n\tif !actual.Contains(5) || !actual.Contains(7) || !actual.Contains(10) {\n\t\tt.Errorf(\"5-10 should contain 5, 7 and 10\")\n\t}\n\n\tif actual.Contains(4) || actual.Contains(11) {\n\t\tt.Errorf(\"5-10 should not contain 4, 11\")\n\t}\n}\n\nfunc TestStringSetParse(t *testing.T) {\n\tfor _, test := range stringSetTests {\n\t\tactual, err := ParseStringSet(test.in)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ParseStringSet(%q) returned error: %v\", test.in, err)\n\t\t} else if fmt.Sprintln(actual) != fmt.Sprintln(test.out) {\n\t\t\tt.Errorf(\"ParseStringSet(%q): got %v, want %v\", test.in, actual, test.out)\n\t\t}\n\t}\n}\n\nfunc TestStringSetContains(t *testing.T) {\n\tss, _ := ParseStringSet(\"google.pl,*.google.com\")\n\n\tif !ss.Contains(\"google.pl\") || !ss.Contains(\"www.google.com\") {\n\t\tt.Errorf(\"google.pl,*.google.com should contain google.pl and www.google.com\")\n\t}\n\n\tif ss.Contains(\"www.google.pl\") || ss.Contains(\"foobar.com\") {\n\t\tt.Errorf(\"google.pl,*.google.com shound not contain www.google.pl and foobar.com\")\n\t}\n}\n\nfunc TestPortSetParse(t *testing.T) {\n\tfor _, test := range portSetTests {\n\t\tactual, err := ParsePortSet(test.in)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ParsePortRange(%q) returned error: %v\", test.in, err)\n\t\t} else if fmt.Sprintln(actual) != fmt.Sprintln(test.out) {\n\t\t\tt.Errorf(\"ParsePortRange(%q): got %v, want %v\", test.in, actual, test.out)\n\t\t}\n\t}\n}\n\nfunc TestPortSetContains(t *testing.T) {\n\tactual, _ := ParsePortSet(\"5-10,20-30\")\n\n\tif !actual.Contains(5) || !actual.Contains(7) || !actual.Contains(10) {\n\t\tt.Errorf(\"5-10,20-30 should contain 5, 7 and 10\")\n\t}\n\n\tif !actual.Contains(20) || !actual.Contains(27) || !actual.Contains(30) {\n\t\tt.Errorf(\"5-10,20-30 should contain 20, 27 and 30\")\n\t}\n\n\tif actual.Contains(4) || actual.Contains(11) || actual.Contains(31) {\n\t\tt.Errorf(\"5-10,20-30 should not contain 4, 11, 31\")\n\t}\n}\n\nfunc TestPermissionsParse(t *testing.T) {\n\tfor _, test := range permissionsTests {\n\t\tactual, err := ParsePermissions(test.in)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ParsePermissions(%q) returned error: %v\", test.in, err)\n\t\t} else if fmt.Sprintln(actual) != fmt.Sprintln(test.out) {\n\t\t\tt.Errorf(\"ParsePermissions(%q): got %v, want %v\", test.in, actual, test.out)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "quic.go",
    "content": "package gost\n\nimport (\n\t\"context\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\tquic \"github.com/quic-go/quic-go\"\n)\n\ntype quicSession struct {\n\tsession quic.EarlyConnection\n}\n\nfunc (session *quicSession) GetConn() (*quicConn, error) {\n\tstream, err := session.session.OpenStreamSync(context.Background())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &quicConn{\n\t\tStream: stream,\n\t\tladdr:  session.session.LocalAddr(),\n\t\traddr:  session.session.RemoteAddr(),\n\t}, nil\n}\n\nfunc (session *quicSession) Close() error {\n\treturn session.session.CloseWithError(quic.ApplicationErrorCode(0), \"closed\")\n}\n\ntype quicTransporter struct {\n\tconfig       *QUICConfig\n\tsessionMutex sync.Mutex\n\tsessions     map[string]*quicSession\n}\n\n// QUICTransporter creates a Transporter that is used by QUIC proxy client.\nfunc QUICTransporter(config *QUICConfig) Transporter {\n\tif config == nil {\n\t\tconfig = &QUICConfig{}\n\t}\n\treturn &quicTransporter{\n\t\tconfig:   config,\n\t\tsessions: make(map[string]*quicSession),\n\t}\n}\n\nfunc (tr *quicTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\tudpAddr, err := net.ResolveUDPAddr(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tsession, ok := tr.sessions[addr]\n\tif !ok {\n\t\tvar pc net.PacketConn\n\t\tpc, err = net.ListenUDP(\"udp\", &net.UDPAddr{IP: net.IPv4zero, Port: 0})\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif tr.config != nil && tr.config.Key != nil {\n\t\t\tpc = &quicCipherConn{PacketConn: pc, key: tr.config.Key}\n\t\t}\n\n\t\tsession, err = tr.initSession(udpAddr, pc)\n\t\tif err != nil {\n\t\t\tpc.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\ttr.sessions[addr] = session\n\t}\n\n\tconn, err = session.GetConn()\n\tif err != nil {\n\t\tsession.Close()\n\t\tdelete(tr.sessions, addr)\n\t\treturn nil, err\n\t}\n\treturn conn, nil\n}\n\nfunc (tr *quicTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\treturn conn, nil\n}\n\nfunc (tr *quicTransporter) initSession(addr net.Addr, conn net.PacketConn) (*quicSession, error) {\n\tconfig := tr.config\n\tif config == nil {\n\t\tconfig = &QUICConfig{}\n\t}\n\tif config.TLSConfig == nil {\n\t\tconfig.TLSConfig = &tls.Config{InsecureSkipVerify: true}\n\t}\n\n\tquicConfig := &quic.Config{\n\t\tHandshakeIdleTimeout: config.Timeout,\n\t\tMaxIdleTimeout:       config.IdleTimeout,\n\t\tKeepAlivePeriod:      config.KeepAlivePeriod,\n\t\tVersions: []quic.VersionNumber{\n\t\t\tquic.Version1,\n\t\t\tquic.Version2,\n\t\t},\n\t}\n\tsession, err := quic.DialEarly(context.Background(), conn, addr, tlsConfigQUICALPN(config.TLSConfig), quicConfig)\n\tif err != nil {\n\t\tlog.Logf(\"quic dial %s: %v\", addr, err)\n\t\treturn nil, err\n\t}\n\treturn &quicSession{session: session}, nil\n}\n\nfunc (tr *quicTransporter) Multiplex() bool {\n\treturn true\n}\n\n// QUICConfig is the config for QUIC client and server\ntype QUICConfig struct {\n\tTLSConfig       *tls.Config\n\tTimeout         time.Duration\n\tKeepAlive       bool\n\tKeepAlivePeriod time.Duration\n\tIdleTimeout     time.Duration\n\tKey             []byte\n}\n\ntype quicListener struct {\n\tln       quic.EarlyListener\n\tconnChan chan net.Conn\n\terrChan  chan error\n}\n\n// QUICListener creates a Listener for QUIC proxy server.\nfunc QUICListener(addr string, config *QUICConfig) (Listener, error) {\n\tif config == nil {\n\t\tconfig = &QUICConfig{}\n\t}\n\tquicConfig := &quic.Config{\n\t\tHandshakeIdleTimeout: config.Timeout,\n\t\tKeepAlivePeriod:      config.KeepAlivePeriod,\n\t\tMaxIdleTimeout:       config.IdleTimeout,\n\t\tVersions: []quic.VersionNumber{\n\t\t\tquic.Version1,\n\t\t\tquic.Version2,\n\t\t},\n\t}\n\n\ttlsConfig := config.TLSConfig\n\tif tlsConfig == nil {\n\t\ttlsConfig = DefaultTLSConfig\n\t}\n\tvar conn net.PacketConn\n\n\tudpAddr, err := net.ResolveUDPAddr(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconn, err = net.ListenUDP(\"udp\", udpAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif config.Key != nil {\n\t\tconn = &quicCipherConn{PacketConn: conn, key: config.Key}\n\t}\n\n\tln, err := quic.ListenEarly(conn, tlsConfigQUICALPN(tlsConfig), quicConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tl := &quicListener{\n\t\tln:       *ln,\n\t\tconnChan: make(chan net.Conn, 1024),\n\t\terrChan:  make(chan error, 1),\n\t}\n\tgo l.listenLoop()\n\n\treturn l, nil\n}\n\nfunc (l *quicListener) listenLoop() {\n\tfor {\n\t\tsession, err := l.ln.Accept(context.Background())\n\t\tif err != nil {\n\t\t\tlog.Log(\"[quic] accept:\", err)\n\t\t\tl.errChan <- err\n\t\t\tclose(l.errChan)\n\t\t\treturn\n\t\t}\n\t\tgo l.sessionLoop(session)\n\t}\n}\n\nfunc (l *quicListener) sessionLoop(session quic.Connection) {\n\tlog.Logf(\"[quic] %s <-> %s\", session.RemoteAddr(), session.LocalAddr())\n\tdefer log.Logf(\"[quic] %s >-< %s\", session.RemoteAddr(), session.LocalAddr())\n\n\tfor {\n\t\tstream, err := session.AcceptStream(context.Background())\n\t\tif err != nil {\n\t\t\tlog.Log(\"[quic] accept stream:\", err)\n\t\t\tsession.CloseWithError(quic.ApplicationErrorCode(0), \"closed\")\n\t\t\treturn\n\t\t}\n\n\t\tcc := &quicConn{Stream: stream, laddr: session.LocalAddr(), raddr: session.RemoteAddr()}\n\t\tselect {\n\t\tcase l.connChan <- cc:\n\t\tdefault:\n\t\t\tcc.Close()\n\t\t\tlog.Logf(\"[quic] %s - %s: connection queue is full\", session.RemoteAddr(), session.LocalAddr())\n\t\t}\n\t}\n}\n\nfunc (l *quicListener) Accept() (conn net.Conn, err error) {\n\tvar ok bool\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err, ok = <-l.errChan:\n\t\tif !ok {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\n\nfunc (l *quicListener) Addr() net.Addr {\n\treturn l.ln.Addr()\n}\n\nfunc (l *quicListener) Close() error {\n\treturn l.ln.Close()\n}\n\ntype quicConn struct {\n\tquic.Stream\n\tladdr net.Addr\n\traddr net.Addr\n}\n\nfunc (c *quicConn) LocalAddr() net.Addr {\n\treturn c.laddr\n}\n\nfunc (c *quicConn) RemoteAddr() net.Addr {\n\treturn c.raddr\n}\n\ntype quicCipherConn struct {\n\tnet.PacketConn\n\tkey []byte\n}\n\nfunc (conn *quicCipherConn) ReadFrom(data []byte) (n int, addr net.Addr, err error) {\n\tn, addr, err = conn.PacketConn.ReadFrom(data)\n\tif err != nil {\n\t\treturn\n\t}\n\tb, err := conn.decrypt(data[:n])\n\tif err != nil {\n\t\treturn\n\t}\n\n\tcopy(data, b)\n\n\treturn len(b), addr, nil\n}\n\nfunc (conn *quicCipherConn) WriteTo(data []byte, addr net.Addr) (n int, err error) {\n\tb, err := conn.encrypt(data)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t_, err = conn.PacketConn.WriteTo(b, addr)\n\tif err != nil {\n\t\treturn\n\t}\n\n\treturn len(b), nil\n}\n\nfunc (conn *quicCipherConn) encrypt(data []byte) ([]byte, error) {\n\tc, err := aes.NewCipher(conn.key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgcm, err := cipher.NewGCM(c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnonce := make([]byte, gcm.NonceSize())\n\tif _, err = io.ReadFull(rand.Reader, nonce); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn gcm.Seal(nonce, nonce, data, nil), nil\n}\n\nfunc (conn *quicCipherConn) decrypt(data []byte) ([]byte, error) {\n\tc, err := aes.NewCipher(conn.key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgcm, err := cipher.NewGCM(c)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnonceSize := gcm.NonceSize()\n\tif len(data) < nonceSize {\n\t\treturn nil, errors.New(\"ciphertext too short\")\n\t}\n\n\tnonce, ciphertext := data[:nonceSize], data[nonceSize:]\n\treturn gcm.Open(nil, nonce, ciphertext, nil)\n}\n\nfunc tlsConfigQUICALPN(tlsConfig *tls.Config) *tls.Config {\n\tif tlsConfig == nil {\n\t\tpanic(\"quic: tlsconfig is nil\")\n\t}\n\ttlsConfigQUIC := tlsConfig.Clone()\n\ttlsConfigQUIC.NextProtos = []string{\"http/3\", \"quic/v1\"}\n\treturn tlsConfigQUIC\n}\n"
  },
  {
    "path": "quic_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc httpOverQUICRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: QUICTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverQUIC(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverQUICRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverQUIC(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: QUICTransporter(&QUICConfig{KeepAlive: true}),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverQUICParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: QUICTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverQUICRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: QUICTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverQUIC(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverQUICRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverQUICRoundtrip(targetURL string, data []byte) error {\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: QUICTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverQUIC(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverQUICRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverQUICRoundtrip(targetURL string, data []byte) error {\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: QUICTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverQUIC(t *testing.T) {\n\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverQUICRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverQUICRoundtrip(targetURL string, data []byte,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: QUICTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverQUIC(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverQUICRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverQUICRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: QUICTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverQUIC(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverQUICRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc quicForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := QUICListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: QUICTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestQUICForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := quicForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc httpOverCipherQUICRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tsum := sha256.Sum256([]byte(\"12345678\"))\n\tcfg := &QUICConfig{\n\t\tKey: sum[:],\n\t}\n\tln, err := QUICListener(\"localhost:0\", cfg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: QUICTransporter(cfg),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverCipherQUIC(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverCipherQUICRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "redirect.go",
    "content": "//go:build linux\n// +build linux\n\npackage gost\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/LiamHaworth/go-tproxy\"\n\t\"github.com/go-log/log\"\n)\n\ntype tcpRedirectHandler struct {\n\toptions *HandlerOptions\n}\n\n// TCPRedirectHandler creates a server Handler for TCP transparent server.\nfunc TCPRedirectHandler(opts ...HandlerOption) Handler {\n\th := &tcpRedirectHandler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *tcpRedirectHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *tcpRedirectHandler) Handle(c net.Conn) {\n\tconn, ok := c.(*net.TCPConn)\n\tif !ok {\n\t\tlog.Log(\"[red-tcp] not a TCP connection\")\n\t}\n\n\tsrcAddr := conn.RemoteAddr()\n\tdstAddr, conn, err := h.getOriginalDstAddr(conn)\n\tif err != nil {\n\t\tlog.Logf(\"[red-tcp] %s -> %s : %s\", srcAddr, dstAddr, err)\n\t\treturn\n\t}\n\tdefer conn.Close()\n\n\tlog.Logf(\"[red-tcp] %s -> %s\", srcAddr, dstAddr)\n\n\tcc, err := h.options.Chain.DialContext(context.Background(),\n\t\t\"tcp\", dstAddr.String(),\n\t\tRetryChainOption(h.options.Retries),\n\t\tTimeoutChainOption(h.options.Timeout),\n\t)\n\tif err != nil {\n\t\tlog.Logf(\"[red-tcp] %s -> %s : %s\", srcAddr, dstAddr, err)\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tlog.Logf(\"[red-tcp] %s <-> %s\", srcAddr, dstAddr)\n\ttransport(conn, cc)\n\tlog.Logf(\"[red-tcp] %s >-< %s\", srcAddr, dstAddr)\n}\n\nfunc (h *tcpRedirectHandler) getOriginalDstAddr(conn *net.TCPConn) (addr net.Addr, c *net.TCPConn, err error) {\n\tdefer conn.Close()\n\n\tfc, err := conn.File()\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer fc.Close()\n\n\tmreq, err := syscall.GetsockoptIPv6Mreq(int(fc.Fd()), syscall.IPPROTO_IP, 80)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// only ipv4 support\n\tip := net.IPv4(mreq.Multiaddr[4], mreq.Multiaddr[5], mreq.Multiaddr[6], mreq.Multiaddr[7])\n\tport := uint16(mreq.Multiaddr[2])<<8 + uint16(mreq.Multiaddr[3])\n\taddr, err = net.ResolveTCPAddr(\"tcp4\", fmt.Sprintf(\"%s:%d\", ip.String(), port))\n\tif err != nil {\n\t\treturn\n\t}\n\n\tcc, err := net.FileConn(fc)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tc, ok := cc.(*net.TCPConn)\n\tif !ok {\n\t\terr = errors.New(\"not a TCP connection\")\n\t}\n\treturn\n}\n\ntype udpRedirectHandler struct {\n\toptions *HandlerOptions\n}\n\n// UDPRedirectHandler creates a server Handler for UDP transparent server.\nfunc UDPRedirectHandler(opts ...HandlerOption) Handler {\n\th := &udpRedirectHandler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *udpRedirectHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *udpRedirectHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\traddr, ok := conn.LocalAddr().(*net.UDPAddr)\n\tif !ok {\n\t\tlog.Log(\"[red-udp] wrong connection type\")\n\t\treturn\n\t}\n\n\tcc, err := h.options.Chain.DialContext(context.Background(),\n\t\t\"udp\", raddr.String(),\n\t\tRetryChainOption(h.options.Retries),\n\t\tTimeoutChainOption(h.options.Timeout),\n\t)\n\tif err != nil {\n\t\tlog.Logf(\"[red-udp] %s - %s : %s\", conn.RemoteAddr(), raddr, err)\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tlog.Logf(\"[red-udp] %s <-> %s\", conn.RemoteAddr(), raddr)\n\ttransport(conn, cc)\n\tlog.Logf(\"[red-udp] %s >-< %s\", conn.RemoteAddr(), raddr)\n}\n\ntype udpRedirectListener struct {\n\t*net.UDPConn\n\tconfig *UDPListenConfig\n}\n\n// UDPRedirectListener creates a Listener for UDP transparent proxy server.\nfunc UDPRedirectListener(addr string, cfg *UDPListenConfig) (Listener, error) {\n\tladdr, err := net.ResolveUDPAddr(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tln, err := tproxy.ListenUDP(\"udp\", laddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cfg == nil {\n\t\tcfg = &UDPListenConfig{}\n\t}\n\treturn &udpRedirectListener{\n\t\tUDPConn: ln,\n\t\tconfig:  cfg,\n\t}, nil\n}\n\nfunc (l *udpRedirectListener) Accept() (conn net.Conn, err error) {\n\tb := make([]byte, mediumBufferSize)\n\n\tn, raddr, dstAddr, err := tproxy.ReadFromUDP(l.UDPConn, b)\n\tif err != nil {\n\t\tlog.Logf(\"[red-udp] %s : %s\", l.Addr(), err)\n\t\treturn\n\t}\n\tlog.Logf(\"[red-udp] %s: %s -> %s\", l.Addr(), raddr, dstAddr)\n\n\tc, err := tproxy.DialUDP(\"udp\", dstAddr, raddr)\n\tif err != nil {\n\t\tlog.Logf(\"[red-udp] %s -> %s : %s\", raddr, dstAddr, err)\n\t\treturn\n\t}\n\n\tttl := l.config.TTL\n\tif ttl <= 0 {\n\t\tttl = defaultTTL\n\t}\n\n\tconn = &udpRedirectServerConn{\n\t\tConn: c,\n\t\tbuf:  b[:n],\n\t\tttl:  ttl,\n\t}\n\treturn\n}\n\nfunc (l *udpRedirectListener) Addr() net.Addr {\n\treturn l.UDPConn.LocalAddr()\n}\n\ntype udpRedirectServerConn struct {\n\tnet.Conn\n\tbuf  []byte\n\tttl  time.Duration\n\tonce sync.Once\n}\n\nfunc (c *udpRedirectServerConn) Read(b []byte) (n int, err error) {\n\tif c.ttl > 0 {\n\t\tc.SetReadDeadline(time.Now().Add(c.ttl))\n\t\tdefer c.SetReadDeadline(time.Time{})\n\t}\n\tc.once.Do(func() {\n\t\tn = copy(b, c.buf)\n\t\tc.buf = nil\n\t})\n\n\tif n == 0 {\n\t\tn, err = c.Conn.Read(b)\n\t}\n\treturn\n}\n\nfunc (c *udpRedirectServerConn) Write(b []byte) (n int, err error) {\n\tif c.ttl > 0 {\n\t\tc.SetWriteDeadline(time.Now().Add(c.ttl))\n\t\tdefer c.SetWriteDeadline(time.Time{})\n\t}\n\treturn c.Conn.Write(b)\n}\n"
  },
  {
    "path": "redirect_other.go",
    "content": "//go:build !linux\n// +build !linux\n\npackage gost\n\nimport (\n\t\"errors\"\n\t\"net\"\n\n\t\"github.com/go-log/log\"\n)\n\ntype tcpRedirectHandler struct {\n\toptions *HandlerOptions\n}\n\n// TCPRedirectHandler creates a server Handler for TCP redirect server.\nfunc TCPRedirectHandler(opts ...HandlerOption) Handler {\n\th := &tcpRedirectHandler{\n\t\toptions: &HandlerOptions{\n\t\t\tChain: new(Chain),\n\t\t},\n\t}\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\treturn h\n}\n\nfunc (h *tcpRedirectHandler) Init(options ...HandlerOption) {\n\tlog.Log(\"[red-tcp] TCP redirect is not available on the Windows platform\")\n}\n\nfunc (h *tcpRedirectHandler) Handle(c net.Conn) {\n\tlog.Log(\"[red-tcp] TCP redirect is not available on the Windows platform\")\n\tc.Close()\n}\n\ntype udpRedirectHandler struct{}\n\n// UDPRedirectHandler creates a server Handler for UDP transparent server.\nfunc UDPRedirectHandler(opts ...HandlerOption) Handler {\n\treturn &udpRedirectHandler{}\n}\n\nfunc (h *udpRedirectHandler) Init(options ...HandlerOption) {\n}\n\nfunc (h *udpRedirectHandler) Handle(conn net.Conn) {\n\tlog.Log(\"[red-udp] UDP redirect is not available on the Windows platform\")\n\tconn.Close()\n}\n\n// UDPRedirectListener creates a Listener for UDP transparent proxy server.\nfunc UDPRedirectListener(addr string, cfg *UDPListenConfig) (Listener, error) {\n\treturn nil, errors.New(\"UDP redirect is not available on the Windows platform\")\n}\n"
  },
  {
    "path": "relay.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-gost/relay\"\n\t\"github.com/go-log/log\"\n)\n\ntype relayConnector struct {\n\tuser       *url.Userinfo\n\tremoteAddr string\n}\n\n// RelayConnector creates a Connector for TCP/UDP data relay.\nfunc RelayConnector(user *url.Userinfo) Connector {\n\treturn &relayConnector{\n\t\tuser: user,\n\t}\n}\n\nfunc (c *relayConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn conn, nil\n}\n\nfunc (c *relayConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tvar udp bool\n\tif network == \"udp\" || network == \"udp4\" || network == \"udp6\" {\n\t\tudp = true\n\t}\n\n\treq := &relay.Request{\n\t\tVersion: relay.Version1,\n\t}\n\tif udp {\n\t\treq.Flags |= relay.FUDP\n\t}\n\n\tif c.user != nil {\n\t\tpwd, _ := c.user.Password()\n\t\treq.Features = append(req.Features, &relay.UserAuthFeature{\n\t\t\tUsername: c.user.Username(),\n\t\t\tPassword: pwd,\n\t\t})\n\t}\n\tif address != \"\" {\n\t\thost, port, _ := net.SplitHostPort(address)\n\t\tnport, _ := strconv.ParseUint(port, 10, 16)\n\t\tif host == \"\" {\n\t\t\thost = net.IPv4zero.String()\n\t\t}\n\n\t\tif nport > 0 {\n\t\t\tvar atype uint8\n\t\t\tip := net.ParseIP(host)\n\t\t\tif ip == nil {\n\t\t\t\tatype = relay.AddrDomain\n\t\t\t} else if ip.To4() == nil {\n\t\t\t\tatype = relay.AddrIPv6\n\t\t\t} else {\n\t\t\t\tatype = relay.AddrIPv4\n\t\t\t}\n\n\t\t\treq.Features = append(req.Features, &relay.AddrFeature{\n\t\t\t\tAType: atype,\n\t\t\t\tHost:  host,\n\t\t\t\tPort:  uint16(nport),\n\t\t\t})\n\t\t}\n\t}\n\n\trc := &relayConn{\n\t\tudp:  udp,\n\t\tConn: conn,\n\t}\n\n\t// write the header at once.\n\tif opts.NoDelay {\n\t\tif _, err := req.WriteTo(rc); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tif _, err := req.WriteTo(&rc.wbuf); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn rc, nil\n}\n\ntype relayHandler struct {\n\t*baseForwardHandler\n}\n\n// RelayHandler creates a server Handler for TCP/UDP relay server.\nfunc RelayHandler(raddr string, opts ...HandlerOption) Handler {\n\th := &relayHandler{\n\t\tbaseForwardHandler: &baseForwardHandler{\n\t\t\traddr:   raddr,\n\t\t\tgroup:   NewNodeGroup(),\n\t\t\toptions: &HandlerOptions{},\n\t\t},\n\t}\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\treturn h\n}\n\nfunc (h *relayHandler) Init(options ...HandlerOption) {\n\th.baseForwardHandler.Init(options...)\n}\n\nfunc (h *relayHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\treq := &relay.Request{}\n\tif _, err := req.ReadFrom(conn); err != nil {\n\t\tlog.Logf(\"[relay] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\n\tif req.Version != relay.Version1 {\n\t\tlog.Logf(\"[relay] %s - %s : bad version\", conn.RemoteAddr(), conn.LocalAddr())\n\t\treturn\n\t}\n\n\tvar user, pass string\n\tvar raddr string\n\tfor _, f := range req.Features {\n\t\tif f.Type() == relay.FeatureUserAuth {\n\t\t\tfeature := f.(*relay.UserAuthFeature)\n\t\t\tuser, pass = feature.Username, feature.Password\n\t\t}\n\t\tif f.Type() == relay.FeatureAddr {\n\t\t\tfeature := f.(*relay.AddrFeature)\n\t\t\traddr = net.JoinHostPort(feature.Host, strconv.Itoa(int(feature.Port)))\n\t\t}\n\t}\n\n\tresp := &relay.Response{\n\t\tVersion: relay.Version1,\n\t\tStatus:  relay.StatusOK,\n\t}\n\tif h.options.Authenticator != nil && !h.options.Authenticator.Authenticate(user, pass) {\n\t\tresp.Status = relay.StatusUnauthorized\n\t\tresp.WriteTo(conn)\n\t\tlog.Logf(\"[relay] %s -> %s : %s unauthorized\", conn.RemoteAddr(), conn.LocalAddr(), user)\n\t\treturn\n\t}\n\n\tif raddr != \"\" {\n\t\tif len(h.group.Nodes()) > 0 {\n\t\t\tresp.Status = relay.StatusForbidden\n\t\t\tresp.WriteTo(conn)\n\t\t\tlog.Logf(\"[relay] %s -> %s : relay to %s is forbidden\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), raddr)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif len(h.group.Nodes()) == 0 {\n\t\t\tresp.Status = relay.StatusBadRequest\n\t\t\tresp.WriteTo(conn)\n\t\t\tlog.Logf(\"[relay] %s -> %s : bad request, target addr is needed\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr())\n\t\t\treturn\n\t\t}\n\t}\n\n\tudp := (req.Flags & relay.FUDP) == relay.FUDP\n\tretries := 1\n\tif h.options.Chain != nil && h.options.Chain.Retries > 0 {\n\t\tretries = h.options.Chain.Retries\n\t}\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tnetwork := \"tcp\"\n\tif udp {\n\t\tnetwork = \"udp\"\n\t}\n\tif !Can(network, raddr, h.options.Whitelist, h.options.Blacklist) {\n\t\tresp.Status = relay.StatusForbidden\n\t\tresp.WriteTo(conn)\n\t\tlog.Logf(\"[relay] %s -> %s : relay to %s is forbidden\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), raddr)\n\t\treturn\n\t}\n\n\tctx := context.TODO()\n\tvar cc net.Conn\n\tvar node Node\n\tvar err error\n\tfor i := 0; i < retries; i++ {\n\t\tif len(h.group.Nodes()) > 0 {\n\t\t\tnode, err = h.group.Next()\n\t\t\tif err != nil {\n\t\t\t\tresp.Status = relay.StatusServiceUnavailable\n\t\t\t\tresp.WriteTo(conn)\n\t\t\t\tlog.Logf(\"[relay] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\traddr = node.Addr\n\t\t}\n\n\t\tlog.Logf(\"[relay] %s -> %s -> %s\", conn.RemoteAddr(), conn.LocalAddr(), raddr)\n\t\tcc, err = h.options.Chain.DialContext(ctx,\n\t\t\tnetwork, raddr,\n\t\t\tRetryChainOption(h.options.Retries),\n\t\t\tTimeoutChainOption(h.options.Timeout),\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[relay] %s -> %s : %s\", conn.RemoteAddr(), raddr, err)\n\t\t\tnode.MarkDead()\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\tif err != nil {\n\t\tresp.Status = relay.StatusServiceUnavailable\n\t\tresp.WriteTo(conn)\n\t\treturn\n\t}\n\n\tnode.ResetDead()\n\tdefer cc.Close()\n\n\tsc := &relayConn{\n\t\tConn:     conn,\n\t\tisServer: true,\n\t\tudp:      udp,\n\t}\n\tresp.WriteTo(&sc.wbuf)\n\tconn = sc\n\n\tlog.Logf(\"[relay] %s <-> %s\", conn.RemoteAddr(), raddr)\n\ttransport(conn, cc)\n\tlog.Logf(\"[relay] %s >-< %s\", conn.RemoteAddr(), raddr)\n}\n\ntype relayConn struct {\n\tnet.Conn\n\tisServer   bool\n\tudp        bool\n\twbuf       bytes.Buffer\n\tonce       sync.Once\n\theaderSent bool\n}\n\nfunc (c *relayConn) Read(b []byte) (n int, err error) {\n\tc.once.Do(func() {\n\t\tif c.isServer {\n\t\t\treturn\n\t\t}\n\t\tresp := new(relay.Response)\n\t\t_, err = resp.ReadFrom(c.Conn)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif resp.Version != relay.Version1 {\n\t\t\terr = relay.ErrBadVersion\n\t\t\treturn\n\t\t}\n\t\tif resp.Status != relay.StatusOK {\n\t\t\terr = fmt.Errorf(\"status %d\", resp.Status)\n\t\t\treturn\n\t\t}\n\t})\n\n\tif err != nil {\n\t\tlog.Logf(\"[relay] %s <- %s: %s\", c.Conn.LocalAddr(), c.Conn.RemoteAddr(), err)\n\t\treturn\n\t}\n\n\tif !c.udp {\n\t\treturn c.Conn.Read(b)\n\t}\n\tvar bb [2]byte\n\t_, err = io.ReadFull(c.Conn, bb[:])\n\tif err != nil {\n\t\treturn\n\t}\n\tdlen := int(binary.BigEndian.Uint16(bb[:]))\n\tif len(b) >= dlen {\n\t\treturn io.ReadFull(c.Conn, b[:dlen])\n\t}\n\tbuf := make([]byte, dlen)\n\t_, err = io.ReadFull(c.Conn, buf)\n\tn = copy(b, buf)\n\treturn\n}\n\nfunc (c *relayConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {\n\tn, err = c.Read(b)\n\taddr = c.Conn.RemoteAddr()\n\treturn\n}\n\nfunc (c *relayConn) Write(b []byte) (n int, err error) {\n\tif len(b) > 0xFFFF {\n\t\terr = errors.New(\"write: data maximum exceeded\")\n\t\treturn\n\t}\n\tn = len(b) // force byte length consistent\n\tif c.wbuf.Len() > 0 {\n\t\tif c.udp {\n\t\t\tvar bb [2]byte\n\t\t\tbinary.BigEndian.PutUint16(bb[:2], uint16(len(b)))\n\t\t\tc.wbuf.Write(bb[:])\n\t\t\tc.headerSent = true\n\t\t}\n\t\tc.wbuf.Write(b) // append the data to the cached header\n\t\t// _, err = c.Conn.Write(c.wbuf.Bytes())\n\t\t// c.wbuf.Reset()\n\t\t_, err = c.wbuf.WriteTo(c.Conn)\n\t\treturn\n\t}\n\n\tif !c.udp {\n\t\treturn c.Conn.Write(b)\n\t}\n\tif !c.headerSent {\n\t\tc.headerSent = true\n\t\tb2 := make([]byte, len(b)+2)\n\t\tcopy(b2, b)\n\t\t_, err = c.Conn.Write(b2)\n\t\treturn\n\t}\n\tnsize := 2 + len(b)\n\tvar buf []byte\n\tif nsize <= mediumBufferSize {\n\t\tbuf = mPool.Get().([]byte)\n\t\tdefer mPool.Put(buf)\n\t} else {\n\t\tbuf = make([]byte, nsize)\n\t}\n\tbinary.BigEndian.PutUint16(buf[:2], uint16(len(b)))\n\tn = copy(buf[2:], b)\n\t_, err = c.Conn.Write(buf[:nsize])\n\treturn\n}\n\nfunc (c *relayConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {\n\treturn c.Write(b)\n}\n"
  },
  {
    "path": "reload.go",
    "content": "package gost\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\n// Reloader is the interface for objects that support live reloading.\ntype Reloader interface {\n\tReload(r io.Reader) error\n\tPeriod() time.Duration\n}\n\n// Stoppable is the interface that indicates a Reloader can be stopped.\ntype Stoppable interface {\n\tStop()\n\tStopped() bool\n}\n\n// PeriodReload reloads the config configFile periodically according to the period of the Reloader r.\nfunc PeriodReload(r Reloader, configFile string) error {\n\tif r == nil || configFile == \"\" {\n\t\treturn nil\n\t}\n\n\tvar lastMod time.Time\n\tfor {\n\t\tif r.Period() < 0 {\n\t\t\tlog.Log(\"[reload] stopped:\", configFile)\n\t\t\treturn nil\n\t\t}\n\n\t\tf, err := os.Open(configFile)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tmt := lastMod\n\t\tif finfo, err := f.Stat(); err == nil {\n\t\t\tmt = finfo.ModTime()\n\t\t}\n\n\t\tif !lastMod.IsZero() && !mt.Equal(lastMod) {\n\t\t\tlog.Log(\"[reload]\", configFile)\n\t\t\tif err := r.Reload(f); err != nil {\n\t\t\t\tlog.Logf(\"[reload] %s: %s\", configFile, err)\n\t\t\t}\n\t\t}\n\t\tf.Close()\n\t\tlastMod = mt\n\n\t\tperiod := r.Period()\n\t\tif period == 0 {\n\t\t\tlog.Log(\"[reload] disabled:\", configFile)\n\t\t\treturn nil\n\t\t}\n\t\tif period < time.Second {\n\t\t\tperiod = time.Second\n\t\t}\n\t\t<-time.After(period)\n\t}\n}\n"
  },
  {
    "path": "resolver.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/miekg/dns\"\n)\n\nvar (\n\t// DefaultResolverTimeout is the default timeout for name resolution.\n\tDefaultResolverTimeout = 5 * time.Second\n)\n\ntype nameServerOptions struct {\n\ttimeout time.Duration\n\tchain   *Chain\n}\n\n// NameServerOption allows a common way to set name server options.\ntype NameServerOption func(*nameServerOptions)\n\n// TimeoutNameServerOption sets the timeout for name server.\nfunc TimeoutNameServerOption(timeout time.Duration) NameServerOption {\n\treturn func(opts *nameServerOptions) {\n\t\topts.timeout = timeout\n\t}\n}\n\n// ChainNameServerOption sets the chain for name server.\nfunc ChainNameServerOption(chain *Chain) NameServerOption {\n\treturn func(opts *nameServerOptions) {\n\t\topts.chain = chain\n\t}\n}\n\n// NameServer is a name server.\n// Currently supported protocol: TCP, UDP and TLS.\ntype NameServer struct {\n\tAddr      string\n\tProtocol  string\n\tHostname  string // for TLS handshake verification\n\texchanger Exchanger\n\toptions   nameServerOptions\n}\n\n// Init initializes the name server.\nfunc (ns *NameServer) Init(opts ...NameServerOption) error {\n\tfor _, opt := range opts {\n\t\topt(&ns.options)\n\t}\n\n\toptions := []ExchangerOption{\n\t\tTimeoutExchangerOption(ns.options.timeout),\n\t}\n\tprotocol := strings.ToLower(ns.Protocol)\n\tswitch protocol {\n\tcase \"tcp\", \"tcp-chain\":\n\t\tif protocol == \"tcp-chain\" {\n\t\t\toptions = append(options, ChainExchangerOption(ns.options.chain))\n\t\t}\n\t\tns.exchanger = NewDNSTCPExchanger(ns.Addr, options...)\n\tcase \"tls\", \"tls-chain\":\n\t\tif protocol == \"tls-chain\" {\n\t\t\toptions = append(options, ChainExchangerOption(ns.options.chain))\n\t\t}\n\t\tcfg := &tls.Config{\n\t\t\tServerName: ns.Hostname,\n\t\t}\n\t\tif cfg.ServerName == \"\" {\n\t\t\tcfg.InsecureSkipVerify = true\n\t\t}\n\t\tns.exchanger = NewDoTExchanger(ns.Addr, cfg, options...)\n\tcase \"https\", \"https-chain\":\n\t\tif protocol == \"https-chain\" {\n\t\t\toptions = append(options, ChainExchangerOption(ns.options.chain))\n\t\t}\n\t\tu, err := url.Parse(ns.Addr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tu.Scheme = \"https\"\n\t\tcfg := &tls.Config{ServerName: ns.Hostname}\n\t\tif cfg.ServerName == \"\" {\n\t\t\tcfg.InsecureSkipVerify = true\n\t\t}\n\t\tns.exchanger = NewDoHExchanger(u, cfg, options...)\n\tcase \"udp\", \"udp-chain\":\n\t\tfallthrough\n\tdefault:\n\t\tif protocol == \"udp-chain\" {\n\t\t\toptions = append(options, ChainExchangerOption(ns.options.chain))\n\t\t}\n\t\tns.exchanger = NewDNSExchanger(ns.Addr, options...)\n\t}\n\n\treturn nil\n}\n\nfunc (ns *NameServer) String() string {\n\taddr := ns.Addr\n\tprot := ns.Protocol\n\tif prot == \"\" {\n\t\tprot = \"udp\"\n\t}\n\treturn fmt.Sprintf(\"%s/%s\", addr, prot)\n}\n\ntype resolverOptions struct {\n\tchain   *Chain\n\ttimeout time.Duration\n\tttl     time.Duration\n\tprefer  string\n\tsrcIP   net.IP\n}\n\n// ResolverOption allows a common way to set Resolver options.\ntype ResolverOption func(*resolverOptions)\n\n// ChainResolverOption sets the chain for Resolver.\nfunc ChainResolverOption(chain *Chain) ResolverOption {\n\treturn func(opts *resolverOptions) {\n\t\topts.chain = chain\n\t}\n}\n\n// TimeoutResolverOption sets the timeout for Resolver.\nfunc TimeoutResolverOption(timeout time.Duration) ResolverOption {\n\treturn func(opts *resolverOptions) {\n\t\topts.timeout = timeout\n\t}\n}\n\n// TTLResolverOption sets the timeout for Resolver.\nfunc TTLResolverOption(ttl time.Duration) ResolverOption {\n\treturn func(opts *resolverOptions) {\n\t\topts.ttl = ttl\n\t}\n}\n\n// PreferResolverOption sets the prefer for Resolver.\nfunc PreferResolverOption(prefer string) ResolverOption {\n\treturn func(opts *resolverOptions) {\n\t\topts.prefer = prefer\n\t}\n}\n\n// SrcIPResolverOption sets the source IP for Resolver.\nfunc SrcIPResolverOption(ip net.IP) ResolverOption {\n\treturn func(opts *resolverOptions) {\n\t\topts.srcIP = ip\n\t}\n}\n\n// Resolver is a name resolver for domain name.\n// It contains a list of name servers.\ntype Resolver interface {\n\t// Init initializes the Resolver instance.\n\tInit(opts ...ResolverOption) error\n\t// Resolve returns a slice of that host's IPv4 and IPv6 addresses.\n\tResolve(host string) ([]net.IP, error)\n\t// Exchange performs a synchronous query,\n\t// It sends the message query and waits for a reply.\n\tExchange(ctx context.Context, query []byte) (reply []byte, err error)\n}\n\n// ReloadResolver is resolover that support live reloading.\ntype ReloadResolver interface {\n\tResolver\n\tReloader\n\tStoppable\n}\n\ntype resolver struct {\n\tservers []NameServer\n\tttl     time.Duration\n\ttimeout time.Duration\n\tperiod  time.Duration\n\tdomain  string\n\tcache   *resolverCache\n\tstopped chan struct{}\n\tmux     sync.RWMutex\n\tprefer  string // ipv4 or ipv6\n\tsrcIP   net.IP // for edns0 subnet option\n\toptions resolverOptions\n}\n\n// NewResolver create a new Resolver with the given name servers and resolution timeout.\nfunc NewResolver(ttl time.Duration, servers ...NameServer) ReloadResolver {\n\tr := newResolver(ttl, servers...)\n\treturn r\n}\n\nfunc newResolver(ttl time.Duration, servers ...NameServer) *resolver {\n\treturn &resolver{\n\t\tservers: servers,\n\t\tcache:   newResolverCache(ttl),\n\t\tstopped: make(chan struct{}),\n\t}\n}\n\nfunc (r *resolver) Init(opts ...ResolverOption) error {\n\tif r == nil {\n\t\treturn nil\n\t}\n\n\tr.mux.Lock()\n\tdefer r.mux.Unlock()\n\n\tfor _, opt := range opts {\n\t\topt(&r.options)\n\t}\n\n\ttimeout := r.timeout\n\tif r.options.timeout != 0 {\n\t\ttimeout = r.options.timeout\n\t}\n\tif timeout <= 0 {\n\t\ttimeout = DefaultResolverTimeout\n\t}\n\n\tif r.options.ttl != 0 {\n\t\tr.ttl = r.options.ttl\n\t}\n\tif r.options.prefer != \"\" {\n\t\tr.prefer = r.options.prefer\n\t}\n\tif r.options.srcIP != nil {\n\t\tr.srcIP = r.options.srcIP\n\t}\n\n\tvar nss []NameServer\n\tfor _, ns := range r.servers {\n\t\tif err := ns.Init( // init all name servers\n\t\t\tChainNameServerOption(r.options.chain),\n\t\t\tTimeoutNameServerOption(timeout),\n\t\t); err != nil {\n\t\t\tcontinue // ignore invalid name servers\n\t\t}\n\t\tnss = append(nss, ns)\n\t}\n\n\tr.servers = nss\n\n\treturn nil\n}\n\nfunc (r *resolver) copyServers() []NameServer {\n\tr.mux.RLock()\n\tdefer r.mux.RUnlock()\n\n\tservers := make([]NameServer, len(r.servers))\n\tcopy(servers, r.servers)\n\n\treturn servers\n}\n\nfunc (r *resolver) Resolve(host string) (ips []net.IP, err error) {\n\tr.mux.RLock()\n\tdomain := r.domain\n\tr.mux.RUnlock()\n\n\tif ip := net.ParseIP(host); ip != nil {\n\t\treturn []net.IP{ip}, nil\n\t}\n\n\tif !strings.Contains(host, \".\") && domain != \"\" {\n\t\thost = host + \".\" + domain\n\t}\n\n\tctx := context.Background()\n\tfor _, ns := range r.copyServers() {\n\t\tips, err = r.resolve(ctx, ns.exchanger, host)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[resolver] %s via %s : %s\", host, ns.String(), err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif Debug {\n\t\t\tlog.Logf(\"[resolver] %s via %s %v\", host, ns.String(), ips)\n\t\t}\n\t\tif len(ips) > 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (r *resolver) resolve(ctx context.Context, ex Exchanger, host string) (ips []net.IP, err error) {\n\tif ex == nil {\n\t\treturn\n\t}\n\n\tr.mux.RLock()\n\tprefer := r.prefer\n\tr.mux.RUnlock()\n\n\tif prefer == \"ipv6\" { // prefer ipv6\n\t\tif ips, err = r.resolve6(ctx, ex, host); len(ips) > 0 {\n\t\t\treturn\n\t\t}\n\t\treturn r.resolve4(ctx, ex, host)\n\t}\n\n\tif ips, err = r.resolve4(ctx, ex, host); len(ips) > 0 {\n\t\treturn\n\t}\n\treturn r.resolve6(ctx, ex, host)\n}\n\nfunc (r *resolver) resolve4(ctx context.Context, ex Exchanger, host string) (ips []net.IP, err error) {\n\tmq := dns.Msg{}\n\tmq.SetQuestion(dns.Fqdn(host), dns.TypeA)\n\treturn r.resolveIPs(ctx, ex, &mq)\n}\n\nfunc (r *resolver) resolve6(ctx context.Context, ex Exchanger, host string) (ips []net.IP, err error) {\n\tmq := dns.Msg{}\n\tmq.SetQuestion(dns.Fqdn(host), dns.TypeAAAA)\n\treturn r.resolveIPs(ctx, ex, &mq)\n}\n\nfunc (r *resolver) resolveIPs(ctx context.Context, ex Exchanger, mq *dns.Msg) (ips []net.IP, err error) {\n\tkey := newResolverCacheKey(&mq.Question[0])\n\tmr := r.cache.loadCache(key)\n\tif mr == nil {\n\t\tr.addSubnetOpt(mq)\n\t\tmr, err = r.exchangeMsg(ctx, ex, mq)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tr.cache.storeCache(key, mr, r.TTL())\n\t}\n\n\tfor _, ans := range mr.Answer {\n\t\tif ar, _ := ans.(*dns.AAAA); ar != nil {\n\t\t\tips = append(ips, ar.AAAA)\n\t\t}\n\t\tif ar, _ := ans.(*dns.A); ar != nil {\n\t\t\tips = append(ips, ar.A)\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (r *resolver) addSubnetOpt(m *dns.Msg) {\n\tif m == nil || r.srcIP == nil {\n\t\treturn\n\t}\n\topt := new(dns.OPT)\n\topt.Hdr.Name = \".\"\n\topt.Hdr.Rrtype = dns.TypeOPT\n\te := new(dns.EDNS0_SUBNET)\n\te.Code = dns.EDNS0SUBNET\n\tif ip := r.srcIP.To4(); ip != nil {\n\t\te.Family = 1\n\t\te.SourceNetmask = 32\n\t\te.Address = ip.To4()\n\t} else {\n\t\te.Family = 2\n\t\te.SourceNetmask = 128\n\t\te.Address = r.srcIP\n\t}\n\topt.Option = append(opt.Option, e)\n\tm.Extra = append(m.Extra, opt)\n}\n\nfunc (r *resolver) Exchange(ctx context.Context, query []byte) (reply []byte, err error) {\n\tmq := &dns.Msg{}\n\tif err = mq.Unpack(query); err != nil {\n\t\treturn\n\t}\n\n\tif len(mq.Question) == 0 {\n\t\treturn nil, errors.New(\"empty question\")\n\t}\n\n\tvar mr *dns.Msg\n\t// Only cache for single question.\n\tif len(mq.Question) == 1 {\n\t\tkey := newResolverCacheKey(&mq.Question[0])\n\t\tmr = r.cache.loadCache(key)\n\t\tif mr != nil {\n\t\t\tlog.Logf(\"[dns] exchange message %d (cached): %s\", mq.Id, mq.Question[0].String())\n\t\t\tmr.Id = mq.Id\n\t\t\treturn mr.Pack()\n\t\t}\n\n\t\tdefer func() {\n\t\t\tif mr != nil {\n\t\t\t\tr.cache.storeCache(key, mr, r.TTL())\n\t\t\t}\n\t\t}()\n\t}\n\n\tr.addSubnetOpt(mq)\n\n\tfor _, ns := range r.copyServers() {\n\t\tlog.Logf(\"[dns] exchange message %d via %s: %s\", mq.Id, ns.String(), mq.Question[0].String())\n\t\tmr, err = r.exchangeMsg(ctx, ns.exchanger, mq)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tlog.Logf(\"[dns] exchange message %d via %s: %s\", mq.Id, ns.String(), err)\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\treturn mr.Pack()\n}\n\nfunc (r *resolver) exchangeMsg(ctx context.Context, ex Exchanger, mq *dns.Msg) (mr *dns.Msg, err error) {\n\tquery, err := mq.Pack()\n\tif err != nil {\n\t\treturn\n\t}\n\treply, err := ex.Exchange(ctx, query)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmr = &dns.Msg{}\n\terr = mr.Unpack(reply)\n\n\treturn\n}\n\nfunc (r *resolver) TTL() time.Duration {\n\tr.mux.RLock()\n\tdefer r.mux.RUnlock()\n\treturn r.ttl\n}\n\nfunc (r *resolver) Reload(rd io.Reader) error {\n\tvar ttl, timeout, period time.Duration\n\tvar domain, prefer string\n\tvar srcIP net.IP\n\tvar nss []NameServer\n\n\tif rd == nil || r.Stopped() {\n\t\treturn nil\n\t}\n\n\tscanner := bufio.NewScanner(rd)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tss := splitLine(line)\n\t\tif len(ss) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch ss[0] {\n\t\tcase \"timeout\": // timeout option\n\t\t\tif len(ss) > 1 {\n\t\t\t\ttimeout, _ = time.ParseDuration(ss[1])\n\t\t\t}\n\t\tcase \"ttl\": // ttl option\n\t\t\tif len(ss) > 1 {\n\t\t\t\tttl, _ = time.ParseDuration(ss[1])\n\t\t\t}\n\t\tcase \"reload\": // reload option\n\t\t\tif len(ss) > 1 {\n\t\t\t\tperiod, _ = time.ParseDuration(ss[1])\n\t\t\t}\n\t\tcase \"domain\":\n\t\t\tif len(ss) > 1 {\n\t\t\t\tdomain = ss[1]\n\t\t\t}\n\t\tcase \"search\", \"sortlist\", \"options\": // we don't support these features in /etc/resolv.conf\n\t\tcase \"prefer\":\n\t\t\tif len(ss) > 1 {\n\t\t\t\tprefer = strings.ToLower(ss[1])\n\t\t\t}\n\t\tcase \"ip\":\n\t\t\tif len(ss) > 1 {\n\t\t\t\tsrcIP = net.ParseIP(ss[1])\n\t\t\t}\n\t\tcase \"nameserver\": // nameserver option, compatible with /etc/resolv.conf\n\t\t\tif len(ss) <= 1 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tss = ss[1:]\n\t\t\tfallthrough\n\t\tdefault:\n\t\t\tvar ns NameServer\n\t\t\tswitch len(ss) {\n\t\t\tcase 0:\n\t\t\t\tbreak\n\t\t\tcase 1:\n\t\t\t\tns.Addr = ss[0]\n\t\t\tcase 2:\n\t\t\t\tns.Addr = ss[0]\n\t\t\t\tns.Protocol = ss[1]\n\t\t\tdefault:\n\t\t\t\tns.Addr = ss[0]\n\t\t\t\tns.Protocol = ss[1]\n\t\t\t\tns.Hostname = ss[2]\n\t\t\t}\n\n\t\t\tif strings.HasPrefix(ns.Addr, \"https\") && ns.Protocol == \"\" {\n\t\t\t\tns.Protocol = \"https\"\n\t\t\t}\n\t\t\tnss = append(nss, ns)\n\t\t}\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\treturn err\n\t}\n\n\tr.mux.Lock()\n\tr.ttl = ttl\n\tr.timeout = timeout\n\tr.domain = domain\n\tr.period = period\n\tr.prefer = prefer\n\tr.srcIP = srcIP\n\tr.servers = nss\n\tr.mux.Unlock()\n\n\tr.Init()\n\n\treturn nil\n}\n\nfunc (r *resolver) Period() time.Duration {\n\tif r.Stopped() {\n\t\treturn -1\n\t}\n\n\tr.mux.RLock()\n\tdefer r.mux.RUnlock()\n\n\treturn r.period\n}\n\n// Stop stops reloading.\nfunc (r *resolver) Stop() {\n\tselect {\n\tcase <-r.stopped:\n\tdefault:\n\t\tclose(r.stopped)\n\t}\n}\n\n// Stopped checks whether the reloader is stopped.\nfunc (r *resolver) Stopped() bool {\n\tselect {\n\tcase <-r.stopped:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (r *resolver) String() string {\n\tif r == nil {\n\t\treturn \"\"\n\t}\n\n\tr.mux.RLock()\n\tdefer r.mux.RUnlock()\n\n\tb := &bytes.Buffer{}\n\tfmt.Fprintf(b, \"TTL %v\\n\", r.ttl)\n\tfmt.Fprintf(b, \"Reload %v\\n\", r.period)\n\tfmt.Fprintf(b, \"Domain %v\\n\", r.domain)\n\tfor i := range r.servers {\n\t\tfmt.Fprintln(b, r.servers[i])\n\t}\n\treturn b.String()\n}\n\ntype resolverCacheKey string\n\n// newResolverCacheKey generates resolver cache key from question of dns query.\nfunc newResolverCacheKey(q *dns.Question) resolverCacheKey {\n\tif q == nil {\n\t\treturn \"\"\n\t}\n\tkey := fmt.Sprintf(\"%s%s.%s\", q.Name, dns.Class(q.Qclass).String(), dns.Type(q.Qtype).String())\n\treturn resolverCacheKey(key)\n}\n\ntype resolverCacheItem struct {\n\tmr  *dns.Msg\n\tts  int64\n\tttl time.Duration\n}\n\ntype resolverCache struct {\n\tm sync.Map\n}\n\nfunc newResolverCache(ttl time.Duration) *resolverCache {\n\treturn &resolverCache{}\n}\n\nfunc (rc *resolverCache) loadCache(key resolverCacheKey) *dns.Msg {\n\tv, ok := rc.m.Load(key)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\titem, ok := v.(*resolverCacheItem)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\telapsed := time.Since(time.Unix(item.ts, 0))\n\tif item.ttl > 0 && elapsed > item.ttl {\n\t\trc.m.Delete(key)\n\t\treturn nil\n\t}\n\tfor _, rr := range item.mr.Answer {\n\t\tif elapsed > time.Duration(rr.Header().Ttl)*time.Second {\n\t\t\trc.m.Delete(key)\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tif Debug {\n\t\tlog.Logf(\"[resolver] cache hit %s\", key)\n\t}\n\n\treturn item.mr.Copy()\n}\n\nfunc (rc *resolverCache) storeCache(key resolverCacheKey, mr *dns.Msg, ttl time.Duration) {\n\tif key == \"\" || mr == nil || ttl < 0 {\n\t\treturn\n\t}\n\n\trc.m.Store(key, &resolverCacheItem{\n\t\tmr:  mr.Copy(),\n\t\tts:  time.Now().Unix(),\n\t\tttl: ttl,\n\t})\n\tif Debug {\n\t\tlog.Logf(\"[resolver] cache store %s\", key)\n\t}\n}\n\n// Exchanger is an interface for DNS synchronous query.\ntype Exchanger interface {\n\tExchange(ctx context.Context, query []byte) ([]byte, error)\n}\n\ntype exchangerOptions struct {\n\tchain   *Chain\n\ttimeout time.Duration\n}\n\n// ExchangerOption allows a common way to set Exchanger options.\ntype ExchangerOption func(opts *exchangerOptions)\n\n// ChainExchangerOption sets the chain for Exchanger.\nfunc ChainExchangerOption(chain *Chain) ExchangerOption {\n\treturn func(opts *exchangerOptions) {\n\t\topts.chain = chain\n\t}\n}\n\n// TimeoutExchangerOption sets the timeout for Exchanger.\nfunc TimeoutExchangerOption(timeout time.Duration) ExchangerOption {\n\treturn func(opts *exchangerOptions) {\n\t\topts.timeout = timeout\n\t}\n}\n\ntype dnsExchanger struct {\n\taddr    string\n\toptions exchangerOptions\n}\n\n// NewDNSExchanger creates a DNS over UDP Exchanger\nfunc NewDNSExchanger(addr string, opts ...ExchangerOption) Exchanger {\n\tvar options exchangerOptions\n\tfor _, opt := range opts {\n\t\topt(&options)\n\t}\n\n\tif _, port, _ := net.SplitHostPort(addr); port == \"\" {\n\t\taddr = net.JoinHostPort(addr, \"53\")\n\t}\n\n\treturn &dnsExchanger{\n\t\taddr:    addr,\n\t\toptions: options,\n\t}\n}\n\nfunc (ex *dnsExchanger) Exchange(ctx context.Context, query []byte) ([]byte, error) {\n\tt := time.Now()\n\tc, err := ex.options.chain.DialContext(ctx,\n\t\t\"udp\", ex.addr,\n\t\tTimeoutChainOption(ex.options.timeout),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc.SetDeadline(time.Now().Add(ex.options.timeout - time.Since(t)))\n\tdefer c.Close()\n\n\tconn := &dns.Conn{\n\t\tConn:    c,\n\t\tUDPSize: 1024,\n\t}\n\tif _, err = conn.Write(query); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmr, err := conn.ReadMsg()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn mr.Pack()\n}\n\ntype dnsTCPExchanger struct {\n\taddr    string\n\toptions exchangerOptions\n}\n\n// NewDNSTCPExchanger creates a DNS over TCP Exchanger\nfunc NewDNSTCPExchanger(addr string, opts ...ExchangerOption) Exchanger {\n\tvar options exchangerOptions\n\tfor _, opt := range opts {\n\t\topt(&options)\n\t}\n\n\tif _, port, _ := net.SplitHostPort(addr); port == \"\" {\n\t\taddr = net.JoinHostPort(addr, \"53\")\n\t}\n\n\treturn &dnsTCPExchanger{\n\t\taddr:    addr,\n\t\toptions: options,\n\t}\n}\n\nfunc (ex *dnsTCPExchanger) Exchange(ctx context.Context, query []byte) ([]byte, error) {\n\tt := time.Now()\n\tc, err := ex.options.chain.DialContext(ctx,\n\t\t\"tcp\", ex.addr,\n\t\tTimeoutChainOption(ex.options.timeout),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc.SetDeadline(time.Now().Add(ex.options.timeout - time.Since(t)))\n\tdefer c.Close()\n\n\tconn := &dns.Conn{\n\t\tConn: c,\n\t}\n\tif _, err = conn.Write(query); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmr, err := conn.ReadMsg()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn mr.Pack()\n}\n\ntype dotExchanger struct {\n\taddr      string\n\ttlsConfig *tls.Config\n\toptions   exchangerOptions\n}\n\n// NewDoTExchanger creates a DNS over TLS Exchanger\nfunc NewDoTExchanger(addr string, tlsConfig *tls.Config, opts ...ExchangerOption) Exchanger {\n\tvar options exchangerOptions\n\tfor _, opt := range opts {\n\t\topt(&options)\n\t}\n\n\tif _, port, _ := net.SplitHostPort(addr); port == \"\" {\n\t\taddr = net.JoinHostPort(addr, \"53\")\n\t}\n\n\tif tlsConfig == nil {\n\t\ttlsConfig = &tls.Config{\n\t\t\tInsecureSkipVerify: true,\n\t\t}\n\t}\n\treturn &dotExchanger{\n\t\taddr:      addr,\n\t\ttlsConfig: tlsConfig,\n\t\toptions:   options,\n\t}\n}\n\nfunc (ex *dotExchanger) dial(ctx context.Context, network, address string) (conn net.Conn, err error) {\n\tconn, err = ex.options.chain.DialContext(ctx,\n\t\tnetwork, address,\n\t\tTimeoutChainOption(ex.options.timeout),\n\t)\n\tif err != nil {\n\t\treturn\n\t}\n\tconn = tls.Client(conn, ex.tlsConfig)\n\n\treturn\n}\n\nfunc (ex *dotExchanger) Exchange(ctx context.Context, query []byte) ([]byte, error) {\n\tt := time.Now()\n\tc, err := ex.dial(ctx, \"tcp\", ex.addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc.SetDeadline(time.Now().Add(ex.options.timeout - time.Since(t)))\n\tdefer c.Close()\n\n\tconn := &dns.Conn{\n\t\tConn: c,\n\t}\n\tif _, err = conn.Write(query); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmr, err := conn.ReadMsg()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn mr.Pack()\n}\n\ntype dohExchanger struct {\n\tendpoint *url.URL\n\tclient   *http.Client\n\toptions  exchangerOptions\n}\n\n// NewDoHExchanger creates a DNS over HTTPS Exchanger\nfunc NewDoHExchanger(urlStr *url.URL, tlsConfig *tls.Config, opts ...ExchangerOption) Exchanger {\n\tvar options exchangerOptions\n\tfor _, opt := range opts {\n\t\topt(&options)\n\t}\n\tex := &dohExchanger{\n\t\tendpoint: urlStr,\n\t\toptions:  options,\n\t}\n\n\tex.client = &http.Client{\n\t\tTimeout: options.timeout,\n\t\tTransport: &http.Transport{\n\t\t\t// Proxy: ProxyFromEnvironment,\n\t\t\tTLSClientConfig:       tlsConfig,\n\t\t\tForceAttemptHTTP2:     true,\n\t\t\tMaxIdleConns:          100,\n\t\t\tIdleConnTimeout:       90 * time.Second,\n\t\t\tTLSHandshakeTimeout:   options.timeout,\n\t\t\tExpectContinueTimeout: 1 * time.Second,\n\t\t\tDialContext:           ex.dialContext,\n\t\t},\n\t}\n\n\treturn ex\n}\n\nfunc (ex *dohExchanger) dialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\treturn ex.options.chain.DialContext(ctx,\n\t\tnetwork, address,\n\t\tTimeoutChainOption(ex.options.timeout),\n\t)\n}\n\nfunc (ex *dohExchanger) Exchange(ctx context.Context, query []byte) ([]byte, error) {\n\treq, err := http.NewRequestWithContext(ctx, \"POST\", ex.endpoint.String(), bytes.NewBuffer(query))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create an HTTPS request: %s\", err)\n\t}\n\n\t// req.Header.Add(\"Content-Type\", \"application/dns-udpwireformat\")\n\treq.Header.Add(\"Content-Type\", \"application/dns-message\")\n\treq.Host = ex.endpoint.Hostname()\n\n\tclient := ex.client\n\tif client == nil {\n\t\tclient = http.DefaultClient\n\t}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to perform an HTTPS request: %s\", err)\n\t}\n\n\t// Check response status code\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"returned status code %d\", resp.StatusCode)\n\t}\n\n\t// Read wireformat response from the body\n\tbuf, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read the response body: %s\", err)\n\t}\n\n\treturn buf, nil\n}\n"
  },
  {
    "path": "resolver_test.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar dnsTests = []struct {\n\tns   NameServer\n\thost string\n\tpass bool\n}{\n\t{NameServer{Addr: \"1.1.1.1\"}, \"192.168.1.1\", true},\n\t{NameServer{Addr: \"1.1.1.1\"}, \"github\", true},\n\t{NameServer{Addr: \"1.1.1.1\"}, \"github.com\", true},\n\t{NameServer{Addr: \"1.1.1.1:53\"}, \"github.com\", true},\n\t{NameServer{Addr: \"1.1.1.1:53\", Protocol: \"tcp\"}, \"github.com\", true},\n\t{NameServer{Addr: \"1.1.1.1:853\", Protocol: \"tls\"}, \"github.com\", true},\n\t{NameServer{Addr: \"1.1.1.1:853\", Protocol: \"tls\", Hostname: \"example.com\"}, \"github.com\", false},\n\t{NameServer{Addr: \"1.1.1.1:853\", Protocol: \"tls\", Hostname: \"cloudflare-dns.com\"}, \"github.com\", true},\n\t{NameServer{Addr: \"https://cloudflare-dns.com/dns-query\", Protocol: \"https\"}, \"github.com\", true},\n\t{NameServer{Addr: \"https://1.0.0.1/dns-query\", Protocol: \"https\"}, \"github.com\", true},\n\t{NameServer{Addr: \"1.1.1.1:12345\"}, \"github.com\", false},\n\t{NameServer{Addr: \"1.1.1.1:12345\", Protocol: \"tcp\"}, \"github.com\", false},\n\t{NameServer{Addr: \"1.1.1.1:12345\", Protocol: \"tls\"}, \"github.com\", false},\n\t{NameServer{Addr: \"https://1.0.0.1:12345/dns-query\", Protocol: \"https\"}, \"github.com\", false},\n}\n\nfunc dnsResolverRoundtrip(t *testing.T, r Resolver, host string) error {\n\tips, err := r.Resolve(host)\n\tt.Log(host, ips, err)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc TestDNSResolver(t *testing.T) {\n\tfor i, tc := range dnsTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\tns := tc.ns\n\t\t\tt.Log(ns)\n\t\t\tr := NewResolver(0, ns)\n\t\t\tresolv := r.(*resolver)\n\t\t\tresolv.domain = \"com\"\n\t\t\tif err := r.Init(); err != nil {\n\t\t\t\tt.Error(\"got error:\", err)\n\t\t\t}\n\t\t\terr := dnsResolverRoundtrip(t, r, tc.host)\n\t\t\tif err != nil {\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Error(\"got error:\", err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Error(\"should failed\")\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar resolverCacheTests = []struct {\n\tname   string\n\tips    []net.IP\n\tttl    time.Duration\n\tresult []net.IP\n}{\n\t{\"\", nil, 0, nil},\n\t{\"\", []net.IP{net.IPv4(192, 168, 1, 1)}, 0, nil},\n\t{\"\", []net.IP{net.IPv4(192, 168, 1, 1)}, 10 * time.Second, nil},\n\t{\"example.com\", nil, 10 * time.Second, nil},\n\t{\"example.com\", []net.IP{}, 10 * time.Second, nil},\n\t{\"example.com\", []net.IP{net.IPv4(192, 168, 1, 1)}, 0, nil},\n\t{\"example.com\", []net.IP{net.IPv4(192, 168, 1, 1)}, -1, nil},\n\t{\"example.com\", []net.IP{net.IPv4(192, 168, 1, 1)}, 10 * time.Second,\n\t\t[]net.IP{net.IPv4(192, 168, 1, 1)}},\n\t{\"example.com\", []net.IP{net.IPv4(192, 168, 1, 1), net.IPv4(192, 168, 1, 2)}, 10 * time.Second,\n\t\t[]net.IP{net.IPv4(192, 168, 1, 1), net.IPv4(192, 168, 1, 2)}},\n}\n\n/*\nfunc TestResolverCache(t *testing.T) {\n\tisEqual := func(a, b []net.IP) bool {\n\t\tif a == nil && b == nil {\n\t\t\treturn true\n\t\t}\n\n\t\tif a == nil || b == nil || len(a) != len(b) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor i := range a {\n\t\t\tif !a[i].Equal(b[i]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tfor i, tc := range resolverCacheTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\tr := newResolver(tc.ttl)\n\t\t\tr.cache.storeCache(tc.name, tc.ips, tc.ttl)\n\t\t\tips := r.cache.loadCache(tc.name, tc.ttl)\n\n\t\t\tif !isEqual(tc.result, ips) {\n\t\t\t\tt.Error(\"unexpected cache value:\", tc.name, ips, tc.ttl)\n\t\t\t}\n\t\t})\n\t}\n}\n*/\n\nvar resolverReloadTests = []struct {\n\tr io.Reader\n\n\ttimeout time.Duration\n\tttl     time.Duration\n\tdomain  string\n\tperiod  time.Duration\n\tns      *NameServer\n\n\tstopped bool\n}{\n\t{\n\t\tr: nil,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"\"),\n\t},\n\t{\n\t\tr:      bytes.NewBufferString(\"reload 10s\"),\n\t\tperiod: 10 * time.Second,\n\t},\n\t{\n\t\tr:       bytes.NewBufferString(\"timeout 10s\\nreload 10s\\n\"),\n\t\ttimeout: 10 * time.Second,\n\t\tperiod:  10 * time.Second,\n\t},\n\t{\n\t\tr:       bytes.NewBufferString(\"ttl 10s\\ntimeout 10s\\nreload 10s\\n\"),\n\t\ttimeout: 10 * time.Second,\n\t\tperiod:  10 * time.Second,\n\t\tttl:     10 * time.Second,\n\t},\n\t{\n\t\tr:       bytes.NewBufferString(\"domain example.com\\nttl 10s\\ntimeout 10s\\nreload 10s\\n\"),\n\t\ttimeout: 10 * time.Second,\n\t\tperiod:  10 * time.Second,\n\t\tttl:     10 * time.Second,\n\t\tdomain:  \"example.com\",\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"1.1.1.1\"),\n\t\tns: &NameServer{\n\t\t\tAddr: \"1.1.1.1\",\n\t\t},\n\t\tstopped: true,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"\\n# comment\\ntimeout 10s\\nsearch\\nnameserver  \\nnameserver 1.1.1.1 udp\"),\n\t\tns: &NameServer{\n\t\t\tProtocol: \"udp\",\n\t\t\tAddr:     \"1.1.1.1\",\n\t\t},\n\t\ttimeout: 10 * time.Second,\n\t\tstopped: true,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"1.1.1.1 tcp\"),\n\t\tns: &NameServer{\n\t\t\tAddr:     \"1.1.1.1\",\n\t\t\tProtocol: \"tcp\",\n\t\t},\n\t\tstopped: true,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"1.1.1.1:853 tls cloudflare-dns.com\"),\n\t\tns: &NameServer{\n\t\t\tAddr:     \"1.1.1.1:853\",\n\t\t\tProtocol: \"tls\",\n\t\t\tHostname: \"cloudflare-dns.com\",\n\t\t},\n\t\tstopped: true,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"1.1.1.1:853 tls\"),\n\t\tns: &NameServer{\n\t\t\tAddr:     \"1.1.1.1:853\",\n\t\t\tProtocol: \"tls\",\n\t\t},\n\t\tstopped: true,\n\t},\n\t{\n\t\tr:       bytes.NewBufferString(\"1.0.0.1:53 https\"),\n\t\tstopped: true,\n\t},\n\t{\n\t\tr: bytes.NewBufferString(\"https://1.0.0.1/dns-query\"),\n\t\tns: &NameServer{\n\t\t\tAddr:     \"https://1.0.0.1/dns-query\",\n\t\t\tProtocol: \"https\",\n\t\t},\n\t\tstopped: true,\n\t},\n}\n\nfunc TestResolverReload(t *testing.T) {\n\tfor i, tc := range resolverReloadTests {\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\tr := newResolver(0)\n\t\t\tif err := r.Reload(tc.r); err != nil {\n\t\t\t\tt.Error(err)\n\t\t\t}\n\t\t\tt.Log(r.String())\n\t\t\tif r.TTL() != tc.ttl {\n\t\t\t\tt.Errorf(\"ttl value should be %v, got %v\",\n\t\t\t\t\ttc.ttl, r.TTL())\n\t\t\t}\n\t\t\tif r.Period() != tc.period {\n\t\t\t\tt.Errorf(\"period value should be %v, got %v\",\n\t\t\t\t\ttc.period, r.period)\n\t\t\t}\n\t\t\tif r.domain != tc.domain {\n\t\t\t\tt.Errorf(\"domain value should be %v, got %v\",\n\t\t\t\t\ttc.domain, r.domain)\n\t\t\t}\n\n\t\t\tvar ns *NameServer\n\t\t\tif len(r.servers) > 0 {\n\t\t\t\tns = &r.servers[0]\n\t\t\t}\n\n\t\t\tif !compareNameServer(ns, tc.ns) {\n\t\t\t\tt.Errorf(\"nameserver not equal, should be %v, got %v\",\n\t\t\t\t\ttc.ns, r.servers)\n\t\t\t}\n\n\t\t\tif tc.stopped {\n\t\t\t\tr.Stop()\n\t\t\t\tif r.Period() >= 0 {\n\t\t\t\t\tt.Errorf(\"period of the stopped reloader should be minus value\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif r.Stopped() != tc.stopped {\n\t\t\t\tt.Errorf(\"stopped value should be %v, got %v\",\n\t\t\t\t\ttc.stopped, r.Stopped())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc compareNameServer(n1, n2 *NameServer) bool {\n\tif n1 == n2 {\n\t\treturn true\n\t}\n\tif n1 == nil || n2 == nil {\n\t\treturn false\n\t}\n\treturn n1.Addr == n2.Addr &&\n\t\tn1.Hostname == n2.Hostname &&\n\t\tn1.Protocol == n2.Protocol\n}\n"
  },
  {
    "path": "selector.go",
    "content": "package gost\n\nimport (\n\t\"errors\"\n\t\"math/rand\"\n\t\"net\"\n\t\"sort\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\nvar (\n\t// ErrNoneAvailable indicates there is no node available.\n\tErrNoneAvailable = errors.New(\"none node available\")\n)\n\n// NodeSelector as a mechanism to pick nodes and mark their status.\ntype NodeSelector interface {\n\tSelect(nodes []Node, opts ...SelectOption) (Node, error)\n}\n\ntype defaultSelector struct {\n}\n\nfunc (s *defaultSelector) Select(nodes []Node, opts ...SelectOption) (Node, error) {\n\tsopts := SelectOptions{}\n\tfor _, opt := range opts {\n\t\topt(&sopts)\n\t}\n\n\tfor _, filter := range sopts.Filters {\n\t\tnodes = filter.Filter(nodes)\n\t}\n\tif len(nodes) == 0 {\n\t\treturn Node{}, ErrNoneAvailable\n\t}\n\tstrategy := sopts.Strategy\n\tif strategy == nil {\n\t\tstrategy = &RoundStrategy{}\n\t}\n\treturn strategy.Apply(nodes), nil\n}\n\n// SelectOption is the option used when making a select call.\ntype SelectOption func(*SelectOptions)\n\n// SelectOptions is the options for node selection.\ntype SelectOptions struct {\n\tFilters  []Filter\n\tStrategy Strategy\n}\n\n// WithFilter adds a filter function to the list of filters\n// used during the Select call.\nfunc WithFilter(f ...Filter) SelectOption {\n\treturn func(o *SelectOptions) {\n\t\to.Filters = append(o.Filters, f...)\n\t}\n}\n\n// WithStrategy sets the selector strategy\nfunc WithStrategy(s Strategy) SelectOption {\n\treturn func(o *SelectOptions) {\n\t\to.Strategy = s\n\t}\n}\n\n// Strategy is a selection strategy e.g random, round-robin.\ntype Strategy interface {\n\tApply([]Node) Node\n\tString() string\n}\n\n// NewStrategy creates a Strategy by the name s.\nfunc NewStrategy(s string) Strategy {\n\tswitch s {\n\tcase \"random\":\n\t\treturn &RandomStrategy{}\n\tcase \"fifo\":\n\t\treturn &FIFOStrategy{}\n\tcase \"round\":\n\t\tfallthrough\n\tdefault:\n\t\treturn &RoundStrategy{}\n\t}\n}\n\n// RoundStrategy is a strategy for node selector.\n// The node will be selected by round-robin algorithm.\ntype RoundStrategy struct {\n\tcounter uint64\n}\n\n// Apply applies the round-robin strategy for the nodes.\nfunc (s *RoundStrategy) Apply(nodes []Node) Node {\n\tif len(nodes) == 0 {\n\t\treturn Node{}\n\t}\n\n\tn := atomic.AddUint64(&s.counter, 1) - 1\n\treturn nodes[int(n%uint64(len(nodes)))]\n}\n\nfunc (s *RoundStrategy) String() string {\n\treturn \"round\"\n}\n\n// RandomStrategy is a strategy for node selector.\n// The node will be selected randomly.\ntype RandomStrategy struct {\n\tSeed int64\n\trand *rand.Rand\n\tonce sync.Once\n\tmux  sync.Mutex\n}\n\n// Apply applies the random strategy for the nodes.\nfunc (s *RandomStrategy) Apply(nodes []Node) Node {\n\ts.once.Do(func() {\n\t\tseed := s.Seed\n\t\tif seed == 0 {\n\t\t\tseed = time.Now().UnixNano()\n\t\t}\n\t\ts.rand = rand.New(rand.NewSource(seed))\n\t})\n\tif len(nodes) == 0 {\n\t\treturn Node{}\n\t}\n\n\ts.mux.Lock()\n\tr := s.rand.Int()\n\ts.mux.Unlock()\n\n\treturn nodes[r%len(nodes)]\n}\n\nfunc (s *RandomStrategy) String() string {\n\treturn \"random\"\n}\n\n// FIFOStrategy is a strategy for node selector.\n// The node will be selected from first to last,\n// and will stick to the selected node until it is failed.\ntype FIFOStrategy struct{}\n\n// Apply applies the fifo strategy for the nodes.\nfunc (s *FIFOStrategy) Apply(nodes []Node) Node {\n\tif len(nodes) == 0 {\n\t\treturn Node{}\n\t}\n\treturn nodes[0]\n}\n\nfunc (s *FIFOStrategy) String() string {\n\treturn \"fifo\"\n}\n\n// Filter is used to filter a node during the selection process\ntype Filter interface {\n\tFilter([]Node) []Node\n\tString() string\n}\n\n// default options for FailFilter\nconst (\n\tDefaultMaxFails    = 1\n\tDefaultFailTimeout = 30 * time.Second\n)\n\n// FailFilter filters the dead node.\n// A node is marked as dead if its failed count is greater than MaxFails.\ntype FailFilter struct {\n\tMaxFails    int\n\tFailTimeout time.Duration\n}\n\n// Filter filters dead nodes.\nfunc (f *FailFilter) Filter(nodes []Node) []Node {\n\tmaxFails := f.MaxFails\n\tif maxFails == 0 {\n\t\tmaxFails = DefaultMaxFails\n\t}\n\tfailTimeout := f.FailTimeout\n\tif failTimeout == 0 {\n\t\tfailTimeout = DefaultFailTimeout\n\t}\n\n\tif len(nodes) <= 1 || maxFails < 0 {\n\t\treturn nodes\n\t}\n\tnl := []Node{}\n\tfor i := range nodes {\n\t\tmarker := nodes[i].marker.Clone()\n\t\t// log.Logf(\"%s: %d/%d %v/%v\", nodes[i], marker.FailCount(), f.MaxFails, marker.FailTime(), f.FailTimeout)\n\t\tif marker.FailCount() < uint32(maxFails) ||\n\t\t\ttime.Since(time.Unix(marker.FailTime(), 0)) >= failTimeout {\n\t\t\tnl = append(nl, nodes[i])\n\t\t}\n\t}\n\treturn nl\n}\n\nfunc (f *FailFilter) String() string {\n\treturn \"fail\"\n}\n\n// FastestFilter filter the fastest node\ntype FastestFilter struct {\n\tmu sync.Mutex\n\n\tpinger        *net.Dialer\n\tpingResult    map[int]int\n\tpingResultTTL map[int]int64\n\n\ttopCount int\n}\n\nfunc NewFastestFilter(pingTimeOut int, topCount int) *FastestFilter {\n\tif pingTimeOut == 0 {\n\t\tpingTimeOut = 3000 // 3s\n\t}\n\treturn &FastestFilter{\n\t\tmu:            sync.Mutex{},\n\t\tpinger:        &net.Dialer{Timeout: time.Millisecond * time.Duration(pingTimeOut)},\n\t\tpingResult:    make(map[int]int, 0),\n\t\tpingResultTTL: make(map[int]int64, 0),\n\t\ttopCount:      topCount,\n\t}\n}\n\nfunc (f *FastestFilter) Filter(nodes []Node) []Node {\n\t// disabled\n\tif f.topCount == 0 {\n\t\treturn nodes\n\t}\n\n\t// get latency with ttl cache\n\tnow := time.Now().Unix()\n\n\tvar getNodeLatency = func(node Node) int {\n\t\tf.mu.Lock()\n\t\tdefer f.mu.Unlock()\n\n\t\tif f.pingResultTTL[node.ID] < now {\n\t\t\tf.pingResultTTL[node.ID] = now + 5 // tmp\n\n\t\t\t// get latency\n\t\t\tgo func(node Node) {\n\t\t\t\tlatency := f.doTcpPing(node.Addr)\n\t\t\t\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\t\t\t\tttl := 300 - int64(120*r.Float64())\n\n\t\t\t\tf.mu.Lock()\n\t\t\t\tdefer f.mu.Unlock()\n\n\t\t\t\tf.pingResult[node.ID] = latency\n\t\t\t\tf.pingResultTTL[node.ID] = now + ttl\n\t\t\t}(node)\n\t\t}\n\t\treturn f.pingResult[node.ID]\n\t}\n\n\t// sort\n\tsort.Slice(nodes, func(i, j int) bool {\n\t\treturn getNodeLatency(nodes[i]) < getNodeLatency(nodes[j])\n\t})\n\n\t// split\n\tif len(nodes) <= f.topCount {\n\t\treturn nodes\n\t}\n\n\treturn nodes[0:f.topCount]\n}\n\nfunc (f *FastestFilter) String() string {\n\treturn \"fastest\"\n}\n\n// doTcpPing\nfunc (f *FastestFilter) doTcpPing(address string) int {\n\tstart := time.Now()\n\tconn, err := f.pinger.Dial(\"tcp\", address)\n\telapsed := time.Since(start)\n\n\tif err == nil {\n\t\t_ = conn.Close()\n\t}\n\n\tlatency := int(elapsed.Milliseconds())\n\tlog.Logf(\"pingDoTCP: %s, latency: %d\", address, latency)\n\treturn latency\n}\n\n// InvalidFilter filters the invalid node.\n// A node is invalid if its port is invalid (negative or zero value).\ntype InvalidFilter struct{}\n\n// Filter filters invalid nodes.\nfunc (f *InvalidFilter) Filter(nodes []Node) []Node {\n\tnl := []Node{}\n\tfor i := range nodes {\n\t\t_, sport, _ := net.SplitHostPort(nodes[i].Addr)\n\t\tif port, _ := strconv.Atoi(sport); port > 0 {\n\t\t\tnl = append(nl, nodes[i])\n\t\t}\n\t}\n\treturn nl\n}\n\nfunc (f *InvalidFilter) String() string {\n\treturn \"invalid\"\n}\n\ntype failMarker struct {\n\tfailTime  int64\n\tfailCount uint32\n\tmux       sync.RWMutex\n}\n\nfunc (m *failMarker) FailTime() int64 {\n\tif m == nil {\n\t\treturn 0\n\t}\n\n\tm.mux.Lock()\n\tdefer m.mux.Unlock()\n\n\treturn m.failTime\n}\n\nfunc (m *failMarker) FailCount() uint32 {\n\tif m == nil {\n\t\treturn 0\n\t}\n\n\tm.mux.Lock()\n\tdefer m.mux.Unlock()\n\n\treturn m.failCount\n}\n\nfunc (m *failMarker) Mark() {\n\tif m == nil {\n\t\treturn\n\t}\n\n\tm.mux.Lock()\n\tdefer m.mux.Unlock()\n\n\tm.failTime = time.Now().Unix()\n\tm.failCount++\n}\n\nfunc (m *failMarker) Reset() {\n\tif m == nil {\n\t\treturn\n\t}\n\n\tm.mux.Lock()\n\tdefer m.mux.Unlock()\n\n\tm.failTime = 0\n\tm.failCount = 0\n}\n\nfunc (m *failMarker) Clone() *failMarker {\n\tif m == nil {\n\t\treturn nil\n\t}\n\n\tm.mux.RLock()\n\tdefer m.mux.RUnlock()\n\n\tfc, ft := m.failCount, m.failTime\n\n\treturn &failMarker{\n\t\tfailCount: fc,\n\t\tfailTime:  ft,\n\t}\n}\n"
  },
  {
    "path": "selector_test.go",
    "content": "package gost\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestRoundStrategy(t *testing.T) {\n\tnodes := []Node{\n\t\tNode{ID: 1},\n\t\tNode{ID: 2},\n\t\tNode{ID: 3},\n\t}\n\ts := NewStrategy(\"round\")\n\tt.Log(s.String())\n\n\tif node := s.Apply(nil); node.ID > 0 {\n\t\tt.Error(\"unexpected node\", node.String())\n\t}\n\tfor i := 0; i <= len(nodes); i++ {\n\t\tnode := s.Apply(nodes)\n\t\tif node.ID != nodes[i%len(nodes)].ID {\n\t\t\tt.Error(\"unexpected node\", node.String())\n\t\t}\n\t}\n}\n\nfunc TestRandomStrategy(t *testing.T) {\n\tnodes := []Node{\n\t\tNode{ID: 1},\n\t\tNode{ID: 2},\n\t\tNode{ID: 3},\n\t}\n\ts := NewStrategy(\"random\")\n\tt.Log(s.String())\n\n\tif node := s.Apply(nil); node.ID > 0 {\n\t\tt.Error(\"unexpected node\", node.String())\n\t}\n\tfor i := 0; i <= len(nodes); i++ {\n\t\tnode := s.Apply(nodes)\n\t\tif node.ID == 0 {\n\t\t\tt.Error(\"unexpected node\", node.String())\n\t\t}\n\t}\n}\n\nfunc TestFIFOStrategy(t *testing.T) {\n\tnodes := []Node{\n\t\tNode{ID: 1},\n\t\tNode{ID: 2},\n\t\tNode{ID: 3},\n\t}\n\ts := NewStrategy(\"fifo\")\n\tt.Log(s.String())\n\n\tif node := s.Apply(nil); node.ID > 0 {\n\t\tt.Error(\"unexpected node\", node.String())\n\t}\n\tfor i := 0; i <= len(nodes); i++ {\n\t\tnode := s.Apply(nodes)\n\t\tif node.ID != 1 {\n\t\t\tt.Error(\"unexpected node\", node.String())\n\t\t}\n\t}\n}\n\nfunc TestFailFilter(t *testing.T) {\n\tnodes := []Node{\n\t\tNode{ID: 1, marker: &failMarker{}},\n\t\tNode{ID: 2, marker: &failMarker{}},\n\t\tNode{ID: 3, marker: &failMarker{}},\n\t}\n\tfilter := &FailFilter{}\n\tt.Log(filter.String())\n\n\tisEqual := func(a, b []Node) bool {\n\t\tif a == nil && b == nil {\n\t\t\treturn true\n\t\t}\n\t\tif a == nil || b == nil || len(a) != len(b) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor i := range a {\n\t\t\tif a[i].ID != b[i].ID {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\tif v := filter.Filter(nil); v != nil {\n\t\tt.Error(\"unexpected node\", v)\n\t}\n\n\tif v := filter.Filter(nodes); !isEqual(v, nodes) {\n\t\tt.Error(\"unexpected node\", v)\n\t}\n\n\tfilter.MaxFails = -1\n\tnodes[0].MarkDead()\n\tif v := filter.Filter(nodes); !isEqual(v, nodes) {\n\t\tt.Error(\"unexpected node\", v)\n\t}\n\n\tfilter.MaxFails = 0\n\tif v := filter.Filter(nodes); isEqual(v, nodes) {\n\t\tt.Error(\"unexpected node\", v)\n\t}\n\n\tfilter.FailTimeout = 5 * time.Second\n\tif v := filter.Filter(nodes); isEqual(v, nodes) {\n\t\tt.Error(\"unexpected node\", v)\n\t}\n\n\tnodes[1].MarkDead()\n\tnodes[2].MarkDead()\n\tif v := filter.Filter(nodes); len(v) > 0 {\n\t\tt.Error(\"unexpected node\", v)\n\t}\n\n\tfor i := range nodes {\n\t\tnodes[i].ResetDead()\n\t}\n\tif v := filter.Filter(nodes); !isEqual(v, nodes) {\n\t\tt.Error(\"unexpected node\", v)\n\t}\n}\n\nfunc TestFastestFilter(t *testing.T) {\n\tnodes := []Node{\n\t\tNode{ID: 1, marker: &failMarker{}, Addr: \"1.0.0.1:80\"},\n\t\tNode{ID: 2, marker: &failMarker{}, Addr: \"1.0.0.2:80\"},\n\t\tNode{ID: 3, marker: &failMarker{}, Addr: \"1.0.0.3:80\"},\n\t}\n\tfilter := NewFastestFilter(0, 2)\n\n\tvar print = func(nodes []Node) []string {\n\t\tvar rows []string\n\t\tfor _, node := range nodes {\n\t\t\trows = append(rows, node.Addr)\n\t\t}\n\t\treturn rows\n\t}\n\n\tresult1 := filter.Filter(nodes)\n\tt.Logf(\"result 1: %+v\", print(result1))\n\n\ttime.Sleep(time.Second)\n\tresult2 := filter.Filter(nodes)\n\tt.Logf(\"result 2: %+v\", print(result2))\n}\n\nfunc TestSelector(t *testing.T) {\n\tnodes := []Node{\n\t\tNode{ID: 1, marker: &failMarker{}},\n\t\tNode{ID: 2, marker: &failMarker{}},\n\t\tNode{ID: 3, marker: &failMarker{}},\n\t}\n\tselector := &defaultSelector{}\n\tif _, err := selector.Select(nil); err != ErrNoneAvailable {\n\t\tt.Error(\"got unexpected error:\", err)\n\t}\n\n\tif node, _ := selector.Select(nodes); node.ID != 1 {\n\t\tt.Error(\"unexpected node:\", node)\n\t}\n\n\tif node, _ := selector.Select(nodes,\n\t\tWithStrategy(NewStrategy(\"\")),\n\t\tWithFilter(&FailFilter{MaxFails: 1, FailTimeout: 3 * time.Second}),\n\t); node.ID != 1 {\n\t\tt.Error(\"unexpected node:\", node)\n\t}\n}\n"
  },
  {
    "path": "server.go",
    "content": "package gost\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\n// Accepter represents a network endpoint that can accept connection from peer.\ntype Accepter interface {\n\tAccept() (net.Conn, error)\n}\n\n// Server is a proxy server.\ntype Server struct {\n\tListener Listener\n\tHandler  Handler\n\toptions  *ServerOptions\n}\n\n// Init intializes server with given options.\nfunc (s *Server) Init(opts ...ServerOption) {\n\tif s.options == nil {\n\t\ts.options = &ServerOptions{}\n\t}\n\tfor _, opt := range opts {\n\t\topt(s.options)\n\t}\n}\n\n// Addr returns the address of the server\nfunc (s *Server) Addr() net.Addr {\n\treturn s.Listener.Addr()\n}\n\n// Close closes the server\nfunc (s *Server) Close() error {\n\treturn s.Listener.Close()\n}\n\n// Serve serves as a proxy server.\nfunc (s *Server) Serve(h Handler, opts ...ServerOption) error {\n\ts.Init(opts...)\n\n\tif s.Listener == nil {\n\t\tln, err := TCPListener(\"\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ts.Listener = ln\n\t}\n\n\tif h == nil {\n\t\th = s.Handler\n\t}\n\tif h == nil {\n\t\th = HTTPHandler()\n\t}\n\n\tl := s.Listener\n\tvar tempDelay time.Duration\n\tfor {\n\t\tconn, e := l.Accept()\n\t\tif e != nil {\n\t\t\tif ne, ok := e.(net.Error); ok && ne.Temporary() {\n\t\t\t\tif tempDelay == 0 {\n\t\t\t\t\ttempDelay = 5 * time.Millisecond\n\t\t\t\t} else {\n\t\t\t\t\ttempDelay *= 2\n\t\t\t\t}\n\t\t\t\tif max := 1 * time.Second; tempDelay > max {\n\t\t\t\t\ttempDelay = max\n\t\t\t\t}\n\t\t\t\tlog.Logf(\"server: Accept error: %v; retrying in %v\", e, tempDelay)\n\t\t\t\ttime.Sleep(tempDelay)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn e\n\t\t}\n\t\ttempDelay = 0\n\n\t\tgo h.Handle(conn)\n\t}\n}\n\n// Run starts to serve.\nfunc (s *Server) Run() error {\n\treturn s.Serve(s.Handler)\n}\n\n// ServerOptions holds the options for Server.\ntype ServerOptions struct {\n}\n\n// ServerOption allows a common way to set server options.\ntype ServerOption func(opts *ServerOptions)\n\n// Listener is a proxy server listener, just like a net.Listener.\ntype Listener interface {\n\tnet.Listener\n}\n\nfunc transport(rw1, rw2 io.ReadWriter) error {\n\terrc := make(chan error, 1)\n\tgo func() {\n\t\terrc <- copyBuffer(rw1, rw2)\n\t}()\n\n\tgo func() {\n\t\terrc <- copyBuffer(rw2, rw1)\n\t}()\n\n\tif err := <-errc; err != nil && err != io.EOF {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc copyBuffer(dst io.Writer, src io.Reader) error {\n\tbuf := lPool.Get().([]byte)\n\tdefer lPool.Put(buf)\n\n\t_, err := io.CopyBuffer(dst, src, buf)\n\treturn err\n}\n"
  },
  {
    "path": "signal.go",
    "content": "//go:build windows\n// +build windows\n\npackage gost\n\nfunc kcpSigHandler() {}\n"
  },
  {
    "path": "signal_unix.go",
    "content": "//go:build !windows\n// +build !windows\n\npackage gost\n\nimport (\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/xtaci/kcp-go/v5\"\n)\n\nfunc kcpSigHandler() {\n\tch := make(chan os.Signal, 1)\n\tsignal.Notify(ch, syscall.SIGUSR1)\n\n\tfor {\n\t\tswitch <-ch {\n\t\tcase syscall.SIGUSR1:\n\t\t\tlog.Logf(\"[kcp] SNMP: %+v\", kcp.DefaultSnmp.Copy())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "snap/snapcraft.yaml",
    "content": "name: gost\nbase: core20\nversion: '2.12.0'\nsummary: A simple security tunnel written in golang\ndescription: |\n  Project: https://github.com/ginuerzh/gost\n  Wiki: https://v2.gost.run\nicon: gost.png\nwebsite: https://v2.gost.run\nlicense: MIT\n\n\nconfinement: strict\ngrade: stable\n\nparts:\n  gost:\n    plugin: go\n    go-channel: latest/stable\n    source: https://github.com/ginuerzh/gost\n    source-subdir: cmd/gost\n    source-type: git\n    source-tag: v2.12.0\n    build-packages:\n    - gcc\n\napps:\n  gost:\n    command: bin/gost\n    plugs: \n    - home\n    - network\n    - network-bind\n\n"
  },
  {
    "path": "sni.go",
    "content": "// SNI proxy based on https://github.com/bradfitz/tcpproxy\n\npackage gost\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash/crc32\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/asaskevich/govalidator\"\n\tdissector \"github.com/go-gost/tls-dissector\"\n\t\"github.com/go-log/log\"\n)\n\ntype sniConnector struct {\n\thost string\n}\n\n// SNIConnector creates a Connector for SNI proxy client.\nfunc SNIConnector(host string) Connector {\n\treturn &sniConnector{host: host}\n}\n\nfunc (c *sniConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *sniConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\treturn &sniClientConn{addr: address, host: c.host, Conn: conn}, nil\n}\n\ntype sniHandler struct {\n\toptions *HandlerOptions\n}\n\n// SNIHandler creates a server Handler for SNI proxy server.\nfunc SNIHandler(opts ...HandlerOption) Handler {\n\th := &sniHandler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *sniHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *sniHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tbr := bufio.NewReader(conn)\n\thdr, err := br.Peek(dissector.RecordHeaderLen)\n\tif err != nil {\n\t\tlog.Logf(\"[sni] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tconn = &bufferdConn{br: br, Conn: conn}\n\n\tif hdr[0] != dissector.Handshake {\n\t\t// We assume it is an HTTP request\n\t\treq, err := http.ReadRequest(bufio.NewReader(conn))\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[sni] %s -> %s : %s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\treturn\n\t\t}\n\n\t\tif !req.URL.IsAbs() && govalidator.IsDNSName(req.Host) {\n\t\t\treq.URL.Scheme = \"http\"\n\t\t}\n\n\t\thandler := &httpHandler{options: h.options}\n\t\thandler.Init()\n\t\thandler.handleRequest(conn, req)\n\t\treturn\n\t}\n\n\tb, host, err := readClientHelloRecord(conn, \"\", false)\n\tif err != nil {\n\t\tlog.Logf(\"[sni] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\n\t_, sport, _ := net.SplitHostPort(h.options.Host)\n\tif sport == \"\" {\n\t\tsport = \"443\"\n\t}\n\thost = net.JoinHostPort(host, sport)\n\n\tlog.Logf(\"[sni] %s -> %s -> %s\",\n\t\tconn.RemoteAddr(), h.options.Node.String(), host)\n\n\tif !Can(\"tcp\", host, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[sni] %s -> %s : Unauthorized to tcp connect to %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), host)\n\t\treturn\n\t}\n\tif h.options.Bypass.Contains(host) {\n\t\tlog.Log(\"[sni] %s - %s bypass %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), host)\n\t\treturn\n\t}\n\n\tretries := 1\n\tif h.options.Chain != nil && h.options.Chain.Retries > 0 {\n\t\tretries = h.options.Chain.Retries\n\t}\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tvar cc net.Conn\n\tvar route *Chain\n\tfor i := 0; i < retries; i++ {\n\t\troute, err = h.options.Chain.selectRouteFor(host)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[sni] %s -> %s : %s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf := bytes.Buffer{}\n\t\tfmt.Fprintf(&buf, \"%s -> %s -> \",\n\t\t\tconn.RemoteAddr(), h.options.Node.String())\n\t\tfor _, nd := range route.route {\n\t\t\tfmt.Fprintf(&buf, \"%d@%s -> \", nd.ID, nd.String())\n\t\t}\n\t\tfmt.Fprintf(&buf, \"%s\", host)\n\t\tlog.Log(\"[route]\", buf.String())\n\n\t\tcc, err = route.Dial(host,\n\t\t\tTimeoutChainOption(h.options.Timeout),\n\t\t\tHostsChainOption(h.options.Hosts),\n\t\t\tResolverChainOption(h.options.Resolver),\n\t\t)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tlog.Logf(\"[sni] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t}\n\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tif _, err := cc.Write(b); err != nil {\n\t\tlog.Logf(\"[sni] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t}\n\n\tlog.Logf(\"[sni] %s <-> %s\", cc.LocalAddr(), host)\n\ttransport(conn, cc)\n\tlog.Logf(\"[sni] %s >-< %s\", cc.LocalAddr(), host)\n}\n\n// sniSniffConn is a net.Conn that reads from r, fails on Writes,\n// and crashes otherwise.\ntype sniSniffConn struct {\n\tr        io.Reader\n\tnet.Conn // nil; crash on any unexpected use\n}\n\nfunc (c sniSniffConn) Read(p []byte) (int, error) { return c.r.Read(p) }\nfunc (sniSniffConn) Write(p []byte) (int, error)  { return 0, io.EOF }\n\ntype sniClientConn struct {\n\taddr       string\n\thost       string\n\tmutex      sync.Mutex\n\tobfuscated bool\n\tnet.Conn\n}\n\nfunc (c *sniClientConn) Write(p []byte) (int, error) {\n\tb, err := c.obfuscate(p)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif _, err = c.Conn.Write(b); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(p), nil\n}\n\nfunc (c *sniClientConn) obfuscate(p []byte) ([]byte, error) {\n\tif c.host == \"\" {\n\t\treturn p, nil\n\t}\n\tc.mutex.Lock()\n\tdefer c.mutex.Unlock()\n\n\tif c.obfuscated {\n\t\treturn p, nil\n\t}\n\n\tif p[0] == dissector.Handshake {\n\t\tb, host, err := readClientHelloRecord(bytes.NewReader(p), c.host, true)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif Debug {\n\t\t\tlog.Logf(\"[sni] obfuscate: %s -> %s\", c.addr, host)\n\t\t}\n\t\tc.obfuscated = true\n\t\treturn b, nil\n\t}\n\n\tbuf := &bytes.Buffer{}\n\tbr := bufio.NewReader(bytes.NewReader(p))\n\tfor {\n\t\ts, err := br.ReadString('\\n')\n\t\tif err != nil {\n\t\t\tif err != io.EOF {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif s != \"\" {\n\t\t\t\tbuf.Write([]byte(s))\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\t// end of HTTP header\n\t\tif s == \"\\r\\n\" {\n\t\t\tbuf.Write([]byte(s))\n\t\t\t// drain the remain bytes.\n\t\t\tio.Copy(buf, br)\n\t\t\tbreak\n\t\t}\n\n\t\tif strings.HasPrefix(s, \"Host\") {\n\t\t\ts = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(s, \"Host:\"), \"\\r\\n\"))\n\t\t\thost := encodeServerName(s)\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[sni] obfuscate: %s -> %s\", s, c.host)\n\t\t\t}\n\t\t\tbuf.WriteString(\"Host: \" + c.host + \"\\r\\n\")\n\t\t\tbuf.WriteString(\"Gost-Target: \" + host + \"\\r\\n\")\n\t\t\t// drain the remain bytes.\n\t\t\tio.Copy(buf, br)\n\t\t\tbreak\n\t\t}\n\t\tbuf.Write([]byte(s))\n\t}\n\tc.obfuscated = true\n\treturn buf.Bytes(), nil\n}\n\nfunc readClientHelloRecord(r io.Reader, host string, isClient bool) ([]byte, string, error) {\n\trecord, err := dissector.ReadRecord(r)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tclientHello := &dissector.ClientHelloMsg{}\n\tif err := clientHello.Decode(record.Opaque); err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tif !isClient {\n\t\tvar extensions []dissector.Extension\n\n\t\tfor _, ext := range clientHello.Extensions {\n\t\t\tif ext.Type() == 0xFFFE {\n\t\t\t\tb, _ := ext.Encode()\n\t\t\t\tif host, err = decodeServerName(string(b)); err == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\textensions = append(extensions, ext)\n\t\t}\n\t\tclientHello.Extensions = extensions\n\t}\n\n\tfor _, ext := range clientHello.Extensions {\n\t\tif ext.Type() == dissector.ExtServerName {\n\t\t\tsnExtension := ext.(*dissector.ServerNameExtension)\n\t\t\tif host == \"\" {\n\t\t\t\thost = snExtension.Name\n\t\t\t}\n\t\t\tif isClient {\n\t\t\t\te, _ := dissector.NewExtension(0xFFFE, []byte(encodeServerName(snExtension.Name)))\n\t\t\t\tclientHello.Extensions = append(clientHello.Extensions, e)\n\t\t\t}\n\t\t\tif host != \"\" {\n\t\t\t\tsnExtension.Name = host\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\trecord.Opaque, err = clientHello.Encode()\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tbuf := &bytes.Buffer{}\n\tif _, err := record.WriteTo(buf); err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\treturn buf.Bytes(), host, nil\n}\n\nfunc encodeServerName(name string) string {\n\tbuf := &bytes.Buffer{}\n\tbinary.Write(buf, binary.BigEndian, crc32.ChecksumIEEE([]byte(name)))\n\tbuf.WriteString(base64.RawURLEncoding.EncodeToString([]byte(name)))\n\treturn base64.RawURLEncoding.EncodeToString(buf.Bytes())\n}\n\nfunc decodeServerName(s string) (string, error) {\n\tb, err := base64.RawURLEncoding.DecodeString(s)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(b) < 4 {\n\t\treturn \"\", errors.New(\"invalid name\")\n\t}\n\tv, err := base64.RawURLEncoding.DecodeString(string(b[4:]))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif crc32.ChecksumIEEE(v) != binary.BigEndian.Uint32(b[:4]) {\n\t\treturn \"\", errors.New(\"invalid name\")\n\t}\n\treturn string(v), nil\n}\n"
  },
  {
    "path": "sni_test.go",
    "content": "package gost\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc sniRoundtrip(client *Client, server *Server, targetURL string, data []byte) (err error) {\n\tconn, err := client.Dial(server.Addr().String())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn, err = client.Handshake(conn, AddrHandshakeOption(server.Addr().String()))\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer conn.Close()\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn.SetDeadline(time.Now().Add(3 * time.Second))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tconn, err = client.Connect(conn, u.Host)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif u.Scheme == \"https\" {\n\t\tconn = tls.Client(conn,\n\t\t\t&tls.Config{\n\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t// ServerName:         u.Hostname(),\n\t\t\t})\n\t\tu.Scheme = \"http\"\n\t}\n\treq, err := http.NewRequest(\n\t\thttp.MethodGet,\n\t\tu.String(),\n\t\tbytes.NewReader(data),\n\t)\n\tif err != nil {\n\t\treturn\n\t}\n\tif err = req.WriteProxy(conn); err != nil {\n\t\treturn\n\t}\n\tresp, err := http.ReadResponse(bufio.NewReader(conn), req)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn errors.New(resp.Status)\n\t}\n\n\trecv, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif !bytes.Equal(data, recv) {\n\t\treturn fmt.Errorf(\"data not equal\")\n\t}\n\n\treturn\n}\n\nfunc sniProxyRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIProxy(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniProxyRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "sockopts_linux.go",
    "content": "package gost\n\nimport \"syscall\"\n\nfunc setSocketMark(fd int, value int) (e error) {\n\treturn syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_MARK, value)\n}\n\nfunc setSocketInterface(fd int, value string) (e error) {\n\treturn syscall.SetsockoptString(fd, syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, value)\n}\n"
  },
  {
    "path": "sockopts_other.go",
    "content": "//go:build !linux\n// +build !linux\n\npackage gost\n\nfunc setSocketMark(fd int, value int) (e error) {\n\treturn nil\n}\n\nfunc setSocketInterface(fd int, value string) (e error) {\n\treturn nil\n}\n"
  },
  {
    "path": "socks.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-gost/gosocks4\"\n\t\"github.com/go-gost/gosocks5\"\n\t\"github.com/go-log/log\"\n\tsmux \"github.com/xtaci/smux\"\n)\n\nconst (\n\t// MethodTLS is an extended SOCKS5 method with tls encryption support.\n\tMethodTLS uint8 = 0x80\n\t// MethodTLSAuth is an extended SOCKS5 method with tls encryption and authentication support.\n\tMethodTLSAuth uint8 = 0x82\n\t// MethodMux is an extended SOCKS5 method for stream multiplexing.\n\tMethodMux = 0x88\n)\n\nconst (\n\t// CmdMuxBind is an extended SOCKS5 request CMD for\n\t// multiplexing transport with the binding server.\n\tCmdMuxBind uint8 = 0xF2\n\t// CmdUDPTun is an extended SOCKS5 request CMD for UDP over TCP.\n\tCmdUDPTun uint8 = 0xF3\n)\n\nvar (\n\t_ net.PacketConn = (*socks5UDPTunnelConn)(nil)\n)\n\ntype clientSelector struct {\n\tmethods   []uint8\n\tUser      *url.Userinfo\n\tTLSConfig *tls.Config\n}\n\nfunc (selector *clientSelector) Methods() []uint8 {\n\tif Debug {\n\t\tlog.Log(\"[socks5] methods:\", selector.methods)\n\t}\n\treturn selector.methods\n}\n\nfunc (selector *clientSelector) AddMethod(methods ...uint8) {\n\tselector.methods = append(selector.methods, methods...)\n}\n\nfunc (selector *clientSelector) Select(methods ...uint8) (method uint8) {\n\treturn\n}\n\nfunc (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {\n\tif Debug {\n\t\tlog.Log(\"[socks5] method selected:\", method)\n\t}\n\tswitch method {\n\tcase MethodTLS:\n\t\tconn = tls.Client(conn, selector.TLSConfig)\n\n\tcase gosocks5.MethodUserPass, MethodTLSAuth:\n\t\tif method == MethodTLSAuth {\n\t\t\tconn = tls.Client(conn, selector.TLSConfig)\n\t\t}\n\n\t\tvar username, password string\n\t\tif selector.User != nil {\n\t\t\tusername = selector.User.Username()\n\t\t\tpassword, _ = selector.User.Password()\n\t\t}\n\n\t\treq := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password)\n\t\tif err := req.Write(conn); err != nil {\n\t\t\tlog.Log(\"[socks5]\", err)\n\t\t\treturn nil, err\n\t\t}\n\t\tif Debug {\n\t\t\tlog.Log(\"[socks5]\", req)\n\t\t}\n\t\tresp, err := gosocks5.ReadUserPassResponse(conn)\n\t\tif err != nil {\n\t\t\tlog.Log(\"[socks5]\", err)\n\t\t\treturn nil, err\n\t\t}\n\t\tif Debug {\n\t\t\tlog.Log(\"[socks5]\", resp)\n\t\t}\n\t\tif resp.Status != gosocks5.Succeeded {\n\t\t\treturn nil, gosocks5.ErrAuthFailure\n\t\t}\n\tcase gosocks5.MethodNoAcceptable:\n\t\treturn nil, gosocks5.ErrBadMethod\n\t}\n\n\treturn conn, nil\n}\n\ntype serverSelector struct {\n\tmethods []uint8\n\t// Users     []*url.Userinfo\n\tAuthenticator Authenticator\n\tTLSConfig     *tls.Config\n}\n\nfunc (selector *serverSelector) Methods() []uint8 {\n\treturn selector.methods\n}\n\nfunc (selector *serverSelector) AddMethod(methods ...uint8) {\n\tselector.methods = append(selector.methods, methods...)\n}\n\nfunc (selector *serverSelector) Select(methods ...uint8) (method uint8) {\n\tif Debug {\n\t\tlog.Logf(\"[socks5] %d %d %v\", gosocks5.Ver5, len(methods), methods)\n\t}\n\tmethod = gosocks5.MethodNoAuth\n\tfor _, m := range methods {\n\t\tif m == MethodTLS {\n\t\t\tmethod = m\n\t\t\tbreak\n\t\t}\n\t}\n\n\t// when Authenticator is set, auth is mandatory\n\tif selector.Authenticator != nil {\n\t\tif method == gosocks5.MethodNoAuth {\n\t\t\tmethod = gosocks5.MethodUserPass\n\t\t}\n\t\tif method == MethodTLS {\n\t\t\tmethod = MethodTLSAuth\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {\n\tif Debug {\n\t\tlog.Logf(\"[socks5] %d %d\", gosocks5.Ver5, method)\n\t}\n\tswitch method {\n\tcase MethodTLS:\n\t\tconn = tls.Server(conn, selector.TLSConfig)\n\n\tcase gosocks5.MethodUserPass, MethodTLSAuth:\n\t\tif method == MethodTLSAuth {\n\t\t\tconn = tls.Server(conn, selector.TLSConfig)\n\t\t}\n\n\t\treq, err := gosocks5.ReadUserPassRequest(conn)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[socks5] %s - %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\treturn nil, err\n\t\t}\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5] %s - %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), req.String())\n\t\t}\n\n\t\tif selector.Authenticator != nil && !selector.Authenticator.Authenticate(req.Username, req.Password) {\n\t\t\tresp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)\n\t\t\tif err := resp.Write(conn); err != nil {\n\t\t\t\tlog.Logf(\"[socks5] %s - %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[socks5] %s - %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), resp)\n\t\t\t}\n\t\t\tlog.Logf(\"[socks5] %s - %s: proxy authentication required\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t\treturn nil, gosocks5.ErrAuthFailure\n\t\t}\n\n\t\tresp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)\n\t\tif err := resp.Write(conn); err != nil {\n\t\t\tlog.Logf(\"[socks5] %s - %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\treturn nil, err\n\t\t}\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5] %s - %s: %s\", conn.RemoteAddr(), conn.LocalAddr(), resp)\n\t\t}\n\tcase gosocks5.MethodNoAcceptable:\n\t\treturn nil, gosocks5.ErrBadMethod\n\t}\n\n\treturn conn, nil\n}\n\ntype socks5Connector struct {\n\tUser *url.Userinfo\n}\n\n// SOCKS5Connector creates a connector for SOCKS5 proxy client.\n// It accepts an optional auth info for SOCKS5 Username/Password Authentication.\nfunc SOCKS5Connector(user *url.Userinfo) Connector {\n\treturn &socks5Connector{User: user}\n}\n\nfunc (c *socks5Connector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *socks5Connector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\tcnr := &socks5UDPTunConnector{User: c.User}\n\t\treturn cnr.ConnectContext(ctx, conn, network, address, options...)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tuser := opts.User\n\tif user == nil {\n\t\tuser = c.User\n\t}\n\tcc, err := socks5Handshake(conn,\n\t\tselectorSocks5HandshakeOption(opts.Selector),\n\t\tuserSocks5HandshakeOption(user),\n\t\tnoTLSSocks5HandshakeOption(opts.NoTLS),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconn = cc\n\n\thost, port, err := net.SplitHostPort(address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp, _ := strconv.Atoi(port)\n\treq := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{\n\t\tType: gosocks5.AddrDomain,\n\t\tHost: host,\n\t\tPort: uint16(p),\n\t})\n\tif err := req.Write(conn); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5]\", req)\n\t}\n\n\treply, err := gosocks5.ReadReply(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5]\", reply)\n\t}\n\n\tif reply.Rep != gosocks5.Succeeded {\n\t\treturn nil, errors.New(\"Service unavailable\")\n\t}\n\n\treturn conn, nil\n}\n\ntype socks5BindConnector struct {\n\tUser *url.Userinfo\n}\n\n// SOCKS5BindConnector creates a connector for SOCKS5 bind.\n// It accepts an optional auth info for SOCKS5 Username/Password Authentication.\nfunc SOCKS5BindConnector(user *url.Userinfo) Connector {\n\treturn &socks5BindConnector{User: user}\n}\n\nfunc (c *socks5BindConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *socks5BindConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tuser := opts.User\n\tif user == nil {\n\t\tuser = c.User\n\t}\n\tcc, err := socks5Handshake(conn,\n\t\tselectorSocks5HandshakeOption(opts.Selector),\n\t\tuserSocks5HandshakeOption(user),\n\t\tnoTLSSocks5HandshakeOption(opts.NoTLS),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconn = cc\n\n\tladdr, err := net.ResolveTCPAddr(\"tcp\", address)\n\tif err != nil {\n\t\tlog.Log(err)\n\t\treturn nil, err\n\t}\n\n\treq := gosocks5.NewRequest(gosocks5.CmdBind, &gosocks5.Addr{\n\t\tType: gosocks5.AddrIPv4,\n\t\tHost: laddr.IP.String(),\n\t\tPort: uint16(laddr.Port),\n\t})\n\n\tif err := req.Write(conn); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5] bind\\n\", req)\n\t}\n\n\treply, err := gosocks5.ReadReply(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5] bind\\n\", reply)\n\t}\n\n\tif reply.Rep != gosocks5.Succeeded {\n\t\tlog.Logf(\"[socks5] bind on %s failure\", address)\n\t\treturn nil, fmt.Errorf(\"SOCKS5 bind on %s failure\", address)\n\t}\n\tbaddr, err := net.ResolveTCPAddr(\"tcp\", reply.Addr.String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlog.Logf(\"[socks5] bind on %s OK\", baddr)\n\n\treturn &socks5BindConn{Conn: conn, laddr: baddr}, nil\n}\n\ntype socks5MuxBindConnector struct{}\n\n// Socks5MuxBindConnector creates a Connector for SOCKS5 multiplex bind client.\nfunc Socks5MuxBindConnector() Connector {\n\treturn &socks5MuxBindConnector{}\n}\n\nfunc (c *socks5MuxBindConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\n// NOTE: the conn must be *muxBindClientConn.\nfunc (c *socks5MuxBindConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\taccepter, ok := conn.(Accepter)\n\tif !ok {\n\t\treturn nil, errors.New(\"wrong connection type\")\n\t}\n\n\treturn accepter.Accept()\n}\n\ntype socks5MuxBindTransporter struct {\n\tbindAddr     string\n\tsessions     map[string]*muxSession // server addr to session mapping\n\tsessionMutex sync.Mutex\n}\n\n// SOCKS5MuxBindTransporter creates a Transporter for SOCKS5 multiplex bind client.\nfunc SOCKS5MuxBindTransporter(bindAddr string) Transporter {\n\treturn &socks5MuxBindTransporter{\n\t\tbindAddr: bindAddr,\n\t\tsessions: make(map[string]*muxSession),\n\t}\n}\n\nfunc (tr *socks5MuxBindTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = DialTimeout\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tsession, ok := tr.sessions[addr]\n\tif session != nil && session.IsClosed() {\n\t\tdelete(tr.sessions, addr)\n\t\tok = false\n\t}\n\tif !ok {\n\t\tif opts.Chain == nil {\n\t\t\tconn, err = net.DialTimeout(\"tcp\", addr, timeout)\n\t\t} else {\n\t\t\tconn, err = opts.Chain.Dial(addr)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tsession = &muxSession{conn: conn}\n\t\ttr.sessions[addr] = session\n\t}\n\treturn session.conn, nil\n}\n\nfunc (tr *socks5MuxBindTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tsession, ok := tr.sessions[opts.Addr]\n\tif !ok || session.session == nil {\n\t\ts, err := tr.initSession(conn, tr.bindAddr, opts)\n\t\tif err != nil {\n\t\t\tconn.Close()\n\t\t\tdelete(tr.sessions, opts.Addr)\n\t\t\treturn nil, err\n\t\t}\n\t\tsession = s\n\t\ttr.sessions[opts.Addr] = session\n\t}\n\n\treturn &muxBindClientConn{session: session}, nil\n}\n\nfunc (tr *socks5MuxBindTransporter) initSession(conn net.Conn, addr string, opts *HandshakeOptions) (*muxSession, error) {\n\tif opts == nil {\n\t\topts = &HandshakeOptions{}\n\t}\n\n\tcc, err := socks5Handshake(conn,\n\t\tuserSocks5HandshakeOption(opts.User),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconn = cc\n\n\tbindAddr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treq := gosocks5.NewRequest(CmdMuxBind, &gosocks5.Addr{\n\t\tType: gosocks5.AddrIPv4,\n\t\tHost: bindAddr.IP.String(),\n\t\tPort: uint16(bindAddr.Port),\n\t})\n\n\tif err = req.Write(conn); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5] mbind\\n\", req)\n\t}\n\n\treply, err := gosocks5.ReadReply(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5] mbind\\n\", reply)\n\t}\n\n\tif reply.Rep != gosocks5.Succeeded {\n\t\tlog.Logf(\"[socks5] mbind on %s failure\", addr)\n\t\treturn nil, fmt.Errorf(\"SOCKS5 mbind on %s failure\", addr)\n\t}\n\tbaddr, err := net.ResolveTCPAddr(\"tcp\", reply.Addr.String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlog.Logf(\"[socks5] mbind on %s OK\", baddr)\n\n\t// Upgrade connection to multiplex stream.\n\tsession, err := smux.Server(conn, smux.DefaultConfig())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &muxSession{conn: conn, session: session}, nil\n}\n\nfunc (tr *socks5MuxBindTransporter) Multiplex() bool {\n\treturn true\n}\n\ntype socks5UDPConnector struct {\n\tUser *url.Userinfo\n}\n\n// SOCKS5UDPConnector creates a connector for SOCKS5 UDP relay.\n// It accepts an optional auth info for SOCKS5 Username/Password Authentication.\nfunc SOCKS5UDPConnector(user *url.Userinfo) Connector {\n\treturn &socks5UDPConnector{User: user}\n}\n\nfunc (c *socks5UDPConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"udp\", address, options...)\n}\n\nfunc (c *socks5UDPConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"tcp\", \"tcp4\", \"tcp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tuser := opts.User\n\tif user == nil {\n\t\tuser = c.User\n\t}\n\tcc, err := socks5Handshake(conn,\n\t\tselectorSocks5HandshakeOption(opts.Selector),\n\t\tuserSocks5HandshakeOption(user),\n\t\tnoTLSSocks5HandshakeOption(opts.NoTLS),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconn = cc\n\n\ttaddr, err := net.ResolveUDPAddr(\"udp\", address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treq := gosocks5.NewRequest(gosocks5.CmdUdp, &gosocks5.Addr{\n\t\tType: gosocks5.AddrIPv4,\n\t})\n\n\tif err := req.Write(conn); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5] udp\\n\", req)\n\t}\n\n\treply, err := gosocks5.ReadReply(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5] udp\\n\", reply)\n\t}\n\n\tif reply.Rep != gosocks5.Succeeded {\n\t\tlog.Logf(\"[socks5] udp relay failure\")\n\t\treturn nil, fmt.Errorf(\"SOCKS5 udp relay failure\")\n\t}\n\tbaddr, err := net.ResolveUDPAddr(\"udp\", reply.Addr.String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlog.Logf(\"[socks5] udp associate on %s OK\", baddr)\n\n\tuc, err := net.DialUDP(\"udp\", nil, baddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// log.Logf(\"udp laddr:%s, raddr:%s\", uc.LocalAddr(), uc.RemoteAddr())\n\n\treturn &socks5UDPConn{UDPConn: uc, taddr: taddr}, nil\n}\n\ntype socks5UDPTunConnector struct {\n\tUser *url.Userinfo\n}\n\n// SOCKS5UDPTunConnector creates a connector for SOCKS5 UDP-over-TCP relay.\n// It accepts an optional auth info for SOCKS5 Username/Password Authentication.\nfunc SOCKS5UDPTunConnector(user *url.Userinfo) Connector {\n\treturn &socks5UDPTunConnector{User: user}\n}\n\nfunc (c *socks5UDPTunConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"udp\", address, options...)\n}\n\nfunc (c *socks5UDPTunConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"tcp\", \"tcp4\", \"tcp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\tuser := opts.User\n\tif user == nil {\n\t\tuser = c.User\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\ttaddr, _ := net.ResolveUDPAddr(\"udp\", address)\n\treturn newSocks5UDPTunnelConn(conn,\n\t\tnil, taddr,\n\t\tselectorSocks5HandshakeOption(opts.Selector),\n\t\tuserSocks5HandshakeOption(user),\n\t\tnoTLSSocks5HandshakeOption(opts.NoTLS),\n\t)\n}\n\ntype socks4Connector struct{}\n\n// SOCKS4Connector creates a Connector for SOCKS4 proxy client.\nfunc SOCKS4Connector() Connector {\n\treturn &socks4Connector{}\n}\n\nfunc (c *socks4Connector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *socks4Connector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\ttaddr, err := net.ResolveTCPAddr(\"tcp4\", address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(taddr.IP) == 0 {\n\t\ttaddr.IP = net.IPv4zero\n\t}\n\n\treq := gosocks4.NewRequest(gosocks4.CmdConnect,\n\t\t&gosocks4.Addr{\n\t\t\tType: gosocks4.AddrIPv4,\n\t\t\tHost: taddr.IP.String(),\n\t\t\tPort: uint16(taddr.Port),\n\t\t}, nil,\n\t)\n\tif err := req.Write(conn); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Logf(\"[socks4] %s\", req)\n\t}\n\n\treply, err := gosocks4.ReadReply(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Logf(\"[socks4] %s\", reply)\n\t}\n\n\tif reply.Code != gosocks4.Granted {\n\t\treturn nil, fmt.Errorf(\"[socks4] %d\", reply.Code)\n\t}\n\n\treturn conn, nil\n}\n\ntype socks4aConnector struct{}\n\n// SOCKS4AConnector creates a Connector for SOCKS4A proxy client.\nfunc SOCKS4AConnector() Connector {\n\treturn &socks4aConnector{}\n}\n\nfunc (c *socks4aConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *socks4aConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\thost, port, err := net.SplitHostPort(address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tp, _ := strconv.Atoi(port)\n\n\treq := gosocks4.NewRequest(gosocks4.CmdConnect,\n\t\t&gosocks4.Addr{Type: gosocks4.AddrDomain, Host: host, Port: uint16(p)}, nil)\n\tif err := req.Write(conn); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Logf(\"[socks4a] %s\", req)\n\t}\n\n\treply, err := gosocks4.ReadReply(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Logf(\"[socks4a] %s\", reply)\n\t}\n\n\tif reply.Code != gosocks4.Granted {\n\t\treturn nil, fmt.Errorf(\"[socks4a] %d\", reply.Code)\n\t}\n\n\treturn conn, nil\n}\n\ntype socks5Handler struct {\n\tselector *serverSelector\n\toptions  *HandlerOptions\n}\n\n// SOCKS5Handler creates a server Handler for SOCKS5 proxy server.\nfunc SOCKS5Handler(opts ...HandlerOption) Handler {\n\th := &socks5Handler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *socks5Handler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n\n\ttlsConfig := h.options.TLSConfig\n\tif tlsConfig == nil {\n\t\ttlsConfig = DefaultTLSConfig\n\t}\n\th.selector = &serverSelector{ // socks5 server selector\n\t\t// Users:     h.options.Users,\n\t\tAuthenticator: h.options.Authenticator,\n\t\tTLSConfig:     tlsConfig,\n\t}\n\t// methods that socks5 server supported\n\th.selector.AddMethod(\n\t\tgosocks5.MethodNoAuth,\n\t\tgosocks5.MethodUserPass,\n\t\tMethodTLS,\n\t\tMethodTLSAuth,\n\t)\n}\n\nfunc (h *socks5Handler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tconn = gosocks5.ServerConn(conn, h.selector)\n\treq, err := gosocks5.ReadRequest(conn)\n\tif err != nil {\n\t\tlog.Logf(\"[socks5] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\n\tif Debug {\n\t\tlog.Logf(\"[socks5] %s -> %s\\n%s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), req)\n\t}\n\tswitch req.Cmd {\n\tcase gosocks5.CmdConnect:\n\t\th.handleConnect(conn, req)\n\n\tcase gosocks5.CmdBind:\n\t\th.handleBind(conn, req)\n\n\tcase gosocks5.CmdUdp:\n\t\th.handleUDPRelay(conn, req)\n\n\tcase CmdMuxBind:\n\t\th.handleMuxBind(conn, req)\n\n\tcase CmdUDPTun:\n\t\th.handleUDPTunnel(conn, req)\n\n\tdefault:\n\t\tlog.Logf(\"[socks5] %s - %s : Unrecognized request: %d\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), req.Cmd)\n\t}\n}\n\nfunc (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) {\n\thost := req.Addr.String()\n\n\tlog.Logf(\"[socks5] %s -> %s -> %s\",\n\t\tconn.RemoteAddr(), h.options.Node.String(), host)\n\n\tif !Can(\"tcp\", host, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[socks5] %s - %s : Unauthorized to tcp connect to %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), host)\n\t\trep := gosocks5.NewReply(gosocks5.NotAllowed, nil)\n\t\trep.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5] %s <- %s\\n%s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), rep)\n\t\t}\n\t\treturn\n\t}\n\tif h.options.Bypass.Contains(host) {\n\t\tlog.Logf(\"[socks5] %s - %s : Bypass %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), host)\n\t\trep := gosocks5.NewReply(gosocks5.NotAllowed, nil)\n\t\trep.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5] %s <- %s\\n%s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), rep)\n\t\t}\n\t\treturn\n\t}\n\n\tretries := 1\n\tif h.options.Chain != nil && h.options.Chain.Retries > 0 {\n\t\tretries = h.options.Chain.Retries\n\t}\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tvar err error\n\tvar cc net.Conn\n\tvar route *Chain\n\tfor i := 0; i < retries; i++ {\n\t\troute, err = h.options.Chain.selectRouteFor(host)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[socks5] %s -> %s : %s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf := bytes.Buffer{}\n\t\tfmt.Fprintf(&buf, \"%s -> %s -> \",\n\t\t\tconn.RemoteAddr(), h.options.Node.String())\n\t\tfor _, nd := range route.route {\n\t\t\tfmt.Fprintf(&buf, \"%d@%s -> \", nd.ID, nd.String())\n\t\t}\n\t\tfmt.Fprintf(&buf, \"%s\", host)\n\t\tlog.Log(\"[route]\", buf.String())\n\n\t\tcc, err = route.Dial(host,\n\t\t\tTimeoutChainOption(h.options.Timeout),\n\t\t\tHostsChainOption(h.options.Hosts),\n\t\t\tResolverChainOption(h.options.Resolver),\n\t\t)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tlog.Logf(\"[socks5] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t}\n\n\tif err != nil {\n\t\trep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)\n\t\trep.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5] %s <- %s\\n%s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), rep)\n\t\t}\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\trep := gosocks5.NewReply(gosocks5.Succeeded, nil)\n\tif err := rep.Write(conn); err != nil {\n\t\tlog.Logf(\"[socks5] %s <- %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tif Debug {\n\t\tlog.Logf(\"[socks5] %s <- %s\\n%s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), rep)\n\t}\n\tlog.Logf(\"[socks5] %s <-> %s\", conn.RemoteAddr(), host)\n\ttransport(conn, cc)\n\tlog.Logf(\"[socks5] %s >-< %s\", conn.RemoteAddr(), host)\n}\n\nfunc (h *socks5Handler) handleBind(conn net.Conn, req *gosocks5.Request) {\n\taddr := req.Addr.String()\n\n\tlog.Logf(\"[socks5-bind] %s -> %s -> %s\",\n\t\tconn.RemoteAddr(), h.options.Node.String(), addr)\n\n\tif h.options.Chain.IsEmpty() {\n\t\tif !Can(\"rtcp\", addr, h.options.Whitelist, h.options.Blacklist) {\n\t\t\tlog.Logf(\"[socks5-bind] %s - %s : Unauthorized to tcp bind to %s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), addr)\n\t\t\treturn\n\t\t}\n\t\th.bindOn(conn, addr)\n\t\treturn\n\t}\n\n\tcc, err := h.options.Chain.Conn()\n\tif err != nil {\n\t\tlog.Logf(\"[socks5-bind] %s <- %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treply := gosocks5.NewReply(gosocks5.Failure, nil)\n\t\treply.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5-bind] %s <- %s\\n%s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), reply)\n\t\t}\n\t\treturn\n\t}\n\n\t// forward request\n\t// note: this type of request forwarding is defined when starting server,\n\t// so we don't need to authenticate it, as it's as explicit as whitelisting\n\tdefer cc.Close()\n\treq.Write(cc)\n\tlog.Logf(\"[socks5-bind] %s <-> %s\", conn.RemoteAddr(), addr)\n\ttransport(conn, cc)\n\tlog.Logf(\"[socks5-bind] %s >-< %s\", conn.RemoteAddr(), addr)\n}\n\nfunc (h *socks5Handler) bindOn(conn net.Conn, addr string) {\n\tbindAddr, _ := net.ResolveTCPAddr(\"tcp\", addr)\n\tln, err := net.ListenTCP(\"tcp\", bindAddr) // strict mode: if the port already in use, it will return error\n\tif err != nil {\n\t\tlog.Logf(\"[socks5-bind] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\tgosocks5.NewReply(gosocks5.Failure, nil).Write(conn)\n\t\treturn\n\t}\n\n\tsocksAddr := toSocksAddr(ln.Addr())\n\t// Issue: may not reachable when host has multi-interface\n\tsocksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())\n\treply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)\n\tif err := reply.Write(conn); err != nil {\n\t\tlog.Logf(\"[socks5-bind] %s <- %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\tln.Close()\n\t\treturn\n\t}\n\tif Debug {\n\t\tlog.Logf(\"[socks5-bind] %s <- %s\\n%s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), reply)\n\t}\n\tlog.Logf(\"[socks5-bind] %s - %s BIND ON %s OK\",\n\t\tconn.RemoteAddr(), conn.LocalAddr(), socksAddr)\n\n\tvar pconn net.Conn\n\taccept := func() <-chan error {\n\t\terrc := make(chan error, 1)\n\n\t\tgo func() {\n\t\t\tdefer close(errc)\n\t\t\tdefer ln.Close()\n\n\t\t\tc, err := ln.AcceptTCP()\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpconn = c\n\t\t}()\n\n\t\treturn errc\n\t}\n\n\tpc1, pc2 := net.Pipe()\n\tpipe := func() <-chan error {\n\t\terrc := make(chan error, 1)\n\n\t\tgo func() {\n\t\t\tdefer close(errc)\n\t\t\tdefer pc1.Close()\n\n\t\t\terrc <- transport(conn, pc1)\n\t\t}()\n\n\t\treturn errc\n\t}\n\n\tdefer pc2.Close()\n\n\tfor {\n\t\tselect {\n\t\tcase err := <-accept():\n\t\t\tif err != nil || pconn == nil {\n\t\t\t\tlog.Logf(\"[socks5-bind] %s <- %s : %v\", conn.RemoteAddr(), addr, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer pconn.Close()\n\n\t\t\treply := gosocks5.NewReply(gosocks5.Succeeded, toSocksAddr(pconn.RemoteAddr()))\n\t\t\tif err := reply.Write(pc2); err != nil {\n\t\t\t\tlog.Logf(\"[socks5-bind] %s <- %s : %v\", conn.RemoteAddr(), addr, err)\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[socks5-bind] %s <- %s\\n%s\", conn.RemoteAddr(), addr, reply)\n\t\t\t}\n\t\t\tlog.Logf(\"[socks5-bind] %s <- %s PEER %s ACCEPTED\", conn.RemoteAddr(), socksAddr, pconn.RemoteAddr())\n\n\t\t\tlog.Logf(\"[socks5-bind] %s <-> %s\", conn.RemoteAddr(), pconn.RemoteAddr())\n\t\t\tif err = transport(pc2, pconn); err != nil {\n\t\t\t\tlog.Logf(\"[socks5-bind] %s - %s : %v\", conn.RemoteAddr(), pconn.RemoteAddr(), err)\n\t\t\t}\n\t\t\tlog.Logf(\"[socks5-bind] %s >-< %s\", conn.RemoteAddr(), pconn.RemoteAddr())\n\t\t\treturn\n\t\tcase err := <-pipe():\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[socks5-bind] %s -> %s : %v\", conn.RemoteAddr(), addr, err)\n\t\t\t}\n\t\t\tln.Close()\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {\n\taddr := req.Addr.String()\n\tif !Can(\"udp\", addr, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[socks5-udp] Unauthorized to udp connect to %s\", addr)\n\t\trep := gosocks5.NewReply(gosocks5.NotAllowed, nil)\n\t\trep.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5-udp] %s <- %s\\n%s\", conn.RemoteAddr(), req.Addr, rep)\n\t\t}\n\t\treturn\n\t}\n\n\trelay, err := net.ListenUDP(\"udp\", &net.UDPAddr{IP: conn.LocalAddr().(*net.TCPAddr).IP, Port: 0}) // use out-going interface's IP\n\tif err != nil {\n\t\tlog.Logf(\"[socks5-udp] %s -> %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treply := gosocks5.NewReply(gosocks5.Failure, nil)\n\t\treply.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5-udp] %s <- %s\\n%s\", conn.RemoteAddr(), conn.LocalAddr(), reply)\n\t\t}\n\t\treturn\n\t}\n\tdefer relay.Close()\n\n\tsocksAddr := toSocksAddr(relay.LocalAddr())\n\treply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)\n\tif err := reply.Write(conn); err != nil {\n\t\tlog.Logf(\"[socks5-udp] %s <- %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tif Debug {\n\t\tlog.Logf(\"[socks5-udp] %s <- %s\\n%s\", conn.RemoteAddr(), conn.LocalAddr(), reply)\n\t}\n\tlog.Logf(\"[socks5-udp] %s - %s BIND ON %s OK\", conn.RemoteAddr(), conn.LocalAddr(), socksAddr)\n\n\t// serve as standard socks5 udp relay local <-> remote\n\tif h.options.Chain.IsEmpty() {\n\t\tpeer, er := net.ListenUDP(\"udp\", nil)\n\t\tif er != nil {\n\t\t\tlog.Logf(\"[socks5-udp] %s -> %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), er)\n\t\t\treturn\n\t\t}\n\t\tdefer peer.Close()\n\n\t\tgo h.transportUDP(relay, peer)\n\t\tlog.Logf(\"[socks5-udp] %s <-> %s : associated on %s\", conn.RemoteAddr(), conn.LocalAddr(), socksAddr)\n\t\tif err := h.discardClientData(conn); err != nil {\n\t\t\tlog.Logf(\"[socks5-udp] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t}\n\t\tlog.Logf(\"[socks5-udp] %s >-< %s : associated on %s\", conn.RemoteAddr(), conn.LocalAddr(), socksAddr)\n\t\treturn\n\t}\n\n\t// forward udp local <-> tunnel\n\tcc, err := h.options.Chain.Conn()\n\t// connection error\n\tif err != nil {\n\t\tlog.Logf(\"[socks5-udp] %s -> %s : %s\", conn.RemoteAddr(), socksAddr, err)\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tcc, err = socks5Handshake(cc, userSocks5HandshakeOption(h.options.Chain.LastNode().User))\n\tif err != nil {\n\t\tlog.Logf(\"[socks5-udp] %s -> %s : %s\", conn.RemoteAddr(), socksAddr, err)\n\t\treturn\n\t}\n\n\tcc.SetWriteDeadline(time.Now().Add(WriteTimeout))\n\tr := gosocks5.NewRequest(CmdUDPTun, nil)\n\tif err := r.Write(cc); err != nil {\n\t\tlog.Logf(\"[socks5-udp] %s -> %s : %s\", conn.RemoteAddr(), cc.RemoteAddr(), err)\n\t\treturn\n\t}\n\tcc.SetWriteDeadline(time.Time{})\n\tif Debug {\n\t\tlog.Logf(\"[socks5-udp] %s -> %s\\n%s\", conn.RemoteAddr(), cc.RemoteAddr(), r)\n\t}\n\tcc.SetReadDeadline(time.Now().Add(ReadTimeout))\n\treply, err = gosocks5.ReadReply(cc)\n\tif err != nil {\n\t\tlog.Logf(\"[socks5-udp] %s -> %s : %s\", conn.RemoteAddr(), cc.RemoteAddr(), err)\n\t\treturn\n\t}\n\tif Debug {\n\t\tlog.Logf(\"[socks5-udp] %s <- %s\\n%s\", conn.RemoteAddr(), cc.RemoteAddr(), reply)\n\t}\n\n\tif reply.Rep != gosocks5.Succeeded {\n\t\tlog.Logf(\"[socks5-udp] %s <- %s : udp associate failed\", conn.RemoteAddr(), cc.RemoteAddr())\n\t\treturn\n\t}\n\tcc.SetReadDeadline(time.Time{})\n\tlog.Logf(\"[socks5-udp] %s <-> %s [tun: %s]\", conn.RemoteAddr(), socksAddr, reply.Addr)\n\n\tgo h.tunnelClientUDP(relay, cc)\n\tlog.Logf(\"[socks5-udp] %s <-> %s\", conn.RemoteAddr(), socksAddr)\n\tif err := h.discardClientData(conn); err != nil {\n\t\tlog.Logf(\"[socks5-udp] %s - %s : %s\", conn.RemoteAddr(), socksAddr, err)\n\t}\n\tlog.Logf(\"[socks5-udp] %s >-< %s\", conn.RemoteAddr(), socksAddr)\n}\n\nfunc (h *socks5Handler) discardClientData(conn net.Conn) (err error) {\n\tb := make([]byte, tinyBufferSize)\n\tn := 0\n\tfor {\n\t\tn, err = conn.Read(b) // discard any data from tcp connection\n\t\tif err != nil {\n\t\t\tif err == io.EOF { // disconnect normally\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\tbreak // client disconnected\n\t\t}\n\t\tlog.Logf(\"[socks5-udp] read %d UNEXPECTED TCP data from client\", n)\n\t}\n\treturn\n}\n\nfunc (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {\n\terrc := make(chan error, 2)\n\n\tvar clientAddr net.Addr\n\n\tgo func() {\n\t\tb := mPool.Get().([]byte)\n\t\tdefer mPool.Put(b)\n\n\t\tfor {\n\t\t\tn, laddr, err := relay.ReadFrom(b)\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif clientAddr == nil {\n\t\t\t\tclientAddr = laddr\n\t\t\t}\n\t\t\tdgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\traddr, err := net.ResolveUDPAddr(\"udp\", dgram.Header.Addr.String())\n\t\t\tif err != nil {\n\t\t\t\tcontinue // drop silently\n\t\t\t}\n\t\t\tif h.options.Bypass.Contains(raddr.String()) {\n\t\t\t\tlog.Log(\"[socks5-udp] [bypass] write to\", raddr)\n\t\t\t\tcontinue // bypass\n\t\t\t}\n\t\t\tif _, err := peer.WriteTo(dgram.Data, raddr); err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[socks5-udp] %s >>> %s length: %d\", relay.LocalAddr(), raddr, len(dgram.Data))\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tb := mPool.Get().([]byte)\n\t\tdefer mPool.Put(b)\n\n\t\tfor {\n\t\t\tn, raddr, err := peer.ReadFrom(b)\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif clientAddr == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif h.options.Bypass.Contains(raddr.String()) {\n\t\t\t\tlog.Log(\"[socks5-udp] [bypass] read from\", raddr)\n\t\t\t\tcontinue // bypass\n\t\t\t}\n\t\t\tbuf := bytes.Buffer{}\n\t\t\tdgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), b[:n])\n\t\t\tdgram.Write(&buf)\n\t\t\tif _, err := relay.WriteTo(buf.Bytes(), clientAddr); err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[socks5-udp] %s <<< %s length: %d\", relay.LocalAddr(), raddr, len(dgram.Data))\n\t\t\t}\n\t\t}\n\t}()\n\n\tselect {\n\tcase err = <-errc:\n\t\t//log.Println(\"w exit\", err)\n\t}\n\n\treturn\n}\n\nfunc (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error) {\n\terrc := make(chan error, 2)\n\n\tvar clientAddr *net.UDPAddr\n\n\tgo func() {\n\t\tb := mPool.Get().([]byte)\n\t\tdefer mPool.Put(b)\n\n\t\tfor {\n\t\t\tn, addr, err := uc.ReadFromUDP(b)\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[udp-tun] %s <- %s : %s\", cc.RemoteAddr(), addr, err)\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// glog.V(LDEBUG).Infof(\"read udp %d, % #x\", n, b[:n])\n\t\t\t// pipe from relay to tunnel\n\t\t\tdgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif clientAddr == nil {\n\t\t\t\tclientAddr = addr\n\t\t\t}\n\t\t\traddr := dgram.Header.Addr.String()\n\t\t\tif h.options.Bypass.Contains(raddr) {\n\t\t\t\tlog.Log(\"[udp-tun] [bypass] write to\", raddr)\n\t\t\t\tcontinue // bypass\n\t\t\t}\n\t\t\tdgram.Header.Rsv = uint16(len(dgram.Data))\n\t\t\tif err := dgram.Write(cc); err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[udp-tun] %s >>> %s length: %d\", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data))\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\tdgram, err := gosocks5.ReadUDPDatagram(cc)\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[udp-tun] %s -> 0 : %s\", cc.RemoteAddr(), err)\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// pipe from tunnel to relay\n\t\t\tif clientAddr == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\traddr := dgram.Header.Addr.String()\n\t\t\tif h.options.Bypass.Contains(raddr) {\n\t\t\t\tlog.Log(\"[udp-tun] [bypass] read from\", raddr)\n\t\t\t\tcontinue // bypass\n\t\t\t}\n\t\t\tdgram.Header.Rsv = 0\n\n\t\t\tbuf := bytes.Buffer{}\n\t\t\tdgram.Write(&buf)\n\t\t\tif _, err := uc.WriteToUDP(buf.Bytes(), clientAddr); err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[udp-tun] %s <<< %s length: %d\", uc.LocalAddr(), dgram.Header.Addr, len(dgram.Data))\n\t\t\t}\n\t\t}\n\t}()\n\n\tselect {\n\tcase err = <-errc:\n\t}\n\n\treturn\n}\n\nfunc (h *socks5Handler) handleUDPTunnel(conn net.Conn, req *gosocks5.Request) {\n\t// serve tunnel udp, tunnel <-> remote, handle tunnel udp request\n\tif h.options.Chain.IsEmpty() {\n\t\taddr := req.Addr.String()\n\n\t\tif !Can(\"rudp\", addr, h.options.Whitelist, h.options.Blacklist) {\n\t\t\tlog.Logf(\"[socks5] udp-tun Unauthorized to udp bind to %s\", addr)\n\t\t\treturn\n\t\t}\n\n\t\tbindAddr, _ := net.ResolveUDPAddr(\"udp\", addr)\n\t\tuc, err := net.ListenUDP(\"udp\", bindAddr)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[socks5] udp-tun %s -> %s : %s\", conn.RemoteAddr(), req.Addr, err)\n\t\t\treturn\n\t\t}\n\t\tdefer uc.Close()\n\n\t\tsocksAddr := toSocksAddr(uc.LocalAddr())\n\t\tsocksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())\n\t\treply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)\n\t\tif err := reply.Write(conn); err != nil {\n\t\t\tlog.Logf(\"[socks5] udp-tun %s <- %s : %s\", conn.RemoteAddr(), socksAddr, err)\n\t\t\treturn\n\t\t}\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5] udp-tun %s <- %s\\n%s\", conn.RemoteAddr(), socksAddr, reply)\n\t\t}\n\t\tlog.Logf(\"[socks5] udp-tun %s <-> %s\", conn.RemoteAddr(), socksAddr)\n\t\th.tunnelServerUDP(conn, uc)\n\t\tlog.Logf(\"[socks5] udp-tun %s >-< %s\", conn.RemoteAddr(), socksAddr)\n\t\treturn\n\t}\n\n\tcc, err := h.options.Chain.Conn()\n\t// connection error\n\tif err != nil {\n\t\tlog.Logf(\"[socks5] udp-tun %s -> %s : %s\", conn.RemoteAddr(), req.Addr, err)\n\t\treply := gosocks5.NewReply(gosocks5.Failure, nil)\n\t\treply.Write(conn)\n\t\tlog.Logf(\"[socks5] udp-tun %s -> %s\\n%s\", conn.RemoteAddr(), req.Addr, reply)\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tcc, err = socks5Handshake(cc, userSocks5HandshakeOption(h.options.Chain.LastNode().User))\n\tif err != nil {\n\t\tlog.Logf(\"[socks5] udp-tun %s -> %s : %s\", conn.RemoteAddr(), req.Addr, err)\n\t\treturn\n\t}\n\t// tunnel <-> tunnel, direct forwarding\n\t// note: this type of request forwarding is defined when starting server\n\t// so we don't need to authenticate it, as it's as explicit as whitelisting\n\treq.Write(cc)\n\n\tlog.Logf(\"[socks5] udp-tun %s <-> %s\", conn.RemoteAddr(), cc.RemoteAddr())\n\ttransport(conn, cc)\n\tlog.Logf(\"[socks5] udp-tun %s >-< %s\", conn.RemoteAddr(), cc.RemoteAddr())\n}\n\nfunc (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err error) {\n\terrc := make(chan error, 2)\n\n\tgo func() {\n\t\tb := mPool.Get().([]byte)\n\t\tdefer mPool.Put(b)\n\n\t\tfor {\n\t\t\tn, addr, err := pc.ReadFrom(b)\n\t\t\tif err != nil {\n\t\t\t\t// log.Logf(\"[udp-tun] %s : %s\", cc.RemoteAddr(), err)\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif h.options.Bypass.Contains(addr.String()) {\n\t\t\t\tlog.Log(\"[socks5] udp-tun bypass read from\", addr)\n\t\t\t\tcontinue // bypass\n\t\t\t}\n\n\t\t\t// pipe from peer to tunnel\n\t\t\tdgram := gosocks5.NewUDPDatagram(\n\t\t\t\tgosocks5.NewUDPHeader(uint16(n), 0, toSocksAddr(addr)), b[:n])\n\t\t\tif err := dgram.Write(cc); err != nil {\n\t\t\t\tlog.Logf(\"[socks5] udp-tun %s <- %s : %s\", cc.RemoteAddr(), dgram.Header.Addr, err)\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[socks5] udp-tun %s <<< %s length: %d\", cc.RemoteAddr(), dgram.Header.Addr, len(dgram.Data))\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\tdgram, err := gosocks5.ReadUDPDatagram(cc)\n\t\t\tif err != nil {\n\t\t\t\t// log.Logf(\"[udp-tun] %s -> 0 : %s\", cc.RemoteAddr(), err)\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// pipe from tunnel to peer\n\t\t\taddr, err := net.ResolveUDPAddr(\"udp\", dgram.Header.Addr.String())\n\t\t\tif err != nil {\n\t\t\t\tcontinue // drop silently\n\t\t\t}\n\t\t\tif h.options.Bypass.Contains(addr.String()) {\n\t\t\t\tlog.Log(\"[socks5] udp-tun bypass write to\", addr)\n\t\t\t\tcontinue // bypass\n\t\t\t}\n\t\t\tif _, err := pc.WriteTo(dgram.Data, addr); err != nil {\n\t\t\t\tlog.Logf(\"[socks5] udp-tun %s -> %s : %s\", cc.RemoteAddr(), addr, err)\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[socks5] udp-tun %s >>> %s length: %d\", cc.RemoteAddr(), addr, len(dgram.Data))\n\t\t\t}\n\t\t}\n\t}()\n\n\tselect {\n\tcase err = <-errc:\n\t}\n\n\treturn\n}\n\nfunc (h *socks5Handler) handleMuxBind(conn net.Conn, req *gosocks5.Request) {\n\tif h.options.Chain.IsEmpty() {\n\t\taddr := req.Addr.String()\n\t\tif !Can(\"rtcp\", addr, h.options.Whitelist, h.options.Blacklist) {\n\t\t\tlog.Logf(\"Unauthorized to tcp mbind to %s\", addr)\n\t\t\treturn\n\t\t}\n\t\th.muxBindOn(conn, addr)\n\t\treturn\n\t}\n\n\tcc, err := h.options.Chain.Conn()\n\tif err != nil {\n\t\tlog.Logf(\"[socks5] mbind %s <- %s : %s\", conn.RemoteAddr(), req.Addr, err)\n\t\treply := gosocks5.NewReply(gosocks5.Failure, nil)\n\t\treply.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks5] mbind %s <- %s\\n%s\", conn.RemoteAddr(), req.Addr, reply)\n\t\t}\n\t\treturn\n\t}\n\n\t// forward request\n\t// note: this type of request forwarding is defined when starting server,\n\t// so we don't need to authenticate it, as it's as explicit as whitelisting.\n\tdefer cc.Close()\n\treq.Write(cc)\n\tlog.Logf(\"[socks5] mbind %s <-> %s\", conn.RemoteAddr(), cc.RemoteAddr())\n\ttransport(conn, cc)\n\tlog.Logf(\"[socks5] mbind %s >-< %s\", conn.RemoteAddr(), cc.RemoteAddr())\n}\n\nfunc (h *socks5Handler) muxBindOn(conn net.Conn, addr string) {\n\tbindAddr, _ := net.ResolveTCPAddr(\"tcp\", addr)\n\tln, err := net.ListenTCP(\"tcp\", bindAddr) // strict mode: if the port already in use, it will return error\n\tif err != nil {\n\t\tlog.Logf(\"[socks5] mbind %s -> %s : %s\", conn.RemoteAddr(), addr, err)\n\t\tgosocks5.NewReply(gosocks5.Failure, nil).Write(conn)\n\t\treturn\n\t}\n\tdefer ln.Close()\n\n\tsocksAddr := toSocksAddr(ln.Addr())\n\t// Issue: may not reachable when host has multi-interface.\n\tsocksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())\n\treply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)\n\tif err := reply.Write(conn); err != nil {\n\t\tlog.Logf(\"[socks5] mbind %s <- %s : %s\", conn.RemoteAddr(), addr, err)\n\t\treturn\n\t}\n\tif Debug {\n\t\tlog.Logf(\"[socks5] mbind %s <- %s\\n%s\", conn.RemoteAddr(), addr, reply)\n\t}\n\tlog.Logf(\"[socks5] mbind %s - %s BIND ON %s OK\", conn.RemoteAddr(), addr, socksAddr)\n\n\t// Upgrade connection to multiplex stream.\n\ts, err := smux.Client(conn, smux.DefaultConfig())\n\tif err != nil {\n\t\tlog.Logf(\"[socks5] mbind %s - %s : %s\", conn.RemoteAddr(), socksAddr, err)\n\t\treturn\n\t}\n\n\tlog.Logf(\"[socks5] mbind %s <-> %s\", conn.RemoteAddr(), socksAddr)\n\tdefer log.Logf(\"[socks5] mbind %s >-< %s\", conn.RemoteAddr(), socksAddr)\n\n\tsession := &muxSession{\n\t\tconn:    conn,\n\t\tsession: s,\n\t}\n\tdefer session.Close()\n\n\tgo func() {\n\t\tfor {\n\t\t\tconn, err := session.Accept()\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[socks5] mbind accept : %v\", err)\n\t\t\t\tln.Close()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconn.Close() // we do not handle incoming connection.\n\t\t}\n\t}()\n\n\tfor {\n\t\tcc, err := ln.Accept()\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[socks5] mbind %s <- %s : %v\", conn.RemoteAddr(), socksAddr, err)\n\t\t\treturn\n\t\t}\n\t\tlog.Logf(\"[socks5] mbind %s <- %s : ACCEPT peer %s\",\n\t\t\tconn.RemoteAddr(), socksAddr, cc.RemoteAddr())\n\n\t\tgo func(c net.Conn) {\n\t\t\tdefer c.Close()\n\n\t\t\tsc, err := session.GetConn()\n\t\t\tif err != nil {\n\t\t\t\tlog.Logf(\"[socks5] mbind %s <- %s : %s\", conn.RemoteAddr(), socksAddr, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer sc.Close()\n\n\t\t\ttransport(sc, c)\n\t\t}(cc)\n\t}\n}\n\n// TODO: support domain\nfunc toSocksAddr(addr net.Addr) *gosocks5.Addr {\n\thost := \"0.0.0.0\"\n\tport := 0\n\taddrType := gosocks5.AddrIPv4\n\tif addr != nil {\n\t\th, p, _ := net.SplitHostPort(addr.String())\n\t\thost = h\n\t\tport, _ = strconv.Atoi(p)\n\t\tif strings.Count(host, \":\") > 0 {\n\t\t\taddrType = gosocks5.AddrIPv6\n\t\t}\n\t}\n\treturn &gosocks5.Addr{\n\t\tType: addrType,\n\t\tHost: host,\n\t\tPort: uint16(port),\n\t}\n}\n\ntype socks4Handler struct {\n\toptions *HandlerOptions\n}\n\n// SOCKS4Handler creates a server Handler for SOCKS4(A) proxy server.\nfunc SOCKS4Handler(opts ...HandlerOption) Handler {\n\th := &socks4Handler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *socks4Handler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *socks4Handler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\treq, err := gosocks4.ReadRequest(conn)\n\tif err != nil {\n\t\tlog.Logf(\"[socks4] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\n\tif Debug {\n\t\tlog.Logf(\"[socks4] %s -> %s\\n%s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), req)\n\t}\n\n\tswitch req.Cmd {\n\tcase gosocks4.CmdConnect:\n\t\th.handleConnect(conn, req)\n\n\tcase gosocks4.CmdBind:\n\t\tlog.Logf(\"[socks4-bind] %s - %s\", conn.RemoteAddr(), req.Addr)\n\t\th.handleBind(conn, req)\n\n\tdefault:\n\t\tlog.Logf(\"[socks4] %s - %s : Unrecognized request: %d\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), req.Cmd)\n\t}\n}\n\nfunc (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) {\n\taddr := req.Addr.String()\n\n\tlog.Logf(\"[socks4] %s -> %s -> %s\",\n\t\tconn.RemoteAddr(), h.options.Node.String(), addr)\n\n\tif !Can(\"tcp\", addr, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[socks4] %s - %s : Unauthorized to tcp connect to %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), addr)\n\t\trep := gosocks4.NewReply(gosocks4.Rejected, nil)\n\t\trep.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks4] %s <- %s\\n%s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), rep)\n\t\t}\n\t\treturn\n\t}\n\tif h.options.Bypass.Contains(addr) {\n\t\tlog.Log(\"[socks4] %s - %s : Bypass %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), addr)\n\t\trep := gosocks4.NewReply(gosocks4.Rejected, nil)\n\t\trep.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks4] %s <- %s\\n%s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), rep)\n\t\t}\n\t\treturn\n\t}\n\n\tretries := 1\n\tif h.options.Chain != nil && h.options.Chain.Retries > 0 {\n\t\tretries = h.options.Chain.Retries\n\t}\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tvar err error\n\tvar cc net.Conn\n\tvar route *Chain\n\tfor i := 0; i < retries; i++ {\n\t\troute, err = h.options.Chain.selectRouteFor(addr)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[socks4] %s -> %s : %s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf := bytes.Buffer{}\n\t\tfmt.Fprintf(&buf, \"%s -> %s -> \",\n\t\t\tconn.RemoteAddr(), h.options.Node.String())\n\t\tfor _, nd := range route.route {\n\t\t\tfmt.Fprintf(&buf, \"%d@%s -> \", nd.ID, nd.String())\n\t\t}\n\t\tfmt.Fprintf(&buf, \"%s\", addr)\n\t\tlog.Log(\"[route]\", buf.String())\n\n\t\tcc, err = route.Dial(addr,\n\t\t\tTimeoutChainOption(h.options.Timeout),\n\t\t\tHostsChainOption(h.options.Hosts),\n\t\t\tResolverChainOption(h.options.Resolver),\n\t\t)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tlog.Logf(\"[socks4] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t}\n\n\tif err != nil {\n\t\trep := gosocks4.NewReply(gosocks4.Failed, nil)\n\t\trep.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks4] %s <- %s\\n%s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), rep)\n\t\t}\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\trep := gosocks4.NewReply(gosocks4.Granted, nil)\n\tif err := rep.Write(conn); err != nil {\n\t\tlog.Logf(\"[socks4] %s <- %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tif Debug {\n\t\tlog.Logf(\"[socks4] %s <- %s\\n%s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), rep)\n\t}\n\n\tlog.Logf(\"[socks4] %s <-> %s\", conn.RemoteAddr(), addr)\n\ttransport(conn, cc)\n\tlog.Logf(\"[socks4] %s >-< %s\", conn.RemoteAddr(), addr)\n}\n\nfunc (h *socks4Handler) handleBind(conn net.Conn, req *gosocks4.Request) {\n\t// TODO: serve socks4 bind\n\tif h.options.Chain.IsEmpty() {\n\t\treply := gosocks4.NewReply(gosocks4.Rejected, nil)\n\t\treply.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks4-bind] %s <- %s\\n%s\", conn.RemoteAddr(), req.Addr, reply)\n\t\t}\n\t\treturn\n\t}\n\n\tcc, err := h.options.Chain.Conn()\n\t// connection error\n\tif err != nil && err != ErrEmptyChain {\n\t\tlog.Logf(\"[socks4-bind] %s <- %s : %s\", conn.RemoteAddr(), req.Addr, err)\n\t\treply := gosocks4.NewReply(gosocks4.Failed, nil)\n\t\treply.Write(conn)\n\t\tif Debug {\n\t\t\tlog.Logf(\"[socks4-bind] %s <- %s\\n%s\", conn.RemoteAddr(), req.Addr, reply)\n\t\t}\n\t\treturn\n\t}\n\n\tdefer cc.Close()\n\t// forward request\n\treq.Write(cc)\n\n\tlog.Logf(\"[socks4-bind] %s <-> %s\", conn.RemoteAddr(), cc.RemoteAddr())\n\ttransport(conn, cc)\n\tlog.Logf(\"[socks4-bind] %s >-< %s\", conn.RemoteAddr(), cc.RemoteAddr())\n}\n\ntype socks5HandshakeOptions struct {\n\tselector  gosocks5.Selector\n\tuser      *url.Userinfo\n\ttlsConfig *tls.Config\n\tnoTLS     bool\n}\n\ntype socks5HandshakeOption func(opts *socks5HandshakeOptions)\n\nfunc selectorSocks5HandshakeOption(selector gosocks5.Selector) socks5HandshakeOption {\n\treturn func(opts *socks5HandshakeOptions) {\n\t\topts.selector = selector\n\t}\n}\n\nfunc userSocks5HandshakeOption(user *url.Userinfo) socks5HandshakeOption {\n\treturn func(opts *socks5HandshakeOptions) {\n\t\topts.user = user\n\t}\n}\n\nfunc noTLSSocks5HandshakeOption(noTLS bool) socks5HandshakeOption {\n\treturn func(opts *socks5HandshakeOptions) {\n\t\topts.noTLS = noTLS\n\t}\n}\n\nfunc socks5Handshake(conn net.Conn, opts ...socks5HandshakeOption) (net.Conn, error) {\n\toptions := socks5HandshakeOptions{}\n\tfor _, opt := range opts {\n\t\topt(&options)\n\t}\n\tselector := options.selector\n\tif selector == nil {\n\t\tcs := &clientSelector{\n\t\t\tTLSConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t\tUser:      options.user,\n\t\t}\n\t\tcs.AddMethod(\n\t\t\tgosocks5.MethodNoAuth,\n\t\t\tgosocks5.MethodUserPass,\n\t\t)\n\t\tif !options.noTLS {\n\t\t\tcs.AddMethod(MethodTLS)\n\t\t}\n\t\tselector = cs\n\t}\n\n\tcc := gosocks5.ClientConn(conn, selector)\n\tif err := cc.Handleshake(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn cc, nil\n}\n\nfunc getSocks5UDPTunnel(chain *Chain, addr net.Addr) (net.Conn, error) {\n\tc, err := chain.Conn()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnode := chain.LastNode()\n\tconn, err := newSocks5UDPTunnelConn(c,\n\t\taddr, nil,\n\t\tuserSocks5HandshakeOption(node.User),\n\t\tnoTLSSocks5HandshakeOption(node.GetBool(\"notls\")),\n\t)\n\tif err != nil {\n\t\tc.Close()\n\t}\n\treturn conn, err\n}\n\ntype socks5UDPTunnelConn struct {\n\tnet.Conn\n\ttaddr net.Addr\n}\n\nfunc newSocks5UDPTunnelConn(conn net.Conn, raddr, taddr net.Addr, opts ...socks5HandshakeOption) (net.Conn, error) {\n\tcc, err := socks5Handshake(conn, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treq := gosocks5.NewRequest(CmdUDPTun, toSocksAddr(raddr))\n\tif err := req.Write(cc); err != nil {\n\t\treturn nil, err\n\t}\n\tif Debug {\n\t\tlog.Log(\"[socks5] udp-tun\", req)\n\t}\n\n\treply, err := gosocks5.ReadReply(cc)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif Debug {\n\t\tlog.Log(\"[socks5] udp-tun\", reply)\n\t}\n\n\tif reply.Rep != gosocks5.Succeeded {\n\t\treturn nil, errors.New(\"socks5 UDP tunnel failure\")\n\t}\n\n\tbaddr, err := net.ResolveUDPAddr(\"udp\", reply.Addr.String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlog.Logf(\"[socks5] udp-tun associate on %s OK\", baddr)\n\n\treturn &socks5UDPTunnelConn{\n\t\tConn:  cc,\n\t\ttaddr: taddr,\n\t}, nil\n}\n\nfunc (c *socks5UDPTunnelConn) Read(b []byte) (n int, err error) {\n\tn, _, err = c.ReadFrom(b)\n\treturn\n}\n\nfunc (c *socks5UDPTunnelConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {\n\tdgram, err := gosocks5.ReadUDPDatagram(c.Conn)\n\tif err != nil {\n\t\treturn\n\t}\n\tn = copy(b, dgram.Data)\n\taddr, err = net.ResolveUDPAddr(\"udp\", dgram.Header.Addr.String())\n\treturn\n}\n\nfunc (c *socks5UDPTunnelConn) Write(b []byte) (n int, err error) {\n\treturn c.WriteTo(b, c.taddr)\n}\n\nfunc (c *socks5UDPTunnelConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {\n\tdgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(len(b)), 0, toSocksAddr(addr)), b)\n\tif err = dgram.Write(c.Conn); err != nil {\n\t\treturn\n\t}\n\treturn len(b), nil\n}\n\n// socks5BindConn is a connection for SOCKS5 bind client.\ntype socks5BindConn struct {\n\traddr net.Addr\n\tladdr net.Addr\n\tnet.Conn\n\thandshaked   bool\n\thandshakeMux sync.Mutex\n}\n\n// Handshake waits for a peer to connect to the bind port.\nfunc (c *socks5BindConn) Handshake() (err error) {\n\tc.handshakeMux.Lock()\n\tdefer c.handshakeMux.Unlock()\n\n\tif c.handshaked {\n\t\treturn nil\n\t}\n\n\tc.handshaked = true\n\n\trep, err := gosocks5.ReadReply(c.Conn)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"bind: read reply %v\", err)\n\t}\n\tif rep.Rep != gosocks5.Succeeded {\n\t\treturn fmt.Errorf(\"bind: peer connect failure\")\n\t}\n\tc.raddr, err = net.ResolveTCPAddr(\"tcp\", rep.Addr.String())\n\treturn\n}\n\nfunc (c *socks5BindConn) Read(b []byte) (n int, err error) {\n\tif err = c.Handshake(); err != nil {\n\t\treturn\n\t}\n\treturn c.Conn.Read(b)\n}\n\nfunc (c *socks5BindConn) Write(b []byte) (n int, err error) {\n\tif err = c.Handshake(); err != nil {\n\t\treturn\n\t}\n\treturn c.Conn.Write(b)\n}\n\nfunc (c *socks5BindConn) LocalAddr() net.Addr {\n\treturn c.laddr\n}\n\nfunc (c *socks5BindConn) RemoteAddr() net.Addr {\n\treturn c.raddr\n}\n\ntype socks5UDPConn struct {\n\t*net.UDPConn\n\ttaddr net.Addr\n}\n\nfunc (c *socks5UDPConn) Read(b []byte) (n int, err error) {\n\tn, _, err = c.ReadFrom(b)\n\treturn\n}\n\nfunc (c *socks5UDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {\n\tdata := mPool.Get().([]byte)\n\tdefer mPool.Put(data)\n\n\tn, err = c.UDPConn.Read(data)\n\tif err != nil {\n\t\treturn\n\t}\n\tdg, err := gosocks5.ReadUDPDatagram(bytes.NewReader(data[:n]))\n\tif err != nil {\n\t\treturn\n\t}\n\n\tn = copy(b, dg.Data)\n\taddr, err = net.ResolveUDPAddr(\"udp\", dg.Header.Addr.String())\n\n\treturn\n}\n\nfunc (c *socks5UDPConn) Write(b []byte) (int, error) {\n\treturn c.WriteTo(b, c.taddr)\n}\n\nfunc (c *socks5UDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tadr, err := gosocks5.NewAddr(addr.String())\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\th := gosocks5.NewUDPHeader(0, 0, adr)\n\tdg := gosocks5.NewUDPDatagram(h, b)\n\tif err = dg.Write(c.UDPConn); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(b), nil\n}\n\n// a dummy client conn for multiplex bind used by SOCKS5 multiplex bind client connector\ntype muxBindClientConn struct {\n\tnopConn\n\tsession *muxSession\n}\n\nfunc (c *muxBindClientConn) Accept() (net.Conn, error) {\n\treturn c.session.Accept()\n}\n"
  },
  {
    "path": "socks_test.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar socks5ProxyTests = []struct {\n\tcliUser  *url.Userinfo\n\tsrvUsers []*url.Userinfo\n\tpass     bool\n}{\n\t{nil, nil, true},\n\t{nil, []*url.Userinfo{url.User(\"admin\")}, false},\n\t{nil, []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, false},\n\t{url.User(\"admin\"), []*url.Userinfo{url.User(\"test\")}, false},\n\t{url.User(\"admin\"), []*url.Userinfo{url.UserPassword(\"admin\", \"123456\")}, false},\n\t{url.User(\"admin\"), []*url.Userinfo{url.User(\"admin\")}, true},\n\t{url.User(\"admin\"), []*url.Userinfo{url.UserPassword(\"admin\", \"\")}, true},\n\t{url.UserPassword(\"admin\", \"123456\"), nil, true},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.User(\"admin\")}, true},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, false},\n\t{url.UserPassword(\"\", \"123456\"), []*url.Userinfo{url.UserPassword(\"\", \"123456\")}, true},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"admin\", \"123456\")}, true},\n\t{url.UserPassword(\"admin\", \"123456\"), []*url.Userinfo{url.UserPassword(\"user\", \"pass\"), url.UserPassword(\"admin\", \"123456\")}, true},\n}\n\nfunc socks5ProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(serverInfo...)),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5Proxy(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5ProxyRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkSOCKS5Proxy(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSOCKS5ProxyParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks4ProxyRoundtrip(targetURL string, data []byte) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4Proxy(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4ProxyRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc BenchmarkSOCKS4Proxy(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSOCKS4ProxyParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks4aProxyRoundtrip(targetURL string, data []byte) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AProxy(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aProxyRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc BenchmarkSOCKS4AProxy(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSOCKS4AProxyParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5BindRoundtrip(t *testing.T, targetURL string, data []byte) (err error) {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5BindConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer conn.Close()\n\n\tconn, err = client.Connect(conn, \"\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tcc, err := net.Dial(\"tcp\", conn.LocalAddr().String())\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tif err = conn.(*socks5BindConn).Handshake(); err != nil {\n\t\treturn\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn\n\t}\n\thc, err := net.Dial(\"tcp\", u.Host)\n\tif err != nil {\n\t\treturn\n\t}\n\tgo transport(hc, conn)\n\n\treturn httpRoundtrip(cc, targetURL, data)\n}\n\nfunc TestSOCKS5Bind(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tif err := socks5BindRoundtrip(t, httpSrv.URL, sendData); err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks5MuxBindRoundtrip(t *testing.T, targetURL string, data []byte) (err error) {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tl, err := net.Listen(\"tcp\", \"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tbindAddr := l.Addr().String()\n\tl.Close()\n\n\tclient := &Client{\n\t\tConnector:   Socks5MuxBindConnector(),\n\t\tTransporter: SOCKS5MuxBindTransporter(bindAddr),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn muxBindRoundtrip(client, server, bindAddr, targetURL, data)\n}\n\nfunc muxBindRoundtrip(client *Client, server *Server, bindAddr, targetURL string, data []byte) (err error) {\n\tcn, err := client.Dial(server.Addr().String())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tconn, err := client.Handshake(cn,\n\t\tAddrHandshakeOption(server.Addr().String()),\n\t\tUserHandshakeOption(url.UserPassword(\"admin\", \"123456\")),\n\t)\n\tif err != nil {\n\t\tcn.Close()\n\t\treturn err\n\t}\n\tdefer conn.Close()\n\n\tcc, err := net.Dial(\"tcp\", bindAddr)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tconn, err = client.Connect(conn, \"\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn\n\t}\n\thc, err := net.Dial(\"tcp\", u.Host)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer hc.Close()\n\n\tgo transport(hc, conn)\n\n\treturn httpRoundtrip(cc, targetURL, data)\n}\n\nfunc TestSOCKS5MuxBind(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tif err := socks5MuxBindRoundtrip(t, httpSrv.URL, sendData); err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc BenchmarkSOCKS5MuxBind(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tl, err := net.Listen(\"tcp\", \"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tbindAddr := l.Addr().String()\n\tl.Close()\n\n\tclient := &Client{\n\t\tConnector:   Socks5MuxBindConnector(),\n\t\tTransporter: SOCKS5MuxBindTransporter(bindAddr),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := muxBindRoundtrip(client, server, bindAddr, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc socks5UDPRoundtrip(t *testing.T, host string, data []byte) (err error) {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5UDPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn udpRoundtrip(t, client, server, host, data)\n}\n\nfunc TestSOCKS5UDP(t *testing.T) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tif err := socks5UDPRoundtrip(t, udpSrv.Addr(), sendData); err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\n// TODO: fix a probability of timeout.\nfunc BenchmarkSOCKS5UDP(b *testing.B) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5UDPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := udpRoundtrip(b, client, server, udpSrv.Addr(), sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSOCKS5UDPSingleConn(b *testing.B) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5UDPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tdefer conn.Close()\n\n\tconn, err = client.Connect(conn, udpSrv.Addr())\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\troundtrip := func(conn net.Conn, data []byte) error {\n\t\tconn.SetDeadline(time.Now().Add(1 * time.Second))\n\t\tdefer conn.SetDeadline(time.Time{})\n\n\t\tif _, err = conn.Write(data); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\trecv := make([]byte, len(data))\n\t\tif _, err = conn.Read(recv); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif !bytes.Equal(data, recv) {\n\t\t\treturn fmt.Errorf(\"data not equal\")\n\t\t}\n\t\treturn nil\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := roundtrip(conn, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc socks5UDPTunRoundtrip(t *testing.T, host string, data []byte) (err error) {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5UDPTunConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn udpRoundtrip(t, client, server, host, data)\n}\n\nfunc TestSOCKS5UDPTun(t *testing.T) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tif err := socks5UDPTunRoundtrip(t, udpSrv.Addr(), sendData); err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc BenchmarkSOCKS5UDPTun(b *testing.B) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5UDPTunConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := udpRoundtrip(b, client, server, udpSrv.Addr(), sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSOCKS5UDPTunSingleConn(b *testing.B) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5UDPTunConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  SOCKS5Handler(UsersHandlerOption(url.UserPassword(\"admin\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tdefer conn.Close()\n\n\tconn, err = client.Connect(conn, udpSrv.Addr())\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\troundtrip := func(conn net.Conn, data []byte) error {\n\t\tconn.SetDeadline(time.Now().Add(1 * time.Second))\n\t\tdefer conn.SetDeadline(time.Time{})\n\n\t\tif _, err = conn.Write(data); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\trecv := make([]byte, len(data))\n\t\tif _, err = conn.Read(recv); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif !bytes.Equal(data, recv) {\n\t\t\treturn fmt.Errorf(\"data not equal\")\n\t\t}\n\t\treturn nil\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := roundtrip(conn, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "ss.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/go-gost/gosocks5\"\n\t\"github.com/go-log/log\"\n\t\"github.com/shadowsocks/go-shadowsocks2/core\"\n\tss \"github.com/shadowsocks/shadowsocks-go/shadowsocks\"\n)\n\nconst (\n\tmaxSocksAddrLen = 259\n)\n\nvar (\n\t_ net.Conn       = (*shadowConn)(nil)\n\t_ net.PacketConn = (*shadowUDPPacketConn)(nil)\n)\n\ntype shadowConnector struct {\n\tcipher core.Cipher\n}\n\n// ShadowConnector creates a Connector for shadowsocks proxy client.\n// It accepts an optional cipher info for shadowsocks data encryption/decryption.\nfunc ShadowConnector(info *url.Userinfo) Connector {\n\treturn &shadowConnector{\n\t\tcipher: initShadowCipher(info),\n\t}\n}\n\nfunc (c *shadowConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *shadowConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tsocksAddr, err := gosocks5.NewAddr(address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trawaddr := sPool.Get().([]byte)\n\tdefer sPool.Put(rawaddr)\n\n\tn, err := socksAddr.Encode(rawaddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tif c.cipher != nil {\n\t\tconn = c.cipher.StreamConn(conn)\n\t}\n\n\tsc := &shadowConn{\n\t\tConn: conn,\n\t}\n\n\t// write the addr at once.\n\tif opts.NoDelay {\n\t\tif _, err := sc.Write(rawaddr[:n]); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tsc.wbuf.Write(rawaddr[:n]) // cache the header\n\t}\n\n\treturn sc, nil\n}\n\ntype shadowHandler struct {\n\tcipher  core.Cipher\n\toptions *HandlerOptions\n}\n\n// ShadowHandler creates a server Handler for shadowsocks proxy server.\nfunc ShadowHandler(opts ...HandlerOption) Handler {\n\th := &shadowHandler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *shadowHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n\tif len(h.options.Users) > 0 {\n\t\th.cipher = initShadowCipher(h.options.Users[0])\n\t}\n}\n\nfunc (h *shadowHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tif h.cipher != nil {\n\t\tconn = &shadowConn{\n\t\t\tConn: h.cipher.StreamConn(conn),\n\t\t}\n\t}\n\n\tconn.SetReadDeadline(time.Now().Add(ReadTimeout))\n\n\taddr, err := readSocksAddr(conn)\n\tif err != nil {\n\t\tlog.Logf(\"[ss] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\treturn\n\t}\n\n\tconn.SetReadDeadline(time.Time{})\n\n\thost := addr.String()\n\tlog.Logf(\"[ss] %s -> %s\",\n\t\tconn.RemoteAddr(), host)\n\n\tif !Can(\"tcp\", host, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[ss] %s - %s : Unauthorized to tcp connect to %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), host)\n\t\treturn\n\t}\n\n\tif h.options.Bypass.Contains(host) {\n\t\tlog.Logf(\"[ss] %s - %s : Bypass %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), host)\n\t\treturn\n\t}\n\n\tretries := 1\n\tif h.options.Chain != nil && h.options.Chain.Retries > 0 {\n\t\tretries = h.options.Chain.Retries\n\t}\n\tif h.options.Retries > 0 {\n\t\tretries = h.options.Retries\n\t}\n\n\tvar cc net.Conn\n\tvar route *Chain\n\tfor i := 0; i < retries; i++ {\n\t\troute, err = h.options.Chain.selectRouteFor(host)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[ss] %s -> %s : %s\",\n\t\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf := bytes.Buffer{}\n\t\tfmt.Fprintf(&buf, \"%s -> %s -> \",\n\t\t\tconn.RemoteAddr(), h.options.Node.String())\n\t\tfor _, nd := range route.route {\n\t\t\tfmt.Fprintf(&buf, \"%d@%s -> \", nd.ID, nd.String())\n\t\t}\n\t\tfmt.Fprintf(&buf, \"%s\", host)\n\t\tlog.Log(\"[route]\", buf.String())\n\n\t\tcc, err = route.Dial(host,\n\t\t\tTimeoutChainOption(h.options.Timeout),\n\t\t\tHostsChainOption(h.options.Hosts),\n\t\t\tResolverChainOption(h.options.Resolver),\n\t\t)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t\tlog.Logf(\"[ss] %s -> %s : %s\",\n\t\t\tconn.RemoteAddr(), conn.LocalAddr(), err)\n\t}\n\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tlog.Logf(\"[ss] %s <-> %s\", conn.RemoteAddr(), host)\n\ttransport(conn, cc)\n\tlog.Logf(\"[ss] %s >-< %s\", conn.RemoteAddr(), host)\n}\n\ntype shadowUDPConnector struct {\n\tcipher core.Cipher\n}\n\n// ShadowUDPConnector creates a Connector for shadowsocks UDP client.\n// It accepts an optional cipher info for shadowsocks data encryption/decryption.\nfunc ShadowUDPConnector(info *url.Userinfo) Connector {\n\treturn &shadowUDPConnector{\n\t\tcipher: initShadowCipher(info),\n\t}\n}\n\nfunc (c *shadowUDPConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"udp\", address, options...)\n}\n\nfunc (c *shadowUDPConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"tcp\", \"tcp4\", \"tcp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\ttaddr, _ := net.ResolveUDPAddr(network, address)\n\tif taddr == nil {\n\t\ttaddr = &net.UDPAddr{}\n\t}\n\n\tpc, ok := conn.(net.PacketConn)\n\tif ok {\n\t\tif c.cipher != nil {\n\t\t\tpc = c.cipher.PacketConn(pc)\n\t\t}\n\n\t\treturn &shadowUDPPacketConn{\n\t\t\tPacketConn: pc,\n\t\t\traddr:      conn.RemoteAddr(),\n\t\t\ttaddr:      taddr,\n\t\t}, nil\n\t}\n\n\tif c.cipher != nil {\n\t\tconn = &shadowConn{\n\t\t\tConn: c.cipher.StreamConn(conn),\n\t\t}\n\t}\n\n\treturn &socks5UDPTunnelConn{\n\t\tConn:  conn,\n\t\ttaddr: taddr,\n\t}, nil\n}\n\ntype shadowUDPHandler struct {\n\tcipher  core.Cipher\n\toptions *HandlerOptions\n}\n\n// ShadowUDPHandler creates a server Handler for shadowsocks UDP relay server.\nfunc ShadowUDPHandler(opts ...HandlerOption) Handler {\n\th := &shadowUDPHandler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *shadowUDPHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n\tif len(h.options.Users) > 0 {\n\t\th.cipher = initShadowCipher(h.options.Users[0])\n\t}\n}\n\nfunc (h *shadowUDPHandler) Handle(conn net.Conn) {\n\tdefer conn.Close()\n\n\tvar cc net.PacketConn\n\tc, err := h.options.Chain.DialContext(context.Background(), \"udp\", \"\")\n\tif err != nil {\n\t\tlog.Logf(\"[ssu] %s: %s\", conn.LocalAddr(), err)\n\t\treturn\n\t}\n\tvar ok bool\n\tcc, ok = c.(net.PacketConn)\n\tif !ok {\n\t\tlog.Logf(\"[ssu] %s: not a packet connection\", conn.LocalAddr())\n\t\treturn\n\t}\n\n\tdefer cc.Close()\n\n\tpc, ok := conn.(net.PacketConn)\n\tif ok {\n\t\tif h.cipher != nil {\n\t\t\tpc = h.cipher.PacketConn(pc)\n\t\t}\n\t\tlog.Logf(\"[ssu] %s <-> %s\", conn.RemoteAddr(), conn.LocalAddr())\n\t\th.transportPacket(pc, cc)\n\t\tlog.Logf(\"[ssu] %s >-< %s\", conn.RemoteAddr(), conn.LocalAddr())\n\t\treturn\n\t}\n\n\tif h.cipher != nil {\n\t\tconn = &shadowConn{\n\t\t\tConn: h.cipher.StreamConn(conn),\n\t\t}\n\t}\n\n\tlog.Logf(\"[ssu] %s <-> %s\", conn.RemoteAddr(), conn.LocalAddr())\n\th.transportUDP(conn, cc)\n\tlog.Logf(\"[ssu] %s >-< %s\", conn.RemoteAddr(), conn.LocalAddr())\n}\n\nfunc (h *shadowUDPHandler) transportPacket(conn, cc net.PacketConn) (err error) {\n\terrc := make(chan error, 1)\n\tvar clientAddr net.Addr\n\n\tgo func() {\n\t\tfor {\n\t\t\terr := func() error {\n\t\t\t\tb := mPool.Get().([]byte)\n\t\t\t\tdefer mPool.Put(b)\n\n\t\t\t\tn, addr, err := conn.ReadFrom(b)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif clientAddr == nil {\n\t\t\t\t\tclientAddr = addr\n\t\t\t\t}\n\n\t\t\t\tr := bytes.NewBuffer(b[:n])\n\t\t\t\tsaddr, err := readSocksAddr(r)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\ttaddr, err := net.ResolveUDPAddr(\"udp\", saddr.String())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif Debug {\n\t\t\t\t\tlog.Logf(\"[ssu] %s >>> %s length: %d\", addr, taddr, r.Len())\n\t\t\t\t}\n\t\t\t\t_, err = cc.WriteTo(r.Bytes(), taddr)\n\t\t\t\treturn err\n\t\t\t}()\n\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\terr := func() error {\n\t\t\t\tb := mPool.Get().([]byte)\n\t\t\t\tdefer mPool.Put(b)\n\n\t\t\t\tn, addr, err := cc.ReadFrom(b)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tif clientAddr == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\tif Debug {\n\t\t\t\t\tlog.Logf(\"[ssu] %s <<< %s length: %d\", clientAddr, addr, n)\n\t\t\t\t}\n\n\t\t\t\tdgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(addr)), b[:n])\n\t\t\t\tbuf := bytes.Buffer{}\n\t\t\t\tif err = dgram.Write(&buf); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\t_, err = conn.WriteTo(buf.Bytes()[3:], clientAddr)\n\t\t\t\treturn err\n\t\t\t}()\n\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\tselect {\n\tcase err = <-errc:\n\t}\n\n\treturn\n}\n\nfunc (h *shadowUDPHandler) transportUDP(conn net.Conn, cc net.PacketConn) error {\n\terrc := make(chan error, 1)\n\n\tgo func() {\n\t\tfor {\n\t\t\ter := func() (err error) {\n\t\t\t\tdgram, err := gosocks5.ReadUDPDatagram(conn)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// log.Logf(\"[ssu] %s - %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif Debug {\n\t\t\t\t\tlog.Logf(\"[ssu] %s >>> %s length: %d\",\n\t\t\t\t\t\tconn.RemoteAddr(), dgram.Header.Addr.String(), len(dgram.Data))\n\t\t\t\t}\n\t\t\t\taddr, err := net.ResolveUDPAddr(\"udp\", dgram.Header.Addr.String())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif h.options.Bypass.Contains(addr.String()) {\n\t\t\t\t\tlog.Log(\"[ssu] bypass\", addr)\n\t\t\t\t\treturn // bypass\n\t\t\t\t}\n\t\t\t\t_, err = cc.WriteTo(dgram.Data, addr)\n\t\t\t\treturn\n\t\t\t}()\n\n\t\t\tif er != nil {\n\t\t\t\terrc <- er\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\ter := func() (err error) {\n\t\t\t\tb := mPool.Get().([]byte)\n\t\t\t\tdefer mPool.Put(b)\n\n\t\t\t\tn, addr, err := cc.ReadFrom(b)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif Debug {\n\t\t\t\t\tlog.Logf(\"[ssu] %s <<< %s length: %d\", conn.RemoteAddr(), addr, n)\n\t\t\t\t}\n\t\t\t\tif h.options.Bypass.Contains(addr.String()) {\n\t\t\t\t\tlog.Log(\"[ssu] bypass\", addr)\n\t\t\t\t\treturn // bypass\n\t\t\t\t}\n\t\t\t\tdgram := gosocks5.NewUDPDatagram(\n\t\t\t\t\tgosocks5.NewUDPHeader(uint16(n), 0, toSocksAddr(addr)), b[:n])\n\t\t\t\tbuf := bytes.Buffer{}\n\t\t\t\tdgram.Write(&buf)\n\t\t\t\t_, err = conn.Write(buf.Bytes())\n\t\t\t\treturn\n\t\t\t}()\n\n\t\t\tif er != nil {\n\t\t\t\terrc <- er\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\terr := <-errc\n\tif err != nil && err == io.EOF {\n\t\terr = nil\n\t}\n\treturn err\n}\n\n// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,\n// we wrap around it to make io.Copy happy.\ntype shadowConn struct {\n\tnet.Conn\n\twbuf bytes.Buffer\n}\n\nfunc (c *shadowConn) Write(b []byte) (n int, err error) {\n\tn = len(b) // force byte length consistent\n\tif c.wbuf.Len() > 0 {\n\t\tc.wbuf.Write(b) // append the data to the cached header\n\t\t_, err = c.Conn.Write(c.wbuf.Bytes())\n\t\tc.wbuf.Reset()\n\t\treturn\n\t}\n\t_, err = c.Conn.Write(b)\n\treturn\n}\n\ntype shadowUDPPacketConn struct {\n\tnet.PacketConn\n\traddr net.Addr\n\ttaddr net.Addr\n}\n\nfunc (c *shadowUDPPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {\n\tbuf := mPool.Get().([]byte)\n\tdefer mPool.Put(buf)\n\n\tbuf[0] = 0\n\tbuf[1] = 0\n\tbuf[2] = 0\n\n\tn, _, err = c.PacketConn.ReadFrom(buf[3:])\n\tif err != nil {\n\t\treturn\n\t}\n\n\tdgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(buf[:n+3]))\n\tif err != nil {\n\t\treturn\n\t}\n\tn = copy(b, dgram.Data)\n\taddr, err = net.ResolveUDPAddr(\"udp\", dgram.Header.Addr.String())\n\n\treturn\n\n}\n\nfunc (c *shadowUDPPacketConn) Read(b []byte) (n int, err error) {\n\tn, _, err = c.ReadFrom(b)\n\treturn\n}\n\nfunc (c *shadowUDPPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {\n\tsa, err := gosocks5.NewAddr(addr.String())\n\tif err != nil {\n\t\treturn\n\t}\n\tvar rawaddr [maxSocksAddrLen]byte\n\tnn, err := sa.Encode(rawaddr[:])\n\tif err != nil {\n\t\treturn\n\t}\n\n\tbuf := mPool.Get().([]byte)\n\tdefer mPool.Put(buf)\n\n\tcopy(buf, rawaddr[:nn])\n\tn = copy(buf[nn:], b)\n\t_, err = c.PacketConn.WriteTo(buf[:n+nn], c.raddr)\n\n\treturn\n}\n\nfunc (c *shadowUDPPacketConn) Write(b []byte) (n int, err error) {\n\treturn c.WriteTo(b, c.taddr)\n}\n\nfunc (c *shadowUDPPacketConn) RemoteAddr() net.Addr {\n\treturn c.raddr\n}\n\ntype shadowCipher struct {\n\tcipher *ss.Cipher\n}\n\nfunc (c *shadowCipher) StreamConn(conn net.Conn) net.Conn {\n\treturn ss.NewConn(conn, c.cipher.Copy())\n}\n\nfunc (c *shadowCipher) PacketConn(conn net.PacketConn) net.PacketConn {\n\treturn ss.NewSecurePacketConn(conn, c.cipher.Copy())\n}\n\nfunc initShadowCipher(info *url.Userinfo) (cipher core.Cipher) {\n\tvar method, password string\n\tif info != nil {\n\t\tmethod = info.Username()\n\t\tpassword, _ = info.Password()\n\t}\n\n\tif method == \"\" || password == \"\" {\n\t\treturn\n\t}\n\n\tcp, _ := ss.NewCipher(method, password)\n\tif cp != nil {\n\t\tcipher = &shadowCipher{cipher: cp}\n\t}\n\tif cipher == nil {\n\t\tvar err error\n\t\tcipher, err = core.PickCipher(method, nil, password)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[ss] %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc readSocksAddr(r io.Reader) (*gosocks5.Addr, error) {\n\taddr := &gosocks5.Addr{}\n\tb := sPool.Get().([]byte)\n\tdefer sPool.Put(b)\n\n\t_, err := io.ReadFull(r, b[:1])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\taddr.Type = b[0]\n\n\tswitch addr.Type {\n\tcase gosocks5.AddrIPv4:\n\t\t_, err = io.ReadFull(r, b[:net.IPv4len])\n\t\taddr.Host = net.IP(b[0:net.IPv4len]).String()\n\tcase gosocks5.AddrIPv6:\n\t\t_, err = io.ReadFull(r, b[:net.IPv6len])\n\t\taddr.Host = net.IP(b[0:net.IPv6len]).String()\n\tcase gosocks5.AddrDomain:\n\t\tif _, err = io.ReadFull(r, b[:1]); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\taddrlen := int(b[0])\n\t\t_, err = io.ReadFull(r, b[:addrlen])\n\t\taddr.Host = string(b[:addrlen])\n\tdefault:\n\t\treturn nil, gosocks5.ErrBadAddrType\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t_, err = io.ReadFull(r, b[:2])\n\taddr.Port = binary.BigEndian.Uint16(b[:2])\n\treturn addr, err\n}\n"
  },
  {
    "path": "ss_test.go",
    "content": "package gost\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc init() {\n\t// ss.Debug = true\n}\n\nvar ssTests = []struct {\n\tclientCipher *url.Userinfo\n\tserverCipher *url.Userinfo\n\tpass         bool\n}{\n\t{nil, nil, true},\n\t{&url.Userinfo{}, &url.Userinfo{}, true},\n\t{url.User(\"abc\"), url.User(\"abc\"), true},\n\t{url.UserPassword(\"abc\", \"def\"), url.UserPassword(\"abc\", \"def\"), true},\n\n\t{url.User(\"aes-128-cfb\"), url.User(\"aes-128-cfb\"), true},\n\t{url.User(\"aes-128-cfb\"), url.UserPassword(\"aes-128-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"aes-128-cfb\", \"123456\"), url.User(\"aes-128-cfb\"), false},\n\t{url.UserPassword(\"aes-128-cfb\", \"123456\"), url.UserPassword(\"aes-128-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"aes-128-cfb\", \"123456\"), url.UserPassword(\"aes-128-cfb\", \"123456\"), true},\n\n\t{url.User(\"aes-192-cfb\"), url.User(\"aes-192-cfb\"), true},\n\t{url.User(\"aes-192-cfb\"), url.UserPassword(\"aes-192-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"aes-192-cfb\", \"123456\"), url.User(\"aes-192-cfb\"), false},\n\t{url.UserPassword(\"aes-192-cfb\", \"123456\"), url.UserPassword(\"aes-192-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"aes-192-cfb\", \"123456\"), url.UserPassword(\"aes-192-cfb\", \"123456\"), true},\n\n\t{url.User(\"aes-256-cfb\"), url.User(\"aes-256-cfb\"), true},\n\t{url.User(\"aes-256-cfb\"), url.UserPassword(\"aes-256-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"aes-256-cfb\", \"123456\"), url.User(\"aes-256-cfb\"), false},\n\t{url.UserPassword(\"aes-256-cfb\", \"123456\"), url.UserPassword(\"aes-256-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"aes-256-cfb\", \"123456\"), url.UserPassword(\"aes-256-cfb\", \"123456\"), true},\n\n\t{url.User(\"aes-128-ctr\"), url.User(\"aes-128-ctr\"), true},\n\t{url.User(\"aes-128-ctr\"), url.UserPassword(\"aes-128-ctr\", \"123456\"), false},\n\t{url.UserPassword(\"aes-128-ctr\", \"123456\"), url.User(\"aes-128-ctr\"), false},\n\t{url.UserPassword(\"aes-128-ctr\", \"123456\"), url.UserPassword(\"aes-128-ctr\", \"abc\"), false},\n\t{url.UserPassword(\"aes-128-ctr\", \"123456\"), url.UserPassword(\"aes-128-ctr\", \"123456\"), true},\n\n\t{url.User(\"aes-192-ctr\"), url.User(\"aes-192-ctr\"), true},\n\t{url.User(\"aes-192-ctr\"), url.UserPassword(\"aes-192-ctr\", \"123456\"), false},\n\t{url.UserPassword(\"aes-192-ctr\", \"123456\"), url.User(\"aes-192-ctr\"), false},\n\t{url.UserPassword(\"aes-192-ctr\", \"123456\"), url.UserPassword(\"aes-192-ctr\", \"abc\"), false},\n\t{url.UserPassword(\"aes-192-ctr\", \"123456\"), url.UserPassword(\"aes-192-ctr\", \"123456\"), true},\n\n\t{url.User(\"aes-256-ctr\"), url.User(\"aes-256-ctr\"), true},\n\t{url.User(\"aes-256-ctr\"), url.UserPassword(\"aes-256-ctr\", \"123456\"), false},\n\t{url.UserPassword(\"aes-256-ctr\", \"123456\"), url.User(\"aes-256-ctr\"), false},\n\t{url.UserPassword(\"aes-256-ctr\", \"123456\"), url.UserPassword(\"aes-256-ctr\", \"abc\"), false},\n\t{url.UserPassword(\"aes-256-ctr\", \"123456\"), url.UserPassword(\"aes-256-ctr\", \"123456\"), true},\n\n\t{url.User(\"des-cfb\"), url.User(\"des-cfb\"), true},\n\t{url.User(\"des-cfb\"), url.UserPassword(\"des-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"des-cfb\", \"123456\"), url.User(\"des-cfb\"), false},\n\t{url.UserPassword(\"des-cfb\", \"123456\"), url.UserPassword(\"des-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"des-cfb\", \"123456\"), url.UserPassword(\"des-cfb\", \"123456\"), true},\n\n\t{url.User(\"bf-cfb\"), url.User(\"bf-cfb\"), true},\n\t{url.User(\"bf-cfb\"), url.UserPassword(\"bf-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"bf-cfb\", \"123456\"), url.User(\"bf-cfb\"), false},\n\t{url.UserPassword(\"bf-cfb\", \"123456\"), url.UserPassword(\"bf-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"bf-cfb\", \"123456\"), url.UserPassword(\"bf-cfb\", \"123456\"), true},\n\n\t{url.User(\"cast5-cfb\"), url.User(\"cast5-cfb\"), true},\n\t{url.User(\"cast5-cfb\"), url.UserPassword(\"cast5-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"cast5-cfb\", \"123456\"), url.User(\"cast5-cfb\"), false},\n\t{url.UserPassword(\"cast5-cfb\", \"123456\"), url.UserPassword(\"cast5-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"cast5-cfb\", \"123456\"), url.UserPassword(\"cast5-cfb\", \"123456\"), true},\n\n\t{url.User(\"rc4-md5\"), url.User(\"rc4-md5\"), true},\n\t{url.User(\"rc4-md5\"), url.UserPassword(\"rc4-md5\", \"123456\"), false},\n\t{url.UserPassword(\"rc4-md5\", \"123456\"), url.User(\"rc4-md5\"), false},\n\t{url.UserPassword(\"rc4-md5\", \"123456\"), url.UserPassword(\"rc4-md5\", \"abc\"), false},\n\t{url.UserPassword(\"rc4-md5\", \"123456\"), url.UserPassword(\"rc4-md5\", \"123456\"), true},\n\n\t{url.User(\"chacha20\"), url.User(\"chacha20\"), true},\n\t{url.User(\"chacha20\"), url.UserPassword(\"chacha20\", \"123456\"), false},\n\t{url.UserPassword(\"chacha20\", \"123456\"), url.User(\"chacha20\"), false},\n\t{url.UserPassword(\"chacha20\", \"123456\"), url.UserPassword(\"chacha20\", \"abc\"), false},\n\t{url.UserPassword(\"chacha20\", \"123456\"), url.UserPassword(\"chacha20\", \"123456\"), true},\n\n\t{url.User(\"chacha20-ietf\"), url.User(\"chacha20-ietf\"), true},\n\t{url.User(\"chacha20-ietf\"), url.UserPassword(\"chacha20-ietf\", \"123456\"), false},\n\t{url.UserPassword(\"chacha20-ietf\", \"123456\"), url.User(\"chacha20-ietf\"), false},\n\t{url.UserPassword(\"chacha20-ietf\", \"123456\"), url.UserPassword(\"chacha20-ietf\", \"abc\"), false},\n\t{url.UserPassword(\"chacha20-ietf\", \"123456\"), url.UserPassword(\"chacha20-ietf\", \"123456\"), true},\n\n\t{url.User(\"salsa20\"), url.User(\"salsa20\"), true},\n\t{url.User(\"salsa20\"), url.UserPassword(\"salsa20\", \"123456\"), false},\n\t{url.UserPassword(\"salsa20\", \"123456\"), url.User(\"salsa20\"), false},\n\t{url.UserPassword(\"salsa20\", \"123456\"), url.UserPassword(\"salsa20\", \"abc\"), false},\n\t{url.UserPassword(\"salsa20\", \"123456\"), url.UserPassword(\"salsa20\", \"123456\"), true},\n\n\t{url.User(\"xchacha20\"), url.User(\"xchacha20\"), true},\n\t{url.User(\"xchacha20\"), url.UserPassword(\"xchacha20\", \"123456\"), false},\n\t{url.UserPassword(\"xchacha20\", \"123456\"), url.User(\"xchacha20\"), false},\n\t{url.UserPassword(\"xchacha20\", \"123456\"), url.UserPassword(\"xchacha20\", \"abc\"), false},\n\t{url.UserPassword(\"xchacha20\", \"123456\"), url.UserPassword(\"xchacha20\", \"123456\"), true},\n\n\t{url.User(\"CHACHA20-IETF-POLY1305\"), url.User(\"CHACHA20-IETF-POLY1305\"), true},\n\t{url.User(\"CHACHA20-IETF-POLY1305\"), url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), false},\n\t{url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), url.User(\"CHACHA20-IETF-POLY1305\"), false},\n\t{url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"abc\"), false},\n\t{url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), true},\n\n\t{url.User(\"AES-128-GCM\"), url.User(\"AES-128-GCM\"), true},\n\t{url.User(\"AES-128-GCM\"), url.UserPassword(\"AES-128-GCM\", \"123456\"), false},\n\t{url.UserPassword(\"AES-128-GCM\", \"123456\"), url.User(\"AES-128-GCM\"), false},\n\t{url.UserPassword(\"AES-128-GCM\", \"123456\"), url.UserPassword(\"AES-128-GCM\", \"abc\"), false},\n\t{url.UserPassword(\"AES-128-GCM\", \"123456\"), url.UserPassword(\"AES-128-GCM\", \"123456\"), true},\n\n\t{url.User(\"AES-192-GCM\"), url.User(\"AES-192-GCM\"), true},\n\t{url.User(\"AES-192-GCM\"), url.UserPassword(\"AES-192-GCM\", \"123456\"), false},\n\t{url.UserPassword(\"AES-192-GCM\", \"123456\"), url.User(\"AES-192-GCM\"), false},\n\t{url.UserPassword(\"AES-192-GCM\", \"123456\"), url.UserPassword(\"AES-192-GCM\", \"abc\"), false},\n\t{url.UserPassword(\"AES-192-GCM\", \"123456\"), url.UserPassword(\"AES-192-GCM\", \"123456\"), true},\n\n\t{url.User(\"AES-256-GCM\"), url.User(\"AES-256-GCM\"), true},\n\t{url.User(\"AES-256-GCM\"), url.UserPassword(\"AES-256-GCM\", \"123456\"), false},\n\t{url.UserPassword(\"AES-256-GCM\", \"123456\"), url.User(\"AES-256-GCM\"), false},\n\t{url.UserPassword(\"AES-256-GCM\", \"123456\"), url.UserPassword(\"AES-256-GCM\", \"abc\"), false},\n\t{url.UserPassword(\"AES-256-GCM\", \"123456\"), url.UserPassword(\"AES-256-GCM\", \"123456\"), true},\n}\n\nvar ssProxyTests = []struct {\n\tclientCipher *url.Userinfo\n\tserverCipher *url.Userinfo\n\tpass         bool\n}{\n\t{nil, nil, true},\n\t{&url.Userinfo{}, &url.Userinfo{}, true},\n\t{url.User(\"abc\"), url.User(\"abc\"), true},\n\t{url.UserPassword(\"abc\", \"def\"), url.UserPassword(\"abc\", \"def\"), true},\n\n\t{url.User(\"aes-128-cfb\"), url.User(\"aes-128-cfb\"), true},\n\t{url.User(\"aes-128-cfb\"), url.UserPassword(\"aes-128-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"aes-128-cfb\", \"123456\"), url.User(\"aes-128-cfb\"), false},\n\t{url.UserPassword(\"aes-128-cfb\", \"123456\"), url.UserPassword(\"aes-128-cfb\", \"123456\"), true},\n\n\t{url.User(\"CHACHA20-IETF-POLY1305\"), url.User(\"CHACHA20-IETF-POLY1305\"), true},\n\t{url.User(\"CHACHA20-IETF-POLY1305\"), url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), false},\n\t{url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), url.User(\"CHACHA20-IETF-POLY1305\"), false},\n\t{url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), true},\n}\n\nfunc ssProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo *url.Userinfo) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  ShadowHandler(UsersHandlerOption(serverInfo)),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestShadowTCP(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := ssProxyRoundtrip(httpSrv.URL, sendData,\n\t\t\t\ttc.clientCipher,\n\t\t\t\ttc.serverCipher,\n\t\t\t)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkSSProxy_AES256(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(url.UserPassword(\"aes-256-cfb\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  ShadowHandler(UsersHandlerOption(url.UserPassword(\"aes-256-cfb\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSSProxy_Chacha20(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(url.UserPassword(\"chacha20\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  ShadowHandler(UsersHandlerOption(url.UserPassword(\"chacha20\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSSProxy_Chacha20_ietf(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(url.UserPassword(\"chacha20-ietf\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  ShadowHandler(UsersHandlerOption(url.UserPassword(\"chacha20-ietf\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSSProxyParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(url.UserPassword(\"chacha20-ietf\", \"123456\")),\n\t\tTransporter: TCPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler:  ShadowHandler(UsersHandlerOption(url.UserPassword(\"chacha20-ietf\", \"123456\"))),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nvar ssuTests = []struct {\n\tclientCipher *url.Userinfo\n\tserverCipher *url.Userinfo\n\tpass         bool\n}{\n\t{nil, nil, true},\n\t{&url.Userinfo{}, &url.Userinfo{}, true},\n\t{url.User(\"abc\"), url.User(\"abc\"), true},\n\t{url.UserPassword(\"abc\", \"def\"), url.UserPassword(\"abc\", \"def\"), true},\n\n\t{url.User(\"aes-128-cfb\"), url.User(\"aes-128-cfb\"), true},\n\t{url.User(\"aes-128-cfb\"), url.UserPassword(\"aes-128-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"aes-128-cfb\", \"123456\"), url.User(\"aes-128-cfb\"), false},\n\t{url.UserPassword(\"aes-128-cfb\", \"123456\"), url.UserPassword(\"aes-128-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"aes-128-cfb\", \"123456\"), url.UserPassword(\"aes-128-cfb\", \"123456\"), true},\n\n\t{url.User(\"aes-192-cfb\"), url.User(\"aes-192-cfb\"), true},\n\t{url.User(\"aes-192-cfb\"), url.UserPassword(\"aes-192-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"aes-192-cfb\", \"123456\"), url.User(\"aes-192-cfb\"), false},\n\t{url.UserPassword(\"aes-192-cfb\", \"123456\"), url.UserPassword(\"aes-192-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"aes-192-cfb\", \"123456\"), url.UserPassword(\"aes-192-cfb\", \"123456\"), true},\n\n\t{url.User(\"aes-256-cfb\"), url.User(\"aes-256-cfb\"), true},\n\t{url.User(\"aes-256-cfb\"), url.UserPassword(\"aes-256-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"aes-256-cfb\", \"123456\"), url.User(\"aes-256-cfb\"), false},\n\t{url.UserPassword(\"aes-256-cfb\", \"123456\"), url.UserPassword(\"aes-256-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"aes-256-cfb\", \"123456\"), url.UserPassword(\"aes-256-cfb\", \"123456\"), true},\n\n\t{url.User(\"aes-128-ctr\"), url.User(\"aes-128-ctr\"), true},\n\t{url.User(\"aes-128-ctr\"), url.UserPassword(\"aes-128-ctr\", \"123456\"), false},\n\t{url.UserPassword(\"aes-128-ctr\", \"123456\"), url.User(\"aes-128-ctr\"), false},\n\t{url.UserPassword(\"aes-128-ctr\", \"123456\"), url.UserPassword(\"aes-128-ctr\", \"abc\"), false},\n\t{url.UserPassword(\"aes-128-ctr\", \"123456\"), url.UserPassword(\"aes-128-ctr\", \"123456\"), true},\n\n\t{url.User(\"aes-192-ctr\"), url.User(\"aes-192-ctr\"), true},\n\t{url.User(\"aes-192-ctr\"), url.UserPassword(\"aes-192-ctr\", \"123456\"), false},\n\t{url.UserPassword(\"aes-192-ctr\", \"123456\"), url.User(\"aes-192-ctr\"), false},\n\t{url.UserPassword(\"aes-192-ctr\", \"123456\"), url.UserPassword(\"aes-192-ctr\", \"abc\"), false},\n\t{url.UserPassword(\"aes-192-ctr\", \"123456\"), url.UserPassword(\"aes-192-ctr\", \"123456\"), true},\n\n\t{url.User(\"aes-256-ctr\"), url.User(\"aes-256-ctr\"), true},\n\t{url.User(\"aes-256-ctr\"), url.UserPassword(\"aes-256-ctr\", \"123456\"), false},\n\t{url.UserPassword(\"aes-256-ctr\", \"123456\"), url.User(\"aes-256-ctr\"), false},\n\t{url.UserPassword(\"aes-256-ctr\", \"123456\"), url.UserPassword(\"aes-256-ctr\", \"abc\"), false},\n\t{url.UserPassword(\"aes-256-ctr\", \"123456\"), url.UserPassword(\"aes-256-ctr\", \"123456\"), true},\n\n\t{url.User(\"des-cfb\"), url.User(\"des-cfb\"), true},\n\t{url.User(\"des-cfb\"), url.UserPassword(\"des-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"des-cfb\", \"123456\"), url.User(\"des-cfb\"), false},\n\t{url.UserPassword(\"des-cfb\", \"123456\"), url.UserPassword(\"des-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"des-cfb\", \"123456\"), url.UserPassword(\"des-cfb\", \"123456\"), true},\n\n\t{url.User(\"bf-cfb\"), url.User(\"bf-cfb\"), true},\n\t{url.User(\"bf-cfb\"), url.UserPassword(\"bf-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"bf-cfb\", \"123456\"), url.User(\"bf-cfb\"), false},\n\t{url.UserPassword(\"bf-cfb\", \"123456\"), url.UserPassword(\"bf-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"bf-cfb\", \"123456\"), url.UserPassword(\"bf-cfb\", \"123456\"), true},\n\n\t{url.User(\"cast5-cfb\"), url.User(\"cast5-cfb\"), true},\n\t{url.User(\"cast5-cfb\"), url.UserPassword(\"cast5-cfb\", \"123456\"), false},\n\t{url.UserPassword(\"cast5-cfb\", \"123456\"), url.User(\"cast5-cfb\"), false},\n\t{url.UserPassword(\"cast5-cfb\", \"123456\"), url.UserPassword(\"cast5-cfb\", \"abc\"), false},\n\t{url.UserPassword(\"cast5-cfb\", \"123456\"), url.UserPassword(\"cast5-cfb\", \"123456\"), true},\n\n\t{url.User(\"rc4-md5\"), url.User(\"rc4-md5\"), true},\n\t{url.User(\"rc4-md5\"), url.UserPassword(\"rc4-md5\", \"123456\"), false},\n\t{url.UserPassword(\"rc4-md5\", \"123456\"), url.User(\"rc4-md5\"), false},\n\t{url.UserPassword(\"rc4-md5\", \"123456\"), url.UserPassword(\"rc4-md5\", \"abc\"), false},\n\t{url.UserPassword(\"rc4-md5\", \"123456\"), url.UserPassword(\"rc4-md5\", \"123456\"), true},\n\n\t{url.User(\"chacha20\"), url.User(\"chacha20\"), true},\n\t{url.User(\"chacha20\"), url.UserPassword(\"chacha20\", \"123456\"), false},\n\t{url.UserPassword(\"chacha20\", \"123456\"), url.User(\"chacha20\"), false},\n\t{url.UserPassword(\"chacha20\", \"123456\"), url.UserPassword(\"chacha20\", \"abc\"), false},\n\t{url.UserPassword(\"chacha20\", \"123456\"), url.UserPassword(\"chacha20\", \"123456\"), true},\n\n\t{url.User(\"chacha20-ietf\"), url.User(\"chacha20-ietf\"), true},\n\t{url.User(\"chacha20-ietf\"), url.UserPassword(\"chacha20-ietf\", \"123456\"), false},\n\t{url.UserPassword(\"chacha20-ietf\", \"123456\"), url.User(\"chacha20-ietf\"), false},\n\t{url.UserPassword(\"chacha20-ietf\", \"123456\"), url.UserPassword(\"chacha20-ietf\", \"abc\"), false},\n\t{url.UserPassword(\"chacha20-ietf\", \"123456\"), url.UserPassword(\"chacha20-ietf\", \"123456\"), true},\n\n\t{url.User(\"salsa20\"), url.User(\"salsa20\"), true},\n\t{url.User(\"salsa20\"), url.UserPassword(\"salsa20\", \"123456\"), false},\n\t{url.UserPassword(\"salsa20\", \"123456\"), url.User(\"salsa20\"), false},\n\t{url.UserPassword(\"salsa20\", \"123456\"), url.UserPassword(\"salsa20\", \"abc\"), false},\n\t{url.UserPassword(\"salsa20\", \"123456\"), url.UserPassword(\"salsa20\", \"123456\"), true},\n\n\t{url.User(\"xchacha20\"), url.User(\"xchacha20\"), true},\n\t{url.User(\"xchacha20\"), url.UserPassword(\"xchacha20\", \"123456\"), false},\n\t{url.UserPassword(\"xchacha20\", \"123456\"), url.User(\"xchacha20\"), false},\n\t{url.UserPassword(\"xchacha20\", \"123456\"), url.UserPassword(\"xchacha20\", \"abc\"), false},\n\t{url.UserPassword(\"xchacha20\", \"123456\"), url.UserPassword(\"xchacha20\", \"123456\"), true},\n\n\t{url.User(\"CHACHA20-IETF-POLY1305\"), url.User(\"CHACHA20-IETF-POLY1305\"), true},\n\t{url.User(\"CHACHA20-IETF-POLY1305\"), url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), false},\n\t{url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), url.User(\"CHACHA20-IETF-POLY1305\"), false},\n\t{url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"abc\"), false},\n\t{url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), url.UserPassword(\"CHACHA20-IETF-POLY1305\", \"123456\"), true},\n\n\t{url.User(\"AES-128-GCM\"), url.User(\"AES-128-GCM\"), true},\n\t{url.User(\"AES-128-GCM\"), url.UserPassword(\"AES-128-GCM\", \"123456\"), false},\n\t{url.UserPassword(\"AES-128-GCM\", \"123456\"), url.User(\"AES-128-GCM\"), false},\n\t{url.UserPassword(\"AES-128-GCM\", \"123456\"), url.UserPassword(\"AES-128-GCM\", \"abc\"), false},\n\t{url.UserPassword(\"AES-128-GCM\", \"123456\"), url.UserPassword(\"AES-128-GCM\", \"123456\"), true},\n\n\t{url.User(\"AES-192-GCM\"), url.User(\"AES-192-GCM\"), true},\n\t{url.User(\"AES-192-GCM\"), url.UserPassword(\"AES-192-GCM\", \"123456\"), false},\n\t{url.UserPassword(\"AES-192-GCM\", \"123456\"), url.User(\"AES-192-GCM\"), false},\n\t{url.UserPassword(\"AES-192-GCM\", \"123456\"), url.UserPassword(\"AES-192-GCM\", \"abc\"), false},\n\t{url.UserPassword(\"AES-192-GCM\", \"123456\"), url.UserPassword(\"AES-192-GCM\", \"123456\"), true},\n\n\t{url.User(\"AES-256-GCM\"), url.User(\"AES-256-GCM\"), true},\n\t{url.User(\"AES-256-GCM\"), url.UserPassword(\"AES-256-GCM\", \"123456\"), false},\n\t{url.UserPassword(\"AES-256-GCM\", \"123456\"), url.User(\"AES-256-GCM\"), false},\n\t{url.UserPassword(\"AES-256-GCM\", \"123456\"), url.UserPassword(\"AES-256-GCM\", \"abc\"), false},\n\t{url.UserPassword(\"AES-256-GCM\", \"123456\"), url.UserPassword(\"AES-256-GCM\", \"123456\"), true},\n}\n\nfunc shadowUDPRoundtrip(t *testing.T, host string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo *url.Userinfo) error {\n\tln, err := UDPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowUDPConnector(clientInfo),\n\t\tTransporter: UDPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler: ShadowUDPHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn udpRoundtrip(t, client, server, host, data)\n}\n\nfunc TestShadowUDP(t *testing.T) {\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssuTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\tudpSrv := newUDPTestServer(udpTestHandler)\n\t\t\tudpSrv.Start()\n\t\t\tdefer udpSrv.Close()\n\n\t\t\terr := shadowUDPRoundtrip(t, udpSrv.Addr(), sendData,\n\t\t\t\ttc.clientCipher,\n\t\t\t\ttc.serverCipher,\n\t\t\t)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkShadowUDP(b *testing.B) {\n\tudpSrv := newUDPTestServer(udpTestHandler)\n\tudpSrv.Start()\n\tdefer udpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := UDPListener(\"localhost:0\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowUDPConnector(url.UserPassword(\"chacha20-ietf\", \"123456\")),\n\t\tTransporter: UDPTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tHandler: ShadowUDPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"chacha20-ietf\", \"123456\")),\n\t\t),\n\t\tListener: ln,\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\tdefer conn.Close()\n\n\tconn, err = client.Connect(conn, udpSrv.Addr())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tconn.SetDeadline(time.Now().Add(3 * time.Second))\n\n\t\tif _, err = conn.Write(sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\n\t\trecv := make([]byte, len(sendData))\n\t\tif _, err = conn.Read(recv); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\n\t\tconn.SetDeadline(time.Time{})\n\n\t\tif !bytes.Equal(sendData, recv) {\n\t\t\tb.Error(\"data not equal\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "ssh.go",
    "content": "package gost\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\t\"golang.org/x/crypto/ssh\"\n)\n\n// Applicable SSH Request types for Port Forwarding - RFC 4254 7.X\nconst (\n\tDirectForwardRequest       = \"direct-tcpip\"         // RFC 4254 7.2\n\tRemoteForwardRequest       = \"tcpip-forward\"        // RFC 4254 7.1\n\tForwardedTCPReturnRequest  = \"forwarded-tcpip\"      // RFC 4254 7.2\n\tCancelRemoteForwardRequest = \"cancel-tcpip-forward\" // RFC 4254 7.1\n\n\tGostSSHTunnelRequest = \"gost-tunnel\" // extended request type for ssh tunnel\n)\n\nvar errSessionDead = errors.New(\"session is dead\")\n\n// ParseSSHKeyFile parses ssh key file.\nfunc ParseSSHKeyFile(fp string) (ssh.Signer, error) {\n\tkey, err := os.ReadFile(fp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn ssh.ParsePrivateKey(key)\n}\n\n// ParseSSHAuthorizedKeysFile parses ssh Authorized Keys file.\nfunc ParseSSHAuthorizedKeysFile(fp string) (map[string]bool, error) {\n\tauthorizedKeysBytes, err := os.ReadFile(fp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tauthorizedKeysMap := make(map[string]bool)\n\tfor len(authorizedKeysBytes) > 0 {\n\t\tpubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tauthorizedKeysMap[string(pubKey.Marshal())] = true\n\t\tauthorizedKeysBytes = rest\n\t}\n\n\treturn authorizedKeysMap, nil\n}\n\ntype sshDirectForwardConnector struct{}\n\n// SSHDirectForwardConnector creates a Connector for SSH TCP direct port forwarding.\nfunc SSHDirectForwardConnector() Connector {\n\treturn &sshDirectForwardConnector{}\n}\n\nfunc (c *sshDirectForwardConnector) Connect(conn net.Conn, raddr string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", raddr, options...)\n}\n\nfunc (c *sshDirectForwardConnector) ConnectContext(ctx context.Context, conn net.Conn, network, raddr string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\topts := &ConnectOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\tcc, ok := conn.(*sshNopConn) // TODO: this is an ugly type assertion, need to find a better solution.\n\tif !ok {\n\t\treturn nil, errors.New(\"ssh: wrong connection type\")\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = ConnectTimeout\n\t}\n\n\tcc.session.conn.SetDeadline(time.Now().Add(timeout))\n\tdefer cc.session.conn.SetDeadline(time.Time{})\n\n\tconn, err := cc.session.client.Dial(\"tcp\", raddr)\n\tif err != nil {\n\t\tlog.Logf(\"[ssh-tcp] %s -> %s : %s\", cc.session.addr, raddr, err)\n\t\treturn nil, err\n\t}\n\treturn conn, nil\n}\n\ntype sshRemoteForwardConnector struct{}\n\n// SSHRemoteForwardConnector creates a Connector for SSH TCP remote port forwarding.\nfunc SSHRemoteForwardConnector() Connector {\n\treturn &sshRemoteForwardConnector{}\n}\n\nfunc (c *sshRemoteForwardConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {\n\treturn c.ConnectContext(context.Background(), conn, \"tcp\", address, options...)\n}\n\nfunc (c *sshRemoteForwardConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {\n\tswitch network {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\treturn nil, fmt.Errorf(\"%s unsupported\", network)\n\t}\n\n\tcc, ok := conn.(*sshNopConn) // TODO: this is an ugly type assertion, need to find a better solution.\n\tif !ok {\n\t\treturn nil, errors.New(\"ssh: wrong connection type\")\n\t}\n\n\tcc.session.once.Do(func() {\n\t\tgo func() {\n\t\t\tdefer log.Log(\"ssh-rtcp: session is closed\")\n\t\t\tdefer close(cc.session.connChan)\n\n\t\t\tif cc.session == nil || cc.session.client == nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif strings.HasPrefix(address, \":\") {\n\t\t\t\taddress = \"0.0.0.0\" + address\n\t\t\t}\n\t\t\tln, err := cc.session.client.Listen(\"tcp\", address)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlog.Log(\"[ssh-rtcp] listening on\", ln.Addr())\n\n\t\t\tfor {\n\t\t\t\trc, err := ln.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Logf(\"[ssh-rtcp] %s <-> %s accpet : %s\", ln.Addr(), address, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// log.Log(\"[ssh-rtcp] accept\", rc.LocalAddr(), rc.RemoteAddr())\n\t\t\t\tselect {\n\t\t\t\tcase cc.session.connChan <- rc:\n\t\t\t\tdefault:\n\t\t\t\t\trc.Close()\n\t\t\t\t\tlog.Logf(\"[ssh-rtcp] %s - %s: connection queue is full\", ln.Addr(), address)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t})\n\n\tsc, ok := <-cc.session.connChan\n\tif !ok {\n\t\treturn nil, errors.New(\"ssh-rtcp: connection is closed\")\n\t}\n\treturn sc, nil\n}\n\ntype sshForwardTransporter struct {\n\tsessions     map[string]*sshSession\n\tsessionMutex sync.Mutex\n}\n\n// SSHForwardTransporter creates a Transporter that is used by SSH port forwarding server.\nfunc SSHForwardTransporter() Transporter {\n\treturn &sshForwardTransporter{\n\t\tsessions: make(map[string]*sshSession),\n\t}\n}\n\nfunc (tr *sshForwardTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = DialTimeout\n\t}\n\n\tsession, ok := tr.sessions[addr]\n\tif !ok || session.Closed() {\n\t\tif opts.Chain == nil {\n\t\t\tconn, err = net.DialTimeout(\"tcp\", addr, timeout)\n\t\t} else {\n\t\t\tconn, err = opts.Chain.Dial(addr)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tsession = &sshSession{\n\t\t\taddr: addr,\n\t\t\tconn: conn,\n\t\t}\n\t\ttr.sessions[addr] = session\n\t}\n\n\treturn session.conn, nil\n}\n\nfunc (tr *sshForwardTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\n\tconfig := ssh.ClientConfig{\n\t\tTimeout:         timeout,\n\t\tHostKeyCallback: ssh.InsecureIgnoreHostKey(),\n\t}\n\tif opts.User != nil {\n\t\tconfig.User = opts.User.Username()\n\t\tif password, _ := opts.User.Password(); password != \"\" {\n\t\t\tconfig.Auth = []ssh.AuthMethod{\n\t\t\t\tssh.Password(password),\n\t\t\t}\n\t\t}\n\t}\n\tif opts.SSHConfig != nil && opts.SSHConfig.Key != nil {\n\t\tconfig.Auth = append(config.Auth, ssh.PublicKeys(opts.SSHConfig.Key))\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tsession, ok := tr.sessions[opts.Addr]\n\tif !ok || session.client == nil {\n\t\tsshConn, chans, reqs, err := ssh.NewClientConn(conn, opts.Addr, &config)\n\t\tif err != nil {\n\t\t\tlog.Log(\"ssh\", err)\n\t\t\tconn.Close()\n\t\t\tdelete(tr.sessions, opts.Addr)\n\t\t\treturn nil, err\n\t\t}\n\n\t\tsession = &sshSession{\n\t\t\taddr:     opts.Addr,\n\t\t\tconn:     conn,\n\t\t\tclient:   ssh.NewClient(sshConn, chans, reqs),\n\t\t\tclosed:   make(chan struct{}),\n\t\t\tdeaded:   make(chan struct{}),\n\t\t\tconnChan: make(chan net.Conn, 1024),\n\t\t}\n\t\ttr.sessions[opts.Addr] = session\n\t\tgo session.Ping(opts.Interval, opts.Timeout, opts.Retry)\n\t\tgo session.waitServer()\n\t\tgo session.waitClose()\n\t}\n\tif session.Closed() {\n\t\tdelete(tr.sessions, opts.Addr)\n\t\treturn nil, errSessionDead\n\t}\n\n\treturn &sshNopConn{session: session}, nil\n}\n\nfunc (tr *sshForwardTransporter) Multiplex() bool {\n\treturn true\n}\n\ntype sshTunnelTransporter struct {\n\tsessions     map[string]*sshSession\n\tsessionMutex sync.Mutex\n}\n\n// SSHTunnelTransporter creates a Transporter that is used by SSH tunnel client.\nfunc SSHTunnelTransporter() Transporter {\n\treturn &sshTunnelTransporter{\n\t\tsessions: make(map[string]*sshSession),\n\t}\n}\n\nfunc (tr *sshTunnelTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = DialTimeout\n\t}\n\n\tsession, ok := tr.sessions[addr]\n\tif !ok || session.Closed() {\n\t\tif opts.Chain == nil {\n\t\t\tconn, err = net.DialTimeout(\"tcp\", addr, timeout)\n\t\t} else {\n\t\t\tconn, err = opts.Chain.Dial(addr)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tsession = &sshSession{\n\t\t\taddr: addr,\n\t\t\tconn: conn,\n\t\t}\n\t\ttr.sessions[addr] = session\n\t}\n\n\treturn session.conn, nil\n}\n\nfunc (tr *sshTunnelTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\n\tconfig := ssh.ClientConfig{\n\t\tTimeout:         timeout,\n\t\tHostKeyCallback: ssh.InsecureIgnoreHostKey(),\n\t}\n\tif opts.User != nil {\n\t\tconfig.User = opts.User.Username()\n\t\tif password, _ := opts.User.Password(); password != \"\" {\n\t\t\tconfig.Auth = []ssh.AuthMethod{\n\t\t\t\tssh.Password(password),\n\t\t\t}\n\t\t}\n\t}\n\tif opts.SSHConfig != nil && opts.SSHConfig.Key != nil {\n\t\tconfig.Auth = append(config.Auth, ssh.PublicKeys(opts.SSHConfig.Key))\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tsession, ok := tr.sessions[opts.Addr]\n\tif !ok || session.client == nil {\n\t\tsshConn, chans, reqs, err := ssh.NewClientConn(conn, opts.Addr, &config)\n\t\tif err != nil {\n\t\t\tconn.Close()\n\t\t\tdelete(tr.sessions, opts.Addr)\n\t\t\treturn nil, err\n\t\t}\n\n\t\tsession = &sshSession{\n\t\t\taddr:   opts.Addr,\n\t\t\tconn:   conn,\n\t\t\tclient: ssh.NewClient(sshConn, chans, reqs),\n\t\t\tclosed: make(chan struct{}),\n\t\t\tdeaded: make(chan struct{}),\n\t\t}\n\t\ttr.sessions[opts.Addr] = session\n\t\tgo session.Ping(opts.Interval, opts.Timeout, opts.Retry)\n\t\tgo session.waitServer()\n\t\tgo session.waitClose()\n\t}\n\n\tif session.Closed() {\n\t\tdelete(tr.sessions, opts.Addr)\n\t\treturn nil, errSessionDead\n\t}\n\n\tchannel, reqs, err := session.client.OpenChannel(GostSSHTunnelRequest, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgo ssh.DiscardRequests(reqs)\n\treturn &sshConn{channel: channel, conn: conn}, nil\n}\n\nfunc (tr *sshTunnelTransporter) Multiplex() bool {\n\treturn true\n}\n\ntype sshSession struct {\n\taddr     string\n\tconn     net.Conn\n\tclient   *ssh.Client\n\tclosed   chan struct{}\n\tdeaded   chan struct{}\n\tonce     sync.Once\n\tconnChan chan net.Conn\n}\n\nfunc (s *sshSession) Ping(interval, timeout time.Duration, retries int) {\n\tif interval <= 0 {\n\t\treturn\n\t}\n\tif timeout <= 0 {\n\t\ttimeout = PingTimeout\n\t}\n\n\tif retries == 0 {\n\t\tretries = 1\n\t}\n\n\tdefer close(s.deaded)\n\n\tlog.Logf(\"[ssh] ping is enabled, interval: %v, timeout: %v, retry: %d\", interval, timeout, retries)\n\tbaseCtx := context.Background()\n\tt := time.NewTicker(interval)\n\tdefer t.Stop()\n\n\tcount := retries + 1\n\tfor {\n\t\tselect {\n\t\tcase <-t.C:\n\t\t\tstart := time.Now()\n\t\t\tif Debug {\n\t\t\t\tlog.Log(\"[ssh] sending ping\")\n\t\t\t}\n\t\t\tctx, cancel := context.WithTimeout(baseCtx, timeout)\n\t\t\tvar err error\n\t\t\tselect {\n\t\t\tcase err = <-s.sendPing():\n\t\t\tcase <-ctx.Done():\n\t\t\t\terr = errors.New(\"Timeout\")\n\t\t\t}\n\t\t\tcancel()\n\t\t\tif err != nil {\n\t\t\t\tlog.Log(\"[ssh] ping:\", err)\n\t\t\t\tcount--\n\t\t\t\tif count == 0 {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif Debug {\n\t\t\t\tlog.Log(\"[ssh] ping OK, RTT:\", time.Since(start))\n\t\t\t}\n\t\t\tcount = retries + 1\n\t\tcase <-s.closed:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (s *sshSession) sendPing() <-chan error {\n\tch := make(chan error, 1)\n\tgo func() {\n\t\tif _, _, err := s.client.SendRequest(\"ping\", true, nil); err != nil {\n\t\t\tch <- err\n\t\t}\n\t\tclose(ch)\n\t}()\n\treturn ch\n}\n\nfunc (s *sshSession) waitServer() error {\n\tdefer close(s.closed)\n\treturn s.client.Wait()\n}\n\nfunc (s *sshSession) waitClose() {\n\tdefer s.client.Close()\n\n\tselect {\n\tcase <-s.deaded:\n\tcase <-s.closed:\n\t}\n}\n\nfunc (s *sshSession) Closed() bool {\n\tselect {\n\tcase <-s.deaded:\n\t\treturn true\n\tcase <-s.closed:\n\t\treturn true\n\tdefault:\n\t}\n\treturn false\n}\n\ntype sshForwardHandler struct {\n\toptions *HandlerOptions\n\tconfig  *ssh.ServerConfig\n}\n\n// SSHForwardHandler creates a server Handler for SSH port forwarding server.\nfunc SSHForwardHandler(opts ...HandlerOption) Handler {\n\th := &sshForwardHandler{}\n\th.Init(opts...)\n\n\treturn h\n}\n\nfunc (h *sshForwardHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n\th.config = &ssh.ServerConfig{}\n\n\th.config.PasswordCallback = defaultSSHPasswordCallback(h.options.Authenticator)\n\tif h.options.Authenticator == nil {\n\t\th.config.NoClientAuth = true\n\t}\n\ttlsConfig := h.options.TLSConfig\n\tif tlsConfig == nil {\n\t\ttlsConfig = DefaultTLSConfig\n\t}\n\tif tlsConfig != nil && len(tlsConfig.Certificates) > 0 {\n\t\tsigner, err := ssh.NewSignerFromKey(tlsConfig.Certificates[0].PrivateKey)\n\t\tif err != nil {\n\t\t\tlog.Log(\"[ssh-forward]\", err)\n\t\t}\n\t\th.config.AddHostKey(signer)\n\t}\n}\n\nfunc (h *sshForwardHandler) Handle(conn net.Conn) {\n\tsshConn, chans, reqs, err := ssh.NewServerConn(conn, h.config)\n\tif err != nil {\n\t\tlog.Logf(\"[ssh-forward] %s -> %s : %s\", conn.RemoteAddr(), h.options.Node.Addr, err)\n\t\tconn.Close()\n\t\treturn\n\t}\n\tdefer sshConn.Close()\n\n\tlog.Logf(\"[ssh-forward] %s <-> %s\", conn.RemoteAddr(), h.options.Node.Addr)\n\th.handleForward(sshConn, chans, reqs)\n\tlog.Logf(\"[ssh-forward] %s >-< %s\", conn.RemoteAddr(), h.options.Node.Addr)\n}\n\nfunc (h *sshForwardHandler) handleForward(conn ssh.Conn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {\n\tquit := make(chan struct{})\n\tdefer close(quit) // quit signal\n\n\tgo func() {\n\t\tfor req := range reqs {\n\t\t\tswitch req.Type {\n\t\t\tcase RemoteForwardRequest:\n\t\t\t\tgo h.tcpipForwardRequest(conn, req, quit)\n\t\t\tdefault:\n\t\t\t\t// log.Log(\"[ssh] unknown request type:\", req.Type, req.WantReply)\n\t\t\t\tif req.WantReply {\n\t\t\t\t\treq.Reply(false, nil)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor newChannel := range chans {\n\t\t\t// Check the type of channel\n\t\t\tt := newChannel.ChannelType()\n\t\t\tswitch t {\n\t\t\tcase DirectForwardRequest:\n\t\t\t\tchannel, requests, err := newChannel.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Log(\"[ssh] Could not accept channel:\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tp := directForward{}\n\t\t\t\tssh.Unmarshal(newChannel.ExtraData(), &p)\n\n\t\t\t\tif p.Host1 == \"<nil>\" {\n\t\t\t\t\tp.Host1 = \"\"\n\t\t\t\t}\n\n\t\t\t\tgo ssh.DiscardRequests(requests)\n\t\t\t\tgo h.directPortForwardChannel(channel, fmt.Sprintf(\"%s:%d\", p.Host1, p.Port1))\n\t\t\tdefault:\n\t\t\t\tlog.Log(\"[ssh] Unknown channel type:\", t)\n\t\t\t\tnewChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf(\"unknown channel type: %s\", t))\n\t\t\t}\n\t\t}\n\t}()\n\n\tconn.Wait()\n}\n\nfunc (h *sshForwardHandler) directPortForwardChannel(channel ssh.Channel, raddr string) {\n\tdefer channel.Close()\n\n\tlog.Logf(\"[ssh-tcp] %s - %s\", h.options.Node.Addr, raddr)\n\n\tif !Can(\"tcp\", raddr, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[ssh-tcp] Unauthorized to tcp connect to %s\", raddr)\n\t\treturn\n\t}\n\n\tif h.options.Bypass.Contains(raddr) {\n\t\tlog.Logf(\"[ssh-tcp] [bypass] %s\", raddr)\n\t\treturn\n\t}\n\n\tconn, err := h.options.Chain.Dial(raddr,\n\t\tRetryChainOption(h.options.Retries),\n\t\tTimeoutChainOption(h.options.Timeout),\n\t\tHostsChainOption(h.options.Hosts),\n\t\tResolverChainOption(h.options.Resolver),\n\t)\n\tif err != nil {\n\t\tlog.Logf(\"[ssh-tcp] %s - %s : %s\", h.options.Node.Addr, raddr, err)\n\t\treturn\n\t}\n\tdefer conn.Close()\n\n\tlog.Logf(\"[ssh-tcp] %s <-> %s\", h.options.Node.Addr, raddr)\n\ttransport(conn, channel)\n\tlog.Logf(\"[ssh-tcp] %s >-< %s\", h.options.Node.Addr, raddr)\n}\n\n// tcpipForward is structure for RFC 4254 7.1 \"tcpip-forward\" request\ntype tcpipForward struct {\n\tHost string\n\tPort uint32\n}\n\nfunc (h *sshForwardHandler) tcpipForwardRequest(sshConn ssh.Conn, req *ssh.Request, quit <-chan struct{}) {\n\tt := tcpipForward{}\n\tssh.Unmarshal(req.Payload, &t)\n\n\taddr := fmt.Sprintf(\"%s:%d\", t.Host, t.Port)\n\n\tif !Can(\"rtcp\", addr, h.options.Whitelist, h.options.Blacklist) {\n\t\tlog.Logf(\"[ssh-rtcp] Unauthorized to tcp bind to %s\", addr)\n\t\treq.Reply(false, nil)\n\t\treturn\n\t}\n\n\tln, err := net.Listen(\"tcp\", addr) // tie to the client connection\n\tif err != nil {\n\t\tlog.Log(\"[ssh-rtcp]\", err)\n\t\treq.Reply(false, nil)\n\t\treturn\n\t}\n\tdefer ln.Close()\n\n\tlog.Log(\"[ssh-rtcp] listening on tcp\", ln.Addr())\n\n\treplyFunc := func() error {\n\t\tif t.Port == 0 && req.WantReply { // Client sent port 0. let them know which port is actually being used\n\t\t\t_, port, err := getHostPortFromAddr(ln.Addr())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tvar b [4]byte\n\t\t\tbinary.BigEndian.PutUint32(b[:], uint32(port))\n\t\t\tt.Port = uint32(port)\n\t\t\treturn req.Reply(true, b[:])\n\t\t}\n\t\treturn req.Reply(true, nil)\n\t}\n\tif err := replyFunc(); err != nil {\n\t\tlog.Log(\"[ssh-rtcp]\", err)\n\t\treturn\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tconn, err := ln.Accept()\n\t\t\tif err != nil { // Unable to accept new connection - listener is likely closed\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tgo func(conn net.Conn) {\n\t\t\t\tdefer conn.Close()\n\n\t\t\t\tp := directForward{}\n\t\t\t\tvar err error\n\n\t\t\t\tvar portnum int\n\t\t\t\tp.Host1 = t.Host\n\t\t\t\tp.Port1 = t.Port\n\t\t\t\tp.Host2, portnum, err = getHostPortFromAddr(conn.RemoteAddr())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tp.Port2 = uint32(portnum)\n\t\t\t\tch, reqs, err := sshConn.OpenChannel(ForwardedTCPReturnRequest, ssh.Marshal(p))\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Log(\"[ssh-rtcp] open forwarded channel:\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer ch.Close()\n\t\t\t\tgo ssh.DiscardRequests(reqs)\n\n\t\t\t\tlog.Logf(\"[ssh-rtcp] %s <-> %s\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t\t\ttransport(ch, conn)\n\t\t\t\tlog.Logf(\"[ssh-rtcp] %s >-< %s\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t\t}(conn)\n\t\t}\n\t}()\n\n\t<-quit\n}\n\n// SSHConfig holds the SSH tunnel server config\ntype SSHConfig struct {\n\tAuthenticator  Authenticator\n\tTLSConfig      *tls.Config\n\tKey            ssh.Signer\n\tAuthorizedKeys map[string]bool\n}\n\ntype sshTunnelListener struct {\n\tnet.Listener\n\tconfig   *ssh.ServerConfig\n\tconnChan chan net.Conn\n\terrChan  chan error\n}\n\n// SSHTunnelListener creates a Listener for SSH tunnel server.\nfunc SSHTunnelListener(addr string, config *SSHConfig) (Listener, error) {\n\tln, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif config == nil {\n\t\tconfig = &SSHConfig{}\n\t}\n\n\tsshConfig := &ssh.ServerConfig{\n\t\tPasswordCallback:  defaultSSHPasswordCallback(config.Authenticator),\n\t\tPublicKeyCallback: defaultSSHPublicKeyCallback(config.AuthorizedKeys),\n\t}\n\n\tif config.Authenticator == nil && len(config.AuthorizedKeys) == 0 {\n\t\tsshConfig.NoClientAuth = true\n\t}\n\n\tsigner := config.Key\n\tif signer == nil {\n\t\tsigner, err = ssh.NewSignerFromKey(DefaultTLSConfig.Certificates[0].PrivateKey)\n\t\tif err != nil {\n\t\t\tln.Close()\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tsshConfig.AddHostKey(signer)\n\n\tl := &sshTunnelListener{\n\t\tListener: tcpKeepAliveListener{ln.(*net.TCPListener)},\n\t\tconfig:   sshConfig,\n\t\tconnChan: make(chan net.Conn, 1024),\n\t\terrChan:  make(chan error, 1),\n\t}\n\n\tgo l.listenLoop()\n\n\treturn l, nil\n}\n\nfunc (l *sshTunnelListener) listenLoop() {\n\tfor {\n\t\tconn, err := l.Listener.Accept()\n\t\tif err != nil {\n\t\t\tlog.Log(\"[ssh] accept:\", err)\n\t\t\tl.errChan <- err\n\t\t\tclose(l.errChan)\n\t\t\treturn\n\t\t}\n\t\tgo l.serveConn(conn)\n\t}\n}\n\nfunc (l *sshTunnelListener) serveConn(conn net.Conn) {\n\tsc, chans, reqs, err := ssh.NewServerConn(conn, l.config)\n\tif err != nil {\n\t\tlog.Logf(\"[ssh] %s -> %s : %s\", conn.RemoteAddr(), conn.LocalAddr(), err)\n\t\tconn.Close()\n\t\treturn\n\t}\n\tdefer sc.Close()\n\n\tgo ssh.DiscardRequests(reqs)\n\tgo func() {\n\t\tfor newChannel := range chans {\n\t\t\t// Check the type of channel\n\t\t\tt := newChannel.ChannelType()\n\t\t\tswitch t {\n\t\t\tcase GostSSHTunnelRequest:\n\t\t\t\tchannel, requests, err := newChannel.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Log(\"[ssh] Could not accept channel:\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tgo ssh.DiscardRequests(requests)\n\t\t\t\tcc := &sshConn{conn: conn, channel: channel}\n\t\t\t\tselect {\n\t\t\t\tcase l.connChan <- cc:\n\t\t\t\tdefault:\n\t\t\t\t\tcc.Close()\n\t\t\t\t\tlog.Logf(\"[ssh] %s - %s: connection queue is full\", conn.RemoteAddr(), l.Addr())\n\t\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tlog.Log(\"[ssh] Unknown channel type:\", t)\n\t\t\t\tnewChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf(\"unknown channel type: %s\", t))\n\t\t\t}\n\t\t}\n\t}()\n\n\tlog.Logf(\"[ssh] %s <-> %s\", conn.RemoteAddr(), conn.LocalAddr())\n\tsc.Wait()\n\tlog.Logf(\"[ssh] %s >-< %s\", conn.RemoteAddr(), conn.LocalAddr())\n}\n\nfunc (l *sshTunnelListener) Accept() (conn net.Conn, err error) {\n\tvar ok bool\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err, ok = <-l.errChan:\n\t\tif !ok {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\n\n// directForward is structure for RFC 4254 7.2 - can be used for \"forwarded-tcpip\" and \"direct-tcpip\"\ntype directForward struct {\n\tHost1 string\n\tPort1 uint32\n\tHost2 string\n\tPort2 uint32\n}\n\nfunc (p directForward) String() string {\n\treturn fmt.Sprintf(\"%s:%d -> %s:%d\", p.Host2, p.Port2, p.Host1, p.Port1)\n}\n\nfunc getHostPortFromAddr(addr net.Addr) (host string, port int, err error) {\n\thost, portString, err := net.SplitHostPort(addr.String())\n\tif err != nil {\n\t\treturn\n\t}\n\tport, err = strconv.Atoi(portString)\n\treturn\n}\n\n// PasswordCallbackFunc is a callback function used by SSH server.\n// It authenticates user using a password.\ntype PasswordCallbackFunc func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error)\n\nfunc defaultSSHPasswordCallback(au Authenticator) PasswordCallbackFunc {\n\tif au == nil {\n\t\treturn nil\n\t}\n\treturn func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {\n\t\tif au.Authenticate(conn.User(), string(password)) {\n\t\t\treturn nil, nil\n\t\t}\n\t\tlog.Logf(\"[ssh] %s -> %s : password rejected for %s\", conn.RemoteAddr(), conn.LocalAddr(), conn.User())\n\t\treturn nil, fmt.Errorf(\"password rejected for %s\", conn.User())\n\t}\n}\n\n// PublicKeyCallbackFunc is a callback function used by SSH server.\n// It offers a public key for authentication.\ntype PublicKeyCallbackFunc func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error)\n\nfunc defaultSSHPublicKeyCallback(keys map[string]bool) PublicKeyCallbackFunc {\n\tif len(keys) == 0 {\n\t\treturn nil\n\t}\n\n\treturn func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {\n\t\tif keys[string(pubKey.Marshal())] {\n\t\t\treturn &ssh.Permissions{\n\t\t\t\t// Record the public key used for authentication.\n\t\t\t\tExtensions: map[string]string{\n\t\t\t\t\t\"pubkey-fp\": ssh.FingerprintSHA256(pubKey),\n\t\t\t\t},\n\t\t\t}, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"unknown public key for %q\", c.User())\n\t}\n}\n\ntype sshNopConn struct {\n\tsession *sshSession\n}\n\nfunc (c *sshNopConn) Read(b []byte) (n int, err error) {\n\treturn 0, &net.OpError{Op: \"read\", Net: \"ssh\", Source: nil, Addr: nil, Err: errors.New(\"read not supported\")}\n}\n\nfunc (c *sshNopConn) Write(b []byte) (n int, err error) {\n\treturn 0, &net.OpError{Op: \"write\", Net: \"ssh\", Source: nil, Addr: nil, Err: errors.New(\"write not supported\")}\n}\n\nfunc (c *sshNopConn) Close() error {\n\treturn nil\n}\n\nfunc (c *sshNopConn) LocalAddr() net.Addr {\n\treturn &net.TCPAddr{\n\t\tIP:   net.IPv4zero,\n\t\tPort: 0,\n\t}\n}\n\nfunc (c *sshNopConn) RemoteAddr() net.Addr {\n\treturn &net.TCPAddr{\n\t\tIP:   net.IPv4zero,\n\t\tPort: 0,\n\t}\n}\n\nfunc (c *sshNopConn) SetDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"ssh\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *sshNopConn) SetReadDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"ssh\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *sshNopConn) SetWriteDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"ssh\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\ntype sshConn struct {\n\tchannel ssh.Channel\n\tconn    net.Conn\n}\n\nfunc (c *sshConn) Read(b []byte) (n int, err error) {\n\treturn c.channel.Read(b)\n}\n\nfunc (c *sshConn) Write(b []byte) (n int, err error) {\n\treturn c.channel.Write(b)\n}\n\nfunc (c *sshConn) Close() error {\n\treturn c.channel.Close()\n}\n\nfunc (c *sshConn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\nfunc (c *sshConn) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\nfunc (c *sshConn) SetDeadline(t time.Time) error {\n\treturn c.conn.SetDeadline(t)\n}\n\nfunc (c *sshConn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\nfunc (c *sshConn) SetWriteDeadline(t time.Time) error {\n\treturn c.conn.SetWriteDeadline(t)\n}\n"
  },
  {
    "path": "ssh_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc sshDirectForwardRoundtrip(targetURL string, data []byte) error {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SSHDirectForwardConnector(),\n\t\tTransporter: SSHForwardTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SSHForwardHandler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSHDirectForward(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := sshDirectForwardRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc BenchmarkSSHDirectForward(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SSHDirectForwardConnector(),\n\t\tTransporter: SSHForwardTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SSHForwardHandler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkSSHDirectForwardParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SSHDirectForwardConnector(),\n\t\tTransporter: SSHForwardTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SSHForwardHandler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc sshRemoteForwardRoundtrip(t *testing.T, targetURL string, data []byte) (err error) {\n\tln, err := TCPListener(\"\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SSHRemoteForwardConnector(),\n\t\tTransporter: SSHForwardTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SSHForwardHandler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\tconn, err := proxyConn(client, server)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer conn.Close()\n\n\tgo func() {\n\t\tconn, err = client.Connect(conn, \":0\")\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}()\n\n\tc, err := net.Dial(\"tcp\", conn.LocalAddr().String())\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer c.Close()\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tcc, err := net.Dial(\"tcp\", u.Host)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer cc.Close()\n\n\tgo transport(conn, cc)\n\n\tt.Log(\"httpRoundtrip\")\n\treturn httpRoundtrip(c, targetURL, data)\n}\n\n// TODO: fix this test\nfunc _TestSSHRemoteForward(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := sshRemoteForwardRoundtrip(t, httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc httpOverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverSSHTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverSSHTunnelRoundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverSSHTunnel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverSSHTunnelParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverSSHTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverSSHTunnelRoundtrip(httpSrv.URL, sendData,\n\t\t\tnil,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error {\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverSSHTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverSSHTunnelRoundtrip(httpSrv.URL, sendData, nil)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error {\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverSSHTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverSSHTunnelRoundtrip(httpSrv.URL, sendData, nil)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverSSHTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverSSHTunnelRoundtrip(httpSrv.URL, sendData,\n\t\t\tnil,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverSSHTunnelRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverSSHTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverSSHTunnelRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc sshForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := SSHTunnelListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: SSHTunnelTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSHForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := sshForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n"
  },
  {
    "path": "tcp.go",
    "content": "package gost\n\nimport \"net\"\n\n// tcpTransporter is a raw TCP transporter.\ntype tcpTransporter struct{}\n\n// TCPTransporter creates a raw TCP client.\nfunc TCPTransporter() Transporter {\n\treturn &tcpTransporter{}\n}\n\nfunc (tr *tcpTransporter) Dial(addr string, options ...DialOption) (net.Conn, error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = DialTimeout\n\t}\n\tif opts.Chain == nil {\n\t\treturn net.DialTimeout(\"tcp\", addr, timeout)\n\t}\n\treturn opts.Chain.Dial(addr)\n}\n\nfunc (tr *tcpTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\treturn conn, nil\n}\n\nfunc (tr *tcpTransporter) Multiplex() bool {\n\treturn false\n}\n\ntype tcpListener struct {\n\tnet.Listener\n}\n\n// TCPListener creates a Listener for TCP proxy server.\nfunc TCPListener(addr string) (Listener, error) {\n\tladdr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tln, err := net.ListenTCP(\"tcp\", laddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &tcpListener{Listener: tcpKeepAliveListener{ln}}, nil\n}\n\ntype tcpKeepAliveListener struct {\n\t*net.TCPListener\n}\n\nfunc (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {\n\ttc, err := ln.AcceptTCP()\n\tif err != nil {\n\t\treturn\n\t}\n\ttc.SetKeepAlive(true)\n\ttc.SetKeepAlivePeriod(KeepAliveTime)\n\treturn tc, nil\n}\n"
  },
  {
    "path": "tls.go",
    "content": "package gost\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\n\tsmux \"github.com/xtaci/smux\"\n)\n\ntype tlsTransporter struct {\n\ttcpTransporter\n}\n\n// TLSTransporter creates a Transporter that is used by TLS proxy client.\nfunc TLSTransporter() Transporter {\n\treturn &tlsTransporter{}\n}\n\nfunc (tr *tlsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\tif opts.TLSConfig == nil {\n\t\topts.TLSConfig = &tls.Config{InsecureSkipVerify: true}\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\n\treturn wrapTLSClient(conn, opts.TLSConfig, timeout)\n}\n\ntype mtlsTransporter struct {\n\ttcpTransporter\n\tsessions     map[string]*muxSession\n\tsessionMutex sync.Mutex\n}\n\n// MTLSTransporter creates a Transporter that is used by multiplex-TLS proxy client.\nfunc MTLSTransporter() Transporter {\n\treturn &mtlsTransporter{\n\t\tsessions: make(map[string]*muxSession),\n\t}\n}\n\nfunc (tr *mtlsTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tsession, ok := tr.sessions[addr]\n\tif session != nil && session.IsClosed() {\n\t\tdelete(tr.sessions, addr)\n\t\tok = false // session is dead\n\t}\n\tif !ok {\n\t\ttimeout := opts.Timeout\n\t\tif timeout <= 0 {\n\t\t\ttimeout = DialTimeout\n\t\t}\n\n\t\tif opts.Chain == nil {\n\t\t\tconn, err = net.DialTimeout(\"tcp\", addr, timeout)\n\t\t} else {\n\t\t\tconn, err = opts.Chain.Dial(addr)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tsession = &muxSession{conn: conn}\n\t\ttr.sessions[addr] = session\n\t}\n\treturn session.conn, nil\n}\n\nfunc (tr *mtlsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tsession, ok := tr.sessions[opts.Addr]\n\tif !ok || session.session == nil {\n\t\ts, err := tr.initSession(opts.Addr, conn, opts)\n\t\tif err != nil {\n\t\t\tconn.Close()\n\t\t\tdelete(tr.sessions, opts.Addr)\n\t\t\treturn nil, err\n\t\t}\n\t\tsession = s\n\t\ttr.sessions[opts.Addr] = session\n\t}\n\tcc, err := session.GetConn()\n\tif err != nil {\n\t\tsession.Close()\n\t\tdelete(tr.sessions, opts.Addr)\n\t\treturn nil, err\n\t}\n\n\treturn cc, nil\n}\n\nfunc (tr *mtlsTransporter) initSession(addr string, conn net.Conn, opts *HandshakeOptions) (*muxSession, error) {\n\tif opts == nil {\n\t\topts = &HandshakeOptions{}\n\t}\n\tif opts.TLSConfig == nil {\n\t\topts.TLSConfig = &tls.Config{InsecureSkipVerify: true}\n\t}\n\tconn, err := wrapTLSClient(conn, opts.TLSConfig, opts.Timeout)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// stream multiplex\n\tsmuxConfig := smux.DefaultConfig()\n\tsession, err := smux.Client(conn, smuxConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &muxSession{conn: conn, session: session}, nil\n}\n\nfunc (tr *mtlsTransporter) Multiplex() bool {\n\treturn true\n}\n\ntype tlsListener struct {\n\tnet.Listener\n}\n\n// TLSListener creates a Listener for TLS proxy server.\nfunc TLSListener(addr string, config *tls.Config) (Listener, error) {\n\tif config == nil {\n\t\tconfig = DefaultTLSConfig\n\t}\n\tln, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)\n\treturn &tlsListener{ln}, nil\n}\n\ntype mtlsListener struct {\n\tln       net.Listener\n\tconnChan chan net.Conn\n\terrChan  chan error\n}\n\n// MTLSListener creates a Listener for multiplex-TLS proxy server.\nfunc MTLSListener(addr string, config *tls.Config) (Listener, error) {\n\tif config == nil {\n\t\tconfig = DefaultTLSConfig\n\t}\n\tln, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tl := &mtlsListener{\n\t\tln:       tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config),\n\t\tconnChan: make(chan net.Conn, 1024),\n\t\terrChan:  make(chan error, 1),\n\t}\n\tgo l.listenLoop()\n\n\treturn l, nil\n}\n\nfunc (l *mtlsListener) listenLoop() {\n\tfor {\n\t\tconn, err := l.ln.Accept()\n\t\tif err != nil {\n\t\t\tlog.Log(\"[mtls] accept:\", err)\n\t\t\tl.errChan <- err\n\t\t\tclose(l.errChan)\n\t\t\treturn\n\t\t}\n\t\tgo l.mux(conn)\n\t}\n}\n\nfunc (l *mtlsListener) mux(conn net.Conn) {\n\tlog.Logf(\"[mtls] %s - %s\", conn.RemoteAddr(), l.Addr())\n\tsmuxConfig := smux.DefaultConfig()\n\tmux, err := smux.Server(conn, smuxConfig)\n\tif err != nil {\n\t\tlog.Logf(\"[mtls] %s - %s : %s\", conn.RemoteAddr(), l.Addr(), err)\n\t\treturn\n\t}\n\tdefer mux.Close()\n\n\tlog.Logf(\"[mtls] %s <-> %s\", conn.RemoteAddr(), l.Addr())\n\tdefer log.Logf(\"[mtls] %s >-< %s\", conn.RemoteAddr(), l.Addr())\n\n\tfor {\n\t\tstream, err := mux.AcceptStream()\n\t\tif err != nil {\n\t\t\tlog.Log(\"[mtls] accept stream:\", err)\n\t\t\treturn\n\t\t}\n\n\t\tcc := &muxStreamConn{Conn: conn, stream: stream}\n\t\tselect {\n\t\tcase l.connChan <- cc:\n\t\tdefault:\n\t\t\tcc.Close()\n\t\t\tlog.Logf(\"[mtls] %s - %s: connection queue is full\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t}\n\t}\n}\n\nfunc (l *mtlsListener) Accept() (conn net.Conn, err error) {\n\tvar ok bool\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err, ok = <-l.errChan:\n\t\tif !ok {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\nfunc (l *mtlsListener) Addr() net.Addr {\n\treturn l.ln.Addr()\n}\n\nfunc (l *mtlsListener) Close() error {\n\treturn l.ln.Close()\n}\n\n// Wrap a net.Conn into a client tls connection, performing any\n// additional verification as needed.\n//\n// As of go 1.3, crypto/tls only supports either doing no certificate\n// verification, or doing full verification including of the peer's\n// DNS name. For consul, we want to validate that the certificate is\n// signed by a known CA, but because consul doesn't use DNS names for\n// node names, we don't verify the certificate DNS names. Since go 1.3\n// no longer supports this mode of operation, we have to do it\n// manually.\n//\n// This code is taken from consul:\n// https://github.com/hashicorp/consul/blob/master/tlsutil/config.go\nfunc wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration) (net.Conn, error) {\n\tvar err error\n\tvar tlsConn *tls.Conn\n\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout // default timeout\n\t}\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\ttlsConn = tls.Client(conn, tlsConfig)\n\n\t// Otherwise perform handshake, but don't verify the domain\n\t//\n\t// The following is lightly-modified from the doFullHandshake\n\t// method in https://golang.org/src/crypto/tls/handshake_client.go\n\tif err = tlsConn.Handshake(); err != nil {\n\t\ttlsConn.Close()\n\t\treturn nil, err\n\t}\n\n\t// We can do this in `tls.Config.VerifyConnection`, which effective for\n\t// other TLS protocols such as WebSocket. See `route.go:parseChainNode`\n\t/*\n\t\t// If crypto/tls is doing verification, there's no need to do our own.\n\t\tif tlsConfig.InsecureSkipVerify == false {\n\t\t\treturn tlsConn, nil\n\t\t}\n\n\t\t// Similarly if we use host's CA, we can do full handshake\n\t\tif tlsConfig.RootCAs == nil {\n\t\t\treturn tlsConn, nil\n\t\t}\n\n\t\topts := x509.VerifyOptions{\n\t\t\tRoots:         tlsConfig.RootCAs,\n\t\t\tCurrentTime:   time.Now(),\n\t\t\tDNSName:       \"\",\n\t\t\tIntermediates: x509.NewCertPool(),\n\t\t}\n\n\t\tcerts := tlsConn.ConnectionState().PeerCertificates\n\t\tfor i, cert := range certs {\n\t\t\tif i == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\topts.Intermediates.AddCert(cert)\n\t\t}\n\n\t\t_, err = certs[0].Verify(opts)\n\t\tif err != nil {\n\t\t\ttlsConn.Close()\n\t\t\treturn nil, err\n\t\t}\n\t*/\n\n\treturn tlsConn, err\n}\n"
  },
  {
    "path": "tls_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc httpOverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := TLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverTLSRoundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverTLS(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TLSListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverTLSParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := TLSListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := TLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverTLSRoundtrip(httpSrv.URL, sendData,\n\t\t\tnil,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error {\n\tln, err := TLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverTLSRoundtrip(httpSrv.URL, sendData, nil)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error {\n\tln, err := TLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverTLSRoundtrip(httpSrv.URL, sendData, nil)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := TLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverTLSRoundtrip(httpSrv.URL, sendData,\n\t\t\tnil,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverTLSRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := TLSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverTLSRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc tlsForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := TLSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: TLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestTLSForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := tlsForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc httpOverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := MTLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverMTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverMTLSRoundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverMTLS(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := MTLSListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverMTLSParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := MTLSListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := MTLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverMTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverMTLSRoundtrip(httpSrv.URL, sendData,\n\t\t\tnil,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error {\n\tln, err := MTLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverMTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverMTLSRoundtrip(httpSrv.URL, sendData, nil)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error {\n\tln, err := MTLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverMTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverMTLSRoundtrip(httpSrv.URL, sendData, nil)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := MTLSListener(\"\", tlsConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverMTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverMTLSRoundtrip(httpSrv.URL, sendData,\n\t\t\tnil,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverMTLSRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := MTLSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverMTLS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverMTLSRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc mtlsForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := MTLSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: MTLSTransporter(),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestMTLSForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := mtlsForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n"
  },
  {
    "path": "tuntap.go",
    "content": "package gost\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/shadowsocks/go-shadowsocks2/core\"\n\t\"github.com/shadowsocks/go-shadowsocks2/shadowaead\"\n\t\"github.com/songgao/water\"\n\t\"github.com/songgao/water/waterutil\"\n\t\"github.com/xtaci/tcpraw\"\n\t\"golang.org/x/net/ipv4\"\n\t\"golang.org/x/net/ipv6\"\n)\n\nvar mIPProts = map[waterutil.IPProtocol]string{\n\twaterutil.HOPOPT:     \"HOPOPT\",\n\twaterutil.ICMP:       \"ICMP\",\n\twaterutil.IGMP:       \"IGMP\",\n\twaterutil.GGP:        \"GGP\",\n\twaterutil.TCP:        \"TCP\",\n\twaterutil.UDP:        \"UDP\",\n\twaterutil.IPv6_Route: \"IPv6-Route\",\n\twaterutil.IPv6_Frag:  \"IPv6-Frag\",\n\twaterutil.IPv6_ICMP:  \"IPv6-ICMP\",\n}\n\nfunc ipProtocol(p waterutil.IPProtocol) string {\n\tif v, ok := mIPProts[p]; ok {\n\t\treturn v\n\t}\n\treturn fmt.Sprintf(\"unknown(%d)\", p)\n}\n\n// IPRoute is an IP routing entry.\ntype IPRoute struct {\n\tDest    *net.IPNet\n\tGateway net.IP\n}\n\n// TunConfig is the config for TUN device.\ntype TunConfig struct {\n\tName    string\n\tAddr    string\n\tPeer    string // peer addr of point-to-point on MacOS\n\tMTU     int\n\tRoutes  []IPRoute\n\tGateway string\n}\n\ntype tunRouteKey [16]byte\n\nfunc ipToTunRouteKey(ip net.IP) (key tunRouteKey) {\n\tcopy(key[:], ip.To16())\n\treturn\n}\n\ntype tunListener struct {\n\taddr   net.Addr\n\tconns  chan net.Conn\n\tclosed chan struct{}\n\tconfig TunConfig\n}\n\n// TunListener creates a listener for tun tunnel.\nfunc TunListener(cfg TunConfig) (Listener, error) {\n\tthreads := 1\n\tln := &tunListener{\n\t\tconns:  make(chan net.Conn, threads),\n\t\tclosed: make(chan struct{}),\n\t\tconfig: cfg,\n\t}\n\n\tfor i := 0; i < threads; i++ {\n\t\tconn, ifce, err := createTun(cfg)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tln.addr = conn.LocalAddr()\n\n\t\taddrs, _ := ifce.Addrs()\n\t\tlog.Logf(\"[tun] %s: name: %s, mtu: %d, addrs: %s\",\n\t\t\tconn.LocalAddr(), ifce.Name, ifce.MTU, addrs)\n\n\t\tln.conns <- conn\n\t}\n\n\treturn ln, nil\n}\n\nfunc (l *tunListener) Accept() (net.Conn, error) {\n\tselect {\n\tcase conn := <-l.conns:\n\t\treturn conn, nil\n\tcase <-l.closed:\n\t}\n\n\treturn nil, errors.New(\"accept on closed listener\")\n}\n\nfunc (l *tunListener) Addr() net.Addr {\n\treturn l.addr\n}\n\nfunc (l *tunListener) Close() error {\n\tselect {\n\tcase <-l.closed:\n\t\treturn errors.New(\"listener has been closed\")\n\tdefault:\n\t\tclose(l.closed)\n\t}\n\treturn nil\n}\n\ntype tunHandler struct {\n\toptions *HandlerOptions\n\troutes  sync.Map\n\tchExit  chan struct{}\n}\n\n// TunHandler creates a handler for tun tunnel.\nfunc TunHandler(opts ...HandlerOption) Handler {\n\th := &tunHandler{\n\t\toptions: &HandlerOptions{},\n\t\tchExit:  make(chan struct{}, 1),\n\t}\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\n\treturn h\n}\n\nfunc (h *tunHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *tunHandler) Handle(conn net.Conn) {\n\tdefer os.Exit(0)\n\tdefer conn.Close()\n\n\tvar err error\n\tvar raddr net.Addr\n\tif addr := h.options.Node.Remote; addr != \"\" {\n\t\traddr, err = net.ResolveUDPAddr(\"udp\", addr)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[tun] %s: remote addr: %v\", conn.LocalAddr(), err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tvar tempDelay time.Duration\n\tfor {\n\t\terr := func() error {\n\t\t\tvar err error\n\t\t\tvar pc net.PacketConn\n\t\t\t// fake tcp mode will be ignored when the client specifies a chain.\n\t\t\tif raddr != nil && !h.options.Chain.IsEmpty() {\n\t\t\t\tcc, err := h.options.Chain.DialContext(context.Background(), \"udp\", raddr.String())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tvar ok bool\n\t\t\t\tpc, ok = cc.(net.PacketConn)\n\t\t\t\tif !ok {\n\t\t\t\t\terr = errors.New(\"not a packet connection\")\n\t\t\t\t\tlog.Logf(\"[tun] %s - %s: %s\", conn.LocalAddr(), raddr, err)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif h.options.TCPMode {\n\t\t\t\t\tif raddr != nil {\n\t\t\t\t\t\tpc, err = tcpraw.Dial(\"tcp\", raddr.String())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpc, err = tcpraw.Listen(\"tcp\", h.options.Node.Addr)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tladdr, _ := net.ResolveUDPAddr(\"udp\", h.options.Node.Addr)\n\t\t\t\t\tpc, err = net.ListenUDP(\"udp\", laddr)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tpc, err = h.initTunnelConn(pc)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn h.transportTun(conn, pc, raddr)\n\t\t}()\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[tun] %s: %v\", conn.LocalAddr(), err)\n\t\t}\n\n\t\tselect {\n\t\tcase <-h.chExit:\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif tempDelay == 0 {\n\t\t\t\ttempDelay = 1000 * time.Millisecond\n\t\t\t} else {\n\t\t\t\ttempDelay *= 2\n\t\t\t}\n\t\t\tif max := 6 * time.Second; tempDelay > max {\n\t\t\t\ttempDelay = max\n\t\t\t}\n\t\t\ttime.Sleep(tempDelay)\n\t\t\tcontinue\n\t\t}\n\t\ttempDelay = 0\n\t}\n}\n\nfunc (h *tunHandler) initTunnelConn(pc net.PacketConn) (net.PacketConn, error) {\n\tif len(h.options.Users) > 0 && h.options.Users[0] != nil {\n\t\tpasswd, _ := h.options.Users[0].Password()\n\t\tcipher, err := core.PickCipher(h.options.Users[0].Username(), nil, passwd)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpc = cipher.PacketConn(pc)\n\t}\n\treturn pc, nil\n}\n\nfunc (h *tunHandler) findRouteFor(dst net.IP) net.Addr {\n\tif v, ok := h.routes.Load(ipToTunRouteKey(dst)); ok {\n\t\treturn v.(net.Addr)\n\t}\n\tfor _, route := range h.options.IPRoutes {\n\t\tif route.Dest.Contains(dst) && route.Gateway != nil {\n\t\t\tif v, ok := h.routes.Load(ipToTunRouteKey(route.Gateway)); ok {\n\t\t\t\treturn v.(net.Addr)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.Addr) error {\n\terrc := make(chan error, 1)\n\n\tgo func() {\n\t\tfor {\n\t\t\terr := func() error {\n\t\t\t\tb := sPool.Get().([]byte)\n\t\t\t\tdefer sPool.Put(b)\n\n\t\t\t\tn, err := tun.Read(b)\n\t\t\t\tif err != nil {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase h.chExit <- struct{}{}:\n\t\t\t\t\tdefault:\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tvar src, dst net.IP\n\t\t\t\tif waterutil.IsIPv4(b[:n]) {\n\t\t\t\t\theader, err := ipv4.ParseHeader(b[:n])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Logf(\"[tun] %s: %v\", tun.LocalAddr(), err)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tlog.Logf(\"[tun] %s -> %s %-4s %d/%-4d %-4x %d\",\n\t\t\t\t\t\t\theader.Src, header.Dst, ipProtocol(waterutil.IPv4Protocol(b[:n])),\n\t\t\t\t\t\t\theader.Len, header.TotalLen, header.ID, header.Flags)\n\t\t\t\t\t}\n\t\t\t\t\tsrc, dst = header.Src, header.Dst\n\t\t\t\t} else if waterutil.IsIPv6(b[:n]) {\n\t\t\t\t\theader, err := ipv6.ParseHeader(b[:n])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Logf(\"[tun] %s: %v\", tun.LocalAddr(), err)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tlog.Logf(\"[tun] %s -> %s %s %d %d\",\n\t\t\t\t\t\t\theader.Src, header.Dst,\n\t\t\t\t\t\t\tipProtocol(waterutil.IPProtocol(header.NextHeader)),\n\t\t\t\t\t\t\theader.PayloadLen, header.TrafficClass)\n\t\t\t\t\t}\n\t\t\t\t\tsrc, dst = header.Src, header.Dst\n\t\t\t\t} else {\n\t\t\t\t\tlog.Logf(\"[tun] unknown packet\")\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\t// client side, deliver packet directly.\n\t\t\t\tif raddr != nil {\n\t\t\t\t\t_, err := conn.WriteTo(b[:n], raddr)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\taddr := h.findRouteFor(dst)\n\t\t\t\tif addr == nil {\n\t\t\t\t\tlog.Logf(\"[tun] no route for %s -> %s\", src, dst)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\tif Debug {\n\t\t\t\t\tlog.Logf(\"[tun] find route: %s -> %s\", dst, addr)\n\t\t\t\t}\n\t\t\t\tif _, err := conn.WriteTo(b[:n], addr); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}()\n\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\terr := func() error {\n\t\t\t\tb := sPool.Get().([]byte)\n\t\t\t\tdefer sPool.Put(b)\n\n\t\t\t\tn, addr, err := conn.ReadFrom(b)\n\t\t\t\tif err != nil &&\n\t\t\t\t\terr != shadowaead.ErrShortPacket {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tvar src, dst net.IP\n\t\t\t\tif waterutil.IsIPv4(b[:n]) {\n\t\t\t\t\theader, err := ipv4.ParseHeader(b[:n])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Logf(\"[tun] %s: %v\", tun.LocalAddr(), err)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tlog.Logf(\"[tun] %s -> %s %-4s %d/%-4d %-4x %d\",\n\t\t\t\t\t\t\theader.Src, header.Dst, ipProtocol(waterutil.IPv4Protocol(b[:n])),\n\t\t\t\t\t\t\theader.Len, header.TotalLen, header.ID, header.Flags)\n\t\t\t\t\t}\n\t\t\t\t\tsrc, dst = header.Src, header.Dst\n\t\t\t\t} else if waterutil.IsIPv6(b[:n]) {\n\t\t\t\t\theader, err := ipv6.ParseHeader(b[:n])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Logf(\"[tun] %s: %v\", tun.LocalAddr(), err)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tlog.Logf(\"[tun] %s -> %s %s %d %d\",\n\t\t\t\t\t\t\theader.Src, header.Dst,\n\t\t\t\t\t\t\tipProtocol(waterutil.IPProtocol(header.NextHeader)),\n\t\t\t\t\t\t\theader.PayloadLen, header.TrafficClass)\n\t\t\t\t\t}\n\t\t\t\t\tsrc, dst = header.Src, header.Dst\n\t\t\t\t} else {\n\t\t\t\t\tlog.Logf(\"[tun] unknown packet\")\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\t// client side, deliver packet to tun device.\n\t\t\t\tif raddr != nil {\n\t\t\t\t\t_, err := tun.Write(b[:n])\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\trkey := ipToTunRouteKey(src)\n\t\t\t\tif actual, loaded := h.routes.LoadOrStore(rkey, addr); loaded {\n\t\t\t\t\tif actual.(net.Addr).String() != addr.String() {\n\t\t\t\t\t\tlog.Logf(\"[tun] update route: %s -> %s (old %s)\",\n\t\t\t\t\t\t\tsrc, addr, actual.(net.Addr))\n\t\t\t\t\t\th.routes.Store(rkey, addr)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tlog.Logf(\"[tun] new route: %s -> %s\", src, addr)\n\t\t\t\t}\n\n\t\t\t\tif addr := h.findRouteFor(dst); addr != nil {\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tlog.Logf(\"[tun] find route: %s -> %s\", dst, addr)\n\t\t\t\t\t}\n\t\t\t\t\t_, err := conn.WriteTo(b[:n], addr)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tif _, err := tun.Write(b[:n]); err != nil {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase h.chExit <- struct{}{}:\n\t\t\t\t\tdefault:\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}()\n\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\terr := <-errc\n\tif err != nil && err == io.EOF {\n\t\terr = nil\n\t}\n\treturn err\n}\n\nvar mEtherTypes = map[waterutil.Ethertype]string{\n\twaterutil.IPv4: \"ip\",\n\twaterutil.ARP:  \"arp\",\n\twaterutil.RARP: \"rarp\",\n\twaterutil.IPv6: \"ip6\",\n}\n\nfunc etherType(et waterutil.Ethertype) string {\n\tif s, ok := mEtherTypes[et]; ok {\n\t\treturn s\n\t}\n\treturn fmt.Sprintf(\"unknown(%v)\", et)\n}\n\n// TapConfig is the config for TAP device.\ntype TapConfig struct {\n\tName    string\n\tAddr    string\n\tMTU     int\n\tRoutes  []string\n\tGateway string\n}\n\ntype tapRouteKey [6]byte\n\nfunc hwAddrToTapRouteKey(addr net.HardwareAddr) (key tapRouteKey) {\n\tcopy(key[:], addr)\n\treturn\n}\n\ntype tapListener struct {\n\taddr   net.Addr\n\tconns  chan net.Conn\n\tclosed chan struct{}\n\tconfig TapConfig\n}\n\n// TapListener creates a listener for tap tunnel.\nfunc TapListener(cfg TapConfig) (Listener, error) {\n\tthreads := 1\n\tln := &tapListener{\n\t\tconns:  make(chan net.Conn, threads),\n\t\tclosed: make(chan struct{}),\n\t\tconfig: cfg,\n\t}\n\n\tfor i := 0; i < threads; i++ {\n\t\tconn, ifce, err := createTap(cfg)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tln.addr = conn.LocalAddr()\n\n\t\taddrs, _ := ifce.Addrs()\n\t\tlog.Logf(\"[tap] %s: name: %s, mac: %s, mtu: %d, addrs: %s\",\n\t\t\tconn.LocalAddr(), ifce.Name, ifce.HardwareAddr, ifce.MTU, addrs)\n\n\t\tln.conns <- conn\n\t}\n\treturn ln, nil\n}\n\nfunc (l *tapListener) Accept() (net.Conn, error) {\n\tselect {\n\tcase conn := <-l.conns:\n\t\treturn conn, nil\n\tcase <-l.closed:\n\t}\n\n\treturn nil, errors.New(\"accept on closed listener\")\n}\n\nfunc (l *tapListener) Addr() net.Addr {\n\treturn l.addr\n}\n\nfunc (l *tapListener) Close() error {\n\tselect {\n\tcase <-l.closed:\n\t\treturn errors.New(\"listener has been closed\")\n\tdefault:\n\t\tclose(l.closed)\n\t}\n\treturn nil\n}\n\ntype tapHandler struct {\n\toptions *HandlerOptions\n\troutes  sync.Map\n\tchExit  chan struct{}\n}\n\n// TapHandler creates a handler for tap tunnel.\nfunc TapHandler(opts ...HandlerOption) Handler {\n\th := &tapHandler{\n\t\toptions: &HandlerOptions{},\n\t\tchExit:  make(chan struct{}, 1),\n\t}\n\tfor _, opt := range opts {\n\t\topt(h.options)\n\t}\n\n\treturn h\n}\n\nfunc (h *tapHandler) Init(options ...HandlerOption) {\n\tif h.options == nil {\n\t\th.options = &HandlerOptions{}\n\t}\n\tfor _, opt := range options {\n\t\topt(h.options)\n\t}\n}\n\nfunc (h *tapHandler) Handle(conn net.Conn) {\n\tdefer os.Exit(0)\n\tdefer conn.Close()\n\n\tvar err error\n\tvar raddr net.Addr\n\tif addr := h.options.Node.Remote; addr != \"\" {\n\t\traddr, err = net.ResolveUDPAddr(\"udp\", addr)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[tap] %s: remote addr: %v\", conn.LocalAddr(), err)\n\t\t\treturn\n\t\t}\n\t}\n\n\tvar tempDelay time.Duration\n\tfor {\n\t\terr := func() error {\n\t\t\tvar err error\n\t\t\tvar pc net.PacketConn\n\t\t\t// fake tcp mode will be ignored when the client specifies a chain.\n\t\t\tif raddr != nil && !h.options.Chain.IsEmpty() {\n\t\t\t\tcc, err := h.options.Chain.DialContext(context.Background(), \"udp\", raddr.String())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tvar ok bool\n\t\t\t\tpc, ok = cc.(net.PacketConn)\n\t\t\t\tif !ok {\n\t\t\t\t\terr = errors.New(\"not a packet connection\")\n\t\t\t\t\tlog.Logf(\"[tap] %s - %s: %s\", conn.LocalAddr(), raddr, err)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif h.options.TCPMode {\n\t\t\t\t\tif raddr != nil {\n\t\t\t\t\t\tpc, err = tcpraw.Dial(\"tcp\", raddr.String())\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpc, err = tcpraw.Listen(\"tcp\", h.options.Node.Addr)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tladdr, _ := net.ResolveUDPAddr(\"udp\", h.options.Node.Addr)\n\t\t\t\t\tpc, err = net.ListenUDP(\"udp\", laddr)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tpc, err = h.initTunnelConn(pc)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn h.transportTap(conn, pc, raddr)\n\t\t}()\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[tap] %s: %v\", conn.LocalAddr(), err)\n\t\t}\n\n\t\tselect {\n\t\tcase <-h.chExit:\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif tempDelay == 0 {\n\t\t\t\ttempDelay = 1000 * time.Millisecond\n\t\t\t} else {\n\t\t\t\ttempDelay *= 2\n\t\t\t}\n\t\t\tif max := 6 * time.Second; tempDelay > max {\n\t\t\t\ttempDelay = max\n\t\t\t}\n\t\t\ttime.Sleep(tempDelay)\n\t\t\tcontinue\n\t\t}\n\t\ttempDelay = 0\n\t}\n}\n\nfunc (h *tapHandler) initTunnelConn(pc net.PacketConn) (net.PacketConn, error) {\n\tif len(h.options.Users) > 0 && h.options.Users[0] != nil {\n\t\tpasswd, _ := h.options.Users[0].Password()\n\t\tcipher, err := core.PickCipher(h.options.Users[0].Username(), nil, passwd)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpc = cipher.PacketConn(pc)\n\t}\n\treturn pc, nil\n}\n\nfunc (h *tapHandler) transportTap(tap net.Conn, conn net.PacketConn, raddr net.Addr) error {\n\terrc := make(chan error, 1)\n\n\tgo func() {\n\t\tfor {\n\t\t\terr := func() error {\n\t\t\t\tb := sPool.Get().([]byte)\n\t\t\t\tdefer sPool.Put(b)\n\n\t\t\t\tn, err := tap.Read(b)\n\t\t\t\tif err != nil {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase h.chExit <- struct{}{}:\n\t\t\t\t\tdefault:\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tsrc := waterutil.MACSource(b[:n])\n\t\t\t\tdst := waterutil.MACDestination(b[:n])\n\t\t\t\teType := etherType(waterutil.MACEthertype(b[:n]))\n\n\t\t\t\tif Debug {\n\t\t\t\t\tlog.Logf(\"[tap] %s -> %s %s %d\", src, dst, eType, n)\n\t\t\t\t}\n\n\t\t\t\t// client side, deliver frame directly.\n\t\t\t\tif raddr != nil {\n\t\t\t\t\t_, err := conn.WriteTo(b[:n], raddr)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t// server side, broadcast.\n\t\t\t\tif waterutil.IsBroadcast(dst) {\n\t\t\t\t\tgo h.routes.Range(func(k, v interface{}) bool {\n\t\t\t\t\t\tconn.WriteTo(b[:n], v.(net.Addr))\n\t\t\t\t\t\treturn true\n\t\t\t\t\t})\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\tvar addr net.Addr\n\t\t\t\tif v, ok := h.routes.Load(hwAddrToTapRouteKey(dst)); ok {\n\t\t\t\t\taddr = v.(net.Addr)\n\t\t\t\t}\n\t\t\t\tif addr == nil {\n\t\t\t\t\tlog.Logf(\"[tap] no route for %s -> %s %s %d\", src, dst, eType, n)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\tif _, err := conn.WriteTo(b[:n], addr); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}()\n\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tfor {\n\t\t\terr := func() error {\n\t\t\t\tb := sPool.Get().([]byte)\n\t\t\t\tdefer sPool.Put(b)\n\n\t\t\t\tn, addr, err := conn.ReadFrom(b)\n\t\t\t\tif err != nil &&\n\t\t\t\t\terr != shadowaead.ErrShortPacket {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tsrc := waterutil.MACSource(b[:n])\n\t\t\t\tdst := waterutil.MACDestination(b[:n])\n\t\t\t\teType := etherType(waterutil.MACEthertype(b[:n]))\n\n\t\t\t\tif Debug {\n\t\t\t\t\tlog.Logf(\"[tap] %s -> %s %s %d\", src, dst, eType, n)\n\t\t\t\t}\n\n\t\t\t\t// client side, deliver frame to tap device.\n\t\t\t\tif raddr != nil {\n\t\t\t\t\t_, err := tap.Write(b[:n])\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t// server side, record route.\n\t\t\t\trkey := hwAddrToTapRouteKey(src)\n\t\t\t\tif actual, loaded := h.routes.LoadOrStore(rkey, addr); loaded {\n\t\t\t\t\tif actual.(net.Addr).String() != addr.String() {\n\t\t\t\t\t\tlog.Logf(\"[tap] update route: %s -> %s (old %s)\",\n\t\t\t\t\t\t\tsrc, addr, actual.(net.Addr))\n\t\t\t\t\t\th.routes.Store(rkey, addr)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tlog.Logf(\"[tap] new route: %s -> %s\", src, addr)\n\t\t\t\t}\n\n\t\t\t\tif waterutil.IsBroadcast(dst) {\n\t\t\t\t\tgo h.routes.Range(func(k, v interface{}) bool {\n\t\t\t\t\t\tif k.(tapRouteKey) != rkey {\n\t\t\t\t\t\t\tconn.WriteTo(b[:n], v.(net.Addr))\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tif v, ok := h.routes.Load(hwAddrToTapRouteKey(dst)); ok {\n\t\t\t\t\tif Debug {\n\t\t\t\t\t\tlog.Logf(\"[tap] find route: %s -> %s\", dst, v)\n\t\t\t\t\t}\n\t\t\t\t\t_, err := conn.WriteTo(b[:n], v.(net.Addr))\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tif _, err := tap.Write(b[:n]); err != nil {\n\t\t\t\t\tselect {\n\t\t\t\t\tcase h.chExit <- struct{}{}:\n\t\t\t\t\tdefault:\n\t\t\t\t\t}\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}()\n\n\t\t\tif err != nil {\n\t\t\t\terrc <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\terr := <-errc\n\tif err != nil && err == io.EOF {\n\t\terr = nil\n\t}\n\treturn err\n}\n\ntype tunTapConn struct {\n\tifce *water.Interface\n\taddr net.Addr\n}\n\nfunc (c *tunTapConn) Read(b []byte) (n int, err error) {\n\treturn c.ifce.Read(b)\n}\n\nfunc (c *tunTapConn) Write(b []byte) (n int, err error) {\n\treturn c.ifce.Write(b)\n}\n\nfunc (c *tunTapConn) Close() (err error) {\n\treturn c.ifce.Close()\n}\n\nfunc (c *tunTapConn) LocalAddr() net.Addr {\n\treturn c.addr\n}\n\nfunc (c *tunTapConn) RemoteAddr() net.Addr {\n\treturn &net.IPAddr{}\n}\n\nfunc (c *tunTapConn) SetDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"tuntap\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *tunTapConn) SetReadDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"tuntap\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\nfunc (c *tunTapConn) SetWriteDeadline(t time.Time) error {\n\treturn &net.OpError{Op: \"set\", Net: \"tuntap\", Source: nil, Addr: nil, Err: errors.New(\"deadline not supported\")}\n}\n\n// IsIPv6Multicast reports whether the address addr is an IPv6 multicast address.\nfunc IsIPv6Multicast(addr net.HardwareAddr) bool {\n\treturn addr[0] == 0x33 && addr[1] == 0x33\n}\n"
  },
  {
    "path": "tuntap_darwin.go",
    "content": "package gost\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/songgao/water\"\n)\n\nfunc createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {\n\tip, _, err := net.ParseCIDR(cfg.Addr)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tifce, err := water.New(water.Config{\n\t\tDeviceType: water.TUN,\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmtu := cfg.MTU\n\tif mtu <= 0 {\n\t\tmtu = DefaultMTU\n\t}\n\n\tpeer := cfg.Peer\n\tif peer == \"\" {\n\t\tpeer = ip.String()\n\t}\n\tcmd := fmt.Sprintf(\"ifconfig %s inet %s %s mtu %d up\",\n\t\tifce.Name(), cfg.Addr, peer, mtu)\n\tlog.Log(\"[tun]\", cmd)\n\targs := strings.Split(cmd, \" \")\n\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\terr = fmt.Errorf(\"%s: %v\", cmd, er)\n\t\treturn\n\t}\n\n\tif err = addTunRoutes(ifce.Name(), cfg.Routes...); err != nil {\n\t\treturn\n\t}\n\n\titf, err = net.InterfaceByName(ifce.Name())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn = &tunTapConn{\n\t\tifce: ifce,\n\t\taddr: &net.IPAddr{IP: ip},\n\t}\n\treturn\n}\n\nfunc createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) {\n\terr = errors.New(\"tap is not supported on darwin\")\n\treturn\n}\n\nfunc addTunRoutes(ifName string, routes ...IPRoute) error {\n\tfor _, route := range routes {\n\t\tif route.Dest == nil {\n\t\t\tcontinue\n\t\t}\n\t\tcmd := fmt.Sprintf(\"route add -net %s -interface %s\", route.Dest.String(), ifName)\n\t\tlog.Log(\"[tun]\", cmd)\n\t\targs := strings.Split(cmd, \" \")\n\t\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\t\treturn fmt.Errorf(\"%s: %v\", cmd, er)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tuntap_linux.go",
    "content": "package gost\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/songgao/water\"\n)\n\nfunc createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {\n\tip, _, err := net.ParseCIDR(cfg.Addr)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tifce, err := water.New(water.Config{\n\t\tDeviceType: water.TUN,\n\t\tPlatformSpecificParams: water.PlatformSpecificParams{\n\t\t\tName: cfg.Name,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmtu := cfg.MTU\n\tif mtu <= 0 {\n\t\tmtu = DefaultMTU\n\t}\n\n\tif err = exeCmd(fmt.Sprintf(\"ip link set dev %s mtu %d\", ifce.Name(), mtu)); err != nil {\n\t\tlog.Log(err)\n\t}\n\n\tif err = exeCmd(fmt.Sprintf(\"ip address add %s dev %s\", cfg.Addr, ifce.Name())); err != nil {\n\t\tlog.Log(err)\n\t}\n\n\tif err = exeCmd(fmt.Sprintf(\"ip link set dev %s up\", ifce.Name())); err != nil {\n\t\tlog.Log(err)\n\t}\n\n\tif err = addTunRoutes(ifce.Name(), cfg.Routes...); err != nil {\n\t\treturn\n\t}\n\n\titf, err = net.InterfaceByName(ifce.Name())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn = &tunTapConn{\n\t\tifce: ifce,\n\t\taddr: &net.IPAddr{IP: ip},\n\t}\n\treturn\n}\n\nfunc createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) {\n\tvar ip net.IP\n\tif cfg.Addr != \"\" {\n\t\tip, _, err = net.ParseCIDR(cfg.Addr)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tifce, err := water.New(water.Config{\n\t\tDeviceType: water.TAP,\n\t\tPlatformSpecificParams: water.PlatformSpecificParams{\n\t\t\tName: cfg.Name,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmtu := cfg.MTU\n\tif mtu <= 0 {\n\t\tmtu = DefaultMTU\n\t}\n\n\tif err = exeCmd(fmt.Sprintf(\"ip link set dev %s mtu %d\", ifce.Name(), mtu)); err != nil {\n\t\tlog.Log(err)\n\t}\n\n\tif cfg.Addr != \"\" {\n\t\tif err = exeCmd(fmt.Sprintf(\"ip address add %s dev %s\", cfg.Addr, ifce.Name())); err != nil {\n\t\t\tlog.Log(err)\n\t\t}\n\t}\n\n\tif err = exeCmd(fmt.Sprintf(\"ip link set dev %s up\", ifce.Name())); err != nil {\n\t\tlog.Log(err)\n\t}\n\n\tif err = addTapRoutes(ifce.Name(), cfg.Gateway, cfg.Routes...); err != nil {\n\t\treturn\n\t}\n\n\titf, err = net.InterfaceByName(ifce.Name())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn = &tunTapConn{\n\t\tifce: ifce,\n\t\taddr: &net.IPAddr{IP: ip},\n\t}\n\treturn\n}\n\nfunc addTunRoutes(ifName string, routes ...IPRoute) error {\n\tfor _, route := range routes {\n\t\tif route.Dest == nil {\n\t\t\tcontinue\n\t\t}\n\t\tcmd := fmt.Sprintf(\"ip route add %s dev %s\", route.Dest.String(), ifName)\n\t\tlog.Logf(\"[tun] %s\", cmd)\n\n\t\targs := strings.Split(cmd, \" \")\n\t\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\t\tlog.Logf(\"[tun] %s: %v\", cmd, er)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc addTapRoutes(ifName string, gw string, routes ...string) error {\n\tfor _, route := range routes {\n\t\tif route == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tcmd := fmt.Sprintf(\"ip route add %s via %s dev %s\", route, gw, ifName)\n\t\tlog.Logf(\"[tap] %s\", cmd)\n\n\t\targs := strings.Split(cmd, \" \")\n\t\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\t\tlog.Logf(\"[tap] %s: %v\", cmd, er)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc exeCmd(cmd string) error {\n\tlog.Log(cmd)\n\n\targs := strings.Split(cmd, \" \")\n\tif err := exec.Command(args[0], args[1:]...).Run(); err != nil {\n\t\treturn fmt.Errorf(\"%s: %v\", cmd, err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "tuntap_unix.go",
    "content": "//go:build !linux && !windows && !darwin\n// +build !linux,!windows,!darwin\n\npackage gost\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/songgao/water\"\n)\n\nfunc createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {\n\tip, _, err := net.ParseCIDR(cfg.Addr)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tifce, err := water.New(water.Config{\n\t\tDeviceType: water.TUN,\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmtu := cfg.MTU\n\tif mtu <= 0 {\n\t\tmtu = DefaultMTU\n\t}\n\n\tcmd := fmt.Sprintf(\"ifconfig %s inet %s mtu %d up\", ifce.Name(), cfg.Addr, mtu)\n\tlog.Log(\"[tun]\", cmd)\n\targs := strings.Split(cmd, \" \")\n\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\terr = fmt.Errorf(\"%s: %v\", cmd, er)\n\t\treturn\n\t}\n\n\tif err = addTunRoutes(ifce.Name(), cfg.Routes...); err != nil {\n\t\treturn\n\t}\n\n\titf, err = net.InterfaceByName(ifce.Name())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn = &tunTapConn{\n\t\tifce: ifce,\n\t\taddr: &net.IPAddr{IP: ip},\n\t}\n\treturn\n}\n\nfunc createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) {\n\tip, _, _ := net.ParseCIDR(cfg.Addr)\n\n\tifce, err := water.New(water.Config{\n\t\tDeviceType: water.TAP,\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmtu := cfg.MTU\n\tif mtu <= 0 {\n\t\tmtu = DefaultMTU\n\t}\n\n\tvar cmd string\n\tif cfg.Addr != \"\" {\n\t\tcmd = fmt.Sprintf(\"ifconfig %s inet %s mtu %d up\", ifce.Name(), cfg.Addr, mtu)\n\t} else {\n\t\tcmd = fmt.Sprintf(\"ifconfig %s mtu %d up\", ifce.Name(), mtu)\n\t}\n\tlog.Log(\"[tap]\", cmd)\n\targs := strings.Split(cmd, \" \")\n\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\terr = fmt.Errorf(\"%s: %v\", cmd, er)\n\t\treturn\n\t}\n\n\tif err = addTapRoutes(ifce.Name(), cfg.Gateway, cfg.Routes...); err != nil {\n\t\treturn\n\t}\n\n\titf, err = net.InterfaceByName(ifce.Name())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn = &tunTapConn{\n\t\tifce: ifce,\n\t\taddr: &net.IPAddr{IP: ip},\n\t}\n\treturn\n}\n\nfunc addTunRoutes(ifName string, routes ...IPRoute) error {\n\tfor _, route := range routes {\n\t\tif route.Dest == nil {\n\t\t\tcontinue\n\t\t}\n\t\tcmd := fmt.Sprintf(\"route add -net %s -interface %s\", route.Dest.String(), ifName)\n\t\tlog.Logf(\"[tun] %s\", cmd)\n\t\targs := strings.Split(cmd, \" \")\n\t\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\t\treturn fmt.Errorf(\"%s: %v\", cmd, er)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc addTapRoutes(ifName string, gw string, routes ...string) error {\n\tfor _, route := range routes {\n\t\tif route == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tcmd := fmt.Sprintf(\"route add -net %s dev %s\", route, ifName)\n\t\tif gw != \"\" {\n\t\t\tcmd += \" gw \" + gw\n\t\t}\n\t\tlog.Logf(\"[tap] %s\", cmd)\n\t\targs := strings.Split(cmd, \" \")\n\t\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\t\treturn fmt.Errorf(\"%s: %v\", cmd, er)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "tuntap_windows.go",
    "content": "package gost\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"os/exec\"\n\t\"strings\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/songgao/water\"\n)\n\nfunc createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {\n\tip, ipNet, err := net.ParseCIDR(cfg.Addr)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tifce, err := water.New(water.Config{\n\t\tDeviceType: water.TUN,\n\t\tPlatformSpecificParams: water.PlatformSpecificParams{\n\t\t\tComponentID:   \"tap0901\",\n\t\t\tInterfaceName: cfg.Name,\n\t\t\tNetwork:       cfg.Addr,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tcmd := fmt.Sprintf(\"netsh interface ip set address name=\\\"%s\\\" \"+\n\t\t\"source=static addr=%s mask=%s gateway=none\",\n\t\tifce.Name(), ip.String(), ipMask(ipNet.Mask))\n\tlog.Log(\"[tun]\", cmd)\n\targs := strings.Split(cmd, \" \")\n\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\terr = fmt.Errorf(\"%s: %v\", cmd, er)\n\t\treturn\n\t}\n\n\tif err = addTunRoutes(ifce.Name(), cfg.Gateway, cfg.Routes...); err != nil {\n\t\treturn\n\t}\n\n\titf, err = net.InterfaceByName(ifce.Name())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn = &tunTapConn{\n\t\tifce: ifce,\n\t\taddr: &net.IPAddr{IP: ip},\n\t}\n\treturn\n}\n\nfunc createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) {\n\tip, ipNet, _ := net.ParseCIDR(cfg.Addr)\n\n\tifce, err := water.New(water.Config{\n\t\tDeviceType: water.TAP,\n\t\tPlatformSpecificParams: water.PlatformSpecificParams{\n\t\t\tComponentID:   \"tap0901\",\n\t\t\tInterfaceName: cfg.Name,\n\t\t\tNetwork:       cfg.Addr,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif ip != nil && ipNet != nil {\n\t\tcmd := fmt.Sprintf(\"netsh interface ip set address name=\\\"%s\\\" \"+\n\t\t\t\"source=static addr=%s mask=%s gateway=none\",\n\t\t\tifce.Name(), ip.String(), ipMask(ipNet.Mask))\n\t\tlog.Log(\"[tap]\", cmd)\n\t\targs := strings.Split(cmd, \" \")\n\t\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\t\terr = fmt.Errorf(\"%s: %v\", cmd, er)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif err = addTapRoutes(ifce.Name(), cfg.Gateway, cfg.Routes...); err != nil {\n\t\treturn\n\t}\n\n\titf, err = net.InterfaceByName(ifce.Name())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tconn = &tunTapConn{\n\t\tifce: ifce,\n\t\taddr: &net.IPAddr{IP: ip},\n\t}\n\treturn\n}\n\nfunc addTunRoutes(ifName string, gw string, routes ...IPRoute) error {\n\tfor _, route := range routes {\n\t\tif route.Dest == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tdeleteRoute(ifName, route.Dest.String())\n\n\t\tcmd := fmt.Sprintf(\"netsh interface ip add route prefix=%s interface=\\\"%s\\\" store=active\",\n\t\t\troute.Dest.String(), ifName)\n\t\tif gw != \"\" {\n\t\t\tcmd += \" nexthop=\" + gw\n\t\t}\n\t\tlog.Logf(\"[tun] %s\", cmd)\n\t\targs := strings.Split(cmd, \" \")\n\t\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\t\treturn fmt.Errorf(\"%s: %v\", cmd, er)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc addTapRoutes(ifName string, gw string, routes ...string) error {\n\tfor _, route := range routes {\n\t\tif route == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tdeleteRoute(ifName, route)\n\n\t\tcmd := fmt.Sprintf(\"netsh interface ip add route prefix=%s interface=\\\"%s\\\" store=active\",\n\t\t\troute, ifName)\n\t\tif gw != \"\" {\n\t\t\tcmd += \" nexthop=\" + gw\n\t\t}\n\t\tlog.Logf(\"[tap] %s\", cmd)\n\t\targs := strings.Split(cmd, \" \")\n\t\tif er := exec.Command(args[0], args[1:]...).Run(); er != nil {\n\t\t\treturn fmt.Errorf(\"%s: %v\", cmd, er)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc deleteRoute(ifName string, route string) error {\n\tcmd := fmt.Sprintf(\"netsh interface ip delete route prefix=%s interface=\\\"%s\\\" store=active\",\n\t\troute, ifName)\n\targs := strings.Split(cmd, \" \")\n\treturn exec.Command(args[0], args[1:]...).Run()\n}\n\nfunc ipMask(mask net.IPMask) string {\n\treturn fmt.Sprintf(\"%d.%d.%d.%d\", mask[0], mask[1], mask[2], mask[3])\n}\n"
  },
  {
    "path": "udp.go",
    "content": "package gost\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-log/log\"\n)\n\n// udpTransporter is a raw UDP transporter.\ntype udpTransporter struct{}\n\n// UDPTransporter creates a Transporter for UDP client.\nfunc UDPTransporter() Transporter {\n\treturn &udpTransporter{}\n}\n\nfunc (tr *udpTransporter) Dial(addr string, options ...DialOption) (net.Conn, error) {\n\ttaddr, err := net.ResolveUDPAddr(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconn, err := net.DialUDP(\"udp\", nil, taddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &udpClientConn{\n\t\tUDPConn: conn,\n\t}, nil\n}\n\nfunc (tr *udpTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\treturn conn, nil\n}\n\nfunc (tr *udpTransporter) Multiplex() bool {\n\treturn false\n}\n\n// UDPListenConfig is the config for UDP Listener.\ntype UDPListenConfig struct {\n\tTTL       time.Duration // timeout per connection\n\tBacklog   int           // connection backlog\n\tQueueSize int           // recv queue size per connection\n}\n\ntype udpListener struct {\n\tln       net.PacketConn\n\tconnChan chan net.Conn\n\terrChan  chan error\n\tconnMap  *udpConnMap\n\tconfig   *UDPListenConfig\n}\n\n// UDPListener creates a Listener for UDP server.\nfunc UDPListener(addr string, cfg *UDPListenConfig) (Listener, error) {\n\tladdr, err := net.ResolveUDPAddr(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tln, err := net.ListenUDP(\"udp\", laddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cfg == nil {\n\t\tcfg = &UDPListenConfig{}\n\t}\n\n\tbacklog := cfg.Backlog\n\tif backlog <= 0 {\n\t\tbacklog = defaultBacklog\n\t}\n\n\tl := &udpListener{\n\t\tln:       ln,\n\t\tconnChan: make(chan net.Conn, backlog),\n\t\terrChan:  make(chan error, 1),\n\t\tconnMap:  new(udpConnMap),\n\t\tconfig:   cfg,\n\t}\n\tgo l.listenLoop()\n\treturn l, nil\n}\n\nfunc (l *udpListener) listenLoop() {\n\tfor {\n\t\t// NOTE: this buffer will be released in the udpServerConn after read.\n\t\tb := mPool.Get().([]byte)\n\n\t\tn, raddr, err := l.ln.ReadFrom(b)\n\t\tif err != nil {\n\t\t\tlog.Logf(\"[udp] peer -> %s : %s\", l.Addr(), err)\n\t\t\tl.Close()\n\t\t\tl.errChan <- err\n\t\t\tclose(l.errChan)\n\t\t\treturn\n\t\t}\n\n\t\tconn, ok := l.connMap.Get(raddr.String())\n\t\tif !ok {\n\t\t\tconn = newUDPServerConn(l.ln, raddr, &udpServerConnConfig{\n\t\t\t\tttl:   l.config.TTL,\n\t\t\t\tqsize: l.config.QueueSize,\n\t\t\t\tonClose: func() {\n\t\t\t\t\tl.connMap.Delete(raddr.String())\n\t\t\t\t\tlog.Logf(\"[udp] %s closed (%d)\", raddr, l.connMap.Size())\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tselect {\n\t\t\tcase l.connChan <- conn:\n\t\t\t\tl.connMap.Set(raddr.String(), conn)\n\t\t\t\tlog.Logf(\"[udp] %s -> %s (%d)\", raddr, l.Addr(), l.connMap.Size())\n\t\t\tdefault:\n\t\t\t\tconn.Close()\n\t\t\t\tlog.Logf(\"[udp] %s - %s: connection queue is full (%d)\", raddr, l.Addr(), cap(l.connChan))\n\t\t\t}\n\t\t}\n\n\t\tselect {\n\t\tcase conn.rChan <- b[:n]:\n\t\t\tif Debug {\n\t\t\t\tlog.Logf(\"[udp] %s >>> %s : length %d\", raddr, l.Addr(), n)\n\t\t\t}\n\t\tdefault:\n\t\t\tlog.Logf(\"[udp] %s -> %s : recv queue is full (%d)\", raddr, l.Addr(), cap(conn.rChan))\n\t\t}\n\t}\n}\n\nfunc (l *udpListener) Accept() (conn net.Conn, err error) {\n\tvar ok bool\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err, ok = <-l.errChan:\n\t\tif !ok {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\n\nfunc (l *udpListener) Addr() net.Addr {\n\treturn l.ln.LocalAddr()\n}\n\nfunc (l *udpListener) Close() error {\n\terr := l.ln.Close()\n\tl.connMap.Range(func(k interface{}, v *udpServerConn) bool {\n\t\tv.Close()\n\t\treturn true\n\t})\n\n\treturn err\n}\n\ntype udpConnMap struct {\n\tsize int64\n\tm    sync.Map\n}\n\nfunc (m *udpConnMap) Get(key interface{}) (conn *udpServerConn, ok bool) {\n\tv, ok := m.m.Load(key)\n\tif ok {\n\t\tconn, ok = v.(*udpServerConn)\n\t}\n\treturn\n}\n\nfunc (m *udpConnMap) Set(key interface{}, conn *udpServerConn) {\n\tm.m.Store(key, conn)\n\tatomic.AddInt64(&m.size, 1)\n}\n\nfunc (m *udpConnMap) Delete(key interface{}) {\n\tm.m.Delete(key)\n\tatomic.AddInt64(&m.size, -1)\n}\n\nfunc (m *udpConnMap) Range(f func(key interface{}, value *udpServerConn) bool) {\n\tm.m.Range(func(k, v interface{}) bool {\n\t\treturn f(k, v.(*udpServerConn))\n\t})\n}\n\nfunc (m *udpConnMap) Size() int64 {\n\treturn atomic.LoadInt64(&m.size)\n}\n\n// udpServerConn is a server side connection for UDP client peer, it implements net.Conn and net.PacketConn.\ntype udpServerConn struct {\n\tconn       net.PacketConn\n\traddr      net.Addr\n\trChan      chan []byte\n\tclosed     chan struct{}\n\tcloseMutex sync.Mutex\n\tnopChan    chan int\n\tconfig     *udpServerConnConfig\n}\n\ntype udpServerConnConfig struct {\n\tttl     time.Duration\n\tqsize   int\n\tonClose func()\n}\n\nfunc newUDPServerConn(conn net.PacketConn, raddr net.Addr, cfg *udpServerConnConfig) *udpServerConn {\n\tif conn == nil || raddr == nil {\n\t\treturn nil\n\t}\n\n\tif cfg == nil {\n\t\tcfg = &udpServerConnConfig{}\n\t}\n\tqsize := cfg.qsize\n\tif qsize <= 0 {\n\t\tqsize = defaultQueueSize\n\t}\n\tc := &udpServerConn{\n\t\tconn:    conn,\n\t\traddr:   raddr,\n\t\trChan:   make(chan []byte, qsize),\n\t\tclosed:  make(chan struct{}),\n\t\tnopChan: make(chan int),\n\t\tconfig:  cfg,\n\t}\n\tgo c.ttlWait()\n\treturn c\n}\n\nfunc (c *udpServerConn) Read(b []byte) (n int, err error) {\n\tn, _, err = c.ReadFrom(b)\n\treturn\n}\n\nfunc (c *udpServerConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {\n\tselect {\n\tcase bb := <-c.rChan:\n\t\tn = copy(b, bb)\n\t\tif cap(bb) == mediumBufferSize {\n\t\t\tmPool.Put(bb[:cap(bb)])\n\t\t}\n\tcase <-c.closed:\n\t\terr = errors.New(\"read from closed connection\")\n\t\treturn\n\t}\n\n\tselect {\n\tcase c.nopChan <- n:\n\tdefault:\n\t}\n\n\taddr = c.raddr\n\n\treturn\n}\n\nfunc (c *udpServerConn) Write(b []byte) (n int, err error) {\n\treturn c.WriteTo(b, c.raddr)\n}\n\nfunc (c *udpServerConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {\n\tn, err = c.conn.WriteTo(b, addr)\n\n\tif n > 0 {\n\t\tif Debug {\n\t\t\tlog.Logf(\"[udp] %s <<< %s : length %d\", addr, c.LocalAddr(), n)\n\t\t}\n\n\t\tselect {\n\t\tcase c.nopChan <- n:\n\t\tdefault:\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (c *udpServerConn) Close() error {\n\tc.closeMutex.Lock()\n\tdefer c.closeMutex.Unlock()\n\n\tselect {\n\tcase <-c.closed:\n\t\treturn errors.New(\"connection is closed\")\n\tdefault:\n\t\tif c.config.onClose != nil {\n\t\t\tc.config.onClose()\n\t\t}\n\t\tclose(c.closed)\n\t}\n\treturn nil\n}\n\nfunc (c *udpServerConn) ttlWait() {\n\tttl := c.config.ttl\n\tif ttl == 0 {\n\t\tttl = defaultTTL\n\t}\n\ttimer := time.NewTimer(ttl)\n\tdefer timer.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-c.nopChan:\n\t\t\tif !timer.Stop() {\n\t\t\t\t<-timer.C\n\t\t\t}\n\t\t\ttimer.Reset(ttl)\n\t\tcase <-timer.C:\n\t\t\tc.Close()\n\t\t\treturn\n\t\tcase <-c.closed:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (c *udpServerConn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\nfunc (c *udpServerConn) RemoteAddr() net.Addr {\n\treturn c.raddr\n}\n\nfunc (c *udpServerConn) SetDeadline(t time.Time) error {\n\treturn c.conn.SetDeadline(t)\n}\n\nfunc (c *udpServerConn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\nfunc (c *udpServerConn) SetWriteDeadline(t time.Time) error {\n\treturn c.conn.SetWriteDeadline(t)\n}\n\ntype udpClientConn struct {\n\t*net.UDPConn\n}\n\nfunc (c *udpClientConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\treturn c.UDPConn.Write(b)\n}\n\nfunc (c *udpClientConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {\n\tn, err = c.UDPConn.Read(b)\n\taddr = c.RemoteAddr()\n\treturn\n}\n"
  },
  {
    "path": "vsock.go",
    "content": "package gost\n\nimport (\n\t\"net\"\n\t\"strconv\"\n\n\t\"github.com/mdlayher/vsock\"\n)\n\n// vsockTransporter is a raw VSOCK transporter.\ntype vsockTransporter struct{}\n\n// VSOCKTransporter creates a raw VSOCK client.\nfunc VSOCKTransporter() Transporter {\n\treturn &vsockTransporter{}\n}\n\nfunc (tr *vsockTransporter) Dial(addr string, options ...DialOption) (net.Conn, error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\tif opts.Chain == nil {\n\t\tvAddr, err := parseAddr(addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn vsock.Dial(vAddr.ContextID, vAddr.Port, nil)\n\t}\n\treturn opts.Chain.Dial(addr)\n}\n\nfunc parseUint32(s string) (uint32, error ) {\n\tn, err := strconv.ParseUint(s, 10, 32)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn uint32(n), nil\n}\n\nfunc parseAddr(addr string) (*vsock.Addr, error) {\n\thostStr, portStr, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\thost := uint32(0)\n\tif hostStr != \"\" {\n\t\thost, err = parseUint32(hostStr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tport, err := parseUint32(portStr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &vsock.Addr{ContextID: host, Port: port}, nil\n}\n\nfunc (tr *vsockTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\treturn conn, nil\n}\n\nfunc (tr *vsockTransporter) Multiplex() bool {\n\treturn false\n}\n\n// VSOCKListener creates a Listener for VSOCK proxy server.\nfunc VSOCKListener(addr string) (Listener, error) {\n\tvAddr, err := parseAddr(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn vsock.Listen(vAddr.Port, nil)\n}\n"
  },
  {
    "path": "ws.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/sha1\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/httputil\"\n\t\"sync\"\n\t\"time\"\n\n\t\"net/url\"\n\n\t\"github.com/go-log/log\"\n\t\"github.com/gorilla/websocket\"\n\tsmux \"github.com/xtaci/smux\"\n)\n\nconst (\n\tdefaultWSPath = \"/ws\"\n)\n\n// WSOptions describes the options for websocket.\ntype WSOptions struct {\n\tReadBufferSize    int\n\tWriteBufferSize   int\n\tHandshakeTimeout  time.Duration\n\tEnableCompression bool\n\tUserAgent         string\n\tPath              string\n}\n\ntype wsTransporter struct {\n\ttcpTransporter\n\toptions *WSOptions\n}\n\n// WSTransporter creates a Transporter that is used by websocket proxy client.\nfunc WSTransporter(opts *WSOptions) Transporter {\n\treturn &wsTransporter{\n\t\toptions: opts,\n\t}\n}\n\nfunc (tr *wsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\twsOptions := tr.options\n\tif opts.WSOptions != nil {\n\t\twsOptions = opts.WSOptions\n\t}\n\tif wsOptions == nil {\n\t\twsOptions = &WSOptions{}\n\t}\n\n\tpath := wsOptions.Path\n\tif path == \"\" {\n\t\tpath = defaultWSPath\n\t}\n\turl := url.URL{Scheme: \"ws\", Host: opts.Host, Path: path}\n\treturn websocketClientConn(url.String(), conn, nil, wsOptions)\n}\n\ntype mwsTransporter struct {\n\ttcpTransporter\n\toptions      *WSOptions\n\tsessions     map[string]*muxSession\n\tsessionMutex sync.Mutex\n}\n\n// MWSTransporter creates a Transporter that is used by multiplex-websocket proxy client.\nfunc MWSTransporter(opts *WSOptions) Transporter {\n\treturn &mwsTransporter{\n\t\toptions:  opts,\n\t\tsessions: make(map[string]*muxSession),\n\t}\n}\n\nfunc (tr *mwsTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tsession, ok := tr.sessions[addr]\n\tif session != nil && session.IsClosed() {\n\t\tdelete(tr.sessions, addr)\n\t\tok = false\n\t}\n\tif !ok {\n\t\ttimeout := opts.Timeout\n\t\tif timeout <= 0 {\n\t\t\ttimeout = DialTimeout\n\t\t}\n\n\t\tif opts.Chain == nil {\n\t\t\tconn, err = net.DialTimeout(\"tcp\", addr, timeout)\n\t\t} else {\n\t\t\tconn, err = opts.Chain.Dial(addr)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tsession = &muxSession{conn: conn}\n\t\ttr.sessions[addr] = session\n\t}\n\treturn session.conn, nil\n}\n\nfunc (tr *mwsTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tsession, ok := tr.sessions[opts.Addr]\n\tif !ok || session.session == nil {\n\t\ts, err := tr.initSession(opts.Addr, conn, opts)\n\t\tif err != nil {\n\t\t\tconn.Close()\n\t\t\tdelete(tr.sessions, opts.Addr)\n\t\t\treturn nil, err\n\t\t}\n\t\tsession = s\n\t\ttr.sessions[opts.Addr] = session\n\t}\n\n\tcc, err := session.GetConn()\n\tif err != nil {\n\t\tsession.Close()\n\t\tdelete(tr.sessions, opts.Addr)\n\t\treturn nil, err\n\t}\n\treturn cc, nil\n}\n\nfunc (tr *mwsTransporter) initSession(addr string, conn net.Conn, opts *HandshakeOptions) (*muxSession, error) {\n\tif opts == nil {\n\t\topts = &HandshakeOptions{}\n\t}\n\twsOptions := tr.options\n\tif opts.WSOptions != nil {\n\t\twsOptions = opts.WSOptions\n\t}\n\tif wsOptions == nil {\n\t\twsOptions = &WSOptions{}\n\t}\n\n\tpath := wsOptions.Path\n\tif path == \"\" {\n\t\tpath = defaultWSPath\n\t}\n\turl := url.URL{Scheme: \"ws\", Host: opts.Host, Path: path}\n\tconn, err := websocketClientConn(url.String(), conn, nil, wsOptions)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// stream multiplex\n\tsmuxConfig := smux.DefaultConfig()\n\tsession, err := smux.Client(conn, smuxConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &muxSession{conn: conn, session: session}, nil\n}\n\nfunc (tr *mwsTransporter) Multiplex() bool {\n\treturn true\n}\n\ntype wssTransporter struct {\n\ttcpTransporter\n\toptions *WSOptions\n}\n\n// WSSTransporter creates a Transporter that is used by websocket secure proxy client.\nfunc WSSTransporter(opts *WSOptions) Transporter {\n\treturn &wssTransporter{\n\t\toptions: opts,\n\t}\n}\n\nfunc (tr *wssTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\twsOptions := tr.options\n\tif opts.WSOptions != nil {\n\t\twsOptions = opts.WSOptions\n\t}\n\tif wsOptions == nil {\n\t\twsOptions = &WSOptions{}\n\t}\n\n\tif opts.TLSConfig == nil {\n\t\topts.TLSConfig = &tls.Config{InsecureSkipVerify: true}\n\t}\n\tpath := wsOptions.Path\n\tif path == \"\" {\n\t\tpath = defaultWSPath\n\t}\n\turl := url.URL{Scheme: \"wss\", Host: opts.Host, Path: path}\n\treturn websocketClientConn(url.String(), conn, opts.TLSConfig, wsOptions)\n}\n\ntype mwssTransporter struct {\n\ttcpTransporter\n\toptions      *WSOptions\n\tsessions     map[string]*muxSession\n\tsessionMutex sync.Mutex\n}\n\n// MWSSTransporter creates a Transporter that is used by multiplex-websocket secure proxy client.\nfunc MWSSTransporter(opts *WSOptions) Transporter {\n\treturn &mwssTransporter{\n\t\toptions:  opts,\n\t\tsessions: make(map[string]*muxSession),\n\t}\n}\n\nfunc (tr *mwssTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {\n\topts := &DialOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tsession, ok := tr.sessions[addr]\n\tif session != nil && session.IsClosed() {\n\t\tdelete(tr.sessions, addr)\n\t\tok = false\n\t}\n\tif !ok {\n\t\ttimeout := opts.Timeout\n\t\tif timeout <= 0 {\n\t\t\ttimeout = DialTimeout\n\t\t}\n\n\t\tif opts.Chain == nil {\n\t\t\tconn, err = net.DialTimeout(\"tcp\", addr, timeout)\n\t\t} else {\n\t\t\tconn, err = opts.Chain.Dial(addr)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tsession = &muxSession{conn: conn}\n\t\ttr.sessions[addr] = session\n\t}\n\treturn session.conn, nil\n}\n\nfunc (tr *mwssTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {\n\topts := &HandshakeOptions{}\n\tfor _, option := range options {\n\t\toption(opts)\n\t}\n\n\ttimeout := opts.Timeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\n\ttr.sessionMutex.Lock()\n\tdefer tr.sessionMutex.Unlock()\n\n\tconn.SetDeadline(time.Now().Add(timeout))\n\tdefer conn.SetDeadline(time.Time{})\n\n\tsession, ok := tr.sessions[opts.Addr]\n\tif !ok || session.session == nil {\n\t\ts, err := tr.initSession(opts.Addr, conn, opts)\n\t\tif err != nil {\n\t\t\tconn.Close()\n\t\t\tdelete(tr.sessions, opts.Addr)\n\t\t\treturn nil, err\n\t\t}\n\t\tsession = s\n\t\ttr.sessions[opts.Addr] = session\n\t}\n\tcc, err := session.GetConn()\n\tif err != nil {\n\t\tsession.Close()\n\t\tdelete(tr.sessions, opts.Addr)\n\t\treturn nil, err\n\t}\n\treturn cc, nil\n}\n\nfunc (tr *mwssTransporter) initSession(addr string, conn net.Conn, opts *HandshakeOptions) (*muxSession, error) {\n\tif opts == nil {\n\t\topts = &HandshakeOptions{}\n\t}\n\twsOptions := tr.options\n\tif opts.WSOptions != nil {\n\t\twsOptions = opts.WSOptions\n\t}\n\tif wsOptions == nil {\n\t\twsOptions = &WSOptions{}\n\t}\n\n\ttlsConfig := opts.TLSConfig\n\tif tlsConfig == nil {\n\t\ttlsConfig = &tls.Config{InsecureSkipVerify: true}\n\t}\n\tpath := wsOptions.Path\n\tif path == \"\" {\n\t\tpath = defaultWSPath\n\t}\n\turl := url.URL{Scheme: \"wss\", Host: opts.Host, Path: path}\n\tconn, err := websocketClientConn(url.String(), conn, tlsConfig, wsOptions)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// stream multiplex\n\tsmuxConfig := smux.DefaultConfig()\n\tsession, err := smux.Client(conn, smuxConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &muxSession{conn: conn, session: session}, nil\n}\n\nfunc (tr *mwssTransporter) Multiplex() bool {\n\treturn true\n}\n\ntype wsListener struct {\n\taddr     net.Addr\n\tupgrader *websocket.Upgrader\n\tsrv      *http.Server\n\tconnChan chan net.Conn\n\terrChan  chan error\n}\n\n// WSListener creates a Listener for websocket proxy server.\nfunc WSListener(addr string, options *WSOptions) (Listener, error) {\n\ttcpAddr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif options == nil {\n\t\toptions = &WSOptions{}\n\t}\n\tl := &wsListener{\n\t\tupgrader: &websocket.Upgrader{\n\t\t\tReadBufferSize:    options.ReadBufferSize,\n\t\t\tWriteBufferSize:   options.WriteBufferSize,\n\t\t\tCheckOrigin:       func(r *http.Request) bool { return true },\n\t\t\tEnableCompression: options.EnableCompression,\n\t\t},\n\t\tconnChan: make(chan net.Conn, 1024),\n\t\terrChan:  make(chan error, 1),\n\t}\n\n\tpath := options.Path\n\tif path == \"\" {\n\t\tpath = defaultWSPath\n\t}\n\tmux := http.NewServeMux()\n\tmux.Handle(path, http.HandlerFunc(l.upgrade))\n\tl.srv = &http.Server{\n\t\tAddr:              addr,\n\t\tHandler:           mux,\n\t\tReadHeaderTimeout: 30 * time.Second,\n\t}\n\n\tln, err := net.ListenTCP(\"tcp\", tcpAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl.addr = ln.Addr()\n\n\tgo func() {\n\t\terr := l.srv.Serve(tcpKeepAliveListener{ln})\n\t\tif err != nil {\n\t\t\tl.errChan <- err\n\t\t}\n\t\tclose(l.errChan)\n\t}()\n\tselect {\n\tcase err := <-l.errChan:\n\t\treturn nil, err\n\tdefault:\n\t}\n\n\treturn l, nil\n}\n\nfunc (l *wsListener) upgrade(w http.ResponseWriter, r *http.Request) {\n\tlog.Logf(\"[ws] %s -> %s\", r.RemoteAddr, l.addr)\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(r, false)\n\t\tlog.Log(string(dump))\n\t}\n\tconn, err := l.upgrader.Upgrade(w, r, nil)\n\tif err != nil {\n\t\tlog.Logf(\"[ws] %s - %s : %s\", r.RemoteAddr, l.addr, err)\n\t\treturn\n\t}\n\tselect {\n\tcase l.connChan <- websocketServerConn(conn):\n\tdefault:\n\t\tconn.Close()\n\t\tlog.Logf(\"[ws] %s - %s: connection queue is full\", r.RemoteAddr, l.addr)\n\t}\n}\n\nfunc (l *wsListener) Accept() (conn net.Conn, err error) {\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err = <-l.errChan:\n\t}\n\treturn\n}\n\nfunc (l *wsListener) Close() error {\n\treturn l.srv.Close()\n}\n\nfunc (l *wsListener) Addr() net.Addr {\n\treturn l.addr\n}\n\ntype mwsListener struct {\n\taddr     net.Addr\n\tupgrader *websocket.Upgrader\n\tsrv      *http.Server\n\tconnChan chan net.Conn\n\terrChan  chan error\n}\n\n// MWSListener creates a Listener for multiplex-websocket proxy server.\nfunc MWSListener(addr string, options *WSOptions) (Listener, error) {\n\ttcpAddr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif options == nil {\n\t\toptions = &WSOptions{}\n\t}\n\tl := &mwsListener{\n\t\tupgrader: &websocket.Upgrader{\n\t\t\tReadBufferSize:    options.ReadBufferSize,\n\t\t\tWriteBufferSize:   options.WriteBufferSize,\n\t\t\tCheckOrigin:       func(r *http.Request) bool { return true },\n\t\t\tEnableCompression: options.EnableCompression,\n\t\t},\n\t\tconnChan: make(chan net.Conn, 1024),\n\t\terrChan:  make(chan error, 1),\n\t}\n\n\tpath := options.Path\n\tif path == \"\" {\n\t\tpath = defaultWSPath\n\t}\n\n\tmux := http.NewServeMux()\n\tmux.Handle(path, http.HandlerFunc(l.upgrade))\n\tl.srv = &http.Server{\n\t\tAddr:              addr,\n\t\tHandler:           mux,\n\t\tReadHeaderTimeout: 30 * time.Second,\n\t}\n\n\tln, err := net.ListenTCP(\"tcp\", tcpAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl.addr = ln.Addr()\n\n\tgo func() {\n\t\terr := l.srv.Serve(tcpKeepAliveListener{ln})\n\t\tif err != nil {\n\t\t\tl.errChan <- err\n\t\t}\n\t\tclose(l.errChan)\n\t}()\n\tselect {\n\tcase err := <-l.errChan:\n\t\treturn nil, err\n\tdefault:\n\t}\n\n\treturn l, nil\n}\n\nfunc (l *mwsListener) upgrade(w http.ResponseWriter, r *http.Request) {\n\tlog.Logf(\"[mws] %s -> %s\", r.RemoteAddr, l.addr)\n\tif Debug {\n\t\tdump, _ := httputil.DumpRequest(r, false)\n\t\tlog.Log(string(dump))\n\t}\n\tconn, err := l.upgrader.Upgrade(w, r, nil)\n\tif err != nil {\n\t\tlog.Logf(\"[mws] %s - %s : %s\", r.RemoteAddr, l.addr, err)\n\t\treturn\n\t}\n\n\tl.mux(websocketServerConn(conn))\n}\n\nfunc (l *mwsListener) mux(conn net.Conn) {\n\tsmuxConfig := smux.DefaultConfig()\n\tmux, err := smux.Server(conn, smuxConfig)\n\tif err != nil {\n\t\tlog.Logf(\"[mws] %s - %s : %s\", conn.RemoteAddr(), l.Addr(), err)\n\t\treturn\n\t}\n\tdefer mux.Close()\n\n\tlog.Logf(\"[mws] %s <-> %s\", conn.RemoteAddr(), l.Addr())\n\tdefer log.Logf(\"[mws] %s >-< %s\", conn.RemoteAddr(), l.Addr())\n\n\tfor {\n\t\tstream, err := mux.AcceptStream()\n\t\tif err != nil {\n\t\t\tlog.Log(\"[mws] accept stream:\", err)\n\t\t\treturn\n\t\t}\n\n\t\tcc := &muxStreamConn{Conn: conn, stream: stream}\n\t\tselect {\n\t\tcase l.connChan <- cc:\n\t\tdefault:\n\t\t\tcc.Close()\n\t\t\tlog.Logf(\"[mws] %s - %s: connection queue is full\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t}\n\t}\n}\n\nfunc (l *mwsListener) Accept() (conn net.Conn, err error) {\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err = <-l.errChan:\n\t}\n\treturn\n}\n\nfunc (l *mwsListener) Close() error {\n\treturn l.srv.Close()\n}\n\nfunc (l *mwsListener) Addr() net.Addr {\n\treturn l.addr\n}\n\ntype wssListener struct {\n\t*wsListener\n}\n\n// WSSListener creates a Listener for websocket secure proxy server.\nfunc WSSListener(addr string, tlsConfig *tls.Config, options *WSOptions) (Listener, error) {\n\ttcpAddr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif options == nil {\n\t\toptions = &WSOptions{}\n\t}\n\tl := &wssListener{\n\t\twsListener: &wsListener{\n\t\t\tupgrader: &websocket.Upgrader{\n\t\t\t\tReadBufferSize:    options.ReadBufferSize,\n\t\t\t\tWriteBufferSize:   options.WriteBufferSize,\n\t\t\t\tCheckOrigin:       func(r *http.Request) bool { return true },\n\t\t\t\tEnableCompression: options.EnableCompression,\n\t\t\t},\n\t\t\tconnChan: make(chan net.Conn, 1024),\n\t\t\terrChan:  make(chan error, 1),\n\t\t},\n\t}\n\n\tif tlsConfig == nil {\n\t\ttlsConfig = DefaultTLSConfig\n\t}\n\n\tpath := options.Path\n\tif path == \"\" {\n\t\tpath = defaultWSPath\n\t}\n\n\tmux := http.NewServeMux()\n\tmux.Handle(path, http.HandlerFunc(l.upgrade))\n\tl.srv = &http.Server{\n\t\tAddr:              addr,\n\t\tTLSConfig:         tlsConfig,\n\t\tHandler:           mux,\n\t\tReadHeaderTimeout: 30 * time.Second,\n\t}\n\n\tln, err := net.ListenTCP(\"tcp\", tcpAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl.addr = ln.Addr()\n\n\tgo func() {\n\t\terr := l.srv.Serve(tls.NewListener(tcpKeepAliveListener{ln}, tlsConfig))\n\t\tif err != nil {\n\t\t\tl.errChan <- err\n\t\t}\n\t\tclose(l.errChan)\n\t}()\n\tselect {\n\tcase err := <-l.errChan:\n\t\treturn nil, err\n\tdefault:\n\t}\n\n\treturn l, nil\n}\n\ntype mwssListener struct {\n\t*mwsListener\n}\n\n// MWSSListener creates a Listener for multiplex-websocket secure proxy server.\nfunc MWSSListener(addr string, tlsConfig *tls.Config, options *WSOptions) (Listener, error) {\n\ttcpAddr, err := net.ResolveTCPAddr(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif options == nil {\n\t\toptions = &WSOptions{}\n\t}\n\tl := &mwssListener{\n\t\tmwsListener: &mwsListener{\n\t\t\tupgrader: &websocket.Upgrader{\n\t\t\t\tReadBufferSize:    options.ReadBufferSize,\n\t\t\t\tWriteBufferSize:   options.WriteBufferSize,\n\t\t\t\tCheckOrigin:       func(r *http.Request) bool { return true },\n\t\t\t\tEnableCompression: options.EnableCompression,\n\t\t\t},\n\t\t\tconnChan: make(chan net.Conn, 1024),\n\t\t\terrChan:  make(chan error, 1),\n\t\t},\n\t}\n\n\tif tlsConfig == nil {\n\t\ttlsConfig = DefaultTLSConfig\n\t}\n\n\tpath := options.Path\n\tif path == \"\" {\n\t\tpath = defaultWSPath\n\t}\n\n\tmux := http.NewServeMux()\n\tmux.Handle(path, http.HandlerFunc(l.upgrade))\n\tl.srv = &http.Server{\n\t\tAddr:              addr,\n\t\tTLSConfig:         tlsConfig,\n\t\tHandler:           mux,\n\t\tReadHeaderTimeout: 30 * time.Second,\n\t}\n\n\tln, err := net.ListenTCP(\"tcp\", tcpAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tl.addr = ln.Addr()\n\n\tgo func() {\n\t\terr := l.srv.Serve(tls.NewListener(tcpKeepAliveListener{ln}, tlsConfig))\n\t\tif err != nil {\n\t\t\tl.errChan <- err\n\t\t}\n\t\tclose(l.errChan)\n\t}()\n\tselect {\n\tcase err := <-l.errChan:\n\t\treturn nil, err\n\tdefault:\n\t}\n\n\treturn l, nil\n}\n\nvar keyGUID = []byte(\"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\")\n\nfunc computeAcceptKey(challengeKey string) string {\n\th := sha1.New()\n\th.Write([]byte(challengeKey))\n\th.Write(keyGUID)\n\treturn base64.StdEncoding.EncodeToString(h.Sum(nil))\n}\n\nfunc generateChallengeKey() (string, error) {\n\tp := make([]byte, 16)\n\tif _, err := io.ReadFull(rand.Reader, p); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.StdEncoding.EncodeToString(p), nil\n}\n\n// TODO: due to the concurrency control in the websocket.Conn,\n// a data race may be met when using with multiplexing.\n// See: https://godoc.org/gopkg.in/gorilla/websocket.v1#hdr-Concurrency\ntype websocketConn struct {\n\tconn *websocket.Conn\n\trb   []byte\n}\n\nfunc websocketClientConn(url string, conn net.Conn, tlsConfig *tls.Config, options *WSOptions) (net.Conn, error) {\n\tif options == nil {\n\t\toptions = &WSOptions{}\n\t}\n\n\ttimeout := options.HandshakeTimeout\n\tif timeout <= 0 {\n\t\ttimeout = HandshakeTimeout\n\t}\n\n\tdialer := websocket.Dialer{\n\t\tReadBufferSize:    options.ReadBufferSize,\n\t\tWriteBufferSize:   options.WriteBufferSize,\n\t\tTLSClientConfig:   tlsConfig,\n\t\tHandshakeTimeout:  timeout,\n\t\tEnableCompression: options.EnableCompression,\n\t\tNetDial: func(net, addr string) (net.Conn, error) {\n\t\t\treturn conn, nil\n\t\t},\n\t}\n\theader := http.Header{}\n\theader.Set(\"User-Agent\", DefaultUserAgent)\n\tif options.UserAgent != \"\" {\n\t\theader.Set(\"User-Agent\", options.UserAgent)\n\t}\n\tc, resp, err := dialer.Dial(url, header)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tresp.Body.Close()\n\treturn &websocketConn{conn: c}, nil\n}\n\nfunc websocketServerConn(conn *websocket.Conn) net.Conn {\n\t// conn.EnableWriteCompression(true)\n\treturn &websocketConn{\n\t\tconn: conn,\n\t}\n}\n\nfunc (c *websocketConn) Read(b []byte) (n int, err error) {\n\tif len(c.rb) == 0 {\n\t\t_, c.rb, err = c.conn.ReadMessage()\n\t}\n\tn = copy(b, c.rb)\n\tc.rb = c.rb[n:]\n\treturn\n}\n\nfunc (c *websocketConn) Write(b []byte) (n int, err error) {\n\terr = c.conn.WriteMessage(websocket.BinaryMessage, b)\n\tn = len(b)\n\treturn\n}\n\nfunc (c *websocketConn) Close() error {\n\treturn c.conn.Close()\n}\n\nfunc (c *websocketConn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\nfunc (c *websocketConn) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\nfunc (c *websocketConn) SetDeadline(t time.Time) error {\n\tif err := c.SetReadDeadline(t); err != nil {\n\t\treturn err\n\t}\n\treturn c.SetWriteDeadline(t)\n}\nfunc (c *websocketConn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\nfunc (c *websocketConn) SetWriteDeadline(t time.Time) error {\n\treturn c.conn.SetWriteDeadline(t)\n}\n"
  },
  {
    "path": "ws_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc httpOverWSRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverWSRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverWS(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverWSParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverWSRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverWSRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverWSRoundtrip(targetURL string, data []byte) error {\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverWSRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverWSRoundtrip(targetURL string, data []byte) error {\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverWS(t *testing.T) {\n\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverWSRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverWSRoundtrip(targetURL string, data []byte,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverWSRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverWSRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverWSRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc wsForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := WSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: WSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestWSForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := wsForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc httpOverMWSRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverMWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverMWSRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverMWS(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverMWSParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tb.Log(ln.Addr())\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverMWSRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverMWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverMWSRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverMWSRoundtrip(targetURL string, data []byte) error {\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverMWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverMWSRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverMWSRoundtrip(targetURL string, data []byte) error {\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverMWS(t *testing.T) {\n\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverMWSRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverMWSRoundtrip(targetURL string, data []byte,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverMWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverMWSRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverMWSRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverMWS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverMWSRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc mwsForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := MWSListener(\"\", nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: MWSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestMWSForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := mwsForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n"
  },
  {
    "path": "wss_test.go",
    "content": "package gost\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\"\n)\n\nfunc httpOverWSSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := WSSListener(\"\", tlsConfig, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverWSSRoundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverWSS(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := WSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverWSSParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := WSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverWSSRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := WSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverWSSRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverWSSRoundtrip(targetURL string, data []byte) error {\n\tln, err := WSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverWSSRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverWSSRoundtrip(targetURL string, data []byte) error {\n\tln, err := WSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverWSS(t *testing.T) {\n\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverWSSRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverWSSRoundtrip(targetURL string, data []byte,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := WSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverWSSRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverWSSRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := WSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverWSSRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc wssForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := WSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: WSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestWSSForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := wssForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n\nfunc httpOverMWSSRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(clientInfo),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestHTTPOverMWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range httpProxyTests {\n\t\terr := httpOverMWSSRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)\n\t\tif err == nil {\n\t\t\tif tc.errStr != \"\" {\n\t\t\t\tt.Errorf(\"#%d should failed with error %s\", i, tc.errStr)\n\t\t\t}\n\t\t} else {\n\t\t\tif tc.errStr == \"\" {\n\t\t\t\tt.Errorf(\"#%d got error %v\", i, err)\n\t\t\t}\n\t\t\tif err.Error() != tc.errStr {\n\t\t\t\tt.Errorf(\"#%d got error %v, want %v\", i, err, tc.errStr)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverMWSS(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tfor i := 0; i < b.N; i++ {\n\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\tb.Error(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkHTTPOverMWSSParallel(b *testing.B) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\tb.Error(err)\n\t}\n\n\tb.Log(ln.Addr())\n\tclient := &Client{\n\t\tConnector:   HTTPConnector(url.UserPassword(\"admin\", \"123456\")),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: HTTPHandler(\n\t\t\tUsersHandlerOption(url.UserPassword(\"admin\", \"123456\")),\n\t\t),\n\t}\n\tgo server.Run()\n\tdefer server.Close()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tif err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc socks5OverMWSSRoundtrip(targetURL string, data []byte,\n\tclientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {\n\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS5Connector(clientInfo),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: SOCKS5Handler(\n\t\t\tUsersHandlerOption(serverInfo...),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS5OverMWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range socks5ProxyTests {\n\t\terr := socks5OverMWSSRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.cliUser,\n\t\t\ttc.srvUsers,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc socks4OverMWSSRoundtrip(targetURL string, data []byte) error {\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4Connector(),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4OverMWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4OverMWSSRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc socks4aOverMWSSRoundtrip(targetURL string, data []byte) error {\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SOCKS4AConnector(),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SOCKS4Handler(),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSOCKS4AOverMWSS(t *testing.T) {\n\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := socks4aOverMWSSRoundtrip(httpSrv.URL, sendData)\n\t// t.Logf(\"#%d %v\", i, err)\n\tif err != nil {\n\t\tt.Errorf(\"got error: %v\", err)\n\t}\n}\n\nfunc ssOverMWSSRoundtrip(targetURL string, data []byte,\n\tclientInfo, serverInfo *url.Userinfo) error {\n\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ShadowConnector(clientInfo),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler: ShadowHandler(\n\t\t\tUsersHandlerOption(serverInfo),\n\t\t),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSSOverMWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tfor i, tc := range ssProxyTests {\n\t\terr := ssOverMWSSRoundtrip(httpSrv.URL, sendData,\n\t\t\ttc.clientCipher,\n\t\t\ttc.serverCipher,\n\t\t)\n\t\tif err == nil {\n\t\t\tif !tc.pass {\n\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t}\n\t\t} else {\n\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\tif tc.pass {\n\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc sniOverMWSSRoundtrip(targetURL string, data []byte, host string) error {\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   SNIConnector(host),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  SNIHandler(HostHandlerOption(u.Host)),\n\t}\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn sniRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestSNIOverMWSS(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\thttpsSrv := httptest.NewTLSServer(httpTestHandler)\n\tdefer httpsSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\tvar sniProxyTests = []struct {\n\t\ttargetURL string\n\t\thost      string\n\t\tpass      bool\n\t}{\n\t\t{httpSrv.URL, \"\", true},\n\t\t{httpSrv.URL, \"example.com\", true},\n\t\t{httpsSrv.URL, \"\", true},\n\t\t{httpsSrv.URL, \"example.com\", true},\n\t}\n\n\tfor i, tc := range sniProxyTests {\n\t\ttc := tc\n\t\tt.Run(fmt.Sprintf(\"#%d\", i), func(t *testing.T) {\n\t\t\terr := sniOverMWSSRoundtrip(tc.targetURL, sendData, tc.host)\n\t\t\tif err == nil {\n\t\t\t\tif !tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d should failed\", i)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// t.Logf(\"#%d %v\", i, err)\n\t\t\t\tif tc.pass {\n\t\t\t\t\tt.Errorf(\"#%d got error: %v\", i, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc mwssForwardTunnelRoundtrip(targetURL string, data []byte) error {\n\tln, err := MWSSListener(\"\", nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tu, err := url.Parse(targetURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclient := &Client{\n\t\tConnector:   ForwardConnector(),\n\t\tTransporter: MWSSTransporter(nil),\n\t}\n\n\tserver := &Server{\n\t\tListener: ln,\n\t\tHandler:  TCPDirectForwardHandler(u.Host),\n\t}\n\tserver.Handler.Init()\n\n\tgo server.Run()\n\tdefer server.Close()\n\n\treturn proxyRoundtrip(client, server, targetURL, data)\n}\n\nfunc TestMWSSForwardTunnel(t *testing.T) {\n\thttpSrv := httptest.NewServer(httpTestHandler)\n\tdefer httpSrv.Close()\n\n\tsendData := make([]byte, 128)\n\trand.Read(sendData)\n\n\terr := mwssForwardTunnelRoundtrip(httpSrv.URL, sendData)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n}\n"
  }
]