mirror of https://github.com/go-gost/gost.git
Compare commits
326 Commits
v3.0.0-bet
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
e2447ce578 | |
|
|
811420e923 | |
|
|
374b46dfe1 | |
|
|
22edb92084 | |
|
|
78a0a8c734 | |
|
|
41e1878ebc | |
|
|
5639c90e98 | |
|
|
36abf9bcd9 | |
|
|
d584b7ac61 | |
|
|
56e2a1c496 | |
|
|
b0bea19275 | |
|
|
cd64f2edd3 | |
|
|
5f04c84e32 | |
|
|
600a64e611 | |
|
|
3d6b16686b | |
|
|
44684d40c3 | |
|
|
2be36abe75 | |
|
|
8db62785fa | |
|
|
8740e6f258 | |
|
|
d4c9ef5056 | |
|
|
c06eb0d331 | |
|
|
c8b48dc248 | |
|
|
45d94cf391 | |
|
|
bc96fe3918 | |
|
|
646d3f906c | |
|
|
4c69940f1e | |
|
|
3d1f6fcbbb | |
|
|
e388426ec6 | |
|
|
340ba32ef0 | |
|
|
96551d5fa5 | |
|
|
8d05a6ed93 | |
|
|
0348a16aa9 | |
|
|
50934e0978 | |
|
|
c2ed9c6f07 | |
|
|
3b9da4e260 | |
|
|
f0a67a1108 | |
|
|
59c9638ce6 | |
|
|
49fa28882f | |
|
|
08c617b54e | |
|
|
d03b0e2360 | |
|
|
245d610baf | |
|
|
f01f0c9215 | |
|
|
9b6e9d9cae | |
|
|
1a0e2b06ac | |
|
|
de8bb498db | |
|
|
1f735d0649 | |
|
|
d173d167da | |
|
|
b593fdb952 | |
|
|
d448628fea | |
|
|
0bfc7f10cd | |
|
|
a24b29c8c2 | |
|
|
87af3da1b6 | |
|
|
dc6fe595e0 | |
|
|
db4a0a9259 | |
|
|
62a097c5e4 | |
|
|
b51cecbbfd | |
|
|
66a260abd1 | |
|
|
1d3603f9ed | |
|
|
952c61c1a2 | |
|
|
6d61ab82db | |
|
|
f61bb2fd72 | |
|
|
4c2835db04 | |
|
|
d662b1fd46 | |
|
|
015923fa5d | |
|
|
f66fce9d49 | |
|
|
982a93eee4 | |
|
|
758251d3f9 | |
|
|
a1e48e164b | |
|
|
581dd74bbb | |
|
|
3c7a984aa3 | |
|
|
2a9b7e7d9b | |
|
|
ab2b9714ee | |
|
|
6d38c24d88 | |
|
|
f376db2000 | |
|
|
161d96f582 | |
|
|
128c0c3117 | |
|
|
66ce980c36 | |
|
|
829148d3f8 | |
|
|
c45f148a88 | |
|
|
fdc88af4ac | |
|
|
c41c5bf79a | |
|
|
b3eeb338fb | |
|
|
72f185bbf6 | |
|
|
a7599990a8 | |
|
|
17ca5600d3 | |
|
|
32ccfe483c | |
|
|
1d0eb06938 | |
|
|
1fa5067d14 | |
|
|
7ddfef85a5 | |
|
|
fa98777ab1 | |
|
|
e53486efe0 | |
|
|
881ebfad5b | |
|
|
d5bb691b38 | |
|
|
24c13b861a | |
|
|
d6daa8bf56 | |
|
|
d18539ef66 | |
|
|
48b0a690c5 | |
|
|
a7037a27bf | |
|
|
ac9960de6d | |
|
|
404e099907 | |
|
|
94cd590142 | |
|
|
d3809ec634 | |
|
|
efb4da6fa6 | |
|
|
2ec8584e96 | |
|
|
f05f5cf862 | |
|
|
bf3018b5b1 | |
|
|
073af6163e | |
|
|
ef07551e01 | |
|
|
8077c039f3 | |
|
|
33c8f3595a | |
|
|
87bff8cac6 | |
|
|
d52fcccf06 | |
|
|
18610d4608 | |
|
|
c1de9e4e72 | |
|
|
1f7c080043 | |
|
|
b091f72900 | |
|
|
8e50e9c085 | |
|
|
7a35d25c5c | |
|
|
8e311e7891 | |
|
|
17fc806c2d | |
|
|
b21fd4a22b | |
|
|
3e8a025c45 | |
|
|
0d7a6f8091 | |
|
|
4a2a2a00de | |
|
|
c71e128fec | |
|
|
1400b5ff5d | |
|
|
cd4fad8c08 | |
|
|
56a68ae06f | |
|
|
5d7100f749 | |
|
|
12871a4ff3 | |
|
|
6130bbc075 | |
|
|
85d4fcaf7d | |
|
|
7904e571c1 | |
|
|
8253c41de6 | |
|
|
2148d10def | |
|
|
8846c69c05 | |
|
|
2161b97064 | |
|
|
e245526b33 | |
|
|
60389894c0 | |
|
|
9ab142ac99 | |
|
|
be1b0627d1 | |
|
|
f4115b867d | |
|
|
a513417d7e | |
|
|
58bd3d062b | |
|
|
d2b79c13a6 | |
|
|
fedd7a7b07 | |
|
|
27f4b6abeb | |
|
|
d4aadbbc14 | |
|
|
4508747635 | |
|
|
50844821f8 | |
|
|
29ec7c94ad | |
|
|
53d6706b5d | |
|
|
594418db53 | |
|
|
72cd708dfe | |
|
|
b576bb5645 | |
|
|
8c529e3595 | |
|
|
b75e133719 | |
|
|
abfbf23842 | |
|
|
42d62550bf | |
|
|
69ba5864ad | |
|
|
fdd3eb61c3 | |
|
|
f68bfdb149 | |
|
|
9e4bca84f1 | |
|
|
925ff89b78 | |
|
|
ca077d418a | |
|
|
7264fca4f8 | |
|
|
bc37fac037 | |
|
|
dc48c4a3b2 | |
|
|
310614663a | |
|
|
8f094d86b0 | |
|
|
638c780acd | |
|
|
1f20f199c0 | |
|
|
8dc6146f79 | |
|
|
ed29b34c68 | |
|
|
b41aea03da | |
|
|
2c17e4417d | |
|
|
ea8ff5f77a | |
|
|
6762957c65 | |
|
|
4c934cd737 | |
|
|
0d494f5ef2 | |
|
|
bf1bab7d84 | |
|
|
b8383b1076 | |
|
|
345da28f01 | |
|
|
907420d18b | |
|
|
c1437794f8 | |
|
|
c2df5650fb | |
|
|
23506579e7 | |
|
|
969db8691b | |
|
|
3b0a2f1bae | |
|
|
b2ed4ae9fd | |
|
|
b2784011d0 | |
|
|
d4e00683c3 | |
|
|
471513896d | |
|
|
6967278600 | |
|
|
2898732c20 | |
|
|
bb0c0450e8 | |
|
|
6bb7dfea8f | |
|
|
e27d2bc593 | |
|
|
3144d61ecd | |
|
|
e4b68d385a | |
|
|
a91300d078 | |
|
|
b8785eb71e | |
|
|
67fcd929be | |
|
|
48c262bf71 | |
|
|
05c35005e9 | |
|
|
f6112773a1 | |
|
|
61002b7c9c | |
|
|
f8a19e0829 | |
|
|
b1ace73943 | |
|
|
3d0e993498 | |
|
|
9cbc875965 | |
|
|
139b934fc6 | |
|
|
c1d9228eee | |
|
|
2ea7dc250a | |
|
|
be33046c16 | |
|
|
d85b8d487b | |
|
|
ab8d77bb5e | |
|
|
8640596064 | |
|
|
8a699e1906 | |
|
|
0d41f5edc8 | |
|
|
87364d2494 | |
|
|
6982b8af63 | |
|
|
0b1540b23a | |
|
|
1bbeeb7e66 | |
|
|
5d4db6e377 | |
|
|
05d06a2718 | |
|
|
420b36b2f8 | |
|
|
3883a4493a | |
|
|
355aaa7690 | |
|
|
c73c98974c | |
|
|
98c9cc2821 | |
|
|
b48d2c0dbc | |
|
|
b7266f4660 | |
|
|
6883c32157 | |
|
|
f1c5f7598c | |
|
|
c31242efbb | |
|
|
6ee632cee2 | |
|
|
8e2060582e | |
|
|
410b4ddf53 | |
|
|
0b6474d836 | |
|
|
4b6d6e7e7c | |
|
|
b2434dd05b | |
|
|
3d15dedab2 | |
|
|
436864ca92 | |
|
|
4847f9ee9f | |
|
|
a88b44d01d | |
|
|
45f1cc2efc | |
|
|
bfa1530ef6 | |
|
|
f02c9810f2 | |
|
|
fda6214c12 | |
|
|
acb8c5bd53 | |
|
|
3f53858c83 | |
|
|
a379565412 | |
|
|
69089b790f | |
|
|
1c2d9b2a5b | |
|
|
8d852b62cf | |
|
|
a80dec0dc1 | |
|
|
62ca8ff2de | |
|
|
b8504f7f28 | |
|
|
95a2f0d649 | |
|
|
d810224e7b | |
|
|
db36804bfa | |
|
|
ad8af1942d | |
|
|
3d01f74f7c | |
|
|
6b05bcb0ab | |
|
|
799c95bfb4 | |
|
|
de0a584175 | |
|
|
381b1c9bf5 | |
|
|
ed7d72bfd8 | |
|
|
98aef6ba90 | |
|
|
dfde6efdeb | |
|
|
8858598cf0 | |
|
|
c765789076 | |
|
|
cf49a3ae46 | |
|
|
fa47726c3d | |
|
|
c351b849e6 | |
|
|
44fca4e04c | |
|
|
a795923980 | |
|
|
d3283d6278 | |
|
|
4b3e711adb | |
|
|
258d9f5b26 | |
|
|
5d9d33f368 | |
|
|
fec33e62ea | |
|
|
2f0da6993b | |
|
|
39cb9c65d9 | |
|
|
4ef4448249 | |
|
|
1151ecd2a4 | |
|
|
df3533b4ef | |
|
|
2400e29fe0 | |
|
|
41bb4cdbb0 | |
|
|
4a192bc1b3 | |
|
|
f17b2d2e96 | |
|
|
e771534920 | |
|
|
68c311a7d1 | |
|
|
1c9974340b | |
|
|
09c12b013c | |
|
|
b86d7ee06d | |
|
|
8274b62672 | |
|
|
7f924fdeb6 | |
|
|
5d797eac43 | |
|
|
2c29028a3b | |
|
|
e8071d9b65 | |
|
|
50cf4edd1f | |
|
|
3d5f0f93fd | |
|
|
aa754f8515 | |
|
|
a7f4d3e63e | |
|
|
180214a124 | |
|
|
baa6f0cea0 | |
|
|
ca0de58757 | |
|
|
5acccebe7b | |
|
|
850bb1379f | |
|
|
9e404471a6 | |
|
|
166317a7d0 | |
|
|
77d9e3798c | |
|
|
5cbba69ae2 | |
|
|
8a8cbdd9e1 | |
|
|
fb5f57fa80 | |
|
|
5603af2fcf | |
|
|
8ae4e43fc3 | |
|
|
96b3b7e962 | |
|
|
85de11ae46 | |
|
|
3a409fea42 | |
|
|
1c65aae3a9 | |
|
|
84255850b6 | |
|
|
54e0091c38 | |
|
|
605c43ca08 |
|
|
@ -1,89 +0,0 @@
|
|||
# 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
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
|
||||
fi
|
||||
|
||||
# Set output parameters.
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=docker_image::${DOCKER_IMAGE}
|
||||
echo ::set-output name=docker_platforms::linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/s390x
|
||||
|
||||
# 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 bake \
|
||||
--set ${{ github.event.repository.name }}.platform=${{ steps.prepare.outputs.docker_platforms }} \
|
||||
--set ${{ github.event.repository.name }}.output=type=image,push=false \
|
||||
--set ${{ github.event.repository.name }}.tags="${{ steps.prepare.outputs.tags }}" \
|
||||
--file docker-compose.yaml
|
||||
|
||||
- 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 bake \
|
||||
--set ${{ github.event.repository.name }}.platform=${{ steps.prepare.outputs.docker_platforms }} \
|
||||
--set ${{ github.event.repository.name }}.output=type=image,push=true \
|
||||
--set ${{ github.event.repository.name }}.tags="${{ steps.prepare.outputs.tags }}" \
|
||||
--file docker-compose.yaml
|
||||
|
||||
- name: Clear
|
||||
if: always()
|
||||
run: |
|
||||
rm -f ${HOME}/.docker/config.json
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# 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 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 }}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
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@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: git fetch --force --tags
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.26'
|
||||
cache: true
|
||||
- name: Install UPX
|
||||
uses: crazy-max/ghaction-upx@v3
|
||||
with:
|
||||
install-only: true
|
||||
# More assembly might be required: Docker logins, GPG, etc. It all depends
|
||||
# on your needs.
|
||||
- uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
# either 'goreleaser' (default) or 'goreleaser-pro':
|
||||
distribution: goreleaser
|
||||
version: '~> v2'
|
||||
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 }}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
name: Trigger nightly build
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# * is a special character in YAML, so you have to quote this string
|
||||
- cron: '00 15 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check_date:
|
||||
runs-on: ubuntu-latest
|
||||
name: Check latest commit
|
||||
outputs:
|
||||
should_run: ${{ steps.should_run.outputs.should_run }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: print latest_commit
|
||||
run: echo ${{ github.sha }}
|
||||
- id: should_run
|
||||
continue-on-error: true
|
||||
name: check latest commit is less than a day
|
||||
if: ${{ github.event_name == 'schedule' }}
|
||||
run: test -z $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
|
||||
trigger-nightly:
|
||||
needs: check_date
|
||||
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||
name: Push tag for nightly build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.NIGHTLY_BUILD_GH_TOKEN }}
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: 'Push new tag'
|
||||
run: |
|
||||
git config user.name "${GITHUB_ACTOR}"
|
||||
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
|
||||
|
||||
# A previous release was created using a lightweight tag
|
||||
# git describe by default includes only annotated tags
|
||||
# git describe --tags includes lightweight tags as well
|
||||
DESCRIBE=`git tag -l --sort=-v:refname | grep -v nightly | head -n 1`
|
||||
MAJOR_VERSION=`echo $DESCRIBE | awk '{split($0,a,"."); print a[1]}'`
|
||||
MINOR_VERSION=`echo $DESCRIBE | awk '{split($0,a,"."); print a[2]}'`
|
||||
PATCH_VERSION=`echo $DESCRIBE | awk '{split($0,a,"."); print a[3]}'`
|
||||
PATCH_VERSION="$((${PATCH_VERSION} + 1))"
|
||||
TAG="${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}-nightly.$(date +'%Y%m%d')"
|
||||
git tag -a $TAG -m "$TAG: nightly build"
|
||||
git push origin $TAG
|
||||
- name: 'Clean up nightly releases'
|
||||
uses: dev-drprasad/delete-older-releases@v0.3.3
|
||||
with:
|
||||
keep_latest: 2
|
||||
delete_tags: true
|
||||
delete_tag_pattern: nightly
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.NIGHTLY_BUILD_GH_TOKEN }}
|
||||
|
|
@ -32,6 +32,10 @@ _testmain.go
|
|||
*.bak
|
||||
|
||||
cmd/gost/gost
|
||||
gost
|
||||
snap
|
||||
|
||||
*.pem
|
||||
*.pem
|
||||
/*.yaml
|
||||
*.txt
|
||||
dist/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
# 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
|
||||
- linux_loong64
|
||||
- freebsd_386
|
||||
- freebsd_amd64
|
||||
- windows_386
|
||||
- windows_amd64
|
||||
- windows_amd64_v3
|
||||
- windows_arm64
|
||||
- android_arm64
|
||||
ldflags:
|
||||
- "-s -w -X 'main.version={{ .Tag }}'"
|
||||
|
||||
upx:
|
||||
- # UPX compression. Disabled by default (see #863): upx --best/--lzma/--brute
|
||||
# adds ~3s startup time on low-spec systems (linux/arm). Release builds can
|
||||
# opt in via GORELEASER_UPX=true environment variable.
|
||||
enabled: false
|
||||
|
||||
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:'
|
||||
|
||||
release:
|
||||
prerelease: auto
|
||||
mode: keep-existing
|
||||
|
||||
# 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
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# CLAUDE.md — gost/
|
||||
|
||||
CLI binary entry point for GOST (GO Simple Tunnel). This module compiles the `gost` command.
|
||||
|
||||
## Build & Run
|
||||
|
||||
```bash
|
||||
cd gost && go build ./cmd/gost/...
|
||||
|
||||
# Cross-compile
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" ./cmd/gost/...
|
||||
|
||||
# Build all platforms
|
||||
make all
|
||||
|
||||
# Run
|
||||
./gost -L "http://:8080" -F "socks5://:1080"
|
||||
./gost -C gost.yml
|
||||
```
|
||||
|
||||
## Structure
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `cmd/gost/main.go` | CLI entry point: flag parsing, multi-worker mode (`--` separator), program launch via `svc.Run` |
|
||||
| `cmd/gost/program.go` | Service lifecycle (`Init`/`Start`/`Stop`), config loading, API/metrics/profiling servers, SIGHUP reload |
|
||||
| `cmd/gost/register.go` | Blank imports for all built-in handlers, listeners, dialers, connectors — triggers `init()` registration |
|
||||
| `cmd/gost/version.go` | Version string (`3.3.0`) |
|
||||
|
||||
## CLI Flags
|
||||
|
||||
| Flag | Usage |
|
||||
|------|-------|
|
||||
| `-L` | Inline service definition (repeatable) |
|
||||
| `-F` | Inline chain node definition (repeatable) |
|
||||
| `-C` | Path to config file (YAML/JSON) |
|
||||
| `-D` / `-DD` | Debug / trace logging |
|
||||
| `-api` | API service address (e.g. `:8080`) |
|
||||
| `-metrics` | Prometheus metrics address |
|
||||
| `-O` | Output merged config as yaml or json, then exit |
|
||||
| `-V` | Print version and exit |
|
||||
|
||||
## Multi-Worker Mode
|
||||
|
||||
Arguments separated by ` -- ` spawn multiple worker processes via `exec.CommandContext`. Each worker runs as a child process with `_GOST_ID` set. Any worker's exit cancels all others. This is triggered in `init()` before flag parsing.
|
||||
|
||||
## Service Lifecycle
|
||||
|
||||
- `Init` → calls `parser.Init()` with all CLI/config inputs
|
||||
- `Start` → `parser.Parse()` → `loader.Load()` → `p.run()` (starts services, API, metrics, profiling)
|
||||
- `Stop` → cancels reload context, closes all services
|
||||
- SIGHUP → `reloadConfig()` re-parses and re-runs without restarting the process
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
- `github.com/go-gost/core` — interface definitions
|
||||
- `github.com/go-gost/x` — all implementations, config, registry
|
||||
- `github.com/judwhite/go-svc` — OS service framework (handles daemon/SIGHUP/SIGTERM)
|
||||
|
||||
## Registration Pattern
|
||||
|
||||
All components register via `init()` side-effects in their packages. `cmd/gost/register.go` triggers them with blank imports. When adding a new built-in handler/listener/dialer/connector, add a blank import here.
|
||||
|
||||
## Tests
|
||||
|
||||
Tests are in `tests/e2e/` — integration tests using the built binary. No unit tests. Run with `go test ./tests/e2e/...`.
|
||||
34
Dockerfile
34
Dockerfile
|
|
@ -1,25 +1,37 @@
|
|||
FROM --platform=$BUILDPLATFORM golang:1.18-alpine as builder
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.6.1 AS xx
|
||||
|
||||
# Convert TARGETPLATFORM to GOARCH format
|
||||
# https://github.com/tonistiigi/xx
|
||||
COPY --from=tonistiigi/xx:golang / /
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26-alpine3.23 AS builder
|
||||
|
||||
# UPX compression disabled by default (see #863): upx --best adds ~3s startup
|
||||
# time on low-spec systems (linux/arm). Builds can opt in by uncommenting the
|
||||
# upx install line and adding upx --best to the build command below.
|
||||
# RUN apk add --no-cache upx || echo "upx not found"
|
||||
|
||||
COPY --from=xx / /
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN apk add --no-cache musl-dev git gcc
|
||||
RUN xx-info env
|
||||
|
||||
ADD . /src
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
WORKDIR /src
|
||||
ENV XX_VERIFY_STATIC=1
|
||||
|
||||
ENV GO111MODULE=on
|
||||
WORKDIR /app
|
||||
|
||||
RUN cd cmd/gost && go env && go build
|
||||
COPY . .
|
||||
|
||||
FROM alpine:latest
|
||||
RUN cd cmd/gost && \
|
||||
xx-go build -ldflags "-s -w" && \
|
||||
xx-verify gost
|
||||
|
||||
FROM alpine:3.23
|
||||
|
||||
# add iptables/nftables for tun/tap
|
||||
RUN apk add --no-cache iptables nftables
|
||||
|
||||
WORKDIR /bin/
|
||||
|
||||
COPY --from=builder /src/cmd/gost/gost .
|
||||
COPY --from=builder /app/cmd/gost/gost .
|
||||
|
||||
ENTRYPOINT ["/bin/gost"]
|
||||
18
Makefile
18
Makefile
|
|
@ -9,6 +9,7 @@ PLATFORM_LIST = \
|
|||
darwin-arm64 \
|
||||
linux-386 \
|
||||
linux-amd64 \
|
||||
linux-amd64v3 \
|
||||
linux-armv5 \
|
||||
linux-armv6 \
|
||||
linux-armv7 \
|
||||
|
|
@ -20,12 +21,15 @@ PLATFORM_LIST = \
|
|||
linux-mips64 \
|
||||
linux-mips64le \
|
||||
linux-s390x \
|
||||
linux-riscv64 \
|
||||
freebsd-386 \
|
||||
freebsd-amd64
|
||||
|
||||
WINDOWS_ARCH_LIST = \
|
||||
windows-386 \
|
||||
windows-amd64
|
||||
windows-amd64 \
|
||||
windows-amd64v3 \
|
||||
windows-arm64
|
||||
|
||||
all: linux-amd64 darwin-amd64 darwin-arm64 windows-amd64 # Most used
|
||||
|
||||
|
|
@ -41,6 +45,9 @@ linux-386:
|
|||
linux-amd64:
|
||||
GOARCH=amd64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
|
||||
|
||||
linux-amd64v3:
|
||||
GOARCH=amd64 GOOS=linux GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
|
||||
|
||||
linux-armv5:
|
||||
GOARCH=arm GOOS=linux GOARM=5 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
|
||||
|
||||
|
|
@ -74,6 +81,9 @@ linux-mips64le:
|
|||
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)
|
||||
|
||||
|
|
@ -86,6 +96,12 @@ windows-386:
|
|||
windows-amd64:
|
||||
GOARCH=amd64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe $(GOFILES)
|
||||
|
||||
windows-amd64v3:
|
||||
GOARCH=amd64 GOOS=windows GOAMD64=v3 $(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))
|
||||
|
||||
|
|
|
|||
94
README.md
94
README.md
|
|
@ -2,37 +2,69 @@
|
|||
|
||||
### GO语言实现的安全隧道
|
||||
|
||||
[](README.md) [](README_en.md)
|
||||
|
||||
## 功能特性
|
||||
|
||||
- [x] 多端口监听
|
||||
- [x] 多级转发链
|
||||
- [x] 多协议支持
|
||||
- [x] TCP/UDP端口转发
|
||||
- [x] TCP/UDP透明代理
|
||||
- [x] DNS解析和代理
|
||||
- [x] TUN/TAP设备
|
||||
- [x] 负载均衡
|
||||
- [x] 路由控制
|
||||
- [x] 动态配置
|
||||
- [x] Prometheus监控指标
|
||||
- [x] Web API
|
||||
- [ ] Web UI
|
||||
- [x] [多端口监听](https://gost.run/getting-started/quick-start/)
|
||||
- [x] [多级转发链](https://gost.run/concepts/chain/)
|
||||
- [x] [多协议支持](https://gost.run/tutorials/protocols/overview/)
|
||||
- [x] [TCP/UDP端口转发](https://gost.run/tutorials/port-forwarding/)
|
||||
- [x] [反向代理](https://gost.run/tutorials/reverse-proxy/)和[隧道](https://gost.run/tutorials/reverse-proxy-tunnel/)
|
||||
- [x] [TCP/UDP透明代理](https://gost.run/tutorials/redirect/)
|
||||
- [x] DNS[解析](https://gost.run/concepts/resolver/)和[代理](https://gost.run/tutorials/dns/)
|
||||
- [x] [TUN/TAP设备](https://gost.run/tutorials/tuntap/)与[TUN2SOCKS](https://gost.run/tutorials/tungo/)
|
||||
- [x] [负载均衡](https://gost.run/concepts/selector/)
|
||||
- [x] [路由控制](https://gost.run/concepts/bypass/)
|
||||
- [x] [准入控制](https://gost.run/concepts/admission/)
|
||||
- [x] [限速限流](https://gost.run/concepts/limiter/)
|
||||
- [x] [插件系统](https://gost.run/concepts/plugin/)
|
||||
- [x] [Prometheus监控指标](https://gost.run/tutorials/metrics/)
|
||||
- [x] [动态配置](https://gost.run/tutorials/api/config/)
|
||||
- [x] [Web API](https://gost.run/tutorials/api/overview/)
|
||||
- [x] [GUI](https://github.com/go-gost/gostctl)/[WebUI](https://github.com/go-gost/gost-ui)
|
||||
|
||||
Wiki站点:[https://latest.gost.run](https://latest.gost.run)
|
||||
## 概览
|
||||
|
||||
Telegram讨论群:[https://t.me/gogost](https://t.me/gogost)
|
||||

|
||||
|
||||
Google讨论组:[https://groups.google.com/d/forum/go-gost](https://groups.google.com/d/forum/go-gost)
|
||||
GOST作为隧道有三种主要使用方式。
|
||||
|
||||
旧版入口:[v2.gost.run](https://v2.gost.run)
|
||||
### 正向代理
|
||||
|
||||
作为代理服务访问网络,可以组合使用多种协议组成转发链进行转发。
|
||||
|
||||

|
||||
|
||||
### 端口转发
|
||||
|
||||
将一个服务的端口映射到另外一个服务的端口,同样可以组合使用多种协议组成转发链进行转发。
|
||||
|
||||

|
||||
|
||||
### 反向代理
|
||||
|
||||
利用隧道和内网穿透将内网服务暴露到公网访问。
|
||||
|
||||

|
||||
|
||||
## 下载安装
|
||||
|
||||
|
||||
### 二进制文件
|
||||
|
||||
[https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
||||
|
||||
### 安装脚本
|
||||
|
||||
```bash
|
||||
# 安装最新版本 [https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
||||
bash <(curl -fsSL https://github.com/go-gost/gost/raw/master/install.sh) --install
|
||||
```
|
||||
```bash
|
||||
# 选择要安装的版本
|
||||
bash <(curl -fsSL https://github.com/go-gost/gost/raw/master/install.sh)
|
||||
```
|
||||
|
||||
### 源码编译
|
||||
|
||||
```
|
||||
|
|
@ -44,9 +76,31 @@ go build
|
|||
### Docker
|
||||
|
||||
```
|
||||
docker pull gogost/gost
|
||||
docker run --rm gogost/gost -V
|
||||
```
|
||||
|
||||
## 工具
|
||||
|
||||
### GUI
|
||||
|
||||
[go-gost/gostctl](https://github.com/go-gost/gostctl)
|
||||
|
||||
### WebUI
|
||||
|
||||
[go-gost/gost-ui](https://github.com/go-gost/gost-ui)
|
||||
|
||||
### Shadowsocks Android插件
|
||||
|
||||
[xausky/ShadowsocksGostPlugin](https://github.com/xausky/ShadowsocksGostPlugin)
|
||||
[hamid-nazari/ShadowsocksGostPlugin](https://github.com/hamid-nazari/ShadowsocksGostPlugin)
|
||||
|
||||
## 帮助与支持
|
||||
|
||||
Wiki站点:[https://gost.run](https://gost.run)
|
||||
|
||||
YouTube: [https://www.youtube.com/@gost-tunnel](https://www.youtube.com/@gost-tunnel)
|
||||
|
||||
Telegram:[https://t.me/gogost](https://t.me/gogost)
|
||||
|
||||
Google讨论组:[https://groups.google.com/d/forum/go-gost](https://groups.google.com/d/forum/go-gost)
|
||||
|
||||
旧版入口:[v2.gost.run](https://v2.gost.run)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
# GO Simple Tunnel
|
||||
|
||||
### A simple security tunnel written in golang
|
||||
|
||||
[](README_en.md) [](README.md)
|
||||
|
||||
## Features
|
||||
|
||||
- [x] [Listening on multiple ports](https://gost.run/en/getting-started/quick-start/)
|
||||
- [x] [Multi-level forwarding chain](https://gost.run/en/concepts/chain/)
|
||||
- [x] Rich protocol
|
||||
- [x] [TCP/UDP port forwarding](https://gost.run/en/tutorials/port-forwarding/)
|
||||
- [x] [Reverse Proxy](https://gost.run/en/tutorials/reverse-proxy/) and [Tunnel](https://gost.run/en/tutorials/reverse-proxy-tunnel/)
|
||||
- [x] [TCP/UDP transparent proxy](https://gost.run/en/tutorials/redirect/)
|
||||
- [x] DNS [resolver](https://gost.run/en/concepts/resolver/) and [proxy](https://gost.run/en/tutorials/dns/)
|
||||
- [x] [TUN/TAP device](https://gost.run/en/tutorials/tuntap/) and [TUN2SOCKS](https://gost.run/en/tutorials/tungo/)
|
||||
- [x] [Load balancing](https://gost.run/en/concepts/selector/)
|
||||
- [x] [Routing control](https://gost.run/en/concepts/bypass/)
|
||||
- [x] [Admission control](https://gost.run/en/concepts/limiter/)
|
||||
- [x] [Bandwidth/Rate Limiter](https://gost.run/en/concepts/limiter/)
|
||||
- [x] [Plugin System](https://gost.run/en/concepts/plugin/)
|
||||
- [x] [Prometheus metrics](https://gost.run/en/tutorials/metrics/)
|
||||
- [x] [Dynamic configuration](https://gost.run/en/tutorials/api/config/)
|
||||
- [x] [Web API](https://gost.run/en/tutorials/api/overview/)
|
||||
- [x] [GUI](https://github.com/go-gost/gostctl)/[WebUI](https://github.com/go-gost/gost-ui)
|
||||
|
||||
## Overview
|
||||
|
||||

|
||||
|
||||
There are three main ways to use GOST as a tunnel.
|
||||
|
||||
### Proxy
|
||||
|
||||
As a proxy service to access the network, multiple protocols can be used in combination to form a forwarding chain for traffic forwarding.
|
||||
|
||||

|
||||
|
||||
### Port Forwarding
|
||||
|
||||
Mapping the port of one service to the port of another service, you can also use a combination of multiple protocols to form a forwarding chain for traffic forwarding.
|
||||
|
||||

|
||||
|
||||
### Reverse Proxy
|
||||
|
||||
Use tunnel and intranet penetration to expose local services behind NAT or firewall to public network for access.
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
### Binary files
|
||||
|
||||
[https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
||||
|
||||
### install script
|
||||
|
||||
```bash
|
||||
# install latest from [https://github.com/go-gost/gost/releases](https://github.com/go-gost/gost/releases)
|
||||
bash <(curl -fsSL https://github.com/go-gost/gost/raw/master/install.sh) --install
|
||||
```
|
||||
```bash
|
||||
# select version for install
|
||||
bash <(curl -fsSL https://github.com/go-gost/gost/raw/master/install.sh)
|
||||
```
|
||||
|
||||
### From source
|
||||
|
||||
```
|
||||
git clone https://github.com/go-gost/gost.git
|
||||
cd gost/cmd/gost
|
||||
go build
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```
|
||||
docker run --rm gogost/gost -V
|
||||
```
|
||||
|
||||
## Tools
|
||||
|
||||
### GUI
|
||||
|
||||
[go-gost/gostctl](https://github.com/go-gost/gostctl)
|
||||
|
||||
### WebUI
|
||||
|
||||
[go-gost/gost-ui](https://github.com/go-gost/gost-ui)
|
||||
|
||||
### Shadowsocks Android
|
||||
|
||||
[hamid-nazari/ShadowsocksGostPlugin](https://github.com/hamid-nazari/ShadowsocksGostPlugin)
|
||||
|
||||
## Support
|
||||
|
||||
Wiki: [https://gost.run](https://gost.run/en/)
|
||||
|
||||
YouTube: [https://www.youtube.com/@gost-tunnel](https://www.youtube.com/@gost-tunnel)
|
||||
|
||||
Telegram: [https://t.me/gogost](https://t.me/gogost)
|
||||
|
||||
Google group: [https://groups.google.com/d/forum/go-gost](https://groups.google.com/d/forum/go-gost)
|
||||
|
||||
Legacy version: [v2.gost.run](https://v2.gost.run/en/)
|
||||
553
cmd/gost/cmd.go
553
cmd/gost/cmd.go
|
|
@ -1,553 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/metadata"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidCmd = errors.New("invalid cmd")
|
||||
ErrInvalidNode = errors.New("invalid node")
|
||||
)
|
||||
|
||||
type stringList []string
|
||||
|
||||
func (l *stringList) String() string {
|
||||
return fmt.Sprintf("%s", *l)
|
||||
}
|
||||
func (l *stringList) Set(value string) error {
|
||||
*l = append(*l, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
|
||||
cfg := &config.Config{}
|
||||
|
||||
if v := os.Getenv("GOST_PROFILING"); v != "" {
|
||||
cfg.Profiling = &config.ProfilingConfig{
|
||||
Addr: v,
|
||||
}
|
||||
}
|
||||
if v := os.Getenv("GOST_METRICS"); v != "" {
|
||||
cfg.Metrics = &config.MetricsConfig{
|
||||
Addr: v,
|
||||
}
|
||||
}
|
||||
|
||||
if v := os.Getenv("GOST_LOGGER_LEVEL"); v != "" {
|
||||
cfg.Log = &config.LogConfig{
|
||||
Level: v,
|
||||
}
|
||||
}
|
||||
|
||||
if v := os.Getenv("GOST_API"); v != "" {
|
||||
cfg.API = &config.APIConfig{
|
||||
Addr: v,
|
||||
}
|
||||
}
|
||||
|
||||
var chain *config.ChainConfig
|
||||
if len(nodes) > 0 {
|
||||
chain = &config.ChainConfig{
|
||||
Name: "chain-0",
|
||||
}
|
||||
cfg.Chains = append(cfg.Chains, chain)
|
||||
}
|
||||
|
||||
for i, node := range nodes {
|
||||
url, err := normCmd(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeConfig, err := buildNodeConfig(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeConfig.Name = "node-0"
|
||||
|
||||
var nodes []*config.NodeConfig
|
||||
for _, host := range strings.Split(nodeConfig.Addr, ",") {
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
nodeCfg := &config.NodeConfig{}
|
||||
*nodeCfg = *nodeConfig
|
||||
nodeCfg.Name = fmt.Sprintf("node-%d", len(nodes))
|
||||
nodeCfg.Addr = host
|
||||
nodes = append(nodes, nodeCfg)
|
||||
}
|
||||
|
||||
mc := nodeConfig.Connector.Metadata
|
||||
md := metadata.NewMetadata(mc)
|
||||
|
||||
hopConfig := &config.HopConfig{
|
||||
Name: fmt.Sprintf("hop-%d", i),
|
||||
Selector: parseSelector(mc),
|
||||
Nodes: nodes,
|
||||
}
|
||||
|
||||
if v := metadata.GetString(md, "bypass"); v != "" {
|
||||
bypassCfg := &config.BypassConfig{
|
||||
Name: fmt.Sprintf("bypass-%d", len(cfg.Bypasses)),
|
||||
}
|
||||
if v[0] == '~' {
|
||||
bypassCfg.Reverse = true
|
||||
v = v[1:]
|
||||
}
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
bypassCfg.Matchers = append(bypassCfg.Matchers, s)
|
||||
}
|
||||
hopConfig.Bypass = bypassCfg.Name
|
||||
cfg.Bypasses = append(cfg.Bypasses, bypassCfg)
|
||||
delete(mc, "bypass")
|
||||
}
|
||||
if v := metadata.GetString(md, "resolver"); v != "" {
|
||||
resolverCfg := &config.ResolverConfig{
|
||||
Name: fmt.Sprintf("resolver-%d", len(cfg.Resolvers)),
|
||||
}
|
||||
for _, rs := range strings.Split(v, ",") {
|
||||
if rs == "" {
|
||||
continue
|
||||
}
|
||||
resolverCfg.Nameservers = append(
|
||||
resolverCfg.Nameservers,
|
||||
&config.NameserverConfig{
|
||||
Addr: rs,
|
||||
},
|
||||
)
|
||||
}
|
||||
hopConfig.Resolver = resolverCfg.Name
|
||||
cfg.Resolvers = append(cfg.Resolvers, resolverCfg)
|
||||
delete(mc, "resolver")
|
||||
}
|
||||
if v := metadata.GetString(md, "hosts"); v != "" {
|
||||
hostsCfg := &config.HostsConfig{
|
||||
Name: fmt.Sprintf("hosts-%d", len(cfg.Hosts)),
|
||||
}
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
ss := strings.SplitN(s, ":", 2)
|
||||
if len(ss) != 2 {
|
||||
continue
|
||||
}
|
||||
hostsCfg.Mappings = append(
|
||||
hostsCfg.Mappings,
|
||||
&config.HostMappingConfig{
|
||||
Hostname: ss[0],
|
||||
IP: ss[1],
|
||||
},
|
||||
)
|
||||
}
|
||||
hopConfig.Hosts = hostsCfg.Name
|
||||
cfg.Hosts = append(cfg.Hosts, hostsCfg)
|
||||
delete(mc, "hosts")
|
||||
}
|
||||
|
||||
if v := metadata.GetString(md, "interface"); v != "" {
|
||||
hopConfig.Interface = v
|
||||
delete(mc, "interface")
|
||||
}
|
||||
if v := metadata.GetInt(md, "so_mark"); v > 0 {
|
||||
hopConfig.SockOpts = &config.SockOptsConfig{
|
||||
Mark: v,
|
||||
}
|
||||
delete(mc, "so_mark")
|
||||
}
|
||||
|
||||
chain.Hops = append(chain.Hops, hopConfig)
|
||||
}
|
||||
|
||||
for i, svc := range services {
|
||||
url, err := normCmd(svc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
service, err := buildServiceConfig(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
service.Name = fmt.Sprintf("service-%d", i)
|
||||
if chain != nil {
|
||||
if service.Listener.Type == "rtcp" || service.Listener.Type == "rudp" {
|
||||
service.Listener.Chain = chain.Name
|
||||
} else {
|
||||
service.Handler.Chain = chain.Name
|
||||
}
|
||||
}
|
||||
cfg.Services = append(cfg.Services, service)
|
||||
|
||||
mh := service.Handler.Metadata
|
||||
md := metadata.NewMetadata(mh)
|
||||
if v := metadata.GetInt(md, "retries"); v > 0 {
|
||||
service.Handler.Retries = v
|
||||
delete(mh, "retries")
|
||||
}
|
||||
if v := metadata.GetString(md, "admission"); v != "" {
|
||||
admCfg := &config.AdmissionConfig{
|
||||
Name: fmt.Sprintf("admission-%d", len(cfg.Admissions)),
|
||||
}
|
||||
if v[0] == '~' {
|
||||
admCfg.Reverse = true
|
||||
v = v[1:]
|
||||
}
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
admCfg.Matchers = append(admCfg.Matchers, s)
|
||||
}
|
||||
service.Admission = admCfg.Name
|
||||
cfg.Admissions = append(cfg.Admissions, admCfg)
|
||||
delete(mh, "admission")
|
||||
}
|
||||
if v := metadata.GetString(md, "bypass"); v != "" {
|
||||
bypassCfg := &config.BypassConfig{
|
||||
Name: fmt.Sprintf("bypass-%d", len(cfg.Bypasses)),
|
||||
}
|
||||
if v[0] == '~' {
|
||||
bypassCfg.Reverse = true
|
||||
v = v[1:]
|
||||
}
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
bypassCfg.Matchers = append(bypassCfg.Matchers, s)
|
||||
}
|
||||
service.Bypass = bypassCfg.Name
|
||||
cfg.Bypasses = append(cfg.Bypasses, bypassCfg)
|
||||
delete(mh, "bypass")
|
||||
}
|
||||
if v := metadata.GetString(md, "resolver"); v != "" {
|
||||
resolverCfg := &config.ResolverConfig{
|
||||
Name: fmt.Sprintf("resolver-%d", len(cfg.Resolvers)),
|
||||
}
|
||||
for _, rs := range strings.Split(v, ",") {
|
||||
if rs == "" {
|
||||
continue
|
||||
}
|
||||
resolverCfg.Nameservers = append(
|
||||
resolverCfg.Nameservers,
|
||||
&config.NameserverConfig{
|
||||
Addr: rs,
|
||||
},
|
||||
)
|
||||
}
|
||||
service.Resolver = resolverCfg.Name
|
||||
cfg.Resolvers = append(cfg.Resolvers, resolverCfg)
|
||||
delete(mh, "resolver")
|
||||
}
|
||||
if v := metadata.GetString(md, "hosts"); v != "" {
|
||||
hostsCfg := &config.HostsConfig{
|
||||
Name: fmt.Sprintf("hosts-%d", len(cfg.Hosts)),
|
||||
}
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
ss := strings.SplitN(s, ":", 2)
|
||||
if len(ss) != 2 {
|
||||
continue
|
||||
}
|
||||
hostsCfg.Mappings = append(
|
||||
hostsCfg.Mappings,
|
||||
&config.HostMappingConfig{
|
||||
Hostname: ss[0],
|
||||
IP: ss[1],
|
||||
},
|
||||
)
|
||||
}
|
||||
service.Hosts = hostsCfg.Name
|
||||
cfg.Hosts = append(cfg.Hosts, hostsCfg)
|
||||
delete(mh, "hosts")
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
|
||||
var handler, listener string
|
||||
schemes := strings.Split(url.Scheme, "+")
|
||||
if len(schemes) == 1 {
|
||||
handler = schemes[0]
|
||||
listener = schemes[0]
|
||||
}
|
||||
if len(schemes) == 2 {
|
||||
handler = schemes[0]
|
||||
listener = schemes[1]
|
||||
}
|
||||
|
||||
svc := &config.ServiceConfig{
|
||||
Addr: url.Host,
|
||||
}
|
||||
|
||||
if h := registry.HandlerRegistry().Get(handler); h == nil {
|
||||
handler = "auto"
|
||||
}
|
||||
if ln := registry.ListenerRegistry().Get(listener); ln == nil {
|
||||
listener = "tcp"
|
||||
if handler == "ssu" {
|
||||
listener = "udp"
|
||||
}
|
||||
}
|
||||
|
||||
// forward mode
|
||||
if remotes := strings.Trim(url.EscapedPath(), "/"); remotes != "" {
|
||||
svc.Forwarder = &config.ForwarderConfig{
|
||||
Targets: strings.Split(remotes, ","),
|
||||
}
|
||||
if handler != "relay" {
|
||||
if listener == "tcp" || listener == "udp" ||
|
||||
listener == "rtcp" || listener == "rudp" ||
|
||||
listener == "tun" || listener == "tap" {
|
||||
handler = listener
|
||||
} else {
|
||||
handler = "forward"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var auth *config.AuthConfig
|
||||
if url.User != nil {
|
||||
auth = &config.AuthConfig{
|
||||
Username: url.User.Username(),
|
||||
}
|
||||
auth.Password, _ = url.User.Password()
|
||||
}
|
||||
|
||||
m := map[string]any{}
|
||||
for k, v := range url.Query() {
|
||||
if len(v) > 0 {
|
||||
m[k] = v[0]
|
||||
}
|
||||
}
|
||||
md := metadata.NewMetadata(m)
|
||||
|
||||
if sa := metadata.GetString(md, "auth"); sa != "" {
|
||||
au, err := parseAuthFromCmd(sa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth = au
|
||||
}
|
||||
delete(m, "auth")
|
||||
|
||||
tlsConfig := &config.TLSConfig{
|
||||
CertFile: metadata.GetString(md, "certFile"),
|
||||
KeyFile: metadata.GetString(md, "keyFile"),
|
||||
CAFile: metadata.GetString(md, "caFile"),
|
||||
}
|
||||
delete(m, "certFile")
|
||||
delete(m, "keyFile")
|
||||
delete(m, "caFile")
|
||||
|
||||
if tlsConfig.CertFile == "" {
|
||||
tlsConfig = nil
|
||||
}
|
||||
|
||||
if v := metadata.GetString(md, "dns"); v != "" {
|
||||
md.Set("dns", strings.Split(v, ","))
|
||||
}
|
||||
if v := metadata.GetString(md, "interface"); v != "" {
|
||||
svc.Interface = v
|
||||
delete(m, "interface")
|
||||
}
|
||||
if v := metadata.GetInt(md, "so_mark"); v > 0 {
|
||||
svc.SockOpts = &config.SockOptsConfig{
|
||||
Mark: v,
|
||||
}
|
||||
delete(m, "so_mark")
|
||||
}
|
||||
|
||||
if svc.Forwarder != nil {
|
||||
svc.Forwarder.Selector = parseSelector(m)
|
||||
}
|
||||
|
||||
svc.Handler = &config.HandlerConfig{
|
||||
Type: handler,
|
||||
Auth: auth,
|
||||
Metadata: m,
|
||||
}
|
||||
svc.Listener = &config.ListenerConfig{
|
||||
Type: listener,
|
||||
TLS: tlsConfig,
|
||||
Metadata: m,
|
||||
}
|
||||
|
||||
if svc.Listener.Type == "ssh" || svc.Listener.Type == "sshd" {
|
||||
svc.Handler.Auth = nil
|
||||
svc.Listener.Auth = auth
|
||||
}
|
||||
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
||||
var connector, dialer string
|
||||
schemes := strings.Split(url.Scheme, "+")
|
||||
if len(schemes) == 1 {
|
||||
connector = schemes[0]
|
||||
dialer = schemes[0]
|
||||
}
|
||||
if len(schemes) == 2 {
|
||||
connector = schemes[0]
|
||||
dialer = schemes[1]
|
||||
}
|
||||
|
||||
node := &config.NodeConfig{
|
||||
Addr: url.Host,
|
||||
}
|
||||
|
||||
if c := registry.ConnectorRegistry().Get(connector); c == nil {
|
||||
connector = "http"
|
||||
}
|
||||
if d := registry.DialerRegistry().Get(dialer); d == nil {
|
||||
dialer = "tcp"
|
||||
if connector == "ssu" {
|
||||
dialer = "udp"
|
||||
}
|
||||
}
|
||||
|
||||
var auth *config.AuthConfig
|
||||
if url.User != nil {
|
||||
auth = &config.AuthConfig{
|
||||
Username: url.User.Username(),
|
||||
}
|
||||
auth.Password, _ = url.User.Password()
|
||||
}
|
||||
|
||||
m := map[string]any{}
|
||||
for k, v := range url.Query() {
|
||||
if len(v) > 0 {
|
||||
m[k] = v[0]
|
||||
}
|
||||
}
|
||||
md := metadata.NewMetadata(m)
|
||||
|
||||
if sauth := metadata.GetString(md, "auth"); sauth != "" && auth == nil {
|
||||
au, err := parseAuthFromCmd(sauth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth = au
|
||||
}
|
||||
delete(m, "auth")
|
||||
|
||||
tlsConfig := &config.TLSConfig{
|
||||
CertFile: metadata.GetString(md, "certFile"),
|
||||
KeyFile: metadata.GetString(md, "keyFile"),
|
||||
CAFile: metadata.GetString(md, "caFile"),
|
||||
Secure: metadata.GetBool(md, "secure"),
|
||||
ServerName: metadata.GetString(md, "serverName"),
|
||||
}
|
||||
if tlsConfig.ServerName == "" {
|
||||
tlsConfig.ServerName = url.Hostname()
|
||||
}
|
||||
delete(m, "certFile")
|
||||
delete(m, "keyFile")
|
||||
delete(m, "caFile")
|
||||
delete(m, "secure")
|
||||
delete(m, "serverName")
|
||||
|
||||
if !tlsConfig.Secure && tlsConfig.CertFile == "" && tlsConfig.CAFile == "" {
|
||||
tlsConfig = nil
|
||||
}
|
||||
|
||||
node.Connector = &config.ConnectorConfig{
|
||||
Type: connector,
|
||||
Auth: auth,
|
||||
Metadata: m,
|
||||
}
|
||||
node.Dialer = &config.DialerConfig{
|
||||
Type: dialer,
|
||||
TLS: tlsConfig,
|
||||
Metadata: m,
|
||||
}
|
||||
|
||||
if node.Dialer.Type == "ssh" || node.Dialer.Type == "sshd" {
|
||||
node.Connector.Auth = nil
|
||||
node.Dialer.Auth = auth
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func normCmd(s string) (*url.URL, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return nil, ErrInvalidCmd
|
||||
}
|
||||
|
||||
if s[0] == ':' || !strings.Contains(s, "://") {
|
||||
s = "auto://" + s
|
||||
}
|
||||
|
||||
url, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if url.Scheme == "https" {
|
||||
url.Scheme = "http+tls"
|
||||
}
|
||||
|
||||
return url, nil
|
||||
}
|
||||
|
||||
func parseAuthFromCmd(sa string) (*config.AuthConfig, error) {
|
||||
v, err := base64.StdEncoding.DecodeString(sa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs := string(v)
|
||||
n := strings.IndexByte(cs, ':')
|
||||
if n < 0 {
|
||||
return &config.AuthConfig{
|
||||
Username: cs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &config.AuthConfig{
|
||||
Username: cs[:n],
|
||||
Password: cs[n+1:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseSelector(m map[string]any) *config.SelectorConfig {
|
||||
md := metadata.NewMetadata(m)
|
||||
strategy := metadata.GetString(md, "strategy")
|
||||
maxFails := metadata.GetInt(md, "maxFails")
|
||||
failTimeout := metadata.GetDuration(md, "failTimeout")
|
||||
if strategy == "" && maxFails <= 0 && failTimeout <= 0 {
|
||||
return nil
|
||||
}
|
||||
if strategy == "" {
|
||||
strategy = "round"
|
||||
}
|
||||
if maxFails <= 0 {
|
||||
maxFails = 1
|
||||
}
|
||||
if failTimeout <= 0 {
|
||||
failTimeout = time.Second
|
||||
}
|
||||
|
||||
delete(m, "strategy")
|
||||
delete(m, "maxFails")
|
||||
delete(m, "failTimeout")
|
||||
|
||||
return &config.SelectorConfig{
|
||||
Strategy: strategy,
|
||||
MaxFails: maxFails,
|
||||
FailTimeout: failTimeout,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/service"
|
||||
"github.com/go-gost/x/api"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
metrics "github.com/go-gost/x/metrics/service"
|
||||
"github.com/go-gost/x/registry"
|
||||
)
|
||||
|
||||
func buildService(cfg *config.Config) (services []service.Service) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, autherCfg := range cfg.Authers {
|
||||
if auther := parsing.ParseAuther(autherCfg); auther != nil {
|
||||
if err := registry.AutherRegistry().Register(autherCfg.Name, auther); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, admissionCfg := range cfg.Admissions {
|
||||
if adm := parsing.ParseAdmission(admissionCfg); adm != nil {
|
||||
if err := registry.AdmissionRegistry().Register(admissionCfg.Name, adm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, bypassCfg := range cfg.Bypasses {
|
||||
if bp := parsing.ParseBypass(bypassCfg); bp != nil {
|
||||
if err := registry.BypassRegistry().Register(bypassCfg.Name, bp); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, resolverCfg := range cfg.Resolvers {
|
||||
r, err := parsing.ParseResolver(resolverCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if r != nil {
|
||||
if err := registry.ResolverRegistry().Register(resolverCfg.Name, r); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, hostsCfg := range cfg.Hosts {
|
||||
if h := parsing.ParseHosts(hostsCfg); h != nil {
|
||||
if err := registry.HostsRegistry().Register(hostsCfg.Name, h); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, recorderCfg := range cfg.Recorders {
|
||||
if h := parsing.ParseRecorder(recorderCfg); h != nil {
|
||||
if err := registry.RecorderRegistry().Register(recorderCfg.Name, h); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, chainCfg := range cfg.Chains {
|
||||
c, err := parsing.ParseChain(chainCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if c != nil {
|
||||
if err := registry.ChainRegistry().Register(chainCfg.Name, c); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, svcCfg := range cfg.Services {
|
||||
svc, err := parsing.ParseService(svcCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if svc != nil {
|
||||
if err := registry.ServiceRegistry().Register(svcCfg.Name, svc); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
services = append(services, svc)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func logFromConfig(cfg *config.LogConfig) logger.Logger {
|
||||
if cfg == nil {
|
||||
cfg = &config.LogConfig{}
|
||||
}
|
||||
opts := []xlogger.LoggerOption{
|
||||
xlogger.FormatLoggerOption(logger.LogFormat(cfg.Format)),
|
||||
xlogger.LevelLoggerOption(logger.LogLevel(cfg.Level)),
|
||||
}
|
||||
|
||||
var out io.Writer = os.Stderr
|
||||
switch cfg.Output {
|
||||
case "none", "null":
|
||||
return xlogger.Nop()
|
||||
case "stdout":
|
||||
out = os.Stdout
|
||||
case "stderr", "":
|
||||
out = os.Stderr
|
||||
default:
|
||||
f, err := os.OpenFile(cfg.Output, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Warnf("log", err)
|
||||
} else {
|
||||
out = f
|
||||
}
|
||||
}
|
||||
opts = append(opts, xlogger.OutputLoggerOption(out))
|
||||
|
||||
return xlogger.NewLogger(opts...)
|
||||
}
|
||||
|
||||
func buildAPIService(cfg *config.APIConfig) (service.Service, error) {
|
||||
auther := parsing.ParseAutherFromAuth(cfg.Auth)
|
||||
if cfg.Auther != "" {
|
||||
auther = registry.AutherRegistry().Get(cfg.Auther)
|
||||
}
|
||||
return api.NewService(
|
||||
cfg.Addr,
|
||||
api.PathPrefixOption(cfg.PathPrefix),
|
||||
api.AccessLogOption(cfg.AccessLog),
|
||||
api.AutherOption(auther),
|
||||
)
|
||||
}
|
||||
|
||||
func buildMetricsService(cfg *config.MetricsConfig) (service.Service, error) {
|
||||
return metrics.NewService(
|
||||
cfg.Addr,
|
||||
metrics.PathOption(cfg.Path),
|
||||
)
|
||||
}
|
||||
171
cmd/gost/main.go
171
cmd/gost/main.go
|
|
@ -1,42 +1,97 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"log"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/metrics"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/parsing"
|
||||
xlogger "github.com/go-gost/x/logger"
|
||||
xmetrics "github.com/go-gost/x/metrics"
|
||||
"github.com/judwhite/go-svc"
|
||||
)
|
||||
|
||||
var (
|
||||
log logger.Logger
|
||||
type stringList []string
|
||||
|
||||
func (l *stringList) String() string {
|
||||
return fmt.Sprintf("%s", *l)
|
||||
}
|
||||
func (l *stringList) Set(value string) error {
|
||||
*l = append(*l, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
outputFormat string
|
||||
services stringList
|
||||
nodes stringList
|
||||
debug bool
|
||||
trace bool
|
||||
apiAddr string
|
||||
metricsAddr string
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile | log.Lmicroseconds)
|
||||
|
||||
args := strings.Join(os.Args[1:], " ")
|
||||
|
||||
if strings.Contains(args, " -- ") {
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
ret int
|
||||
)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
for wid, wargs := range strings.Split(" "+args+" ", " -- ") {
|
||||
wg.Add(1)
|
||||
go func(wid int, wargs string) {
|
||||
defer wg.Done()
|
||||
defer cancel()
|
||||
worker(wid, strings.Split(wargs, " "), &ctx, &ret)
|
||||
}(wid, strings.TrimSpace(wargs))
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
os.Exit(ret)
|
||||
}
|
||||
}
|
||||
|
||||
func worker(id int, args []string, ctx *context.Context, ret *int) {
|
||||
cmd := exec.CommandContext(*ctx, os.Args[0], args...)
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("_GOST_ID=%d", id))
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if cmd.ProcessState.Exited() {
|
||||
*ret = cmd.ProcessState.ExitCode()
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
var printVersion bool
|
||||
|
||||
flag.Var(&services, "L", "service list")
|
||||
flag.Var(&nodes, "F", "chain node list")
|
||||
flag.StringVar(&cfgFile, "C", "", "configure file")
|
||||
flag.StringVar(&cfgFile, "C", "", "configuration file")
|
||||
flag.BoolVar(&printVersion, "V", false, "print version")
|
||||
flag.StringVar(&outputFormat, "O", "", "output format, one of yaml|json format")
|
||||
flag.BoolVar(&debug, "D", false, "debug mode")
|
||||
flag.BoolVar(&trace, "DD", false, "trace mode")
|
||||
flag.StringVar(&apiAddr, "api", "", "api service address")
|
||||
flag.StringVar(&metricsAddr, "metrics", "", "metrics service address")
|
||||
flag.Parse()
|
||||
|
|
@ -46,105 +101,15 @@ func init() {
|
|||
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
log = xlogger.NewLogger()
|
||||
logger.SetDefault(log)
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := &config.Config{}
|
||||
var err error
|
||||
if len(services) > 0 || apiAddr != "" {
|
||||
cfg, err = buildConfigFromCmd(services, nodes)
|
||||
if debug && cfg != nil {
|
||||
if cfg.Log == nil {
|
||||
cfg.Log = &config.LogConfig{}
|
||||
}
|
||||
cfg.Log.Level = string(logger.DebugLevel)
|
||||
}
|
||||
if apiAddr != "" {
|
||||
cfg.API = &config.APIConfig{
|
||||
Addr: apiAddr,
|
||||
}
|
||||
}
|
||||
if metricsAddr != "" {
|
||||
cfg.Metrics = &config.MetricsConfig{
|
||||
Addr: metricsAddr,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if cfgFile != "" {
|
||||
err = cfg.ReadFile(cfgFile)
|
||||
} else {
|
||||
err = cfg.Load()
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log = logFromConfig(cfg.Log)
|
||||
|
||||
log := xlogger.NewLogger()
|
||||
logger.SetDefault(log)
|
||||
|
||||
if outputFormat != "" {
|
||||
if err := cfg.Write(os.Stdout, outputFormat); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Exit(0)
|
||||
p := &program{}
|
||||
|
||||
if err := svc.Run(p); err != nil {
|
||||
logger.Default().Fatal(err)
|
||||
}
|
||||
|
||||
if cfg.Profiling != nil {
|
||||
go func() {
|
||||
addr := cfg.Profiling.Addr
|
||||
if addr == "" {
|
||||
addr = ":6060"
|
||||
}
|
||||
log.Info("profiling server on ", addr)
|
||||
log.Fatal(http.ListenAndServe(addr, nil))
|
||||
}()
|
||||
}
|
||||
|
||||
if cfg.API != nil {
|
||||
s, err := buildAPIService(cfg.API)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
go func() {
|
||||
log.Info("api service on ", s.Addr())
|
||||
log.Fatal(s.Serve())
|
||||
}()
|
||||
}
|
||||
|
||||
if cfg.Metrics != nil {
|
||||
metrics.SetGlobal(xmetrics.NewMetrics())
|
||||
if cfg.Metrics.Addr != "" {
|
||||
s, err := buildMetricsService(cfg.Metrics)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
go func() {
|
||||
defer s.Close()
|
||||
log.Info("metrics service on ", s.Addr())
|
||||
log.Fatal(s.Serve())
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
parsing.BuildDefaultTLSConfig(cfg.TLS)
|
||||
|
||||
services := buildService(cfg)
|
||||
for _, svc := range services {
|
||||
svc := svc
|
||||
go func() {
|
||||
svc.Serve()
|
||||
svc.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
select {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,274 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/go-gost/core/auth"
|
||||
"github.com/go-gost/core/logger"
|
||||
"github.com/go-gost/core/service"
|
||||
api_service "github.com/go-gost/x/api/service"
|
||||
xauth "github.com/go-gost/x/auth"
|
||||
"github.com/go-gost/x/config"
|
||||
"github.com/go-gost/x/config/loader"
|
||||
auth_parser "github.com/go-gost/x/config/parsing/auth"
|
||||
"github.com/go-gost/x/config/parsing/parser"
|
||||
xmetrics "github.com/go-gost/x/metrics"
|
||||
metrics "github.com/go-gost/x/metrics/service"
|
||||
"github.com/go-gost/x/registry"
|
||||
"github.com/judwhite/go-svc"
|
||||
)
|
||||
|
||||
type program struct {
|
||||
srvApi service.Service
|
||||
srvMetrics service.Service
|
||||
srvProfiling *http.Server
|
||||
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (p *program) Init(env svc.Environment) error {
|
||||
parser.Init(parser.Args{
|
||||
CfgFile: cfgFile,
|
||||
Services: services,
|
||||
Nodes: nodes,
|
||||
Debug: debug,
|
||||
Trace: trace,
|
||||
ApiAddr: apiAddr,
|
||||
MetricsAddr: metricsAddr,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *program) Start() error {
|
||||
cfg, err := parser.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if outputFormat != "" {
|
||||
if err := cfg.Write(os.Stdout, outputFormat); err != nil {
|
||||
return err
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
config.Set(cfg)
|
||||
|
||||
if err := loader.Load(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.run(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
p.cancel = cancel
|
||||
go p.reload(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *program) run(cfg *config.Config) error {
|
||||
for _, svc := range registry.ServiceRegistry().GetAll() {
|
||||
svc := svc
|
||||
go func() {
|
||||
svc.Serve()
|
||||
}()
|
||||
}
|
||||
|
||||
if p.srvApi != nil {
|
||||
p.srvApi.Close()
|
||||
p.srvApi = nil
|
||||
}
|
||||
if cfg.API != nil {
|
||||
s, err := buildApiService(cfg.API)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.srvApi = s
|
||||
|
||||
go func() {
|
||||
defer s.Close()
|
||||
|
||||
log := logger.Default().WithFields(map[string]any{"kind": "service", "service": "@api"})
|
||||
|
||||
log.Info("listening on ", s.Addr())
|
||||
if err := s.Serve(); !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if p.srvMetrics != nil {
|
||||
p.srvMetrics.Close()
|
||||
p.srvMetrics = nil
|
||||
}
|
||||
if cfg.Metrics != nil && cfg.Metrics.Addr != "" {
|
||||
s, err := buildMetricsService(cfg.Metrics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.srvMetrics = s
|
||||
|
||||
xmetrics.Enable(true)
|
||||
|
||||
go func() {
|
||||
defer s.Close()
|
||||
|
||||
log := logger.Default().WithFields(map[string]any{"kind": "service", "service": "@metrics"})
|
||||
|
||||
log.Info("listening on ", s.Addr())
|
||||
if err := s.Serve(); !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if p.srvProfiling != nil {
|
||||
p.srvProfiling.Close()
|
||||
p.srvProfiling = nil
|
||||
}
|
||||
if cfg.Profiling != nil {
|
||||
addr := cfg.Profiling.Addr
|
||||
if addr == "" {
|
||||
addr = ":6060"
|
||||
}
|
||||
s := &http.Server{
|
||||
Addr: addr,
|
||||
}
|
||||
p.srvProfiling = s
|
||||
|
||||
go func() {
|
||||
defer s.Close()
|
||||
|
||||
log := logger.Default().WithFields(map[string]any{"kind": "service", "service": "@profiling"})
|
||||
|
||||
log.Info("listening on ", addr)
|
||||
if err := s.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *program) Stop() error {
|
||||
if p.cancel != nil {
|
||||
p.cancel()
|
||||
}
|
||||
|
||||
for name, srv := range registry.ServiceRegistry().GetAll() {
|
||||
srv.Close()
|
||||
logger.Default().Debugf("service %s shutdown", name)
|
||||
}
|
||||
|
||||
if p.srvApi != nil {
|
||||
p.srvApi.Close()
|
||||
logger.Default().Debug("service @api shutdown")
|
||||
}
|
||||
if p.srvMetrics != nil {
|
||||
p.srvMetrics.Close()
|
||||
logger.Default().Debug("service @metrics shutdown")
|
||||
}
|
||||
if p.srvProfiling != nil {
|
||||
p.srvProfiling.Close()
|
||||
logger.Default().Debug("service @profiling shutdown")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *program) reload(ctx context.Context) {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
if err := p.reloadConfig(); err != nil {
|
||||
logger.Default().Error(err)
|
||||
} else {
|
||||
logger.Default().Info("config reloaded")
|
||||
}
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *program) reloadConfig() error {
|
||||
cfg, err := parser.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Set(cfg)
|
||||
|
||||
if err := loader.Load(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.run(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildApiService(cfg *config.APIConfig) (service.Service, error) {
|
||||
var authers []auth.Authenticator
|
||||
if auther := auth_parser.ParseAutherFromAuth(cfg.Auth); auther != nil {
|
||||
authers = append(authers, auther)
|
||||
}
|
||||
if cfg.Auther != "" {
|
||||
authers = append(authers, registry.AutherRegistry().Get(cfg.Auther))
|
||||
}
|
||||
|
||||
var auther auth.Authenticator
|
||||
if len(authers) > 0 {
|
||||
auther = xauth.AuthenticatorGroup(authers...)
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
addr := cfg.Addr
|
||||
if strings.HasPrefix(addr, "unix://") {
|
||||
network = "unix"
|
||||
addr = strings.TrimPrefix(addr, "unix://")
|
||||
}
|
||||
return api_service.NewService(
|
||||
network, addr,
|
||||
api_service.PathPrefixOption(cfg.PathPrefix),
|
||||
api_service.AccessLogOption(cfg.AccessLog),
|
||||
api_service.AutherOption(auther),
|
||||
)
|
||||
}
|
||||
|
||||
func buildMetricsService(cfg *config.MetricsConfig) (service.Service, error) {
|
||||
auther := auth_parser.ParseAutherFromAuth(cfg.Auth)
|
||||
if cfg.Auther != "" {
|
||||
auther = registry.AutherRegistry().Get(cfg.Auther)
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
addr := cfg.Addr
|
||||
if strings.HasPrefix(addr, "unix://") {
|
||||
network = "unix"
|
||||
addr = strings.TrimPrefix(addr, "unix://")
|
||||
}
|
||||
return metrics.NewService(
|
||||
network, addr,
|
||||
metrics.PathOption(cfg.Path),
|
||||
metrics.AutherOption(auther),
|
||||
)
|
||||
}
|
||||
|
|
@ -2,48 +2,70 @@ package main
|
|||
|
||||
import (
|
||||
// Register connectors
|
||||
_ "github.com/go-gost/x/connector/direct"
|
||||
_ "github.com/go-gost/x/connector/forward"
|
||||
_ "github.com/go-gost/x/connector/http"
|
||||
_ "github.com/go-gost/x/connector/http2"
|
||||
_ "github.com/go-gost/x/connector/masque"
|
||||
_ "github.com/go-gost/x/connector/relay"
|
||||
_ "github.com/go-gost/x/connector/router"
|
||||
_ "github.com/go-gost/x/connector/serial"
|
||||
_ "github.com/go-gost/x/connector/sni"
|
||||
_ "github.com/go-gost/x/connector/socks/v4"
|
||||
_ "github.com/go-gost/x/connector/socks/v5"
|
||||
_ "github.com/go-gost/x/connector/ss"
|
||||
_ "github.com/go-gost/x/connector/ss/udp"
|
||||
_ "github.com/go-gost/x/connector/sshd"
|
||||
_ "github.com/go-gost/x/connector/tcp"
|
||||
_ "github.com/go-gost/x/connector/tunnel"
|
||||
_ "github.com/go-gost/x/connector/unix"
|
||||
_ "github.com/go-gost/x/connector/masque"
|
||||
|
||||
// Register dialers
|
||||
_ "github.com/go-gost/x/dialer/direct"
|
||||
_ "github.com/go-gost/x/dialer/dtls"
|
||||
_ "github.com/go-gost/x/dialer/ftcp"
|
||||
_ "github.com/go-gost/x/dialer/grpc"
|
||||
_ "github.com/go-gost/x/dialer/http2"
|
||||
_ "github.com/go-gost/x/dialer/http2/h2"
|
||||
_ "github.com/go-gost/x/dialer/http3"
|
||||
_ "github.com/go-gost/x/dialer/http3/masque"
|
||||
_ "github.com/go-gost/x/dialer/http3/wt"
|
||||
_ "github.com/go-gost/x/dialer/icmp"
|
||||
_ "github.com/go-gost/x/dialer/kcp"
|
||||
_ "github.com/go-gost/x/dialer/mtcp"
|
||||
_ "github.com/go-gost/x/dialer/mtls"
|
||||
_ "github.com/go-gost/x/dialer/mws"
|
||||
_ "github.com/go-gost/x/dialer/obfs/http"
|
||||
_ "github.com/go-gost/x/dialer/obfs/tls"
|
||||
_ "github.com/go-gost/x/dialer/pht"
|
||||
_ "github.com/go-gost/x/dialer/quic"
|
||||
_ "github.com/go-gost/x/dialer/serial"
|
||||
_ "github.com/go-gost/x/dialer/ssh"
|
||||
_ "github.com/go-gost/x/dialer/sshd"
|
||||
_ "github.com/go-gost/x/dialer/tcp"
|
||||
_ "github.com/go-gost/x/dialer/tls"
|
||||
_ "github.com/go-gost/x/dialer/udp"
|
||||
_ "github.com/go-gost/x/dialer/unix"
|
||||
_ "github.com/go-gost/x/dialer/ws"
|
||||
|
||||
// Register handlers
|
||||
_ "github.com/go-gost/x/handler/api"
|
||||
_ "github.com/go-gost/x/handler/auto"
|
||||
_ "github.com/go-gost/x/handler/dns"
|
||||
_ "github.com/go-gost/x/handler/file"
|
||||
_ "github.com/go-gost/x/handler/forward/local"
|
||||
_ "github.com/go-gost/x/handler/forward/remote"
|
||||
_ "github.com/go-gost/x/handler/http"
|
||||
_ "github.com/go-gost/x/handler/http2"
|
||||
_ "github.com/go-gost/x/handler/http3"
|
||||
_ "github.com/go-gost/x/handler/masque"
|
||||
_ "github.com/go-gost/x/handler/metrics"
|
||||
_ "github.com/go-gost/x/handler/redirect/tcp"
|
||||
_ "github.com/go-gost/x/handler/redirect/udp"
|
||||
_ "github.com/go-gost/x/handler/relay"
|
||||
_ "github.com/go-gost/x/handler/router"
|
||||
_ "github.com/go-gost/x/handler/serial"
|
||||
_ "github.com/go-gost/x/handler/sni"
|
||||
_ "github.com/go-gost/x/handler/socks/v4"
|
||||
_ "github.com/go-gost/x/handler/socks/v5"
|
||||
|
|
@ -52,16 +74,23 @@ import (
|
|||
_ "github.com/go-gost/x/handler/sshd"
|
||||
_ "github.com/go-gost/x/handler/tap"
|
||||
_ "github.com/go-gost/x/handler/tun"
|
||||
_ "github.com/go-gost/x/handler/tungo"
|
||||
_ "github.com/go-gost/x/handler/tunnel"
|
||||
_ "github.com/go-gost/x/handler/unix"
|
||||
|
||||
// Register listeners
|
||||
_ "github.com/go-gost/x/listener/dns"
|
||||
_ "github.com/go-gost/x/listener/dtls"
|
||||
_ "github.com/go-gost/x/listener/ftcp"
|
||||
_ "github.com/go-gost/x/listener/grpc"
|
||||
_ "github.com/go-gost/x/listener/http2"
|
||||
_ "github.com/go-gost/x/listener/http2/h2"
|
||||
_ "github.com/go-gost/x/listener/http3"
|
||||
_ "github.com/go-gost/x/listener/http3/h3"
|
||||
_ "github.com/go-gost/x/listener/http3/wt"
|
||||
_ "github.com/go-gost/x/listener/icmp"
|
||||
_ "github.com/go-gost/x/listener/kcp"
|
||||
_ "github.com/go-gost/x/listener/mtcp"
|
||||
_ "github.com/go-gost/x/listener/mtls"
|
||||
_ "github.com/go-gost/x/listener/mws"
|
||||
_ "github.com/go-gost/x/listener/obfs/http"
|
||||
|
|
@ -72,12 +101,15 @@ import (
|
|||
_ "github.com/go-gost/x/listener/redirect/udp"
|
||||
_ "github.com/go-gost/x/listener/rtcp"
|
||||
_ "github.com/go-gost/x/listener/rudp"
|
||||
_ "github.com/go-gost/x/listener/serial"
|
||||
_ "github.com/go-gost/x/listener/ssh"
|
||||
_ "github.com/go-gost/x/listener/sshd"
|
||||
_ "github.com/go-gost/x/listener/tap"
|
||||
_ "github.com/go-gost/x/listener/tcp"
|
||||
_ "github.com/go-gost/x/listener/tls"
|
||||
_ "github.com/go-gost/x/listener/tun"
|
||||
_ "github.com/go-gost/x/listener/tungo"
|
||||
_ "github.com/go-gost/x/listener/udp"
|
||||
_ "github.com/go-gost/x/listener/unix"
|
||||
_ "github.com/go-gost/x/listener/ws"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
package main
|
||||
|
||||
const (
|
||||
version = "3.0.0-beta.2"
|
||||
var (
|
||||
version = "3.3.0"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
version: "3.4"
|
||||
services:
|
||||
gost:
|
||||
build: .
|
||||
214
go.mod
214
go.mod
|
|
@ -1,98 +1,170 @@
|
|||
module github.com/go-gost/gost
|
||||
|
||||
go 1.18
|
||||
|
||||
replace github.com/templexxx/cpu v0.0.7 => github.com/templexxx/cpu v0.0.10-0.20211111114238-98168dcec14a
|
||||
go 1.26.3
|
||||
|
||||
require (
|
||||
github.com/go-gost/core v0.0.0-20220413143512-acee88323487
|
||||
github.com/go-gost/x v0.0.0-20220413143619-7a334c173cb8
|
||||
github.com/go-gost/core v0.4.1
|
||||
github.com/go-gost/x v0.10.11-0.20260605152603-e45d9a8cc81a
|
||||
github.com/judwhite/go-svc v1.2.1
|
||||
github.com/moby/moby/client v0.4.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/testcontainers/testcontainers-go v0.42.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/alessio/shellescape v1.4.1 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/coreos/go-iptables v0.5.0 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/docker/libcontainer v2.2.1+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/gin-contrib/cors v1.3.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.7.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dunglas/httpsfv v1.1.0 // indirect
|
||||
github.com/ebitengine/purego v0.10.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/cors v1.7.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.7.7 // indirect
|
||||
github.com/go-gost/gosocks4 v0.0.1 // indirect
|
||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 // indirect
|
||||
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 // indirect
|
||||
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.1 // indirect
|
||||
github.com/gin-gonic/gin v1.10.1 // indirect
|
||||
github.com/go-gost/go-shadowsocks2 v0.1.3 // indirect
|
||||
github.com/go-gost/gosocks4 v0.1.0 // indirect
|
||||
github.com/go-gost/gosocks5 v0.5.0 // indirect
|
||||
github.com/go-gost/plugin v0.3.0 // indirect
|
||||
github.com/go-gost/relay v0.6.0 // indirect
|
||||
github.com/go-gost/tls-dissector v0.2.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid v1.3.1 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.9 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.25.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/marten-seemann/qpack v0.2.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.47 // indirect
|
||||
github.com/milosgajdos/tenus v0.0.3 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
|
||||
github.com/klauspost/compress v1.18.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/reedsolomon v1.11.8 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.61 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.2.0 // indirect
|
||||
github.com/moby/moby/api v1.54.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pion/dtls/v3 v3.1.1 // indirect
|
||||
github.com/pion/logging v0.2.4 // indirect
|
||||
github.com/pion/transport/v4 v4.0.1 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.1 // indirect
|
||||
github.com/quic-go/webtransport-go v0.10.0 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/rs/xid v1.3.0 // indirect
|
||||
github.com/shadowsocks/go-shadowsocks2 v0.1.5 // indirect
|
||||
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/shadowsocks/go-shadowsocks2 v0.1.6-0.20241020092332-e1fe9ea73740 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.26.3 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.10.1 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/templexxx/cpu v0.0.7 // indirect
|
||||
github.com/templexxx/xorsimd v0.4.1 // indirect
|
||||
github.com/tjfoc/gmsm v1.3.2 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1 // indirect
|
||||
github.com/xtaci/smux v1.5.16 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/templexxx/cpu v0.1.1 // indirect
|
||||
github.com/templexxx/xorsimd v0.4.3 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/vulcand/predicate v1.2.0 // indirect
|
||||
github.com/xjasonlyu/tun2socks/v2 v2.6.0 // indirect
|
||||
github.com/xtaci/kcp-go/v5 v5.6.5 // indirect
|
||||
github.com/xtaci/smux v1.5.31 // indirect
|
||||
github.com/xtaci/tcpraw v1.2.25 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.10 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect
|
||||
google.golang.org/grpc v1.45.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
github.com/yl2chen/cidranger v1.0.2 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zalando/go-keyring v0.2.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.41.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.41.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
|
||||
golang.org/x/mod v0.34.0 // indirect
|
||||
golang.org/x/net v0.53.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/term v0.42.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20 // indirect
|
||||
)
|
||||
|
|
|
|||
45
gost.yml
45
gost.yml
|
|
@ -32,9 +32,11 @@ services:
|
|||
abc: xyz
|
||||
def: 456
|
||||
forwarder:
|
||||
targets:
|
||||
- 192.168.1.1:1234
|
||||
- 192.168.1.2:2345
|
||||
nodes:
|
||||
- name: target-0
|
||||
addr: 192.168.1.1:1234
|
||||
- name: target-1
|
||||
addr: 192.168.1.2:2345
|
||||
selector:
|
||||
strategy: rand
|
||||
maxFails: 1
|
||||
|
|
@ -48,6 +50,7 @@ chains:
|
|||
failTimeout: 30s
|
||||
hops:
|
||||
- name: hop-0
|
||||
- name: hop-1
|
||||
interface: 192.168.1.2
|
||||
selector:
|
||||
strategy: rand
|
||||
|
|
@ -78,6 +81,38 @@ chains:
|
|||
metadata:
|
||||
bar: baz
|
||||
|
||||
hops:
|
||||
- name: hop-0
|
||||
interface: 192.168.1.2
|
||||
selector:
|
||||
strategy: rand
|
||||
maxFails: 3
|
||||
failTimeout: 60s
|
||||
bypass: bypass-0
|
||||
nodes:
|
||||
- name: node-0
|
||||
addr: ":1080"
|
||||
interface: eth1
|
||||
bypass: bypass-0
|
||||
connector:
|
||||
type: socks5
|
||||
auth:
|
||||
username: user
|
||||
password: pass
|
||||
metadata:
|
||||
foo: bar
|
||||
dialer:
|
||||
type: tcp
|
||||
auth:
|
||||
username: user
|
||||
password: pass
|
||||
tls:
|
||||
caFile: "ca.pem"
|
||||
secure: true
|
||||
serverName: "example.com"
|
||||
metadata:
|
||||
bar: baz
|
||||
|
||||
tls:
|
||||
certFile: "cert.pem"
|
||||
keyFile: "key.pem"
|
||||
|
|
@ -93,14 +128,14 @@ authers:
|
|||
|
||||
admissions:
|
||||
- name: admission-0
|
||||
reverse: false
|
||||
whitelist: false
|
||||
matchers:
|
||||
- 127.0.0.1
|
||||
- 192.168.0.0/16
|
||||
|
||||
bypasses:
|
||||
- name: bypass-0
|
||||
reverse: false
|
||||
whitelist: false
|
||||
matchers:
|
||||
- "*.example.com"
|
||||
- .example.org
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Check Root User
|
||||
|
||||
# If you want to run as another user, please modify $EUID to be owned by this user
|
||||
if [[ "$EUID" -ne '0' ]]; then
|
||||
echo "$(tput setaf 1)Error: You must run this script as root!$(tput sgr0)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set the desired GitHub repository
|
||||
repo="go-gost/gost"
|
||||
base_url="https://api.github.com/repos/$repo/releases"
|
||||
|
||||
# Function to download and install gost
|
||||
install_gost() {
|
||||
version=$1
|
||||
# Detect the operating system
|
||||
if [[ "$(uname)" == "Linux" ]]; then
|
||||
os="linux"
|
||||
elif [[ "$(uname)" == "Darwin" ]]; then
|
||||
os="darwin"
|
||||
elif [[ "$(uname)" == "MINGW"* ]]; then
|
||||
os="windows"
|
||||
else
|
||||
echo "Unsupported operating system."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect the CPU architecture
|
||||
arch=$(uname -m)
|
||||
case $arch in
|
||||
x86_64)
|
||||
cpu_arch="amd64"
|
||||
;;
|
||||
armv5*)
|
||||
cpu_arch="armv5"
|
||||
;;
|
||||
armv6*)
|
||||
cpu_arch="armv6"
|
||||
;;
|
||||
armv7*)
|
||||
cpu_arch="armv7"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
cpu_arch="arm64"
|
||||
;;
|
||||
i686)
|
||||
cpu_arch="386"
|
||||
;;
|
||||
mips64*)
|
||||
cpu_arch="mips64"
|
||||
;;
|
||||
mips*)
|
||||
cpu_arch="mips"
|
||||
;;
|
||||
mipsel*)
|
||||
cpu_arch="mipsle"
|
||||
;;
|
||||
riscv64)
|
||||
cpu_arch="riscv64"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported CPU architecture."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
get_download_url="$base_url/tags/$version"
|
||||
download_url=$(curl -s "$get_download_url" | awk -F'"' -v re=".*${os}.*${cpu_arch}.*" '/"browser_download_url":/ && $4 ~ re { print $4 }' | head -n 1)
|
||||
|
||||
# Download and install the binary
|
||||
install_path="/usr/local/bin"
|
||||
echo "Downloading and installing gost version $version..."
|
||||
curl -fsSL "$download_url" | tar -xzC "$install_path" gost
|
||||
chmod +x "$install_path/gost"
|
||||
|
||||
# Remove binary from macOS quarantine when installing for first time
|
||||
[[ "$os" == "darwin" ]] && { xattr -d com.apple.quarantine "$install_path/gost" 2>&-; }
|
||||
|
||||
echo "gost installation completed!"
|
||||
}
|
||||
|
||||
# Retrieve available versions from GitHub API
|
||||
versions=$(curl -s "$base_url" | awk -F'"' '/"tag_name":/ {print $4}')
|
||||
|
||||
# Check if --install option provided
|
||||
if [[ "$1" == "--install" ]]; then
|
||||
# Install the latest version automatically
|
||||
latest_version=$(echo "$versions" | head -n 1)
|
||||
install_gost $latest_version
|
||||
else
|
||||
# Display available versions to the user
|
||||
echo "Available gost versions:"
|
||||
select version in $versions; do
|
||||
if [[ -n $version ]]; then
|
||||
install_gost $version
|
||||
break
|
||||
else
|
||||
echo "Invalid choice! Please select a valid option."
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
FROM alpine:3.22
|
||||
|
||||
# add tools needed by e2e containers and health checks
|
||||
RUN apk add --no-cache iptables curl netcat-openbsd python3
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# End-to-End Tests
|
||||
|
||||
Integration tests that spin up real gost instances inside Docker containers and verify protocol behavior over the network.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Docker](https://docs.docker.com/get-docker/) (running daemon)
|
||||
- Go toolchain (for compiling the gost binary under test)
|
||||
|
||||
## Running
|
||||
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
# Run all e2e tests
|
||||
go test ./tests/e2e/ -v -timeout 10m
|
||||
|
||||
# Run a specific test suite
|
||||
go test ./tests/e2e/ -v -run TestShadowsocksSuite -timeout 5m
|
||||
go test ./tests/e2e/ -v -run TestParallelSelectorSuite -timeout 5m
|
||||
|
||||
# Use a pre-built gost binary (skips compilation)
|
||||
go test ./tests/e2e/ -v -gost-bin /path/to/gost
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
tests/e2e/
|
||||
├── Dockerfile # Shared base image (Alpine + curl, python3, etc.)
|
||||
├── main_test.go # TestMain: compiles gost, creates Docker network
|
||||
├── utils.go # Helpers: container lifecycle, config rendering
|
||||
├── scripts/
|
||||
│ ├── tcp_echo.py # HTTP echo server (responds with "hello-gost")
|
||||
│ └── udp_echo.py # UDP echo server (reflects payloads)
|
||||
├── testdata/ # config files or data files for running cases
|
||||
├── shadowsocks_test.go # Shadowsocks protocol tests
|
||||
└── parallel_selector_test.go # Parallel node selector tests
|
||||
```
|
||||
|
||||
### How it works
|
||||
|
||||
1. **TestMain** (`main_test.go`) compiles the gost binary from `../../cmd/gost` (unless `-gost-bin` is provided) and creates a shared Docker network for all containers.
|
||||
2. Each test suite starts echo server containers (TCP/UDP), then launches separate gost containers for server and client roles.
|
||||
3. Client configs use Go template syntax (`{{.ServerAddr}}`) so the server address is injected at runtime.
|
||||
4. Tests verify end-to-end connectivity by sending traffic through the gost proxy chain and checking that the echo server responds correctly.
|
||||
|
||||
## Tips
|
||||
|
||||
- Increase `-timeout` for CI or slow networks. Container image builds on first run take extra time.
|
||||
- Use `-gost-bin` to avoid recompiling when iterating on tests locally.
|
||||
- Add `-v` to see container log output on failure.
|
||||
- RunGostContainer will wait for exposedPorts automatically, but it's not reliable for udp ports. So, you should check the readiness of udp ports inside cases.
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/testcontainers/testcontainers-go/network"
|
||||
)
|
||||
|
||||
var SharedNetworkName string
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.Parse()
|
||||
ctx := context.Background()
|
||||
|
||||
shouldCleanup := false
|
||||
|
||||
if GostBinPath == "" {
|
||||
GostBinPath = "/tmp/gost-test-bin"
|
||||
cmd := exec.Command("go", "build", "-o", GostBinPath, "../../cmd/gost")
|
||||
cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Printf("Failed to compile gost: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
shouldCleanup = true
|
||||
}
|
||||
|
||||
net, err := network.New(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create network: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
SharedNetworkName = net.Name
|
||||
|
||||
code := m.Run()
|
||||
|
||||
net.Remove(ctx)
|
||||
if shouldCleanup {
|
||||
os.Remove(GostBinPath)
|
||||
}
|
||||
|
||||
os.Exit(code)
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
)
|
||||
|
||||
type ParallelSelectorSuite struct {
|
||||
suite.Suite
|
||||
ctx context.Context
|
||||
echoC testcontainers.Container
|
||||
echoIP string
|
||||
}
|
||||
|
||||
func (s *ParallelSelectorSuite) SetupSuite() {
|
||||
s.ctx = context.Background()
|
||||
|
||||
echoC, err := RunEchoContainer(s.ctx, SharedNetworkName)
|
||||
s.Require().NoError(err)
|
||||
s.echoC = echoC
|
||||
|
||||
echoIP, err := echoC.ContainerIP(s.ctx)
|
||||
s.Require().NoError(err)
|
||||
s.echoIP = echoIP
|
||||
}
|
||||
|
||||
func (s *ParallelSelectorSuite) TearDownSuite() {
|
||||
if s.echoC != nil {
|
||||
s.echoC.Terminate(s.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ParallelSelectorSuite) TestParallelSelector() {
|
||||
gostC, err := RunGostContainerWithPorts(s.ctx, SharedNetworkName, "testdata/parallel_selector/server.yaml", "8080/tcp")
|
||||
s.Require().NoError(err)
|
||||
defer gostC.Terminate(s.ctx)
|
||||
|
||||
// Test the proxy by running curl inside the gost container
|
||||
cmd := []string{"curl", "-v", "-s", "-x", "http://127.0.0.1:8080", fmt.Sprintf("http://%s:5678", s.echoIP)}
|
||||
code, out, err := gostC.Exec(s.ctx, cmd)
|
||||
s.Require().NoError(err)
|
||||
|
||||
body, err := io.ReadAll(out)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(0, code)
|
||||
|
||||
s.Require().Contains(string(body), "hello-gost")
|
||||
}
|
||||
|
||||
func TestParallelSelectorSuite(t *testing.T) {
|
||||
suite.Run(t, new(ParallelSelectorSuite))
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
body = b"hello-gost"
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def log_message(self, format, *args):
|
||||
return
|
||||
|
||||
|
||||
HTTPServer(("0.0.0.0", 5678), Handler).serve_forever()
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import socket
|
||||
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind(("0.0.0.0", 5679))
|
||||
|
||||
while True:
|
||||
data, addr = sock.recvfrom(2048)
|
||||
sock.sendto(data, addr)
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
)
|
||||
|
||||
type ShadowsocksSuite struct {
|
||||
suite.Suite
|
||||
ctx context.Context
|
||||
echoC testcontainers.Container
|
||||
echoIP string
|
||||
udpC testcontainers.Container
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) SetupSuite() {
|
||||
s.ctx = context.Background()
|
||||
|
||||
s.T().Logf("start tcp echo container...")
|
||||
echoC, err := RunEchoContainer(s.ctx, SharedNetworkName)
|
||||
s.Require().NoError(err)
|
||||
s.echoC = echoC
|
||||
|
||||
echoIP, err := echoC.ContainerIP(s.ctx)
|
||||
s.Require().NoError(err)
|
||||
s.echoIP = echoIP
|
||||
|
||||
s.T().Logf("start udp echo container...")
|
||||
udpC, err := RunUDPEchoContainer(s.ctx, SharedNetworkName)
|
||||
s.Require().NoError(err)
|
||||
s.udpC = udpC
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) TearDownSuite() {
|
||||
if s.echoC != nil {
|
||||
s.echoC.Terminate(s.ctx)
|
||||
}
|
||||
if s.udpC != nil {
|
||||
s.udpC.Terminate(s.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) TestShadowsocksTCP() {
|
||||
s.runTCPCase("aes256gcm", "testdata/shadowsocks/tcp_server_aes256gcm.yaml", "testdata/shadowsocks/tcp_client_aes256gcm.yaml")
|
||||
s.runTCPCase("chacha20", "testdata/shadowsocks/tcp_server_chacha20.yaml", "testdata/shadowsocks/tcp_client_chacha20.yaml")
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) TestShadowsocks2022TCP() {
|
||||
s.runTCPCase("2022-aes128", "testdata/shadowsocks/tcp_server_2022_aes128.yaml", "testdata/shadowsocks/tcp_client_2022_aes128.yaml")
|
||||
s.runTCPCase("2022-aes256", "testdata/shadowsocks/tcp_server_2022_aes256.yaml", "testdata/shadowsocks/tcp_client_2022_aes256.yaml")
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) TestShadowsocks2022TCPMultiPSK() {
|
||||
s.runTCPCase("2022-aes128-multipsk", "testdata/shadowsocks/tcp_server_2022_aes128_multipsk.yaml", "testdata/shadowsocks/tcp_client_2022_aes128_multipsk.yaml")
|
||||
s.runTCPCase("2022-aes256-multipsk", "testdata/shadowsocks/tcp_server_2022_aes256_multipsk.yaml", "testdata/shadowsocks/tcp_client_2022_aes256_multipsk.yaml")
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) TestShadowsocksUDP() {
|
||||
s.runUDPCase("aes256gcm", "testdata/shadowsocks/udp_server_aes256gcm.yaml", "testdata/shadowsocks/udp_client_aes256gcm.yaml")
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) TestShadowsocks2022UDP() {
|
||||
s.runUDPCase("2022-aes128", "testdata/shadowsocks/udp_server_2022_aes128.yaml", "testdata/shadowsocks/udp_client_2022_aes128.yaml")
|
||||
s.runUDPCase("2022-aes256", "testdata/shadowsocks/udp_server_2022_aes256.yaml", "testdata/shadowsocks/udp_client_2022_aes256.yaml")
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) runUDPCase(name, serverConfig, clientConfig string) {
|
||||
s.T().Run(name, func(t *testing.T) {
|
||||
serverAlias := name + "-ssu-server"
|
||||
serverC, err := RunGostContainerWithOptions(s.ctx, SharedNetworkName, serverConfig, []string{serverAlias}, []string{"8389/udp"})
|
||||
s.Require().NoError(err)
|
||||
defer serverC.Terminate(s.ctx)
|
||||
|
||||
rendered, err := RenderConfig(clientConfig, ConfigData{ServerAddr: serverAlias + ":8389"})
|
||||
s.Require().NoError(err)
|
||||
defer os.Remove(rendered)
|
||||
|
||||
clientC, err := RunGostContainerWithPorts(
|
||||
s.ctx,
|
||||
SharedNetworkName,
|
||||
rendered,
|
||||
"9000/udp",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
defer clientC.Terminate(s.ctx)
|
||||
|
||||
host, err := clientC.Host(s.ctx)
|
||||
s.Require().NoError(err)
|
||||
port, err := clientC.MappedPort(s.ctx, "9000/udp")
|
||||
s.Require().NoError(err)
|
||||
|
||||
conn, err := net.DialTimeout("udp", net.JoinHostPort(host, port.Port()), 5*time.Second)
|
||||
s.Require().NoError(err)
|
||||
defer conn.Close()
|
||||
|
||||
payload := []byte("hello-gost-udp")
|
||||
buf := make([]byte, 2048)
|
||||
var n int
|
||||
for i := range 5 {
|
||||
_, err = conn.Write(payload)
|
||||
s.Require().NoError(err)
|
||||
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
n, err = conn.Read(buf)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
s.T().Logf("udp read attempt %d failed: %v, retrying...", i+1, err)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
if err != nil {
|
||||
DumpLogs(s.T(), s.ctx, name+" udp client logs", clientC)
|
||||
DumpLogs(s.T(), s.ctx, name+" udp server logs", serverC)
|
||||
}
|
||||
s.Require().NoError(err)
|
||||
s.Require().Contains(string(buf[:n]), "hello-gost")
|
||||
})
|
||||
}
|
||||
|
||||
func TestShadowsocksSuite(t *testing.T) {
|
||||
suite.Run(t, new(ShadowsocksSuite))
|
||||
}
|
||||
|
||||
func (s *ShadowsocksSuite) runTCPCase(name, serverConfig, clientConfig string) {
|
||||
s.T().Run(name, func(t *testing.T) {
|
||||
serverAlias := name + "-ss-server"
|
||||
serverC, err := RunGostContainerWithOptions(s.ctx, SharedNetworkName, serverConfig, []string{serverAlias}, []string{"8388/tcp"})
|
||||
s.Require().NoError(err)
|
||||
defer serverC.Terminate(s.ctx)
|
||||
|
||||
rendered, err := RenderConfig(clientConfig, ConfigData{ServerAddr: serverAlias + ":8388"})
|
||||
s.Require().NoError(err)
|
||||
defer os.Remove(rendered)
|
||||
|
||||
clientC, err := RunGostContainerWithPorts(s.ctx, SharedNetworkName, rendered, "8080/tcp")
|
||||
s.Require().NoError(err)
|
||||
defer clientC.Terminate(s.ctx)
|
||||
|
||||
cmd := []string{"curl", "-v", "-s", "-x", "http://127.0.0.1:8080", fmt.Sprintf("http://%s:5678", s.echoIP)}
|
||||
code, out, err := clientC.Exec(s.ctx, cmd)
|
||||
s.Require().NoError(err)
|
||||
|
||||
body, err := io.ReadAll(out)
|
||||
s.Require().NoError(err)
|
||||
if code != 0 || !strings.Contains(string(body), "hello-gost") {
|
||||
DumpLogs(s.T(), s.ctx, name+" client logs", clientC)
|
||||
DumpLogs(s.T(), s.ctx, name+" server logs", serverC)
|
||||
}
|
||||
s.Require().Equal(0, code)
|
||||
s.Require().Contains(string(body), "hello-gost")
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
services:
|
||||
- name: proxy
|
||||
addr: :8080
|
||||
handler:
|
||||
type: http
|
||||
chain: my-chain
|
||||
listener:
|
||||
type: tcp
|
||||
|
||||
- name: dummy-1
|
||||
addr: :18081
|
||||
handler:
|
||||
type: http
|
||||
|
||||
chains:
|
||||
- name: my-chain
|
||||
hops:
|
||||
- name: hop-1
|
||||
selector:
|
||||
strategy: parallel
|
||||
nodes:
|
||||
- name: node-1
|
||||
addr: 127.0.0.1:18081
|
||||
connector:
|
||||
type: http
|
||||
# non existed node
|
||||
- name: node-2
|
||||
addr: 127.0.0.1:18082
|
||||
connector:
|
||||
type: http
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
services:
|
||||
- name: http-proxy
|
||||
addr: :8080
|
||||
handler:
|
||||
type: http
|
||||
chain: ss-tcp-chain
|
||||
listener:
|
||||
type: tcp
|
||||
|
||||
chains:
|
||||
- name: ss-tcp-chain
|
||||
hops:
|
||||
- name: ss-hop
|
||||
nodes:
|
||||
- name: ss-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ss
|
||||
auth:
|
||||
username: 2022-blake3-aes-128-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||
dialer:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
services:
|
||||
- name: http-proxy
|
||||
addr: :8080
|
||||
handler:
|
||||
type: http
|
||||
chain: ss-tcp-chain
|
||||
listener:
|
||||
type: tcp
|
||||
|
||||
chains:
|
||||
- name: ss-tcp-chain
|
||||
hops:
|
||||
- name: ss-hop
|
||||
nodes:
|
||||
- name: ss-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ss
|
||||
auth:
|
||||
username: 2022-blake3-aes-128-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1Ng==:Vbwi6yqCwvPMPR1bCi32Dg==
|
||||
dialer:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
services:
|
||||
- name: http-proxy
|
||||
addr: :8080
|
||||
handler:
|
||||
type: http
|
||||
chain: ss-tcp-chain
|
||||
listener:
|
||||
type: tcp
|
||||
|
||||
chains:
|
||||
- name: ss-tcp-chain
|
||||
hops:
|
||||
- name: ss-hop
|
||||
nodes:
|
||||
- name: ss-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ss
|
||||
auth:
|
||||
username: 2022-blake3-aes-256-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||
dialer:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
services:
|
||||
- name: http-proxy
|
||||
addr: :8080
|
||||
handler:
|
||||
type: http
|
||||
chain: ss-tcp-chain
|
||||
listener:
|
||||
type: tcp
|
||||
|
||||
chains:
|
||||
- name: ss-tcp-chain
|
||||
hops:
|
||||
- name: ss-hop
|
||||
nodes:
|
||||
- name: ss-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ss
|
||||
auth:
|
||||
username: 2022-blake3-aes-256-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=:YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||
dialer:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
services:
|
||||
- name: http-proxy
|
||||
addr: :8080
|
||||
handler:
|
||||
type: http
|
||||
chain: ss-tcp-chain
|
||||
listener:
|
||||
type: tcp
|
||||
|
||||
chains:
|
||||
- name: ss-tcp-chain
|
||||
hops:
|
||||
- name: ss-hop
|
||||
nodes:
|
||||
- name: ss-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ss
|
||||
auth:
|
||||
username: aes-256-gcm
|
||||
password: secret
|
||||
dialer:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
services:
|
||||
- name: http-proxy
|
||||
addr: :8080
|
||||
handler:
|
||||
type: http
|
||||
chain: ss-tcp-chain
|
||||
listener:
|
||||
type: tcp
|
||||
|
||||
chains:
|
||||
- name: ss-tcp-chain
|
||||
hops:
|
||||
- name: ss-hop
|
||||
nodes:
|
||||
- name: ss-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ss
|
||||
auth:
|
||||
username: chacha20-ietf-poly1305
|
||||
password: secret
|
||||
dialer:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
- name: ss-server
|
||||
addr: :8388
|
||||
handler:
|
||||
type: ss
|
||||
auth:
|
||||
username: 2022-blake3-aes-128-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||
listener:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
services:
|
||||
- name: ss-server
|
||||
addr: :8388
|
||||
handler:
|
||||
type: ss
|
||||
auth:
|
||||
username: 2022-blake3-aes-128-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||
metadata:
|
||||
users:
|
||||
test: Vbwi6yqCwvPMPR1bCi32Dg==
|
||||
listener:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
- name: ss-server
|
||||
addr: :8388
|
||||
handler:
|
||||
type: ss
|
||||
auth:
|
||||
username: 2022-blake3-aes-256-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||
listener:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
services:
|
||||
- name: ss-server
|
||||
addr: :8388
|
||||
handler:
|
||||
type: ss
|
||||
auth:
|
||||
username: 2022-blake3-aes-256-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||
metadata:
|
||||
users:
|
||||
test: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||
listener:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
- name: ss-server
|
||||
addr: :8388
|
||||
handler:
|
||||
type: ss
|
||||
auth:
|
||||
username: aes-256-gcm
|
||||
password: secret
|
||||
listener:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
- name: ss-server
|
||||
addr: :8388
|
||||
handler:
|
||||
type: ss
|
||||
auth:
|
||||
username: chacha20-ietf-poly1305
|
||||
password: secret
|
||||
listener:
|
||||
type: tcp
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
services:
|
||||
- name: udp-proxy
|
||||
addr: :9000
|
||||
handler:
|
||||
type: udp
|
||||
chain: ssu-chain
|
||||
forwarder:
|
||||
nodes:
|
||||
- name: udp-echo
|
||||
addr: udp-echo:5679
|
||||
listener:
|
||||
type: udp
|
||||
|
||||
chains:
|
||||
- name: ssu-chain
|
||||
hops:
|
||||
- name: ssu-hop
|
||||
nodes:
|
||||
- name: ssu-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ssu
|
||||
auth:
|
||||
username: 2022-blake3-aes-128-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||
dialer:
|
||||
type: udp
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
services:
|
||||
- name: udp-proxy
|
||||
addr: :9000
|
||||
handler:
|
||||
type: udp
|
||||
chain: ssu-chain
|
||||
forwarder:
|
||||
nodes:
|
||||
- name: udp-echo
|
||||
addr: udp-echo:5679
|
||||
listener:
|
||||
type: udp
|
||||
|
||||
chains:
|
||||
- name: ssu-chain
|
||||
hops:
|
||||
- name: ssu-hop
|
||||
nodes:
|
||||
- name: ssu-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ssu
|
||||
auth:
|
||||
username: 2022-blake3-aes-256-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||
dialer:
|
||||
type: udp
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
services:
|
||||
- name: udp-proxy
|
||||
addr: :9000
|
||||
handler:
|
||||
type: udp
|
||||
chain: ssu-chain
|
||||
forwarder:
|
||||
nodes:
|
||||
- name: udp-echo
|
||||
addr: udp-echo:5679
|
||||
listener:
|
||||
type: udp
|
||||
|
||||
chains:
|
||||
- name: ssu-chain
|
||||
hops:
|
||||
- name: ssu-hop
|
||||
nodes:
|
||||
- name: ssu-node
|
||||
addr: {{.ServerAddr}}
|
||||
connector:
|
||||
type: ssu
|
||||
auth:
|
||||
username: aes-256-gcm
|
||||
password: secret
|
||||
dialer:
|
||||
type: udp
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
- name: ssu-server
|
||||
addr: :8389
|
||||
handler:
|
||||
type: ssu
|
||||
auth:
|
||||
username: 2022-blake3-aes-128-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1Ng==
|
||||
listener:
|
||||
type: udp
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
- name: ssu-server
|
||||
addr: :8389
|
||||
handler:
|
||||
type: ssu
|
||||
auth:
|
||||
username: 2022-blake3-aes-256-gcm
|
||||
password: MTIzNDU2Nzg5MDEyMzQ1NjEyMzQ1Njc4OTAxMjM0NTY=
|
||||
listener:
|
||||
type: udp
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
- name: ssu-server
|
||||
addr: :8389
|
||||
handler:
|
||||
type: ssu
|
||||
auth:
|
||||
username: aes-256-gcm
|
||||
password: secret
|
||||
listener:
|
||||
type: udp
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
var GostBinPath string
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&GostBinPath, "gost-bin", "", "Path to a pre-built gost binary (skips compilation)")
|
||||
}
|
||||
|
||||
type ConfigData struct {
|
||||
ServerAddr string
|
||||
}
|
||||
|
||||
func DumpLogs(t *testing.T, ctx context.Context, label string, c testcontainers.Container) {
|
||||
logs, err := c.Logs(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer logs.Close()
|
||||
|
||||
body, err := io.ReadAll(logs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("%s:\n%s", label, string(body))
|
||||
}
|
||||
|
||||
func RenderConfig(tmplPath string, data ConfigData) (string, error) {
|
||||
tmpl, err := template.ParseFiles(tmplPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
f, err := os.CreateTemp("", "gost-e2e-config-*.yaml")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := tmpl.Execute(f, data); err != nil {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
return "", err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
os.Remove(f.Name())
|
||||
return "", err
|
||||
}
|
||||
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
func RunEchoContainer(ctx context.Context, networkName string) (testcontainers.Container, error) {
|
||||
req := echoContainerRequest(ctx, networkName)
|
||||
return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
})
|
||||
}
|
||||
|
||||
func echoContainerRequest(_ context.Context, networkName string) testcontainers.ContainerRequest {
|
||||
return testcontainers.ContainerRequest{
|
||||
FromDockerfile: testcontainers.FromDockerfile{
|
||||
Context: ".",
|
||||
Dockerfile: "Dockerfile",
|
||||
Repo: "gost-e2e",
|
||||
Tag: "latest",
|
||||
KeepImage: true,
|
||||
BuildOptionsModifier: func(opts *client.ImageBuildOptions) {
|
||||
opts.NetworkMode = "host"
|
||||
},
|
||||
},
|
||||
Networks: []string{networkName},
|
||||
NetworkAliases: map[string][]string{
|
||||
networkName: {"tcp-echo"},
|
||||
},
|
||||
Files: []testcontainers.ContainerFile{
|
||||
{HostFilePath: "scripts/tcp_echo.py", ContainerFilePath: "/scripts/tcp_echo.py", FileMode: 0644},
|
||||
},
|
||||
ExposedPorts: []string{"5678/tcp"},
|
||||
Cmd: []string{"python3", "/scripts/tcp_echo.py"},
|
||||
WaitingFor: wait.ForExposedPort(),
|
||||
}
|
||||
}
|
||||
|
||||
func RunUDPEchoContainer(ctx context.Context, networkName string) (testcontainers.Container, error) {
|
||||
req := udpEchoContainerRequest(ctx, networkName)
|
||||
return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
})
|
||||
}
|
||||
|
||||
func udpEchoContainerRequest(_ context.Context, networkName string) testcontainers.ContainerRequest {
|
||||
return testcontainers.ContainerRequest{
|
||||
FromDockerfile: testcontainers.FromDockerfile{
|
||||
Context: ".",
|
||||
Dockerfile: "Dockerfile",
|
||||
Repo: "gost-e2e",
|
||||
Tag: "latest",
|
||||
KeepImage: true,
|
||||
BuildOptionsModifier: func(opts *client.ImageBuildOptions) {
|
||||
opts.NetworkMode = "host"
|
||||
},
|
||||
},
|
||||
Networks: []string{networkName},
|
||||
NetworkAliases: map[string][]string{
|
||||
networkName: {"udp-echo"},
|
||||
},
|
||||
Files: []testcontainers.ContainerFile{
|
||||
{HostFilePath: "scripts/udp_echo.py", ContainerFilePath: "/scripts/udp_echo.py", FileMode: 0644},
|
||||
},
|
||||
ExposedPorts: []string{"5679/udp"},
|
||||
Cmd: []string{"python3", "/scripts/udp_echo.py"},
|
||||
WaitingFor: wait.ForExposedPort().SkipInternalCheck(),
|
||||
}
|
||||
}
|
||||
|
||||
func RunGostContainer(ctx context.Context, networkName, yamlPath string) (testcontainers.Container, error) {
|
||||
return runGostContainer(ctx, networkName, yamlPath, nil, nil)
|
||||
}
|
||||
|
||||
func RunGostContainerWithPorts(ctx context.Context, networkName, yamlPath string, exposedPorts ...string) (testcontainers.Container, error) {
|
||||
return runGostContainer(ctx, networkName, yamlPath, nil, exposedPorts)
|
||||
}
|
||||
|
||||
func RunGostContainerWithOptions(ctx context.Context, networkName, yamlPath string, aliases, exposedPorts []string) (testcontainers.Container, error) {
|
||||
return runGostContainer(ctx, networkName, yamlPath, aliases, exposedPorts)
|
||||
}
|
||||
|
||||
func runGostContainer(ctx context.Context, networkName, yamlPath string, aliases, exposedPorts []string) (testcontainers.Container, error) {
|
||||
req := testcontainers.ContainerRequest{
|
||||
FromDockerfile: testcontainers.FromDockerfile{
|
||||
Context: ".",
|
||||
Dockerfile: "Dockerfile",
|
||||
Repo: "gost-e2e",
|
||||
Tag: "latest",
|
||||
KeepImage: true,
|
||||
BuildOptionsModifier: func(opts *client.ImageBuildOptions) {
|
||||
opts.NetworkMode = "host"
|
||||
},
|
||||
},
|
||||
ExposedPorts: exposedPorts,
|
||||
// interal check for udp ports will be failed
|
||||
WaitingFor: wait.ForExposedPort().SkipInternalCheck(),
|
||||
Networks: []string{networkName},
|
||||
NetworkAliases: map[string][]string{
|
||||
networkName: aliases,
|
||||
},
|
||||
Files: []testcontainers.ContainerFile{
|
||||
{HostFilePath: GostBinPath, ContainerFilePath: "/bin/gost", FileMode: 0755},
|
||||
{HostFilePath: yamlPath, ContainerFilePath: "/config.yaml", FileMode: 0644},
|
||||
},
|
||||
Cmd: []string{"/bin/gost", "-C", "/config.yaml"},
|
||||
}
|
||||
|
||||
return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue