Compare commits

..

88 Commits

Author SHA1 Message Date
ginuerzh
87d6a2fdc2 update Dockerfile 2024-10-11 16:02:44 +08:00
ginuerzh
91e12c4428 update github actions 2024-10-11 00:23:09 +08:00
ginuerzh
48c7970942
Merge pull request #1038 from ShuBo6/master
fix: file.Close()
2024-08-01 21:00:06 +08:00
ginuerzh
de49ba2aba
Merge pull request #1042 from mengzhuo/mengzhuo-patch-1
chore: add riscv64 in buildx
2024-08-01 20:59:30 +08:00
Meng Zhuo
2732481bfc
chore: add riscv64 in buildx
Tested on visionfive2
```
root@visionfive2-1:~/gost/cmd/gost# uname -a
Linux visionfive2-1 5.15.0-starfive #1 SMP Sun Jun 11 07:48:39 UTC 2023 riscv64 GNU/Linux
root@visionfive2-1:~/gost/cmd/gost# ./gost -V
gost 2.12.0 (go1.22.3 linux/riscv64)
```
2024-07-22 11:08:35 +08:00
ShuBo6
e30f120045 fix: file.Close() 2024-07-07 13:00:14 +08:00
ginuerzh
08c54cd8af update go.mod 2024-06-13 23:39:11 +08:00
ginuerzh
31a9a45495
Merge pull request #1021 from ginuerzh/dependabot/go_modules/github.com/quic-go/quic-go-0.42.0
Bump github.com/quic-go/quic-go from 0.32.0 to 0.42.0
2024-06-13 23:12:06 +08:00
ginuerzh
81128f9439
Merge branch 'master' into dependabot/go_modules/github.com/quic-go/quic-go-0.42.0 2024-06-13 23:11:45 +08:00
ginuerzh
8508797355
Merge pull request #1024 from ginuerzh/dependabot/go_modules/golang.org/x/net-0.23.0
Bump golang.org/x/net from 0.10.0 to 0.23.0
2024-06-13 23:07:27 +08:00
ginuerzh
654791e635
Merge branch 'master' into dependabot/go_modules/golang.org/x/net-0.23.0 2024-06-13 23:07:11 +08:00
ginuerzh
ac1e8968d3
Merge pull request #1030 from ge9/master
use outgoing interface's IP for UDP relay port
2024-06-13 23:04:54 +08:00
ginuerzh
ee07120254
Merge pull request #1014 from ChengDaqi2023/oscs_fix_cnbkq28au51oj0c3u3o0
fix(sec): upgrade github.com/quic-go/quic-go to 0.40.1
2024-06-13 23:02:27 +08:00
ginuerzh
32b79a37fa
Merge branch 'master' into oscs_fix_cnbkq28au51oj0c3u3o0 2024-06-13 23:02:17 +08:00
ginuerzh
44aac5a5d9
Merge pull request #983 from honwen/master
bump deps; bump sdk to 1.21
2024-06-13 22:58:39 +08:00
ginuerzh
17685b4eac
Merge branch 'master' into master 2024-06-13 22:55:58 +08:00
tiancheng91
77262f2454 fix: concurrent map access 2024-06-13 22:49:50 +08:00
tiancheng91
94f812b026 fix: ping result ttl random 2024-06-13 22:49:50 +08:00
tiancheng91
2faecc1be8 feat: support node sort by tcp ping latency 2024-06-13 22:49:50 +08:00
ge9
00c36ab83d use outgoing interface's IP for UDP relay port 2024-06-13 11:00:47 +09:00
dependabot[bot]
cfe4abe4f9
Bump golang.org/x/net from 0.10.0 to 0.23.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.10.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.10.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-19 11:42:07 +00:00
dependabot[bot]
45ebea56ce
Bump github.com/quic-go/quic-go from 0.32.0 to 0.42.0
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.32.0 to 0.42.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.32.0...v0.42.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-02 14:16:42 +00:00
ChengDaqi2023
5e6f5c3df2 update github.com/quic-go/quic-go v0.32.0 to 0.40.1 2024-02-22 21:33:03 +08:00
honwen.chan
0056fd6560 bump deps; bump sdk to 1.21 2024-02-14 23:08:24 +08:00
guoguangwu
fd57e80709 chore: remove refs to deprecated io/ioutil 2024-02-02 17:22:04 +08:00
zzq
7e1af1b557 fix 2024-02-02 17:20:25 +08:00
suguds
13b9748c9c update golang.org/x/crypto v0.5.0 to 0.17.0 2024-02-02 17:20:02 +08:00
Jeffrey Zhang
7a2490134a support vsock 2023-10-10 19:27:13 +08:00
guoguangwu
c5d9bc8907 chore: remove refs to deprecated io/ioutil 2023-06-19 19:27:03 +08:00
dependabot[bot]
1c62376e08 Bump golang.org/x/net from 0.5.0 to 0.7.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.5.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-07 23:52:55 +08:00
ginuerzh
0f7376bd10 go1.20 2023-02-04 18:37:14 +08:00
ginuerzh
8a29ddabea add goreleaser github action 2023-01-31 21:33:14 +08:00
ginuerzh
729d0e7000 v2.11.5 2023-01-20 14:43:13 +08:00
koaiwu
b7beb1729e toSocksAddr supports IPv6 2023-01-19 14:46:23 +08:00
koaiwu
847ee05fd4 parseIP supports IPv6 2023-01-19 14:46:23 +08:00
Anton Tolchanov
c07cdeff92 Do not exit the server loop on obfs4 connection errors
obfs4Listener.Accept() returns an error when obfs4 handshake fails, which in
practice happens routinely when someone scans a machine that has an open
listening port.

Currently the accept loop in server.go exits on first such error, which makes
further connections to the same port impossible.

This change wraps obfs4 handshake errors into a custom error type that
satisfies net.Error and presents itself as temporary, which will prevent such
errors from aborting the server.
2023-01-19 14:43:10 +08:00
ginuerzh
005cff5888 #889: add http tunnel mode for HTTP handler 2022-11-16 15:21:01 +08:00
ginuerzh
aa8312a902 fix issue #883 2022-11-16 14:30:42 +08:00
ginuerzh
b0bb26fc95 v2.11.4 2022-09-03 20:51:23 +08:00
ginuerzh
97dda762e0 fix #870 2022-08-31 17:52:52 +08:00
ginuerzh
81854a61e0 fix snapcraft 2022-08-19 22:30:16 +08:00
ginuerzh
910d58c8ab fix snap 2022-08-19 12:08:17 +08:00
ginuerzh
ac554f670c add proxyAgent options for http/http2 handler 2022-08-18 20:03:03 +08:00
ginuerzh
b9e61dca1a go1.19 2022-08-18 18:21:21 +08:00
Costin Manolache
3322613d3c Wait for the second copy error 2022-08-18 15:32:51 +08:00
PPPPP
7485f9a753 Update go.sum 2022-08-18 13:10:09 +08:00
PPPPP
6129f5e940 Update go.mod 2022-08-18 13:10:09 +08:00
Kebin Liu
a263a9f173 Add older style build comment
To compatible with older Go version on some cloud environment , such as Google App Engine using 1.16
2022-08-18 13:09:38 +08:00
ginuerzh
0247b941ac fix resolver fallback 2022-05-01 20:49:33 +08:00
ginuerzh
31aee78e79 fix issues #820 and #821 2022-05-01 20:15:57 +08:00
IndexDoge
f94293b454 feat: add bind interface
fix: clone route without interface and mark config
2022-04-19 20:02:16 +08:00
ginuerzh
45340b2845 update kcp 2022-04-13 21:06:29 +08:00
ginuerzh
74659324c8 update snapcraft build 2022-04-13 17:09:01 +08:00
ginuerzh
cc87118242 update README_en.md 2022-04-07 23:13:52 +08:00
ginuerzh
3df387579c update README.md 2022-04-07 22:51:42 +08:00
ginuerzh
ca632e8909 fix SO_MARK on non-linux OS 2022-04-07 22:43:12 +08:00
ginuerzh
bbeaafc897 update version 2022-04-07 22:28:32 +08:00
p_caiwfeng
ffecc464fe surround interface name with double quote in case of name have space 2022-04-07 22:08:00 +08:00
purerosefallen
11d4838804 remote dups 2022-04-07 22:07:20 +08:00
purerosefallen
79b086df90 add mark option 2022-04-07 22:07:20 +08:00
hulb
40ccfecb36 update quick-go dep to support golang-1.18 2022-04-05 17:49:36 +08:00
openwrt2223
937b27dd95 update:update package 2021-12-16 22:38:27 +08:00
sleshep
27dec2d2ac update: ugprade quic-go==v0.24.0 2021-11-23 07:45:12 -08:00
naison
8f08304b75 ignore linux error: File exists while add same route twice 2021-08-22 22:57:00 +08:00
guqing637
a4695ece2d Update go.mod and go.sum 2021-03-24 15:04:00 +08:00
guqing637
fc971d7f2d Update dependency ! 2021-03-20 17:41:23 +08:00
Guangming Li
8dd4d8d9a1 fix obfs-tls with shadowsocks aead issue #695 2021-02-06 13:13:40 +08:00
Hubix9
e16ac5a58a Add whitelist/blacklist support for relay 2021-02-06 13:10:58 +08:00
ginuerzh
4712d6c9dd
Merge pull request #689 from btwiuse/master
bump github.com/milosgajdos/tenus to v0.0.3
2021-02-06 13:09:14 +08:00
navigaid
5d69ecf203 bump github.com/milosgajdos/tenus to v0.0.3 2021-01-04 20:17:27 +08:00
Soff
1a56a878ac Add darwin-arm64 for Apple M1 devices 2020-12-07 07:42:31 +08:00
proxy666-dev
b5d8d44c19 fix issue https://github.com/ginuerzh/gost/issues/617 2020-12-06 18:22:53 +08:00
Yang.Liu
3ea5708819 Fix: relay udp forward 2020-11-18 21:43:50 +08:00
Meng Zhuo
1c32df37bb Close connection if authentication failed
libcurl and some proxy application will keep sending authentication in
same connection if auth failed which is not supported.

fixes #583
2020-11-18 21:42:38 +08:00
luyuhuang
fd079dd066 certificate pinning for servers without domain 2020-11-18 21:40:14 +08:00
ginuerzh
f7995ab564
Merge pull request #655 from moonfruit/homebrew
Add installation instructions for Homebrew
2020-10-20 23:29:42 +08:00
MoonFruit
b9a965f4c1
Add installation instructions for Homebrew 2020-10-20 14:43:15 +08:00
ginuerzh
2707a8f0a9 v2.11.1 2020-05-23 21:26:33 +08:00
ginuerzh
d11f824858 update go.mod 2020-05-23 21:19:29 +08:00
ginuerzh
b285bcd6ce support mutual TLS authentication 2020-05-23 21:07:37 +08:00
ginuerzh
60d7e01164 update go.mod 2020-05-23 13:25:09 +08:00
ginuerzh
de43d87af3 rm shadowstream used in tuntap 2020-05-23 11:19:22 +08:00
ginuerzh
2c0eb8df9a fix issue #520 2020-05-23 11:14:49 +08:00
Scott Edlund
6e46ac03c7 Build go binaries without QEMU 2020-04-14 21:43:16 +08:00
hwchan
f0c9079f0a sni: work with non-ssl 2020-04-14 21:31:25 +08:00
Raymond Liu
8ab2fe6f77 Print version info to Stdout instead of Stderr
Otherwise we can't get the right version information by this:
```shell
gost -V | cut -d' ' -f2
```
2020-03-20 21:31:41 +08:00
ginuerzh
d474a0c417 update workflow 2020-03-14 15:05:06 +08:00
ginuerzh
9f1f492b3c add workflow for github actions 2020-03-14 14:21:23 +08:00
57 changed files with 1455 additions and 629 deletions

72
.github/workflows/buildx.yml vendored Normal file
View File

@ -0,0 +1,72 @@
# ref: https://docs.docker.com/ci-cd/github-actions/
# https://blog.oddbit.com/post/2020-09-25-building-multi-architecture-im/
name: docker
on:
push:
branches:
- master
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Prepare
id: prepare
run: |
DOCKER_IMAGE=${{ secrets.DOCKER_IMAGE }}
VERSION=latest
# If this is git tag, use the tag name as a docker tag
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
fi
TAGS="${DOCKER_IMAGE}:${VERSION}"
# If the VERSION looks like a version number, assume that
# this is the most recent version of the image and also
# tag it 'latest'.
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
MAJOR_VERSION=`echo $VERSION | awk '{split($0,a,"."); print a[1]}'`
MINOR_VERSION=`echo $VERSION | awk '{split($0,a,"."); print a[2]}'`
TAGS="$TAGS,${DOCKER_IMAGE}:${MAJOR_VERSION},${DOCKER_IMAGE}:${MAJOR_VERSION}.${MINOR_VERSION},${DOCKER_IMAGE}:latest"
fi
# Set output parameters.
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
echo "docker_image=${DOCKER_IMAGE}" >> $GITHUB_OUTPUT
echo "docker_platforms=linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/s390x,linux/riscv64" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
- name: Environment
run: |
echo home=$HOME
echo git_ref=$GITHUB_REF
echo git_sha=$GITHUB_SHA
echo image=${{ steps.prepare.outputs.docker_image }}
echo tags=${{ steps.prepare.outputs.tags }}
echo platforms=${{ steps.prepare.outputs.docker_platforms }}
echo avail_platforms=${{ steps.buildx.outputs.platforms }}
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Buildx and push
uses: docker/build-push-action@v6
with:
platforms: ${{ steps.prepare.outputs.docker_platforms }}
push: true
tags: ${{ steps.prepare.outputs.tags }}

38
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: goreleaser
on:
push:
# run only against tags
tags:
- 'v*'
permissions:
contents: write
# packages: write
# issues: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- run: git fetch --force --tags
- uses: actions/setup-go@v3
with:
go-version: '1.22'
cache: true
# More assembly might be required: Docker logins, GPG, etc. It all depends
# on your needs.
- uses: goreleaser/goreleaser-action@v4
with:
# either 'goreleaser' (default) or 'goreleaser-pro':
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Your GoReleaser Pro key, if you are using the 'goreleaser-pro'
# distribution:
# GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}

3
.gitignore vendored
View File

@ -9,6 +9,7 @@ _test
release release
debian debian
bin bin
dist/
# Architecture specific extensions/prefixes # Architecture specific extensions/prefixes
*.[568vq] *.[568vq]
@ -30,5 +31,5 @@ _testmain.go
*.bak *.bak
.vscode/
cmd/gost/gost cmd/gost/gost
snap

58
.goreleaser.yaml Normal file
View File

@ -0,0 +1,58 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
# you may remove this if you don't need go generate
# - go generate ./...
builds:
- env:
- CGO_ENABLED=0
main: ./cmd/gost
targets:
- darwin_amd64
- darwin_arm64
- linux_386
- linux_amd64
- linux_amd64_v3
- linux_arm_5
- linux_arm_6
- linux_arm_7
- linux_arm64
- linux_mips_softfloat
- linux_mips_hardfloat
- linux_mipsle_softfloat
- linux_mipsle_hardfloat
- linux_mips64
- linux_mips64le
- linux_s390x
- linux_riscv64
- freebsd_386
- freebsd_amd64
- windows_386
- windows_amd64
- windows_amd64_v3
- windows_arm64
archives:
- format: tar.gz
# use zip for windows archives
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
# The lines beneath this are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

View File

@ -1,19 +1,32 @@
FROM golang:1-alpine as builder FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.5.0 AS xx
RUN apk add --no-cache musl-dev git gcc FROM --platform=$BUILDPLATFORM golang:1.23-alpine3.20 AS builder
ADD . /src COPY --from=xx / /
WORKDIR /src ARG TARGETPLATFORM
ENV GO111MODULE=on RUN xx-info env
RUN cd cmd/gost && go build ENV CGO_ENABLED=0
FROM alpine:latest ENV XX_VERIFY_STATIC=1
WORKDIR /app
COPY . .
RUN cd cmd/gost && \
xx-go build && \
xx-verify gost
FROM alpine:3.20
# add iptables for tun/tap
RUN apk add --no-cache iptables
WORKDIR /bin/ WORKDIR /bin/
COPY --from=builder /src/cmd/gost/gost . COPY --from=builder /app/cmd/gost/gost .
ENTRYPOINT ["/bin/gost"] ENTRYPOINT ["/bin/gost"]

View File

