Compare commits

..

9 Commits

Author SHA1 Message Date
nadoo
bb439c9345 redir: fix test error
Some checks failed
Build / Build (push) Has been cancelled
2025-02-28 00:42:46 +08:00
nadoo
6aee7b35c0 chore: update math/rand to math/rand/v2 2025-02-28 00:40:20 +08:00
nadoo
8e81e09a8f pool: optimized the bytes buffer pool
Some checks are pending
Build / Build (push) Waiting to run
2025-02-26 21:44:54 +08:00
nadoo
bd40b07388 chore: update dependencies and optimize some code style 2025-02-26 21:34:43 +08:00
nadoo
40809b56a9 chore: use Go1.24
Some checks failed
Build / Build (push) Has been cancelled
2025-02-22 12:27:37 +08:00
nadoo
2f154678a9 docker: update binary folder 2024-12-21 23:36:52 +08:00
nadoo
b598c03dab chore: update dependencies 2024-12-21 20:05:13 +08:00
nadoo
1108ef29a0 chore: update dependencies 2024-08-16 22:12:40 +08:00
nadoo
708db591e9 ci: use Go1.23 2024-08-15 00:10:25 +08:00
51 changed files with 444 additions and 1515 deletions

View File

@ -7,7 +7,7 @@ RUN apk add --no-cache ca-certificates
ARG TARGETPLATFORM
RUN case $TARGETPLATFORM in \
'linux/386') \
export FOLDER='default_linux_386'; \
export FOLDER='default_linux_386_sse2'; \
;; \
'linux/amd64') \
export FOLDER='default_linux_amd64_v1'; \
@ -19,10 +19,10 @@ RUN case $TARGETPLATFORM in \
export FOLDER='default_linux_arm_7'; \
;; \
'linux/arm64') \
export FOLDER='default_linux_arm64'; \
export FOLDER='default_linux_arm64_v8.0'; \
;; \
'linux/riscv64') \
export FOLDER='default_linux_riscv64'; \
export FOLDER='default_linux_riscv64_rva20u64'; \
;; \
*) echo >&2 "error: unsupported architecture '$TARGETPLATFORM'"; exit 1 ;; \
esac \

View File

@ -35,7 +35,7 @@ jobs:
cache: true
- name: Test
run: go test -v .
run: go test -v ./...
- name: Build
uses: goreleaser/goreleaser-action@v6

2
.gitignore vendored
View File

@ -17,12 +17,14 @@
# custom
.idea
.vscode
.zed
.DS_Store
# dev test only
/dev/
dev*.go
*_test.go
dist

View File

