Compare commits

..

86 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
68 changed files with 2574 additions and 845 deletions

View File

@ -1,74 +0,0 @@
# ref: https://github.com/crazy-max/diun/blob/master/.github/workflows/build.yml
name: Docker
on: [push]
jobs:
build:
runs-on: ubuntu-18.04
steps:
- name: Prepare
id: prepare
run: |
if [[ $GITHUB_REF == refs/tags/* ]]; then
echo ::set-output name=version::${GITHUB_REF#refs/tags/v}
elif [[ $GITHUB_REF == refs/heads/master ]]; then
echo ::set-output name=version::latest
elif [[ $GITHUB_REF == refs/heads/* ]]; then
echo ::set-output name=version::${GITHUB_REF#refs/heads/}
else
echo ::set-output name=version::snapshot
fi
echo ::set-output name=docker_platforms::linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/386
echo ::set-output name=docker_image::${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }}
# https://github.com/crazy-max/ghaction-docker-buildx
- name: Set up Docker Buildx
id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
version: latest
- name: Environment
run: |
echo home=$HOME
echo git_ref=$GITHUB_REF
echo git_sha=$GITHUB_SHA
echo version=${{ steps.prepare.outputs.version }}
echo image=${{ steps.prepare.outputs.docker_image }}
echo platforms=${{ steps.prepare.outputs.docker_platforms }}
echo avail_platforms=${{ steps.buildx.outputs.platforms }}
# https://github.com/actions/checkout
- name: Checkout
uses: actions/checkout@v2
- name: Docker Buildx (no push)
run: |
docker buildx build \
--platform ${{ steps.prepare.outputs.docker_platforms }} \
--output "type=image,push=false" \
--tag "${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}" \
--file Dockerfile .
- name: Docker Login
if: success()
env:
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
run: |
echo "${DOCKER_PASSWORD}" | docker login --username "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Docker Buildx (push)
if: success()
run: |
docker buildx build \
--platform ${{ steps.prepare.outputs.docker_platforms }} \
--output "type=image,push=true" \
--tag "${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}" \
--file Dockerfile .
- name: Clear
if: always()
run: |
rm -f ${HOME}/.docker/config.json

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
debian
bin
dist/
# Architecture specific extensions/prefixes
*.[568vq]
@ -30,5 +31,5 @@ _testmain.go
*.bak
.vscode/
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 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/
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
VERSION=$(shell cat gost.go | grep 'Version =' | sed 's/.*\"\(.*\)\".*/\1/g')
GOBUILD=CGO_ENABLED=0 go build --ldflags="-s -w" -v -x -a
GOFILES=cmd/gost/*
GOFILES=cmd/gost/*.go
PLATFORM_LIST = \
darwin-amd64 \
darwin-arm64 \
linux-386 \
linux-amd64 \
linux-armv5 \
@ -18,18 +19,24 @@ PLATFORM_LIST = \
linux-mipsle-hardfloat \
linux-mips64 \
linux-mips64le \
linux-s390x \
linux-riscv64 \
freebsd-386 \
freebsd-amd64
WINDOWS_ARCH_LIST = \
windows-386 \
windows-amd64
windows-amd64 \
windows-arm64
all: linux-amd64 darwin-amd64 windows-amd64 # Most used
darwin-amd64:
GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
darwin-arm64:
GOARCH=arm64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
linux-386:
GOARCH=386 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
@ -66,6 +73,12 @@ linux-mips64:
linux-mips64le:
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:
GOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
@ -78,6 +91,9 @@ windows-386:
windows-amd64:
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))
zip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST))

View File

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

View File

@ -4,33 +4,32 @@ gost - GO Simple Tunnel
### A simple security tunnel written in Golang
[![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)
[![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)
[![Snap Status](https://build.snapcraft.io/badge/ginuerzh/gost.svg)](https://build.snapcraft.io/user/ginuerzh/gost)
[![Docker Build Status](https://img.shields.io/docker/build/ginuerzh/gost.svg)](https://hub.docker.com/r/ginuerzh/gost/)
[![Docker](https://img.shields.io/docker/pulls/ginuerzh/gost.svg)](https://hub.docker.com/r/ginuerzh/gost/)
[![gost](https://snapcraft.io/gost/badge.svg)](https://snapcraft.io/gost)
Features
------
* Listening on multiple ports
* Multi-level forward proxy - proxy chain
* Standard HTTP/HTTPS/HTTP2/SOCKS4(A)/SOCKS5 proxy protocols support
* [Probing resistance](https://docs.ginuerzh.xyz/gost/en/probe_resist/) support for web proxy
* [Support multiple tunnel types](https://docs.ginuerzh.xyz/gost/en/configuration/)
* [TLS encryption via negotiation support for SOCKS5 proxy](https://docs.ginuerzh.xyz/gost/en/socks/)
* [Tunnel UDP over TCP](https://docs.ginuerzh.xyz/gost/en/socks/)
* [TCP/UDP Transparent proxy](https://docs.ginuerzh.xyz/gost/en/redirect/)
* [Local/remote TCP/UDP port forwarding](https://docs.ginuerzh.xyz/gost/en/port-forwarding/)
* [Shadowsocks protocol](https://docs.ginuerzh.xyz/gost/en/ss/)
* [SNI proxy](https://docs.ginuerzh.xyz/gost/en/sni/)
* [Permission control](https://docs.ginuerzh.xyz/gost/en/permission/)
* [Load balancing](https://docs.ginuerzh.xyz/gost/en/load-balancing/)
* [Routing control](https://docs.ginuerzh.xyz/gost/en/bypass/)
* DNS [resolver](https://docs.ginuerzh.xyz/gost/resolver/) and [proxy](https://docs.ginuerzh.xyz/gost/dns/)
* [TUN/TAP device](https://docs.ginuerzh.xyz/gost/en/tuntap/)
* [Probing resistance](https://v2.gost.run/en/probe_resist/) support for web proxy
* [Support multiple tunnel types](https://v2.gost.run/en/configuration/)
* [TLS encryption via negotiation support for SOCKS5 proxy](https://v2.gost.run/en/socks/)
* [Tunnel UDP over TCP](https://v2.gost.run/en/socks/)
* [TCP/UDP Transparent proxy](https://v2.gost.run/en/redirect/)
* [Local/remote TCP/UDP port forwarding](https://v2.gost.run/en/port-forwarding/)
* [Shadowsocks protocol](https://v2.gost.run/en/ss/)
* [SNI proxy](https://v2.gost.run/en/sni/)
* [Permission control](https://v2.gost.run/en/permission/)
* [Load balancing](https://v2.gost.run/en/load-balancing/)
* [Routing control](https://v2.gost.run/en/bypass/)
* DNS [resolver](https://v2.gost.run/resolver/) and [proxy](https://v2.gost.run/dns/)
* [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>
@ -54,7 +53,13 @@ go build
#### Docker
```bash
docker pull ginuerzh/gost
docker run --rm ginuerzh/gost -V
```
#### Homebrew
```bash
brew install gost
```
#### Ubuntu store
@ -213,7 +218,7 @@ gost -L=:8080 -F=h2://server_ip:443
#### 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:

298
bypass.go Normal file
View File

@ -0,0 +1,298 @@
package gost
import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"strconv"
"strings"
"sync"
"time"
glob "github.com/gobwas/glob"
)
// Matcher is a generic pattern matcher,
// it gives the match result of the given pattern for specific v.
type Matcher interface {
Match(v string) bool
String() string
}
// NewMatcher creates a Matcher for the given pattern.
// The acutal Matcher depends on the pattern:
// IP Matcher if pattern is a valid IP address.
// CIDR Matcher if pattern is a valid CIDR address.
// Domain Matcher if both of the above are not.
func NewMatcher(pattern string) Matcher {
if pattern == "" {
return nil
}
if ip := net.ParseIP(pattern); ip != nil {
return IPMatcher(ip)
}
if _, inet, err := net.ParseCIDR(pattern); err == nil {
return CIDRMatcher(inet)
}
return DomainMatcher(pattern)
}
type ipMatcher struct {
ip net.IP
}
// IPMatcher creates a Matcher for a specific IP address.
func IPMatcher(ip net.IP) Matcher {
return &ipMatcher{
ip: ip,
}
}
func (m *ipMatcher) Match(ip string) bool {
if m == nil {
return false
}
return m.ip.Equal(net.ParseIP(ip))
}
func (m *ipMatcher) String() string {
return "ip " + m.ip.String()
}
type cidrMatcher struct {
ipNet *net.IPNet
}
// CIDRMatcher creates a Matcher for a specific CIDR notation IP address.
func CIDRMatcher(inet *net.IPNet) Matcher {
return &cidrMatcher{
ipNet: inet,
}
}
func (m *cidrMatcher) Match(ip string) bool {
if m == nil || m.ipNet == nil {
return false
}
return m.ipNet.Contains(net.ParseIP(ip))
}
func (m *cidrMatcher) String() string {
return "cidr " + m.ipNet.String()
}
type domainMatcher struct {
pattern string
glob glob.Glob
}
// DomainMatcher creates a Matcher for a specific domain pattern,
// the pattern can be a plain domain such as 'example.com',
// a wildcard such as '*.exmaple.com' or a special wildcard '.example.com'.
func DomainMatcher(pattern string) Matcher {
p := pattern
if strings.HasPrefix(pattern, ".") {
p = pattern[1:] // trim the prefix '.'
pattern = "*" + p
}
return &domainMatcher{
pattern: p,
glob: glob.MustCompile(pattern),
}
}
func (m *domainMatcher) Match(domain string) bool {
if m == nil || m.glob == nil {
return false
}
if domain == m.pattern {
return true
}
return m.glob.Match(domain)
}
func (m *domainMatcher) String() string {
return "domain " + m.pattern
}
// Bypass is a filter for address (IP or domain).
// It contains a list of matchers.
type Bypass struct {
matchers []Matcher
period time.Duration // the period for live reloading
reversed bool
stopped chan struct{}
mux sync.RWMutex
}
// NewBypass creates and initializes a new Bypass using matchers as its match rules.
// The rules will be reversed if the reversed is true.
func NewBypass(reversed bool, matchers ...Matcher) *Bypass {
return &Bypass{
matchers: matchers,
reversed: reversed,
stopped: make(chan struct{}),
}
}
// NewBypassPatterns creates and initializes a new Bypass using matcher patterns as its match rules.
// The rules will be reversed if the reverse is true.
func NewBypassPatterns(reversed bool, patterns ...string) *Bypass {
var matchers []Matcher
for _, pattern := range patterns {
if m := NewMatcher(pattern); m != nil {
matchers = append(matchers, m)
}
}
bp := NewBypass(reversed)
bp.AddMatchers(matchers...)
return bp
}
// Contains reports whether the bypass includes addr.
func (bp *Bypass) Contains(addr string) bool {
if bp == nil || addr == "" {
return false
}
// try to strip the port
if host, port, _ := net.SplitHostPort(addr); host != "" && port != "" {
if p, _ := strconv.Atoi(port); p > 0 { // port is valid
addr = host
}
}
bp.mux.RLock()
defer bp.mux.RUnlock()
if len(bp.matchers) == 0 {
return false
}
var matched bool
for _, matcher := range bp.matchers {
if matcher == nil {
continue
}
if matcher.Match(addr) {
matched = true
break
}
}
return !bp.reversed && matched ||
bp.reversed && !matched
}
// AddMatchers appends matchers to the bypass matcher list.
func (bp *Bypass) AddMatchers(matchers ...Matcher) {
bp.mux.Lock()
defer bp.mux.Unlock()
bp.matchers = append(bp.matchers, matchers...)
}
// Matchers return the bypass matcher list.
func (bp *Bypass) Matchers() []Matcher {
bp.mux.RLock()
defer bp.mux.RUnlock()
return bp.matchers
}
// Reversed reports whether the rules of the bypass are reversed.
func (bp *Bypass) Reversed() bool {
bp.mux.RLock()
defer bp.mux.RUnlock()
return bp.reversed
}
// Reload parses config from r, then live reloads the bypass.
func (bp *Bypass) Reload(r io.Reader) error {
var matchers []Matcher
var period time.Duration
var reversed bool
if r == nil || bp.Stopped() {
return nil
}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
ss := splitLine(line)
if len(ss) == 0 {
continue
}
switch ss[0] {
case "reload": // reload option
if len(ss) > 1 {
period, _ = time.ParseDuration(ss[1])
}
case "reverse": // reverse option
if len(ss) > 1 {
reversed, _ = strconv.ParseBool(ss[1])
}
default:
matchers = append(matchers, NewMatcher(ss[0]))
}
}
if err := scanner.Err(); err != nil {
return err
}
bp.mux.Lock()
defer bp.mux.Unlock()
bp.matchers = matchers
bp.period = period
bp.reversed = reversed
return nil
}
// Period returns the reload period.
func (bp *Bypass) Period() time.Duration {
if bp.Stopped() {
return -1
}
bp.mux.RLock()
defer bp.mux.RUnlock()
return bp.period
}
// Stop stops reloading.
func (bp *Bypass) Stop() {
select {
case <-bp.stopped:
default:
close(bp.stopped)
}
}
// Stopped checks whether the reloader is stopped.
func (bp *Bypass) Stopped() bool {
select {
case <-bp.stopped:
return true
default:
return false
}
}
func (bp *Bypass) String() string {
b := &bytes.Buffer{}
fmt.Fprintf(b, "reversed: %v\n", bp.Reversed())
fmt.Fprintf(b, "reload: %v\n", bp.Period())
for _, m := range bp.Matchers() {
b.WriteString(m.String())
b.WriteByte('\n')
}
return b.String()
}

303
bypass_test.go Normal file
View File

@ -0,0 +1,303 @@
package gost
import (
"bytes"
"fmt"
"io"
"testing"
"time"
)
var bypassContainTests = []struct {
patterns []string
reversed bool
addr string
bypassed bool
}{
// empty pattern
{[]string{""}, false, "", false},
{[]string{""}, false, "192.168.1.1", false},
{[]string{""}, true, "", false},
{[]string{""}, true, "192.168.1.1", false},
// IP address
{[]string{"192.168.1.1"}, false, "192.168.1.1", true},
{[]string{"192.168.1.1"}, true, "192.168.1.1", false},
{[]string{"192.168.1.1"}, false, "192.168.1.2", false},
{[]string{"192.168.1.1"}, true, "192.168.1.2", true},
{[]string{"0.0.0.0"}, false, "0.0.0.0", true},
{[]string{"0.0.0.0"}, true, "0.0.0.0", false},
// CIDR address
{[]string{"192.168.1.0/0"}, false, "1.2.3.4", true},
{[]string{"192.168.1.0/0"}, true, "1.2.3.4", false},
{[]string{"192.168.1.0/8"}, false, "192.1.0.255", true},
{[]string{"192.168.1.0/8"}, true, "192.1.0.255", false},
{[]string{"192.168.1.0/8"}, false, "191.1.0.255", false},
{[]string{"192.168.1.0/8"}, true, "191.1.0.255", true},
{[]string{"192.168.1.0/16"}, false, "192.168.0.255", true},
{[]string{"192.168.1.0/16"}, true, "192.168.0.255", false},
{[]string{"192.168.1.0/16"}, false, "192.0.1.255", false},
{[]string{"192.168.1.0/16"}, true, "192.0.0.255", true},
{[]string{"192.168.1.0/24"}, false, "192.168.1.255", true},
{[]string{"192.168.1.0/24"}, true, "192.168.1.255", false},
{[]string{"192.168.1.0/24"}, false, "192.168.0.255", false},
{[]string{"192.168.1.0/24"}, true, "192.168.0.255", true},
{[]string{"192.168.1.1/32"}, false, "192.168.1.1", true},
{[]string{"192.168.1.1/32"}, true, "192.168.1.1", false},
{[]string{"192.168.1.1/32"}, false, "192.168.1.2", false},
{[]string{"192.168.1.1/32"}, true, "192.168.1.2", true},
// plain domain
{[]string{"www.example.com"}, false, "www.example.com", true},
{[]string{"www.example.com"}, true, "www.example.com", false},
{[]string{"http://www.example.com"}, false, "http://www.example.com", true},
{[]string{"http://www.example.com"}, true, "http://www.example.com", false},
{[]string{"http://www.example.com"}, false, "http://example.com", false},
{[]string{"http://www.example.com"}, true, "http://example.com", true},
{[]string{"www.example.com"}, false, "example.com", false},
{[]string{"www.example.com"}, true, "example.com", true},
// host:port
{[]string{"192.168.1.1"}, false, "192.168.1.1:80", true},
{[]string{"192.168.1.1"}, true, "192.168.1.1:80", false},
{[]string{"192.168.1.1:80"}, false, "192.168.1.1", false},
{[]string{"192.168.1.1:80"}, true, "192.168.1.1", true},
{[]string{"192.168.1.1:80"}, false, "192.168.1.1:80", false},
{[]string{"192.168.1.1:80"}, true, "192.168.1.1:80", true},
{[]string{"192.168.1.1:80"}, false, "192.168.1.1:8080", false},
{[]string{"192.168.1.1:80"}, true, "192.168.1.1:8080", true},
{[]string{"example.com"}, false, "example.com:80", true},
{[]string{"example.com"}, true, "example.com:80", false},
{[]string{"example.com:80"}, false, "example.com", false},
{[]string{"example.com:80"}, true, "example.com", true},
{[]string{"example.com:80"}, false, "example.com:80", false},
{[]string{"example.com:80"}, true, "example.com:80", true},
{[]string{"example.com:80"}, false, "example.com:8080", false},
{[]string{"example.com:80"}, true, "example.com:8080", true},
// domain wildcard
{[]string{"*"}, false, "", false},
{[]string{"*"}, false, "192.168.1.1", true},
{[]string{"*"}, false, "192.168.0.0/16", true},
{[]string{"*"}, false, "http://example.com", true},
{[]string{"*"}, false, "example.com:80", true},
{[]string{"*"}, true, "", false},
{[]string{"*"}, true, "192.168.1.1", false},
{[]string{"*"}, true, "192.168.0.0/16", false},
{[]string{"*"}, true, "http://example.com", false},
{[]string{"*"}, true, "example.com:80", false},
// sub-domain
{[]string{"*.example.com"}, false, "example.com", false},
{[]string{"*.example.com"}, false, "http://example.com", false},
{[]string{"*.example.com"}, false, "www.example.com", true},
{[]string{"*.example.com"}, false, "http://www.example.com", true},
{[]string{"*.example.com"}, false, "abc.def.example.com", true},
{[]string{"*.*.example.com"}, false, "example.com", false},
{[]string{"*.*.example.com"}, false, "www.example.com", false},
{[]string{"*.*.example.com"}, false, "abc.def.example.com", true},
{[]string{"*.*.example.com"}, false, "abc.def.ghi.example.com", true},
{[]string{"**.example.com"}, false, "example.com", false},
{[]string{"**.example.com"}, false, "www.example.com", true},
{[]string{"**.example.com"}, false, "abc.def.ghi.example.com", true},
// prefix wildcard
{[]string{"*example.com"}, false, "example.com", true},
{[]string{"*example.com"}, false, "www.example.com", true},
{[]string{"*example.com"}, false, "abc.defexample.com", true},
{[]string{"*example.com"}, false, "abc.def-example.com", true},
{[]string{"*example.com"}, false, "abc.def.example.com", true},
{[]string{"*example.com"}, false, "http://www.example.com", true},
{[]string{"*example.com"}, false, "e-xample.com", false},
{[]string{"http://*.example.com"}, false, "example.com", false},
{[]string{"http://*.example.com"}, false, "http://example.com", false},
{[]string{"http://*.example.com"}, false, "http://www.example.com", true},
{[]string{"http://*.example.com"}, false, "https://www.example.com", false},
{[]string{"http://*.example.com"}, false, "http://abc.def.example.com", true},
{[]string{"www.*.com"}, false, "www.example.com", true},
{[]string{"www.*.com"}, false, "www.abc.def.com", true},
{[]string{"www.*.*.com"}, false, "www.example.com", false},
{[]string{"www.*.*.com"}, false, "www.abc.def.com", true},
{[]string{"www.*.*.com"}, false, "www.abc.def.ghi.com", true},
{[]string{"www.*example*.com"}, false, "www.example.com", true},
{[]string{"www.*example*.com"}, false, "www.abc.example.def.com", true},
{[]string{"www.*example*.com"}, false, "www.e-xample.com", false},
{[]string{"www.example.*"}, false, "www.example.com", true},
{[]string{"www.example.*"}, false, "www.example.io", true},
{[]string{"www.example.*"}, false, "www.example.com.cn", true},
{[]string{".example.com"}, false, "www.example.com", true},
{[]string{".example.com"}, false, "example.com", true},
{[]string{".example.com"}, false, "www.example.com.cn", false},
{[]string{"example.com*"}, false, "example.com", true},
{[]string{"example.com:*"}, false, "example.com", false},
{[]string{"example.com:*"}, false, "example.com:80", false},
{[]string{"example.com:*"}, false, "example.com:8080", false},
{[]string{"example.com:*"}, false, "example.com:http", true},
{[]string{"example.com:*"}, false, "http://example.com:80", false},
{[]string{"*example.com*"}, false, "example.com:80", true},
{[]string{"*example.com:*"}, false, "example.com:80", false},
{[]string{".example.com:*"}, false, "www.example.com", false},
{[]string{".example.com:*"}, false, "http://www.example.com", false},
{[]string{".example.com:*"}, false, "example.com:80", false},
{[]string{".example.com:*"}, false, "www.example.com:8080", false},
{[]string{".example.com:*"}, false, "http://www.example.com:80", true},
}
func TestBypassContains(t *testing.T) {
for i, tc := range bypassContainTests {
tc := tc
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
bp := NewBypassPatterns(tc.reversed, tc.patterns...)
if bp.Contains(tc.addr) != tc.bypassed {
t.Errorf("#%d test failed: %v, %s", i, tc.patterns, tc.addr)
}
})
}
}
var bypassReloadTests = []struct {
r io.Reader
reversed bool
period time.Duration
addr string
bypassed bool
stopped bool
}{
{
r: nil,
reversed: false,
period: 0,
addr: "192.168.1.1",
bypassed: false,
stopped: false,
},
{
r: bytes.NewBufferString(""),
reversed: false,
period: 0,
addr: "192.168.1.1",
bypassed: false,
stopped: false,
},
{
r: bytes.NewBufferString("reverse true\nreload 10s"),
reversed: true,
period: 10 * time.Second,
addr: "192.168.1.1",
bypassed: false,
stopped: false,
},
{
r: bytes.NewBufferString("reverse false\nreload 10s\n192.168.1.1"),
reversed: false,
period: 10 * time.Second,
addr: "192.168.1.1",
bypassed: true,
stopped: false,
},
{
r: bytes.NewBufferString("#reverse true\n#reload 10s\n192.168.0.0/16"),
reversed: false,
period: 0,
addr: "192.168.10.2",
bypassed: true,
stopped: true,
},
{
r: bytes.NewBufferString("#reverse true\n#reload 10s\n192.168.1.0/24 #comment"),
reversed: false,
period: 0,
addr: "192.168.10.2",
bypassed: false,
stopped: true,
},
{
r: bytes.NewBufferString("reverse false\nreload 10s\n192.168.1.1\n#example.com"),
reversed: false,
period: 10 * time.Second,
addr: "example.com",
bypassed: false,
stopped: false,
},
{
r: bytes.NewBufferString("#reverse true\n#reload 10s\n192.168.1.1\n#example.com"),
reversed: false,
period: 0,
addr: "192.168.1.1",
bypassed: true,
stopped: true,
},
{
r: bytes.NewBufferString("#reverse true\n#reload 10s\nexample.com #comment"),
reversed: false,
period: 0,
addr: "example.com",
bypassed: true,
stopped: true,
},
{
r: bytes.NewBufferString("#reverse true\n#reload 10s\n.example.com"),
reversed: false,
period: 0,
addr: "example.com",
bypassed: true,
stopped: true,
},
{
r: bytes.NewBufferString("#reverse true\n#reload 10s\n*.example.com"),
reversed: false,
period: 0,
addr: "example.com",
bypassed: false,
stopped: true,
},
}
func TestByapssReload(t *testing.T) {
for i, tc := range bypassReloadTests {
bp := NewBypass(false)
if err := bp.Reload(tc.r); err != nil {
t.Error(err)
}
t.Log(bp.String())
if bp.Reversed() != tc.reversed {
t.Errorf("#%d test failed: reversed value should be %v, got %v",
i, tc.reversed, bp.reversed)
}
if bp.Period() != tc.period {
t.Errorf("#%d test failed: period value should be %v, got %v",
i, tc.period, bp.Period())
}
if bp.Contains(tc.addr) != tc.bypassed {
t.Errorf("#%d test failed: %v, %s", i, bp.reversed, tc.addr)
}
if tc.stopped {
bp.Stop()
if bp.Period() >= 0 {
t.Errorf("period of the stopped reloader should be minus value")
}
bp.Stop()
}
if bp.Stopped() != tc.stopped {
t.Errorf("#%d test failed: stopped value should be %v, got %v",
i, tc.stopped, bp.Stopped())
}
}
}

View File

@ -3,11 +3,12 @@ package gost
import (
"context"
"errors"
"fmt"
"net"
"syscall"
"time"
"github.com/go-gost/hosts"
"github.com/go-gost/log"
"github.com/go-log/log"
)
var (
@ -19,6 +20,8 @@ var (
type Chain struct {
isRoute bool
Retries int
Mark int
Interface string
nodeGroups []*NodeGroup
route []Node // nodes in the selected route
}
@ -35,10 +38,14 @@ func NewChain(nodes ...Node) *Chain {
// newRoute creates a chain route.
// a chain route is the final route after node selection.
func newRoute(nodes ...Node) *Chain {
chain := NewChain(nodes...)
chain.isRoute = true
return chain
func (c *Chain) newRoute(nodes ...Node) *Chain {
route := NewChain(nodes...)
route.isRoute = true
if !c.IsEmpty() {
route.Interface = c.Interface
route.Mark = c.Mark
}
return route
}
// Nodes returns the proxy nodes that the chain holds.
@ -136,6 +143,9 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
if options == nil {
options = &ChainOptions{}
}
if c == nil {
c = &Chain{}
}
route, err := c.selectRouteFor(address)
if err != nil {
return nil, err
@ -144,6 +154,9 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
ipAddr := address
if address != "" {
ipAddr = c.resolve(address, options.Resolver, options.Hosts)
if ipAddr == "" {
return nil, fmt.Errorf("resolver: domain %s does not exists", address)
}
}
timeout := options.Timeout
@ -151,6 +164,32 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
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() {
switch network {
case "udp", "udp4", "udp6":
@ -161,6 +200,7 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
}
d := &net.Dialer{
Timeout: timeout,
Control: controlFunction,
// LocalAddr: laddr, // TODO: optional local address
}
return d.DialContext(ctx, network, ipAddr)
@ -180,25 +220,25 @@ func (c *Chain) dialWithOptions(ctx context.Context, network, address string, op
return cc, nil
}
func (*Chain) resolve(addr string, resolver Resolver, hosts hosts.Hosts) string {
func (*Chain) resolve(addr string, resolver Resolver, hosts *Hosts) string {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return addr
}
if hosts != nil {
if ip := hosts.Lookup(host); ip != nil {
return net.JoinHostPort(ip.String(), port)
}
if ip := hosts.Lookup(host); ip != nil {
return net.JoinHostPort(ip.String(), port)
}
if resolver != nil {
ips, err := resolver.Resolve(host)
if err != nil {
log.Logf("[resolver] %s: %v", host, err)
}
if len(ips) > 0 {
return net.JoinHostPort(ips[0].String(), port)
if len(ips) == 0 {
log.Logf("[resolver] %s: domain does not exists", host)
return ""
}
return net.JoinHostPort(ips[0].String(), port)
}
return addr
}
@ -289,13 +329,13 @@ func (c *Chain) selectRoute() (route *Chain, err error) {
// selectRouteFor selects route with bypass testing.
func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {
if c.IsEmpty() {
return newRoute(), nil
return c.newRoute(), nil
}
if c.isRoute {
return c, nil
}
route = newRoute()
route = c.newRoute()
var nl []Node
for _, group := range c.nodeGroups {
@ -305,7 +345,7 @@ func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {
return
}
if node.Bypasser != nil && node.Bypasser.Bypass(addr) {
if node.Bypass.Contains(addr) {
break
}
@ -313,7 +353,7 @@ func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {
node.DialOptions = append(node.DialOptions,
ChainDialOption(route),
)
route = newRoute() // cutoff the chain for multiplex node.
route = c.newRoute() // cutoff the chain for multiplex node.
}
route.AddNode(node)
@ -329,8 +369,9 @@ func (c *Chain) selectRouteFor(addr string) (route *Chain, err error) {
type ChainOptions struct {
Retries int
Timeout time.Duration
Hosts hosts.Hosts
Hosts *Hosts
Resolver Resolver
Mark int
}
// ChainOption allows a common way to set chain options.
@ -351,7 +392,7 @@ func TimeoutChainOption(timeout time.Duration) ChainOption {
}
// HostsChainOption specifies the hosts used by Chain.Dial.
func HostsChainOption(hosts hosts.Hosts) ChainOption {
func HostsChainOption(hosts *Hosts) ChainOption {
return func(opts *ChainOptions) {
opts.Hosts = hosts
}

View File

@ -7,7 +7,7 @@ import (
"net/url"
"time"
"github.com/ginuerzh/gosocks5"
"github.com/go-gost/gosocks5"
)
// Client is a proxy client.

View File

@ -1,5 +0,0 @@
reload 3s
reverse true
.baidu.com

View File

@ -5,17 +5,13 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"io/ioutil"
"fmt"
"net"
"net/url"
"os"
"strings"
"github.com/ginuerzh/gost"
"github.com/go-gost/bypass"
"github.com/go-gost/hosts"
"github.com/go-gost/reloader"
)
var (
@ -47,8 +43,9 @@ var (
defaultKeyFile = "key.pem"
)
// Load the certificate from cert and key files, will use the default certificate if the provided info are invalid.
func tlsConfig(certFile, keyFile string) (*tls.Config, error) {
// Load the certificate from cert & key files and optional client CA file,
// will use the default certificate if the provided info are invalid.
func tlsConfig(certFile, keyFile, caFile string) (*tls.Config, error) {
if certFile == "" || keyFile == "" {
certFile, keyFile = defaultCertFile, defaultKeyFile
}
@ -57,7 +54,19 @@ func tlsConfig(certFile, keyFile string) (*tls.Config, error) {
if err != nil {
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) {
@ -65,12 +74,12 @@ func loadCA(caFile string) (cp *x509.CertPool, err error) {
return
}
cp = x509.NewCertPool()
data, err := ioutil.ReadFile(caFile)
data, err := os.ReadFile(caFile)
if err != nil {
return nil, err
}
if !cp.AppendCertsFromPEM(data) {
return nil, errors.New("AppendCertsFromPEM failed")
return nil, fmt.Errorf("loadCA %s: AppendCertsFromPEM failed", caFile)
}
return
}
@ -101,6 +110,7 @@ func parseUsers(authFile string) (users []*url.Userinfo, err error) {
if err != nil {
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
@ -133,7 +143,7 @@ func parseAuthenticator(s string) (gost.Authenticator, error) {
au := gost.NewLocalAuthenticator(nil)
au.Reload(f)
go reloader.PeriodReload(au, s)
go gost.PeriodReload(au, s)
return au, nil
}
@ -146,42 +156,47 @@ func parseIP(s string, port string) (ips []string) {
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)
if err != nil {
ss := strings.Split(s, ",")
for _, s := range ss {
s = strings.TrimSpace(s)
if s != "" {
// TODO: support IPv6
if !strings.Contains(s, ":") {
s = s + ":" + port
}
ips = append(ips, s)
ips = append(ips, addrFn(s, port))
}
}
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
if !strings.Contains(line, ":") {
line = line + ":" + port
}
ips = append(ips, line)
ips = append(ips, addrFn(line, port))
}
return
}
func parseBypasser(s string) bypass.Bypasser {
func parseBypass(s string) *gost.Bypass {
if s == "" {
return nil
}
var matchers []bypass.Matcher
var matchers []gost.Matcher
var reversed bool
if strings.HasPrefix(s, "~") {
reversed = true
@ -195,17 +210,15 @@ func parseBypasser(s string) bypass.Bypasser {
if s == "" {
continue
}
matchers = append(matchers, bypass.NewMatcher(s))
matchers = append(matchers, gost.NewMatcher(s))
}
return bypass.NewBypasser(reversed, matchers...)
return gost.NewBypass(reversed, matchers...)
}
defer f.Close()
bp := bypass.NewBypasser(reversed)
if r, ok := bp.(reloader.Reloader); ok {
r.Reload(f)
go reloader.PeriodReload(r, s)
}
bp := gost.NewBypass(reversed)
bp.Reload(f)
go gost.PeriodReload(bp, s)
return bp
}
@ -262,25 +275,24 @@ func parseResolver(cfg string) gost.Resolver {
resolver := gost.NewResolver(0)
resolver.Reload(f)
go reloader.PeriodReload(resolver, cfg)
go gost.PeriodReload(resolver, cfg)
return resolver
}
func parseHosts(s string) hosts.Hosts {
func parseHosts(s string) *gost.Hosts {
f, err := os.Open(s)
if err != nil {
return nil
}
defer f.Close()
hsts := hosts.NewHosts()
if r, ok := hsts.(reloader.Reloader); ok {
r.Reload(f)
go reloader.PeriodReload(r, s)
}
hosts := gost.NewHosts()
hosts.Reload(f)
return hsts
go gost.PeriodReload(hosts, s)
return hosts
}
func parseIPRoutes(s string) (routes []gost.IPRoute) {

View File

@ -5,14 +5,14 @@ import (
"errors"
"flag"
"fmt"
stdlog "log"
"net/http"
_ "net/http/pprof"
"os"
"runtime"
_ "net/http/pprof"
"github.com/ginuerzh/gost"
"github.com/go-gost/log"
"github.com/go-log/log"
)
var (
@ -23,8 +23,7 @@ var (
)
func init() {
stdlog.SetFlags(stdlog.LstdFlags | stdlog.Lshortfile)
gost.SetLogger(&log.StdLogger{})
gost.SetLogger(&gost.LogLogger{})
var (
printVersion bool
@ -32,7 +31,9 @@ func init() {
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.IntVar(&baseCfg.route.Mark, "M", 0, "Specify out connection mark")
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(&printVersion, "V", false, "print version")
if pprofEnabled {
@ -41,7 +42,7 @@ func init() {
flag.Parse()
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)
os.Exit(0)
}
@ -68,7 +69,7 @@ func main() {
}
// 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 {
// generate random self-signed certificate.
cert, err := gost.GenCertificate()

View File

@ -5,24 +5,24 @@ import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"strconv"
"strings"
"time"
"github.com/ginuerzh/gost"
"github.com/go-gost/reloader"
)
type peerConfig struct {
Strategy string `json:"strategy"`
MaxFails int `json:"max_fails"`
FailTimeout time.Duration
period time.Duration // the period for live reloading
Nodes []string `json:"nodes"`
group *gost.NodeGroup
baseNodes []gost.Node
stopped chan struct{}
Strategy string `json:"strategy"`
MaxFails int `json:"max_fails"`
FastestCount int `json:"fastest_count"` // topN fastest node count
FailTimeout time.Duration
period time.Duration // the period for live reloading
Nodes []string `json:"nodes"`
group *gost.NodeGroup
baseNodes []gost.Node
stopped chan struct{}
}
func newPeerConfig() *peerConfig {
@ -53,6 +53,7 @@ func (cfg *peerConfig) Reload(r io.Reader) error {
FailTimeout: cfg.FailTimeout,
},
&gost.InvalidFilter{},
gost.NewFastestFilter(0, cfg.FastestCount),
),
gost.WithStrategy(gost.NewStrategy(cfg.Strategy)),
)
@ -75,8 +76,8 @@ func (cfg *peerConfig) Reload(r io.Reader) error {
nodes := group.SetNodes(gNodes...)
for _, node := range nodes[len(cfg.baseNodes):] {
if s, ok := node.Bypasser.(reloader.Stoppable); ok {
s.Stop() // clear the old nodes
if node.Bypass != nil {
node.Bypass.Stop() // clear the old nodes
}
}
@ -84,7 +85,7 @@ func (cfg *peerConfig) Reload(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 {
return err
}
@ -127,6 +128,8 @@ func (cfg *peerConfig) parse(r io.Reader) error {
cfg.Strategy = ss[1]
case "max_fails":
cfg.MaxFails, _ = strconv.Atoi(ss[1])
case "fastest_count":
cfg.FastestCount, _ = strconv.Atoi(ss[1])
case "fail_timeout":
cfg.FailTimeout, _ = time.ParseDuration(ss[1])
case "reload":

View File

@ -3,17 +3,17 @@ package main
import (
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"net"
"net/url"
"os"
"strings"
"time"
"github.com/ginuerzh/gost"
"github.com/go-gost/hosts"
"github.com/go-gost/log"
"github.com/go-gost/reloader"
"github.com/go-log/log"
)
type stringList []string
@ -30,11 +30,15 @@ type route struct {
ServeNodes stringList
ChainNodes stringList
Retries int
Mark int
Interface string
}
func (r *route) parseChain() (*gost.Chain, error) {
chain := gost.NewChain()
chain.Retries = r.Retries
chain.Mark = r.Mark
chain.Interface = r.Interface
gid := 1 // group ID
for _, ns := range r.ChainNodes {
@ -62,6 +66,7 @@ func (r *route) parseChain() (*gost.Chain, error) {
FailTimeout: nodes[0].GetDuration("fail_timeout"),
},
&gost.InvalidFilter{},
gost.NewFastestFilter(0, nodes[0].GetInt("fastest_count")),
),
gost.WithStrategy(gost.NewStrategy(nodes[0].Get("strategy"))),
)
@ -78,7 +83,7 @@ func (r *route) parseChain() (*gost.Chain, error) {
peerCfg.Reload(f)
f.Close()
go reloader.PeriodReload(peerCfg, cfg)
go gost.PeriodReload(peerCfg, cfg)
}
chain.AddNodeGroup(ngroup)
@ -130,6 +135,35 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
InsecureSkipVerify: !node.GetBool("secure"),
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.EnableCompression = node.GetBool("compression")
wsOpts.ReadBufferSize = node.GetInt("rbuf")
@ -179,6 +213,12 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
Timeout: timeout,
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 != "" {
sum := sha256.Sum256([]byte(cipher))
@ -202,6 +242,8 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
tr = gost.FakeTCPTransporter()
case "udp":
tr = gost.UDPTransporter()
case "vsock":
tr = gost.VSOCKTransporter()
default:
tr = gost.TCPTransporter()
}
@ -276,7 +318,7 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) {
Transporter: tr,
}
node.Bypasser = parseBypasser(node.Get("bypass"))
node.Bypass = parseBypass(node.Get("bypass"))
ips := parseIP(node.Get("ip"), sport)
for _, ip := range ips {
@ -345,7 +387,7 @@ func (r *route) GenRouters() ([]router, error) {
}
}
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 != "" {
return nil, err
}
@ -425,6 +467,12 @@ func (r *route) GenRouters() ([]router, error) {
Timeout: timeout,
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 != "" {
sum := sha256.Sum256([]byte(cipher))
config.Key = sum[:]
@ -444,6 +492,8 @@ func (r *route) GenRouters() ([]router, error) {
chain.Nodes()[len(chain.Nodes())-1].Client.Transporter = gost.SSHForwardTransporter()
}
ln, err = gost.TCPListener(node.Addr)
case "vsock":
ln, err = gost.VSOCKListener(node.Addr)
case "udp":
ln, err = gost.UDPListener(node.Addr, &gost.UDPListenConfig{
TTL: ttl,
@ -582,7 +632,7 @@ func (r *route) GenRouters() ([]router, error) {
}
}
node.Bypasser = parseBypasser(node.Get("bypass"))
node.Bypass = parseBypass(node.Get("bypass"))
hosts := parseHosts(node.Get("hosts"))
ips := parseIP(node.Get("ip"), "")
@ -608,7 +658,7 @@ func (r *route) GenRouters() ([]router, error) {
gost.StrategyHandlerOption(gost.NewStrategy(node.Get("strategy"))),
gost.MaxFailsHandlerOption(node.GetInt("max_fails")),
gost.FailTimeoutHandlerOption(node.GetDuration("fail_timeout")),
gost.BypasserHandlerOption(node.Bypasser),
gost.BypassHandlerOption(node.Bypass),
gost.ResolverHandlerOption(resolver),
gost.HostsHandlerOption(hosts),
gost.RetryHandlerOption(node.GetInt("retry")), // override the global retry option.
@ -619,6 +669,8 @@ func (r *route) GenRouters() ([]router, error) {
gost.IPsHandlerOption(ips),
gost.TCPModeHandlerOption(node.GetBool("tcp")),
gost.IPRoutesHandlerOption(tunRoutes...),
gost.ProxyAgentHandlerOption(node.Get("proxyAgent")),
gost.HTTPTunnelHandlerOption(node.GetBool("httpTunnel")),
)
rt := router{
@ -641,7 +693,7 @@ type router struct {
handler gost.Handler
chain *gost.Chain
resolver gost.Resolver
hosts hosts.Hosts
hosts *gost.Hosts
}
func (r *router) Serve() error {

View File

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

10
dns.go
View File

@ -7,15 +7,13 @@ import (
"encoding/base64"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"strconv"
"strings"
"time"
"github.com/go-gost/bpool"
"github.com/go-gost/log"
"github.com/go-log/log"
"github.com/miekg/dns"
)
@ -60,8 +58,8 @@ func (h *dnsHandler) Init(opts ...HandlerOption) {
func (h *dnsHandler) Handle(conn net.Conn) {
defer conn.Close()
b := bpool.Get(mediumBufferSize)
defer bpool.Put(b)
b := mPool.Get().([]byte)
defer mPool.Put(b)
n, err := conn.Read(b)
if err != nil {
@ -268,7 +266,7 @@ func (l *dnsListener) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
buf, err = ioutil.ReadAll(r.Body)
buf, err = io.ReadAll(r.Body)
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return

View File

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

View File

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

View File

@ -5,7 +5,7 @@ import (
"net"
"time"
"github.com/go-gost/log"
"github.com/go-log/log"
"github.com/xtaci/tcpraw"
)

98
go.mod
View File

@ -1,52 +1,60 @@
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 (
git.torproject.org/pluggable-transports/goptlib.git v0.0.0-20180321061416-7d56ec4f381e
git.torproject.org/pluggable-transports/obfs4.git v0.0.0-20181103133120-08f4d470188e
git.torproject.org/pluggable-transports/goptlib.git v1.3.0
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed
github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63 // indirect
github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/coreos/go-iptables v0.4.5 // indirect
github.com/dchest/siphash v1.2.1 // indirect
github.com/docker/libcontainer v2.2.1+incompatible
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/bpool v1.0.0
github.com/go-gost/bypass v1.0.0
github.com/go-gost/hosts v1.0.0
github.com/go-gost/log v1.0.0
github.com/go-gost/relay v0.1.0
github.com/go-gost/reloader v1.1.0
github.com/golang/mock v1.2.0 // indirect
github.com/google/gopacket v1.1.17 // indirect
github.com/gorilla/websocket v1.4.0 // indirect
github.com/hashicorp/golang-lru v0.5.0 // indirect
github.com/klauspost/compress v1.4.1
github.com/klauspost/cpuid v1.2.0 // indirect
github.com/klauspost/reedsolomon v1.7.0 // indirect
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f // indirect
github.com/lucas-clemente/quic-go v0.10.0
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced // indirect
github.com/miekg/dns v1.1.27
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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.3.0
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451
github.com/go-log/log v0.2.0
github.com/gobwas/glob v0.2.3
github.com/gorilla/websocket v1.5.1
github.com/klauspost/compress v1.17.6
github.com/mdlayher/vsock v1.2.1
github.com/miekg/dns v1.1.58
github.com/quic-go/quic-go v0.45.0
github.com/ryanuber/go-glob v1.0.0
github.com/shadowsocks/go-shadowsocks2 v0.1.5
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/xtaci/kcp-go/v5 v5.6.7
github.com/xtaci/smux v1.5.24
github.com/xtaci/tcpraw v1.2.25
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
gopkg.in/gorilla/websocket.v1 v1.4.0
gopkg.in/xtaci/kcp-go.v4 v4.3.2
gopkg.in/xtaci/smux.v1 v1.0.7
gitlab.com/yawning/obfs4.git v0.0.0-20220204003609-77af0cba934d
golang.org/x/crypto v0.24.0
golang.org/x/net v0.26.0
)
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
)

300
go.sum
View File

@ -1,138 +1,204 @@
git.torproject.org/pluggable-transports/goptlib.git v0.0.0-20180321061416-7d56ec4f381e h1:PYcONLFUhr00kGrq7Mf14JRtoXHG7BOSKIfIha0Hu5Q=
git.torproject.org/pluggable-transports/goptlib.git v0.0.0-20180321061416-7d56ec4f381e/go.mod h1:YT4XMSkuEXbtqlydr9+OxqFAyspUv0Gr9qhM3B++o/Q=
git.torproject.org/pluggable-transports/obfs4.git v0.0.0-20181103133120-08f4d470188e h1:c8h60PKrRxEB5debIHBmP7T+s/EUNXTklXqlmJfYiJQ=
git.torproject.org/pluggable-transports/obfs4.git v0.0.0-20181103133120-08f4d470188e/go.mod h1:jRZbfRcLIgFQoCw6tRmsnETVyIj54jOmXhHCYYa0jbs=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
filippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20 h1:iJoUgXvhagsNMrJrvavw7vu1eG8+hm6jLOxlLFcoODw=
filippo.io/edwards25519 v1.0.0-rc.1.0.20210721174708-390f27c3be20/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
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/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/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861 h1:x17NvoJaphEzay72TFej4OSSsgu3xRYBLkbIwdofS/4=
github.com/bifurcation/mint v0.0.0-20181105071958-a14404e9a861/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
github.com/docker/libcontainer v2.2.1+incompatible h1:++SbbkCw+X8vAd4j2gOCzZ2Nn7s2xFALTf7LZKmM1/0=
github.com/docker/libcontainer v2.2.1+incompatible/go.mod h1:osvj61pYsqhNCMLGX31xr7klUBhHb/ZBuXS0o1Fvwbw=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ginuerzh/gosocks4 v0.0.1 h1:ojDKUyz+uaEeRm2usY1cyQiXTqJqrKxfeE6SVBXq4m0=
github.com/ginuerzh/gosocks4 v0.0.1/go.mod h1:8SdwBMKjfJ9+BfP2vDJM1jcrgWUbWV6qxBPHHVrwptY=
github.com/ginuerzh/gosocks5 v0.2.0 h1:K0Ua23U9LU3BZrf3XpGDcs0mP8DiEpa6PJE4TA/MU3s=
github.com/ginuerzh/gosocks5 v0.2.0/go.mod h1:qp22mr6tH/prEoaN0pFukq76LlScIE+F2rP2ZP5ZHno=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200224064855-24ab2b3a3796 h1:VPXbYRvZUzTemsI7u0FzOnEuHeHwQuMTPXApAu8aeX4=
github.com/ginuerzh/tls-dissector v0.0.2-0.20200224064855-24ab2b3a3796/go.mod h1:YyzP8PQrGwDH/XsfHJXwqdHLwWvBYxu77YVKm0+68f0=
github.com/go-gost/bpool v1.0.0 h1:Og+6SH6SooHqf8CIwuxcPRHq8k0Si4YEfO2mBEi3/Uk=
github.com/go-gost/bpool v1.0.0/go.mod h1:y/Pywm22A4OrJqNF/mL4nW7yb9fCdhlO8cxjyparkFI=
github.com/go-gost/bypass v1.0.0 h1:ZhLzA3WY9JDxmpyuGwVUH0ubaFOmWWzNpejzwExvhMA=
github.com/go-gost/bypass v1.0.0/go.mod h1:r2MYlxn1/fs24NFs+h/m9HiZKfckBrTnONXklxRUwcE=
github.com/go-gost/hosts v1.0.0 h1:KnUBEuIZ6CtMAvGN0n8vYvYahPuMMAu58HGFm2QKEhE=
github.com/go-gost/hosts v1.0.0/go.mod h1:6MrBWeZaRKo/ZwKhtSRK49C+rd3b8Hun4rhuRiYT6bI=
github.com/go-gost/log v1.0.0 h1:maSjjMvQqLSQYb0Ta5nJTdlRI+aiLMt6WIBYVxajJgs=
github.com/go-gost/log v1.0.0/go.mod h1:FCOaaJQ7moHTlLxYk7dsFewlS68U9A1GG3OR+yXkF6s=
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-gost/reloader v1.1.0 h1:ovbHzWTzmKVZdqgmuBsLIeCq6GRAMlcwargAxk4roBo=
github.com/go-gost/reloader v1.1.0/go.mod h1:9EbWvTaddhwuIKlR3Byto/oHamAf/kIGKf333ALfpT0=
github.com/dchest/siphash v1.2.2 h1:9DFz8tQwl9pTVt5iok/9zKyzA1Q6bRGiF3HPiEEVr9I=
github.com/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
github.com/go-gost/gosocks5 v0.3.0 h1:Hkmp9YDRBSCJd7xywW6dBPT6B9aQTkuWd+3WCheJiJA=
github.com/go-gost/gosocks5 v0.3.0/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 h1:xj8gUZGYO3nb5+6Bjw9+tsFkA9sYynrOvDvvC4uDV2I=
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
github.com/go-log/log v0.2.0 h1:z8i91GBudxD5L3RmF0KVpetCbcGWAV7q1Tw1eRwQM9Q=
github.com/go-log/log v0.2.0/go.mod h1:xzCnwajcues/6w7lne3yK2QU7DBPW7kqbgPGG5AF65U=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
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/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
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/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/reedsolomon v1.7.0 h1:pLFmRKGko2ZieiTGyo9DahLCIuljyxm+Zzhz/fYEonE=
github.com/klauspost/reedsolomon v1.7.0/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
github.com/libp2p/go-buffer-pool v0.0.3-0.20191206215133-4016316b51a2 h1:pSnSOZz0cmK3c6944tZdBq7Je9Qf28w80n+PCaq+2Qk=
github.com/libp2p/go-buffer-pool v0.0.3-0.20191206215133-4016316b51a2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f h1:sSeNEkJrs+0F9TUau0CgWTTNEwF23HST3Eq0A+QIx+A=
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04=
github.com/lucas-clemente/quic-go v0.10.0 h1:xEF+pSHYAOcu+U10Meunf+DTtc8vhQDRqlA0BJ6hufc=
github.com/lucas-clemente/quic-go v0.10.0/go.mod h1:wuD+2XqEx8G9jtwx5ou2BEYBsE+whgQmlj0Vz/77PrY=
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced h1:zqEC1GJZFbGZA0tRyNZqRjep92K5fujFtFsu5ZW7Aug=
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58=
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/milosgajdos83/tenus v0.0.0-20190415114537-1f3ed00ae7d8 h1:4WFQEfEJ7zaHYViIVM2Cd6tnQOOhiEHbmQtlcV7aOpc=
github.com/milosgajdos83/tenus v0.0.0-20190415114537-1f3ed00ae7d8/go.mod h1:G95Wwn625/q6JCCytI4VR/a5VtPwrtI0B+Q1Gi38QLA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/shadowsocks/go-shadowsocks2 v0.1.0 h1:jQhkjAmMuOTQ7B04bnrRJ5IAoZEwoaXXkKspE7rQ6ck=
github.com/shadowsocks/go-shadowsocks2 v0.1.0/go.mod h1:/0aFGbhK8mtOX4J/6kTJsPLZlEs9KnzKoWCOCvjd7vk=
github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba h1:tJgNXb3S+RkB4kNPi6N5OmEWe3m+Y3Qs6LUMiNDAONM=
github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY=
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b h1:+y4hCMc/WKsDbAPsOQZgBSaSZ26uh2afyaWeVg/3s/c=
github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM=
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/reedsolomon v1.12.0 h1:I5FEp3xSwVCcEh3F5A7dofEfhXdF/bWhQWPH+XwBFno=
github.com/klauspost/reedsolomon v1.12.0/go.mod h1:EPLZJeh4l27pUGC3aXOjheaoh1I9yut7xTURiW3LQ9Y=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/quic-go/quic-go v0.45.0 h1:OHmkQGM37luZITyTSu6ff03HP/2IrwDX1ZFiNEhSFUE=
github.com/quic-go/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
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/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=
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-20190426145343-a29dc8fdc734/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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/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/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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-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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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-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/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-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-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gorilla/websocket.v1 v1.4.0 h1:lREme3ezAGPCpxSHwjGkHhAJX+ed2B6vzAJ+kaqBEIM=
gopkg.in/gorilla/websocket.v1 v1.4.0/go.mod h1:Ons1i8d00TjvJPdla7bJyeXFsdOacUyrTYbg9IetsIE=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/xtaci/kcp-go.v4 v4.3.2 h1:S9IF+L55Ugzl/hVA6wvuL3SuAtTUzH2cBBC88MXQxnE=
gopkg.in/xtaci/kcp-go.v4 v4.3.2/go.mod h1:fFYTlSOHNOHDNTKfoqarZMQsu7g7oXKwJ9wq0i9lODc=
gopkg.in/xtaci/smux.v1 v1.0.7 h1:qootIZs4ZPSx5blhvgaFpx2epdFSWkyw99xT+q0mRXI=
gopkg.in/xtaci/smux.v1 v1.0.7/go.mod h1:NbrPjLp8lNAYN8KqTplnFr2JjIBbr7CdHBkHtHsXtWA=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.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=

29
gost.go
View File

@ -13,13 +13,14 @@ import (
"net"
"net/http"
"strings"
"sync"
"time"
"github.com/go-gost/log"
"github.com/go-log/log"
)
// Version is the gost version.
const Version = "2.11.0"
const Version = "2.12.0"
// Debug is a flag that enables the debug log.
var Debug bool
@ -31,6 +32,24 @@ var (
largeBufferSize = 32 * 1024 // 32KB large buffer
)
var (
sPool = sync.Pool{
New: func() interface{} {
return make([]byte, smallBufferSize)
},
}
mPool = sync.Pool{
New: func() interface{} {
return make([]byte, mediumBufferSize)
},
}
lPool = sync.Pool{
New: func() interface{} {
return make([]byte, largeBufferSize)
},
}
)
var (
// KeepAliveTime is the keep alive time period for TCP connection.
KeepAliveTime = 180 * time.Second
@ -61,6 +80,8 @@ var (
// DefaultUserAgent is the default HTTP User-Agent header used by HTTP and websocket.
DefaultUserAgent = "Chrome/78.0.3904.106"
DefaultProxyAgent = "gost/" + Version
// DefaultMTU is the default mtu for tun/tap device
DefaultMTU = 1350
)
@ -128,9 +149,7 @@ func (rw *readWriter) Write(p []byte) (n int, err error) {
return rw.w.Write(p)
}
var (
nopClientConn = &nopConn{}
)
var nopClientConn = &nopConn{}
// a nop connection implements net.Conn,
// it does nothing.

BIN
gost.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -7,11 +7,9 @@ import (
"net/url"
"time"
"github.com/ginuerzh/gosocks4"
"github.com/ginuerzh/gosocks5"
"github.com/go-gost/bypass"
"github.com/go-gost/hosts"
"github.com/go-gost/log"
"github.com/go-gost/gosocks4"
"github.com/go-gost/gosocks5"
"github.com/go-log/log"
)
// Handler is a proxy server handler
@ -32,11 +30,11 @@ type HandlerOptions struct {
Strategy Strategy
MaxFails int
FailTimeout time.Duration
Bypasser bypass.Bypasser
Bypass *Bypass
Retries int
Timeout time.Duration
Resolver Resolver
Hosts hosts.Hosts
Hosts *Hosts
ProbeResist string
KnockingHost string
Node Node
@ -44,6 +42,8 @@ type HandlerOptions struct {
IPs []string
TCPMode bool
IPRoutes []IPRoute
ProxyAgent string
HTTPTunnel bool
}
// HandlerOption allows a common way to set handler options.
@ -109,9 +109,9 @@ func BlacklistHandlerOption(blacklist *Permissions) HandlerOption {
}
// BypassHandlerOption sets the bypass option of HandlerOptions.
func BypasserHandlerOption(bypasser bypass.Bypasser) HandlerOption {
func BypassHandlerOption(bypass *Bypass) HandlerOption {
return func(opts *HandlerOptions) {
opts.Bypasser = bypasser
opts.Bypass = bypass
}
}
@ -158,7 +158,7 @@ func ResolverHandlerOption(resolver Resolver) HandlerOption {
}
// HostsHandlerOption sets the Hosts option of HandlerOptions.
func HostsHandlerOption(hosts hosts.Hosts) HandlerOption {
func HostsHandlerOption(hosts *Hosts) HandlerOption {
return func(opts *HandlerOptions) {
opts.Hosts = hosts
}
@ -213,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 {
options *HandlerOptions
}

160
hosts.go Normal file
View File

@ -0,0 +1,160 @@
package gost
import (
"bufio"
"io"
"net"
"sync"
"time"
"github.com/go-log/log"
)
// Host is a static mapping from hostname to IP.
type Host struct {
IP net.IP
Hostname string
Aliases []string
}
// NewHost creates a Host.
func NewHost(ip net.IP, hostname string, aliases ...string) Host {
return Host{
IP: ip,
Hostname: hostname,
Aliases: aliases,
}
}
// Hosts is a static table lookup for hostnames.
// For each host a single line should be present with the following information:
// IP_address canonical_hostname [aliases...]
// Fields of the entry are separated by any number of blanks and/or tab characters.
// Text from a "#" character until the end of the line is a comment, and is ignored.
type Hosts struct {
hosts []Host
period time.Duration
stopped chan struct{}
mux sync.RWMutex
}
// NewHosts creates a Hosts with optional list of hosts.
func NewHosts(hosts ...Host) *Hosts {
return &Hosts{
hosts: hosts,
stopped: make(chan struct{}),
}
}
// AddHost adds host(s) to the host table.
func (h *Hosts) AddHost(host ...Host) {
h.mux.Lock()
defer h.mux.Unlock()
h.hosts = append(h.hosts, host...)
}
// Lookup searches the IP address corresponds to the given host from the host table.
func (h *Hosts) Lookup(host string) (ip net.IP) {
if h == nil || host == "" {
return
}
h.mux.RLock()
defer h.mux.RUnlock()
for _, h := range h.hosts {
if h.Hostname == host {
ip = h.IP
break
}
for _, alias := range h.Aliases {
if alias == host {
ip = h.IP
break
}
}
}
if ip != nil && Debug {
log.Logf("[hosts] hit: %s %s", host, ip.String())
}
return
}
// Reload parses config from r, then live reloads the hosts.
func (h *Hosts) Reload(r io.Reader) error {
var period time.Duration
var hosts []Host
if r == nil || h.Stopped() {
return nil
}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
ss := splitLine(line)
if len(ss) < 2 {
continue // invalid lines are ignored
}
switch ss[0] {
case "reload": // reload option
period, _ = time.ParseDuration(ss[1])
default:
ip := net.ParseIP(ss[0])
if ip == nil {
break // invalid IP addresses are ignored
}
host := Host{
IP: ip,
Hostname: ss[1],
}
if len(ss) > 2 {
host.Aliases = ss[2:]
}
hosts = append(hosts, host)
}
}
if err := scanner.Err(); err != nil {
return err
}
h.mux.Lock()
h.period = period
h.hosts = hosts
h.mux.Unlock()
return nil
}
// Period returns the reload period
func (h *Hosts) Period() time.Duration {
if h.Stopped() {
return -1
}
h.mux.RLock()
defer h.mux.RUnlock()
return h.period
}
// Stop stops reloading.
func (h *Hosts) Stop() {
select {
case <-h.stopped:
default:
close(h.stopped)
}
}
// Stopped checks whether the reloader is stopped.
func (h *Hosts) Stopped() bool {
select {
case <-h.stopped:
return true
default:
return false
}
}

130
hosts_test.go Normal file
View File

@ -0,0 +1,130 @@
package gost
import (
"bytes"
"io"
"net"
"testing"
"time"
)
var hostsLookupTests = []struct {
hosts []Host
host string
ip net.IP
}{
{nil, "", nil},
{nil, "example.com", nil},
{[]Host{}, "", nil},
{[]Host{}, "example.com", nil},
{[]Host{NewHost(nil, "")}, "", nil},
{[]Host{NewHost(nil, "example.com")}, "example.com", nil},
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "")}, "", nil},
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "example.com")}, "example.com", net.IPv4(192, 168, 1, 1)},
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "example.com")}, "example", nil},
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "example.com", "example", "examples")}, "example", net.IPv4(192, 168, 1, 1)},
{[]Host{NewHost(net.IPv4(192, 168, 1, 1), "example.com", "example", "examples")}, "examples", net.IPv4(192, 168, 1, 1)},
}
func TestHostsLookup(t *testing.T) {
for i, tc := range hostsLookupTests {
hosts := NewHosts()
hosts.AddHost(tc.hosts...)
ip := hosts.Lookup(tc.host)
if !ip.Equal(tc.ip) {
t.Errorf("#%d test failed: lookup should be %s, got %s", i, tc.ip, ip)
}
}
}
var HostsReloadTests = []struct {
r io.Reader
period time.Duration
host string
ip net.IP
stopped bool
}{
{
r: nil,
period: 0,
host: "",
ip: nil,
},
{
r: bytes.NewBufferString(""),
period: 0,
host: "example.com",
ip: nil,
},
{
r: bytes.NewBufferString("reload 10s"),
period: 10 * time.Second,
host: "example.com",
ip: nil,
},
{
r: bytes.NewBufferString("#reload 10s\ninvalid.ip.addr example.com"),
period: 0,
ip: nil,
},
{
r: bytes.NewBufferString("reload 10s\n192.168.1.1"),
period: 10 * time.Second,
host: "",
ip: nil,
},
{
r: bytes.NewBufferString("#reload 10s\n192.168.1.1 example.com"),
period: 0,
host: "example.com",
ip: net.IPv4(192, 168, 1, 1),
},
{
r: bytes.NewBufferString("#reload 10s\n#192.168.1.1 example.com"),
period: 0,
host: "example.com",
ip: nil,
stopped: true,
},
{
r: bytes.NewBufferString("#reload 10s\n192.168.1.1 example.com example examples"),
period: 0,
host: "example",
ip: net.IPv4(192, 168, 1, 1),
stopped: true,
},
{
r: bytes.NewBufferString("#reload 10s\n192.168.1.1 example.com example examples"),
period: 0,
host: "examples",
ip: net.IPv4(192, 168, 1, 1),
stopped: true,
},
}
func TestHostsReload(t *testing.T) {
for i, tc := range HostsReloadTests {
hosts := NewHosts()
if err := hosts.Reload(tc.r); err != nil {
t.Error(err)
}
if hosts.Period() != tc.period {
t.Errorf("#%d test failed: period value should be %v, got %v",
i, tc.period, hosts.Period())
}
ip := hosts.Lookup(tc.host)
if !ip.Equal(tc.ip) {
t.Errorf("#%d test failed: lookup should be %s, got %s", i, tc.ip, ip)
}
if tc.stopped {
hosts.Stop()
if hosts.Period() >= 0 {
t.Errorf("period of the stopped reloader should be minus value")
}
}
if hosts.Stopped() != tc.stopped {
t.Errorf("#%d test failed: stopped value should be %v, got %v",
i, tc.stopped, hosts.Stopped())
}
}
}

90
http.go
View File

@ -6,6 +6,7 @@ import (
"context"
"encoding/base64"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
@ -15,7 +16,7 @@ import (
"strings"
"time"
"github.com/go-gost/log"
"github.com/go-log/log"
)
type httpConnector struct {
@ -173,7 +174,12 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
ProtoMinor: 1,
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) {
log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s",
@ -189,7 +195,7 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(host) {
if h.options.Bypass.Contains(host) {
resp.StatusCode = http.StatusForbidden
log.Logf("[http] %s - %s bypass %s",
@ -252,7 +258,9 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
// forward http request
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)
if err == nil {
return
@ -285,27 +293,65 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
}
defer cc.Close()
if req.Method == http.MethodConnect {
b := []byte("HTTP/1.1 200 Connection established\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
if Debug {
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(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
}
if req.Method != http.MethodConnect {
h.handleProxy(conn, cc, req)
return
}
b := []byte("HTTP/1.1 200 Connection established\r\n" +
"Proxy-Agent: " + proxyAgent + "\r\n\r\n")
if Debug {
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(b))
}
conn.Write(b)
log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host)
transport(conn, cc)
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) {
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
if Debug && (u != "" || p != "") {
@ -363,10 +409,16 @@ func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.
conn.RemoteAddr(), conn.LocalAddr())
resp.StatusCode = http.StatusProxyAuthRequired
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 {
resp.Header = http.Header{}
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 {
resp.Header.Set("Connection", "keep-alive")
}

View File

@ -9,7 +9,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httputil"
@ -20,7 +19,7 @@ import (
"sync"
"time"
"github.com/go-gost/log"
"github.com/go-log/log"
"golang.org/x/net/http2"
)
@ -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))
}
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) {
log.Logf("[http2] %s - %s : Unauthorized to tcp connect to %s",
@ -374,7 +377,7 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(host) {
if h.options.Bypass.Contains(host) {
log.Logf("[http2] %s - %s bypass %s",
r.RemoteAddr, laddr, host)
w.WriteHeader(http.StatusForbidden)
@ -385,7 +388,7 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
ProtoMajor: 2,
ProtoMinor: 0,
Header: http.Header{},
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
Body: io.NopCloser(bytes.NewReader([]byte{})),
}
if !h.authenticate(w, r, resp) {
@ -535,7 +538,7 @@ func (h *http2Handler) authenticate(w http.ResponseWriter, r *http.Request, resp
} else {
resp.Header = http.Header{}
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 {
resp.Header.Set("Content-Type", "text/html")
}

View File

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

View File

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

44
kcp.go
View File

@ -13,11 +13,11 @@ import (
"sync"
"github.com/go-gost/log"
"github.com/go-log/log"
"github.com/klauspost/compress/snappy"
"github.com/xtaci/kcp-go/v5"
"github.com/xtaci/smux"
"github.com/xtaci/tcpraw"
"gopkg.in/xtaci/kcp-go.v4"
"gopkg.in/xtaci/smux.v1"
)
var (
@ -43,6 +43,9 @@ type KCPConfig struct {
Resend int `json:"resend"`
NoCongestion int `json:"nc"`
SockBuf int `json:"sockbuf"`
SmuxBuf int `json:"smuxbuf"`
StreamBuf int `json:"streambuf"`
SmuxVer int `json:"smuxver"`
KeepAlive int `json:"keepalive"`
SnmpLog string `json:"snmplog"`
SnmpPeriod int `json:"snmpperiod"`
@ -62,6 +65,16 @@ func (c *KCPConfig) Init() {
case "fast3":
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 (
@ -83,6 +96,9 @@ var (
Resend: 0,
NoCongestion: 0,
SockBuf: 4194304,
SmuxVer: 1,
SmuxBuf: 4194304,
StreamBuf: 2097152,
KeepAlive: 10,
SnmpLog: "",
SnmpPeriod: 60,
@ -231,8 +247,14 @@ func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPCon
// stream multiplex
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
if err := smux.VerifyConfig(smuxConfig); err != nil {
return nil, err
}
var cc net.Conn = kcpconn
if !config.NoComp {
cc = newCompStreamConn(kcpconn)
@ -332,7 +354,9 @@ func (l *kcpListener) listenLoop() {
func (l *kcpListener) mux(conn net.Conn) {
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
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) {
n, err = c.w.Write(b)
err = c.w.Flush()
return n, err
if _, err = c.w.Write(b); err != nil {
return 0, err
}
if err = c.w.Flush(); err != nil {
return 0, err
}
return len(b), err
}
func (c *compStreamConn) Close() error {

36
log.go Normal file
View File

@ -0,0 +1,36 @@
package gost
import (
"fmt"
"log"
)
func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
// LogLogger uses the standard log package as the logger
type LogLogger struct {
}
// Log uses the standard log library log.Output
func (l *LogLogger) Log(v ...interface{}) {
log.Output(3, fmt.Sprintln(v...))
}
// Logf uses the standard log library log.Output
func (l *LogLogger) Logf(format string, v ...interface{}) {
log.Output(3, fmt.Sprintf(format, v...))
}
// NopLogger is a dummy logger that discards the log outputs
type NopLogger struct {
}
// Log does nothing
func (l *NopLogger) Log(v ...interface{}) {
}
// Logf does nothing
func (l *NopLogger) Logf(format string, v ...interface{}) {
}

2
mux.go
View File

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

View File

@ -8,8 +8,6 @@ import (
"strings"
"sync"
"time"
"github.com/go-gost/bypass"
)
var (
@ -33,7 +31,7 @@ type Node struct {
ConnectOptions []ConnectOption
Client *Client
marker *failMarker
Bypasser bypass.Bypasser
Bypass *Bypass
}
// ParseNode parses the node info.
@ -92,6 +90,7 @@ func ParseNode(s string) (node Node, err error) {
case "ftcp": // fake TCP
case "dns":
case "redu", "redirectu": // UDP tproxy
case "vsock":
default:
node.Transport = "tcp"
}

177
obfs.go
View File

@ -17,12 +17,12 @@ import (
"sync"
"time"
"github.com/go-gost/log"
"github.com/go-log/log"
pt "git.torproject.org/pluggable-transports/goptlib.git"
"git.torproject.org/pluggable-transports/obfs4.git/transports/base"
"git.torproject.org/pluggable-transports/obfs4.git/transports/obfs4"
dissector "github.com/ginuerzh/tls-dissector"
dissector "github.com/go-gost/tls-dissector"
"gitlab.com/yawning/obfs4.git/transports/base"
"gitlab.com/yawning/obfs4.git/transports/obfs4"
)
const (
@ -313,8 +313,31 @@ var (
0x0601, 0x0602, 0x0603, 0x0501, 0x0502, 0x0503, 0x0401, 0x0402,
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 {
net.Conn
rbuf bytes.Buffer
@ -322,15 +345,96 @@ type obfsTLSConn struct {
host string
isServer bool
handshaked chan struct{}
parser *obfsTLSParser
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.
func ClientObfsTLSConn(conn net.Conn, host string) net.Conn {
return &obfsTLSConn{
Conn: conn,
host: host,
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 {
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
}
@ -521,19 +599,30 @@ func (c *obfsTLSConn) Read(b []byte) (n int, err error) {
return
}
}
select {
case <-c.handshaked:
}
if c.rbuf.Len() > 0 {
return c.rbuf.Read(b)
if c.isServer {
if c.rbuf.Len() > 0 {
return c.rbuf.Read(b)
}
record := &dissector.Record{}
if _, err = record.ReadFrom(c.Conn); err != nil {
return
}
n = copy(b, record.Opaque)
_, 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])
}
}
record := &dissector.Record{}
if _, err = record.ReadFrom(c.Conn); err != nil {
return
}
n = copy(b, record.Opaque)
_, err = c.rbuf.Write(record.Opaque[n:])
return
}
@ -715,6 +804,16 @@ func Obfs4Listener(addr string) (Listener, error) {
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) {
conn, err := l.Listener.Accept()
if err != nil {
@ -723,7 +822,7 @@ func (l *obfs4Listener) Accept() (net.Conn, error) {
cc, err := obfs4ServerConn(l.addr, conn)
if err != nil {
conn.Close()
return nil, err
return nil, TempError{err}
}
return cc, nil
}

165
quic.go
View File

@ -1,6 +1,7 @@
package gost
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
@ -11,17 +12,16 @@ import (
"sync"
"time"
"github.com/go-gost/log"
quic "github.com/lucas-clemente/quic-go"
"github.com/go-log/log"
quic "github.com/quic-go/quic-go"
)
type quicSession struct {
conn net.Conn
session quic.Session
session quic.EarlyConnection
}
func (session *quicSession) GetConn() (*quicConn, error) {
stream, err := session.session.OpenStreamSync()
stream, err := session.session.OpenStreamSync(context.Background())
if err != nil {
return nil, err
}
@ -33,7 +33,7 @@ func (session *quicSession) GetConn() (*quicConn, error) {
}
func (session *quicSession) Close() error {
return session.session.Close()
return session.session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
}
type quicTransporter struct {
@ -59,100 +59,71 @@ func (tr *quicTransporter) Dial(addr string, options ...DialOption) (conn net.Co
option(opts)
}
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
tr.sessionMutex.Lock()
defer tr.sessionMutex.Unlock()
session, ok := tr.sessions[addr]
if !ok {
var cc *net.UDPConn
cc, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
var pc net.PacketConn
pc, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
if err != nil {
return
}
conn = cc
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
}
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) {
opts := &HandshakeOptions{}
for _, option := range options {
option(opts)
}
return conn, nil
}
func (tr *quicTransporter) initSession(addr net.Addr, conn net.PacketConn) (*quicSession, error) {
config := tr.config
if opts.QUICConfig != nil {
config = opts.QUICConfig
if config == nil {
config = &QUICConfig{}
}
if config.TLSConfig == nil {
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{
HandshakeTimeout: config.Timeout,
KeepAlive: config.KeepAlive,
IdleTimeout: config.IdleTimeout,
HandshakeIdleTimeout: config.Timeout,
MaxIdleTimeout: config.IdleTimeout,
KeepAlivePeriod: config.KeepAlivePeriod,
Versions: []quic.VersionNumber{
quic.VersionGQUIC43,
quic.VersionGQUIC39,
quic.Version1,
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 {
log.Logf("quic dial %s: %v", addr, err)
return nil, err
}
return &quicSession{conn: conn, session: session}, nil
return &quicSession{session: session}, nil
}
func (tr *quicTransporter) Multiplex() bool {
@ -161,15 +132,16 @@ func (tr *quicTransporter) Multiplex() bool {
// QUICConfig is the config for QUIC client and server
type QUICConfig struct {
TLSConfig *tls.Config
Timeout time.Duration
KeepAlive bool
IdleTimeout time.Duration
Key []byte
TLSConfig *tls.Config
Timeout time.Duration
KeepAlive bool
KeepAlivePeriod time.Duration
IdleTimeout time.Duration
Key []byte
}
type quicListener struct {
ln quic.Listener
ln quic.EarlyListener
connChan chan net.Conn
errChan chan error
}
@ -180,39 +152,41 @@ func QUICListener(addr string, config *QUICConfig) (Listener, error) {
config = &QUICConfig{}
}
quicConfig := &quic.Config{
HandshakeTimeout: config.Timeout,
KeepAlive: config.KeepAlive,
IdleTimeout: config.IdleTimeout,
HandshakeIdleTimeout: config.Timeout,
KeepAlivePeriod: config.KeepAlivePeriod,
MaxIdleTimeout: config.IdleTimeout,
Versions: []quic.VersionNumber{
quic.Version1,
quic.Version2,
},
}
tlsConfig := config.TLSConfig
if tlsConfig == nil {
tlsConfig = DefaultTLSConfig
}
var conn net.PacketConn
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
lconn, err := net.ListenUDP("udp", udpAddr)
conn, err = net.ListenUDP("udp", udpAddr)
if err != nil {
return nil, err
}
conn = lconn
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 {
return nil, err
}
l := &quicListener{
ln: ln,
ln: *ln,
connChan: make(chan net.Conn, 1024),
errChan: make(chan error, 1),
}
@ -223,7 +197,7 @@ func QUICListener(addr string, config *QUICConfig) (Listener, error) {
func (l *quicListener) listenLoop() {
for {
session, err := l.ln.Accept()
session, err := l.ln.Accept(context.Background())
if err != nil {
log.Log("[quic] accept:", 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())
defer log.Logf("[quic] %s >-< %s", session.RemoteAddr(), session.LocalAddr())
for {
stream, err := session.AcceptStream()
stream, err := session.AcceptStream(context.Background())
if err != nil {
log.Log("[quic] accept stream:", err)
session.Close()
session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
return
}
@ -291,12 +265,12 @@ func (c *quicConn) RemoteAddr() net.Addr {
}
type quicCipherConn struct {
*net.UDPConn
net.PacketConn
key []byte
}
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 {
return
}
@ -316,7 +290,7 @@ func (conn *quicCipherConn) WriteTo(data []byte, addr net.Addr) (n int, err erro
return
}
_, err = conn.UDPConn.WriteTo(b, addr)
_, err = conn.PacketConn.WriteTo(b, addr)
if err != nil {
return
}
@ -362,3 +336,12 @@ func (conn *quicCipherConn) decrypt(data []byte) ([]byte, error) {
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
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
package gost
@ -12,7 +13,7 @@ import (
"time"
"github.com/LiamHaworth/go-tproxy"
"github.com/go-gost/log"
"github.com/go-log/log"
)
type tcpRedirectHandler struct {

View File

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

View File

@ -13,9 +13,8 @@ import (
"sync"
"time"
"github.com/go-gost/bpool"
"github.com/go-gost/log"
"github.com/go-gost/relay"
"github.com/go-log/log"
)
type relayConnector struct {
@ -85,7 +84,7 @@ func (c *relayConnector) ConnectContext(ctx context.Context, conn net.Conn, netw
atype = relay.AddrIPv4
}
req.Features = append(req.Features, &relay.TargetAddrFeature{
req.Features = append(req.Features, &relay.AddrFeature{
AType: atype,
Host: host,
Port: uint16(nport),
@ -156,8 +155,8 @@ func (h *relayHandler) Handle(conn net.Conn) {
feature := f.(*relay.UserAuthFeature)
user, pass = feature.Username, feature.Password
}
if f.Type() == relay.FeatureTargetAddr {
feature := f.(*relay.TargetAddrFeature)
if f.Type() == relay.FeatureAddr {
feature := f.(*relay.AddrFeature)
raddr = net.JoinHostPort(feature.Host, strconv.Itoa(int(feature.Port)))
}
}
@ -204,6 +203,13 @@ func (h *relayHandler) Handle(conn net.Conn) {
if 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()
var cc net.Conn
@ -258,10 +264,11 @@ func (h *relayHandler) Handle(conn net.Conn) {
type relayConn struct {
net.Conn
isServer bool
udp bool
wbuf bytes.Buffer
once sync.Once
isServer bool
udp bool
wbuf bytes.Buffer
once sync.Once
headerSent bool
}
func (c *relayConn) Read(b []byte) (n int, err error) {
@ -324,6 +331,7 @@ func (c *relayConn) Write(b []byte) (n int, err error) {
var bb [2]byte
binary.BigEndian.PutUint16(bb[:2], uint16(len(b)))
c.wbuf.Write(bb[:])
c.headerSent = true
}
c.wbuf.Write(b) // append the data to the cached header
// _, err = c.Conn.Write(c.wbuf.Bytes())
@ -335,12 +343,18 @@ func (c *relayConn) Write(b []byte) (n int, err error) {
if !c.udp {
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)
var buf []byte
if nsize <= mediumBufferSize {
buf = bpool.Get(mediumBufferSize)
defer bpool.Put(buf)
buf = mPool.Get().([]byte)
defer mPool.Put(buf)
} else {
buf = make([]byte, nsize)
}

65
reload.go Normal file
View File

@ -0,0 +1,65 @@
package gost
import (
"io"
"os"
"time"
"github.com/go-log/log"
)
// Reloader is the interface for objects that support live reloading.
type Reloader interface {
Reload(r io.Reader) error
Period() time.Duration
}
// Stoppable is the interface that indicates a Reloader can be stopped.
type Stoppable interface {
Stop()
Stopped() bool
}
// PeriodReload reloads the config configFile periodically according to the period of the Reloader r.
func PeriodReload(r Reloader, configFile string) error {
if r == nil || configFile == "" {
return nil
}
var lastMod time.Time
for {
if r.Period() < 0 {
log.Log("[reload] stopped:", configFile)
return nil
}
f, err := os.Open(configFile)
if err != nil {
return err
}
mt := lastMod
if finfo, err := f.Stat(); err == nil {
mt = finfo.ModTime()
}
if !lastMod.IsZero() && !mt.Equal(lastMod) {
log.Log("[reload]", configFile)
if err := r.Reload(f); err != nil {
log.Logf("[reload] %s: %s", configFile, err)
}
}
f.Close()
lastMod = mt
period := r.Period()
if period == 0 {
log.Log("[reload] disabled:", configFile)
return nil
}
if period < time.Second {
period = time.Second
}
<-time.After(period)
}
}

View File

@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
@ -16,8 +15,7 @@ import (
"sync"
"time"
"github.com/go-gost/log"
"github.com/go-gost/reloader"
"github.com/go-log/log"
"github.com/miekg/dns"
)
@ -181,8 +179,8 @@ type Resolver interface {
// ReloadResolver is resolover that support live reloading.
type ReloadResolver interface {
Resolver
reloader.Reloader
reloader.Stoppable
Reloader
Stoppable
}
type resolver struct {
@ -264,9 +262,7 @@ func (r *resolver) copyServers() []NameServer {
defer r.mux.RUnlock()
servers := make([]NameServer, len(r.servers))
for i := range r.servers {
servers[i] = r.servers[i]
}
copy(servers, r.servers)
return servers
}
@ -313,17 +309,28 @@ func (r *resolver) resolve(ctx context.Context, ex Exchanger, host string) (ips
r.mux.RUnlock()
if prefer == "ipv6" { // prefer ipv6
mq := &dns.Msg{}
mq.SetQuestion(dns.Fqdn(host), dns.TypeAAAA)
ips, err = r.resolveIPs(ctx, ex, mq)
if err != nil || len(ips) > 0 {
if ips, err = r.resolve6(ctx, ex, host); len(ips) > 0 {
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)
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) {
@ -710,7 +717,8 @@ func (ex *dnsExchanger) Exchange(ctx context.Context, query []byte) ([]byte, err
defer c.Close()
conn := &dns.Conn{
Conn: c,
Conn: c,
UDPSize: 1024,
}
if _, err = conn.Write(query); err != nil {
return nil, err
@ -906,7 +914,7 @@ func (ex *dohExchanger) Exchange(ctx context.Context, query []byte) ([]byte, err
}
// Read wireformat response from the body
buf, err := ioutil.ReadAll(resp.Body)
buf, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read the response body: %s", err)
}

View File

@ -4,15 +4,18 @@ import (
"errors"
"math/rand"
"net"
"sort"
"strconv"
"sync"
"sync/atomic"
"time"
"github.com/go-log/log"
)
var (
// 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.
@ -205,6 +208,94 @@ func (f *FailFilter) String() string {
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.
// A node is invalid if its port is invalid (negative or zero value).
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) {
nodes := []Node{
Node{ID: 1, marker: &failMarker{}},

View File

@ -5,8 +5,7 @@ import (
"net"
"time"
"github.com/go-gost/bpool"
"github.com/go-gost/log"
"github.com/go-log/log"
)
// Accepter represents a network endpoint that can accept connection from peer.
@ -113,16 +112,16 @@ func transport(rw1, rw2 io.ReadWriter) error {
errc <- copyBuffer(rw2, rw1)
}()
err := <-errc
if err != nil && err == io.EOF {
err = nil
if err := <-errc; err != nil && err != io.EOF {
return err
}
return err
return nil
}
func copyBuffer(dst io.Writer, src io.Reader) error {
buf := bpool.Get(largeBufferSize)
defer bpool.Put(buf)
buf := lPool.Get().([]byte)
defer lPool.Put(buf)
_, err := io.CopyBuffer(dst, src, buf)
return err

View File

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

View File

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package gost
@ -7,8 +8,8 @@ import (
"os/signal"
"syscall"
"github.com/go-gost/log"
"gopkg.in/xtaci/kcp-go.v4"
"github.com/go-log/log"
"github.com/xtaci/kcp-go/v5"
)
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

11
sni.go
View File

@ -17,8 +17,9 @@ import (
"strings"
"sync"
dissector "github.com/ginuerzh/tls-dissector"
"github.com/go-gost/log"
"github.com/asaskevich/govalidator"
dissector "github.com/go-gost/tls-dissector"
"github.com/go-log/log"
)
type sniConnector struct {
@ -86,6 +87,10 @@ func (h *sniHandler) Handle(conn net.Conn) {
return
}
if !req.URL.IsAbs() && govalidator.IsDNSName(req.Host) {
req.URL.Scheme = "http"
}
handler := &httpHandler{options: h.options}
handler.Init()
handler.handleRequest(conn, req)
@ -113,7 +118,7 @@ func (h *sniHandler) Handle(conn net.Conn) {
conn.RemoteAddr(), conn.LocalAddr(), host)
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(host) {
if h.options.Bypass.Contains(host) {
log.Log("[sni] %s - %s bypass %s",
conn.RemoteAddr(), conn.LocalAddr(), host)
return

View File

@ -7,7 +7,7 @@ import (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"net/url"
@ -69,7 +69,7 @@ func sniRoundtrip(client *Client, server *Server, targetURL string, data []byte)
return errors.New(resp.Status)
}
recv, err := ioutil.ReadAll(resp.Body)
recv, err := io.ReadAll(resp.Body)
if err != nil {
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,14 +10,14 @@ import (
"net"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/ginuerzh/gosocks4"
"github.com/ginuerzh/gosocks5"
"github.com/go-gost/bpool"
"github.com/go-gost/log"
smux "gopkg.in/xtaci/smux.v1"
"github.com/go-gost/gosocks4"
"github.com/go-gost/gosocks5"
"github.com/go-log/log"
smux "github.com/xtaci/smux"
)
const (
@ -900,7 +900,7 @@ func (h *socks5Handler) handleConnect(conn net.Conn, req *gosocks5.Request) {
}
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(host) {
if h.options.Bypass.Contains(host) {
log.Logf("[socks5] %s - %s : Bypass %s",
conn.RemoteAddr(), conn.LocalAddr(), host)
rep := gosocks5.NewReply(gosocks5.NotAllowed, nil)
@ -1125,7 +1125,7 @@ func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {
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 {
log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
@ -1138,7 +1138,6 @@ func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {
defer relay.Close()
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)
if err := reply.Write(conn); err != nil {
log.Logf("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
@ -1239,8 +1238,8 @@ func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
var clientAddr net.Addr
go func() {
b := bpool.Get(mediumBufferSize)
defer bpool.Put(b)
b := mPool.Get().([]byte)
defer mPool.Put(b)
for {
n, laddr, err := relay.ReadFrom(b)
@ -1261,7 +1260,7 @@ func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
if err != nil {
continue // drop silently
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(raddr.String()) {
if h.options.Bypass.Contains(raddr.String()) {
log.Log("[socks5-udp] [bypass] write to", raddr)
continue // bypass
}
@ -1276,8 +1275,8 @@ func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
}()
go func() {
b := bpool.Get(mediumBufferSize)
defer bpool.Put(b)
b := mPool.Get().([]byte)
defer mPool.Put(b)
for {
n, raddr, err := peer.ReadFrom(b)
@ -1288,7 +1287,7 @@ func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
if clientAddr == nil {
continue
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(raddr.String()) {
if h.options.Bypass.Contains(raddr.String()) {
log.Log("[socks5-udp] [bypass] read from", raddr)
continue // bypass
}
@ -1319,8 +1318,8 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
var clientAddr *net.UDPAddr
go func() {
b := bpool.Get(mediumBufferSize)
defer bpool.Put(b)
b := mPool.Get().([]byte)
defer mPool.Put(b)
for {
n, addr, err := uc.ReadFromUDP(b)
@ -1341,7 +1340,7 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
clientAddr = addr
}
raddr := dgram.Header.Addr.String()
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(raddr) {
if h.options.Bypass.Contains(raddr) {
log.Log("[udp-tun] [bypass] write to", raddr)
continue // bypass
}
@ -1370,7 +1369,7 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
continue
}
raddr := dgram.Header.Addr.String()
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(raddr) {
if h.options.Bypass.Contains(raddr) {
log.Log("[udp-tun] [bypass] read from", raddr)
continue // bypass
}
@ -1459,8 +1458,8 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err err
errc := make(chan error, 2)
go func() {
b := bpool.Get(mediumBufferSize)
defer bpool.Put(b)
b := mPool.Get().([]byte)
defer mPool.Put(b)
for {
n, addr, err := pc.ReadFrom(b)
@ -1469,7 +1468,7 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err err
errc <- err
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(addr.String()) {
if h.options.Bypass.Contains(addr.String()) {
log.Log("[socks5] udp-tun bypass read from", addr)
continue // bypass
}
@ -1502,7 +1501,7 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err err
if err != nil {
continue // drop silently
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(addr.String()) {
if h.options.Bypass.Contains(addr.String()) {
log.Log("[socks5] udp-tun bypass write to", addr)
continue // bypass
}
@ -1631,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 {
host := "0.0.0.0"
port := 0
addrType := gosocks5.AddrIPv4
if addr != nil {
h, p, _ := net.SplitHostPort(addr.String())
host = h
port, _ = strconv.Atoi(p)
if strings.Count(host, ":") > 0 {
addrType = gosocks5.AddrIPv6
}
}
return &gosocks5.Addr{
Type: gosocks5.AddrIPv4,
Type: addrType,
Host: host,
Port: uint16(port),
}
@ -1715,7 +1718,7 @@ func (h *socks4Handler) handleConnect(conn net.Conn, req *gosocks4.Request) {
}
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(addr) {
if h.options.Bypass.Contains(addr) {
log.Log("[socks4] %s - %s : Bypass %s",
conn.RemoteAddr(), conn.LocalAddr(), addr)
rep := gosocks4.NewReply(gosocks4.Rejected, nil)
@ -2034,8 +2037,8 @@ func (c *socks5UDPConn) Read(b []byte) (n int, err error) {
}
func (c *socks5UDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
data := bpool.Get(mediumBufferSize)
defer bpool.Put(data)
data := mPool.Get().([]byte)
defer mPool.Put(data)
n, err = c.UDPConn.Read(data)
if err != nil {

41
ss.go
View File

@ -10,9 +10,8 @@ import (
"net/url"
"time"
"github.com/ginuerzh/gosocks5"
"github.com/go-gost/bpool"
"github.com/go-gost/log"
"github.com/go-gost/gosocks5"
"github.com/go-log/log"
"github.com/shadowsocks/go-shadowsocks2/core"
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
)
@ -62,8 +61,8 @@ func (c *shadowConnector) ConnectContext(ctx context.Context, conn net.Conn, net
if err != nil {
return nil, err
}
rawaddr := bpool.Get(smallBufferSize)
defer bpool.Put(rawaddr)
rawaddr := sPool.Get().([]byte)
defer sPool.Put(rawaddr)
n, err := socksAddr.Encode(rawaddr)
if err != nil {
@ -149,7 +148,7 @@ func (h *shadowHandler) Handle(conn net.Conn) {
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(host) {
if h.options.Bypass.Contains(host) {
log.Logf("[ss] %s - %s : Bypass %s",
conn.RemoteAddr(), conn.LocalAddr(), host)
return
@ -341,8 +340,8 @@ func (h *shadowUDPHandler) transportPacket(conn, cc net.PacketConn) (err error)
go func() {
for {
err := func() error {
b := bpool.Get(mediumBufferSize)
defer bpool.Put(b)
b := mPool.Get().([]byte)
defer mPool.Put(b)
n, addr, err := conn.ReadFrom(b)
if err != nil {
@ -378,8 +377,8 @@ func (h *shadowUDPHandler) transportPacket(conn, cc net.PacketConn) (err error)
go func() {
for {
err := func() error {
b := bpool.Get(mediumBufferSize)
defer bpool.Put(b)
b := mPool.Get().([]byte)
defer mPool.Put(b)
n, addr, err := cc.ReadFrom(b)
if err != nil {
@ -435,7 +434,7 @@ func (h *shadowUDPHandler) transportUDP(conn net.Conn, cc net.PacketConn) error
if err != nil {
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(addr.String()) {
if h.options.Bypass.Contains(addr.String()) {
log.Log("[ssu] bypass", addr)
return // bypass
}
@ -453,8 +452,8 @@ func (h *shadowUDPHandler) transportUDP(conn net.Conn, cc net.PacketConn) error
go func() {
for {
er := func() (err error) {
b := bpool.Get(mediumBufferSize)
defer bpool.Put(b)
b := mPool.Get().([]byte)
defer mPool.Put(b)
n, addr, err := cc.ReadFrom(b)
if err != nil {
@ -463,7 +462,7 @@ func (h *shadowUDPHandler) transportUDP(conn net.Conn, cc net.PacketConn) error
if Debug {
log.Logf("[ssu] %s <<< %s length: %d", conn.RemoteAddr(), addr, n)
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(addr.String()) {
if h.options.Bypass.Contains(addr.String()) {
log.Log("[ssu] bypass", addr)
return // bypass
}
@ -515,8 +514,8 @@ type shadowUDPPacketConn struct {
}
func (c *shadowUDPPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
buf := bpool.Get(mediumBufferSize)
defer bpool.Put(buf)
buf := mPool.Get().([]byte)
defer mPool.Put(buf)
buf[0] = 0
buf[1] = 0
@ -554,8 +553,8 @@ func (c *shadowUDPPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error
return
}
buf := bpool.Get(mediumBufferSize)
defer bpool.Put(buf)
buf := mPool.Get().([]byte)
defer mPool.Put(buf)
copy(buf, rawaddr[:nn])
n = copy(buf[nn:], b)
@ -581,7 +580,7 @@ func (c *shadowCipher) StreamConn(conn net.Conn) net.Conn {
}
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) {
@ -612,8 +611,8 @@ func initShadowCipher(info *url.Userinfo) (cipher core.Cipher) {
func readSocksAddr(r io.Reader) (*gosocks5.Addr, error) {
addr := &gosocks5.Addr{}
b := bpool.Get(smallBufferSize)
defer bpool.Put(b)
b := sPool.Get().([]byte)
defer sPool.Put(b)
_, err := io.ReadFull(r, b[:1])
if err != nil {

24
ssh.go
View File

@ -6,14 +6,14 @@ import (
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/go-gost/log"
"github.com/go-log/log"
"golang.org/x/crypto/ssh"
)
@ -27,20 +27,20 @@ const (
GostSSHTunnelRequest = "gost-tunnel" // extended request type for ssh tunnel
)
var (
errSessionDead = errors.New("session is dead")
)
var errSessionDead = errors.New("session is dead")
// ParseSSHKeyFile parses ssh key file.
func ParseSSHKeyFile(fp string) (ssh.Signer, error) {
key, err := ioutil.ReadFile(fp)
key, err := os.ReadFile(fp)
if err != nil {
return nil, err
}
return ssh.ParsePrivateKey(key)
}
// ParseSSHAuthorizedKeysFile parses ssh Authorized Keys file.
func ParseSSHAuthorizedKeysFile(fp string) (map[string]bool, error) {
authorizedKeysBytes, err := ioutil.ReadFile(fp)
authorizedKeysBytes, err := os.ReadFile(fp)
if err != nil {
return nil, err
}
@ -57,8 +57,7 @@ func ParseSSHAuthorizedKeysFile(fp string) (map[string]bool, error) {
return authorizedKeysMap, nil
}
type sshDirectForwardConnector struct {
}
type sshDirectForwardConnector struct{}
// SSHDirectForwardConnector creates a Connector for SSH TCP direct port forwarding.
func SSHDirectForwardConnector() Connector {
@ -101,8 +100,7 @@ func (c *sshDirectForwardConnector) ConnectContext(ctx context.Context, conn net
return conn, nil
}
type sshRemoteForwardConnector struct {
}
type sshRemoteForwardConnector struct{}
// SSHRemoteForwardConnector creates a Connector for SSH TCP remote port forwarding.
func SSHRemoteForwardConnector() Connector {
@ -608,7 +606,7 @@ func (h *sshForwardHandler) directPortForwardChannel(channel ssh.Channel, raddr
return
}
if h.options.Bypasser != nil && h.options.Bypasser.Bypass(raddr) {
if h.options.Bypass.Contains(raddr) {
log.Logf("[ssh-tcp] [bypass] %s", raddr)
return
}
@ -648,7 +646,7 @@ func (h *sshForwardHandler) tcpipForwardRequest(sshConn ssh.Conn, req *ssh.Reque
return
}
ln, err := net.Listen("tcp", addr) //tie to the client connection
ln, err := net.Listen("tcp", addr) // tie to the client connection
if err != nil {
log.Log("[ssh-rtcp]", err)
req.Reply(false, nil)

65
tls.go
View File

@ -2,15 +2,14 @@ package gost
import (
"crypto/tls"
"crypto/x509"
"errors"
"net"
"sync"
"time"
"github.com/go-gost/log"
"github.com/go-log/log"
smux "gopkg.in/xtaci/smux.v1"
smux "github.com/xtaci/smux"
)
type tlsTransporter struct {
@ -290,36 +289,40 @@ func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration)
return nil, err
}
// If crypto/tls is doing verification, there's no need to do our own.
if tlsConfig.InsecureSkipVerify == false {
return tlsConn, nil
}
// Similarly if we use host's CA, we can do full handshake
if tlsConfig.RootCAs == nil {
return tlsConn, nil
}
opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := tlsConn.ConnectionState().PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
// 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 tlsConfig.InsecureSkipVerify == false {
return tlsConn, nil
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
tlsConn.Close()
return nil, err
}
// Similarly if we use host's CA, we can do full handshake
if tlsConfig.RootCAs == nil {
return tlsConn, nil
}
opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := tlsConn.ConnectionState().PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
tlsConn.Close()
return nil, err
}
*/
return tlsConn, err
}

View File

@ -10,8 +10,7 @@ import (
"sync"
"time"
"github.com/go-gost/bpool"
"github.com/go-gost/log"
"github.com/go-log/log"
"github.com/shadowsocks/go-shadowsocks2/core"
"github.com/shadowsocks/go-shadowsocks2/shadowaead"
"github.com/songgao/water"
@ -261,8 +260,8 @@ func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.A
go func() {
for {
err := func() error {
b := bpool.Get(smallBufferSize)
defer bpool.Put(b)
b := sPool.Get().([]byte)
defer sPool.Put(b)
n, err := tun.Read(b)
if err != nil {
@ -335,8 +334,8 @@ func (h *tunHandler) transportTun(tun net.Conn, conn net.PacketConn, raddr net.A
go func() {
for {
err := func() error {
b := bpool.Get(smallBufferSize)
defer bpool.Put(b)
b := sPool.Get().([]byte)
defer sPool.Put(b)
n, addr, err := conn.ReadFrom(b)
if err != nil &&
@ -637,8 +636,8 @@ func (h *tapHandler) transportTap(tap net.Conn, conn net.PacketConn, raddr net.A
go func() {
for {
err := func() error {
b := bpool.Get(smallBufferSize)
defer bpool.Put(b)
b := sPool.Get().([]byte)
defer sPool.Put(b)
n, err := tap.Read(b)
if err != nil {
@ -697,8 +696,8 @@ func (h *tapHandler) transportTap(tap net.Conn, conn net.PacketConn, raddr net.A
go func() {
for {
err := func() error {
b := bpool.Get(smallBufferSize)
defer bpool.Put(b)
b := sPool.Get().([]byte)
defer sPool.Put(b)
n, addr, err := conn.ReadFrom(b)
if err != nil &&

View File

@ -7,7 +7,7 @@ import (
"os/exec"
"strings"
"github.com/go-gost/log"
"github.com/go-log/log"
"github.com/songgao/water"
)

View File

@ -3,15 +3,15 @@ package gost
import (
"fmt"
"net"
"os/exec"
"strings"
"github.com/docker/libcontainer/netlink"
"github.com/go-gost/log"
"github.com/milosgajdos83/tenus"
"github.com/go-log/log"
"github.com/songgao/water"
)
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 {
return
}
@ -26,35 +26,21 @@ func createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {
return
}
link, err := tenus.NewLinkFrom(ifce.Name())
if err != nil {
return
}
mtu := cfg.MTU
if mtu <= 0 {
mtu = DefaultMTU
}
cmd := fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu)
log.Log("[tun]", cmd)
if er := link.SetLinkMTU(mtu); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
if err = exeCmd(fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu)); err != nil {
log.Log(err)
}
cmd = fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name())
log.Log("[tun]", cmd)
if er := link.SetLinkIp(ip, ipNet); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
if err = exeCmd(fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name())); err != nil {
log.Log(err)
}
cmd = fmt.Sprintf("ip link set dev %s up", ifce.Name())
log.Log("[tun]", cmd)
if er := link.SetLinkUp(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
if err = exeCmd(fmt.Sprintf("ip link set dev %s up", ifce.Name())); err != nil {
log.Log(err)
}
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) {
var ip net.IP
var ipNet *net.IPNet
if cfg.Addr != "" {
ip, ipNet, err = net.ParseCIDR(cfg.Addr)
ip, _, err = net.ParseCIDR(cfg.Addr)
if err != nil {
return
}
@ -93,37 +78,23 @@ func createTap(cfg TapConfig) (conn net.Conn, itf *net.Interface, err error) {
return
}
link, err := tenus.NewLinkFrom(ifce.Name())
if err != nil {
return
}
mtu := cfg.MTU
if mtu <= 0 {
mtu = DefaultMTU
}
cmd := fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu)
log.Log("[tap]", cmd)
if er := link.SetLinkMTU(mtu); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
if err = exeCmd(fmt.Sprintf("ip link set dev %s mtu %d", ifce.Name(), mtu)); err != nil {
log.Log(err)
}
if cfg.Addr != "" {
cmd = fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name())
log.Log("[tap]", cmd)
if er := link.SetLinkIp(ip, ipNet); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
if err = exeCmd(fmt.Sprintf("ip address add %s dev %s", cfg.Addr, ifce.Name())); err != nil {
log.Log(err)
}
}
cmd = fmt.Sprintf("ip link set dev %s up", ifce.Name())
log.Log("[tap]", cmd)
if er := link.SetLinkUp(); er != nil {
err = fmt.Errorf("%s: %v", cmd, er)
return
if err = exeCmd(fmt.Sprintf("ip link set dev %s up", ifce.Name())); err != nil {
log.Log(err)
}
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)
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
@ -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)
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
}
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
package gost
@ -8,7 +9,7 @@ import (
"os/exec"
"strings"
"github.com/go-gost/log"
"github.com/go-log/log"
"github.com/songgao/water"
)

View File

@ -6,7 +6,7 @@ import (
"os/exec"
"strings"
"github.com/go-gost/log"
"github.com/go-log/log"
"github.com/songgao/water"
)
@ -28,7 +28,7 @@ func createTun(cfg TunConfig) (conn net.Conn, itf *net.Interface, err error) {
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",
ifce.Name(), ip.String(), ipMask(ipNet.Mask))
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 {
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",
ifce.Name(), ip.String(), ipMask(ipNet.Mask))
log.Log("[tap]", cmd)
@ -105,7 +105,7 @@ func addTunRoutes(ifName string, gw string, routes ...IPRoute) error {
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)
if gw != "" {
cmd += " nexthop=" + gw
@ -127,7 +127,7 @@ func addTapRoutes(ifName string, gw string, routes ...string) error {
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)
if gw != "" {
cmd += " nexthop=" + gw
@ -142,7 +142,7 @@ func addTapRoutes(ifName string, gw string, routes ...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)
args := strings.Split(cmd, " ")
return exec.Command(args[0], args[1:]...).Run()

7
udp.go
View File

@ -7,8 +7,7 @@ import (
"sync/atomic"
"time"
"github.com/go-gost/bpool"
"github.com/go-gost/log"
"github.com/go-log/log"
)
// udpTransporter is a raw UDP transporter.
@ -91,7 +90,7 @@ func UDPListener(addr string, cfg *UDPListenConfig) (Listener, error) {
func (l *udpListener) listenLoop() {
for {
// NOTE: this buffer will be released in the udpServerConn after read.
b := bpool.Get(mediumBufferSize)
b := mPool.Get().([]byte)
n, raddr, err := l.ln.ReadFrom(b)
if err != nil {
@ -244,7 +243,7 @@ func (c *udpServerConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
case bb := <-c.rChan:
n = copy(b, bb)
if cap(bb) == mediumBufferSize {
bpool.Put(bb[:cap(bb)])
mPool.Put(bb[:cap(bb)])
}
case <-c.closed:
err = errors.New("read from closed connection")

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)
}

6
ws.go
View File

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