@ -2,10 +2,11 @@ NAME=gost
BINDIR=bin BINDIR=bin
VERSION=$(shell cat gost.go | grep 'Version =' | sed 's/.*\"\(.*\)\".*/\1/g') VERSION=$(shell cat gost.go | grep 'Version =' | sed 's/.*\"\(.*\)\".*/\1/g')
GOBUILD=CGO_ENABLED=0 go build --ldflags="-s -w" -v -x -a GOBUILD=CGO_ENABLED=0 go build --ldflags="-s -w" -v -x -a
GOFILES=cmd/gost/* GOFILES=cmd/gost/*.go
PLATFORM_LIST = \ PLATFORM_LIST = \
darwin-amd64 \ darwin-amd64 \
darwin-arm64 \
linux-386 \ linux-386 \
linux-amd64 \ linux-amd64 \
linux-armv5 \ linux-armv5 \
@ -18,18 +19,24 @@ PLATFORM_LIST = \
linux-mipsle-hardfloat \ linux-mipsle-hardfloat \
linux-mips64 \ linux-mips64 \
linux-mips64le \ linux-mips64le \
linux-s390x \
linux-riscv64 \
freebsd-386 \ freebsd-386 \
freebsd-amd64 freebsd-amd64
WINDOWS_ARCH_LIST = \ WINDOWS_ARCH_LIST = \
windows-386 \ windows-386 \
windows-amd64 windows-amd64 \
windows-arm64
all: linux-amd64 darwin-amd64 windows-amd64 # Most used all: linux-amd64 darwin-amd64 windows-amd64 # Most used
darwin-amd64: darwin-amd64:
GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES) GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
darwin-arm64:
GOARCH=arm64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
linux-386: linux-386:
GOARCH=386 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES) GOARCH=386 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
@ -66,6 +73,12 @@ linux-mips64:
linux-mips64le: linux-mips64le:
GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES) GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
linux-s390x:
GOARCH=s390x GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
linux-riscv64:
GOARCH=riscv64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
freebsd-386: freebsd-386:
GOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES) GOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
@ -78,6 +91,9 @@ windows-386:
windows-amd64: windows-amd64:
GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe $(GOFILES) GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe $(GOFILES)
windows-arm64:
GOARCH=arm64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe $(GOFILES)
gz_releases=$(addsuffix .gz, $(PLATFORM_LIST)) gz_releases=$(addsuffix .gz, $(PLATFORM_LIST))
zip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST)) zip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST))

View File

@ -1,39 +1,40 @@
gost - GO Simple Tunnel GO Simple Tunnel
====== ======
### GO语言实现的安全隧道 ### GO语言实现的安全隧道
[![GoDoc](https://godoc.org/github.com/ginuerzh/gost?status.svg)](https://godoc.org/github.com/ginuerzh/gost) [![GoDoc](https://godoc.org/github.com/ginuerzh/gost?status.svg)](https://godoc.org/github.com/ginuerzh/gost)
[![Build Status](https://travis-ci.org/ginuerzh/gost.svg?branch=master)](https://travis-ci.org/ginuerzh/gost)
[![Go Report Card](https://goreportcard.com/badge/github.com/ginuerzh/gost)](https://goreportcard.com/report/github.com/ginuerzh/gost) [![Go Report Card](https://goreportcard.com/badge/github.com/ginuerzh/gost)](https://goreportcard.com/report/github.com/ginuerzh/gost)
[![codecov](https://codecov.io/gh/ginuerzh/gost/branch/master/graphs/badge.svg)](https://codecov.io/gh/ginuerzh/gost/branch/master) [![codecov](https://codecov.io/gh/ginuerzh/gost/branch/master/graphs/badge.svg)](https://codecov.io/gh/ginuerzh/gost/branch/master)
[![GitHub release](https://img.shields.io/github/release/ginuerzh/gost.svg)](https://github.com/ginuerzh/gost/releases/latest) [![GitHub release](https://img.shields.io/github/release/ginuerzh/gost.svg)](https://github.com/ginuerzh/gost/releases/latest)
[![Snap Status](https://build.snapcraft.io/badge/ginuerzh/gost.svg)](https://build.snapcraft.io/user/ginuerzh/gost) [![Docker](https://img.shields.io/docker/pulls/ginuerzh/gost.svg)](https://hub.docker.com/r/ginuerzh/gost/)
[![Docker Build Status](https://img.shields.io/docker/build/ginuerzh/gost.svg)](https://hub.docker.com/r/ginuerzh/gost/) [![gost](https://snapcraft.io/gost/badge.svg)](https://snapcraft.io/gost)
[English README](README_en.md) [English README](README_en.md)
### [V3版本已经可用欢迎抢先体验](https://latest.gost.run)
特性 特性
------ ------
* 多端口监听 * 多端口监听
* 可设置转发代理,支持多级转发(代理链) * 可设置转发代理,支持多级转发(代理链)
* 支持标准HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5代理协议 * 支持标准HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5代理协议
* Web代理支持[探测防御](https://docs.ginuerzh.xyz/gost/probe_resist/) * Web代理支持[探测防御](https://v2.gost.run/probe_resist/)
* [支持多种隧道类型](https://docs.ginuerzh.xyz/gost/configuration/) * [支持多种隧道类型](https://v2.gost.run/configuration/)
* [SOCKS5代理支持TLS协商加密](https://docs.ginuerzh.xyz/gost/socks/) * [SOCKS5代理支持TLS协商加密](https://v2.gost.run/socks/)
* [Tunnel UDP over TCP](https://docs.ginuerzh.xyz/gost/socks/) * [Tunnel UDP over TCP](https://v2.gost.run/socks/)
* [TCP/UDP透明代理](https://docs.ginuerzh.xyz/gost/redirect/) * [TCP/UDP透明代理](https://v2.gost.run/redirect/)
* [本地/远程TCP/UDP端口转发](https://docs.ginuerzh.xyz/gost/port-forwarding/) * [本地/远程TCP/UDP端口转发](https://v2.gost.run/port-forwarding/)
* [支持Shadowsocks(TCP/UDP)协议](https://docs.ginuerzh.xyz/gost/ss/) * [支持Shadowsocks(TCP/UDP)协议](https://v2.gost.run/ss/)
* [支持SNI代理](https://docs.ginuerzh.xyz/gost/sni/) * [支持SNI代理](https://v2.gost.run/sni/)
* [权限控制](https://docs.ginuerzh.xyz/gost/permission/) * [权限控制](https://v2.gost.run/permission/)
* [负载均衡](https://docs.ginuerzh.xyz/gost/load-balancing/) * [负载均衡](https://v2.gost.run/load-balancing/)
* [路由控制](https://docs.ginuerzh.xyz/gost/bypass/) * [路由控制](https://v2.gost.run/bypass/)
* DNS[解析](https://docs.ginuerzh.xyz/gost/resolver/)和[代理](https://docs.ginuerzh.xyz/gost/dns/) * DNS[解析](https://v2.gost.run/resolver/)和[代理](https://v2.gost.run/dns/)
* [TUN/TAP设备](https://docs.ginuerzh.xyz/gost/tuntap/) * [TUN/TAP设备](https://v2.gost.run/tuntap/)
Wiki站点: <https://docs.ginuerzh.xyz/gost/> Wiki站点: [v2.gost.run](https://v2.gost.run)
Telegram讨论群: <https://t.me/gogost> Telegram讨论群: <https://t.me/gogost>
@ -57,11 +58,18 @@ go build
#### Docker #### Docker
```bash ```bash
docker pull ginuerzh/gost docker run --rm ginuerzh/gost -V
```
#### Homebrew
```bash
brew install gost
``` ```
#### Ubuntu商店 #### Ubuntu商店
```bash ```bash
sudo snap install core sudo snap install core
sudo snap install gost sudo snap install gost
@ -187,7 +195,7 @@ gost -L=:8080 -F=h2://server_ip:443
``` ```
#### QUIC #### QUIC
gost对QUIC的支持是基于[quic-go](https://github.com/lucas-clemente/quic-go)库。 gost对QUIC的支持是基于[quic-go](https://github.com/quic-go/quic-go)库。
服务端: 服务端:
```bash ```bash

View File

@ -4,33 +4,32 @@ gost - GO Simple Tunnel
### A simple security tunnel written in Golang ### A simple security tunnel written in Golang
[![GoDoc](https://godoc.org/github.com/ginuerzh/gost?status.svg)](https://godoc.org/github.com/ginuerzh/gost) [![GoDoc](https://godoc.org/github.com/ginuerzh/gost?status.svg)](https://godoc.org/github.com/ginuerzh/gost)
[![Build Status](https://travis-ci.org/ginuerzh/gost.svg?branch=master)](https://travis-ci.org/ginuerzh/gost)
[![Go Report Card](https://goreportcard.com/badge/github.com/ginuerzh/gost)](https://goreportcard.com/report/github.com/ginuerzh/gost) [![Go Report Card](https://goreportcard.com/badge/github.com/ginuerzh/gost)](https://goreportcard.com/report/github.com/ginuerzh/gost)
[![codecov](https://codecov.io/gh/ginuerzh/gost/branch/master/graphs/badge.svg)](https://codecov.io/gh/ginuerzh/gost/branch/master) [![codecov](https://codecov.io/gh/ginuerzh/gost/branch/master/graphs/badge.svg)](https://codecov.io/gh/ginuerzh/gost/branch/master)
[![GitHub release](https://img.shields.io/github/release/ginuerzh/gost.svg)](https://github.com/ginuerzh/gost/releases/latest) [![GitHub release](https://img.shields.io/github/release/ginuerzh/gost.svg)](https://github.com/ginuerzh/gost/releases/latest)
[![Snap Status](https://build.snapcraft.io/badge/ginuerzh/gost.svg)](https://build.snapcraft.io/user/ginuerzh/gost) [![Docker](https://img.shields.io/docker/pulls/ginuerzh/gost.svg)](https://hub.docker.com/r/ginuerzh/gost/)
[![Docker Build Status](https://img.shields.io/docker/build/ginuerzh/gost.svg)](https://hub.docker.com/r/ginuerzh/gost/) [![gost](https://snapcraft.io/gost/badge.svg)](https://snapcraft.io/gost)
Features Features
------ ------
* Listening on multiple ports * Listening on multiple ports
* Multi-level forward proxy - proxy chain * Multi-level forward proxy - proxy chain
* Standard HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5 proxy protocols support * Standard HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5 proxy protocols support
* [Probing resistance](https://docs.ginuerzh.xyz/gost/en/probe_resist/) support for web proxy * [Probing resistance](https://v2.gost.run/en/probe_resist/) support for web proxy
* [Support multiple tunnel types](https://docs.ginuerzh.xyz/gost/en/configuration/) * [Support multiple tunnel types](https://v2.gost.run/en/configuration/)
* [TLS encryption via negotiation support for SOCKS5 proxy](https://docs.ginuerzh.xyz/gost/en/socks/) * [TLS encryption via negotiation support for SOCKS5 proxy](https://v2.gost.run/en/socks/)
* [Tunnel UDP over TCP](https://docs.ginuerzh.xyz/gost/en/socks/) * [Tunnel UDP over TCP](https://v2.gost.run/en/socks/)
* [TCP/UDP Transparent proxy](https://docs.ginuerzh.xyz/gost/en/redirect/) * [TCP/UDP Transparent proxy](https://v2.gost.run/en/redirect/)
* [Local/remote TCP/UDP port forwarding](https://docs.ginuerzh.xyz/gost/en/port-forwarding/) * [Local/remote TCP/UDP port forwarding](https://v2.gost.run/en/port-forwarding/)
* [Shadowsocks protocol](https://docs.ginuerzh.xyz/gost/en/ss/) * [Shadowsocks protocol](https://v2.gost.run/en/ss/)
* [SNI proxy](https://docs.ginuerzh.xyz/gost/en/sni/) * [SNI proxy](https://v2.gost.run/en/sni/)
* [Permission control](https://docs.ginuerzh.xyz/gost/en/permission/) * [Permission control](https://v2.gost.run/en/permission/)
* [Load balancing](https://docs.ginuerzh.xyz/gost/en/load-balancing/) * [Load balancing](https://v2.gost.run/en/load-balancing/)
* [Routing control](https://docs.ginuerzh.xyz/gost/en/bypass/) * [Routing control](https://v2.gost.run/en/bypass/)
* DNS [resolver](https://docs.ginuerzh.xyz/gost/resolver/) and [proxy](https://docs.ginuerzh.xyz/gost/dns/) * DNS [resolver](https://v2.gost.run/resolver/) and [proxy](https://v2.gost.run/dns/)
* [TUN/TAP device](https://docs.ginuerzh.xyz/gost/en/tuntap/) * [TUN/TAP device](https://v2.gost.run/en/tuntap/)
Wiki: <https://docs.ginuerzh.xyz/gost/en/> Wiki: [v2.gost.run](https://v2.gost.run/en/)
Telegram group: <https://t.me/gogost> Telegram group: <https://t.me/gogost>
@ -54,7 +53,13 @@ go build
#### Docker #### Docker
```bash ```bash
docker pull ginuerzh/gost docker run --rm ginuerzh/gost -V
```
#### Homebrew
```bash
brew install gost
``` ```
#### Ubuntu store #### Ubuntu store
@ -213,7 +218,7 @@ gost -L=:8080 -F=h2://server_ip:443
#### QUIC #### QUIC
Support for QUIC is based on library [quic-go](https://github.com/lucas-clemente/quic-go). Support for QUIC is based on library [quic-go](https://github.com/quic-go/quic-go).
Server: Server:

View File

@ -3,7 +3,9 @@ package gost
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"net" "net"
"syscall"
"time" "time"
"github.com/go-log/log" "github.com/go-log/log"
@ -18,6 +20,8 @@ var (
type Chain struct { type Chain struct {
isRoute bool isRoute bool
Retries int Retries int
Mark int
Interface string
nodeGroups []*NodeGroup nodeGroups []*NodeGroup
route []Node // nodes in the selected route route []Node // nodes in the selected route
} }
@ -34,10 +38,14 @@ func NewChain(nodes ...Node) *Chain {
// newRoute creates a chain route. // newRoute creates a chain route.
// a chain route is the final route after node selection. // a chain route is the final route after node selection.
func newRoute(nodes ...Node) *Chain { func (c *Chain) newRoute(nodes ...Node) *Chain {
chain := NewChain(nodes...) route := NewChain(nodes...)
chain.isRoute = true route.isRoute = true
return chain if !c.IsEmpty() {
route.Interface = c.Interface
route.Mark = c.Mark
}
return route
} }
// Nodes returns the proxy nodes that the chain holds. // Nodes returns the proxy nodes that the chain holds.
@ -135,6 +143,9 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
if options == nil { if options == nil {
options = &ChainOptions{} options = &ChainOptions{}
} }
if c == nil {
c = &Chain{}
}
route, err := c.selectRouteFor(address) route, err := c.selectRouteFor(address)
if err != nil { if err != nil {
return nil, err return nil, err
@ -143,6 +154,9 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
ipAddr := address ipAddr := address
if address != "" { if address != "" {
ipAddr = c.resolve(address, options.Resolver, options.Hosts) ipAddr = c.resolve(address, options.Resolver, options.Hosts)
if ipAddr == "" {
return nil, fmt.Errorf("resolver: domain %s does not exists", address)
}
} }
timeout := options.Timeout timeout := options.Timeout
@ -150,6 +164,32 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
timeout = DialTimeout timeout = DialTimeout
} }
var controlFunction func(_ string, _ string, c syscall.RawConn) error = nil
if c.Mark > 0 {
controlFunction = func(_, _ string, cc syscall.RawConn) error {
return cc.Control(func(fd uintptr) {
ex := setSocketMark(int(fd), c.Mark)
if ex != nil {
log.Logf("net dialer set mark %d error: %s", c.Mark, ex)
} else {
// log.Logf("net dialer set mark %d success", options.Mark)
}
})
}
}
if c.Interface != "" {
controlFunction = func(_, _ string, cc syscall.RawConn) error {
return cc.Control(func(fd uintptr) {
err := setSocketInterface(int(fd), c.Interface)
if err != nil {
log.Logf("net dialer set interface %s error: %s", c.Interface, err)
}
})
}
}
if route.IsEmpty() { if route.IsEmpty() {
switch network { switch network {
case "udp", "udp4", "udp6": case "udp", "udp4", "udp6":
@ -160,6 +200,7 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
} }
d := &net.Dialer{ d := &net.Dialer{
Timeout: timeout, Timeout: timeout,
Control: controlFunction,
// LocalAddr: laddr, // TODO: optional local address // LocalAddr: laddr, // TODO: optional local address
} }
return d.DialContext(ctx, network, ipAddr) return d.DialContext(ctx, network, ipAddr)
@ -193,9 +234,11 @@ func (*Chain) resolve(addr string, resolver Resolver, hosts *Hosts) string {
if err != nil { if err != nil {
log.Logf("[resolver] %s: %v", host, err) log.Logf("[resolver] %s: %v", host, err)
} }
if len(ips) > 0 { if len(ips) == 0 {
return net.JoinHostPort(ips[0].String(), port) log.Logf("[resolver] %s: domain does not exists", host)
return ""
} }
return net.JoinHostPort(ips[0].String(), port)
} }
return addr return addr
} }
@ -286,13 +329,13 @@ func (c *Chain) selectRoute() (route *Chain, err error) {
// selectRouteFor selects route with bypass testing. // selectRouteFor selects route with bypass testing.
func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) { func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {
if c.IsEmpty() { if c.IsEmpty() {
return newRoute(), nil return c.newRoute(), nil
} }
if c.isRoute { if c.isRoute {
return c, nil return c, nil
} }
route = newRoute() route = c.newRoute()
var nl []Node var nl []Node
for _, group := range c.nodeGroups { for _, group := range c.nodeGroups {
@ -310,7 +353,7 @@ func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {
node.DialOptions = append(node.DialOptions, node.DialOptions = append(node.DialOptions,
ChainDialOption(route), ChainDialOption(route),
) )
route = newRoute() // cutoff the chain for multiplex node. route = c.newRoute() // cutoff the chain for multiplex node.
} }
route.AddNode(node) route.AddNode(node)
@ -328,6 +371,7 @@ type ChainOptions struct {
Timeout time.Duration Timeout time.Duration
Hosts *Hosts Hosts *Hosts
Resolver Resolver Resolver Resolver
Mark int
} }
// ChainOption allows a common way to set chain options. // ChainOption allows a common way to set chain options.

View File

@ -7,7 +7,7 @@ import (
"net/url" "net/url"
"time" "time"
"github.com/ginuerzh/gosocks5" "github.com/go-gost/gosocks5"
) )
// Client is a proxy client. // Client is a proxy client.
@ -83,6 +83,7 @@ type Transporter interface {
type DialOptions struct { type DialOptions struct {
Timeout time.Duration Timeout time.Duration
Chain *Chain Chain *Chain
Host string
} }
// DialOption allows a common way to set DialOptions. // DialOption allows a common way to set DialOptions.
@ -102,6 +103,13 @@ func ChainDialOption(chain *Chain) DialOption {
} }
} }
// HostDialOption specifies the host used by Transporter.Dial
func HostDialOption(host string) DialOption {
return func(opts *DialOptions) {
opts.Host = host
}
}
// HandshakeOptions describes the options for handshake. // HandshakeOptions describes the options for handshake.
type HandshakeOptions struct { type HandshakeOptions struct {
Addr string Addr string

View File

@ -5,8 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"errors" "fmt"
"io/ioutil"
"net" "net"
"net/url" "net/url"
"os" "os"
@ -44,8 +43,9 @@ var (
defaultKeyFile = "key.pem" defaultKeyFile = "key.pem"
) )
// Load the certificate from cert and key files, will use the default certificate if the provided info are invalid. // Load the certificate from cert & key files and optional client CA file,
func tlsConfig(certFile, keyFile string) (*tls.Config, error) { // will use the default certificate if the provided info are invalid.
func tlsConfig(certFile, keyFile, caFile string) (*tls.Config, error) {
if certFile == "" || keyFile == "" { if certFile == "" || keyFile == "" {
certFile, keyFile = defaultCertFile, defaultKeyFile certFile, keyFile = defaultCertFile, defaultKeyFile
} }
@ -54,7 +54,19 @@ func tlsConfig(certFile, keyFile string) (*tls.Config, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
cfg := &tls.Config{Certificates: []tls.Certificate{cert}}
pool, err := loadCA(caFile)
if err != nil {
return nil, err
}
if pool != nil {
cfg.ClientCAs = pool
cfg.ClientAuth = tls.RequireAndVerifyClientCert
}
return cfg, nil
} }
func loadCA(caFile string) (cp *x509.CertPool, err error) { func loadCA(caFile string) (cp *x509.CertPool, err error) {
@ -62,12 +74,12 @@ func loadCA(caFile string) (cp *x509.CertPool, err error) {
return return
} }
cp = x509.NewCertPool() cp = x509.NewCertPool()
data, err := ioutil.ReadFile(caFile) data, err := os.ReadFile(caFile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !cp.AppendCertsFromPEM(data) { if !cp.AppendCertsFromPEM(data) {
return nil, errors.New("AppendCertsFromPEM failed") return nil, fmt.Errorf("loadCA %s: AppendCertsFromPEM failed", caFile)
} }
return return
} }
@ -98,6 +110,7 @@ func parseUsers(authFile string) (users []*url.Userinfo, err error) {
if err != nil { if err != nil {
return return
} }
defer file.Close()
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
for scanner.Scan() { for scanner.Scan() {
line := strings.TrimSpace(scanner.Text()) line := strings.TrimSpace(scanner.Text())
@ -143,33 +156,38 @@ func parseIP(s string, port string) (ips []string) {
port = "8080" // default port port = "8080" // default port
} }
addrFn := func(s, port string) string {
c := strings.Count(s, ":")
if c == 0 || //ipv4 or domain
s[len(s)-1] == ']' { //[ipv6]
return s + ":" + port
}
if c > 1 && s[0] != '[' { // ipv6
return "[" + s + "]:" + port
}
return s //ipv4:port or [ipv6]:port
}
file, err := os.Open(s) file, err := os.Open(s)
if err != nil { if err != nil {
ss := strings.Split(s, ",") ss := strings.Split(s, ",")
for _, s := range ss { for _, s := range ss {
s = strings.TrimSpace(s) s = strings.TrimSpace(s)
if s != "" { if s != "" {
// TODO: support IPv6 ips = append(ips, addrFn(s, port))
if !strings.Contains(s, ":") {
s = s + ":" + port
}
ips = append(ips, s)
} }
} }
return return
} }
defer file.Close()
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
for scanner.Scan() { for scanner.Scan() {
line := strings.TrimSpace(scanner.Text()) line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") { if line == "" || strings.HasPrefix(line, "#") {
continue continue
} }
if !strings.Contains(line, ":") { ips = append(ips, addrFn(line, port))
line = line + ":" + port
}
ips = append(ips, line)
} }
return return
} }

View File

@ -31,7 +31,9 @@ func init() {
flag.Var(&baseCfg.route.ChainNodes, "F", "forward address, can make a forward chain") flag.Var(&baseCfg.route.ChainNodes, "F", "forward address, can make a forward chain")
flag.Var(&baseCfg.route.ServeNodes, "L", "listen address, can listen on multiple ports (required)") flag.Var(&baseCfg.route.ServeNodes, "L", "listen address, can listen on multiple ports (required)")
flag.IntVar(&baseCfg.route.Mark, "M", 0, "Specify out connection mark")
flag.StringVar(&configureFile, "C", "", "configure file") flag.StringVar(&configureFile, "C", "", "configure file")
flag.StringVar(&baseCfg.route.Interface, "I", "", "Interface to bind")
flag.BoolVar(&baseCfg.Debug, "D", false, "enable debug log") flag.BoolVar(&baseCfg.Debug, "D", false, "enable debug log")
flag.BoolVar(&printVersion, "V", false, "print version") flag.BoolVar(&printVersion, "V", false, "print version")
if pprofEnabled { if pprofEnabled {
@ -40,7 +42,7 @@ func init() {
flag.Parse() flag.Parse()
if printVersion { if printVersion {
fmt.Fprintf(os.Stderr, "gost %s (%s %s/%s)\n", fmt.Fprintf(os.Stdout, "gost %s (%s %s/%s)\n",
gost.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH) gost.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
os.Exit(0) os.Exit(0)
} }
@ -67,7 +69,7 @@ func main() {
} }
// NOTE: as of 2.6, you can use custom cert/key files to initialize the default certificate. // NOTE: as of 2.6, you can use custom cert/key files to initialize the default certificate.
tlsConfig, err := tlsConfig(defaultCertFile, defaultKeyFile) tlsConfig, err := tlsConfig(defaultCertFile, defaultKeyFile, "")
if err != nil { if err != nil {
// generate random self-signed certificate. // generate random self-signed certificate.
cert, err := gost.GenCertificate() cert, err := gost.GenCertificate()

View File

@ -5,7 +5,6 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -16,8 +15,10 @@ import (
type peerConfig struct { type peerConfig struct {
Strategy string `json:"strategy"` Strategy string `json:"strategy"`
MaxFails int `json:"max_fails"` MaxFails int `json:"max_fails"`
FastestCount int `json:"fastest_count"` // topN fastest node count
FailTimeout time.Duration FailTimeout time.Duration
period time.Duration // the period for live reloading period time.Duration // the period for live reloading
Nodes []string `json:"nodes"` Nodes []string `json:"nodes"`
group *gost.NodeGroup group *gost.NodeGroup
baseNodes []gost.Node baseNodes []gost.Node
@ -52,6 +53,7 @@ func (cfg *peerConfig) Reload(r io.Reader) error {
FailTimeout: cfg.FailTimeout, FailTimeout: cfg.FailTimeout,
}, },
&gost.InvalidFilter{}, &gost.InvalidFilter{},
gost.NewFastestFilter(0, cfg.FastestCount),
), ),
gost.WithStrategy(gost.NewStrategy(cfg.Strategy)), gost.WithStrategy(gost.NewStrategy(cfg.Strategy)),
) )
@ -83,7 +85,7 @@ func (cfg *peerConfig) Reload(r io.Reader) error {
} }
func (cfg *peerConfig) parse(r io.Reader) error { func (cfg *peerConfig) parse(r io.Reader) error {
data, err := ioutil.ReadAll(r) data, err := io.ReadAll(r)
if err != nil { if err != nil {
return err return err
} }
@ -126,6 +128,8 @@ func (cfg *peerConfig) parse(r io.Reader) error {
cfg.Strategy = ss[1] cfg.Strategy = ss[1]
case "max_fails": case "max_fails":
cfg.MaxFails, _ = strconv.Atoi(ss[1]) cfg.MaxFails, _ = strconv.Atoi(ss[1])
case "fastest_count":
cfg.FastestCount, _ = strconv.Atoi(ss[1])
case "fail_timeout": case "fail_timeout":
cfg.FailTimeout, _ = time.ParseDuration(ss[1]) cfg.FailTimeout, _ = time.ParseDuration(ss[1])
case "reload": case "reload":

View File

@ -3,12 +3,14 @@ package main
import ( import (
"crypto/sha256" "crypto/sha256"
"crypto/tls" "crypto/tls"
"crypto/x509"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
"os" "os"
"strings" "strings"
"time"
"github.com/ginuerzh/gost" "github.com/ginuerzh/gost"
"github.com/go-log/log" "github.com/go-log/log"
@ -28,11 +30,15 @@ type route struct {
ServeNodes stringList ServeNodes stringList
ChainNodes stringList ChainNodes stringList
Retries int Retries int
Mark int
Interface string
} }
func (r *route) parseChain() (*gost.Chain, error) { func (r *route) parseChain() (*gost.Chain, error) {
chain := gost.NewChain() chain := gost.NewChain()
chain.Retries = r.Retries chain.Retries = r.Retries
chain.Mark = r.Mark
chain.Interface = r.Interface
gid := 1 // group ID gid := 1 // group ID
for _, ns := range r.ChainNodes { for _, ns := range r.ChainNodes {
@ -60,6 +66,7 @@ func (r *route) parseChain() (*gost.Chain, error) {
FailTimeout: nodes[0].GetDuration("fail_timeout"), FailTimeout: nodes[0].GetDuration("fail_timeout"),
}, },
&gost.InvalidFilter{}, &gost.InvalidFilter{},
gost.NewFastestFilter(0, nodes[0].GetInt("fastest_count")),
), ),
gost.WithStrategy(gost.NewStrategy(nodes[0].Get("strategy"))), gost.WithStrategy(gost.NewStrategy(nodes[0].Get("strategy"))),
) )
@ -128,6 +135,35 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
InsecureSkipVerify: !node.GetBool("secure"), InsecureSkipVerify: !node.GetBool("secure"),
RootCAs: rootCAs, RootCAs: rootCAs,
} }
// If the argument `ca` is given, but not open `secure`, we verify the
// certificate manually.
if rootCAs != nil && !node.GetBool("secure") {
tlsCfg.VerifyConnection = func(state tls.ConnectionState) error {
opts := x509.VerifyOptions{
Roots: rootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := state.PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
return err
}
}
if cert, err := tls.LoadX509KeyPair(node.Get("cert"), node.Get("key")); err == nil {
tlsCfg.Certificates = []tls.Certificate{cert}
}
wsOpts := &gost.WSOptions{} wsOpts := &gost.WSOptions{}
wsOpts.EnableCompression = node.GetBool("compression") wsOpts.EnableCompression = node.GetBool("compression")
wsOpts.ReadBufferSize = node.GetInt("rbuf") wsOpts.ReadBufferSize = node.GetInt("rbuf")
@ -177,6 +213,12 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
Timeout: timeout, Timeout: timeout,
IdleTimeout: node.GetDuration("idle"), IdleTimeout: node.GetDuration("idle"),
} }
if config.KeepAlive {
config.KeepAlivePeriod = node.GetDuration("ttl")
if config.KeepAlivePeriod == 0 {
config.KeepAlivePeriod = 10 * time.Second
}
}
if cipher := node.Get("cipher"); cipher != "" { if cipher := node.Get("cipher"); cipher != "" {
sum := sha256.Sum256([]byte(cipher)) sum := sha256.Sum256([]byte(cipher))
@ -200,6 +242,8 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
tr = gost.FakeTCPTransporter() tr = gost.FakeTCPTransporter()
case "udp": case "udp":
tr = gost.UDPTransporter() tr = gost.UDPTransporter()
case "vsock":
tr = gost.VSOCKTransporter()
default: default:
tr = gost.TCPTransporter() tr = gost.TCPTransporter()
} }
@ -234,8 +278,14 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
connector = gost.AutoConnector(node.User) connector = gost.AutoConnector(node.User)
} }
host := node.Get("host")
if host == "" {
host = node.Host
}
node.DialOptions = append(node.DialOptions, node.DialOptions = append(node.DialOptions,
gost.TimeoutDialOption(timeout), gost.TimeoutDialOption(timeout),
gost.HostDialOption(host),
) )
node.ConnectOptions = []gost.ConnectOption{ node.ConnectOptions = []gost.ConnectOption{
@ -244,11 +294,6 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
gost.NoDelayConnectOption(node.GetBool("nodelay")), gost.NoDelayConnectOption(node.GetBool("nodelay")),
} }
host := node.Get("host")
if host == "" {
host = node.Host
}
sshConfig := &gost.SSHConfig{} sshConfig := &gost.SSHConfig{}
if s := node.Get("ssh_key"); s != "" { if s := node.Get("ssh_key"); s != "" {
key, err := gost.ParseSSHKeyFile(s) key, err := gost.ParseSSHKeyFile(s)
@ -342,7 +387,7 @@ func (r *route) GenRouters() ([]router, error) {
} }
} }
certFile, keyFile := node.Get("cert"), node.Get("key") certFile, keyFile := node.Get("cert"), node.Get("key")
tlsCfg, err := tlsConfig(certFile, keyFile) tlsCfg, err := tlsConfig(certFile, keyFile, node.Get("ca"))
if err != nil && certFile != "" && keyFile != "" { if err != nil && certFile != "" && keyFile != "" {
return nil, err return nil, err
} }
@ -422,6 +467,12 @@ func (r *route) GenRouters() ([]router, error) {
Timeout: timeout, Timeout: timeout,
IdleTimeout: node.GetDuration("idle"), IdleTimeout: node.GetDuration("idle"),
} }
if config.KeepAlive {
config.KeepAlivePeriod = node.GetDuration("ttl")
if config.KeepAlivePeriod == 0 {
config.KeepAlivePeriod = 10 * time.Second
}
}
if cipher := node.Get("cipher"); cipher != "" { if cipher := node.Get("cipher"); cipher != "" {
sum := sha256.Sum256([]byte(cipher)) sum := sha256.Sum256([]byte(cipher))
config.Key = sum[:] config.Key = sum[:]
@ -441,6 +492,8 @@ func (r *route) GenRouters() ([]router, error) {
chain.Nodes()[len(chain.Nodes())-1].Client.Transporter = gost.SSHForwardTransporter() chain.Nodes()[len(chain.Nodes())-1].Client.Transporter = gost.SSHForwardTransporter()
} }
ln, err = gost.TCPListener(node.Addr) ln, err = gost.TCPListener(node.Addr)
case "vsock":
ln, err = gost.VSOCKListener(node.Addr)
case "udp": case "udp":
ln, err = gost.UDPListener(node.Addr, &gost.UDPListenConfig{ ln, err = gost.UDPListener(node.Addr, &gost.UDPListenConfig{
TTL: ttl, TTL: ttl,
@ -616,6 +669,8 @@ func (r *route) GenRouters() ([]router, error) {
gost.IPsHandlerOption(ips), gost.IPsHandlerOption(ips),
gost.TCPModeHandlerOption(node.GetBool("tcp")), gost.TCPModeHandlerOption(node.GetBool("tcp")),
gost.IPRoutesHandlerOption(tunRoutes...), gost.IPRoutesHandlerOption(tunRoutes...),
gost.ProxyAgentHandlerOption(node.Get("proxyAgent")),
gost.HTTPTunnelHandlerOption(node.GetBool("httpTunnel")),
) )
rt := router{ rt := router{

View File

@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -36,7 +35,7 @@ func init() {
var ( var (
httpTestHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { httpTestHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
data, _ := ioutil.ReadAll(r.Body) data, _ := io.ReadAll(r.Body)
if len(data) == 0 { if len(data) == 0 {
data = []byte("Hello World!") data = []byte("Hello World!")
} }
@ -87,7 +86,7 @@ func httpRoundtrip(conn net.Conn, targetURL string, data []byte) (err error) {
return errors.New(resp.Status) return errors.New(resp.Status)
} }
recv, err := ioutil.ReadAll(resp.Body) recv, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return return
} }

3
dns.go
View File

@ -7,7 +7,6 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"strconv" "strconv"
@ -267,7 +266,7 @@ func (l *dnsListener) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
buf, err = ioutil.ReadAll(r.Body) buf, err = io.ReadAll(r.Body)
if err != nil { if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return

View File

@ -6,7 +6,7 @@ import (
"net" "net"
"strconv" "strconv"
"github.com/ginuerzh/gosocks5" "github.com/go-gost/gosocks5"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
) )

View File

@ -10,9 +10,9 @@ import (
"fmt" "fmt"
"github.com/ginuerzh/gosocks5" "github.com/go-gost/gosocks5"
"github.com/go-log/log" "github.com/go-log/log"
smux "gopkg.in/xtaci/smux.v1" smux "github.com/xtaci/smux"
) )
type forwardConnector struct { type forwardConnector struct {
@ -131,6 +131,7 @@ func (h *tcpDirectForwardHandler) Handle(conn net.Conn) {
cc, err = h.options.Chain.Dial(node.Addr, cc, err = h.options.Chain.Dial(node.Addr,
RetryChainOption(h.options.Retries), RetryChainOption(h.options.Retries),
TimeoutChainOption(h.options.Timeout), TimeoutChainOption(h.options.Timeout),
ResolverChainOption(h.options.Resolver),
) )
if err != nil { if err != nil {
log.Logf("[tcp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) log.Logf("[tcp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
@ -197,7 +198,12 @@ func (h *udpDirectForwardHandler) Handle(conn net.Conn) {
} }
} }
cc, err := h.options.Chain.DialContext(context.Background(), "udp", node.Addr) cc, err := h.options.Chain.DialContext(
context.Background(),
"udp",
node.Addr,
ResolverChainOption(h.options.Resolver),
)
if err != nil { if err != nil {
node.MarkDead() node.MarkDead()
log.Logf("[udp] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) log.Logf("[udp] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)

93
go.mod
View File

@ -1,49 +1,60 @@
module github.com/ginuerzh/gost module github.com/ginuerzh/gost
go 1.13 go 1.22
replace github.com/templexxx/cpu v0.0.7 => github.com/templexxx/cpu v0.0.10-0.20211111114238-98168dcec14a
require ( require (
git.torproject.org/pluggable-transports/goptlib.git v0.0.0-20180321061416-7d56ec4f381e git.torproject.org/pluggable-transports/goptlib.git v1.3.0
git.torproject.org/pluggable-transports/obfs4.git v0.0.0-20181103133120-08f4d470188e
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed
github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 // indirect github.com/go-gost/gosocks4 v0.0.1
github.com/cheekybits/genny v1.0.0 // indirect github.com/go-gost/gosocks5 v0.3.0
github.com/coreos/go-iptables v0.4.5 // indirect github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7
github.com/dchest/siphash v1.2.1 // indirect github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451
github.com/docker/libcontainer v2.2.1+incompatible github.com/go-log/log v0.2.0
github.com/ginuerzh/gosocks4 v0.0.1
github.com/ginuerzh/gosocks5 v0.2.0
github.com/ginuerzh/tls-dissector v0.0.2-0.20200224064855-24ab2b3a3796
github.com/go-gost/relay v0.1.0
github.com/go-log/log v0.1.0
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/golang/mock v1.2.0 // indirect github.com/gorilla/websocket v1.5.1
github.com/google/gopacket v1.1.17 // indirect github.com/klauspost/compress v1.17.6
github.com/gorilla/websocket v1.4.0 // indirect github.com/mdlayher/vsock v1.2.1
github.com/hashicorp/golang-lru v0.5.0 // indirect github.com/miekg/dns v1.1.58
github.com/klauspost/compress v1.4.1 github.com/quic-go/quic-go v0.45.0
github.com/klauspost/cpuid v1.2.0 // indirect github.com/ryanuber/go-glob v1.0.0
github.com/klauspost/reedsolomon v1.7.0 // indirect github.com/shadowsocks/go-shadowsocks2 v0.1.5
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f // indirect github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
github.com/lucas-clemente/quic-go v0.10.0 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced // indirect github.com/xtaci/kcp-go/v5 v5.6.7
github.com/miekg/dns v1.1.27 github.com/xtaci/smux v1.5.24
github.com/milosgajdos83/tenus v0.0.0-20190415114537-1f3ed00ae7d8
github.com/onsi/ginkgo v1.7.0 // indirect
github.com/onsi/gomega v1.4.3 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735
github.com/shadowsocks/go-shadowsocks2 v0.1.0
github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
github.com/tjfoc/gmsm v1.0.1 // indirect
github.com/xtaci/tcpraw v1.2.25 github.com/xtaci/tcpraw v1.2.25
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d gitlab.com/yawning/obfs4.git v0.0.0-20220204003609-77af0cba934d
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 golang.org/x/crypto v0.24.0
gopkg.in/gorilla/websocket.v1 v1.4.0 golang.org/x/net v0.26.0
gopkg.in/xtaci/kcp-go.v4 v4.3.2 )
gopkg.in/xtaci/smux.v1 v1.0.7
require (
filippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20 // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/coreos/go-iptables v0.6.0 // indirect
github.com/dchest/siphash v1.2.2 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/klauspost/reedsolomon v1.12.0 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/templexxx/cpu v0.1.0 // indirect
github.com/templexxx/xorsimd v0.4.2 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect
gitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.22.0 // indirect
) )

307
go.sum
View File

@ -1,145 +1,204 @@
git.torproject.org/pluggable-transports/goptlib.git v0.0.0-20180321061416-7d56ec4f381e h1:PYcONLFUhr00kGrq7Mf14JRtoXHG7BOSKIfIha0Hu5Q= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
git.torproject.org/pluggable-transports/goptlib.git v0.0.0-20180321061416-7d56ec4f381e/go.mod h1:YT4XMSkuEXbtqlydr9+OxqFAyspUv0Gr9qhM3B++o/Q= filippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20 h1:iJoUgXvhagsNMrJrvavw7vu1eG8+hm6jLOxlLFcoODw=
git.torproject.org/pluggable-transports/obfs4.git v0.0.0-20181103133120-08f4d470188e h1:c8h60PKrRxEB5debIHBmP7T+s/EUNXTklXqlmJfYiJQ= filippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
git.torproject.org/pluggable-transports/obfs4.git v0.0.0-20181103133120-08f4d470188e/go.mod h1:jRZbfRcLIgFQoCw6tRmsnETVyIj54jOmXhHCYYa0jbs= git.torproject.org/pluggable-transports/goptlib.git v1.0.0/go.mod h1:YT4XMSkuEXbtqlydr9+OxqFAyspUv0Gr9qhM3B++o/Q=
git.torproject.org/pluggable-transports/goptlib.git v1.3.0 h1:G+iuRUblCCC2xnO+0ag1/4+aaM98D5mjWP1M0v9s8a0=
git.torproject.org/pluggable-transports/goptlib.git v1.3.0/go.mod h1:4PBMl1dg7/3vMWSoWb46eGWlrxkUyn/CAJmxhDLAlDs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed h1:eqa6queieK8SvoszxCu0WwH7lSVeL4/N/f1JwOMw1G4= github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed h1:eqa6queieK8SvoszxCu0WwH7lSVeL4/N/f1JwOMw1G4=
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed/go.mod h1:rA52xkgZwql9LRZXWb2arHEFP6qSR48KY2xOfWzEciQ= github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed/go.mod h1:rA52xkgZwql9LRZXWb2arHEFP6qSR48KY2xOfWzEciQ=
github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63 h1:I6/SJSN9wJMJ+ZyQaCHUlzoTA4ypU5Bb44YWR1wTY/0=
github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63/go.mod h1:nf+Komq6fVP4SwmKEaVGxHTyQGKREVlwjQKpvOV39yE=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 h1:x17NvoJaphEzay72TFej4OSSsgu3xRYBLkbIwdofS/4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/dchest/siphash v1.2.0 h1:YWOShuhvg0GqbQpMa60QlCGtEyf7O7HC1Jf0VjdQ60M= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/siphash v1.2.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4=
github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
github.com/docker/libcontainer v2.2.1+incompatible h1:++SbbkCw+X8vAd4j2gOCzZ2Nn7s2xFALTf7LZKmM1/0= github.com/dchest/siphash v1.2.2 h1:9DFz8tQwl9pTVt5iok/9zKyzA1Q6bRGiF3HPiEEVr9I=
github.com/docker/libcontainer v2.2.1+incompatible/go.mod h1:osvj61pYsqhNCMLGX31xr7klUBhHb/ZBuXS0o1Fvwbw= github.com/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/ginuerzh/gosocks4 v0.0.1 h1:ojDKUyz+uaEeRm2usY1cyQiXTqJqrKxfeE6SVBXq4m0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ginuerzh/gosocks4 v0.0.1/go.mod h1:8SdwBMKjfJ9+BfP2vDJM1jcrgWUbWV6qxBPHHVrwptY= github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=
github.com/ginuerzh/gosocks5 v0.2.0 h1:K0Ua23U9LU3BZrf3XpGDcs0mP8DiEpa6PJE4TA/MU3s= github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
github.com/ginuerzh/gosocks5 v0.2.0/go.mod h1:qp22mr6tH/prEoaN0pFukq76LlScIE+F2rP2ZP5ZHno= github.com/go-gost/gosocks5 v0.3.0 h1:Hkmp9YDRBSCJd7xywW6dBPT6B9aQTkuWd+3WCheJiJA=
github.com/ginuerzh/tls-dissector v0.0.1 h1:yF6fIt78TO4CdjiLLn6R8r0XajQJE1Lbnuq6rP8mGW8= github.com/go-gost/gosocks5 v0.3.0/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
github.com/ginuerzh/tls-dissector v0.0.1/go.mod h1:u/kbBOqIOgJv39gywuUb3VwyzdZG5DKquOqfToKE6lk= github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200223041816-c0cb3da7ea91 h1:bFBTbZglO4xNVWSLwDEcVKBIurTXGL2sNKi9UuQima4= github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200223041816-c0cb3da7ea91/go.mod h1:YyzP8PQrGwDH/XsfHJXwqdHLwWvBYxu77YVKm0+68f0= github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 h1:xj8gUZGYO3nb5+6Bjw9+tsFkA9sYynrOvDvvC4uDV2I=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200223072427-83db9c3e4eb5 h1:pmGmno31njvF5xncoDcDuM8mE1984cxrQ0DeVD4lVfA= github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200223072427-83db9c3e4eb5/go.mod h1:YyzP8PQrGwDH/XsfHJXwqdHLwWvBYxu77YVKm0+68f0= github.com/go-log/log v0.2.0 h1:z8i91GBudxD5L3RmF0KVpetCbcGWAV7q1Tw1eRwQM9Q=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200223110639-e9c10af0eb19 h1:t/AZCq8FiVNN+Mx6UmIv7bXj3+OVThg070G8ajZ3wJw= github.com/go-log/log v0.2.0/go.mod h1:xzCnwajcues/6w7lne3yK2QU7DBPW7kqbgPGG5AF65U=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200223110639-e9c10af0eb19/go.mod h1:YyzP8PQrGwDH/XsfHJXwqdHLwWvBYxu77YVKm0+68f0= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200223121713-a8bf02a99d69 h1:h9lREy0OWSTrjweGxduikppA2tCjGPoUj32SVHI3dr0= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200223121713-a8bf02a99d69/go.mod h1:YyzP8PQrGwDH/XsfHJXwqdHLwWvBYxu77YVKm0+68f0= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200224064855-24ab2b3a3796 h1:VPXbYRvZUzTemsI7u0FzOnEuHeHwQuMTPXApAu8aeX4= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200224064855-24ab2b3a3796/go.mod h1:YyzP8PQrGwDH/XsfHJXwqdHLwWvBYxu77YVKm0+68f0=
github.com/go-gost/relay v0.1.0 h1:UOf2YwAzzaUjY5mdpMuLfSw0vz62iIFYk7oJQkuhlGw=
github.com/go-gost/relay v0.1.0/go.mod h1:YFCpddLOFE3NlIkeDWRdEs8gL/GFsqXdtaf8SV5v4YQ=
github.com/go-log/log v0.1.0 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U=
github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/klauspost/reedsolomon v1.7.0 h1:pLFmRKGko2ZieiTGyo9DahLCIuljyxm+Zzhz/fYEonE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/klauspost/reedsolomon v1.7.0/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f h1:sSeNEkJrs+0F9TUau0CgWTTNEwF23HST3Eq0A+QIx+A= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/lucas-clemente/quic-go v0.10.0 h1:xEF+pSHYAOcu+U10Meunf+DTtc8vhQDRqlA0BJ6hufc= github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
github.com/lucas-clemente/quic-go v0.10.0/go.mod h1:wuD+2XqEx8G9jtwx5ou2BEYBsE+whgQmlj0Vz/77PrY= github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced h1:zqEC1GJZFbGZA0tRyNZqRjep92K5fujFtFsu5ZW7Aug= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/milosgajdos83/tenus v0.0.0-20190415114537-1f3ed00ae7d8 h1:4WFQEfEJ7zaHYViIVM2Cd6tnQOOhiEHbmQtlcV7aOpc= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/milosgajdos83/tenus v0.0.0-20190415114537-1f3ed00ae7d8/go.mod h1:G95Wwn625/q6JCCytI4VR/a5VtPwrtI0B+Q1Gi38QLA= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/klauspost/reedsolomon v1.12.0/go.mod h1:EPLZJeh4l27pUGC3aXOjheaoh1I9yut7xTURiW3LQ9Y=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/shadowsocks/go-shadowsocks2 v0.0.12-0.20191211020244-a57bc393e43a h1:cxYYZwo6iuuJ/5f8x1mHnya7xvSF3cDrOh8Pqh7RZ/w= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/shadowsocks/go-shadowsocks2 v0.0.12-0.20191211020244-a57bc393e43a/go.mod h1:/0aFGbhK8mtOX4J/6kTJsPLZlEs9KnzKoWCOCvjd7vk= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/shadowsocks/go-shadowsocks2 v0.1.0 h1:jQhkjAmMuOTQ7B04bnrRJ5IAoZEwoaXXkKspE7rQ6ck= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/shadowsocks/go-shadowsocks2 v0.1.0/go.mod h1:/0aFGbhK8mtOX4J/6kTJsPLZlEs9KnzKoWCOCvjd7vk= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba h1:tJgNXb3S+RkB4kNPi6N5OmEWe3m+Y3Qs6LUMiNDAONM= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b h1:+y4hCMc/WKsDbAPsOQZgBSaSZ26uh2afyaWeVg/3s/c= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/quic-go/quic-go v0.45.0 h1:OHmkQGM37luZITyTSu6ff03HP/2IrwDX1ZFiNEhSFUE=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/quic-go/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28=
github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM=
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 h1:XU9hik0exChEmY92ALW4l9WnDodxLVS9yOSNh2SizaQ=
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=
github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=
github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/xtaci/kcp-go/v5 v5.6.7 h1:7+rnxNFIsjEwTXQk4cSZpXM4pO0hqtpwE1UFFoJBffA=
github.com/xtaci/kcp-go/v5 v5.6.7/go.mod h1:oE9j2NVqAkuKO5o8ByKGch3vgVX3BNf8zqP8JiGq0bM=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
github.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk= github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk= github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= gitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb h1:qRSZHsODmAP5qDvb3YsO7Qnf3TRiVbGxNG/WYnlM4/o=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= gitlab.com/yawning/edwards25519-extra.git v0.0.0-20211229043746-2f91fcc9fbdb/go.mod h1:gvdJuZuO/tPZyhEV8K3Hmoxv/DWud5L4qEQxfYjEUTo=
gitlab.com/yawning/obfs4.git v0.0.0-20220204003609-77af0cba934d h1:tJ8F7ABaQ3p3wjxwXiWSktVDgjZEXkvaRawd2rIq5ws=
gitlab.com/yawning/obfs4.git v0.0.0-20220204003609-77af0cba934d/go.mod h1:9GcM8QNU9/wXtEEH2q8bVOnPI7FtIF6VVLzZ1l6Hgf8=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
gopkg.in/gorilla/websocket.v1 v1.4.0 h1:lREme3ezAGPCpxSHwjGkHhAJX+ed2B6vzAJ+kaqBEIM= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
gopkg.in/gorilla/websocket.v1 v1.4.0/go.mod h1:Ons1i8d00TjvJPdla7bJyeXFsdOacUyrTYbg9IetsIE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
gopkg.in/xtaci/kcp-go.v4 v4.3.2 h1:S9IF+L55Ugzl/hVA6wvuL3SuAtTUzH2cBBC88MXQxnE= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
gopkg.in/xtaci/kcp-go.v4 v4.3.2/go.mod h1:fFYTlSOHNOHDNTKfoqarZMQsu7g7oXKwJ9wq0i9lODc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
gopkg.in/xtaci/smux.v1 v1.0.7 h1:qootIZs4ZPSx5blhvgaFpx2epdFSWkyw99xT+q0mRXI= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
gopkg.in/xtaci/smux.v1 v1.0.7/go.mod h1:NbrPjLp8lNAYN8KqTplnFr2JjIBbr7CdHBkHtHsXtWA= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -20,7 +20,7 @@ import (
) )
// Version is the gost version. // Version is the gost version.
const Version = "2.11.0" const Version = "2.12.0"
// Debug is a flag that enables the debug log. // Debug is a flag that enables the debug log.
var Debug bool var Debug bool
@ -80,6 +80,8 @@ var (
// DefaultUserAgent is the default HTTP User-Agent header used by HTTP and websocket. // DefaultUserAgent is the default HTTP User-Agent header used by HTTP and websocket.
DefaultUserAgent = "Chrome/78.0.3904.106" DefaultUserAgent = "Chrome/78.0.3904.106"
DefaultProxyAgent = "gost/" + Version
// DefaultMTU is the default mtu for tun/tap device // DefaultMTU is the default mtu for tun/tap device
DefaultMTU = 1350 DefaultMTU = 1350
) )
@ -147,9 +149,7 @@ func (rw *readWriter) Write(p []byte) (n int, err error) {
return rw.w.Write(p) return rw.w.Write(p)
} }
var ( var nopClientConn = &nopConn{}
nopClientConn = &nopConn{}
)
// a nop connection implements net.Conn, // a nop connection implements net.Conn,
// it does nothing. // it does nothing.

BIN
gost.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -7,8 +7,8 @@ import (
"net/url" "net/url"
"time" "time"
"github.com/ginuerzh/gosocks4" "github.com/go-gost/gosocks4"
"github.com/ginuerzh/gosocks5" "github.com/go-gost/gosocks5"
"github.com/go-log/log" "github.com/go-log/log"
) )
@ -42,6 +42,8 @@ type HandlerOptions struct {
IPs []string IPs []string
TCPMode bool TCPMode bool
IPRoutes []IPRoute IPRoutes []IPRoute
ProxyAgent string
HTTPTunnel bool
} }
// HandlerOption allows a common way to set handler options. // HandlerOption allows a common way to set handler options.
@ -211,6 +213,20 @@ func IPRoutesHandlerOption(routes ...IPRoute) HandlerOption {
} }
} }
// ProxyAgentHandlerOption sets the proxy agent for http handler.
func ProxyAgentHandlerOption(agent string) HandlerOption {
return func(opts *HandlerOptions) {
opts.ProxyAgent = agent
}
}
// HTTPTunnelHandlerOption sets the Tunnel mode for HTTP client used in HTTP handler.
func HTTPTunnelHandlerOption(tunnelMode bool) HandlerOption {
return func(opts *HandlerOptions) {
opts.HTTPTunnel = tunnelMode
}
}
type autoHandler struct { type autoHandler struct {
options *HandlerOptions options *HandlerOptions
} }

78
http.go
View File

@ -6,6 +6,7 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
@ -173,7 +174,12 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
ProtoMinor: 1, ProtoMinor: 1,
Header: http.Header{}, Header: http.Header{},
} }
resp.Header.Add("Proxy-Agent", "gost/"+Version)
proxyAgent := DefaultProxyAgent
if h.options.ProxyAgent != "" {
proxyAgent = h.options.ProxyAgent
}
resp.Header.Add("Proxy-Agent", proxyAgent)
if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) { if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) {
log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s", log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s",
@ -252,7 +258,9 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
// forward http request // forward http request
lastNode := route.LastNode() lastNode := route.LastNode()
if req.Method != http.MethodConnect && lastNode.Protocol == "http" { if req.Method != http.MethodConnect &&
lastNode.Protocol == "http" &&
!h.options.HTTPTunnel {
err = h.forwardRequest(conn, req, route) err = h.forwardRequest(conn, req, route)
if err == nil { if err == nil {
return return
@ -285,27 +293,65 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
} }
defer cc.Close() defer cc.Close()
if req.Method == http.MethodConnect { if req.Method != http.MethodConnect {
h.handleProxy(conn, cc, req)
return
}
b := []byte("HTTP/1.1 200 Connection established\r\n" + b := []byte("HTTP/1.1 200 Connection established\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\r\n") "Proxy-Agent: " + proxyAgent + "\r\n\r\n")
if Debug { if Debug {
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(b)) log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(b))
} }
conn.Write(b) conn.Write(b)
} else {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
return
}
}
log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host) log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host)
transport(conn, cc) transport(conn, cc)
log.Logf("[http] %s >-< %s", conn.RemoteAddr(), host) log.Logf("[http] %s >-< %s", conn.RemoteAddr(), host)
} }
func (h *httpHandler) handleProxy(rw, cc io.ReadWriter, req *http.Request) (err error) {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
return err
}
ch := make(chan error, 1)
go func() {
ch <- copyBuffer(rw, cc)
}()
for {
err := func() error {
req, err := http.ReadRequest(bufio.NewReader(rw))
if err != nil {
return err
}
if Debug {
dump, _ := httputil.DumpRequest(req, false)
log.Log(string(dump))
}
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
return err
}
return nil
}()
ch <- err
if err != nil {
break
}
}
return <-ch
}
func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response) (ok bool) { func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response) (ok bool) {
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization")) u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
if Debug && (u != "" || p != "") { if Debug && (u != "" || p != "") {
@ -363,10 +409,16 @@ func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.
conn.RemoteAddr(), conn.LocalAddr()) conn.RemoteAddr(), conn.LocalAddr())
resp.StatusCode = http.StatusProxyAuthRequired resp.StatusCode = http.StatusProxyAuthRequired
resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"") resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
if strings.ToLower(req.Header.Get("Proxy-Connection")) == "keep-alive" {
// XXX libcurl will keep sending auth request in same conn
// which we don't supported yet.
resp.Header.Add("Connection", "close")
resp.Header.Add("Proxy-Connection", "close")
}
} else { } else {
resp.Header = http.Header{} resp.Header = http.Header{}
resp.Header.Set("Server", "nginx/1.14.1") resp.Header.Set("Server", "nginx/1.14.1")
resp.Header.Set("Date", time.Now().Format(http.TimeFormat)) resp.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
resp.Header.Set("Connection", "keep-alive") resp.Header.Set("Connection", "keep-alive")
} }

View File

@ -9,7 +9,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
@ -234,7 +233,7 @@ func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, err
transport := http2.Transport{ transport := http2.Transport{
TLSClientConfig: tr.tlsConfig, TLSClientConfig: tr.tlsConfig,
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { DialTLS: func(network, adr string, cfg *tls.Config) (net.Conn, error) {
conn, err := opts.Chain.Dial(addr) conn, err := opts.Chain.Dial(addr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -256,13 +255,13 @@ func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, err
pr, pw := io.Pipe() pr, pw := io.Pipe()
req := &http.Request{ req := &http.Request{
Method: http.MethodConnect, Method: http.MethodConnect,
URL: &url.URL{Scheme: "https", Host: addr}, URL: &url.URL{Scheme: "https", Host: opts.Host},
Header: make(http.Header), Header: make(http.Header),
Proto: "HTTP/2.0", Proto: "HTTP/2.0",
ProtoMajor: 2, ProtoMajor: 2,
ProtoMinor: 0, ProtoMinor: 0,
Body: pr, Body: pr,
Host: addr, Host: opts.Host,
ContentLength: -1, ContentLength: -1,
} }
if tr.path != "" { if tr.path != "" {
@ -365,7 +364,11 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
log.Logf("[http2] %s - %s\n%s", r.RemoteAddr, laddr, string(dump)) log.Logf("[http2] %s - %s\n%s", r.RemoteAddr, laddr, string(dump))
} }
w.Header().Set("Proxy-Agent", "gost/"+Version) proxyAgent := DefaultProxyAgent
if h.options.ProxyAgent != "" {
proxyAgent = h.options.ProxyAgent
}
w.Header().Set("Proxy-Agent", proxyAgent)
if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) { if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) {
log.Logf("[http2] %s - %s : Unauthorized to tcp connect to %s", log.Logf("[http2] %s - %s : Unauthorized to tcp connect to %s",
@ -385,7 +388,7 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
ProtoMajor: 2, ProtoMajor: 2,
ProtoMinor: 0, ProtoMinor: 0,
Header: http.Header{}, Header: http.Header{},
Body: ioutil.NopCloser(bytes.NewReader([]byte{})), Body: io.NopCloser(bytes.NewReader([]byte{})),
} }
if !h.authenticate(w, r, resp) { if !h.authenticate(w, r, resp) {
@ -535,7 +538,7 @@ func (h *http2Handler) authenticate(w http.ResponseWriter, r *http.Request, resp
} else { } else {
resp.Header = http.Header{} resp.Header = http.Header{}
resp.Header.Set("Server", "nginx/1.14.1") resp.Header.Set("Server", "nginx/1.14.1")
resp.Header.Set("Date", time.Now().Format(http.TimeFormat)) resp.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
if resp.ContentLength > 0 { if resp.ContentLength > 0 {
resp.Header.Set("Content-Type", "text/html") resp.Header.Set("Content-Type", "text/html")
} }

View File

@ -5,7 +5,7 @@ import (
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io/ioutil" "io"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -997,7 +997,7 @@ func TestHTTP2ProxyWithWebProbeResist(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
recv, _ := ioutil.ReadAll(conn) recv, _ := io.ReadAll(conn)
if !bytes.Equal(recv, []byte("Hello World!")) { if !bytes.Equal(recv, []byte("Hello World!")) {
t.Error("data not equal") t.Error("data not equal")
} }
@ -1053,7 +1053,7 @@ func TestHTTP2ProxyWithHostProbeResist(t *testing.T) {
Proto: "HTTP/2.0", Proto: "HTTP/2.0",
ProtoMajor: 2, ProtoMajor: 2,
ProtoMinor: 0, ProtoMinor: 0,
Body: ioutil.NopCloser(bytes.NewReader(sendData)), Body: io.NopCloser(bytes.NewReader(sendData)),
Host: "github.com:443", Host: "github.com:443",
ContentLength: int64(len(sendData)), ContentLength: int64(len(sendData)),
} }
@ -1068,7 +1068,7 @@ func TestHTTP2ProxyWithHostProbeResist(t *testing.T) {
t.Error("got non-200 status:", resp.Status) t.Error("got non-200 status:", resp.Status)
} }
recv, _ := ioutil.ReadAll(resp.Body) recv, _ := io.ReadAll(resp.Body)
if !bytes.Equal(sendData, recv) { if !bytes.Equal(sendData, recv) {
t.Error("data not equal") t.Error("data not equal")
} }
@ -1105,7 +1105,7 @@ func TestHTTP2ProxyWithFileProbeResist(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
recv, _ := ioutil.ReadAll(conn) recv, _ := io.ReadAll(conn)
if !bytes.Equal(recv, []byte("Hello World!")) { if !bytes.Equal(recv, []byte("Hello World!")) {
t.Error("data not equal") t.Error("data not equal")
} }

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"io/ioutil" "io"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -249,7 +249,7 @@ func TestHTTPProxyWithWebProbeResist(t *testing.T) {
t.Error("got status:", resp.Status) t.Error("got status:", resp.Status)
} }
recv, _ := ioutil.ReadAll(resp.Body) recv, _ := io.ReadAll(resp.Body)
if !bytes.Equal(recv, []byte("Hello World!")) { if !bytes.Equal(recv, []byte("Hello World!")) {
t.Error("data not equal") t.Error("data not equal")
} }
@ -296,7 +296,7 @@ func TestHTTPProxyWithHostProbeResist(t *testing.T) {
t.Error("got status:", resp.Status) t.Error("got status:", resp.Status)
} }
recv, _ := ioutil.ReadAll(resp.Body) recv, _ := io.ReadAll(resp.Body)
if !bytes.Equal(sendData, recv) { if !bytes.Equal(sendData, recv) {
t.Error("data not equal") t.Error("data not equal")
} }
@ -332,7 +332,7 @@ func TestHTTPProxyWithFileProbeResist(t *testing.T) {
t.Error("got status:", resp.Status) t.Error("got status:", resp.Status)
} }
recv, _ := ioutil.ReadAll(resp.Body) recv, _ := io.ReadAll(resp.Body)
if !bytes.Equal(recv, []byte("Hello World!")) { if !bytes.Equal(recv, []byte("Hello World!")) {
t.Error("data not equal, got:", string(recv)) t.Error("data not equal, got:", string(recv))
} }

42
kcp.go
View File

@ -15,9 +15,9 @@ import (
"github.com/go-log/log" "github.com/go-log/log"
"github.com/klauspost/compress/snappy" "github.com/klauspost/compress/snappy"
"github.com/xtaci/kcp-go/v5"
"github.com/xtaci/smux"
"github.com/xtaci/tcpraw" "github.com/xtaci/tcpraw"
"gopkg.in/xtaci/kcp-go.v4"
"gopkg.in/xtaci/smux.v1"
) )
var ( var (
@ -43,6 +43,9 @@ type KCPConfig struct {
Resend int `json:"resend"` Resend int `json:"resend"`
NoCongestion int `json:"nc"` NoCongestion int `json:"nc"`
SockBuf int `json:"sockbuf"` SockBuf int `json:"sockbuf"`
SmuxBuf int `json:"smuxbuf"`
StreamBuf int `json:"streambuf"`
SmuxVer int `json:"smuxver"`
KeepAlive int `json:"keepalive"` KeepAlive int `json:"keepalive"`
SnmpLog string `json:"snmplog"` SnmpLog string `json:"snmplog"`
SnmpPeriod int `json:"snmpperiod"` SnmpPeriod int `json:"snmpperiod"`
@ -62,6 +65,16 @@ func (c *KCPConfig) Init() {
case "fast3": case "fast3":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 10, 2, 1 c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 10, 2, 1
} }
if c.SmuxVer <= 0 {
c.SmuxVer = 1
}
if c.SmuxBuf <= 0 {
c.SmuxBuf = c.SockBuf
}
if c.StreamBuf <= 0 {
c.StreamBuf = c.SockBuf / 2
}
log.Logf("%#v", c)
} }
var ( var (
@ -83,6 +96,9 @@ var (
Resend: 0, Resend: 0,
NoCongestion: 0, NoCongestion: 0,
SockBuf: 4194304, SockBuf: 4194304,
SmuxVer: 1,
SmuxBuf: 4194304,
StreamBuf: 2097152,
KeepAlive: 10, KeepAlive: 10,
SnmpLog: "", SnmpLog: "",
SnmpPeriod: 60, SnmpPeriod: 60,
@ -231,8 +247,14 @@ func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPCon
// stream multiplex // stream multiplex
smuxConfig := smux.DefaultConfig() smuxConfig := smux.DefaultConfig()
smuxConfig.MaxReceiveBuffer = config.SockBuf smuxConfig.Version = config.SmuxVer
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
smuxConfig.MaxStreamBuffer = config.StreamBuf
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
if err := smux.VerifyConfig(smuxConfig); err != nil {
return nil, err
}
var cc net.Conn = kcpconn var cc net.Conn = kcpconn
if !config.NoComp { if !config.NoComp {
cc = newCompStreamConn(kcpconn) cc = newCompStreamConn(kcpconn)
@ -332,7 +354,9 @@ func (l *kcpListener) listenLoop() {
func (l *kcpListener) mux(conn net.Conn) { func (l *kcpListener) mux(conn net.Conn) {
smuxConfig := smux.DefaultConfig() smuxConfig := smux.DefaultConfig()
smuxConfig.MaxReceiveBuffer = l.config.SockBuf smuxConfig.Version = l.config.SmuxVer
smuxConfig.MaxReceiveBuffer = l.config.SmuxBuf
smuxConfig.MaxStreamBuffer = l.config.StreamBuf
smuxConfig.KeepAliveInterval = time.Duration(l.config.KeepAlive) * time.Second smuxConfig.KeepAliveInterval = time.Duration(l.config.KeepAlive) * time.Second
log.Logf("[kcp] %s - %s", conn.RemoteAddr(), l.Addr()) log.Logf("[kcp] %s - %s", conn.RemoteAddr(), l.Addr())
@ -473,9 +497,13 @@ func (c *compStreamConn) Read(b []byte) (n int, err error) {
} }
func (c *compStreamConn) Write(b []byte) (n int, err error) { func (c *compStreamConn) Write(b []byte) (n int, err error) {
n, err = c.w.Write(b) if _, err = c.w.Write(b); err != nil {
err = c.w.Flush() return 0, err
return n, err }
if err = c.w.Flush(); err != nil {
return 0, err
}
return len(b), err
} }
func (c *compStreamConn) Close() error { func (c *compStreamConn) Close() error {

2
mux.go
View File

@ -3,7 +3,7 @@ package gost
import ( import (
"net" "net"
smux "gopkg.in/xtaci/smux.v1" smux "github.com/xtaci/smux"
) )
type muxStreamConn struct { type muxStreamConn struct {

View File

@ -90,6 +90,7 @@ func ParseNode(s string) (node Node, err error) {
case "ftcp": // fake TCP case "ftcp": // fake TCP
case "dns": case "dns":
case "redu", "redirectu": // UDP tproxy case "redu", "redirectu": // UDP tproxy
case "vsock":
default: default:
node.Transport = "tcp" node.Transport = "tcp"
} }

159
obfs.go
View File

@ -20,9 +20,9 @@ import (
"github.com/go-log/log" "github.com/go-log/log"
pt "git.torproject.org/pluggable-transports/goptlib.git" pt "git.torproject.org/pluggable-transports/goptlib.git"
"git.torproject.org/pluggable-transports/obfs4.git/transports/base" dissector "github.com/go-gost/tls-dissector"
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4" "gitlab.com/yawning/obfs4.git/transports/base"
dissector "github.com/ginuerzh/tls-dissector" "gitlab.com/yawning/obfs4.git/transports/obfs4"
) )
const ( const (
@ -313,8 +313,31 @@ var (
0x0601, 0x0602, 0x0603, 0x0501, 0x0502, 0x0503, 0x0401, 0x0402, 0x0601, 0x0602, 0x0603, 0x0501, 0x0502, 0x0503, 0x0401, 0x0402,
0x0403, 0x0301, 0x0302, 0x0303, 0x0201, 0x0202, 0x0203, 0x0403, 0x0301, 0x0302, 0x0303, 0x0201, 0x0202, 0x0203,
} }
tlsRecordTypes = []uint8{0x16, 0x14, 0x16, 0x17}
tlsVersionMinors = []uint8{0x01, 0x03, 0x03, 0x03}
ErrBadType = errors.New("bad type")
ErrBadMajorVersion = errors.New("bad major version")
ErrBadMinorVersion = errors.New("bad minor version")
ErrMaxDataLen = errors.New("bad tls data len")
) )
const (
tlsRecordStateType = iota
tlsRecordStateVersion0
tlsRecordStateVersion1
tlsRecordStateLength0
tlsRecordStateLength1
tlsRecordStateData
)
type obfsTLSParser struct {
step uint8
state uint8
length uint16
}
type obfsTLSConn struct { type obfsTLSConn struct {
net.Conn net.Conn
rbuf bytes.Buffer rbuf bytes.Buffer
@ -322,15 +345,96 @@ type obfsTLSConn struct {
host string host string
isServer bool isServer bool
handshaked chan struct{} handshaked chan struct{}
parser *obfsTLSParser
handshakeMutex sync.Mutex handshakeMutex sync.Mutex
} }
func (r *obfsTLSParser) Parse(b []byte) (int, error) {
i := 0
last := 0
length := len(b)
for i < length {
ch := b[i]
switch r.state {
case tlsRecordStateType:
if tlsRecordTypes[r.step] != ch {
return 0, ErrBadType
}
r.state = tlsRecordStateVersion0
i++
case tlsRecordStateVersion0:
if ch != 0x03 {
return 0, ErrBadMajorVersion
}
r.state = tlsRecordStateVersion1
i++
case tlsRecordStateVersion1:
if ch != tlsVersionMinors[r.step] {
return 0, ErrBadMinorVersion
}
r.state = tlsRecordStateLength0
i++
case tlsRecordStateLength0:
r.length = uint16(ch) << 8
r.state = tlsRecordStateLength1
i++
case tlsRecordStateLength1:
r.length |= uint16(ch)
if r.step == 0 {
r.length = 91
} else if r.step == 1 {
r.length = 1
} else if r.length > maxTLSDataLen {
return 0, ErrMaxDataLen
}
if r.length > 0 {
r.state = tlsRecordStateData
} else {
r.state = tlsRecordStateType
r.step++
}
i++
case tlsRecordStateData:
left := uint16(length - i)
if left > r.length {
left = r.length
}
if r.step >= 2 {
skip := i - last
copy(b[last:], b[i:length])
length -= int(skip)
last += int(left)
i = last
} else {
i += int(left)
}
r.length -= left
if r.length == 0 {
if r.step < 3 {
r.step++
}
r.state = tlsRecordStateType
}
}
}
if last == 0 {
return 0, nil
} else if last < length {
length -= last
}
return length, nil
}
// ClientObfsTLSConn creates a connection for obfs-tls client. // ClientObfsTLSConn creates a connection for obfs-tls client.
func ClientObfsTLSConn(conn net.Conn, host string) net.Conn { func ClientObfsTLSConn(conn net.Conn, host string) net.Conn {
return &obfsTLSConn{ return &obfsTLSConn{
Conn: conn, Conn: conn,
host: host, host: host,
handshaked: make(chan struct{}), handshaked: make(chan struct{}),
parser: &obfsTLSParser{},
} }
} }
@ -416,32 +520,6 @@ func (c *obfsTLSConn) clientHandshake(payload []byte) error {
if _, err := record.WriteTo(c.Conn); err != nil { if _, err := record.WriteTo(c.Conn); err != nil {
return err return err
} }
// server hello handshake message
if _, err := record.ReadFrom(c.Conn); err != nil {
return err
}
if record.Type != dissector.Handshake {
return dissector.ErrBadType
}
// change cipher spec message
if _, err := record.ReadFrom(c.Conn); err != nil {
return err
}
if record.Type != dissector.ChangeCipherSpec {
return dissector.ErrBadType
}
// encrypted handshake message
if _, err := record.ReadFrom(c.Conn); err != nil {
return err
}
if record.Type != dissector.Handshake {
return dissector.ErrBadType
}
_, err = c.rbuf.Write(record.Opaque)
return err return err
} }
@ -521,10 +599,12 @@ func (c *obfsTLSConn) Read(b []byte) (n int, err error) {
return return
} }
} }
select { select {
case <-c.handshaked: case <-c.handshaked:
} }
if c.isServer {
if c.rbuf.Len() > 0 { if c.rbuf.Len() > 0 {
return c.rbuf.Read(b) return c.rbuf.Read(b)
} }
@ -534,6 +614,15 @@ func (c *obfsTLSConn) Read(b []byte) (n int, err error) {
} }
n = copy(b, record.Opaque) n = copy(b, record.Opaque)
_, err = c.rbuf.Write(record.Opaque[n:]) _, err = c.rbuf.Write(record.Opaque[n:])
} else {
n, err = c.Conn.Read(b)
if err != nil {
return
}
if n > 0 {
n, err = c.parser.Parse(b[:n])
}
}
return return
} }
@ -715,6 +804,16 @@ func Obfs4Listener(addr string) (Listener, error) {
return l, nil return l, nil
} }
// TempError satisfies the net.Error interface and presents itself
// as temporary to make sure that it gets retried by the Accept loop
// in server.go.
type TempError struct {
error
}
func (e TempError) Timeout() bool { return false }
func (e TempError) Temporary() bool { return true }
func (l *obfs4Listener) Accept() (net.Conn, error) { func (l *obfs4Listener) Accept() (net.Conn, error) {
conn, err := l.Listener.Accept() conn, err := l.Listener.Accept()
if err != nil { if err != nil {
@ -723,7 +822,7 @@ func (l *obfs4Listener) Accept() (net.Conn, error) {
cc, err := obfs4ServerConn(l.addr, conn) cc, err := obfs4ServerConn(l.addr, conn)
if err != nil { if err != nil {
conn.Close() conn.Close()
return nil, err return nil, TempError{err}
} }
return cc, nil return cc, nil
} }

151
quic.go
View File

@ -1,6 +1,7 @@
package gost package gost
import ( import (
"context"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
@ -12,16 +13,15 @@ import (
"time" "time"
"github.com/go-log/log" "github.com/go-log/log"
quic "github.com/lucas-clemente/quic-go" quic "github.com/quic-go/quic-go"
) )
type quicSession struct { type quicSession struct {
conn net.Conn session quic.EarlyConnection
session quic.Session
} }
func (session *quicSession) GetConn() (*quicConn, error) { func (session *quicSession) GetConn() (*quicConn, error) {
stream, err := session.session.OpenStreamSync() stream, err := session.session.OpenStreamSync(context.Background())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -33,7 +33,7 @@ func (session *quicSession) GetConn() (*quicConn, error) {
} }
func (session *quicSession) Close() error { func (session *quicSession) Close() error {
return session.session.Close() return session.session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
} }
type quicTransporter struct { type quicTransporter struct {
@ -59,100 +59,71 @@ func (tr *quicTransporter) Dial(addr string, options ...DialOption) (conn net.Co
option(opts) option(opts)
} }
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
tr.sessionMutex.Lock() tr.sessionMutex.Lock()
defer tr.sessionMutex.Unlock() defer tr.sessionMutex.Unlock()
session, ok := tr.sessions[addr] session, ok := tr.sessions[addr]
if !ok { if !ok {
var cc *net.UDPConn var pc net.PacketConn
cc, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) pc, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
if err != nil { if err != nil {
return return
} }
conn = cc
if tr.config != nil && tr.config.Key != nil { if tr.config != nil && tr.config.Key != nil {
conn = &quicCipherConn{UDPConn: cc, key: tr.config.Key} pc = &quicCipherConn{PacketConn: pc, key: tr.config.Key}
} }
session = &quicSession{conn: conn} session, err = tr.initSession(udpAddr, pc)
if err != nil {
pc.Close()
return nil, err
}
tr.sessions[addr] = session tr.sessions[addr] = session
} }
return session.conn, nil
conn, err = session.GetConn()
if err != nil {
session.Close()
delete(tr.sessions, addr)
return nil, err
}
return conn, nil
} }
func (tr *quicTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) { func (tr *quicTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
opts := &HandshakeOptions{} return conn, nil
for _, option := range options {
option(opts)
} }
func (tr *quicTransporter) initSession(addr net.Addr, conn net.PacketConn) (*quicSession, error) {
config := tr.config config := tr.config
if opts.QUICConfig != nil { if config == nil {
config = opts.QUICConfig config = &QUICConfig{}
} }
if config.TLSConfig == nil { if config.TLSConfig == nil {
config.TLSConfig = &tls.Config{InsecureSkipVerify: true} config.TLSConfig = &tls.Config{InsecureSkipVerify: true}
} }
tr.sessionMutex.Lock()
defer tr.sessionMutex.Unlock()
timeout := opts.Timeout
if timeout <= 0 {
timeout = HandshakeTimeout
}
conn.SetDeadline(time.Now().Add(timeout))
defer conn.SetDeadline(time.Time{})
session, ok := tr.sessions[opts.Addr]
if session != nil && session.conn != conn {
conn.Close()
return nil, errors.New("quic: unrecognized connection")
}
if !ok || session.session == nil {
s, err := tr.initSession(opts.Addr, conn, config)
if err != nil {
conn.Close()
delete(tr.sessions, opts.Addr)
return nil, err
}
session = s
tr.sessions[opts.Addr] = session
}
cc, err := session.GetConn()
if err != nil {
session.Close()
delete(tr.sessions, opts.Addr)
return nil, err
}
return cc, nil
}
func (tr *quicTransporter) initSession(addr string, conn net.Conn, config *QUICConfig) (*quicSession, error) {
udpConn, ok := conn.(net.PacketConn)
if !ok {
return nil, errors.New("quic: wrong connection type")
}
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
quicConfig := &quic.Config{ quicConfig := &quic.Config{
HandshakeTimeout: config.Timeout, HandshakeIdleTimeout: config.Timeout,
KeepAlive: config.KeepAlive, MaxIdleTimeout: config.IdleTimeout,
IdleTimeout: config.IdleTimeout, KeepAlivePeriod: config.KeepAlivePeriod,
Versions: []quic.VersionNumber{ Versions: []quic.VersionNumber{
quic.VersionGQUIC43, quic.Version1,
quic.VersionGQUIC39, quic.Version2,
}, },
} }
session, err := quic.Dial(udpConn, udpAddr, addr, config.TLSConfig, quicConfig) session, err := quic.DialEarly(context.Background(), conn, addr, tlsConfigQUICALPN(config.TLSConfig), quicConfig)
if err != nil { if err != nil {
log.Logf("quic dial %s: %v", addr, err) log.Logf("quic dial %s: %v", addr, err)
return nil, err return nil, err
} }
return &quicSession{conn: conn, session: session}, nil return &quicSession{session: session}, nil
} }
func (tr *quicTransporter) Multiplex() bool { func (tr *quicTransporter) Multiplex() bool {
@ -164,12 +135,13 @@ type QUICConfig struct {
TLSConfig *tls.Config TLSConfig *tls.Config
Timeout time.Duration Timeout time.Duration
KeepAlive bool KeepAlive bool
KeepAlivePeriod time.Duration
IdleTimeout time.Duration IdleTimeout time.Duration
Key []byte Key []byte
} }
type quicListener struct { type quicListener struct {
ln quic.Listener ln quic.EarlyListener
connChan chan net.Conn connChan chan net.Conn
errChan chan error errChan chan error
} }
@ -180,39 +152,41 @@ func QUICListener(addr string, config *QUICConfig) (Listener, error) {
config = &QUICConfig{} config = &QUICConfig{}
} }
quicConfig := &quic.Config{ quicConfig := &quic.Config{
HandshakeTimeout: config.Timeout, HandshakeIdleTimeout: config.Timeout,
KeepAlive: config.KeepAlive, KeepAlivePeriod: config.KeepAlivePeriod,
IdleTimeout: config.IdleTimeout, MaxIdleTimeout: config.IdleTimeout,
Versions: []quic.VersionNumber{
quic.Version1,
quic.Version2,
},
} }
tlsConfig := config.TLSConfig tlsConfig := config.TLSConfig
if tlsConfig == nil { if tlsConfig == nil {
tlsConfig = DefaultTLSConfig tlsConfig = DefaultTLSConfig
} }
var conn net.PacketConn var conn net.PacketConn
udpAddr, err := net.ResolveUDPAddr("udp", addr) udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
lconn, err := net.ListenUDP("udp", udpAddr) conn, err = net.ListenUDP("udp", udpAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
conn = lconn
if config.Key != nil { if config.Key != nil {
conn = &quicCipherConn{UDPConn: lconn, key: config.Key} conn = &quicCipherConn{PacketConn: conn, key: config.Key}
} }
ln, err := quic.Listen(conn, tlsConfig, quicConfig) ln, err := quic.ListenEarly(conn, tlsConfigQUICALPN(tlsConfig), quicConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
l := &quicListener{ l := &quicListener{
ln: ln, ln: *ln,
connChan: make(chan net.Conn, 1024), connChan: make(chan net.Conn, 1024),
errChan: make(chan error, 1), errChan: make(chan error, 1),
} }
@ -223,7 +197,7 @@ func QUICListener(addr string, config *QUICConfig) (Listener, error) {
func (l *quicListener) listenLoop() { func (l *quicListener) listenLoop() {
for { for {
session, err := l.ln.Accept() session, err := l.ln.Accept(context.Background())
if err != nil { if err != nil {
log.Log("[quic] accept:", err) log.Log("[quic] accept:", err)
l.errChan <- err l.errChan <- err
@ -234,15 +208,15 @@ func (l *quicListener) listenLoop() {
} }
} }
func (l *quicListener) sessionLoop(session quic.Session) { func (l *quicListener) sessionLoop(session quic.Connection) {
log.Logf("[quic] %s <-> %s", session.RemoteAddr(), session.LocalAddr()) log.Logf("[quic] %s <-> %s", session.RemoteAddr(), session.LocalAddr())
defer log.Logf("[quic] %s >-< %s", session.RemoteAddr(), session.LocalAddr()) defer log.Logf("[quic] %s >-< %s", session.RemoteAddr(), session.LocalAddr())
for { for {
stream, err := session.AcceptStream() stream, err := session.AcceptStream(context.Background())
if err != nil { if err != nil {
log.Log("[quic] accept stream:", err) log.Log("[quic] accept stream:", err)
session.Close() session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
return return
} }
@ -291,12 +265,12 @@ func (c *quicConn) RemoteAddr() net.Addr {
} }
type quicCipherConn struct { type quicCipherConn struct {
*net.UDPConn net.PacketConn
key []byte key []byte
} }
func (conn *quicCipherConn) ReadFrom(data []byte) (n int, addr net.Addr, err error) { func (conn *quicCipherConn) ReadFrom(data []byte) (n int, addr net.Addr, err error) {
n, addr, err = conn.UDPConn.ReadFrom(data) n, addr, err = conn.PacketConn.ReadFrom(data)
if err != nil { if err != nil {
return return
} }
@ -316,7 +290,7 @@ func (conn *quicCipherConn) WriteTo(data []byte, addr net.Addr) (n int, err erro
return return
} }
_, err = conn.UDPConn.WriteTo(b, addr) _, err = conn.PacketConn.WriteTo(b, addr)
if err != nil { if err != nil {
return return
} }
@ -362,3 +336,12 @@ func (conn *quicCipherConn) decrypt(data []byte) ([]byte, error) {
nonce, ciphertext := data[:nonceSize], data[nonceSize:] nonce, ciphertext := data[:nonceSize], data[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil) return gcm.Open(nil, nonce, ciphertext, nil)
} }
func tlsConfigQUICALPN(tlsConfig *tls.Config) *tls.Config {
if tlsConfig == nil {
panic("quic: tlsconfig is nil")
}
tlsConfigQUIC := tlsConfig.Clone()
tlsConfigQUIC.NextProtos = []string{"http/3", "quic/v1"}
return tlsConfigQUIC
}

View File

@ -1,3 +1,4 @@
//go:build linux
// +build linux // +build linux
package gost package gost

View File

@ -1,3 +1,4 @@
//go:build !linux
// +build !linux // +build !linux
package gost package gost
@ -35,8 +36,7 @@ func (h *tcpRedirectHandler) Handle(c net.Conn) {
c.Close() c.Close()
} }
type udpRedirectHandler struct { type udpRedirectHandler struct{}
}
// UDPRedirectHandler creates a server Handler for UDP transparent server. // UDPRedirectHandler creates a server Handler for UDP transparent server.
func UDPRedirectHandler(opts ...HandlerOption) Handler { func UDPRedirectHandler(opts ...HandlerOption) Handler {

View File

@ -84,7 +84,7 @@ func (c *relayConnector) ConnectContext(ctx context.Context, conn net.Conn, netw
atype = relay.AddrIPv4 atype = relay.AddrIPv4
} }
req.Features = append(req.Features, &relay.TargetAddrFeature{ req.Features = append(req.Features, &relay.AddrFeature{
AType: atype, AType: atype,
Host: host, Host: host,
Port: uint16(nport), Port: uint16(nport),
@ -155,8 +155,8 @@ func (h *relayHandler) Handle(conn net.Conn) {
feature := f.(*relay.UserAuthFeature) feature := f.(*relay.UserAuthFeature)
user, pass = feature.Username, feature.Password user, pass = feature.Username, feature.Password
} }
if f.Type() == relay.FeatureTargetAddr { if f.Type() == relay.FeatureAddr {
feature := f.(*relay.TargetAddrFeature) feature := f.(*relay.AddrFeature)
raddr = net.JoinHostPort(feature.Host, strconv.Itoa(int(feature.Port))) raddr = net.JoinHostPort(feature.Host, strconv.Itoa(int(feature.Port)))
} }
} }
@ -203,6 +203,13 @@ func (h *relayHandler) Handle(conn net.Conn) {
if udp { if udp {
network = "udp" network = "udp"
} }
if !Can(network, raddr, h.options.Whitelist, h.options.Blacklist) {
resp.Status = relay.StatusForbidden
resp.WriteTo(conn)
log.Logf("[relay] %s -> %s : relay to %s is forbidden",
conn.RemoteAddr(), conn.LocalAddr(), raddr)
return
}
ctx := context.TODO() ctx := context.TODO()
var cc net.Conn var cc net.Conn
@ -261,6 +268,7 @@ type relayConn struct {
udp bool udp bool
wbuf bytes.Buffer wbuf bytes.Buffer
once sync.Once once sync.Once
headerSent bool
} }
func (c *relayConn) Read(b []byte) (n int, err error) { func (c *relayConn) Read(b []byte) (n int, err error) {
@ -323,6 +331,7 @@ func (c *relayConn) Write(b []byte) (n int, err error) {
var bb [2]byte var bb [2]byte
binary.BigEndian.PutUint16(bb[:2], uint16(len(b))) binary.BigEndian.PutUint16(bb[:2], uint16(len(b)))
c.wbuf.Write(bb[:]) c.wbuf.Write(bb[:])
c.headerSent = true
} }
c.wbuf.Write(b) // append the data to the cached header c.wbuf.Write(b) // append the data to the cached header
// _, err = c.Conn.Write(c.wbuf.Bytes()) // _, err = c.Conn.Write(c.wbuf.Bytes())
@ -334,7 +343,13 @@ func (c *relayConn) Write(b []byte) (n int, err error) {
if !c.udp { if !c.udp {
return c.Conn.Write(b) return c.Conn.Write(b)
} }
if !c.headerSent {
c.headerSent = true
b2 := make([]byte, len(b)+2)
copy(b2, b)
_, err = c.Conn.Write(b2)
return
}
nsize := 2 + len(b) nsize := 2 + len(b)
var buf []byte var buf []byte
if nsize <= mediumBufferSize { if nsize <= mediumBufferSize {

View File

@ -8,7 +8,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -263,9 +262,7 @@ func (r *resolver) copyServers() []NameServer {
defer r.mux.RUnlock() defer r.mux.RUnlock()
servers := make([]NameServer, len(r.servers)) servers := make([]NameServer, len(r.servers))
for i := range r.servers { copy(servers, r.servers)
servers[i] = r.servers[i]
}
return servers return servers
} }
@ -312,17 +309,28 @@ func (r *resolver) resolve(ctx context.Context, ex Exchanger, host string) (ips
r.mux.RUnlock() r.mux.RUnlock()
if prefer == "ipv6" { // prefer ipv6 if prefer == "ipv6" { // prefer ipv6
mq := &dns.Msg{} if ips, err = r.resolve6(ctx, ex, host); len(ips) > 0 {
mq.SetQuestion(dns.Fqdn(host), dns.TypeAAAA)
ips, err = r.resolveIPs(ctx, ex, mq)
if err != nil || len(ips) > 0 {
return return
} }
return r.resolve4(ctx, ex, host)
} }
mq := &dns.Msg{} if ips, err = r.resolve4(ctx, ex, host); len(ips) > 0 {
return
}
return r.resolve6(ctx, ex, host)
}
func (r *resolver) resolve4(ctx context.Context, ex Exchanger, host string) (ips []net.IP, err error) {
mq := dns.Msg{}
mq.SetQuestion(dns.Fqdn(host), dns.TypeA) mq.SetQuestion(dns.Fqdn(host), dns.TypeA)
return r.resolveIPs(ctx, ex, mq) return r.resolveIPs(ctx, ex, &mq)
}
func (r *resolver) resolve6(ctx context.Context, ex Exchanger, host string) (ips []net.IP, err error) {
mq := dns.Msg{}
mq.SetQuestion(dns.Fqdn(host), dns.TypeAAAA)
return r.resolveIPs(ctx, ex, &mq)
} }
func (r *resolver) resolveIPs(ctx context.Context, ex Exchanger, mq *dns.Msg) (ips []net.IP, err error) { func (r *resolver) resolveIPs(ctx context.Context, ex Exchanger, mq *dns.Msg) (ips []net.IP, err error) {
@ -710,6 +718,7 @@ func (ex *dnsExchanger) Exchange(ctx context.Context, query []byte) ([]byte, err
conn := &dns.Conn{ conn := &dns.Conn{
Conn: c, Conn: c,
UDPSize: 1024,
} }
if _, err = conn.Write(query); err != nil { if _, err = conn.Write(query); err != nil {
return nil, err return nil, err
@ -905,7 +914,7 @@ func (ex *dohExchanger) Exchange(ctx context.Context, query []byte) ([]byte, err
} }
// Read wireformat response from the body // Read wireformat response from the body
buf, err := ioutil.ReadAll(resp.Body) buf, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read the response body: %s", err) return nil, fmt.Errorf("failed to read the response body: %s", err)
} }

View File

@ -4,15 +4,18 @@ import (
"errors" "errors"
"math/rand" "math/rand"
"net" "net"
"sort"
"strconv" "strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/go-log/log"
) )
var ( var (
// ErrNoneAvailable indicates there is no node available. // ErrNoneAvailable indicates there is no node available.
ErrNoneAvailable = errors.New("none available") ErrNoneAvailable = errors.New("none node available")
) )
// NodeSelector as a mechanism to pick nodes and mark their status. // NodeSelector as a mechanism to pick nodes and mark their status.
@ -205,6 +208,94 @@ func (f *FailFilter) String() string {
return "fail" return "fail"
} }
// FastestFilter filter the fastest node
type FastestFilter struct {
mu sync.Mutex
pinger *net.Dialer
pingResult map[int]int
pingResultTTL map[int]int64
topCount int
}
func NewFastestFilter(pingTimeOut int, topCount int) *FastestFilter {
if pingTimeOut == 0 {
pingTimeOut = 3000 // 3s
}
return &FastestFilter{
mu: sync.Mutex{},
pinger: &net.Dialer{Timeout: time.Millisecond * time.Duration(pingTimeOut)},
pingResult: make(map[int]int, 0),
pingResultTTL: make(map[int]int64, 0),
topCount: topCount,
}
}
func (f *FastestFilter) Filter(nodes []Node) []Node {
// disabled
if f.topCount == 0 {
return nodes
}
// get latency with ttl cache
now := time.Now().Unix()
var getNodeLatency = func(node Node) int {
f.mu.Lock()
defer f.mu.Unlock()
if f.pingResultTTL[node.ID] < now {
f.pingResultTTL[node.ID] = now + 5 // tmp
// get latency
go func(node Node) {
latency := f.doTcpPing(node.Addr)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
ttl := 300 - int64(120*r.Float64())
f.mu.Lock()
defer f.mu.Unlock()
f.pingResult[node.ID] = latency
f.pingResultTTL[node.ID] = now + ttl
}(node)
}
return f.pingResult[node.ID]
}
// sort
sort.Slice(nodes, func(i, j int) bool {
return getNodeLatency(nodes[i]) < getNodeLatency(nodes[j])
})
// split
if len(nodes) <= f.topCount {
return nodes
}
return nodes[0:f.topCount]
}
func (f *FastestFilter) String() string {
return "fastest"
}
// doTcpPing
func (f *FastestFilter) doTcpPing(address string) int {
start := time.Now()
conn, err := f.pinger.Dial("tcp", address)
elapsed := time.Since(start)
if err == nil {
_ = conn.Close()
}
latency := int(elapsed.Milliseconds())
log.Logf("pingDoTCP: %s, latency: %d", address, latency)
return latency
}
// InvalidFilter filters the invalid node. // InvalidFilter filters the invalid node.
// A node is invalid if its port is invalid (negative or zero value). // A node is invalid if its port is invalid (negative or zero value).
type InvalidFilter struct{} type InvalidFilter struct{}

View File

@ -127,6 +127,30 @@ func TestFailFilter(t *testing.T) {
} }
} }
func TestFastestFilter(t *testing.T) {
nodes := []Node{
Node{ID: 1, marker: &failMarker{}, Addr: "1.0.0.1:80"},
Node{ID: 2, marker: &failMarker{}, Addr: "1.0.0.2:80"},
Node{ID: 3, marker: &failMarker{}, Addr: "1.0.0.3:80"},
}
filter := NewFastestFilter(0, 2)
var print = func(nodes []Node) []string {
var rows []string
for _, node := range nodes {
rows = append(rows, node.Addr)
}
return rows
}
result1 := filter.Filter(nodes)
t.Logf("result 1: %+v", print(result1))
time.Sleep(time.Second)
result2 := filter.Filter(nodes)
t.Logf("result 2: %+v", print(result2))
}
func TestSelector(t *testing.T) { func TestSelector(t *testing.T) {
nodes := []Node{ nodes := []Node{
Node{ID: 1, marker: &failMarker{}}, Node{ID: 1, marker: &failMarker{}},

View File

@ -112,13 +112,13 @@ func transport(rw1, rw2 io.ReadWriter) error {
errc <- copyBuffer(rw2, rw1) errc <- copyBuffer(rw2, rw1)
}() }()
err := <-errc if err := <-errc; err != nil && err != io.EOF {
if err != nil && err == io.EOF {
err = nil
}
return err return err
} }
return nil
}
func copyBuffer(dst io.Writer, src io.Reader) error { func copyBuffer(dst io.Writer, src io.Reader) error {
buf := lPool.Get().([]byte) buf := lPool.Get().([]byte)
defer lPool.Put(buf) defer lPool.Put(buf)

View File

@ -1,3 +1,4 @@
//go:build windows
// +build windows // +build windows
package gost package gost

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows // +build !windows
package gost package gost
@ -8,7 +9,7 @@ import (
"syscall" "syscall"
"github.com/go-log/log" "github.com/go-log/log"
"gopkg.in/xtaci/kcp-go.v4" "github.com/xtaci/kcp-go/v5"
) )
func kcpSigHandler() { func kcpSigHandler() {

34
snap/snapcraft.yaml Normal file
View File

@ -0,0 +1,34 @@
name: gost
base: core20
version: '2.12.0'
summary: A simple security tunnel written in golang
description: |
Project: https://github.com/ginuerzh/gost
Wiki: https://v2.gost.run
icon: gost.png
website: https://v2.gost.run
license: MIT
confinement: strict
grade: stable
parts:
gost:
plugin: go
go-channel: latest/stable
source: https://github.com/ginuerzh/gost
source-subdir: cmd/gost
source-type: git
source-tag: v2.12.0
build-packages:
- gcc
apps:
gost:
command: bin/gost
plugs:
- home
- network
- network-bind

View File

@ -1,43 +0,0 @@
name: gost
type: app
version: '2.11.0'
title: GO Simple Tunnel
summary: A simple security tunnel written in golang
description: |
https://github.com/ginuerzh/gost
confinement: strict
grade: stable
base: core18
license: MIT
parts:
gost:
plugin: nil
build-snaps: [go/1.13/stable]
source: https://github.com/ginuerzh/gost.git
source-subdir: cmd/gost
source-type: git
source-branch: '2'
build-packages:
- build-essential
override-build: |
set -ex
echo "Starting override-build:"
pwd
cd $SNAPCRAFT_PART_BUILD
GO111MODULE=on CGO_ENABLED=0 go build --ldflags="-s -w"
./gost -V
echo "Installing to ${SNAPCRAFT_PART_INSTALL}..."
install -d $SNAPCRAFT_PART_INSTALL/bin
cp -v gost $SNAPCRAFT_PART_INSTALL/bin/
echo "Override-build done!"
apps:
gost:
command: bin/gost
plugs:
- home
- network
- network-bind

7
sni.go
View File

@ -17,7 +17,8 @@ import (
"strings" "strings"
"sync" "sync"
dissector "github.com/ginuerzh/tls-dissector" "github.com/asaskevich/govalidator"
dissector "github.com/go-gost/tls-dissector"
"github.com/go-log/log" "github.com/go-log/log"
) )
@ -86,6 +87,10 @@ func (h *sniHandler) Handle(conn net.Conn) {
return return
} }
if !req.URL.IsAbs() && govalidator.IsDNSName(req.Host) {
req.URL.Scheme = "http"
}
handler := &httpHandler{options: h.options} handler := &httpHandler{options: h.options}
handler.Init() handler.Init()
handler.handleRequest(conn, req) handler.handleRequest(conn, req)

View File

@ -7,7 +7,7 @@ import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
@ -69,7 +69,7 @@ func sniRoundtrip(client *Client, server *Server, targetURL string, data []byte)
return errors.New(resp.Status) return errors.New(resp.Status)
} }
recv, err := ioutil.ReadAll(resp.Body) recv, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return return
} }

11
sockopts_linux.go Normal file
View File

@ -0,0 +1,11 @@
package gost
import "syscall"
func setSocketMark(fd int, value int) (e error) {
return syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_MARK, value)
}
func setSocketInterface(fd int, value string) (e error) {
return syscall.SetsockoptString(fd, syscall.SOL_SOCKET, syscall.SO_BINDTODEVICE, value)
}

12
sockopts_other.go Normal file
View File

@ -0,0 +1,12 @@
//go:build !linux
// +build !linux
package gost
func setSocketMark(fd int, value int) (e error) {
return nil
}
func setSocketInterface(fd int, value string) (e error) {
return nil
}

View File

@ -10,13 +10,14 @@ import (
"net" "net"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
"github.com/ginuerzh/gosocks4" "github.com/go-gost/gosocks4"
"github.com/ginuerzh/gosocks5" "github.com/go-gost/gosocks5"
"github.com/go-log/log" "github.com/go-log/log"
smux "gopkg.in/xtaci/smux.v1" smux "github.com/xtaci/smux"
) )
const ( const (
@ -1124,7 +1125,7 @@ func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {
return return
} }
relay, err := net.ListenUDP("udp", nil) relay, err := net.ListenUDP("udp", &net.UDPAddr{IP: conn.LocalAddr().(*net.TCPAddr).IP, Port: 0}) // use out-going interface's IP
if err != nil { if err != nil {
log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
reply := gosocks5.NewReply(gosocks5.Failure, nil) reply := gosocks5.NewReply(gosocks5.Failure, nil)
@ -1137,7 +1138,6 @@ func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {
defer relay.Close() defer relay.Close()
socksAddr := toSocksAddr(relay.LocalAddr()) socksAddr := toSocksAddr(relay.LocalAddr())
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) // replace the IP to the out-going interface's
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr) reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
if err := reply.Write(conn); err != nil { if err := reply.Write(conn); err != nil {
log.Logf("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err) log.Logf("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
@ -1630,17 +1630,21 @@ func (h *socks5Handler) muxBindOn(conn net.Conn, addr string) {
} }
} }
// TODO: support ipv6 and domain // TODO: support domain
func toSocksAddr(addr net.Addr) *gosocks5.Addr { func toSocksAddr(addr net.Addr) *gosocks5.Addr {
host := "0.0.0.0" host := "0.0.0.0"
port := 0 port := 0
addrType := gosocks5.AddrIPv4
if addr != nil { if addr != nil {
h, p, _ := net.SplitHostPort(addr.String()) h, p, _ := net.SplitHostPort(addr.String())
host = h host = h
port, _ = strconv.Atoi(p) port, _ = strconv.Atoi(p)
if strings.Count(host, ":") > 0 {
addrType = gosocks5.AddrIPv6
}
} }
return &gosocks5.Addr{ return &gosocks5.Addr{
Type: gosocks5.AddrIPv4, Type: addrType,
Host: host, Host: host,
Port: uint16(port), Port: uint16(port),
} }

4
ss.go
View File

@ -10,7 +10,7 @@ import (
"net/url" "net/url"
"time" "time"
"github.com/ginuerzh/gosocks5" "github.com/go-gost/gosocks5"
"github.com/go-log/log" "github.com/go-log/log"
"github.com/shadowsocks/go-shadowsocks2/core" "github.com/shadowsocks/go-shadowsocks2/core"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
@ -580,7 +580,7 @@ func (c *shadowCipher) StreamConn(conn net.Conn) net.Conn {
} }
func (c *shadowCipher) PacketConn(conn net.PacketConn) net.PacketConn { func (c *shadowCipher) PacketConn(conn net.PacketConn) net.PacketConn {
return ss.NewSecurePacketConn(conn, c.cipher.Copy(), false) return ss.NewSecurePacketConn(conn, c.cipher.Copy())
} }
func initShadowCipher(info *url.Userinfo) (cipher core.Cipher) { func initShadowCipher(info *url.Userinfo) (cipher core.Cipher) {

18
ssh.go
View File

@ -6,8 +6,8 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -27,20 +27,20 @@ const (
GostSSHTunnelRequest = "gost-tunnel" // extended request type for ssh tunnel GostSSHTunnelRequest = "gost-tunnel" // extended request type for ssh tunnel
) )
var ( var errSessionDead = errors.New("session is dead")
errSessionDead = errors.New("session is dead")
)
// ParseSSHKeyFile parses ssh key file.
func ParseSSHKeyFile(fp string) (ssh.Signer, error) { func ParseSSHKeyFile(fp string) (ssh.Signer, error) {
key, err := ioutil.ReadFile(fp) key, err := os.ReadFile(fp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ssh.ParsePrivateKey(key) return ssh.ParsePrivateKey(key)
} }
// ParseSSHAuthorizedKeysFile parses ssh Authorized Keys file.
func ParseSSHAuthorizedKeysFile(fp string) (map[string]bool, error) { func ParseSSHAuthorizedKeysFile(fp string) (map[string]bool, error) {
authorizedKeysBytes, err := ioutil.ReadFile(fp) authorizedKeysBytes, err := os.ReadFile(fp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -57,8 +57,7 @@ func ParseSSHAuthorizedKeysFile(fp string) (map[string]bool, error) {
return authorizedKeysMap, nil return authorizedKeysMap, nil
} }
type sshDirectForwardConnector struct { type sshDirectForwardConnector struct{}
}
// SSHDirectForwardConnector creates a Connector for SSH TCP direct port forwarding. // SSHDirectForwardConnector creates a Connector for SSH TCP direct port forwarding.
func SSHDirectForwardConnector() Connector { func SSHDirectForwardConnector() Connector {
@ -101,8 +100,7 @@ func (c *sshDirectForwardConnector) ConnectContext(ctx context.Context, conn net
return conn, nil return conn, nil
} }
type sshRemoteForwardConnector struct { type sshRemoteForwardConnector struct{}
}
// SSHRemoteForwardConnector creates a Connector for SSH TCP remote port forwarding. // SSHRemoteForwardConnector creates a Connector for SSH TCP remote port forwarding.
func SSHRemoteForwardConnector() Connector { func SSHRemoteForwardConnector() Connector {

7
tls.go
View File

@ -2,7 +2,6 @@ package gost
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509"
"errors" "errors"
"net" "net"
"sync" "sync"
@ -10,7 +9,7 @@ import (
"github.com/go-log/log" "github.com/go-log/log"
smux "gopkg.in/xtaci/smux.v1" smux "github.com/xtaci/smux"
) )
type tlsTransporter struct { type tlsTransporter struct {
@ -290,6 +289,9 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration)
return nil, err return nil, err
} }
// We can do this in `tls.Config.VerifyConnection`, which effective for
// other TLS protocols such as WebSocket. See `route.go:parseChainNode`
/*
// If crypto/tls is doing verification, there's no need to do our own. // If crypto/tls is doing verification, there's no need to do our own.
if tlsConfig.InsecureSkipVerify == false { if tlsConfig.InsecureSkipVerify == false {
return tlsConn, nil return tlsConn, nil
@ -320,6 +322,7 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration)
tlsConn.Close() tlsConn.Close()
return nil, err return nil, err
} }
*/
return tlsConn, err return tlsConn, err
} }