@ -38,10 +38,10 @@ archives:
builds:
- default
wrap_in_directory: true
format: tar.gz
formats: tar.gz
format_overrides:
- goos: windows
format: zip
formats: zip
files:
- LICENSE
- README.md
@ -49,7 +49,7 @@ archives:
- systemd/*
snapshot:
name_template: '{{ incpatch .Version }}-dev-{{.ShortCommit}}'
version_template: '{{ incpatch .Version }}-dev-{{.ShortCommit}}'
checksum:
name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"

View File

@ -1,5 +1,5 @@
# Build Stage
FROM golang:1.20-alpine AS build-env
FROM golang:1.24-alpine AS build-env
ADD . /src
RUN apk --no-cache add git \
&& cd /src && go build -v -ldflags "-s -w"

View File

@ -444,7 +444,7 @@ glider -config CONFIG_PATH
## Linux Daemon
- systemd: [https://github.com/nadoo/glider/blob/master/systemd/](https://github.com/nadoo/glider/blob/master/systemd/)
- systemd: [https://github.com/nadoo/glider/tree/main/systemd](https://github.com/nadoo/glider/tree/main/systemd)
- <details> <summary>docker: click to see details</summary>

View File

@ -99,12 +99,12 @@ check=disable: disable health check`)
}
if *scheme != "" {
fmt.Fprintf(flag.Output(), proxy.Usage(*scheme))
fmt.Fprint(flag.Output(), proxy.Usage(*scheme))
os.Exit(0)
}
if *example {
fmt.Fprintf(flag.Output(), examples)
fmt.Fprint(flag.Output(), examples)
os.Exit(0)
}

View File

@ -179,7 +179,7 @@ func (c *Client) exchange(qname string, reqBytes []byte, preferTCP bool) (
ups := c.UpStream(qname)
server = ups.Server()
for i := 0; i < ups.Len(); i++ {
for range ups.Len() {
var rc net.Conn
rc, err = dialer.Dial(network, server)
if err != nil {

View File

@ -5,7 +5,7 @@ import (
"encoding/binary"
"errors"
"io"
"math/rand"
"math/rand/v2"
"net/netip"
"strings"
)
@ -146,7 +146,7 @@ func UnmarshalMessage(b []byte) (*Message, error) {
// resp answers
rrIdx := HeaderLen + qLen
for i := 0; i < int(m.Header.ANCOUNT); i++ {
for range int(m.Header.ANCOUNT) {
rr := &RR{}
rrLen, err := m.UnmarshalRR(rrIdx, rr)
if err != nil {
@ -210,10 +210,11 @@ func (h *Header) SetAncount(ancount int) {
h.ANCOUNT = uint16(ancount)
}
func (h *Header) setFlag(QR uint16, Opcode uint16, AA uint16,
TC uint16, RD uint16, RA uint16, RCODE uint16) {
h.Bits = QR<<15 + Opcode<<11 + AA<<10 + TC<<9 + RD<<8 + RA<<7 + RCODE
}
// Not used now, but keep it for future use.
// func (h *Header) setFlag(QR uint16, Opcode uint16, AA uint16,
// TC uint16, RD uint16, RA uint16, RCODE uint16) {
// 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.
func (h *Header) MarshalTo(w io.Writer) (int, error) {

25
go.mod
View File

@ -1,36 +1,29 @@
module github.com/nadoo/glider
go 1.20
go 1.24
require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d
github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb
github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152
github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
github.com/nadoo/conflag v0.3.1
github.com/nadoo/ipset v0.5.0
github.com/xtaci/kcp-go/v5 v5.6.12
golang.org/x/crypto v0.26.0
golang.org/x/sys v0.24.0
github.com/xtaci/kcp-go/v5 v5.6.18
golang.org/x/crypto v0.35.0
golang.org/x/sys v0.30.0
)
require (
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/klauspost/reedsolomon v1.12.3 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/klauspost/reedsolomon v1.12.4 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/templexxx/cpu v0.1.1 // indirect
github.com/templexxx/xorsimd v0.4.3 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
golang.org/x/net v0.28.0 // indirect
)
replace (
// Go1.20
github.com/u-root/uio => github.com/u-root/uio v0.0.0-20240207222400-ab2ff1dfd969
github.com/xtaci/kcp-go/v5 => github.com/xtaci/kcp-go/v5 v5.6.1
golang.org/x/net v0.35.0 // indirect
)

81
go.sum
View File

@ -33,99 +33,74 @@ 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.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5 h1:GkMacU5ftc+IEg1449N3UEy2XLDz58W4fkrRu2fibb8=
github.com/insomniacslk/dhcp v0.0.0-20240812123929-b105c29bd1b5/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
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.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/klauspost/reedsolomon v1.12.3 h1:tzUznbfc3OFwJaTebv/QdhnFf2Xvb7gZ24XaHLBPmdc=
github.com/klauspost/reedsolomon v1.12.3/go.mod h1:3K5rXwABAvzGeR01r6pWZieUALXO/Tq7bFKGIb4m4WI=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/reedsolomon v1.12.4 h1:5aDr3ZGoJbgu/8+j45KtUJxzYm8k08JGtB9Wx1VQ4OA=
github.com/klauspost/reedsolomon v1.12.4/go.mod h1:d3CzOMOt0JXGIFZm1StgkyF14EYr3xneR2rNWo7NcMU=
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
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/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.1.1 h1:isxHaxBXpYFWnk2DReuKkigaZyrjs2+9ypIdGP4h+HI=
github.com/templexxx/cpu v0.1.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/templexxx/xorsimd v0.4.3 h1:9AQTFHd7Bhk3dIT7Al2XeBX5DWOvsUPZCuhyAtNbHjU=
github.com/templexxx/xorsimd v0.4.3/go.mod h1:oZQcD6RFDisW2Am58dSAGwwL6rHjbzrlu25VDqfWkQg=
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/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/u-root/uio v0.0.0-20240207222400-ab2ff1dfd969 h1:4+l/zS5QI+CxIMvb02kWu7J4xMbg6nkebX64Y/X6fG8=
github.com/u-root/uio v0.0.0-20240207222400-ab2ff1dfd969/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
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/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
github.com/xtaci/kcp-go/v5 v5.6.18 h1:7oV4mc272pcnn39/13BB11Bx7hJM4ogMIEokJYVWn4g=
github.com/xtaci/kcp-go/v5 v5.6.18/go.mod h1:75S1AKYYzNUSXIv30h+jPKJYZUwqpfvLshu63nCNSOM=
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/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-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-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-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.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
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-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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
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-20190412213103-97732733099d/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-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -133,13 +108,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@ -154,9 +123,7 @@ 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.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -17,7 +17,7 @@ import (
)
var (
version = "0.16.4"
version = "0.17.0"
config = parseConfig()
)

View File

@ -3,6 +3,7 @@ package pool
import (
"math/bits"
"sync"
"unsafe"
)
const (
@ -17,11 +18,12 @@ var (
)
func init() {
for i := 0; i < num; i++ {
for i := range num {
size := 1 << i
sizes[i] = size
pools[i].New = func() any {
return make([]byte, size)
buf := make([]byte, size)
return unsafe.SliceData(buf)
}
}
}
@ -30,11 +32,10 @@ func init() {
// otherwise, this function will call make([]byte, size) directly.
func GetBuffer(size int) []byte {
if size >= 1 && size <= maxsize {
i := bits.Len32(uint32(size)) - 1
if sizes[i] < size {
i += 1
i := bits.Len32(uint32(size - 1))
if p := pools[i].Get().(*byte); p != nil {
return unsafe.Slice(p, 1<<i)[:size]
}
return pools[i].Get().([]byte)[:size]
}
return make([]byte, size)
}
@ -42,9 +43,9 @@ func GetBuffer(size int) []byte {
// PutBuffer puts a buffer into pool.
func PutBuffer(buf []byte) {
if size := cap(buf); size >= 1 && size <= maxsize {
i := bits.Len32(uint32(size)) - 1
i := bits.Len32(uint32(size - 1))
if sizes[i] == size {
pools[i].Put(buf)
pools[i].Put(unsafe.SliceData(buf))
}
}
}

View File

@ -1,21 +0,0 @@
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.

View File

@ -1,3 +1,25 @@
// 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
import (
@ -38,16 +60,18 @@ const (
// Frame defines a packet from or to be multiplexed into a single connection
type Frame struct {
ver byte
cmd byte
sid uint32
data []byte
ver byte // version
cmd byte // command
sid uint32 // stream id
data []byte // payload
}
// newFrame creates a new frame with given version, command and stream id
func newFrame(version byte, cmd byte, sid uint32) Frame {
return Frame{ver: version, cmd: cmd, sid: sid}
}
// rawHeader is a byte array representation of Frame header
type rawHeader [headerSize]byte
func (h rawHeader) Version() byte {
@ -71,6 +95,7 @@ func (h rawHeader) String() string {
h.Version(), h.Cmd(), h.StreamID(), h.Length())
}
// updHeader is a byte array representation of cmdUPD
type updHeader [szCmdUPD]byte
func (h updHeader) Consumed() uint32 {

View File

@ -1,7 +1,25 @@
// Package smux is a multiplexing library for Golang.
// MIT License
//
// 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.
// 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
import (

View File

@ -1,86 +0,0 @@
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")
}
}

View File

@ -1,3 +1,25 @@
// 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
import (
@ -6,6 +28,7 @@ import (
"errors"
"io"
"net"
"runtime"
"sync"
"sync/atomic"
"time"
@ -16,25 +39,38 @@ import (
const (
defaultAcceptBacklog = 1024
maxShaperSize = 1024
openCloseTimeout = 30 * time.Second // stream open/close timeout
openCloseTimeout = 30 * time.Second // Timeout for opening/closing streams
)
// define frame class
// CLASSID represents the class of a frame
type CLASSID int
const (
CLSCTRL CLASSID = iota
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 (
ErrInvalidProtocol = errors.New("invalid protocol")
ErrConsumed = errors.New("peer consumed more than sent")
ErrGoAway = errors.New("stream id overflows, should start a new connection")
ErrTimeout = errors.New("timeout")
ErrWouldBlock = errors.New("operation would block on IO")
ErrInvalidProtocol = errors.New("invalid protocol")
ErrConsumed = errors.New("peer consumed more than sent")
ErrGoAway = errors.New("stream id overflows, should start a new connection")
ErrTimeout net.Error = &timeoutError{}
ErrWouldBlock = errors.New("operation would block on IO")
)
// writeRequest represents a request to write a frame
type writeRequest struct {
class CLASSID
frame Frame
@ -42,6 +78,7 @@ type writeRequest struct {
result chan writeResult
}
// writeResult represents the result of a write request
type writeResult struct {
n int
err error
@ -58,7 +95,7 @@ type Session struct {
bucket int32 // token bucket
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
die chan struct{} // flag session has died
@ -77,7 +114,7 @@ type Session struct {
chProtoError chan struct{}
protoErrorOnce sync.Once
chAccepts chan *Stream
chAccepts chan *stream
dataReady int32 // flag data has arrived
@ -85,7 +122,7 @@ type Session struct {
deadline atomic.Value
requestID uint32 // write request monotonic increasing
requestID uint32 // Monotonic increasing write request ID
shaper chan writeRequest // a shaper for writing
writes chan writeRequest
}
@ -95,8 +132,8 @@ func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
s.die = make(chan struct{})
s.conn = conn
s.config = config
s.streams = make(map[uint32]*Stream)
s.chAccepts = make(chan *Stream, defaultAcceptBacklog)
s.streams = make(map[uint32]*stream)
s.chAccepts = make(chan *stream, defaultAcceptBacklog)
s.bucket = int32(config.MaxReceiveBuffer)
s.bucketNotify = make(chan struct{}, 1)
s.shaper = make(chan writeRequest)
@ -144,7 +181,7 @@ func (s *Session) OpenStream() (*Stream, error) {
stream := newStream(sid, s.config.MaxFrameSize, s)
if _, err := s.writeFrame(newFrame(byte(s.config.Version), cmdSYN, sid)); err != nil {
if _, err := s.writeControlFrame(newFrame(byte(s.config.Version), cmdSYN, sid)); err != nil {
return nil, err
}
@ -159,7 +196,14 @@ func (s *Session) OpenStream() (*Stream, error) {
return nil, io.ErrClosedPipe
default:
s.streams[sid] = stream
return stream, nil
wrapper := &Stream{stream: stream}
// NOTE(x): disabled finalizer for issue #997
/*
runtime.SetFinalizer(wrapper, func(s *Stream) {
s.Close()
})
*/
return wrapper, nil
}
}
@ -180,7 +224,11 @@ func (s *Session) AcceptStream() (*Stream, error) {
select {
case stream := <-s.chAccepts:
return stream, nil
wrapper := &Stream{stream: stream}
runtime.SetFinalizer(wrapper, func(s *Stream) {
s.Close()
})
return wrapper, nil
case <-deadline:
return nil, ErrTimeout
case <-s.chSocketReadError:
@ -302,12 +350,15 @@ func (s *Session) RemoteAddr() net.Addr {
// notify the session that a stream has closed
func (s *Session) streamClosed(sid uint32) {
s.streamLock.Lock()
if n := s.streams[sid].recycleTokens(); n > 0 { // return remaining tokens to the bucket
if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
s.notifyBucket()
if stream, ok := s.streams[sid]; ok {
n := stream.recycleTokens()
if n > 0 { // return remaining tokens to the bucket
if atomic.AddInt32(&s.bucket, int32(n)) > 0 {
s.notifyBucket()
}
}
delete(s.streams, sid)
}
delete(s.streams, sid)
s.streamLock.Unlock()
}
@ -342,7 +393,7 @@ func (s *Session) recvLoop() {
sid := hdr.StreamID()
switch hdr.Cmd() {
case cmdNOP:
case cmdSYN:
case cmdSYN: // stream opening
s.streamLock.Lock()
if _, ok := s.streams[sid]; !ok {
stream := newStream(sid, s.config.MaxFrameSize, s)
@ -353,22 +404,26 @@ func (s *Session) recvLoop() {
}
}
s.streamLock.Unlock()
case cmdFIN:
case cmdFIN: // stream closing
s.streamLock.Lock()
if stream, ok := s.streams[sid]; ok {
stream.fin()
stream.notifyReadEvent()
}
s.streamLock.Unlock()
case cmdPSH:
case cmdPSH: // data frame
if hdr.Length() > 0 {
newbuf := pool.GetBuffer(int(hdr.Length()))
if written, err := io.ReadFull(s.conn, newbuf); err == nil {
s.streamLock.Lock()
if stream, ok := s.streams[sid]; ok {
stream.pushBytes(newbuf)
// a stream used some token
atomic.AddInt32(&s.bucket, -int32(written))
stream.notifyReadEvent()
} else {
// data directed to a missing/closed stream, recycle the buffer immediately.
pool.PutBuffer(newbuf)
}
s.streamLock.Unlock()
} else {
@ -376,7 +431,7 @@ func (s *Session) recvLoop() {
return
}
}
case cmdUPD:
case cmdUPD: // a window update signal
if _, err := io.ReadFull(s.conn, updHdr[:]); err == nil {
s.streamLock.Lock()
if stream, ok := s.streams[sid]; ok {
@ -398,6 +453,7 @@ func (s *Session) recvLoop() {
}
}
// keepalive sends NOP frame to peer to keep the connection alive, and detect dead peers
func (s *Session) keepalive() {
tickerPing := time.NewTicker(s.config.KeepAliveInterval)
tickerTimeout := time.NewTicker(s.config.KeepAliveTimeout)
@ -423,7 +479,8 @@ func (s *Session) keepalive() {
}
}
// shaper shapes the sending sequence among streams
// shaperLoop implements a priority queue for write requests,
// some control messages are prioritized over data messages
func (s *Session) shaperLoop() {
var reqs shaperHeap
var next writeRequest
@ -464,6 +521,7 @@ func (s *Session) shaperLoop() {
}
}
// sendLoop sends frames to the underlying connection
func (s *Session) sendLoop() {
var buf []byte
var n int
@ -491,6 +549,7 @@ func (s *Session) sendLoop() {
binary.LittleEndian.PutUint16(buf[2:], uint16(len(request.frame.data)))
binary.LittleEndian.PutUint32(buf[4:], request.frame.sid)
// support for scatter-gather I/O
if len(vec) > 0 {
vec[0] = buf[:headerSize]
vec[1] = request.frame.data
@ -522,10 +581,13 @@ func (s *Session) sendLoop() {
}
}
// writeFrame writes the frame to the underlying connection
// writeControlFrame writes the control frame to the underlying connection
// and returns the number of bytes written if successful
func (s *Session) writeFrame(f Frame) (n int, err error) {
return s.writeFrameInternal(f, time.After(openCloseTimeout), CLSCTRL)
func (s *Session) writeControlFrame(f Frame) (n int, err error) {
timer := time.NewTimer(openCloseTimeout)
defer timer.Stop()
return s.writeFrameInternal(f, timer.C, CLSCTRL)
}
// internal writeFrame version to support deadline used in keepalive

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,42 @@
// 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
// _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 {
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
func (h shaperHeap) Len() int { return len(h) }
// 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

View File

@ -1,50 +0,0 @@
package smux
import (
"container/heap"
"testing"
)
func TestShaper(t *testing.T) {
w1 := writeRequest{seq: 1}
w2 := writeRequest{seq: 2}
w3 := writeRequest{seq: 3}
w4 := writeRequest{seq: 4}
w5 := writeRequest{seq: 5}
var reqs shaperHeap
heap.Push(&reqs, w5)
heap.Push(&reqs, w4)
heap.Push(&reqs, w3)
heap.Push(&reqs, w2)
heap.Push(&reqs, w1)
for len(reqs) > 0 {
w := heap.Pop(&reqs).(writeRequest)
t.Log("sid:", w.frame.sid, "seq:", w.seq)
}
}
func TestShaper2(t *testing.T) {
w1 := writeRequest{class: CLSDATA, seq: 1} // stream 0
w2 := writeRequest{class: CLSDATA, seq: 2}
w3 := writeRequest{class: CLSDATA, seq: 3}
w4 := writeRequest{class: CLSDATA, seq: 4}
w5 := writeRequest{class: CLSDATA, seq: 5}
w6 := writeRequest{class: CLSCTRL, seq: 6, frame: Frame{sid: 10}} // ctrl 1
w7 := writeRequest{class: CLSCTRL, seq: 7, frame: Frame{sid: 11}} // ctrl 2
var reqs shaperHeap
heap.Push(&reqs, w6)
heap.Push(&reqs, w5)
heap.Push(&reqs, w4)
heap.Push(&reqs, w3)
heap.Push(&reqs, w2)
heap.Push(&reqs, w1)
heap.Push(&reqs, w7)
for len(reqs) > 0 {
w := heap.Pop(&reqs).(writeRequest)
t.Log("sid:", w.frame.sid, "seq:", w.seq)
}
}

View File

@ -1,3 +1,25 @@
// 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
import (
@ -11,36 +33,41 @@ import (
"github.com/nadoo/glider/pkg/pool"
)
// Stream implements net.Conn
// wrapper for GC
type Stream struct {
id uint32
*stream
}
// Stream implements net.Conn
type stream struct {
id uint32 // Stream identifier
sess *Session
buffers [][]byte
heads [][]byte // slice heads kept for recycle
buffers [][]byte // the sequential buffers of stream
heads [][]byte // slice heads of the buffers above, kept for recycle
bufferLock sync.Mutex
frameSize int
bufferLock sync.Mutex // Mutex to protect access to buffers
frameSize int // Maximum frame size for the stream
// notify a read event
chReadEvent chan struct{}
// flag the stream has closed
die chan struct{}
dieOnce sync.Once
dieOnce sync.Once // Ensures die channel is closed only once
// FIN command
chFinEvent chan struct{}
finEventOnce sync.Once
finEventOnce sync.Once // Ensures chFinEvent is closed only once
// deadlines
readDeadline atomic.Value
writeDeadline atomic.Value
// per stream sliding window control
numRead uint32 // number of consumed bytes
numRead uint32 // count num of bytes read
numWritten uint32 // count num of bytes written
incr uint32 // counting for sending
incr uint32 // bytes sent since last window update
// UPD command
peerConsumed uint32 // num of bytes the peer has consumed
@ -48,9 +75,9 @@ type Stream struct {
chUpdate chan struct{} // notify of remote data consuming and window update
}
// newStream initiates a Stream struct
func newStream(id uint32, frameSize int, sess *Session) *Stream {
s := new(Stream)
// newStream initializes and returns a new Stream.
func newStream(id uint32, frameSize int, sess *Session) *stream {
s := new(stream)
s.id = id
s.chReadEvent = make(chan struct{}, 1)
s.chUpdate = make(chan struct{}, 1)
@ -59,16 +86,17 @@ func newStream(id uint32, frameSize int, sess *Session) *Stream {
s.die = make(chan struct{})
s.chFinEvent = make(chan struct{})
s.peerWindow = initialPeerWindow // set to initial window size
return s
}
// ID returns the unique stream ID.
func (s *Stream) ID() uint32 {
// ID returns the stream's unique identifier.
func (s *stream) ID() uint32 {
return s.id
}
// Read implements net.Conn
func (s *Stream) Read(b []byte) (n int, err error) {
// Read reads data from the stream into the provided buffer.
func (s *stream) Read(b []byte) (n int, err error) {
for {
n, err = s.tryRead(b)
if err == ErrWouldBlock {
@ -81,8 +109,8 @@ func (s *Stream) Read(b []byte) (n int, err error) {
}
}
// tryRead is the nonblocking version of Read
func (s *Stream) tryRead(b []byte) (n int, err error) {
// tryRead attempts to read data from the stream without blocking.
func (s *stream) tryRead(b []byte) (n int, err error) {
if s.sess.config.Version == 2 {
return s.tryReadv2(b)
}
@ -91,6 +119,7 @@ func (s *Stream) tryRead(b []byte) (n int, err error) {
return 0, nil
}
// A critical section to copy data from buffers to
s.bufferLock.Lock()
if len(s.buffers) > 0 {
n = copy(b, s.buffers[0])
@ -118,7 +147,8 @@ func (s *Stream) tryRead(b []byte) (n int, err error) {
}
}
func (s *Stream) tryReadv2(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) {
if len(b) == 0 {
return 0, nil
}
@ -139,20 +169,25 @@ func (s *Stream) tryReadv2(b []byte) (n int, err error) {
// in an ideal environment:
// if more than half of buffer has consumed, send read ack to peer
// based on round-trip time of ACK, continuous flowing data
// won't slow down because of waiting for ACK, as long as the
// consumer keeps on reading data
// s.numRead == n also notify window at the first read
// based on round-trip time of ACK, continous flowing data
// won't slow down due to waiting for ACK, as long as the
// consumer keeps on reading data.
//
// s.numRead == n implies that it's the initial reading
s.numRead += 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) {
notifyConsumed = s.numRead
s.incr = 0
s.incr = 0 // reset couting for next window update
}
s.bufferLock.Unlock()
if n > 0 {
s.sess.returnTokens(n)
// send window update if necessary
if notifyConsumed > 0 {
err := s.sendWindowUpdate(notifyConsumed)
return n, err
@ -170,7 +205,12 @@ func (s *Stream) tryReadv2(b []byte) (n int, err error) {
}
// WriteTo implements io.WriteTo
func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
// WriteTo writes data to w until there's no more data to write or when an error occurs.
// 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 {
return s.writeTov2(w)
}
@ -187,6 +227,7 @@ func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
if buf != nil {
nw, ew := w.Write(buf)
// NOTE: WriteTo is a reader, so we need to return tokens here
s.sess.returnTokens(len(buf))
pool.PutBuffer(buf)
if nw > 0 {
@ -202,7 +243,8 @@ func (s *Stream) WriteTo(w io.Writer) (n int64, err error) {
}
}
func (s *Stream) writeTov2(w io.Writer) (n int64, err error) {
// check comments in WriteTo
func (s *stream) writeTov2(w io.Writer) (n int64, err error) {
for {
var notifyConsumed uint32
var buf []byte
@ -222,6 +264,7 @@ func (s *Stream) writeTov2(w io.Writer) (n int64, err error) {
if buf != nil {
nw, ew := w.Write(buf)
// NOTE: WriteTo is a reader, so we need to return tokens here
s.sess.returnTokens(len(buf))
pool.PutBuffer(buf)
if nw > 0 {
@ -243,7 +286,8 @@ func (s *Stream) writeTov2(w io.Writer) (n int64, err error) {
}
}
func (s *Stream) sendWindowUpdate(consumed uint32) error {
// sendWindowUpdate sends a window update frame to the peer.
func (s *stream) sendWindowUpdate(consumed uint32) error {
var timer *time.Timer
var deadline <-chan time.Time
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
@ -257,11 +301,12 @@ func (s *Stream) sendWindowUpdate(consumed uint32) error {
binary.LittleEndian.PutUint32(hdr[:], consumed)
binary.LittleEndian.PutUint32(hdr[4:], uint32(s.sess.config.MaxStreamBuffer))
frame.data = hdr[:]
_, err := s.sess.writeFrameInternal(frame, deadline, CLSDATA)
_, err := s.sess.writeFrameInternal(frame, deadline, CLSCTRL)
return err
}
func (s *Stream) waitRead() error {
// waitRead blocks until a read event occurs or a deadline is reached.
func (s *stream) waitRead() error {
var timer *time.Timer
var deadline <-chan time.Time
if d, ok := s.readDeadline.Load().(time.Time); ok && !d.IsZero() {
@ -271,10 +316,10 @@ func (s *Stream) waitRead() error {
}
select {
case <-s.chReadEvent:
case <-s.chReadEvent: // notify some data has arrived, or closed
return nil
case <-s.chFinEvent:
// BUG(xtaci): Fix for https://github.com/xtaci/smux/issues/82
// BUGFIX(xtaci): Fix for https://github.com/xtaci/smux/issues/82
s.bufferLock.Lock()
defer s.bufferLock.Unlock()
if len(s.buffers) > 0 {
@ -297,7 +342,7 @@ func (s *Stream) waitRead() error {
//
// Note that the behavior when multiple goroutines write concurrently is not deterministic,
// 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 {
return s.writeV2(b)
}
@ -311,6 +356,8 @@ func (s *Stream) Write(b []byte) (n int, err error) {
// check if stream has closed
select {
case <-s.chFinEvent: // passive closing
return 0, io.EOF
case <-s.die:
return 0, io.ErrClosedPipe
default:
@ -338,7 +385,8 @@ func (s *Stream) Write(b []byte) (n int, err error) {
return sent, nil
}
func (s *Stream) writeV2(b []byte) (n int, err error) {
// writeV2 writes data to the stream for version 2 streams.
func (s *stream) writeV2(b []byte) (n int, err error) {
// check empty input
if len(b) == 0 {
return 0, nil
@ -346,6 +394,8 @@ func (s *Stream) writeV2(b []byte) (n int, err error) {
// check if stream has closed
select {
case <-s.chFinEvent:
return 0, io.EOF
case <-s.die:
return 0, io.ErrClosedPipe
default:
@ -372,14 +422,18 @@ func (s *Stream) writeV2(b []byte) (n int, err error) {
// even if uint32 overflow, this math still works:
// eg1: uint32(0) - uint32(math.MaxUint32) = 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))
if inflight < 0 {
if inflight < 0 { // security check for malformed data
return 0, ErrConsumed
}
// make sure you understand 'win' is calculated in modular arithmetic(2^32(4GB))
win := int32(atomic.LoadUint32(&s.peerWindow)) - inflight
if win > 0 {
// determine how many bytes to send
if win > int32(len(b)) {
bts = b
b = nil
@ -388,13 +442,17 @@ func (s *Stream) writeV2(b []byte) (n int, err error) {
b = b[win:]
}
// frame split and transmit
for len(bts) > 0 {
// splitting frame
sz := len(bts)
if sz > s.frameSize {
sz = s.frameSize
}
frame.data = bts[:sz]
bts = bts[sz:]
// transmit of frame
n, err := s.sess.writeFrameInternal(frame, deadline, CLSDATA)
atomic.AddUint32(&s.numWritten, uint32(sz))
sent += n
@ -404,12 +462,12 @@ func (s *Stream) writeV2(b []byte) (n int, err error) {
}
}
// if there is any data remaining to be sent
// if there is any data left to be sent,
// wait until stream closes, window changes or deadline reached
// this blocking behavior will inform upper layer to do flow control
// this blocking behavior will back propagate flow control to upper layer.
if len(b) > 0 {
select {
case <-s.chFinEvent: // if fin arrived, future window update is impossible
case <-s.chFinEvent:
return 0, io.EOF
case <-s.die:
return sent, io.ErrClosedPipe
@ -417,7 +475,7 @@ func (s *Stream) writeV2(b []byte) (n int, err error) {
return sent, ErrTimeout
case <-s.sess.chSocketWriteError:
return sent, s.sess.socketWriteError.Load().(error)
case <-s.chUpdate:
case <-s.chUpdate: // notify of remote data consuming and window update
continue
}
} else {
@ -427,7 +485,7 @@ func (s *Stream) writeV2(b []byte) (n int, err error) {
}
// Close implements net.Conn
func (s *Stream) Close() error {
func (s *stream) Close() error {
var once bool
var err error
s.dieOnce.Do(func() {
@ -436,7 +494,13 @@ func (s *Stream) Close() error {
})
if once {
_, err = s.sess.writeFrame(newFrame(byte(s.sess.config.Version), cmdFIN, s.id))
// send FIN in order
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)
return err
} else {
@ -446,14 +510,14 @@ func (s *Stream) Close() error {
// GetDieCh returns a readonly chan which can be readable
// when the stream is to be closed.
func (s *Stream) GetDieCh() <-chan struct{} {
func (s *stream) GetDieCh() <-chan struct{} {
return s.die
}
// SetReadDeadline sets the read deadline as defined by
// net.Conn.SetReadDeadline.
// 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.notifyReadEvent()
return nil
@ -462,7 +526,7 @@ func (s *Stream) SetReadDeadline(t time.Time) error {
// SetWriteDeadline sets the write deadline as defined by
// net.Conn.SetWriteDeadline.
// 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)
return nil
}
@ -470,7 +534,7 @@ func (s *Stream) SetWriteDeadline(t time.Time) error {
// SetDeadline sets both read and write deadlines as defined by
// net.Conn.SetDeadline.
// 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 {
return err
}
@ -481,10 +545,10 @@ func (s *Stream) SetDeadline(t time.Time) error {
}
// 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
func (s *Stream) LocalAddr() net.Addr {
func (s *stream) LocalAddr() net.Addr {
if ts, ok := s.sess.conn.(interface {
LocalAddr() net.Addr
}); ok {
@ -494,7 +558,7 @@ func (s *Stream) LocalAddr() net.Addr {
}
// RemoteAddr satisfies net.Conn interface
func (s *Stream) RemoteAddr() net.Addr {
func (s *stream) RemoteAddr() net.Addr {
if ts, ok := s.sess.conn.(interface {
RemoteAddr() net.Addr
}); ok {
@ -504,7 +568,7 @@ func (s *Stream) RemoteAddr() net.Addr {
}
// 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.buffers = append(s.buffers, buf)
s.heads = append(s.heads, buf)
@ -513,7 +577,7 @@ func (s *Stream) pushBytes(buf []byte) (written int, err error) {
}
// recycleTokens transform remaining bytes to tokens(will truncate buffer)
func (s *Stream) recycleTokens() (n int) {
func (s *stream) recycleTokens() (n int) {
s.bufferLock.Lock()
for k := range s.buffers {
n += len(s.buffers[k])
@ -526,7 +590,7 @@ func (s *Stream) recycleTokens() (n int) {
}
// notify read event
func (s *Stream) notifyReadEvent() {
func (s *stream) notifyReadEvent() {
select {
case s.chReadEvent <- struct{}{}:
default:
@ -534,7 +598,7 @@ func (s *Stream) notifyReadEvent() {
}
// 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.peerWindow, window)
select {
@ -544,7 +608,7 @@ func (s *Stream) update(consumed uint32, window uint32) {
}
// mark this stream has been closed in protocol
func (s *Stream) fin() {
func (s *stream) fin() {
s.finEventOnce.Do(func() {
close(s.chFinEvent)
})

View File

@ -33,6 +33,8 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
}
buf := pool.GetBytesBuffer()
defer pool.PutBytesBuffer(buf)
buf.WriteString("CONNECT " + addr + " HTTP/1.1\r\n")
buf.WriteString("Host: " + addr + "\r\n")
buf.WriteString("Proxy-Connection: Keep-Alive\r\n")
@ -45,7 +47,6 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) {
// header ended
buf.WriteString("\r\n")
_, err = rc.Write(buf.Bytes())
pool.PutBytesBuffer(buf)
if err != nil {
return nil, err
}

View File

@ -64,10 +64,7 @@ func (c *TLSObfsConn) Write(b []byte) (int, error) {
n := len(b)
for i := 0; i < n; i += chunkSize {
buf.Reset()
end := i + chunkSize
if end > n {
end = n
}
end := min(i+chunkSize, n)
buf.Write([]byte{0x17, 0x03, 0x03})
binary.Write(buf, binary.BigEndian, uint16(len(b[i:end])))

View File

@ -60,7 +60,7 @@ func (s *RedirProxy) ListenAndServe() {
return
}
log.F("[redir] listening TCP on " + s.addr)
log.F("[redir] listening TCP on %s", s.addr)
for {
c, err := l.Accept()

View File

@ -5,10 +5,10 @@ import (
"crypto/cipher"
"crypto/des"
"crypto/md5"
"crypto/rand"
"crypto/rc4"
"encoding/binary"
"errors"
"math/rand"
"github.com/aead/chacha20"
"github.com/dgryski/go-camellia"

View File

@ -8,9 +8,7 @@ import (
"bytes"
"errors"
"fmt"
"math/rand"
"net"
"time"
"github.com/nadoo/glider/pkg/pool"
"github.com/nadoo/glider/proxy"
@ -21,10 +19,6 @@ import (
var bufSize = proxy.TCPBufSize
func init() {
rand.Seed(time.Now().UnixNano())
}
// SSTCPConn the struct that override the net.Conn methods
type SSTCPConn struct {
net.Conn

View File

@ -1,7 +1,7 @@
package obfs
import (
"math/rand"
"math/rand/v2"
)
func init() {
@ -13,7 +13,7 @@ func newHttpPost() IObfs {
// newHttpSimple create a http_simple object
t := &httpSimplePost{
userAgentIndex: rand.Intn(len(requestUserAgent)),
userAgentIndex: rand.IntN(len(requestUserAgent)),
methodGet: false,
}
return t

View File

@ -4,7 +4,7 @@ import (
"bytes"
"encoding/hex"
"fmt"
"math/rand"
"math/rand/v2"
"strings"
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
@ -55,7 +55,7 @@ func newHttpSimple() IObfs {
t := &httpSimplePost{
rawTransSent: false,
rawTransReceived: false,
userAgentIndex: rand.Intn(len(requestUserAgent)),
userAgentIndex: rand.IntN(len(requestUserAgent)),
methodGet: true,
}
return t
@ -80,14 +80,14 @@ func (t *httpSimplePost) GetData() any {
func (t *httpSimplePost) boundary() (ret string) {
set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
for i := 0; i < 32; i++ {
ret = fmt.Sprintf("%s%c", ret, set[rand.Intn(len(set))])
for range 32 {
ret = fmt.Sprintf("%s%c", ret, set[rand.IntN(len(set))])
}
return
}
func (t *httpSimplePost) data2URLEncode(data []byte) (ret string) {
for i := 0; i < len(data); i++ {
for i := range data {
ret = fmt.Sprintf("%s%%%s", ret, hex.EncodeToString([]byte{data[i]}))
}
return
@ -101,12 +101,12 @@ func (t *httpSimplePost) Encode(data []byte) (encodedData []byte, err error) {
dataLength := len(data)
var headData []byte
if headSize := t.IVLen + t.HeadLen; dataLength-headSize > 64 {
headData = make([]byte, headSize+rand.Intn(64))
headData = make([]byte, headSize+rand.IntN(64))
} else {
headData = make([]byte, dataLength)
}
copy(headData, data[0:len(headData)])
requestPathIndex := rand.Intn(len(requestPath)/2) * 2
requestPathIndex := rand.IntN(len(requestPath)/2) * 2
host := t.Host
var customHead string
@ -122,7 +122,7 @@ func (t *httpSimplePost) Encode(data []byte) (encodedData []byte, err error) {
}
hosts := strings.Split(param, ",")
if len(hosts) > 0 {
host = strings.TrimSpace(hosts[rand.Intn(len(hosts))])
host = strings.TrimSpace(hosts[rand.IntN(len(hosts))])
}
}
method := "GET /"

View File

@ -1,7 +1,8 @@
package obfs
import (
"math/rand"
crand "crypto/rand"
"math/rand/v2"
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
)
@ -57,9 +58,9 @@ func (r *randomHead) Encode(data []byte) (encodedData []byte, err error) {
r.rawTransSent = true
}
} else {
size := rand.Intn(96) + 8
size := rand.IntN(96) + 8
encodedData = make([]byte, size)
rand.Read(encodedData)
crand.Read(encodedData)
ssr.SetCRC32(encodedData, size)
d := make([]byte, dataLength)

View File

@ -3,10 +3,11 @@ package obfs
import (
"bytes"
"crypto/hmac"
crand "crypto/rand"
"encoding/binary"
"fmt"
"log"
"math/rand"
"math/rand/v2"
"strings"
"time"
@ -64,7 +65,7 @@ func (t *tls12TicketAuth) GetData() any {
t.data = &tlsAuthData{}
b := make([]byte, 32)
rand.Read(b)
crand.Read(b)
copy(t.data.localClientID[:], b)
}
return t.data
@ -76,7 +77,7 @@ func (t *tls12TicketAuth) getHost() string {
hosts := strings.Split(t.Param, ",")
if len(hosts) > 0 {
host = hosts[rand.Intn(len(hosts))]
host = hosts[rand.IntN(len(hosts))]
host = strings.TrimSpace(host)
}
}
@ -96,7 +97,6 @@ func packData(prefixData []byte, suffixData []byte) (outData []byte) {
func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
encodedData = make([]byte, 0)
rand.Seed(time.Now().UnixNano())
switch t.handshakeStatus {
case 8:
if len(data) < 1024 {
@ -108,7 +108,7 @@ func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
start := 0
var l int
for len(data)-start > 2048 {
l = rand.Intn(4096) + 100
l = rand.IntN(4096) + 100
if l > len(data)-start {
l = len(data) - start
}
@ -129,7 +129,7 @@ func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
start := 0
var l int
for len(data)-start > 2048 {
l = rand.Intn(4096) + 100
l = rand.IntN(4096) + 100
if 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)
handshakeFinish := []byte("\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00\x20")
copy(hmacData, handshakeFinish)
rand.Read(hmacData[11:33])
crand.Read(hmacData[11:33])
h := t.hmacSHA1(hmacData[:33])
copy(hmacData[33:], h)
encodedData = append(hmacData, t.sendSaver...)
@ -169,11 +169,11 @@ func (t *tls12TicketAuth) Encode(data []byte) (encodedData []byte, err error) {
tlsDataLen += len(sni)
copy(tlsData[tlsDataLen:], tlsData2)
tlsDataLen += len(tlsData2)
ticketLen := rand.Intn(164)*2 + 64
ticketLen := rand.IntN(164)*2 + 64
tlsData[tlsDataLen-1] = uint8(ticketLen & 0xff)
tlsData[tlsDataLen-2] = uint8(ticketLen >> 8)
//ticketLen := 208
rand.Read(tlsData[tlsDataLen : tlsDataLen+ticketLen])
crand.Read(tlsData[tlsDataLen : tlsDataLen+ticketLen])
tlsDataLen += ticketLen
copy(tlsData[tlsDataLen:], tlsData3)
tlsDataLen += len(tlsData3)
@ -278,7 +278,7 @@ func (t *tls12TicketAuth) packAuthData() (outData []byte) {
now := time.Now().Unix()
binary.BigEndian.PutUint32(outData[0:4], uint32(now))
rand.Read(outData[4 : 4+18])
crand.Read(outData[4 : 4+18])
hash := t.hmacSHA1(outData[:outSize-ssr.ObfsHMACSHA1Len])
copy(outData[outSize-ssr.ObfsHMACSHA1Len:], hash)

View File

@ -4,9 +4,10 @@ import (
"bytes"
"crypto/aes"
"crypto/cipher"
crand "crypto/rand"
"encoding/base64"
"encoding/binary"
"math/rand"
"math/rand/v2"
"strconv"
"strings"
"time"
@ -74,15 +75,14 @@ func (a *authAES128) GetData() any {
func (a *authAES128) packData(data []byte) (outData []byte) {
dataLength := len(data)
randLength := 1
rand.Seed(time.Now().UnixNano())
if dataLength <= 1200 {
if a.packID > 4 {
randLength += rand.Intn(32)
randLength += rand.IntN(32)
} else {
if dataLength > 900 {
randLength += rand.Intn(128)
randLength += rand.IntN(128)
} 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])
copy(outData[2:4], h[:2])
// 4~rand length+4, rand number
rand.Read(outData[4 : 4+randLength])
crand.Read(outData[4 : 4+randLength])
// 4, rand length
if randLength < 128 {
outData[4] = byte(randLength & 0xFF)
@ -121,11 +121,10 @@ func (a *authAES128) packData(data []byte) (outData []byte) {
func (a *authAES128) packAuthData(data []byte) (outData []byte) {
dataLength := len(data)
var randLength int
rand.Seed(time.Now().UnixNano())
if dataLength > 400 {
randLength = rand.Intn(512)
randLength = rand.IntN(512)
} else {
randLength = rand.Intn(1024)
randLength = rand.IntN(1024)
}
dataOffset := randLength + 16 + 4 + 4 + 7
@ -136,7 +135,7 @@ func (a *authAES128) packAuthData(data []byte) (outData []byte) {
copy(key, a.IV)
copy(key[a.IVLen:], a.Key)
rand.Read(outData[dataOffset-randLength:])
crand.Read(outData[dataOffset-randLength:])
a.data.mutex.Lock()
a.data.connectionID++
if a.data.connectionID > 0xFF000000 {
@ -144,9 +143,9 @@ func (a *authAES128) packAuthData(data []byte) (outData []byte) {
}
if len(a.data.clientID) == 0 {
a.data.clientID = make([]byte, 8)
rand.Read(a.data.clientID)
crand.Read(a.data.clientID)
b := make([]byte, 4)
rand.Read(b)
crand.Read(b)
a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
}
copy(encrypt[4:], a.data.clientID)
@ -163,13 +162,13 @@ func (a *authAES128) packAuthData(data []byte) (outData []byte) {
uid := make([]byte, 4)
if len(params) >= 2 {
if userID, err := strconv.ParseUint(params[0], 10, 32); err != nil {
rand.Read(uid)
crand.Read(uid)
} else {
binary.LittleEndian.PutUint32(uid, uint32(userID))
a.userKey = a.hashDigest([]byte(params[1]))
}
} else {
rand.Read(uid)
crand.Read(uid)
}
if a.userKey == nil {
@ -196,7 +195,7 @@ func (a *authAES128) packAuthData(data []byte) (outData []byte) {
h := a.hmac(key, encrypt[0:20])
copy(encrypt[20:], h[:4])
rand.Read(outData[0:1])
crand.Read(outData[0:1])
h = a.hmac(key, outData[0:1])
copy(outData[1:], h[0:7-1])

View File

@ -6,9 +6,9 @@ import (
"bytes"
"crypto/aes"
stdCipher "crypto/cipher"
"crypto/rand"
"encoding/base64"
"encoding/binary"
"math/rand"
"strconv"
"strings"
"time"
@ -194,7 +194,7 @@ func (a *authChainA) packAuthData(data []byte) (outData []byte) {
copy(a.userKey, a.Key)
}
}
for i := 0; i < 4; i++ {
for i := range 4 {
uid[i] = a.uid[i] ^ a.lastClientHash[8+i]
}
base64UserKey = base64.StdEncoding.EncodeToString(a.userKey)

View File

@ -34,14 +34,14 @@ func (a *authChainA) authChainBInitDataSize() {
random.InitFromBin(a.Key)
length := random.Next()%8 + 4
a.dataSizeList = make([]int, length)
for i := 0; i < int(length); i++ {
for i := range int(length) {
a.dataSizeList[i] = int(random.Next() % 2340 % 2040 % 1440)
}
sort.Ints(a.dataSizeList)
length = random.Next()%16 + 8
a.dataSizeList2 = make([]int, length)
for i := 0; i < int(length); i++ {
for i := range int(length) {
a.dataSizeList2[i] = int(random.Next() % 2340 % 2040 % 1440)
}
sort.Ints(a.dataSizeList2)

View File

@ -2,8 +2,9 @@ package protocol
import (
"bytes"
crand "crypto/rand"
"encoding/binary"
"math/rand"
"math/rand/v2"
"time"
"github.com/nadoo/glider/proxy/ssr/internal/ssr"
@ -53,9 +54,9 @@ func (a *authSHA1v4) packData(data []byte) (outData []byte) {
if dataLength <= 1300 {
if dataLength > 400 {
randLength += rand.Intn(128)
randLength += rand.IntN(128)
} else {
randLength += rand.Intn(1024)
randLength += rand.IntN(1024)
}
}
@ -90,9 +91,9 @@ func (a *authSHA1v4) packAuthData(data []byte) (outData []byte) {
randLength := 1
if dataLength <= 1300 {
if dataLength > 400 {
randLength += rand.Intn(128)
randLength += rand.IntN(128)
} else {
randLength += rand.Intn(1024)
randLength += rand.IntN(1024)
}
}
dataOffset := randLength + 4 + 2
@ -104,9 +105,9 @@ func (a *authSHA1v4) packAuthData(data []byte) (outData []byte) {
}
if len(a.data.clientID) == 0 {
a.data.clientID = make([]byte, 8)
rand.Read(a.data.clientID)
crand.Read(a.data.clientID)
b := make([]byte, 4)
rand.Read(b)
crand.Read(b)
a.data.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
}
// 0-1, out length
@ -122,7 +123,7 @@ func (a *authSHA1v4) packAuthData(data []byte) (outData []byte) {
// 2~6, crc of out length+salt+key
binary.LittleEndian.PutUint32(outData[2:], crc32)
// 6~rand length+6, rand numbers
rand.Read(outData[dataOffset-randLength : dataOffset])
crand.Read(outData[dataOffset-randLength : dataOffset])
// 6, rand length
if randLength < 128 {
outData[6] = byte(randLength & 0xFF)

View File

@ -11,7 +11,7 @@ func init() {
}
func createCRC32Table() {
for i := 0; i < 256; i++ {
for i := range 256 {
crc := uint32(i)
for j := 8; j > 0; j-- {
if crc&1 == 1 {

View File

@ -37,7 +37,7 @@ func (ctx *Shift128plusContext) InitFromBinDatalen(bin []byte, datalen int) {
ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8])
ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:])
for i := 0; i < 4; i++ {
for range 4 {
ctx.Next()
}
}

View File

@ -1,3 +1,5 @@
//go:build linux
package tproxy
import (

View File

@ -1,3 +1,5 @@
//go:build linux
package tproxy
import (

View File

@ -5,13 +5,14 @@ import (
"crypto/cipher"
"crypto/hmac"
"crypto/md5"
crand "crypto/rand"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"hash/fnv"
"io"
"math/rand"
"math/rand/v2"
"net"
"runtime"
"strings"
@ -121,7 +122,7 @@ func NewClient(uuidStr, security string, alterID int, aead bool) (*Client, error
// NewConn returns a new vmess conn.
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}
var err error
@ -131,12 +132,12 @@ func (c *Client) NewConn(rc net.Conn, target string, cmd CmdType) (*Conn, error)
}
randBytes := pool.GetBuffer(32)
rand.Read(randBytes)
crand.Read(randBytes)
copy(conn.reqBodyIV[:], randBytes[:16])
copy(conn.reqBodyKey[:], randBytes[16:32])
pool.PutBuffer(randBytes)
conn.reqRespV = byte(rand.Intn(1 << 8))
conn.reqRespV = byte(rand.IntN(1 << 8))
if conn.aead {
bodyIV := sha256.Sum256(conn.reqBodyIV[:])
@ -192,7 +193,7 @@ func (c *Conn) Request(cmd CmdType) error {
buf.WriteByte(c.opt) // Opt
// pLen and Sec
paddingLen := rand.Intn(16)
paddingLen := rand.IntN(16)
pSec := byte(paddingLen<<4) | c.security // P(4bit) and Sec(4bit)
buf.WriteByte(pSec)
@ -207,7 +208,7 @@ func (c *Conn) Request(cmd CmdType) error {
// padding
if paddingLen > 0 {
padding := pool.GetBuffer(paddingLen)
rand.Read(padding)
crand.Read(padding)
buf.Write(padding)
pool.PutBuffer(padding)
}

View File

@ -43,7 +43,7 @@ func nextID(oldID [16]byte) (newID [16]byte) {
func (u *User) GenAlterIDUsers(alterID int) []*User {
users := make([]*User, alterID)
preID := u.UUID
for i := 0; i < alterID; i++ {
for i := range alterID {
newID := nextID(preID)
// 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}

View File

@ -1,3 +1,5 @@
//go:build linux
package vsock
import (

View File

@ -1,3 +1,5 @@
//go:build linux
package vsock
import (

View File

@ -1,6 +1,7 @@
//go:build linux
// Source code from:
// https://github.com/linuxkit/virtsock/tree/master/pkg/vsock
package vsock
import (

View File

@ -1,3 +1,5 @@
//go:build linux
package vsock
import (

View File

@ -25,7 +25,7 @@ package ws
import (
"encoding/binary"
"io"
"math/rand"
"math/rand/v2"
"net"
"github.com/nadoo/glider/pkg/pool"
@ -93,7 +93,7 @@ func (w *frameWriter) Write(b []byte) (int, error) {
defer pool.PutBuffer(payload)
// payload with mask
for i := 0; i < nPayload; i++ {
for i := range nPayload {
payload[i] = b[i] ^ w.maskKey[i%4]
}

View File

@ -225,7 +225,7 @@ func (p *FwdrGroup) Check() {
log.F("[group] %s: using check config: %s", p.name, p.config.Check)
for i := 0; i < len(p.fwdrs); i++ {
for i := range p.fwdrs {
go p.check(p.fwdrs[i], checker)
}
}

View File

@ -1,3 +1,5 @@
//go:build linux
package dhcpd
import (

View File

@ -1,3 +1,5 @@
//go:build linux
package dhcpd
import (

View File

@ -1,9 +1,11 @@
//go:build linux
package dhcpd
import (
"bytes"
"errors"
"math/rand"
"math/rand/v2"
"net"
"net/netip"
"sync"
@ -43,7 +45,7 @@ func NewPool(lease time.Duration, start, end netip.Addr) (*Pool, error) {
go func() {
for now := range time.Tick(time.Second) {
p.mutex.Lock()
for i := 0; i < len(items); i++ {
for i := range len(items) {
if !items[i].expire.IsZero() && now.After(items[i].expire) {
items[i].mac = nil
items[i].expire = time.Time{}
@ -81,7 +83,7 @@ func (p *Pool) LeaseIP(mac net.HardwareAddr, ip netip.Addr) (netip.Addr, error)
}
// lease new ip
idx := rand.Intn(len(p.items))
idx := rand.IntN(len(p.items))
for _, item := range p.items[idx:] {
if item.mac == nil {
item.mac = mac

View File

@ -1,3 +1,5 @@
//go:build linux
package dhcpd
import (