mirror of
https://github.com/nadoo/glider.git
synced 2025-04-21 19:52:07 +08:00
Compare commits
No commits in common. "main" and "v0.16.2" have entirely different histories.
@ -7,7 +7,7 @@ RUN apk add --no-cache ca-certificates
|
|||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
RUN case $TARGETPLATFORM in \
|
RUN case $TARGETPLATFORM in \
|
||||||
'linux/386') \
|
'linux/386') \
|
||||||
export FOLDER='default_linux_386_sse2'; \
|
export FOLDER='default_linux_386'; \
|
||||||
;; \
|
;; \
|
||||||
'linux/amd64') \
|
'linux/amd64') \
|
||||||
export FOLDER='default_linux_amd64_v1'; \
|
export FOLDER='default_linux_amd64_v1'; \
|
||||||
@ -19,10 +19,10 @@ RUN case $TARGETPLATFORM in \
|
|||||||
export FOLDER='default_linux_arm_7'; \
|
export FOLDER='default_linux_arm_7'; \
|
||||||
;; \
|
;; \
|
||||||
'linux/arm64') \
|
'linux/arm64') \
|
||||||
export FOLDER='default_linux_arm64_v8.0'; \
|
export FOLDER='default_linux_arm64'; \
|
||||||
;; \
|
;; \
|
||||||
'linux/riscv64') \
|
'linux/riscv64') \
|
||||||
export FOLDER='default_linux_riscv64_rva20u64'; \
|
export FOLDER='default_linux_riscv64'; \
|
||||||
;; \
|
;; \
|
||||||
*) echo >&2 "error: unsupported architecture '$TARGETPLATFORM'"; exit 1 ;; \
|
*) echo >&2 "error: unsupported architecture '$TARGETPLATFORM'"; exit 1 ;; \
|
||||||
esac \
|
esac \
|
||||||
|
72
.github/workflows/build.yml
vendored
72
.github/workflows/build.yml
vendored
@ -2,9 +2,9 @@ name: Build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- "dev"
|
- 'dev'
|
||||||
tags:
|
tags:
|
||||||
- "*"
|
- '*'
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
@ -19,63 +19,96 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set Vars
|
- name: Set Vars
|
||||||
run: |
|
run: |
|
||||||
echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
|
echo "GO_MOD_VERSION=$(grep -P "go \d+\." go.mod | cut -d " " -f2)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
check-latest: true
|
check-latest: true
|
||||||
go-version-file: "go.mod"
|
go-version: ${{ env.GO_MOD_VERSION}}
|
||||||
cache: true
|
|
||||||
|
- name: Set up Cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/go/pkg/mod
|
||||||
|
~/.cache/go-build
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test -v ./...
|
run: go test -v .
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: goreleaser/goreleaser-action@v6
|
uses: goreleaser/goreleaser-action@v2
|
||||||
if: "!startsWith(github.ref, 'refs/tags/')"
|
if: "!startsWith(github.ref, 'refs/tags/')"
|
||||||
with:
|
with:
|
||||||
args: build --snapshot --clean
|
version: latest
|
||||||
|
args: build --snapshot --rm-dist
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifact - Linux amd64
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
if: "!startsWith(github.ref, 'refs/tags/')"
|
if: "!startsWith(github.ref, 'refs/tags/')"
|
||||||
with:
|
with:
|
||||||
name: ${{ env.APP_NAME }}-dev-${{ env.SHA_SHORT }}
|
name: ${{ env.APP_NAME }}-dev-${{ env.SHA_SHORT }}-linux-amd64
|
||||||
path: |
|
path: |
|
||||||
./dist/default_linux_amd64_v1/${{ env.APP_NAME }}
|
./dist/default_linux_amd64_v1/${{ env.APP_NAME }}
|
||||||
|
|
||||||
|
- name: Upload Artifact - Linux arm64
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: "!startsWith(github.ref, 'refs/tags/')"
|
||||||
|
with:
|
||||||
|
name: ${{ env.APP_NAME }}-dev-${{ env.SHA_SHORT }}-linux-arm64
|
||||||
|
path: |
|
||||||
./dist/default_linux_arm64/${{ env.APP_NAME }}
|
./dist/default_linux_arm64/${{ env.APP_NAME }}
|
||||||
|
|
||||||
|
- name: Upload Artifact - macOS arm64
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: "!startsWith(github.ref, 'refs/tags/')"
|
||||||
|
with:
|
||||||
|
name: ${{ env.APP_NAME }}-dev-${{ env.SHA_SHORT }}-macos-arm64
|
||||||
|
path: |
|
||||||
./dist/default_darwin_arm64/${{ env.APP_NAME }}
|
./dist/default_darwin_arm64/${{ env.APP_NAME }}
|
||||||
|
|
||||||
|
- name: Upload Artifact - Windows amd64
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
if: "!startsWith(github.ref, 'refs/tags/')"
|
||||||
|
with:
|
||||||
|
name: ${{ env.APP_NAME }}-dev-${{ env.SHA_SHORT }}-windows-amd64
|
||||||
|
path: |
|
||||||
./dist/default_windows_amd64_v1/${{ env.APP_NAME }}.exe
|
./dist/default_windows_amd64_v1/${{ env.APP_NAME }}.exe
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: goreleaser/goreleaser-action@v6
|
uses: goreleaser/goreleaser-action@v2
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
with:
|
with:
|
||||||
args: release --clean
|
version: latest
|
||||||
|
args: release --rm-dist
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Docker - Set up Buildx
|
- name: Docker - Set up Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Docker - Login to DockerHub
|
- name: Docker - Login to DockerHub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Docker - Login to GHCR
|
- name: Docker - Login to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
@ -83,7 +116,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Docker - Docker meta
|
- name: Docker - Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ env.DOCKERHUB_REPO }}
|
${{ env.DOCKERHUB_REPO }}
|
||||||
@ -94,10 +127,11 @@ jobs:
|
|||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
|
||||||
- name: Docker - Build and push
|
- name: Docker - Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: .Dockerfile
|
file: .Dockerfile
|
||||||
platforms: ${{ env.PLATFORMS }}
|
platforms: ${{ env.PLATFORMS }}
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9
|
- uses: actions/stale@v5
|
||||||
with:
|
with:
|
||||||
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
||||||
days-before-stale: 90
|
days-before-stale: 90
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -17,14 +17,12 @@
|
|||||||
# custom
|
# custom
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
.zed
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# dev test only
|
# dev test only
|
||||||
/dev/
|
/dev/
|
||||||
dev*.go
|
dev*.go
|
||||||
|
|
||||||
*_test.go
|
|
||||||
|
|
||||||
dist
|
dist
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
version: 2
|
|
||||||
|
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
@ -37,11 +35,13 @@ archives:
|
|||||||
- id: default
|
- id: default
|
||||||
builds:
|
builds:
|
||||||
- default
|
- default
|
||||||
|
replacements:
|
||||||
|
darwin: macos
|
||||||
wrap_in_directory: true
|
wrap_in_directory: true
|
||||||
formats: tar.gz
|
format: tar.gz
|
||||||
format_overrides:
|
format_overrides:
|
||||||
- goos: windows
|
- goos: windows
|
||||||
formats: zip
|
format: zip
|
||||||
files:
|
files:
|
||||||
- LICENSE
|
- LICENSE
|
||||||
- README.md
|
- README.md
|
||||||
@ -49,7 +49,7 @@ archives:
|
|||||||
- systemd/*
|
- systemd/*
|
||||||
|
|
||||||
snapshot:
|
snapshot:
|
||||||
version_template: '{{ incpatch .Version }}-dev-{{.ShortCommit}}'
|
name_template: '{{ incpatch .Version }}-dev-{{.ShortCommit}}'
|
||||||
|
|
||||||
checksum:
|
checksum:
|
||||||
name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
|
name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
|
||||||
@ -83,7 +83,7 @@ nfpms:
|
|||||||
dst: /etc/systemd/system/glider@.service
|
dst: /etc/systemd/system/glider@.service
|
||||||
|
|
||||||
- src: config/glider.conf.example
|
- src: config/glider.conf.example
|
||||||
dst: /etc/glider/glider.conf.example
|
dst: /etc/glider/glider.conf
|
||||||
|
|
||||||
scripts:
|
scripts:
|
||||||
postinstall: "systemd/postinstall.sh"
|
postinstall: "systemd/postinstall.sh"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM golang:1.24-alpine AS build-env
|
FROM golang:1.18-alpine AS build-env
|
||||||
ADD . /src
|
ADD . /src
|
||||||
RUN apk --no-cache add git \
|
RUN apk --no-cache add git \
|
||||||
&& cd /src && go build -v -ldflags "-s -w"
|
&& cd /src && go build -v -ldflags "-s -w"
|
||||||
|
20
README.md
20
README.md
@ -3,7 +3,7 @@
|
|||||||
[](https://go.dev/dl/)
|
[](https://go.dev/dl/)
|
||||||
[](https://goreportcard.com/report/github.com/nadoo/glider)
|
[](https://goreportcard.com/report/github.com/nadoo/glider)
|
||||||
[](https://github.com/nadoo/glider/releases)
|
[](https://github.com/nadoo/glider/releases)
|
||||||
[](https://github.com/nadoo/glider/actions)
|
[](https://github.com/nadoo/glider/actions)
|
||||||
[](https://hub.docker.com/r/nadoo/glider)
|
[](https://hub.docker.com/r/nadoo/glider)
|
||||||
|
|
||||||
glider is a forward proxy with multiple protocols support, and also a dns/dhcp server with ipset management features(like dnsmasq).
|
glider is a forward proxy with multiple protocols support, and also a dns/dhcp server with ipset management features(like dnsmasq).
|
||||||
@ -82,10 +82,7 @@ we can set up local listeners as proxy servers, and forward requests to internet
|
|||||||
|
|
||||||
- Binary: [https://github.com/nadoo/glider/releases](https://github.com/nadoo/glider/releases)
|
- Binary: [https://github.com/nadoo/glider/releases](https://github.com/nadoo/glider/releases)
|
||||||
- Docker: `docker pull nadoo/glider`
|
- Docker: `docker pull nadoo/glider`
|
||||||
- Manjaro: `pamac install glider`
|
|
||||||
- ArchLinux: `sudo pacman -S glider`
|
- ArchLinux: `sudo pacman -S glider`
|
||||||
- Homebrew: `brew install glider`
|
|
||||||
- MacPorts: `sudo port install glider`
|
|
||||||
- Source: `go install github.com/nadoo/glider@latest`
|
- Source: `go install github.com/nadoo/glider@latest`
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -224,7 +221,7 @@ Help:
|
|||||||
|
|
||||||
see README.md and glider.conf.example for more details.
|
see README.md and glider.conf.example for more details.
|
||||||
--
|
--
|
||||||
glider 0.16.4, https://github.com/nadoo/glider (glider.proxy@gmail.com)
|
glider 0.16.1, https://github.com/nadoo/glider (glider.proxy@gmail.com)
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
@ -238,10 +235,7 @@ glider 0.16.4, https://github.com/nadoo/glider (glider.proxy@gmail.com)
|
|||||||
Direct scheme:
|
Direct scheme:
|
||||||
direct://
|
direct://
|
||||||
|
|
||||||
Only needed when you want to specify the outgoing interface:
|
Only needed when you want to load balance multiple interfaces directly:
|
||||||
glider -verbose -listen :8443 -forward direct://#interface=eth0
|
|
||||||
|
|
||||||
Or load balance multiple interfaces directly:
|
|
||||||
glider -verbose -listen :8443 -forward direct://#interface=eth0 -forward direct://#interface=eth1 -strategy rr
|
glider -verbose -listen :8443 -forward direct://#interface=eth0 -forward direct://#interface=eth1 -strategy rr
|
||||||
|
|
||||||
Or you can use the high availability mode:
|
Or you can use the high availability mode:
|
||||||
@ -413,7 +407,7 @@ Examples:
|
|||||||
glider -listen udp://:53 -forward socks5://serverA:1080,udp://8.8.8.8:53
|
glider -listen udp://:53 -forward socks5://serverA:1080,udp://8.8.8.8:53
|
||||||
-udp tunnel: listen on :53 and forward all udp requests to 8.8.8.8:53 via remote socks5 server.
|
-udp tunnel: listen on :53 and forward all udp requests to 8.8.8.8:53 via remote socks5 server.
|
||||||
|
|
||||||
glider -verbose -dns=:53 -dnsserver=8.8.8.8:53 -forward socks5://serverA:1080 -dnsrecord=abc.com/1.2.3.4
|
glider -verbose -listen -dns=:53 -dnsserver=8.8.8.8:53 -forward socks5://serverA:1080 -dnsrecord=abc.com/1.2.3.4
|
||||||
-dns over proxy: listen on :53 as dns server, forward to 8.8.8.8:53 via socks5 server.
|
-dns over proxy: listen on :53 as dns server, forward to 8.8.8.8:53 via socks5 server.
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -444,7 +438,7 @@ glider -config CONFIG_PATH
|
|||||||
|
|
||||||
## Linux Daemon
|
## Linux Daemon
|
||||||
|
|
||||||
- systemd: [https://github.com/nadoo/glider/tree/main/systemd](https://github.com/nadoo/glider/tree/main/systemd)
|
- systemd: [https://github.com/nadoo/glider/blob/master/systemd/](https://github.com/nadoo/glider/blob/master/systemd/)
|
||||||
|
|
||||||
- <details> <summary>docker: click to see details</summary>
|
- <details> <summary>docker: click to see details</summary>
|
||||||
|
|
||||||
@ -486,7 +480,7 @@ glider -config CONFIG_PATH
|
|||||||
// _ "github.com/nadoo/glider/proxy/kcp"
|
// _ "github.com/nadoo/glider/proxy/kcp"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Build it:
|
3. Build it(requires **Go 1.18+** )
|
||||||
```bash
|
```bash
|
||||||
go build -v -ldflags "-s -w"
|
go build -v -ldflags "-s -w"
|
||||||
```
|
```
|
||||||
@ -538,5 +532,5 @@ glider -config CONFIG_PATH
|
|||||||
|
|
||||||
- [ipset](https://github.com/nadoo/ipset): netlink ipset package for Go.
|
- [ipset](https://github.com/nadoo/ipset): netlink ipset package for Go.
|
||||||
- [conflag](https://github.com/nadoo/conflag): a drop-in replacement for Go's standard flag package with config file support.
|
- [conflag](https://github.com/nadoo/conflag): a drop-in replacement for Go's standard flag package with config file support.
|
||||||
- [ArchLinux](https://archlinux.org/packages/extra/x86_64/glider): a great linux distribution with glider pre-built package.
|
- [ArchLinux](https://www.archlinux.org/packages/community/x86_64/glider): a great linux distribution with glider pre-built package.
|
||||||
- [urlencode](https://www.w3schools.com/tags/ref_urlencode.asp): you should encode special characters in scheme url. e.g., `@`->`%40`
|
- [urlencode](https://www.w3schools.com/tags/ref_urlencode.asp): you should encode special characters in scheme url. e.g., `@`->`%40`
|
||||||
|
@ -99,12 +99,12 @@ check=disable: disable health check`)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *scheme != "" {
|
if *scheme != "" {
|
||||||
fmt.Fprint(flag.Output(), proxy.Usage(*scheme))
|
fmt.Fprintf(flag.Output(), proxy.Usage(*scheme))
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *example {
|
if *example {
|
||||||
fmt.Fprint(flag.Output(), examples)
|
fmt.Fprintf(flag.Output(), examples)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,6 +249,6 @@ Examples:
|
|||||||
glider -listen udp://:53 -forward socks5://serverA:1080,udp://8.8.8.8:53
|
glider -listen udp://:53 -forward socks5://serverA:1080,udp://8.8.8.8:53
|
||||||
-udp tunnel: listen on :53 and forward all udp requests to 8.8.8.8:53 via remote socks5 server.
|
-udp tunnel: listen on :53 and forward all udp requests to 8.8.8.8:53 via remote socks5 server.
|
||||||
|
|
||||||
glider -verbose -dns=:53 -dnsserver=8.8.8.8:53 -forward socks5://serverA:1080 -dnsrecord=abc.com/1.2.3.4
|
glider -verbose -listen -dns=:53 -dnsserver=8.8.8.8:53 -forward socks5://serverA:1080 -dnsrecord=abc.com/1.2.3.4
|
||||||
-dns over proxy: listen on :53 as dns server, forward to 8.8.8.8:53 via socks5 server.
|
-dns over proxy: listen on :53 as dns server, forward to 8.8.8.8:53 via socks5 server.
|
||||||
`
|
`
|
||||||
|
@ -31,7 +31,7 @@ forward=ss://method:pass@1.1.1.1:8443
|
|||||||
# upstream forward proxy (forward chain)
|
# upstream forward proxy (forward chain)
|
||||||
forward=http://1.1.1.1:8080,socks5://2.2.2.2:1080
|
forward=http://1.1.1.1:8080,socks5://2.2.2.2:1080
|
||||||
|
|
||||||
# multiple upstream proxies forward strategy
|
# multiple upstream proxies forwad strategy
|
||||||
strategy=rr
|
strategy=rr
|
||||||
|
|
||||||
# forwarder health check
|
# forwarder health check
|
||||||
|
@ -82,8 +82,7 @@ Set server's nameserver to glider:
|
|||||||
echo nameserver 127.0.0.1 > /etc/resolv.conf
|
echo nameserver 127.0.0.1 > /etc/resolv.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Client settings
|
#### Client DNS settings
|
||||||
Use the linux server's ip as your gateway.
|
|
||||||
Use the linux server's ip as your dns server.
|
Use the linux server's ip as your dns server.
|
||||||
|
|
||||||
#### When client requesting to access http://example1.com (in office.rule), the whole process:
|
#### When client requesting to access http://example1.com (in office.rule), the whole process:
|
||||||
@ -92,10 +91,10 @@ DNS Resolving:
|
|||||||
2. upstream dns server choice: glider will lookup it's rule config and find out the dns server to use for this domain(matched "example1.com" in office.rule, so 208.67.222.222:53 will be chosen)
|
2. upstream dns server choice: glider will lookup it's rule config and find out the dns server to use for this domain(matched "example1.com" in office.rule, so 208.67.222.222:53 will be chosen)
|
||||||
3. glider uses the forwarder in office.rule to ask 208.67.222.222:53 for the resolve answers(dns over proxy).
|
3. glider uses the forwarder in office.rule to ask 208.67.222.222:53 for the resolve answers(dns over proxy).
|
||||||
4. glider updates it's office rule config, adds the resolved ip address to it.
|
4. glider updates it's office rule config, adds the resolved ip address to it.
|
||||||
5. glider adds the resolved ip into ipset "glider", and returns the dns answer to client.
|
5. glider adds the resolved ip into ipset "glider", and return the dns answer to client.
|
||||||
|
|
||||||
Destination Accessing:
|
Destination Accessing:
|
||||||
1. client sends http request to the resolved ip of example1.com.
|
1. client sends http request to the resolved ip of example1.com.
|
||||||
2. linux gateway server will get the request.
|
2. linux gateway server will get the request.
|
||||||
3. iptables matches the ip in ipset "glider" and redirect this request to :1081(glider)
|
3. iptabes matches the ip in ipset "glider" and redirect this request to :1081(glider)
|
||||||
4. glider finds the ip in office rule, and then choose a forwarder in office.rule to complete the request.
|
4. glider finds the ip in office rule, and then choose a forwarder in office.rule to complete the request.
|
||||||
|
@ -32,17 +32,16 @@ verbose=True
|
|||||||
# different protocols.
|
# different protocols.
|
||||||
|
|
||||||
# listen on 8443, serve as http/socks5 proxy on the same port.
|
# listen on 8443, serve as http/socks5 proxy on the same port.
|
||||||
# listen=:8443
|
listen=:8443
|
||||||
listen=127.0.0.1:8443
|
|
||||||
|
|
||||||
# listen on 8448 as a ss server.
|
# listen on 8448 as a ss server.
|
||||||
# listen=ss://AEAD_CHACHA20_POLY1305:pass@:8448
|
# listen=ss://AEAD_CHACHA20_POLY1305:pass@:8448
|
||||||
|
|
||||||
# listen on 8080 as a http proxy server.
|
# listen on 8080 as a http proxy server.
|
||||||
# listen=http://:8080
|
listen=http://:8080
|
||||||
|
|
||||||
# listen on 1080 as a socks5 proxy server.
|
# listen on 1080 as a socks5 proxy server.
|
||||||
# listen=socks5://:1080
|
listen=socks5://:1080
|
||||||
|
|
||||||
# listen on 1234 as vless proxy server.
|
# listen on 1234 as vless proxy server.
|
||||||
# listen=vless://uuid@:1234
|
# listen=vless://uuid@:1234
|
||||||
@ -80,10 +79,10 @@ listen=127.0.0.1:8443
|
|||||||
# listen=ws://:1234/path?host=domain.com,vless://707f20ea-d4b8-4d1d-8e2e-2c86cb2ed97a@?fallback=127.0.0.1:80
|
# listen=ws://:1234/path?host=domain.com,vless://707f20ea-d4b8-4d1d-8e2e-2c86cb2ed97a@?fallback=127.0.0.1:80
|
||||||
|
|
||||||
# trojan server
|
# trojan server
|
||||||
# listen=trojan://PASSWORD@:1234?cert=/path/to/cert&key=/path/to/key&fallback=127.0.0.1
|
# listen=trojan://PASSWORD:1234?cert=/path/to/cert&key=/path/to/key&fallback=127.0.0.1
|
||||||
|
|
||||||
# trojanc server (trojan without tls)
|
# trojanc server (trojan without tls)
|
||||||
# listen=trojanc://PASSWORD@:1234?fallback=127.0.0.1
|
# listen=trojanc://PASSWORD:1234?fallback=127.0.0.1
|
||||||
|
|
||||||
# FORWARDERS
|
# FORWARDERS
|
||||||
# ----------
|
# ----------
|
||||||
@ -140,7 +139,7 @@ listen=127.0.0.1:8443
|
|||||||
# forward=tls://server.com:443,vmess://5a146038-0b56-4e95-b1dc-5c6f5a32cd98
|
# forward=tls://server.com:443,vmess://5a146038-0b56-4e95-b1dc-5c6f5a32cd98
|
||||||
|
|
||||||
# vmess over websocket
|
# vmess over websocket
|
||||||
# forward=ws://1.1.1.1:80/path?host=server.com,vmess://chacha20-poly1305:5a146038-0b56-4e95-b1dc-5c6f5a32cd98@
|
# forward=ws://1.1.1.1:80/path?host=server.com,vmess://chacha20-poly1305:5a146038-0b56-4e95-b1dc-5c6f5a32cd98
|
||||||
|
|
||||||
# vmess over ws over tls
|
# vmess over ws over tls
|
||||||
# forward=tls://server.com:443,ws://,vmess://5a146038-0b56-4e95-b1dc-5c6f5a32cd98
|
# forward=tls://server.com:443,ws://,vmess://5a146038-0b56-4e95-b1dc-5c6f5a32cd98
|
||||||
@ -223,7 +222,7 @@ checkdisabledonly=false
|
|||||||
# we can specify different upstream dns server in rule file for different destinations.
|
# we can specify different upstream dns server in rule file for different destinations.
|
||||||
|
|
||||||
# Setup a dns forwarding server
|
# Setup a dns forwarding server
|
||||||
# dns=:53
|
dns=:53
|
||||||
|
|
||||||
# global remote dns server (you can specify different dns server in rule file)
|
# global remote dns server (you can specify different dns server in rule file)
|
||||||
dnsserver=8.8.8.8:53
|
dnsserver=8.8.8.8:53
|
||||||
@ -280,7 +279,7 @@ dnsrecord=www.example.com/2606:2800:220:1:248:1893:25c8:1946
|
|||||||
# Specify additional forward rules.
|
# Specify additional forward rules.
|
||||||
#
|
#
|
||||||
# specify rules folder, so all *.rule files under this folder will be parsed as rule file
|
# specify rules folder, so all *.rule files under this folder will be parsed as rule file
|
||||||
# rules-dir=rules.d
|
rules-dir=rules.d
|
||||||
#
|
#
|
||||||
# specify a rule file
|
# specify a rule file
|
||||||
#rulefile=office.rule
|
#rulefile=office.rule
|
||||||
|
@ -179,7 +179,7 @@ func (c *Client) exchange(qname string, reqBytes []byte, preferTCP bool) (
|
|||||||
|
|
||||||
ups := c.UpStream(qname)
|
ups := c.UpStream(qname)
|
||||||
server = ups.Server()
|
server = ups.Server()
|
||||||
for range ups.Len() {
|
for i := 0; i < ups.Len(); i++ {
|
||||||
var rc net.Conn
|
var rc net.Conn
|
||||||
rc, err = dialer.Dial(network, server)
|
rc, err = dialer.Dial(network, server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -5,14 +5,17 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UDPMaxLen is the max size of udp dns request.
|
// UDPMaxLen is the max size of udp dns request.
|
||||||
// https://www.dnsflagday.net/2020/
|
// https://www.rfc-editor.org/rfc/rfc1035#section-4.2.1
|
||||||
const UDPMaxLen = 1232
|
// Messages carried by UDP are restricted to 512 bytes (not counting the IP
|
||||||
|
// or UDP headers). Longer messages are truncated and the TC bit is set in
|
||||||
|
// the header.
|
||||||
|
const UDPMaxLen = 512
|
||||||
|
|
||||||
// HeaderLen is the length of dns msg header.
|
// HeaderLen is the length of dns msg header.
|
||||||
const HeaderLen = 12
|
const HeaderLen = 12
|
||||||
@ -146,7 +149,7 @@ func UnmarshalMessage(b []byte) (*Message, error) {
|
|||||||
|
|
||||||
// resp answers
|
// resp answers
|
||||||
rrIdx := HeaderLen + qLen
|
rrIdx := HeaderLen + qLen
|
||||||
for range int(m.Header.ANCOUNT) {
|
for i := 0; i < int(m.Header.ANCOUNT); i++ {
|
||||||
rr := &RR{}
|
rr := &RR{}
|
||||||
rrLen, err := m.UnmarshalRR(rrIdx, rr)
|
rrLen, err := m.UnmarshalRR(rrIdx, rr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -181,6 +184,7 @@ func UnmarshalMessage(b []byte) (*Message, error) {
|
|||||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||||
// | ARCOUNT |
|
// | ARCOUNT |
|
||||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||||
|
//
|
||||||
type Header struct {
|
type Header struct {
|
||||||
ID uint16
|
ID uint16
|
||||||
Bits uint16
|
Bits uint16
|
||||||
@ -210,11 +214,10 @@ func (h *Header) SetAncount(ancount int) {
|
|||||||
h.ANCOUNT = uint16(ancount)
|
h.ANCOUNT = uint16(ancount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not used now, but keep it for future use.
|
func (h *Header) setFlag(QR uint16, Opcode uint16, AA uint16,
|
||||||
// func (h *Header) setFlag(QR uint16, Opcode uint16, AA uint16,
|
TC uint16, RD uint16, RA uint16, RCODE uint16) {
|
||||||
// TC uint16, RD uint16, RA uint16, RCODE uint16) {
|
h.Bits = QR<<15 + Opcode<<11 + AA<<10 + TC<<9 + RD<<8 + RA<<7 + RCODE
|
||||||
// h.Bits = QR<<15 + Opcode<<11 + AA<<10 + TC<<9 + RD<<8 + RA<<7 + RCODE
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// MarshalTo marshals header struct to []byte and write to w.
|
// MarshalTo marshals header struct to []byte and write to w.
|
||||||
func (h *Header) MarshalTo(w io.Writer) (int, error) {
|
func (h *Header) MarshalTo(w io.Writer) (int, error) {
|
||||||
|
31
go.mod
31
go.mod
@ -1,29 +1,36 @@
|
|||||||
module github.com/nadoo/glider
|
module github.com/nadoo/glider
|
||||||
|
|
||||||
go 1.24
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
|
||||||
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d
|
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d
|
||||||
github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb
|
github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb
|
||||||
github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152
|
github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
|
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f
|
||||||
github.com/nadoo/conflag v0.3.1
|
github.com/nadoo/conflag v0.3.1
|
||||||
github.com/nadoo/ipset v0.5.0
|
github.com/nadoo/ipset v0.5.0
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.18
|
github.com/xtaci/kcp-go/v5 v5.6.1
|
||||||
golang.org/x/crypto v0.35.0
|
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88
|
||||||
golang.org/x/sys v0.30.0
|
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
|
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.12.4 // indirect
|
github.com/klauspost/reedsolomon v1.9.16 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 // indirect
|
||||||
|
github.com/mdlayher/raw v0.1.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/templexxx/cpu v0.1.1 // indirect
|
github.com/templexxx/cpu v0.0.10-0.20211111114238-98168dcec14a // indirect
|
||||||
github.com/templexxx/xorsimd v0.4.3 // indirect
|
github.com/templexxx/xorsimd v0.4.1 // indirect
|
||||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
|
||||||
golang.org/x/net v0.35.0 // indirect
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Replace dependency modules with local developing copy
|
||||||
|
// use `go list -m all` to confirm the final module used
|
||||||
|
// replace (
|
||||||
|
// github.com/nadoo/conflag => ../conflag
|
||||||
|
// )
|
||||||
|
143
go.sum
143
go.sum
@ -18,6 +18,7 @@ github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:ucvhdsUCE3TH0Lo
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@ -33,82 +34,154 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
|||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo=
|
||||||
github.com/klauspost/reedsolomon v1.12.4 h1:5aDr3ZGoJbgu/8+j45KtUJxzYm8k08JGtB9Wx1VQ4OA=
|
github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
|
||||||
github.com/klauspost/reedsolomon v1.12.4/go.mod h1:d3CzOMOt0JXGIFZm1StgkyF14EYr3xneR2rNWo7NcMU=
|
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
|
||||||
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
|
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
|
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||||
|
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
|
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
|
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
|
||||||
|
github.com/klauspost/reedsolomon v1.9.16 h1:mR0AwphBwqFv/I3B9AHtNKvzuowI1vrj8/3UX4XRmHA=
|
||||||
|
github.com/klauspost/reedsolomon v1.9.16/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk=
|
||||||
|
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
||||||
|
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
|
||||||
|
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
|
||||||
|
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||||
|
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||||
|
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||||
|
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||||
|
github.com/mdlayher/packet v1.0.0 h1:InhZJbdShQYt6XV2GPj5XHxChzOfhJJOMbvnGAmOfQ8=
|
||||||
|
github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU=
|
||||||
|
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||||
|
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
|
||||||
|
github.com/mdlayher/raw v0.1.0 h1:K4PFMVy+AFsp0Zdlrts7yNhxc/uXoPVHi9RzRvtZF2Y=
|
||||||
|
github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5s9Sg=
|
||||||
|
github.com/mdlayher/socket v0.2.1 h1:F2aaOwb53VsBE+ebRS9bLd7yPOfYUMC8lOODdCBDY6w=
|
||||||
|
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
|
||||||
|
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
|
||||||
github.com/nadoo/conflag v0.3.1 h1:4pHkLIz8PUsfg6ajNYRRSY3bt6m2LPsu6KOzn5uIXQw=
|
github.com/nadoo/conflag v0.3.1 h1:4pHkLIz8PUsfg6ajNYRRSY3bt6m2LPsu6KOzn5uIXQw=
|
||||||
github.com/nadoo/conflag v0.3.1/go.mod h1:dzFfDUpXdr2uS2oV+udpy5N2vfNOu/bFzjhX1WI52co=
|
github.com/nadoo/conflag v0.3.1/go.mod h1:dzFfDUpXdr2uS2oV+udpy5N2vfNOu/bFzjhX1WI52co=
|
||||||
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
|
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
|
||||||
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
|
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
|
||||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/templexxx/cpu v0.1.1 h1:isxHaxBXpYFWnk2DReuKkigaZyrjs2+9ypIdGP4h+HI=
|
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||||
github.com/templexxx/cpu v0.1.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||||
github.com/templexxx/xorsimd v0.4.3 h1:9AQTFHd7Bhk3dIT7Al2XeBX5DWOvsUPZCuhyAtNbHjU=
|
github.com/templexxx/cpu v0.0.10-0.20211111114238-98168dcec14a h1:f0GQM8LuKYnXdNLcAg+di6PULSlR5iQtZT3bDwDRiA0=
|
||||||
github.com/templexxx/xorsimd v0.4.3/go.mod h1:oZQcD6RFDisW2Am58dSAGwwL6rHjbzrlu25VDqfWkQg=
|
github.com/templexxx/cpu v0.0.10-0.20211111114238-98168dcec14a/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||||
|
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
|
||||||
|
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
|
||||||
|
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME=
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.18 h1:7oV4mc272pcnn39/13BB11Bx7hJM4ogMIEokJYVWn4g=
|
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
|
||||||
github.com/xtaci/kcp-go/v5 v5.6.18/go.mod h1:75S1AKYYzNUSXIv30h+jPKJYZUwqpfvLshu63nCNSOM=
|
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
|
||||||
|
github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
|
||||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4WwuI12DUd2to3nCYe2eayMIw=
|
||||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
||||||
|
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
|
||||||
|
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
@ -123,7 +196,9 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
|
|||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
2
main.go
2
main.go
@ -17,7 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "0.17.0"
|
version = "0.16.2"
|
||||||
config = parseConfig()
|
config = parseConfig()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ package pool
|
|||||||
import (
|
import (
|
||||||
"math/bits"
|
"math/bits"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -18,12 +17,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
for i := range num {
|
for i := 0; i < num; i++ {
|
||||||
size := 1 << i
|
size := 1 << i
|
||||||
sizes[i] = size
|
sizes[i] = size
|
||||||
pools[i].New = func() any {
|
pools[i].New = func() any {
|
||||||
buf := make([]byte, size)
|
return make([]byte, size)
|
||||||
return unsafe.SliceData(buf)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,10 +30,11 @@ func init() {
|
|||||||
// otherwise, this function will call make([]byte, size) directly.
|
// otherwise, this function will call make([]byte, size) directly.
|
||||||
func GetBuffer(size int) []byte {
|
func GetBuffer(size int) []byte {
|
||||||
if size >= 1 && size <= maxsize {
|
if size >= 1 && size <= maxsize {
|
||||||
i := bits.Len32(uint32(size - 1))
|
i := bits.Len32(uint32(size)) - 1
|
||||||
if p := pools[i].Get().(*byte); p != nil {
|
if sizes[i] < size {
|
||||||
return unsafe.Slice(p, 1<<i)[:size]
|
i += 1
|
||||||
}
|
}
|
||||||
|
return pools[i].Get().([]byte)[:size]
|
||||||
}
|
}
|
||||||
return make([]byte, size)
|
return make([]byte, size)
|
||||||
}
|
}
|
||||||
@ -43,9 +42,9 @@ func GetBuffer(size int) []byte {
|
|||||||
// PutBuffer puts a buffer into pool.
|
// PutBuffer puts a buffer into pool.
|
||||||
func PutBuffer(buf []byte) {
|
func PutBuffer(buf []byte) {
|
||||||
if size := cap(buf); size >= 1 && size <= maxsize {
|
if size := cap(buf); size >= 1 && size <= maxsize {
|
||||||
i := bits.Len32(uint32(size - 1))
|
i := bits.Len32(uint32(size)) - 1
|
||||||
if sizes[i] == size {
|
if sizes[i] == size {
|
||||||
pools[i].Put(unsafe.SliceData(buf))
|
pools[i].Put(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
pkg/smux/LICENSE
Normal file
21
pkg/smux/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2016-2017 Daniel Fu
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -1,25 +1,3 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016-2017 xtaci
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -60,18 +38,16 @@ const (
|
|||||||
|
|
||||||
// Frame defines a packet from or to be multiplexed into a single connection
|
// Frame defines a packet from or to be multiplexed into a single connection
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
ver byte // version
|
ver byte
|
||||||
cmd byte // command
|
cmd byte
|
||||||
sid uint32 // stream id
|
sid uint32
|
||||||
data []byte // payload
|
data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFrame creates a new frame with given version, command and stream id
|
|
||||||
func newFrame(version byte, cmd byte, sid uint32) Frame {
|
func newFrame(version byte, cmd byte, sid uint32) Frame {
|
||||||
return Frame{ver: version, cmd: cmd, sid: sid}
|
return Frame{ver: version, cmd: cmd, sid: sid}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rawHeader is a byte array representation of Frame header
|
|
||||||
type rawHeader [headerSize]byte
|
type rawHeader [headerSize]byte
|
||||||
|
|
||||||
func (h rawHeader) Version() byte {
|
func (h rawHeader) Version() byte {
|
||||||
@ -95,7 +71,6 @@ func (h rawHeader) String() string {
|
|||||||
h.Version(), h.Cmd(), h.StreamID(), h.Length())
|
h.Version(), h.Cmd(), h.StreamID(), h.Length())
|
||||||
}
|
}
|
||||||
|
|
||||||
// updHeader is a byte array representation of cmdUPD
|
|
||||||
type updHeader [szCmdUPD]byte
|
type updHeader [szCmdUPD]byte
|
||||||
|
|
||||||
func (h updHeader) Consumed() uint32 {
|
func (h updHeader) Consumed() uint32 {
|
||||||
|
@ -1,25 +1,7 @@
|
|||||||
// MIT License
|
// Package smux is a multiplexing library for Golang.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2016-2017 xtaci
|
// It relies on an underlying connection to provide reliability and ordering, such as TCP or KCP,
|
||||||
//
|
// and provides stream-oriented multiplexing over a single channel.
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
86
pkg/smux/mux_test.go
Normal file
86
pkg/smux/mux_test.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package smux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type buffer struct {
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *buffer) Close() error {
|
||||||
|
b.Buffer.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig(t *testing.T) {
|
||||||
|
VerifyConfig(DefaultConfig())
|
||||||
|
|
||||||
|
config := DefaultConfig()
|
||||||
|
config.KeepAliveInterval = 0
|
||||||
|
err := VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.KeepAliveInterval = 10
|
||||||
|
config.KeepAliveTimeout = 5
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxFrameSize = 0
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxFrameSize = 65536
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxReceiveBuffer = 0
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxStreamBuffer = 0
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config = DefaultConfig()
|
||||||
|
config.MaxStreamBuffer = 100
|
||||||
|
config.MaxReceiveBuffer = 99
|
||||||
|
err = VerifyConfig(config)
|
||||||
|
t.Log(err)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bts buffer
|
||||||
|
if _, err := Server(&bts, config); err == nil {
|
||||||
|
t.Fatal("server started with wrong config")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := Client(&bts, config); err == nil {
|
||||||
|
t.Fatal("client started with wrong config")
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +1,13 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016-2017 xtaci
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -38,52 +17,32 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
defaultAcceptBacklog = 1024
|
defaultAcceptBacklog = 1024
|
||||||
maxShaperSize = 1024
|
|
||||||
openCloseTimeout = 30 * time.Second // Timeout for opening/closing streams
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CLASSID represents the class of a frame
|
|
||||||
type CLASSID int
|
|
||||||
|
|
||||||
const (
|
|
||||||
CLSCTRL CLASSID = iota // prioritized control signal
|
|
||||||
CLSDATA
|
|
||||||
)
|
|
||||||
|
|
||||||
// timeoutError representing timeouts for operations such as accept, read and write
|
|
||||||
//
|
|
||||||
// To better cooperate with the standard library, timeoutError should implement the standard library's `net.Error`.
|
|
||||||
//
|
|
||||||
// For example, using smux to implement net.Listener and work with http.Server, the keep-alive connection (*smux.Stream) will be unexpectedly closed.
|
|
||||||
// For more details, see https://github.com/xtaci/smux/pull/99.
|
|
||||||
type timeoutError struct{}
|
|
||||||
|
|
||||||
func (timeoutError) Error() string { return "timeout" }
|
|
||||||
func (timeoutError) Temporary() bool { return true }
|
|
||||||
func (timeoutError) Timeout() bool { return true }
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidProtocol = errors.New("invalid protocol")
|
ErrInvalidProtocol = errors.New("invalid protocol")
|
||||||
ErrConsumed = errors.New("peer consumed more than sent")
|
ErrConsumed = errors.New("peer consumed more than sent")
|
||||||
ErrGoAway = errors.New("stream id overflows, should start a new connection")
|
ErrGoAway = errors.New("stream id overflows, should start a new connection")
|
||||||
ErrTimeout net.Error = &timeoutError{}
|
// ErrTimeout = errors.New("timeout")
|
||||||
|
ErrTimeout = fmt.Errorf("smux: %w", os.ErrDeadlineExceeded)
|
||||||
ErrWouldBlock = errors.New("operation would block on IO")
|
ErrWouldBlock = errors.New("operation would block on IO")
|
||||||
)
|
)
|
||||||
|
|
||||||
// writeRequest represents a request to write a frame
|
|
||||||
type writeRequest struct {
|
type writeRequest struct {
|
||||||
class CLASSID
|
prio uint32
|
||||||
frame Frame
|
frame Frame
|
||||||
seq uint32
|
|
||||||
result chan writeResult
|
result chan writeResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeResult represents the result of a write request
|
|
||||||
type writeResult struct {
|
type writeResult struct {
|
||||||
n int
|
n int
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type buffersWriter interface {
|
||||||
|
WriteBuffers(v [][]byte) (n int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
// Session defines a multiplexed connection for streams
|
// Session defines a multiplexed connection for streams
|
||||||
type Session struct {
|
type Session struct {
|
||||||
conn io.ReadWriteCloser
|
conn io.ReadWriteCloser
|
||||||
@ -95,7 +54,7 @@ type Session struct {
|
|||||||
bucket int32 // token bucket
|
bucket int32 // token bucket
|
||||||
bucketNotify chan struct{} // used for waiting for tokens
|
bucketNotify chan struct{} // used for waiting for tokens
|
||||||
|
|
||||||
streams map[uint32]*stream // all streams in this session
|
streams map[uint32]*Stream // all streams in this session
|
||||||
streamLock sync.Mutex // locks streams
|
streamLock sync.Mutex // locks streams
|
||||||
|
|
||||||
die chan struct{} // flag session has died
|
die chan struct{} // flag session has died
|
||||||
@ -114,7 +73,7 @@ type Session struct {
|
|||||||
chProtoError chan struct{}
|
chProtoError chan struct{}
|
||||||
protoErrorOnce sync.Once
|
protoErrorOnce sync.Once
|
||||||
|
|
||||||
chAccepts chan *stream
|
chAccepts chan *Stream
|
||||||
|
|
||||||
dataReady int32 // flag data has arrived
|
dataReady int32 // flag data has arrived
|
||||||
|
|
||||||
@ -122,7 +81,6 @@ type Session struct {
|
|||||||
|
|
||||||
deadline atomic.Value
|
deadline atomic.Value
|
||||||
|
|
||||||
requestID uint32 // Monotonic increasing write request ID
|
|
||||||
shaper chan writeRequest // a shaper for writing
|
shaper chan writeRequest // a shaper for writing
|
||||||
writes chan writeRequest
|
writes chan writeRequest
|
||||||
}
|
}
|
||||||
@ -132,8 +90,8 @@ func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
|
|||||||
s.die = make(chan struct{})
|
s.die = make(chan struct{})
|
||||||
s.conn = conn
|
s.conn = conn
|
||||||
s.config = config
|
s.config = config
|
||||||
s.streams = make(map[uint32]*stream)
|
s.streams = make(map[uint32]*Stream)
|
||||||
s.chAccepts = make(chan *stream, defaultAcceptBacklog)
|
s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
|
||||||
s.bucket = int32(config.MaxReceiveBuffer)
|
s.bucket = int32(config.MaxReceiveBuffer)
|
||||||
s.bucketNotify = make(chan struct{}, 1)
|
s.bucketNotify = make(chan struct{}, 1)
|
||||||
s.shaper = make(chan writeRequest)
|
s.shaper = make(chan writeRequest)
|
||||||
@ -181,7 +139,7 @@ func (s *Session) OpenStream() (*Stream, error) {
|
|||||||
|
|
||||||
stream := newStream(sid, s.config.MaxFrameSize, s)
|
stream := newStream(sid, s.config.MaxFrameSize, s)
|
||||||
|
|
||||||
if _, err := s.writeControlFrame(newFrame(byte(s.config.Version), cmdSYN, sid)); err != nil {
|
if _, err := s.writeFrame(newFrame(byte(s.config.Version), cmdSYN, sid)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,14 +154,7 @@ func (s *Session) OpenStream() (*Stream, error) {
|
|||||||
return nil, io.ErrClosedPipe
|
return nil, io.ErrClosedPipe
|
||||||
default:
|
default:
|
||||||
s.streams[sid] = stream
|
s.streams[sid] = stream
|
||||||
wrapper := &Stream{stream: stream}
|
return stream, nil
|
||||||
// NOTE(x): disabled finalizer for issue #997
|
|
||||||
/*
|
|
||||||
runtime.SetFinalizer(wrapper, func(s *Stream) {
|
|
||||||
s.Close()
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
return wrapper, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,11 +175,7 @@ func (s *Session) AcceptStream() (*Stream, error) {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
case stream := <-s.chAccepts:
|
case stream := <-s.chAccepts:
|
||||||
wrapper := &Stream{stream: stream}
|
return stream, nil
|
||||||
runtime.SetFinalizer(wrapper, func(s *Stream) {
|
|
||||||
s.Close()
|
|
||||||
})
|
|
||||||
return wrapper, nil
|
|
||||||
case <-deadline:
|
case <-deadline:
|
||||||
return nil, ErrTimeout
|
return nil, ErrTimeout
|
||||||
case <-s.chSocketReadError:
|
case <-s.chSocketReadError:
|
||||||
@ -265,12 +212,6 @@ func (s *Session) Close() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloseChan can be used by someone who wants to be notified immediately when this
|
|
||||||
// session is closed
|
|
||||||
func (s *Session) CloseChan() <-chan struct{} {
|
|
||||||
return s.die
|
|
||||||
}
|
|
||||||
|
|
||||||
// notifyBucket notifies recvLoop that bucket is available
|
// notifyBucket notifies recvLoop that bucket is available
|
||||||
func (s *Session) notifyBucket() {
|
func (s *Session) notifyBucket() {
|
||||||
select {
|
select {
|
||||||
@ -350,15 +291,12 @@ func (s *Session) RemoteAddr() net.Addr {
|
|||||||
// notify the session that a stream has closed
|
// notify the session that a stream has closed
|
||||||
func (s *Session) streamClosed(sid uint32) {
|
func (s *Session) streamClosed(sid uint32) {
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if stream, ok := s.streams[sid]; ok {
|
if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
|
||||||
n := stream.recycleTokens()
|
|
||||||
if n > 0 { // return remaining tokens to the bucket
|
|
||||||
if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
|
if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
|
||||||
s.notifyBucket()
|
s.notifyBucket()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(s.streams, sid)
|
delete(s.streams, sid)
|
||||||
}
|
|
||||||
s.streamLock.Unlock()
|
s.streamLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,7 +331,7 @@ func (s *Session) recvLoop() {
|
|||||||
sid := hdr.StreamID()
|
sid := hdr.StreamID()
|
||||||
switch hdr.Cmd() {
|
switch hdr.Cmd() {
|
||||||
case cmdNOP:
|
case cmdNOP:
|
||||||
case cmdSYN: // stream opening
|
case cmdSYN:
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if _, ok := s.streams[sid]; !ok {
|
if _, ok := s.streams[sid]; !ok {
|
||||||
stream := newStream(sid, s.config.MaxFrameSize, s)
|
stream := newStream(sid, s.config.MaxFrameSize, s)
|
||||||
@ -404,26 +342,22 @@ func (s *Session) recvLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.streamLock.Unlock()
|
s.streamLock.Unlock()
|
||||||
case cmdFIN: // stream closing
|
case cmdFIN:
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if stream, ok := s.streams[sid]; ok {
|
if stream, ok := s.streams[sid]; ok {
|
||||||
stream.fin()
|
stream.fin()
|
||||||
stream.notifyReadEvent()
|
stream.notifyReadEvent()
|
||||||
}
|
}
|
||||||
s.streamLock.Unlock()
|
s.streamLock.Unlock()
|
||||||
case cmdPSH: // data frame
|
case cmdPSH:
|
||||||
if hdr.Length() > 0 {
|
if hdr.Length() > 0 {
|
||||||
newbuf := pool.GetBuffer(int(hdr.Length()))
|
newbuf := pool.GetBuffer(int(hdr.Length()))
|
||||||
if written, err := io.ReadFull(s.conn, newbuf); err == nil {
|
if written, err := io.ReadFull(s.conn, newbuf); err == nil {
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if stream, ok := s.streams[sid]; ok {
|
if stream, ok := s.streams[sid]; ok {
|
||||||
stream.pushBytes(newbuf)
|
stream.pushBytes(newbuf)
|
||||||
// a stream used some token
|
|
||||||
atomic.AddInt32(&s.bucket, -int32(written))
|
atomic.AddInt32(&s.bucket, -int32(written))
|
||||||
stream.notifyReadEvent()
|
stream.notifyReadEvent()
|
||||||
} else {
|
|
||||||
// data directed to a missing/closed stream, recycle the buffer immediately.
|
|
||||||
pool.PutBuffer(newbuf)
|
|
||||||
}
|
}
|
||||||
s.streamLock.Unlock()
|
s.streamLock.Unlock()
|
||||||
} else {
|
} else {
|
||||||
@ -431,7 +365,7 @@ func (s *Session) recvLoop() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case cmdUPD: // a window update signal
|
case cmdUPD:
|
||||||
if _, err := io.ReadFull(s.conn, updHdr[:]); err == nil {
|
if _, err := io.ReadFull(s.conn, updHdr[:]); err == nil {
|
||||||
s.streamLock.Lock()
|
s.streamLock.Lock()
|
||||||
if stream, ok := s.streams[sid]; ok {
|
if stream, ok := s.streams[sid]; ok {
|
||||||
@ -453,7 +387,6 @@ func (s *Session) recvLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// keepalive sends NOP frame to peer to keep the connection alive, and detect dead peers
|
|
||||||
func (s *Session) keepalive() {
|
func (s *Session) keepalive() {
|
||||||
tickerPing := time.NewTicker(s.config.KeepAliveInterval)
|
tickerPing := time.NewTicker(s.config.KeepAliveInterval)
|
||||||
tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
|
tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
|
||||||
@ -462,7 +395,7 @@ func (s *Session) keepalive() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-tickerPing.C:
|
case <-tickerPing.C:
|
||||||
s.writeFrameInternal(newFrame(byte(s.config.Version), cmdNOP, 0), tickerPing.C, CLSCTRL)
|
s.writeFrameInternal(newFrame(byte(s.config.Version), cmdNOP, 0), tickerPing.C, 0)
|
||||||
s.notifyBucket() // force a signal to the recvLoop
|
s.notifyBucket() // force a signal to the recvLoop
|
||||||
case <-tickerTimeout.C:
|
case <-tickerTimeout.C:
|
||||||
if !atomic.CompareAndSwapInt32(&s.dataReady, 1, 0) {
|
if !atomic.CompareAndSwapInt32(&s.dataReady, 1, 0) {
|
||||||
@ -479,16 +412,13 @@ func (s *Session) keepalive() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shaperLoop implements a priority queue for write requests,
|
// shaper shapes the sending sequence among streams
|
||||||
// some control messages are prioritized over data messages
|
|
||||||
func (s *Session) shaperLoop() {
|
func (s *Session) shaperLoop() {
|
||||||
var reqs shaperHeap
|
var reqs shaperHeap
|
||||||
var next writeRequest
|
var next writeRequest
|
||||||
var chWrite chan writeRequest
|
var chWrite chan writeRequest
|
||||||
var chShaper chan writeRequest
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// chWrite is not available until it has packet to send
|
|
||||||
if len(reqs) > 0 {
|
if len(reqs) > 0 {
|
||||||
chWrite = s.writes
|
chWrite = s.writes
|
||||||
next = heap.Pop(&reqs).(writeRequest)
|
next = heap.Pop(&reqs).(writeRequest)
|
||||||
@ -496,22 +426,10 @@ func (s *Session) shaperLoop() {
|
|||||||
chWrite = nil
|
chWrite = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// control heap size, chShaper is not available until packets are less than maximum allowed
|
|
||||||
if len(reqs) >= maxShaperSize {
|
|
||||||
chShaper = nil
|
|
||||||
} else {
|
|
||||||
chShaper = s.shaper
|
|
||||||
}
|
|
||||||
|
|
||||||
// assertion on non nil
|
|
||||||
if chShaper == nil && chWrite == nil {
|
|
||||||
panic("both channel are nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-s.die:
|
case <-s.die:
|
||||||
return
|
return
|
||||||
case r := <-chShaper:
|
case r := <-s.shaper:
|
||||||
if chWrite != nil { // next is valid, reshape
|
if chWrite != nil { // next is valid, reshape
|
||||||
heap.Push(&reqs, next)
|
heap.Push(&reqs, next)
|
||||||
}
|
}
|
||||||
@ -521,17 +439,13 @@ func (s *Session) shaperLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendLoop sends frames to the underlying connection
|
|
||||||
func (s *Session) sendLoop() {
|
func (s *Session) sendLoop() {
|
||||||
var buf []byte
|
var buf []byte
|
||||||
var n int
|
var n int
|
||||||
var err error
|
var err error
|
||||||
var vec [][]byte // vector for writeBuffers
|
var vec [][]byte // vector for writeBuffers
|
||||||
|
|
||||||
bw, ok := s.conn.(interface {
|
bw, ok := s.conn.(buffersWriter)
|
||||||
WriteBuffers(v [][]byte) (n int, err error)
|
|
||||||
})
|
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
buf = make([]byte, headerSize)
|
buf = make([]byte, headerSize)
|
||||||
vec = make([][]byte, 2)
|
vec = make([][]byte, 2)
|
||||||
@ -549,7 +463,6 @@ func (s *Session) sendLoop() {
|
|||||||
binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
|
binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
|
||||||
binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)
|
binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)
|
||||||
|
|
||||||
// support for scatter-gather I/O
|
|
||||||
if len(vec) > 0 {
|
if len(vec) > 0 {
|
||||||
vec[0] = buf[:headerSize]
|
vec[0] = buf[:headerSize]
|
||||||
vec[1] = request.frame.data
|
vec[1] = request.frame.data
|
||||||
@ -581,21 +494,17 @@ func (s *Session) sendLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeControlFrame writes the control frame to the underlying connection
|
// writeFrame writes the frame to the underlying connection
|
||||||
// and returns the number of bytes written if successful
|
// and returns the number of bytes written if successful
|
||||||
func (s *Session) writeControlFrame(f Frame) (n int, err error) {
|
func (s *Session) writeFrame(f Frame) (n int, err error) {
|
||||||
timer := time.NewTimer(openCloseTimeout)
|
return s.writeFrameInternal(f, nil, 0)
|
||||||
defer timer.Stop()
|
|
||||||
|
|
||||||
return s.writeFrameInternal(f, timer.C, CLSCTRL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal writeFrame version to support deadline used in keepalive
|
// internal writeFrame version to support deadline used in keepalive
|
||||||
func (s *Session) writeFrameInternal(f Frame, deadline <-chan time.Time, class CLASSID) (int, error) {
|
func (s *Session) writeFrameInternal(f Frame, deadline <-chan time.Time, prio uint32) (int, error) {
|
||||||
req := writeRequest{
|
req := writeRequest{
|
||||||
class: class,
|
prio: prio,
|
||||||
frame: f,
|
frame: f,
|
||||||
seq: atomic.AddUint32(&s.requestID, 1),
|
|
||||||
result: make(chan writeResult, 1),
|
result: make(chan writeResult, 1),
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
1090
pkg/smux/session_test.go
Normal file
1090
pkg/smux/session_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,53 +1,17 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016-2017 xtaci
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
// _itimediff returns the time difference between two uint32 values.
|
|
||||||
// The result is a signed 32-bit integer representing the difference between 'later' and 'earlier'.
|
|
||||||
func _itimediff(later, earlier uint32) int32 {
|
func _itimediff(later, earlier uint32) int32 {
|
||||||
return (int32)(later - earlier)
|
return (int32)(later - earlier)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shaperHeap is a min-heap of writeRequest.
|
|
||||||
// It orders writeRequests by class first, then by sequence number within the same class.
|
|
||||||
type shaperHeap []writeRequest
|
type shaperHeap []writeRequest
|
||||||
|
|
||||||
func (h shaperHeap) Len() int { return len(h) }
|
func (h shaperHeap) Len() int { return len(h) }
|
||||||
|
func (h shaperHeap) Less(i, j int) bool { return _itimediff(h[j].prio, h[i].prio) > 0 }
|
||||||
// Less determines the ordering of elements in the heap.
|
|
||||||
// Requests are ordered by their class first. If two requests have the same class,
|
|
||||||
// they are ordered by their sequence numbers.
|
|
||||||
func (h shaperHeap) Less(i, j int) bool {
|
|
||||||
if h[i].class != h[j].class {
|
|
||||||
return h[i].class < h[j].class
|
|
||||||
}
|
|
||||||
return _itimediff(h[j].seq, h[i].seq) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h shaperHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
func (h shaperHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||||
func (h *shaperHeap) Push(x interface{}) { *h = append(*h, x.(writeRequest)) }
|
func (h *shaperHeap) Push(x any) { *h = append(*h, x.(writeRequest)) }
|
||||||
|
|
||||||
func (h *shaperHeap) Pop() interface{} {
|
func (h *shaperHeap) Pop() any {
|
||||||
old := *h
|
old := *h
|
||||||
n := len(old)
|
n := len(old)
|
||||||
x := old[n-1]
|
x := old[n-1]
|
||||||
|
32
pkg/smux/shaper_test.go
Normal file
32
pkg/smux/shaper_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package smux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/heap"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShaper(t *testing.T) {
|
||||||
|
w1 := writeRequest{prio: 10}
|
||||||
|
w2 := writeRequest{prio: 10}
|
||||||
|
w3 := writeRequest{prio: 20}
|
||||||
|
w4 := writeRequest{prio: 100}
|
||||||
|
w5 := writeRequest{prio: (1 << 32) - 1}
|
||||||
|
|
||||||
|
var reqs shaperHeap
|
||||||
|
heap.Push(&reqs, w5)
|
||||||
|
heap.Push(&reqs, w4)
|
||||||
|
heap.Push(&reqs, w3)
|
||||||
|
heap.Push(&reqs, w2)
|
||||||
|
heap.Push(&reqs, w1)
|
||||||
|
|
||||||
|
var lastPrio = reqs[0].prio
|
||||||
|
for len(reqs) > 0 {
|
||||||
|
w := heap.Pop(&reqs).(writeRequest)
|
||||||
|
if int32(w.prio-lastPrio) < 0 {
|
||||||
|
t.Fatal("incorrect shaper priority")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("prio:", w.prio)
|
||||||
|
lastPrio = w.prio
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,3 @@
|
|||||||
// MIT License
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016-2017 xtaci
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
package smux
|
package smux
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -33,41 +11,36 @@ import (
|
|||||||
"github.com/nadoo/glider/pkg/pool"
|
"github.com/nadoo/glider/pkg/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// wrapper for GC
|
|
||||||
type Stream struct {
|
|
||||||
*stream
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream implements net.Conn
|
// Stream implements net.Conn
|
||||||
type stream struct {
|
type Stream struct {
|
||||||
id uint32 // Stream identifier
|
id uint32
|
||||||
sess *Session
|
sess *Session
|
||||||
|
|
||||||
buffers [][]byte // the sequential buffers of stream
|
buffers [][]byte
|
||||||
heads [][]byte // slice heads of the buffers above, kept for recycle
|
heads [][]byte // slice heads kept for recycle
|
||||||
|
|
||||||
bufferLock sync.Mutex // Mutex to protect access to buffers
|
bufferLock sync.Mutex
|
||||||
frameSize int // Maximum frame size for the stream
|
frameSize int
|
||||||
|
|
||||||
// notify a read event
|
// notify a read event
|
||||||
chReadEvent chan struct{}
|
chReadEvent chan struct{}
|
||||||
|
|
||||||
// flag the stream has closed
|
// flag the stream has closed
|
||||||
die chan struct{}
|
die chan struct{}
|
||||||
dieOnce sync.Once // Ensures die channel is closed only once
|
dieOnce sync.Once
|
||||||
|
|
||||||
// FIN command
|
// FIN command
|
||||||
chFinEvent chan struct{}
|
chFinEvent chan struct{}
|
||||||
finEventOnce sync.Once // Ensures chFinEvent is closed only once
|
finEventOnce sync.Once
|
||||||
|
|
||||||
// deadlines
|
// deadlines
|
||||||
readDeadline atomic.Value
|
readDeadline atomic.Value
|
||||||
writeDeadline atomic.Value
|
writeDeadline atomic.Value
|
||||||
|
|
||||||
// per stream sliding window control
|
// per stream sliding window control
|
||||||
numRead uint32 // count num of bytes read
|
numRead uint32 // number of consumed bytes
|
||||||
numWritten uint32 // count num of bytes written
|
numWritten uint32 // count num of bytes written
|
||||||
incr uint32 // bytes sent since last window update
|
incr uint32 // counting for sending
|
||||||
|
|
||||||
// UPD command
|
// UPD command
|
||||||
peerConsumed uint32 // num of bytes the peer has consumed
|
peerConsumed uint32 // num of bytes the peer has consumed
|
||||||
@ -75,9 +48,9 @@ type stream struct {
|
|||||||
chUpdate chan struct{} // notify of remote data consuming and window update
|
chUpdate chan struct{} // notify of remote data consuming and window update
|
||||||
}
|
}
|
||||||
|
|
||||||
// newStream initializes and returns a new Stream.
|
// newStream initiates a Stream struct
|
||||||
func newStream(id uint32, frameSize int, sess *Session) *stream {
|
func newStream(id uint32, frameSize int, sess *Session) *Stream {
|
||||||
s := new(stream)
|
s := new(Stream)
|
||||||
s.id = id
|
s.id = id
|
||||||
s.chReadEvent = make(chan struct{}, 1)
|
s.chReadEvent = make(chan struct{}, 1)
|
||||||
s.chUpdate = make(chan struct{}, 1)
|
s.chUpdate = make(chan struct{}, 1)
|
||||||
@ -86,17 +59,16 @@ func newStream(id uint32, frameSize int, sess *Session) *stream {
|
|||||||
s.die = make(chan struct{})
|
s.die = make(chan struct{})
|
||||||
s.chFinEvent = make(chan struct{})
|
s.chFinEvent = make(chan struct{})
|
||||||
s.peerWindow = initialPeerWindow // set to initial window size
|
s.peerWindow = initialPeerWindow // set to initial window size
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns the stream's unique identifier.
|
// ID returns the unique stream ID.
|
||||||
func (s *stream) ID() uint32 {
|
func (s *Stream) ID() uint32 {
|
||||||
return s.id
|
return s.id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read reads data from the stream into the provided buffer.
|
// Read implements net.Conn
|
||||||
func (s *stream) Read(b []byte) (n int, err error) {
|
func (s *Stream) Read(b []byte) (n int, err error) {
|
||||||
for {
|
for {
|
||||||
n, err = s.tryRead(b)
|
n, err = s.tryRead(b)
|
||||||
if err == ErrWouldBlock {
|
if err == ErrWouldBlock {
|
||||||
@ -109,8 +81,8 @@ func (s *stream) Read(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryRead attempts to read data from the stream without blocking.
|
// tryRead is the nonblocking version of Read
|
||||||
func (s *stream) tryRead(b []byte) (n int, err error) {
|
func (s *Stream) tryRead(b []byte) (n int, err error) {
|
||||||
if s.sess.config.Version == 2 {
|
if s.sess.config.Version == 2 {
|
||||||
return s.tryReadv2(b)
|
return s.tryReadv2(b)
|
||||||
}
|
}
|
||||||
@ -119,7 +91,6 @@ func (s *stream) tryRead(b []byte) (n int, err error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// A critical section to copy data from buffers to
|
|
||||||
s.bufferLock.Lock()
|
s.bufferLock.Lock()
|
||||||
if len(s.buffers) > 0 {
|
if len(s.buffers) > 0 {
|
||||||
n = copy(b, s.buffers[0])
|
n = copy(b, s.buffers[0])
|
||||||
@ -147,8 +118,7 @@ func (s *stream) tryRead(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryReadv2 is the non-blocking version of Read for version 2 streams.
|
func (s *Stream) tryReadv2(b []byte) (n int, err error) {
|
||||||
func (s *stream) tryReadv2(b []byte) (n int, err error) {
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@ -169,31 +139,25 @@ func (s *stream) tryReadv2(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
// in an ideal environment:
|
// in an ideal environment:
|
||||||
// if more than half of buffer has consumed, send read ack to peer
|
// if more than half of buffer has consumed, send read ack to peer
|
||||||
// based on round-trip time of ACK, continous flowing data
|
// based on round-trip time of ACK, continuous flowing data
|
||||||
// won't slow down due to waiting for ACK, as long as the
|
// won't slow down because of waiting for ACK, as long as the
|
||||||
// consumer keeps on reading data.
|
// consumer keeps on reading data
|
||||||
//
|
// s.numRead == n also notify window at the first read
|
||||||
// s.numRead == n implies that it's the initial reading
|
|
||||||
s.numRead += uint32(n)
|
s.numRead += uint32(n)
|
||||||
s.incr += uint32(n)
|
s.incr += uint32(n)
|
||||||
|
|
||||||
// for initial reading, send window update
|
|
||||||
if s.incr >= uint32(s.sess.config.MaxStreamBuffer/2) || s.numRead == uint32(n) {
|
if s.incr >= uint32(s.sess.config.MaxStreamBuffer/2) || s.numRead == uint32(n) {
|
||||||
notifyConsumed = s.numRead
|
notifyConsumed = s.numRead
|
||||||
s.incr = 0 // reset couting for next window update
|
s.incr = 0
|
||||||
}
|
}
|
||||||
s.bufferLock.Unlock()
|
s.bufferLock.Unlock()
|
||||||
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
s.sess.returnTokens(n)
|
s.sess.returnTokens(n)
|
||||||
|
|
||||||
// send window update if necessary
|
|
||||||
if notifyConsumed > 0 {
|
if notifyConsumed > 0 {
|
||||||
err := s.sendWindowUpdate(notifyConsumed)
|
err := s.sendWindowUpdate(notifyConsumed)
|
||||||
return n, err
|
return n, err
|
||||||
} else {
|
|
||||||
return n, nil
|
|
||||||
}
|
}
|
||||||
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@ -205,12 +169,7 @@ func (s *stream) tryReadv2(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteTo implements io.WriteTo
|
// WriteTo implements io.WriteTo
|
||||||
// WriteTo writes data to w until there's no more data to write or when an error occurs.
|
func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
// The return value n is the number of bytes written. Any error encountered during the write is also returned.
|
|
||||||
// WriteTo calls Write in a loop until there is no more data to write or when an error occurs.
|
|
||||||
// If the underlying stream is a v2 stream, it will send window update to peer when necessary.
|
|
||||||
// If the underlying stream is a v1 stream, it will not send window update to peer.
|
|
||||||
func (s *stream) WriteTo(w io.Writer) (n int64, err error) {
|
|
||||||
if s.sess.config.Version == 2 {
|
if s.sess.config.Version == 2 {
|
||||||
return s.writeTov2(w)
|
return s.writeTov2(w)
|
||||||
}
|
}
|
||||||
@ -227,7 +186,6 @@ func (s *stream) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
|
|
||||||
if buf != nil {
|
if buf != nil {
|
||||||
nw, ew := w.Write(buf)
|
nw, ew := w.Write(buf)
|
||||||
// NOTE: WriteTo is a reader, so we need to return tokens here
|
|
||||||
s.sess.returnTokens(len(buf))
|
s.sess.returnTokens(len(buf))
|
||||||
pool.PutBuffer(buf)
|
pool.PutBuffer(buf)
|
||||||
if nw > 0 {
|
if nw > 0 {
|
||||||
@ -243,8 +201,7 @@ func (s *stream) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check comments in WriteTo
|
func (s *Stream) writeTov2(w io.Writer) (n int64, err error) {
|
||||||
func (s *stream) writeTov2(w io.Writer) (n int64, err error) {
|
|
||||||
for {
|
for {
|
||||||
var notifyConsumed uint32
|
var notifyConsumed uint32
|
||||||
var buf []byte
|
var buf []byte
|
||||||
@ -264,7 +221,6 @@ func (s *stream) writeTov2(w io.Writer) (n int64, err error) {
|
|||||||
|
|
||||||
if buf != nil {
|
if buf != nil {
|
||||||
nw, ew := w.Write(buf)
|
nw, ew := w.Write(buf)
|
||||||
// NOTE: WriteTo is a reader, so we need to return tokens here
|
|
||||||
s.sess.returnTokens(len(buf))
|
s.sess.returnTokens(len(buf))
|
||||||
pool.PutBuffer(buf)
|
pool.PutBuffer(buf)
|
||||||
if nw > 0 {
|
if nw > 0 {
|
||||||
@ -286,8 +242,7 @@ func (s *stream) writeTov2(w io.Writer) (n int64, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendWindowUpdate sends a window update frame to the peer.
|
func (s *Stream) sendWindowUpdate(consumed uint32) error {
|
||||||
func (s *stream) sendWindowUpdate(consumed uint32) error {
|
|
||||||
var timer *time.Timer
|
var timer *time.Timer
|
||||||
var deadline <-chan time.Time
|
var deadline <-chan time.Time
|
||||||
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
||||||
@ -301,12 +256,11 @@ func (s *stream) sendWindowUpdate(consumed uint32) error {
|
|||||||
binary.LittleEndian.PutUint32(hdr[:], consumed)
|
binary.LittleEndian.PutUint32(hdr[:], consumed)
|
||||||
binary.LittleEndian.PutUint32(hdr[4:], uint32(s.sess.config.MaxStreamBuffer))
|
binary.LittleEndian.PutUint32(hdr[4:], uint32(s.sess.config.MaxStreamBuffer))
|
||||||
frame.data = hdr[:]
|
frame.data = hdr[:]
|
||||||
_, err := s.sess.writeFrameInternal(frame, deadline, CLSCTRL)
|
_, err := s.sess.writeFrameInternal(frame, deadline, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitRead blocks until a read event occurs or a deadline is reached.
|
func (s *Stream) waitRead() error {
|
||||||
func (s *stream) waitRead() error {
|
|
||||||
var timer *time.Timer
|
var timer *time.Timer
|
||||||
var deadline <-chan time.Time
|
var deadline <-chan time.Time
|
||||||
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
|
||||||
@ -316,15 +270,9 @@ func (s *stream) waitRead() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-s.chReadEvent: // notify some data has arrived, or closed
|
case <-s.chReadEvent:
|
||||||
return nil
|
return nil
|
||||||
case <-s.chFinEvent:
|
case <-s.chFinEvent:
|
||||||
// BUGFIX(xtaci): Fix for https://github.com/xtaci/smux/issues/82
|
|
||||||
s.bufferLock.Lock()
|
|
||||||
defer s.bufferLock.Unlock()
|
|
||||||
if len(s.buffers) > 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return io.EOF
|
return io.EOF
|
||||||
case <-s.sess.chSocketReadError:
|
case <-s.sess.chSocketReadError:
|
||||||
return s.sess.socketReadError.Load().(error)
|
return s.sess.socketReadError.Load().(error)
|
||||||
@ -342,7 +290,7 @@ func (s *stream) waitRead() error {
|
|||||||
//
|
//
|
||||||
// Note that the behavior when multiple goroutines write concurrently is not deterministic,
|
// Note that the behavior when multiple goroutines write concurrently is not deterministic,
|
||||||
// frames may interleave in random way.
|
// frames may interleave in random way.
|
||||||
func (s *stream) Write(b []byte) (n int, err error) {
|
func (s *Stream) Write(b []byte) (n int, err error) {
|
||||||
if s.sess.config.Version == 2 {
|
if s.sess.config.Version == 2 {
|
||||||
return s.writeV2(b)
|
return s.writeV2(b)
|
||||||
}
|
}
|
||||||
@ -356,8 +304,6 @@ func (s *stream) Write(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
// check if stream has closed
|
// check if stream has closed
|
||||||
select {
|
select {
|
||||||
case <-s.chFinEvent: // passive closing
|
|
||||||
return 0, io.EOF
|
|
||||||
case <-s.die:
|
case <-s.die:
|
||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
default:
|
default:
|
||||||
@ -374,7 +320,7 @@ func (s *stream) Write(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
frame.data = bts[:sz]
|
frame.data = bts[:sz]
|
||||||
bts = bts[sz:]
|
bts = bts[sz:]
|
||||||
n, err := s.sess.writeFrameInternal(frame, deadline, CLSDATA)
|
n, err := s.sess.writeFrameInternal(frame, deadline, s.numWritten)
|
||||||
s.numWritten++
|
s.numWritten++
|
||||||
sent += n
|
sent += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -385,8 +331,7 @@ func (s *stream) Write(b []byte) (n int, err error) {
|
|||||||
return sent, nil
|
return sent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeV2 writes data to the stream for version 2 streams.
|
func (s *Stream) writeV2(b []byte) (n int, err error) {
|
||||||
func (s *stream) writeV2(b []byte) (n int, err error) {
|
|
||||||
// check empty input
|
// check empty input
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
@ -394,8 +339,6 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
// check if stream has closed
|
// check if stream has closed
|
||||||
select {
|
select {
|
||||||
case <-s.chFinEvent:
|
|
||||||
return 0, io.EOF
|
|
||||||
case <-s.die:
|
case <-s.die:
|
||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
default:
|
default:
|
||||||
@ -422,18 +365,14 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
// even if uint32 overflow, this math still works:
|
// even if uint32 overflow, this math still works:
|
||||||
// eg1: uint32(0) - uint32(math.MaxUint32) = 1
|
// eg1: uint32(0) - uint32(math.MaxUint32) = 1
|
||||||
// eg2: int32(uint32(0) - uint32(1)) = -1
|
// eg2: int32(uint32(0) - uint32(1)) = -1
|
||||||
//
|
// security check for misbehavior
|
||||||
// basicially, you can take it as a MODULAR ARITHMETIC
|
|
||||||
inflight := int32(atomic.LoadUint32(&s.numWritten) - atomic.LoadUint32(&s.peerConsumed))
|
inflight := int32(atomic.LoadUint32(&s.numWritten) - atomic.LoadUint32(&s.peerConsumed))
|
||||||
if inflight < 0 { // security check for malformed data
|
if inflight < 0 {
|
||||||
return 0, ErrConsumed
|
return 0, ErrConsumed
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure you understand 'win' is calculated in modular arithmetic(2^32(4GB))
|
|
||||||
win := int32(atomic.LoadUint32(&s.peerWindow)) - inflight
|
win := int32(atomic.LoadUint32(&s.peerWindow)) - inflight
|
||||||
|
|
||||||
if win > 0 {
|
if win > 0 {
|
||||||
// determine how many bytes to send
|
|
||||||
if win > int32(len(b)) {
|
if win > int32(len(b)) {
|
||||||
bts = b
|
bts = b
|
||||||
b = nil
|
b = nil
|
||||||
@ -442,18 +381,14 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
b = b[win:]
|
b = b[win:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// frame split and transmit
|
|
||||||
for len(bts) > 0 {
|
for len(bts) > 0 {
|
||||||
// splitting frame
|
|
||||||
sz := len(bts)
|
sz := len(bts)
|
||||||
if sz > s.frameSize {
|
if sz > s.frameSize {
|
||||||
sz = s.frameSize
|
sz = s.frameSize
|
||||||
}
|
}
|
||||||
frame.data = bts[:sz]
|
frame.data = bts[:sz]
|
||||||
bts = bts[sz:]
|
bts = bts[sz:]
|
||||||
|
n, err := s.sess.writeFrameInternal(frame, deadline, atomic.LoadUint32(&s.numWritten))
|
||||||
// transmit of frame
|
|
||||||
n, err := s.sess.writeFrameInternal(frame, deadline, CLSDATA)
|
|
||||||
atomic.AddUint32(&s.numWritten, uint32(sz))
|
atomic.AddUint32(&s.numWritten, uint32(sz))
|
||||||
sent += n
|
sent += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -462,12 +397,12 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is any data left to be sent,
|
// if there is any data remaining to be sent
|
||||||
// wait until stream closes, window changes or deadline reached
|
// wait until stream closes, window changes or deadline reached
|
||||||
// this blocking behavior will back propagate flow control to upper layer.
|
// this blocking behavior will inform upper layer to do flow control
|
||||||
if len(b) > 0 {
|
if len(b) > 0 {
|
||||||
select {
|
select {
|
||||||
case <-s.chFinEvent:
|
case <-s.chFinEvent: // if fin arrived, future window update is impossible
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
case <-s.die:
|
case <-s.die:
|
||||||
return sent, io.ErrClosedPipe
|
return sent, io.ErrClosedPipe
|
||||||
@ -475,7 +410,7 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
return sent, ErrTimeout
|
return sent, ErrTimeout
|
||||||
case <-s.sess.chSocketWriteError:
|
case <-s.sess.chSocketWriteError:
|
||||||
return sent, s.sess.socketWriteError.Load().(error)
|
return sent, s.sess.socketWriteError.Load().(error)
|
||||||
case <-s.chUpdate: // notify of remote data consuming and window update
|
case <-s.chUpdate:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -485,7 +420,7 @@ func (s *stream) writeV2(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close implements net.Conn
|
// Close implements net.Conn
|
||||||
func (s *stream) Close() error {
|
func (s *Stream) Close() error {
|
||||||
var once bool
|
var once bool
|
||||||
var err error
|
var err error
|
||||||
s.dieOnce.Do(func() {
|
s.dieOnce.Do(func() {
|
||||||
@ -494,30 +429,23 @@ func (s *stream) Close() error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if once {
|
if once {
|
||||||
// send FIN in order
|
_, err = s.sess.writeFrame(newFrame(byte(s.sess.config.Version), cmdFIN, s.id))
|
||||||
f := newFrame(byte(s.sess.config.Version), cmdFIN, s.id)
|
|
||||||
|
|
||||||
timer := time.NewTimer(openCloseTimeout)
|
|
||||||
defer timer.Stop()
|
|
||||||
|
|
||||||
_, err = s.sess.writeFrameInternal(f, timer.C, CLSDATA)
|
|
||||||
s.sess.streamClosed(s.id)
|
s.sess.streamClosed(s.id)
|
||||||
return err
|
return err
|
||||||
} else {
|
|
||||||
return io.ErrClosedPipe
|
|
||||||
}
|
}
|
||||||
|
return io.ErrClosedPipe
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDieCh returns a readonly chan which can be readable
|
// GetDieCh returns a readonly chan which can be readable
|
||||||
// when the stream is to be closed.
|
// when the stream is to be closed.
|
||||||
func (s *stream) GetDieCh() <-chan struct{} {
|
func (s *Stream) GetDieCh() <-chan struct{} {
|
||||||
return s.die
|
return s.die
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetReadDeadline sets the read deadline as defined by
|
// SetReadDeadline sets the read deadline as defined by
|
||||||
// net.Conn.SetReadDeadline.
|
// net.Conn.SetReadDeadline.
|
||||||
// A zero time value disables the deadline.
|
// A zero time value disables the deadline.
|
||||||
func (s *stream) SetReadDeadline(t time.Time) error {
|
func (s *Stream) SetReadDeadline(t time.Time) error {
|
||||||
s.readDeadline.Store(t)
|
s.readDeadline.Store(t)
|
||||||
s.notifyReadEvent()
|
s.notifyReadEvent()
|
||||||
return nil
|
return nil
|
||||||
@ -526,7 +454,7 @@ func (s *stream) SetReadDeadline(t time.Time) error {
|
|||||||
// SetWriteDeadline sets the write deadline as defined by
|
// SetWriteDeadline sets the write deadline as defined by
|
||||||
// net.Conn.SetWriteDeadline.
|
// net.Conn.SetWriteDeadline.
|
||||||
// A zero time value disables the deadline.
|
// A zero time value disables the deadline.
|
||||||
func (s *stream) SetWriteDeadline(t time.Time) error {
|
func (s *Stream) SetWriteDeadline(t time.Time) error {
|
||||||
s.writeDeadline.Store(t)
|
s.writeDeadline.Store(t)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -534,7 +462,7 @@ func (s *stream) SetWriteDeadline(t time.Time) error {
|
|||||||
// SetDeadline sets both read and write deadlines as defined by
|
// SetDeadline sets both read and write deadlines as defined by
|
||||||
// net.Conn.SetDeadline.
|
// net.Conn.SetDeadline.
|
||||||
// A zero time value disables the deadlines.
|
// A zero time value disables the deadlines.
|
||||||
func (s *stream) SetDeadline(t time.Time) error {
|
func (s *Stream) SetDeadline(t time.Time) error {
|
||||||
if err := s.SetReadDeadline(t); err != nil {
|
if err := s.SetReadDeadline(t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -545,10 +473,10 @@ func (s *stream) SetDeadline(t time.Time) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// session closes
|
// session closes
|
||||||
func (s *stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }
|
func (s *Stream) sessionClose() { s.dieOnce.Do(func() { close(s.die) }) }
|
||||||
|
|
||||||
// LocalAddr satisfies net.Conn interface
|
// LocalAddr satisfies net.Conn interface
|
||||||
func (s *stream) LocalAddr() net.Addr {
|
func (s *Stream) LocalAddr() net.Addr {
|
||||||
if ts, ok := s.sess.conn.(interface {
|
if ts, ok := s.sess.conn.(interface {
|
||||||
LocalAddr() net.Addr
|
LocalAddr() net.Addr
|
||||||
}); ok {
|
}); ok {
|
||||||
@ -558,7 +486,7 @@ func (s *stream) LocalAddr() net.Addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoteAddr satisfies net.Conn interface
|
// RemoteAddr satisfies net.Conn interface
|
||||||
func (s *stream) RemoteAddr() net.Addr {
|
func (s *Stream) RemoteAddr() net.Addr {
|
||||||
if ts, ok := s.sess.conn.(interface {
|
if ts, ok := s.sess.conn.(interface {
|
||||||
RemoteAddr() net.Addr
|
RemoteAddr() net.Addr
|
||||||
}); ok {
|
}); ok {
|
||||||
@ -568,7 +496,7 @@ func (s *stream) RemoteAddr() net.Addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pushBytes append buf to buffers
|
// pushBytes append buf to buffers
|
||||||
func (s *stream) pushBytes(buf []byte) (written int, err error) {
|
func (s *Stream) pushBytes(buf []byte) (written int, err error) {
|
||||||
s.bufferLock.Lock()
|
s.bufferLock.Lock()
|
||||||
s.buffers = append(s.buffers, buf)
|
s.buffers = append(s.buffers, buf)
|
||||||
s.heads = append(s.heads, buf)
|
s.heads = append(s.heads, buf)
|
||||||
@ -577,7 +505,7 @@ func (s *stream) pushBytes(buf []byte) (written int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// recycleTokens transform remaining bytes to tokens(will truncate buffer)
|
// recycleTokens transform remaining bytes to tokens(will truncate buffer)
|
||||||
func (s *stream) recycleTokens() (n int) {
|
func (s *Stream) recycleTokens() (n int) {
|
||||||
s.bufferLock.Lock()
|
s.bufferLock.Lock()
|
||||||
for k := range s.buffers {
|
for k := range s.buffers {
|
||||||
n += len(s.buffers[k])
|
n += len(s.buffers[k])
|
||||||
@ -590,7 +518,7 @@ func (s *stream) recycleTokens() (n int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// notify read event
|
// notify read event
|
||||||
func (s *stream) notifyReadEvent() {
|
func (s *Stream) notifyReadEvent() {
|
||||||
select {
|
select {
|
||||||
case s.chReadEvent <- struct{}{}:
|
case s.chReadEvent <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
@ -598,7 +526,7 @@ func (s *stream) notifyReadEvent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update command
|
// update command
|
||||||
func (s *stream) update(consumed uint32, window uint32) {
|
func (s *Stream) update(consumed uint32, window uint32) {
|
||||||
atomic.StoreUint32(&s.peerConsumed, consumed)
|
atomic.StoreUint32(&s.peerConsumed, consumed)
|
||||||
atomic.StoreUint32(&s.peerWindow, window)
|
atomic.StoreUint32(&s.peerWindow, window)
|
||||||
select {
|
select {
|
||||||
@ -608,7 +536,7 @@ func (s *stream) update(consumed uint32, window uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mark this stream has been closed in protocol
|
// mark this stream has been closed in protocol
|
||||||
func (s *stream) fin() {
|
func (s *Stream) fin() {
|
||||||
s.finEventOnce.Do(func() {
|
s.finEventOnce.Do(func() {
|
||||||
close(s.chFinEvent)
|
close(s.chFinEvent)
|
||||||
})
|
})
|
||||||
|
@ -139,10 +139,7 @@ func init() {
|
|||||||
Direct scheme:
|
Direct scheme:
|
||||||
direct://
|
direct://
|
||||||
|
|
||||||
Only needed when you want to specify the outgoing interface:
|
Only needed when you want to load balance multiple interfaces directly:
|
||||||
glider -verbose -listen :8443 -forward direct://#interface=eth0
|
|
||||||
|
|
||||||
Or load balance multiple interfaces directly:
|
|
||||||
glider -verbose -listen :8443 -forward direct://#interface=eth0 -forward direct://#interface=eth1 -strategy rr
|
glider -verbose -listen :8443 -forward direct://#interface=eth0 -forward direct://#interface=eth1 -strategy rr
|
||||||
|
|
||||||
Or you can use the high availability mode:
|
Or you can use the high availability mode:
|
||||||
|
@ -33,8 +33,6 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf := pool.GetBytesBuffer()
|
buf := pool.GetBytesBuffer()
|
||||||
defer pool.PutBytesBuffer(buf)
|
|
||||||
|
|
||||||
buf.WriteString("CONNECT " + addr + " HTTP/1.1\r\n")
|
buf.WriteString("CONNECT " + addr + " HTTP/1.1\r\n")
|
||||||
buf.WriteString("Host: " + addr + "\r\n")
|
buf.WriteString("Host: " + addr + "\r\n")
|
||||||
buf.WriteString("Proxy-Connection: Keep-Alive\r\n")
|
buf.WriteString("Proxy-Connection: Keep-Alive\r\n")
|
||||||
@ -47,6 +45,7 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
|
|||||||
// header ended
|
// header ended
|
||||||
buf.WriteString("\r\n")
|
buf.WriteString("\r\n")
|
||||||
_, err = rc.Write(buf.Bytes())
|
_, err = rc.Write(buf.Bytes())
|
||||||
|
pool.PutBytesBuffer(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,10 @@ func (c *TLSObfsConn) Write(b []byte) (int, error) {
|
|||||||
n := len(b)
|
n := len(b)
|
||||||
for i := 0; i < n; i += chunkSize {
|
for i := 0; i < n; i += chunkSize {
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
end := min(i+chunkSize, n)
|
end := i + chunkSize
|
||||||
|
if end > n {
|
||||||
|
end = n
|
||||||
|
}
|
||||||
|
|
||||||
buf.Write([]byte{0x17, 0x03, 0x03})
|
buf.Write([]byte{0x17, 0x03, 0x03})
|
||||||
binary.Write(buf, binary.BigEndian, uint16(len(b[i:end])))
|
binary.Write(buf, binary.BigEndian, uint16(len(b[i:end])))
|
||||||
|
@ -60,7 +60,7 @@ func (s *RedirProxy) ListenAndServe() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.F("[redir] listening TCP on %s", s.addr)
|
log.F("[redir] listening TCP on " + s.addr)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
c, err := l.Accept()
|
c, err := l.Accept()
|
||||||
|
@ -59,7 +59,7 @@ func (s *SS) Serve(c net.Conn) {
|
|||||||
|
|
||||||
tgt, err := socks.ReadAddr(sc)
|
tgt, err := socks.ReadAddr(sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[ss] %s <-> target error: %v", c.RemoteAddr(), err)
|
log.F("[ss] failed to get target address: %v", err)
|
||||||
proxy.Copy(io.Discard, c) // https://github.com/nadoo/glider/issues/180
|
proxy.Copy(io.Discard, c) // https://github.com/nadoo/glider/issues/180
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import (
|
|||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/des"
|
"crypto/des"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rc4"
|
"crypto/rc4"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
"github.com/aead/chacha20"
|
"github.com/aead/chacha20"
|
||||||
"github.com/dgryski/go-camellia"
|
"github.com/dgryski/go-camellia"
|
||||||
|
@ -8,7 +8,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/nadoo/glider/pkg/pool"
|
"github.com/nadoo/glider/pkg/pool"
|
||||||
"github.com/nadoo/glider/proxy"
|
"github.com/nadoo/glider/proxy"
|
||||||
@ -19,6 +21,10 @@ import (
|
|||||||
|
|
||||||
var bufSize = proxy.TCPBufSize
|
var bufSize = proxy.TCPBufSize
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
// SSTCPConn the struct that override the net.Conn methods
|
// SSTCPConn the struct that override the net.Conn methods
|
||||||
type SSTCPConn struct {
|
type SSTCPConn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package obfs
|
package obfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -13,7 +13,7 @@ func newHttpPost() IObfs {
|
|||||||
// newHttpSimple create a http_simple object
|
// newHttpSimple create a http_simple object
|
||||||
|
|
||||||
t := &httpSimplePost{
|
t := &httpSimplePost{
|
||||||
userAgentIndex: rand.IntN(len(requestUserAgent)),
|
userAgentIndex: rand.Intn(len(requestUserAgent)),
|
||||||
methodGet: false,
|
methodGet: false,
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
|
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
|
||||||
@ -55,7 +55,7 @@ func newHttpSimple() IObfs {
|
|||||||
t := &httpSimplePost{
|
t := &httpSimplePost{
|
||||||
rawTransSent: false,
|
rawTransSent: false,
|
||||||
rawTransReceived: false,
|
rawTransReceived: false,
|
||||||
userAgentIndex: rand.IntN(len(requestUserAgent)),
|
userAgentIndex: rand.Intn(len(requestUserAgent)),
|
||||||
methodGet: true,
|
methodGet: true,
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
@ -80,14 +80,14 @@ func (t *httpSimplePost) GetData() any {
|
|||||||
func (t *httpSimplePost) boundary() (ret string) {
|
func (t *httpSimplePost) boundary() (ret string) {
|
||||||
|
|
||||||
set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
for range 32 {
|
for i := 0; i < 32; i++ {
|
||||||
ret = fmt.Sprintf("%s%c", ret, set[rand.IntN(len(set))])
|
ret = fmt.Sprintf("%s%c", ret, set[rand.Intn(len(set))])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *httpSimplePost) data2URLEncode(data []byte) (ret string) {
|
func (t *httpSimplePost) data2URLEncode(data []byte) (ret string) {
|
||||||
for i := range data {
|
for i := 0; i < len(data); i++ {
|
||||||
ret = fmt.Sprintf("%s%%%s", ret, hex.EncodeToString([]byte{data[i]}))
|
ret = fmt.Sprintf("%s%%%s", ret, hex.EncodeToString([]byte{data[i]}))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -101,12 +101,12 @@ func (t *httpSimplePost) Encode(data []byte) (encodedData []byte, err error) {
|
|||||||
dataLength := len(data)
|
dataLength := len(data)
|
||||||
var headData []byte
|
var headData []byte
|
||||||
if headSize := t.IVLen + t.HeadLen; dataLength-headSize > 64 {
|
if headSize := t.IVLen + t.HeadLen; dataLength-headSize > 64 {
|
||||||
headData = make([]byte, headSize+rand.IntN(64))
|
headData = make([]byte, headSize+rand.Intn(64))
|
||||||
} else {
|
} else {
|
||||||
headData = make([]byte, dataLength)
|
headData = make([]byte, dataLength)
|
||||||
}
|
}
|
||||||
copy(headData, data[0:len(headData)])
|
copy(headData, data[0:len(headData)])
|
||||||
requestPathIndex := rand.IntN(len(requestPath)/2) * 2
|
requestPathIndex := rand.Intn(len(requestPath)/2) * 2
|
||||||
host := t.Host
|
host := t.Host
|
||||||
var customHead string
|
var customHead string
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ func (t *httpSimplePost) Encode(data []byte) (encodedData []byte, err error) {
|
|||||||
}
|
}
|
||||||
hosts := strings.Split(param, ",")
|
hosts := strings.Split(param, ",")
|
||||||
if len(hosts) > 0 {
|
if len(hosts) > 0 {
|
||||||
host = strings.TrimSpace(hosts[rand.IntN(len(hosts))])
|
host = strings.TrimSpace(hosts[rand.Intn(len(hosts))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
method := "GET /"
|
method := "GET /"
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package obfs
|
package obfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
crand "crypto/rand"
|
"math/rand"
|
||||||
"math/rand/v2"
|
|
||||||
|
|
||||||
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
|
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
|
||||||
)
|
)
|
||||||
@ -58,9 +57,9 @@ func (r *randomHead) Encode(data []byte) (encodedData []byte, err error) {
|
|||||||
r.rawTransSent = true
|
r.rawTransSent = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
size := rand.IntN(96) + 8
|
size := rand.Intn(96) + 8
|
||||||
encodedData = make([]byte, size)
|
encodedData = make([]byte, size)
|
||||||
crand.Read(encodedData)
|
rand.Read(encodedData)
|
||||||
ssr.SetCRC32(encodedData, size)
|
ssr.SetCRC32(encodedData, size)
|
||||||
|
|
||||||
d := make([]byte, dataLength)
|
d := make([]byte, dataLength)
|
||||||
|
@ -3,11 +3,10 @@ package obfs
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
crand "crypto/rand"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -65,7 +64,7 @@ func (t *tls12TicketAuth) GetData() any {
|
|||||||
t.data = &tlsAuthData{}
|
t.data = &tlsAuthData{}
|
||||||
b := make([]byte, 32)
|
b := make([]byte, 32)
|
||||||
|
|
||||||
crand.Read(b)
|
rand.Read(b)
|
||||||
copy(t.data.localClientID[:], b)
|
copy(t.data.localClientID[:], b)
|
||||||
}
|
}
|
||||||
return t.data
|
return t.data
|
||||||
@ -77,7 +76,7 @@ func (t *tls12TicketAuth) getHost() string {
|
|||||||
hosts := strings.Split(t.Param, ",")
|
hosts := strings.Split(t.Param, ",")
|
||||||
if len(hosts) > 0 {
|
if len(hosts) > 0 {
|
||||||
|
|
||||||
host = hosts[rand.IntN(len(hosts))]
|
host = hosts[rand.Intn(len(hosts))]
|
||||||
host = strings.TrimSpace(host)
|
host = strings.TrimSpace(host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,6 +96,7 @@ func packData(prefixData []byte, suffixData []byte) (outData []byte) {
|
|||||||
|
|
||||||
func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
|
func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
|
||||||
encodedData = make([]byte, 0)
|
encodedData = make([]byte, 0)
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
switch t.handshakeStatus {
|
switch t.handshakeStatus {
|
||||||
case 8:
|
case 8:
|
||||||
if len(data) < 1024 {
|
if len(data) < 1024 {
|
||||||
@ -108,7 +108,7 @@ func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
|
|||||||
start := 0
|
start := 0
|
||||||
var l int
|
var l int
|
||||||
for len(data)-start > 2048 {
|
for len(data)-start > 2048 {
|
||||||
l = rand.IntN(4096) + 100
|
l = rand.Intn(4096) + 100
|
||||||
if l > len(data)-start {
|
if l > len(data)-start {
|
||||||
l = len(data) - start
|
l = len(data) - start
|
||||||
}
|
}
|
||||||
@ -129,7 +129,7 @@ func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
|
|||||||
start := 0
|
start := 0
|
||||||
var l int
|
var l int
|
||||||
for len(data)-start > 2048 {
|
for len(data)-start > 2048 {
|
||||||
l = rand.IntN(4096) + 100
|
l = rand.Intn(4096) + 100
|
||||||
if l > len(data)-start {
|
if l > len(data)-start {
|
||||||
l = len(data) - start
|
l = len(data) - start
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
|
|||||||
hmacData := make([]byte, 43)
|
hmacData := make([]byte, 43)
|
||||||
handshakeFinish := []byte("\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00\x20")
|
handshakeFinish := []byte("\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00\x20")
|
||||||
copy(hmacData, handshakeFinish)
|
copy(hmacData, handshakeFinish)
|
||||||
crand.Read(hmacData[11:33])
|
rand.Read(hmacData[11:33])
|
||||||
h := t.hmacSHA1(hmacData[:33])
|
h := t.hmacSHA1(hmacData[:33])
|
||||||
copy(hmacData[33:], h)
|
copy(hmacData[33:], h)
|
||||||
encodedData = append(hmacData, t.sendSaver...)
|
encodedData = append(hmacData, t.sendSaver...)
|
||||||
@ -169,11 +169,11 @@ func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
|
|||||||
tlsDataLen += len(sni)
|
tlsDataLen += len(sni)
|
||||||
copy(tlsData[tlsDataLen:], tlsData2)
|
copy(tlsData[tlsDataLen:], tlsData2)
|
||||||
tlsDataLen += len(tlsData2)
|
tlsDataLen += len(tlsData2)
|
||||||
ticketLen := rand.IntN(164)*2 + 64
|
ticketLen := rand.Intn(164)*2 + 64
|
||||||
tlsData[tlsDataLen-1] = uint8(ticketLen & 0xff)
|
tlsData[tlsDataLen-1] = uint8(ticketLen & 0xff)
|
||||||
tlsData[tlsDataLen-2] = uint8(ticketLen >> 8)
|
tlsData[tlsDataLen-2] = uint8(ticketLen >> 8)
|
||||||
//ticketLen := 208
|
//ticketLen := 208
|
||||||
crand.Read(tlsData[tlsDataLen : tlsDataLen+ticketLen])
|
rand.Read(tlsData[tlsDataLen : tlsDataLen+ticketLen])
|
||||||
tlsDataLen += ticketLen
|
tlsDataLen += ticketLen
|
||||||
copy(tlsData[tlsDataLen:], tlsData3)
|
copy(tlsData[tlsDataLen:], tlsData3)
|
||||||
tlsDataLen += len(tlsData3)
|
tlsDataLen += len(tlsData3)
|
||||||
@ -278,7 +278,7 @@ func (t *tls12TicketAuth) packAuthData() (outData []byte) {
|
|||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
binary.BigEndian.PutUint32(outData[0:4], uint32(now))
|
binary.BigEndian.PutUint32(outData[0:4], uint32(now))
|
||||||
|
|
||||||
crand.Read(outData[4 : 4+18])
|
rand.Read(outData[4 : 4+18])
|
||||||
|
|
||||||
hash := t.hmacSHA1(outData[:outSize-ssr.ObfsHMACSHA1Len])
|
hash := t.hmacSHA1(outData[:outSize-ssr.ObfsHMACSHA1Len])
|
||||||
copy(outData[outSize-ssr.ObfsHMACSHA1Len:], hash)
|
copy(outData[outSize-ssr.ObfsHMACSHA1Len:], hash)
|
||||||
|
@ -4,10 +4,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
crand "crypto/rand"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -75,14 +74,15 @@ func (a *authAES128) GetData() any {
|
|||||||
func (a *authAES128) packData(data []byte) (outData []byte) {
|
func (a *authAES128) packData(data []byte) (outData []byte) {
|
||||||
dataLength := len(data)
|
dataLength := len(data)
|
||||||
randLength := 1
|
randLength := 1
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
if dataLength <= 1200 {
|
if dataLength <= 1200 {
|
||||||
if a.packID > 4 {
|
if a.packID > 4 {
|
||||||
randLength += rand.IntN(32)
|
randLength += rand.Intn(32)
|
||||||
} else {
|
} else {
|
||||||
if dataLength > 900 {
|
if dataLength > 900 {
|
||||||
randLength += rand.IntN(128)
|
randLength += rand.Intn(128)
|
||||||
} else {
|
} else {
|
||||||
randLength += rand.IntN(512)
|
randLength += rand.Intn(512)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ func (a *authAES128) packData(data []byte) (outData []byte) {
|
|||||||
h := a.hmac(key, outData[0:2])
|
h := a.hmac(key, outData[0:2])
|
||||||
copy(outData[2:4], h[:2])
|
copy(outData[2:4], h[:2])
|
||||||
// 4~rand length+4, rand number
|
// 4~rand length+4, rand number
|
||||||
crand.Read(outData[4 : 4+randLength])
|
rand.Read(outData[4 : 4+randLength])
|
||||||
// 4, rand length
|
// 4, rand length
|
||||||
if randLength < 128 {
|
if randLength < 128 {
|
||||||
outData[4] = byte(randLength & 0xFF)
|
outData[4] = byte(randLength & 0xFF)
|
||||||
@ -121,10 +121,11 @@ func (a *authAES128) packData(data []byte) (outData []byte) {
|
|||||||
func (a *authAES128) packAuthData(data []byte) (outData []byte) {
|
func (a *authAES128) packAuthData(data []byte) (outData []byte) {
|
||||||
dataLength := len(data)
|
dataLength := len(data)
|
||||||
var randLength int
|
var randLength int
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
if dataLength > 400 {
|
if dataLength > 400 {
|
||||||
randLength = rand.IntN(512)
|
randLength = rand.Intn(512)
|
||||||
} else {
|
} else {
|
||||||
randLength = rand.IntN(1024)
|
randLength = rand.Intn(1024)
|
||||||
}
|
}
|
||||||
|
|
||||||
dataOffset := randLength + 16 + 4 + 4 + 7
|
dataOffset := randLength + 16 + 4 + 4 + 7
|
||||||
@ -135,7 +136,7 @@ func (a *authAES128) packAuthData(data []byte) (outData []byte) {
|
|||||||
copy(key, a.IV)
|
copy(key, a.IV)
|
||||||
copy(key[a.IVLen:], a.Key)
|
copy(key[a.IVLen:], a.Key)
|
||||||
|
|
||||||
crand.Read(outData[dataOffset-randLength:])
|
rand.Read(outData[dataOffset-randLength:])
|
||||||
a.data.mutex.Lock()
|
a.data.mutex.Lock()
|
||||||
a.data.connectionID++
|
a.data.connectionID++
|
||||||
if a.data.connectionID > 0xFF000000 {
|
if a.data.connectionID > 0xFF000000 {
|
||||||
@ -143,9 +144,9 @@ func (a *authAES128) packAuthData(data []byte) (outData []byte) {
|
|||||||
}
|
}
|
||||||
if len(a.data.clientID) == 0 {
|
if len(a.data.clientID) == 0 {
|
||||||
a.data.clientID = make([]byte, 8)
|
a.data.clientID = make([]byte, 8)
|
||||||
crand.Read(a.data.clientID)
|
rand.Read(a.data.clientID)
|
||||||
b := make([]byte, 4)
|
b := make([]byte, 4)
|
||||||
crand.Read(b)
|
rand.Read(b)
|
||||||
a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
|
a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
|
||||||
}
|
}
|
||||||
copy(encrypt[4:], a.data.clientID)
|
copy(encrypt[4:], a.data.clientID)
|
||||||
@ -162,13 +163,13 @@ func (a *authAES128) packAuthData(data []byte) (outData []byte) {
|
|||||||
uid := make([]byte, 4)
|
uid := make([]byte, 4)
|
||||||
if len(params) >= 2 {
|
if len(params) >= 2 {
|
||||||
if userID, err := strconv.ParseUint(params[0], 10, 32); err != nil {
|
if userID, err := strconv.ParseUint(params[0], 10, 32); err != nil {
|
||||||
crand.Read(uid)
|
rand.Read(uid)
|
||||||
} else {
|
} else {
|
||||||
binary.LittleEndian.PutUint32(uid, uint32(userID))
|
binary.LittleEndian.PutUint32(uid, uint32(userID))
|
||||||
a.userKey = a.hashDigest([]byte(params[1]))
|
a.userKey = a.hashDigest([]byte(params[1]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
crand.Read(uid)
|
rand.Read(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.userKey == nil {
|
if a.userKey == nil {
|
||||||
@ -195,7 +196,7 @@ func (a *authAES128) packAuthData(data []byte) (outData []byte) {
|
|||||||
h := a.hmac(key, encrypt[0:20])
|
h := a.hmac(key, encrypt[0:20])
|
||||||
copy(encrypt[20:], h[:4])
|
copy(encrypt[20:], h[:4])
|
||||||
|
|
||||||
crand.Read(outData[0:1])
|
rand.Read(outData[0:1])
|
||||||
h = a.hmac(key, outData[0:1])
|
h = a.hmac(key, outData[0:1])
|
||||||
copy(outData[1:], h[0:7-1])
|
copy(outData[1:], h[0:7-1])
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
stdCipher "crypto/cipher"
|
stdCipher "crypto/cipher"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -194,7 +194,7 @@ func (a *authChainA) packAuthData(data []byte) (outData []byte) {
|
|||||||
copy(a.userKey, a.Key)
|
copy(a.userKey, a.Key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := range 4 {
|
for i := 0; i < 4; i++ {
|
||||||
uid[i] = a.uid[i] ^ a.lastClientHash[8+i]
|
uid[i] = a.uid[i] ^ a.lastClientHash[8+i]
|
||||||
}
|
}
|
||||||
base64UserKey = base64.StdEncoding.EncodeToString(a.userKey)
|
base64UserKey = base64.StdEncoding.EncodeToString(a.userKey)
|
||||||
|
@ -34,14 +34,14 @@ func (a *authChainA) authChainBInitDataSize() {
|
|||||||
random.InitFromBin(a.Key)
|
random.InitFromBin(a.Key)
|
||||||
length := random.Next()%8 + 4
|
length := random.Next()%8 + 4
|
||||||
a.dataSizeList = make([]int, length)
|
a.dataSizeList = make([]int, length)
|
||||||
for i := range int(length) {
|
for i := 0; i < int(length); i++ {
|
||||||
a.dataSizeList[i] = int(random.Next() % 2340 % 2040 % 1440)
|
a.dataSizeList[i] = int(random.Next() % 2340 % 2040 % 1440)
|
||||||
}
|
}
|
||||||
sort.Ints(a.dataSizeList)
|
sort.Ints(a.dataSizeList)
|
||||||
|
|
||||||
length = random.Next()%16 + 8
|
length = random.Next()%16 + 8
|
||||||
a.dataSizeList2 = make([]int, length)
|
a.dataSizeList2 = make([]int, length)
|
||||||
for i := range int(length) {
|
for i := 0; i < int(length); i++ {
|
||||||
a.dataSizeList2[i] = int(random.Next() % 2340 % 2040 % 1440)
|
a.dataSizeList2[i] = int(random.Next() % 2340 % 2040 % 1440)
|
||||||
}
|
}
|
||||||
sort.Ints(a.dataSizeList2)
|
sort.Ints(a.dataSizeList2)
|
||||||
|
@ -2,9 +2,8 @@ package protocol
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
crand "crypto/rand"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
|
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
|
||||||
@ -54,9 +53,9 @@ func (a *authSHA1v4) packData(data []byte) (outData []byte) {
|
|||||||
|
|
||||||
if dataLength <= 1300 {
|
if dataLength <= 1300 {
|
||||||
if dataLength > 400 {
|
if dataLength > 400 {
|
||||||
randLength += rand.IntN(128)
|
randLength += rand.Intn(128)
|
||||||
} else {
|
} else {
|
||||||
randLength += rand.IntN(1024)
|
randLength += rand.Intn(1024)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,9 +90,9 @@ func (a *authSHA1v4) packAuthData(data []byte) (outData []byte) {
|
|||||||
randLength := 1
|
randLength := 1
|
||||||
if dataLength <= 1300 {
|
if dataLength <= 1300 {
|
||||||
if dataLength > 400 {
|
if dataLength > 400 {
|
||||||
randLength += rand.IntN(128)
|
randLength += rand.Intn(128)
|
||||||
} else {
|
} else {
|
||||||
randLength += rand.IntN(1024)
|
randLength += rand.Intn(1024)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataOffset := randLength + 4 + 2
|
dataOffset := randLength + 4 + 2
|
||||||
@ -105,9 +104,9 @@ func (a *authSHA1v4) packAuthData(data []byte) (outData []byte) {
|
|||||||
}
|
}
|
||||||
if len(a.data.clientID) == 0 {
|
if len(a.data.clientID) == 0 {
|
||||||
a.data.clientID = make([]byte, 8)
|
a.data.clientID = make([]byte, 8)
|
||||||
crand.Read(a.data.clientID)
|
rand.Read(a.data.clientID)
|
||||||
b := make([]byte, 4)
|
b := make([]byte, 4)
|
||||||
crand.Read(b)
|
rand.Read(b)
|
||||||
a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
|
a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
|
||||||
}
|
}
|
||||||
// 0-1, out length
|
// 0-1, out length
|
||||||
@ -123,7 +122,7 @@ func (a *authSHA1v4) packAuthData(data []byte) (outData []byte) {
|
|||||||
// 2~6, crc of out length+salt+key
|
// 2~6, crc of out length+salt+key
|
||||||
binary.LittleEndian.PutUint32(outData[2:], crc32)
|
binary.LittleEndian.PutUint32(outData[2:], crc32)
|
||||||
// 6~rand length+6, rand numbers
|
// 6~rand length+6, rand numbers
|
||||||
crand.Read(outData[dataOffset-randLength : dataOffset])
|
rand.Read(outData[dataOffset-randLength : dataOffset])
|
||||||
// 6, rand length
|
// 6, rand length
|
||||||
if randLength < 128 {
|
if randLength < 128 {
|
||||||
outData[6] = byte(randLength & 0xFF)
|
outData[6] = byte(randLength & 0xFF)
|
||||||
|
@ -11,7 +11,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createCRC32Table() {
|
func createCRC32Table() {
|
||||||
for i := range 256 {
|
for i := 0; i < 256; i++ {
|
||||||
crc := uint32(i)
|
crc := uint32(i)
|
||||||
for j := 8; j > 0; j-- {
|
for j := 8; j > 0; j-- {
|
||||||
if crc&1 == 1 {
|
if crc&1 == 1 {
|
||||||
|
@ -37,7 +37,7 @@ func (ctx *Shift128plusContext) InitFromBinDatalen(bin []byte, datalen int) {
|
|||||||
ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8])
|
ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8])
|
||||||
ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:])
|
ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:])
|
||||||
|
|
||||||
for range 4 {
|
for i := 0; i < 4; i++ {
|
||||||
ctx.Next()
|
ctx.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package tproxy
|
package tproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -5,14 +5,13 @@ import (
|
|||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
crand "crypto/rand"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"io"
|
"io"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@ -117,12 +116,15 @@ func NewClient(uuidStr, security string, alterID int, aead bool) (*Client, error
|
|||||||
return nil, errors.New("unknown security type: " + security)
|
return nil, errors.New("unknown security type: " + security)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: give rand a new seed to avoid the same sequence of values
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConn returns a new vmess conn.
|
// NewConn returns a new vmess conn.
|
||||||
func (c *Client) NewConn(rc net.Conn, target string, cmd CmdType) (*Conn, error) {
|
func (c *Client) NewConn(rc net.Conn, target string, cmd CmdType) (*Conn, error) {
|
||||||
r := rand.IntN(c.count)
|
r := rand.Intn(c.count)
|
||||||
conn := &Conn{user: c.users[r], opt: c.opt, aead: c.aead, security: c.security, Conn: rc}
|
conn := &Conn{user: c.users[r], opt: c.opt, aead: c.aead, security: c.security, Conn: rc}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -132,12 +134,12 @@ func (c *Client) NewConn(rc net.Conn, target string, cmd CmdType) (*Conn, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
randBytes := pool.GetBuffer(32)
|
randBytes := pool.GetBuffer(32)
|
||||||
crand.Read(randBytes)
|
rand.Read(randBytes)
|
||||||
copy(conn.reqBodyIV[:], randBytes[:16])
|
copy(conn.reqBodyIV[:], randBytes[:16])
|
||||||
copy(conn.reqBodyKey[:], randBytes[16:32])
|
copy(conn.reqBodyKey[:], randBytes[16:32])
|
||||||
pool.PutBuffer(randBytes)
|
pool.PutBuffer(randBytes)
|
||||||
|
|
||||||
conn.reqRespV = byte(rand.IntN(1 << 8))
|
conn.reqRespV = byte(rand.Intn(1 << 8))
|
||||||
|
|
||||||
if conn.aead {
|
if conn.aead {
|
||||||
bodyIV := sha256.Sum256(conn.reqBodyIV[:])
|
bodyIV := sha256.Sum256(conn.reqBodyIV[:])
|
||||||
@ -193,7 +195,7 @@ func (c *Conn) Request(cmd CmdType) error {
|
|||||||
buf.WriteByte(c.opt) // Opt
|
buf.WriteByte(c.opt) // Opt
|
||||||
|
|
||||||
// pLen and Sec
|
// pLen and Sec
|
||||||
paddingLen := rand.IntN(16)
|
paddingLen := rand.Intn(16)
|
||||||
pSec := byte(paddingLen<<4) | c.security // P(4bit) and Sec(4bit)
|
pSec := byte(paddingLen<<4) | c.security // P(4bit) and Sec(4bit)
|
||||||
buf.WriteByte(pSec)
|
buf.WriteByte(pSec)
|
||||||
|
|
||||||
@ -208,7 +210,7 @@ func (c *Conn) Request(cmd CmdType) error {
|
|||||||
// padding
|
// padding
|
||||||
if paddingLen > 0 {
|
if paddingLen > 0 {
|
||||||
padding := pool.GetBuffer(paddingLen)
|
padding := pool.GetBuffer(paddingLen)
|
||||||
crand.Read(padding)
|
rand.Read(padding)
|
||||||
buf.Write(padding)
|
buf.Write(padding)
|
||||||
pool.PutBuffer(padding)
|
pool.PutBuffer(padding)
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func nextID(oldID [16]byte) (newID [16]byte) {
|
|||||||
func (u *User) GenAlterIDUsers(alterID int) []*User {
|
func (u *User) GenAlterIDUsers(alterID int) []*User {
|
||||||
users := make([]*User, alterID)
|
users := make([]*User, alterID)
|
||||||
preID := u.UUID
|
preID := u.UUID
|
||||||
for i := range alterID {
|
for i := 0; i < alterID; i++ {
|
||||||
newID := nextID(preID)
|
newID := nextID(preID)
|
||||||
// NOTE: alterID user is a user which have a different uuid but a same cmdkey with the primary user.
|
// NOTE: alterID user is a user which have a different uuid but a same cmdkey with the primary user.
|
||||||
users[i] = &User{UUID: newID, CmdKey: u.CmdKey}
|
users[i] = &User{UUID: newID, CmdKey: u.CmdKey}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package vsock
|
package vsock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package vsock
|
package vsock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
// Source code from:
|
// Source code from:
|
||||||
// https://github.com/linuxkit/virtsock/tree/master/pkg/vsock
|
// https://github.com/linuxkit/virtsock/tree/master/pkg/vsock
|
||||||
|
|
||||||
package vsock
|
package vsock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package vsock
|
package vsock
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -25,7 +25,7 @@ package ws
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/nadoo/glider/pkg/pool"
|
"github.com/nadoo/glider/pkg/pool"
|
||||||
@ -93,7 +93,7 @@ func (w *frameWriter) Write(b []byte) (int, error) {
|
|||||||
defer pool.PutBuffer(payload)
|
defer pool.PutBuffer(payload)
|
||||||
|
|
||||||
// payload with mask
|
// payload with mask
|
||||||
for i := range nPayload {
|
for i := 0; i < nPayload; i++ {
|
||||||
payload[i] = b[i] ^ w.maskKey[i%4]
|
payload[i] = b[i] ^ w.maskKey[i%4]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ func (p *FwdrGroup) Check() {
|
|||||||
|
|
||||||
log.F("[group] %s: using check config: %s", p.name, p.config.Check)
|
log.F("[group] %s: using check config: %s", p.name, p.config.Check)
|
||||||
|
|
||||||
for i := range p.fwdrs {
|
for i := 0; i < len(p.fwdrs); i++ {
|
||||||
go p.check(p.fwdrs[i], checker)
|
go p.check(p.fwdrs[i], checker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -128,69 +126,58 @@ func (d *dhcpd) handleDHCP(serverIP net.IP, mask net.IPMask, pool *Pool) server4
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var reqIP netip.Addr
|
|
||||||
var reqType, replyType dhcpv4.MessageType
|
var reqType, replyType dhcpv4.MessageType
|
||||||
|
switch reqType = m.MessageType(); reqType {
|
||||||
reqType = m.MessageType()
|
|
||||||
log.F("[dpcpd] %s: %s from %v(%v)", d.name, reqType, m.ClientHWAddr, m.ClientIPAddr)
|
|
||||||
|
|
||||||
switch reqType {
|
|
||||||
case dhcpv4.MessageTypeDiscover:
|
case dhcpv4.MessageTypeDiscover:
|
||||||
replyType = dhcpv4.MessageTypeOffer
|
replyType = dhcpv4.MessageTypeOffer
|
||||||
case dhcpv4.MessageTypeInform:
|
case dhcpv4.MessageTypeRequest, dhcpv4.MessageTypeInform:
|
||||||
replyType = dhcpv4.MessageTypeAck
|
replyType = dhcpv4.MessageTypeAck
|
||||||
case dhcpv4.MessageTypeRequest:
|
case dhcpv4.MessageTypeRelease:
|
||||||
replyType = dhcpv4.MessageTypeAck
|
|
||||||
if m.Options.Has(dhcpv4.OptionRequestedIPAddress) {
|
|
||||||
reqIP, _ = netip.AddrFromSlice(m.Options.Get(dhcpv4.OptionRequestedIPAddress))
|
|
||||||
} else {
|
|
||||||
// client uses Unicast to renew ip address lease, just take client ip
|
|
||||||
reqIP = netip.AddrFrom4([4]byte(m.ClientIPAddr.To4()))
|
|
||||||
}
|
|
||||||
case dhcpv4.MessageTypeRelease, dhcpv4.MessageTypeDecline:
|
|
||||||
pool.ReleaseIP(m.ClientHWAddr)
|
pool.ReleaseIP(m.ClientHWAddr)
|
||||||
|
log.F("[dpcpd] %s:%v released ip %v", d.name, m.ClientHWAddr, m.ClientIPAddr)
|
||||||
|
return
|
||||||
|
case dhcpv4.MessageTypeDecline:
|
||||||
|
pool.ReleaseIP(m.ClientHWAddr)
|
||||||
|
log.F("[dpcpd] %s: received decline message from %v", d.name, m.ClientHWAddr)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
log.F("[dpcpd] %s: can't handle type %v from %v", d.name, reqType, m.ClientHWAddr)
|
log.F("[dpcpd] %s: can't handle type %v", d.name, reqType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
replyIP, err := pool.LeaseIP(m.ClientHWAddr, reqIP)
|
replyIP, err := pool.LeaseIP(m.ClientHWAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[dpcpd] %s: can not assign IP for %v, error: %s", d.name, m.ClientHWAddr, err)
|
log.F("[dpcpd] %s: can not assign IP, error %s", d.name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if reqType == dhcpv4.MessageTypeRequest && !reqIP.IsUnspecified() && reqIP != replyIP {
|
reply, err := dhcpv4.NewReplyFromRequest(m,
|
||||||
replyType = dhcpv4.MessageTypeNak
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := dhcpv4.NewReplyFromRequest(m,
|
|
||||||
dhcpv4.WithMessageType(replyType),
|
dhcpv4.WithMessageType(replyType),
|
||||||
|
dhcpv4.WithServerIP(serverIP),
|
||||||
dhcpv4.WithNetmask(mask),
|
dhcpv4.WithNetmask(mask),
|
||||||
dhcpv4.WithYourIP(replyIP.AsSlice()),
|
dhcpv4.WithYourIP(replyIP.AsSlice()),
|
||||||
dhcpv4.WithRouter(serverIP),
|
dhcpv4.WithRouter(serverIP),
|
||||||
dhcpv4.WithDNS(serverIP),
|
dhcpv4.WithDNS(serverIP),
|
||||||
dhcpv4.WithServerIP(serverIP), //
|
|
||||||
// RFC 2131, Section 4.3.1. IP lease time: MUST
|
|
||||||
dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(d.lease)),
|
|
||||||
// RFC 2131, Section 4.3.1. Server Identifier: MUST
|
// RFC 2131, Section 4.3.1. Server Identifier: MUST
|
||||||
dhcpv4.WithOption(dhcpv4.OptServerIdentifier(serverIP)),
|
dhcpv4.WithOption(dhcpv4.OptServerIdentifier(serverIP)),
|
||||||
|
// RFC 2131, Section 4.3.1. IP lease time: MUST
|
||||||
|
dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(d.lease)),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("[dpcpd] %s: can not create reply message, error: %s", d.name, err)
|
log.F("[dpcpd] %s: can not create reply message, error %s", d.name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := reply(d.iface, resp); err != nil {
|
if val := m.Options.Get(dhcpv4.OptionClientIdentifier); len(val) > 0 {
|
||||||
log.F("[dpcpd] %s: could not write to %v(%v): %s",
|
reply.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionClientIdentifier, val))
|
||||||
d.name, resp.ClientHWAddr, peer, err)
|
}
|
||||||
|
|
||||||
|
if _, err := conn.WriteTo(reply.ToBytes(), peer); err != nil {
|
||||||
|
log.F("[dpcpd] %s: could not write to client %s(%s): %s", d.name, peer, reply.ClientHWAddr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.F("[dpcpd] %s: %s to %v for %v",
|
log.F("[dpcpd] %s: lease %v to client %v", d.name, replyIP, reply.ClientHWAddr)
|
||||||
d.name, replyType, resp.ClientHWAddr, replyIP)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package dhcpd
|
package dhcpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"math/rand/v2"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
@ -40,12 +38,13 @@ func NewPool(lease time.Duration, start, end netip.Addr) (*Pool, error) {
|
|||||||
for n := s; n <= e; n++ {
|
for n := s; n <= e; n++ {
|
||||||
items = append(items, &item{ip: numToIPv4(n)})
|
items = append(items, &item{ip: numToIPv4(n)})
|
||||||
}
|
}
|
||||||
|
rand.Seed(time.Now().Unix())
|
||||||
|
|
||||||
p := &Pool{items: items, lease: lease}
|
p := &Pool{items: items, lease: lease}
|
||||||
go func() {
|
go func() {
|
||||||
for now := range time.Tick(time.Second) {
|
for now := range time.Tick(time.Second) {
|
||||||
p.mutex.Lock()
|
p.mutex.Lock()
|
||||||
for i := range len(items) {
|
for i := 0; i < len(items); i++ {
|
||||||
if !items[i].expire.IsZero() && now.After(items[i].expire) {
|
if !items[i].expire.IsZero() && now.After(items[i].expire) {
|
||||||
items[i].mac = nil
|
items[i].mac = nil
|
||||||
items[i].expire = time.Time{}
|
items[i].expire = time.Time{}
|
||||||
@ -59,31 +58,17 @@ func NewPool(lease time.Duration, start, end netip.Addr) (*Pool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LeaseIP leases an ip to mac from dhcp pool.
|
// LeaseIP leases an ip to mac from dhcp pool.
|
||||||
func (p *Pool) LeaseIP(mac net.HardwareAddr, ip netip.Addr) (netip.Addr, error) {
|
func (p *Pool) LeaseIP(mac net.HardwareAddr) (netip.Addr, error) {
|
||||||
p.mutex.Lock()
|
p.mutex.Lock()
|
||||||
defer p.mutex.Unlock()
|
defer p.mutex.Unlock()
|
||||||
|
|
||||||
// static ip and leased ip
|
|
||||||
for _, item := range p.items {
|
for _, item := range p.items {
|
||||||
if bytes.Equal(mac, item.mac) {
|
if bytes.Equal(mac, item.mac) {
|
||||||
if !item.expire.IsZero() {
|
|
||||||
item.expire = time.Now().Add(p.lease)
|
|
||||||
}
|
|
||||||
return item.ip, nil
|
return item.ip, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// requested ip
|
idx := rand.Intn(len(p.items))
|
||||||
for _, item := range p.items {
|
|
||||||
if item.ip == ip && item.mac == nil {
|
|
||||||
item.mac = mac
|
|
||||||
item.expire = time.Now().Add(p.lease)
|
|
||||||
return item.ip, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lease new ip
|
|
||||||
idx := rand.IntN(len(p.items))
|
|
||||||
for _, item := range p.items[idx:] {
|
for _, item := range p.items[idx:] {
|
||||||
if item.mac == nil {
|
if item.mac == nil {
|
||||||
item.mac = mac
|
item.mac = mac
|
||||||
@ -111,7 +96,7 @@ func (p *Pool) LeaseStaticIP(mac net.HardwareAddr, ip netip.Addr) {
|
|||||||
for _, item := range p.items {
|
for _, item := range p.items {
|
||||||
if item.ip == ip {
|
if item.ip == ip {
|
||||||
item.mac = mac
|
item.mac = mac
|
||||||
item.expire = time.Time{}
|
item.expire = time.Now().Add(time.Hour * 24 * 365 * 50) // 50 years
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,8 +107,7 @@ func (p *Pool) ReleaseIP(mac net.HardwareAddr) {
|
|||||||
defer p.mutex.Unlock()
|
defer p.mutex.Unlock()
|
||||||
|
|
||||||
for _, item := range p.items {
|
for _, item := range p.items {
|
||||||
// not static ip
|
if bytes.Equal(mac, item.mac) {
|
||||||
if !item.expire.IsZero() && bytes.Equal(mac, item.mac) {
|
|
||||||
item.mac = nil
|
item.mac = nil
|
||||||
item.expire = time.Time{}
|
item.expire = time.Time{}
|
||||||
}
|
}
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
package dhcpd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
|
||||||
|
|
||||||
"github.com/nadoo/glider/pkg/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func reply(iface *net.Interface, resp *dhcpv4.DHCPv4) error {
|
|
||||||
p := [590]byte{12: 0x08, //ethernet layer: 14 bytes
|
|
||||||
14: 0x45, 16: 0x02, 17: 0x40, 22: 0x40, 23: 0x11, //ip layer: 20 bytes
|
|
||||||
35: 67, 37: 68, 38: 0x02, 39: 0x2c, //udp layer: 8 bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(p[0:], resp.ClientHWAddr[0:6])
|
|
||||||
copy(p[6:], iface.HardwareAddr[0:6])
|
|
||||||
copy(p[26:], resp.ServerIPAddr[0:4])
|
|
||||||
copy(p[30:], resp.YourIPAddr[0:4])
|
|
||||||
|
|
||||||
// ip layer checksum
|
|
||||||
checksum := checksum(p[14:34])
|
|
||||||
binary.BigEndian.PutUint16(p[24:], checksum)
|
|
||||||
|
|
||||||
// dhcp payload
|
|
||||||
copy(p[42:], resp.ToBytes())
|
|
||||||
|
|
||||||
// udp layer checksum, set to zero
|
|
||||||
// https://datatracker.ietf.org/doc/html/rfc768
|
|
||||||
// An all zero transmitted checksum value means that the transmitter generated no
|
|
||||||
// checksum (for debugging or for higher level protocols that don't care).
|
|
||||||
|
|
||||||
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, 0)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot open socket: %v", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err = syscall.Close(fd)
|
|
||||||
if err != nil {
|
|
||||||
log.F("dhcpd: cannot close socket: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
|
||||||
if err != nil {
|
|
||||||
log.F("dhcpd: cannot set option for socket: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var hwAddr [8]byte
|
|
||||||
copy(hwAddr[0:6], resp.ClientHWAddr[0:6])
|
|
||||||
ethAddr := syscall.SockaddrLinklayer{
|
|
||||||
Protocol: 0,
|
|
||||||
Ifindex: iface.Index,
|
|
||||||
Halen: 6,
|
|
||||||
Addr: hwAddr,
|
|
||||||
}
|
|
||||||
err = syscall.Sendto(fd, p[:], 0, ðAddr)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot send frame via socket: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checksum(bytes []byte) uint16 {
|
|
||||||
var csum uint32
|
|
||||||
for i := 0; i < len(bytes); i += 2 {
|
|
||||||
csum += uint32(bytes[i]) << 8
|
|
||||||
csum += uint32(bytes[i+1])
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
// Break when sum is less or equals to 0xFFFF
|
|
||||||
if csum <= 65535 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Add carry to the sum
|
|
||||||
csum = (csum >> 16) + uint32(uint16(csum))
|
|
||||||
}
|
|
||||||
// Flip all the bits
|
|
||||||
return ^uint16(csum)
|
|
||||||
}
|
|
@ -2,10 +2,6 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if test ! -f "/etc/glider/glider.conf"; then
|
|
||||||
cp /etc/glider/glider.conf.example /etc/glider/glider.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
/bin/systemctl daemon-reload
|
/bin/systemctl daemon-reload
|
||||||
|
|
||||||
if /bin/systemctl is-active --quiet glider@glider; then
|
if /bin/systemctl is-active --quiet glider@glider; then
|
||||||
|
Loading…
Reference in New Issue
Block a user