View File

@ -13,7 +13,6 @@ import (
"github.com/go-log/log" "github.com/go-log/log"
"github.com/shadowsocks/go-shadowsocks2/core" "github.com/shadowsocks/go-shadowsocks2/core"
"github.com/shadowsocks/go-shadowsocks2/shadowaead" "github.com/shadowsocks/go-shadowsocks2/shadowaead"
"github.com/shadowsocks/go-shadowsocks2/shadowstream"
"github.com/songgao/water" "github.com/songgao/water"
"github.com/songgao/water/waterutil" "github.com/songgao/water/waterutil"
"github.com/xtaci/tcpraw" "github.com/xtaci/tcpraw"
@ -340,7 +339,7 @@ func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.A
n, addr, err := conn.ReadFrom(b) n, addr, err := conn.ReadFrom(b)
if err != nil && if err != nil &&
err != shadowaead.ErrShortPacket && err != shadowstream.ErrShortPacket { err != shadowaead.ErrShortPacket {
return err return err
} }
@ -702,7 +701,7 @@ func (h *tapHandler) transportTap(tap net.Conn, conn net.PacketConn, raddr net.A
n, addr, err := conn.ReadFrom(b) n, addr, err := conn.ReadFrom(b)
if err != nil && if err != nil &&
err != shadowaead.ErrShortPacket && err != shadowstream.ErrShortPacket { err != shadowaead.ErrShortPacket {
return err return err
} }

View File

@ -3,15 +3,15 @@ package gost
import ( import (
"fmt" "fmt"
"net" "net"
"os/exec"
"strings"
"github.com/docker/libcontainer/netlink"
"github.com/go-log/log" "github.com/go-log/log"
"github.com/milosgajdos83/tenus"
"github.com/songgao/water" "github.com/songgao/water"
) )
func createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) { func createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {
ip, ipNet, err := net.ParseCIDR(cfg.Addr) ip, _, err := net.ParseCIDR(cfg.Addr)
if err != nil { if err != nil {
return return
} }
@ -26,35 +26,21 @@ func createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {
return return
} }
link, err := tenus.NewLinkFrom(ifce.Name())
if err != nil {
return
}
mtu := cfg.MTU mtu := cfg.MTU
if mtu <= 0 { if mtu <= 0 {
mtu = DefaultMTU mtu = DefaultMTU
} }
cmd := fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu) if err = exeCmd(fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu)); err != nil {
log.Log("[tun]", cmd) log.Log(err)
if er := link.SetLinkMTU(mtu); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
} }
cmd = fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name()) if err = exeCmd(fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name())); err != nil {
log.Log("[tun]", cmd) log.Log(err)
if er := link.SetLinkIp(ip, ipNet); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
} }
cmd = fmt.Sprintf("ip link set dev %s up", ifce.Name()) if err = exeCmd(fmt.Sprintf("ip link set dev %s up", ifce.Name())); err != nil {
log.Log("[tun]", cmd) log.Log(err)
if er := link.SetLinkUp(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
} }
if err = addTunRoutes(ifce.Name(), cfg.Routes...); err != nil { if err = addTunRoutes(ifce.Name(), cfg.Routes...); err != nil {
@ -75,9 +61,8 @@ func createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {
func createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) { func createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) {
var ip net.IP var ip net.IP
var ipNet *net.IPNet
if cfg.Addr != "" { if cfg.Addr != "" {
ip, ipNet, err = net.ParseCIDR(cfg.Addr) ip, _, err = net.ParseCIDR(cfg.Addr)
if err != nil { if err != nil {
return return
} }
@ -93,37 +78,23 @@ func createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) {
return return
} }
link, err := tenus.NewLinkFrom(ifce.Name())
if err != nil {
return
}
mtu := cfg.MTU mtu := cfg.MTU
if mtu <= 0 { if mtu <= 0 {
mtu = DefaultMTU mtu = DefaultMTU
} }
cmd := fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu) if err = exeCmd(fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu)); err != nil {
log.Log("[tap]", cmd) log.Log(err)
if er := link.SetLinkMTU(mtu); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
} }
if cfg.Addr != "" { if cfg.Addr != "" {
cmd = fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name()) if err = exeCmd(fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name())); err != nil {
log.Log("[tap]", cmd) log.Log(err)
if er := link.SetLinkIp(ip, ipNet); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
} }
} }
cmd = fmt.Sprintf("ip link set dev %s up", ifce.Name()) if err = exeCmd(fmt.Sprintf("ip link set dev %s up", ifce.Name())); err != nil {
log.Log("[tap]", cmd) log.Log(err)
if er := link.SetLinkUp(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
} }
if err = addTapRoutes(ifce.Name(), cfg.Gateway, cfg.Routes...); err != nil { if err = addTapRoutes(ifce.Name(), cfg.Gateway, cfg.Routes...); err != nil {
@ -149,8 +120,10 @@ func addTunRoutes(ifName string, routes ...IPRoute) error {
} }
cmd := fmt.Sprintf("ip route add %s dev %s", route.Dest.String(), ifName) cmd := fmt.Sprintf("ip route add %s dev %s", route.Dest.String(), ifName)
log.Logf("[tun] %s", cmd) log.Logf("[tun] %s", cmd)
if err := netlink.AddRoute(route.Dest.String(), "", "", ifName); err != nil {
return fmt.Errorf("%s: %v", cmd, err) args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
log.Logf("[tun] %s: %v", cmd, er)
} }
} }
return nil return nil
@ -163,9 +136,22 @@ func addTapRoutes(ifName string, gw string, routes ...string) error {
} }
cmd := fmt.Sprintf("ip route add %s via %s dev %s", route, gw, ifName) cmd := fmt.Sprintf("ip route add %s via %s dev %s", route, gw, ifName)
log.Logf("[tap] %s", cmd) log.Logf("[tap] %s", cmd)
if err := netlink.AddRoute(route, "", gw, ifName); err != nil {
return fmt.Errorf("%s: %v", cmd, err) args := strings.Split(cmd, " ")
if er := exec.Command(args[0], args[1:]...).Run(); er != nil {
log.Logf("[tap] %s: %v", cmd, er)
} }
} }
return nil return nil
} }
func exeCmd(cmd string) error {
log.Log(cmd)
args := strings.Split(cmd, " ")
if err := exec.Command(args[0], args[1:]...).Run(); err != nil {
return fmt.Errorf("%s: %v", cmd, err)
}
return nil
}

View File

@ -1,3 +1,4 @@
//go:build !linux && !windows && !darwin
// +build !linux,!windows,!darwin // +build !linux,!windows,!darwin
package gost package gost

View File

@ -28,7 +28,7 @@ func createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {
return return
} }
cmd := fmt.Sprintf("netsh interface ip set address name=%s "+ cmd := fmt.Sprintf("netsh interface ip set address name=\"%s\" "+
"source=static addr=%s mask=%s gateway=none", "source=static addr=%s mask=%s gateway=none",
ifce.Name(), ip.String(), ipMask(ipNet.Mask)) ifce.Name(), ip.String(), ipMask(ipNet.Mask))
log.Log("[tun]", cmd) log.Log("[tun]", cmd)
@ -70,7 +70,7 @@ func createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) {
} }
if ip != nil && ipNet != nil { if ip != nil && ipNet != nil {
cmd := fmt.Sprintf("netsh interface ip set address name=%s "+ cmd := fmt.Sprintf("netsh interface ip set address name=\"%s\" "+
"source=static addr=%s mask=%s gateway=none", "source=static addr=%s mask=%s gateway=none",
ifce.Name(), ip.String(), ipMask(ipNet.Mask)) ifce.Name(), ip.String(), ipMask(ipNet.Mask))
log.Log("[tap]", cmd) log.Log("[tap]", cmd)
@ -105,7 +105,7 @@ func addTunRoutes(ifName string, gw string, routes ...IPRoute) error {
deleteRoute(ifName, route.Dest.String()) deleteRoute(ifName, route.Dest.String())
cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=%s store=active", cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=\"%s\" store=active",
route.Dest.String(), ifName) route.Dest.String(), ifName)
if gw != "" { if gw != "" {
cmd += " nexthop=" + gw cmd += " nexthop=" + gw
@ -127,7 +127,7 @@ func addTapRoutes(ifName string, gw string, routes ...string) error {
deleteRoute(ifName, route) deleteRoute(ifName, route)
cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=%s store=active", cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=\"%s\" store=active",
route, ifName) route, ifName)
if gw != "" { if gw != "" {
cmd += " nexthop=" + gw cmd += " nexthop=" + gw
@ -142,7 +142,7 @@ func addTapRoutes(ifName string, gw string, routes ...string) error {
} }
func deleteRoute(ifName string, route string) error { func deleteRoute(ifName string, route string) error {
cmd := fmt.Sprintf("netsh interface ip delete route prefix=%s interface=%s store=active", cmd := fmt.Sprintf("netsh interface ip delete route prefix=%s interface=\"%s\" store=active",
route, ifName) route, ifName)
args := strings.Split(cmd, " ") args := strings.Split(cmd, " ")
return exec.Command(args[0], args[1:]...).Run() return exec.Command(args[0], args[1:]...).Run()

76
vsock.go Normal file
View File

@ -0,0 +1,76 @@
package gost
import (
"net"
"strconv"
"github.com/mdlayher/vsock"
)
// vsockTransporter is a raw VSOCK transporter.
type vsockTransporter struct{}
// VSOCKTransporter creates a raw VSOCK client.
func VSOCKTransporter() Transporter {
return &vsockTransporter{}
}
func (tr *vsockTransporter) Dial(addr string, options ...DialOption) (net.Conn, error) {
opts := &DialOptions{}
for _, option := range options {
option(opts)
}
if opts.Chain == nil {
vAddr, err := parseAddr(addr)
if err != nil {
return nil, err
}
return vsock.Dial(vAddr.ContextID, vAddr.Port, nil)
}
return opts.Chain.Dial(addr)
}
func parseUint32(s string) (uint32, error ) {
n, err := strconv.ParseUint(s, 10, 32)
if err != nil {
return 0, err
}
return uint32(n), nil
}
func parseAddr(addr string) (*vsock.Addr, error) {
hostStr, portStr, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
host := uint32(0)
if hostStr != "" {
host, err = parseUint32(hostStr)
if err != nil {
return nil, err
}
}
port, err := parseUint32(portStr)
if err != nil {
return nil, err
}
return &vsock.Addr{ContextID: host, Port: port}, nil
}
func (tr *vsockTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
return conn, nil
}
func (tr *vsockTransporter) Multiplex() bool {
return false
}
// VSOCKListener creates a Listener for VSOCK proxy server.
func VSOCKListener(addr string) (Listener, error) {
vAddr, err := parseAddr(addr)
if err != nil {
return nil, err
}
return vsock.Listen(vAddr.Port, nil)
}

4
ws.go
View File

@ -15,8 +15,8 @@ import (
"net/url" "net/url"
"github.com/go-log/log" "github.com/go-log/log"
"gopkg.in/gorilla/websocket.v1" "github.com/gorilla/websocket"
smux "gopkg.in/xtaci/smux.v1" smux "github.com/xtaci/smux"
) )
const ( const (