mirror of
https://github.com/SagerNet/sing-shadowsocks.git
synced 2025-04-04 12:27:39 +03:00
Compare commits
20 commits
Author | SHA1 | Date | |
---|---|---|---|
|
33d53914d7 | ||
|
54dd1546c6 | ||
|
e3930e4fe3 | ||
|
a9d165b21b | ||
|
fed8d42732 | ||
|
f86df97602 | ||
|
0c1612d823 | ||
|
126234728c | ||
|
a446ff2f57 | ||
|
b044960bd3 | ||
|
f3f7b6309b | ||
|
d83f8fe119 | ||
|
f772573405 | ||
|
6489261191 | ||
|
39baf9c17e | ||
|
1a7c32b4e2 | ||
|
5c830455eb | ||
|
17988ac0d3 | ||
|
59babbdfa5 | ||
|
769c01d6bb |
21 changed files with 560 additions and 337 deletions
43
.github/workflows/debug.yml
vendored
43
.github/workflows/debug.yml
vendored
|
@ -1,43 +0,0 @@
|
|||
name: Debug build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/debug.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Debug build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get latest go version
|
||||
id: version
|
||||
run: |
|
||||
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
- name: Add cache to Go proxy
|
||||
run: |
|
||||
version=`git rev-parse HEAD`
|
||||
mkdir build
|
||||
pushd build
|
||||
go mod init build
|
||||
go get -v github.com/sagernet/sing-shadowsocks@$version
|
||||
popd
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
18
.github/workflows/lint.yml
vendored
18
.github/workflows/lint.yml
vendored
|
@ -1,8 +1,9 @@
|
|||
name: Lint
|
||||
name: lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
@ -10,6 +11,7 @@ on:
|
|||
- '!.github/workflows/lint.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
|
@ -18,24 +20,20 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Get latest go version
|
||||
id: version
|
||||
run: |
|
||||
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ steps.version.outputs.go_version }}
|
||||
go-version: ^1.22
|
||||
- name: Cache go module
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
key: go-${{ hashFiles('**/go.sum') }}
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
112
.github/workflows/test.yml
vendored
Normal file
112
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '.github/**'
|
||||
- '!.github/workflows/debug.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Linux
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build_go118:
|
||||
name: Linux (Go 1.18)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.18
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build_go120:
|
||||
name: Linux (Go 1.20)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.20
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build_go121:
|
||||
name: Linux (Go 1.21)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ~1.21
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build_windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
||||
build_darwin:
|
||||
name: macOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ^1.22
|
||||
continue-on-error: true
|
||||
- name: Build
|
||||
run: |
|
||||
make test
|
|
@ -13,5 +13,3 @@ linters-settings:
|
|||
- standard
|
||||
- prefix(github.com/sagernet/)
|
||||
- default
|
||||
staticcheck:
|
||||
go: '1.20'
|
||||
|
|
2
Makefile
2
Makefile
|
@ -1,7 +1,7 @@
|
|||
fmt:
|
||||
@gofumpt -l -w .
|
||||
@gofmt -s -w .
|
||||
@gci write --custom-order -s "standard,prefix(github.com/sagernet/),default" .
|
||||
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
||||
|
||||
fmt_install:
|
||||
go install -v mvdan.cc/gofumpt@latest
|
||||
|
|
8
go.mod
8
go.mod
|
@ -3,12 +3,12 @@ module github.com/sagernet/sing-shadowsocks
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/sagernet/sing v0.1.7
|
||||
golang.org/x/crypto v0.6.0
|
||||
lukechampine.com/blake3 v1.1.7
|
||||
github.com/sagernet/sing v0.4.1
|
||||
golang.org/x/crypto v0.23.0
|
||||
lukechampine.com/blake3 v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
)
|
||||
|
|
23
go.sum
23
go.sum
|
@ -1,11 +1,16 @@
|
|||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/sagernet/sing v0.1.7 h1:g4vjr3q8SUlBZSx97Emz5OBfSMBxxW5Q8C2PfdoSo08=
|
||||
github.com/sagernet/sing v0.1.7/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||
lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk=
|
||||
github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
|
||||
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
|
|
23
none.go
23
none.go
|
@ -2,13 +2,11 @@ package shadowsocks
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/udpnat"
|
||||
|
@ -96,17 +94,6 @@ func (c *noneConn) FrontHeadroom() int {
|
|||
return 0
|
||||
}
|
||||
|
||||
func (c *noneConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if !c.handshake {
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
return bufio.Copy(c.Conn, r)
|
||||
}
|
||||
|
||||
func (c *noneConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return bufio.Copy(w, c.Conn)
|
||||
}
|
||||
|
||||
func (c *noneConn) RemoteAddr() net.Addr {
|
||||
return c.destination.TCPAddr()
|
||||
}
|
||||
|
@ -155,16 +142,18 @@ func (c *nonePacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr = destination.UDPAddr()
|
||||
if destination.IsFqdn() {
|
||||
addr = destination
|
||||
} else {
|
||||
addr = destination.UDPAddr()
|
||||
}
|
||||
n = copy(p, buffer.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
func (c *nonePacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
destination := M.SocksaddrFromNet(addr)
|
||||
_buffer := buf.StackNewSize(M.SocksaddrSerializer.AddrPortLen(destination) + len(p))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewSize(M.SocksaddrSerializer.AddrPortLen(destination) + len(p))
|
||||
defer buffer.Release()
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(buffer, destination)
|
||||
if err != nil {
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
|
@ -118,19 +117,15 @@ type clientConn struct {
|
|||
}
|
||||
|
||||
func (c *clientConn) writeRequest(payload []byte) error {
|
||||
_salt := buf.StackNewSize(c.keySaltLength)
|
||||
defer common.KeepAlive(_salt)
|
||||
salt := common.Dup(_salt)
|
||||
salt := buf.NewSize(c.keySaltLength)
|
||||
defer salt.Release()
|
||||
salt.WriteRandom(c.keySaltLength)
|
||||
|
||||
_key := buf.StackNewSize(c.keySaltLength)
|
||||
key := common.Dup(_key)
|
||||
key := buf.NewSize(c.keySaltLength)
|
||||
|
||||
Kdf(c.key, salt.Bytes(), key)
|
||||
writeCipher, err := c.constructor(key.Bytes())
|
||||
key.Release()
|
||||
common.KeepAlive(_key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -166,17 +161,13 @@ func (c *clientConn) writeRequest(payload []byte) error {
|
|||
}
|
||||
|
||||
func (c *clientConn) readResponse() error {
|
||||
_salt := buf.StackNewSize(c.keySaltLength)
|
||||
defer common.KeepAlive(_salt)
|
||||
salt := common.Dup(_salt)
|
||||
salt := buf.NewSize(c.keySaltLength)
|
||||
defer salt.Release()
|
||||
_, err := salt.ReadFullFrom(c.Conn, c.keySaltLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_key := buf.StackNewSize(c.keySaltLength)
|
||||
defer common.KeepAlive(_key)
|
||||
key := common.Dup(_key)
|
||||
key := buf.NewSize(c.keySaltLength)
|
||||
defer key.Release()
|
||||
Kdf(c.key, salt.Bytes(), key)
|
||||
readCipher, err := c.constructor(key.Bytes())
|
||||
|
@ -220,11 +211,12 @@ func (c *clientConn) Write(p []byte) (n int, err error) {
|
|||
return c.writer.Write(p)
|
||||
}
|
||||
|
||||
func (c *clientConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if c.writer == nil {
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
return c.writer.ReadFrom(r)
|
||||
func (c *clientConn) NeedHandshake() bool {
|
||||
return c.writer == nil
|
||||
}
|
||||
|
||||
func (c *clientConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *clientConn) Upstream() any {
|
||||
|
@ -240,13 +232,14 @@ func (c *clientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksad
|
|||
defer buffer.Release()
|
||||
header := buf.With(buffer.ExtendHeader(c.keySaltLength + M.SocksaddrSerializer.AddrPortLen(destination)))
|
||||
header.WriteRandom(c.keySaltLength)
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
|
||||
_key := buf.StackNewSize(c.keySaltLength)
|
||||
key := common.Dup(_key)
|
||||
err := M.SocksaddrSerializer.WriteAddrPort(header, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := buf.NewSize(c.keySaltLength)
|
||||
Kdf(c.key, buffer.To(c.keySaltLength), key)
|
||||
writeCipher, err := c.constructor(key.Bytes())
|
||||
key.Release()
|
||||
common.KeepAlive(_key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -264,12 +257,10 @@ func (c *clientPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
|||
if buffer.Len() < c.keySaltLength {
|
||||
return M.Socksaddr{}, io.ErrShortBuffer
|
||||
}
|
||||
_key := buf.StackNewSize(c.keySaltLength)
|
||||
key := common.Dup(_key)
|
||||
key := buf.NewSize(c.keySaltLength)
|
||||
Kdf(c.key, buffer.To(c.keySaltLength), key)
|
||||
readCipher, err := c.constructor(key.Bytes())
|
||||
key.Release()
|
||||
common.KeepAlive(_key)
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, err
|
||||
}
|
||||
|
@ -291,16 +282,19 @@ func (c *clientPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error)
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr = destination.UDPAddr()
|
||||
if destination.IsFqdn() {
|
||||
addr = destination
|
||||
} else {
|
||||
addr = destination.UDPAddr()
|
||||
}
|
||||
n = copy(p, buffer.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
func (c *clientPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
destination := M.SocksaddrFromNet(addr)
|
||||
_buffer := buf.StackNewSize(c.keySaltLength + M.SocksaddrSerializer.AddrPortLen(destination) + len(p) + Overhead)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewSize(c.keySaltLength + M.SocksaddrSerializer.AddrPortLen(destination) + len(p) + Overhead)
|
||||
defer buffer.Release()
|
||||
buffer.Resize(c.keySaltLength+M.SocksaddrSerializer.AddrPortLen(destination), 0)
|
||||
common.Must1(buffer.Write(p))
|
||||
err = c.WritePacket(buffer, destination)
|
||||
|
|
|
@ -2,8 +2,6 @@ package shadowaead
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"net"
|
||||
|
@ -13,14 +11,11 @@ import (
|
|||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/udpnat"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
var ErrBadHeader = E.New("bad header")
|
||||
|
@ -28,47 +23,22 @@ var ErrBadHeader = E.New("bad header")
|
|||
var _ shadowsocks.Service = (*Service)(nil)
|
||||
|
||||
type Service struct {
|
||||
name string
|
||||
keySaltLength int
|
||||
constructor func(key []byte) (cipher.AEAD, error)
|
||||
key []byte
|
||||
password string
|
||||
handler shadowsocks.Handler
|
||||
udpNat *udpnat.Service[netip.AddrPort]
|
||||
*Method
|
||||
password string
|
||||
handler shadowsocks.Handler
|
||||
udpNat *udpnat.Service[netip.AddrPort]
|
||||
}
|
||||
|
||||
func NewService(method string, key []byte, password string, udpTimeout int64, handler shadowsocks.Handler) (*Service, error) {
|
||||
m, err := New(method, key, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &Service{
|
||||
name: method,
|
||||
Method: m,
|
||||
handler: handler,
|
||||
udpNat: udpnat.New[netip.AddrPort](udpTimeout, handler),
|
||||
}
|
||||
switch method {
|
||||
case "aes-128-gcm":
|
||||
s.keySaltLength = 16
|
||||
s.constructor = aeadCipher(aes.NewCipher, cipher.NewGCM)
|
||||
case "aes-192-gcm":
|
||||
s.keySaltLength = 24
|
||||
s.constructor = aeadCipher(aes.NewCipher, cipher.NewGCM)
|
||||
case "aes-256-gcm":
|
||||
s.keySaltLength = 32
|
||||
s.constructor = aeadCipher(aes.NewCipher, cipher.NewGCM)
|
||||
case "chacha20-ietf-poly1305":
|
||||
s.keySaltLength = 32
|
||||
s.constructor = chacha20poly1305.New
|
||||
case "xchacha20-ietf-poly1305":
|
||||
s.keySaltLength = 32
|
||||
s.constructor = chacha20poly1305.NewX
|
||||
}
|
||||
if len(key) == s.keySaltLength {
|
||||
s.key = key
|
||||
} else if len(key) > 0 {
|
||||
return nil, shadowsocks.ErrBadKey
|
||||
} else if password != "" {
|
||||
s.key = shadowsocks.Key([]byte(password), s.keySaltLength)
|
||||
} else {
|
||||
return nil, shadowsocks.ErrMissingPassword
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
@ -89,24 +59,20 @@ func (s *Service) NewConnection(ctx context.Context, conn net.Conn, metadata M.M
|
|||
}
|
||||
|
||||
func (s *Service) newConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
_header := buf.StackNewSize(s.keySaltLength + PacketLengthBufferSize + Overhead)
|
||||
defer common.KeepAlive(_header)
|
||||
header := common.Dup(_header)
|
||||
header := buf.NewSize(s.keySaltLength + PacketLengthBufferSize + Overhead)
|
||||
defer header.Release()
|
||||
|
||||
_, err := header.ReadOnceFrom(conn)
|
||||
_, err := header.ReadFullFrom(conn, header.FreeLen())
|
||||
if err != nil {
|
||||
return E.Cause(err, "read header")
|
||||
} else if !header.IsFull() {
|
||||
return ErrBadHeader
|
||||
}
|
||||
|
||||
_key := buf.StackNewSize(s.keySaltLength)
|
||||
key := common.Dup(_key)
|
||||
key := buf.NewSize(s.keySaltLength)
|
||||
Kdf(s.key, header.To(s.keySaltLength), key)
|
||||
readCipher, err := s.constructor(key.Bytes())
|
||||
key.Release()
|
||||
common.KeepAlive(_key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -126,9 +92,9 @@ func (s *Service) newConnection(ctx context.Context, conn net.Conn, metadata M.M
|
|||
metadata.Destination = destination
|
||||
|
||||
return s.handler.NewConnection(ctx, &serverConn{
|
||||
Service: s,
|
||||
Conn: conn,
|
||||
reader: reader,
|
||||
Method: s.Method,
|
||||
Conn: conn,
|
||||
reader: reader,
|
||||
}, metadata)
|
||||
}
|
||||
|
||||
|
@ -137,7 +103,7 @@ func (s *Service) NewError(ctx context.Context, err error) {
|
|||
}
|
||||
|
||||
type serverConn struct {
|
||||
*Service
|
||||
*Method
|
||||
net.Conn
|
||||
access sync.Mutex
|
||||
reader *Reader
|
||||
|
@ -145,20 +111,16 @@ type serverConn struct {
|
|||
}
|
||||
|
||||
func (c *serverConn) writeResponse(payload []byte) (n int, err error) {
|
||||
_salt := buf.StackNewSize(c.keySaltLength)
|
||||
salt := common.Dup(_salt)
|
||||
salt := buf.NewSize(c.keySaltLength)
|
||||
salt.WriteRandom(c.keySaltLength)
|
||||
|
||||
_key := buf.StackNewSize(c.keySaltLength)
|
||||
key := common.Dup(_key)
|
||||
key := buf.NewSize(c.keySaltLength)
|
||||
|
||||
Kdf(c.key, salt.Bytes(), key)
|
||||
writeCipher, err := c.constructor(key.Bytes())
|
||||
key.Release()
|
||||
common.KeepAlive(_key)
|
||||
if err != nil {
|
||||
salt.Release()
|
||||
common.KeepAlive(_salt)
|
||||
return
|
||||
}
|
||||
writer := NewWriter(c.Conn, writeCipher, MaxPacketSize)
|
||||
|
@ -166,7 +128,6 @@ func (c *serverConn) writeResponse(payload []byte) (n int, err error) {
|
|||
header := writer.Buffer()
|
||||
common.Must1(header.Write(salt.Bytes()))
|
||||
salt.Release()
|
||||
common.KeepAlive(_salt)
|
||||
|
||||
bufferedWriter := writer.BufferedWriter(header.Len())
|
||||
if len(payload) > 0 {
|
||||
|
@ -202,26 +163,23 @@ func (c *serverConn) Write(p []byte) (n int, err error) {
|
|||
return c.writeResponse(p)
|
||||
}
|
||||
|
||||
func (c *serverConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if c.writer == nil {
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
return c.writer.ReadFrom(r)
|
||||
}
|
||||
|
||||
func (c *serverConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return c.reader.WriteTo(w)
|
||||
}
|
||||
|
||||
func (c *serverConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *serverConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
func (s *Service) ReaderMTU() int {
|
||||
func (c *serverConn) ReaderMTU() int {
|
||||
return MaxPacketSize
|
||||
}
|
||||
|
||||
func (s *Service) WriteIsThreadUnsafe() {
|
||||
func (c *Service) WriteIsThreadUnsafe() {
|
||||
}
|
||||
|
||||
func (s *Service) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata M.Metadata) error {
|
||||
|
@ -236,12 +194,10 @@ func (s *Service) newPacket(ctx context.Context, conn N.PacketConn, buffer *buf.
|
|||
if buffer.Len() < s.keySaltLength {
|
||||
return io.ErrShortBuffer
|
||||
}
|
||||
_key := buf.StackNewSize(s.keySaltLength)
|
||||
key := common.Dup(_key)
|
||||
key := buf.NewSize(s.keySaltLength)
|
||||
Kdf(s.key, buffer.To(s.keySaltLength), key)
|
||||
readCipher, err := s.constructor(key.Bytes())
|
||||
key.Release()
|
||||
common.KeepAlive(_key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -260,13 +216,13 @@ func (s *Service) newPacket(ctx context.Context, conn N.PacketConn, buffer *buf.
|
|||
metadata.Protocol = "shadowsocks"
|
||||
metadata.Destination = destination
|
||||
s.udpNat.NewPacket(ctx, metadata.Source.AddrPort(), buffer, metadata, func(natConn N.PacketConn) N.PacketWriter {
|
||||
return &serverPacketWriter{s, conn, natConn}
|
||||
return &serverPacketWriter{s.Method, conn, natConn}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
type serverPacketWriter struct {
|
||||
*Service
|
||||
*Method
|
||||
source N.PacketConn
|
||||
nat N.PacketConn
|
||||
}
|
||||
|
@ -279,12 +235,10 @@ func (w *serverPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socks
|
|||
buffer.Release()
|
||||
return err
|
||||
}
|
||||
_key := buf.StackNewSize(w.keySaltLength)
|
||||
key := common.Dup(_key)
|
||||
key := buf.NewSize(w.keySaltLength)
|
||||
Kdf(w.key, buffer.To(w.keySaltLength), key)
|
||||
writeCipher, err := w.constructor(key.Bytes())
|
||||
key.Release()
|
||||
common.KeepAlive(_key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -308,3 +262,10 @@ func (w *serverPacketWriter) WriterMTU() int {
|
|||
func (w *serverPacketWriter) Upstream() any {
|
||||
return w.source
|
||||
}
|
||||
|
||||
func (w *serverPacketWriter) ReaderMTU() int {
|
||||
return MaxPacketSize
|
||||
}
|
||||
|
||||
func (w *serverPacketWriter) WriteIsThreadUnsafe() {
|
||||
}
|
||||
|
|
202
shadowaead/service_multi.go
Normal file
202
shadowaead/service_multi.go
Normal file
|
@ -0,0 +1,202 @@
|
|||
package shadowaead
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/cipher"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/udpnat"
|
||||
)
|
||||
|
||||
var _ shadowsocks.MultiService[int] = (*MultiService[int])(nil)
|
||||
|
||||
type MultiService[U comparable] struct {
|
||||
name string
|
||||
methodMap map[U]*Method
|
||||
handler shadowsocks.Handler
|
||||
udpNat *udpnat.Service[netip.AddrPort]
|
||||
}
|
||||
|
||||
func NewMultiService[U comparable](method string, udpTimeout int64, handler shadowsocks.Handler) (*MultiService[U], error) {
|
||||
s := &MultiService[U]{
|
||||
name: method,
|
||||
handler: handler,
|
||||
udpNat: udpnat.New[netip.AddrPort](udpTimeout, handler),
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) Name() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) UpdateUsers(userList []U, keyList [][]byte) error {
|
||||
s.methodMap = make(map[U]*Method)
|
||||
for i, user := range userList {
|
||||
key := keyList[i]
|
||||
method, err := New(s.name, key, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.methodMap[user] = method
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) UpdateUsersWithPasswords(userList []U, passwordList []string) error {
|
||||
s.methodMap = make(map[U]*Method)
|
||||
for i, user := range userList {
|
||||
password := passwordList[i]
|
||||
method, err := New(s.name, nil, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.methodMap[user] = method
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
err := s.newConnection(ctx, conn, metadata)
|
||||
if err != nil {
|
||||
err = &shadowsocks.ServerConnError{Conn: conn, Source: metadata.Source, Cause: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) newConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
var user U
|
||||
var method *Method
|
||||
for u, m := range s.methodMap {
|
||||
user, method = u, m
|
||||
break
|
||||
}
|
||||
if method == nil {
|
||||
return shadowsocks.ErrNoUsers
|
||||
}
|
||||
header := buf.NewSize(method.keySaltLength + PacketLengthBufferSize + Overhead)
|
||||
defer header.Release()
|
||||
|
||||
_, err := header.ReadFullFrom(conn, header.FreeLen())
|
||||
if err != nil {
|
||||
return E.Cause(err, "read header")
|
||||
} else if !header.IsFull() {
|
||||
return ErrBadHeader
|
||||
}
|
||||
|
||||
var reader *Reader
|
||||
var readCipher cipher.AEAD
|
||||
for u, m := range s.methodMap {
|
||||
key := buf.NewSize(method.keySaltLength)
|
||||
Kdf(m.key, header.To(m.keySaltLength), key)
|
||||
readCipher, err = m.constructor(key.Bytes())
|
||||
key.Release()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reader = NewReader(conn, readCipher, MaxPacketSize)
|
||||
|
||||
err = reader.ReadWithLengthChunk(header.From(method.keySaltLength))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
user, method = u, m
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destination, err := M.SocksaddrSerializer.ReadAddrPort(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metadata.Protocol = "shadowsocks"
|
||||
metadata.Destination = destination
|
||||
|
||||
return s.handler.NewConnection(auth.ContextWithUser(ctx, user), &serverConn{
|
||||
Method: method,
|
||||
Conn: conn,
|
||||
reader: reader,
|
||||
}, metadata)
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) WriteIsThreadUnsafe() {
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata M.Metadata) error {
|
||||
err := s.newPacket(ctx, conn, buffer, metadata)
|
||||
if err != nil {
|
||||
err = &shadowsocks.ServerPacketError{Source: metadata.Source, Cause: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) newPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata M.Metadata) error {
|
||||
var user U
|
||||
var method *Method
|
||||
for u, m := range s.methodMap {
|
||||
user, method = u, m
|
||||
break
|
||||
}
|
||||
if method == nil {
|
||||
return shadowsocks.ErrNoUsers
|
||||
}
|
||||
if buffer.Len() < method.keySaltLength {
|
||||
return io.ErrShortBuffer
|
||||
}
|
||||
var readCipher cipher.AEAD
|
||||
var err error
|
||||
decrypted := make([]byte, 0, buffer.Len())
|
||||
for u, m := range s.methodMap {
|
||||
key := buf.NewSize(m.keySaltLength)
|
||||
Kdf(m.key, buffer.To(m.keySaltLength), key)
|
||||
readCipher, err = m.constructor(key.Bytes())
|
||||
key.Release()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var packet []byte
|
||||
packet, err = readCipher.Open(decrypted, rw.ZeroBytes[:readCipher.NonceSize()], buffer.From(m.keySaltLength), nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
buffer.Advance(m.keySaltLength)
|
||||
buffer.Truncate(len(packet))
|
||||
copy(buffer.Bytes(), packet)
|
||||
|
||||
user, method = u, m
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destination, err := M.SocksaddrSerializer.ReadAddrPort(buffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metadata.Protocol = "shadowsocks"
|
||||
metadata.Destination = destination
|
||||
s.udpNat.NewPacket(auth.ContextWithUser(ctx, user), metadata.Source.AddrPort(), buffer, metadata, func(natConn N.PacketConn) N.PacketWriter {
|
||||
return &serverPacketWriter{method, conn, natConn}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) NewError(ctx context.Context, err error) {
|
||||
s.handler.NewError(ctx, err)
|
||||
}
|
|
@ -64,7 +64,7 @@ func init() {
|
|||
random.InitializeSeed()
|
||||
}
|
||||
|
||||
func NewWithPassword(method string, password string, options ...MethodOption) (shadowsocks.Method, error) {
|
||||
func NewWithPassword(method string, password string, timeFunc func() time.Time) (shadowsocks.Method, error) {
|
||||
var pskList [][]byte
|
||||
if password == "" {
|
||||
return nil, ErrMissingPSK
|
||||
|
@ -78,12 +78,13 @@ func NewWithPassword(method string, password string, options ...MethodOption) (s
|
|||
}
|
||||
pskList[i] = kb
|
||||
}
|
||||
return New(method, pskList, options...)
|
||||
return New(method, pskList, timeFunc)
|
||||
}
|
||||
|
||||
func New(method string, pskList [][]byte, options ...MethodOption) (shadowsocks.Method, error) {
|
||||
func New(method string, pskList [][]byte, timeFunc func() time.Time) (shadowsocks.Method, error) {
|
||||
m := &Method{
|
||||
name: method,
|
||||
name: method,
|
||||
timeFunc: timeFunc,
|
||||
}
|
||||
|
||||
switch method {
|
||||
|
@ -146,9 +147,6 @@ func New(method string, pskList [][]byte, options ...MethodOption) (shadowsocks.
|
|||
}
|
||||
|
||||
m.pskList = pskList
|
||||
for _, option := range options {
|
||||
option(m)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
|
@ -158,10 +156,10 @@ func Key(key []byte, keyLength int) []byte {
|
|||
}
|
||||
|
||||
func SessionKey(psk []byte, salt []byte, keyLength int) []byte {
|
||||
sessionKey := buf.Make(len(psk) + len(salt))
|
||||
sessionKey := make([]byte, len(psk)+len(salt))
|
||||
copy(sessionKey, psk)
|
||||
copy(sessionKey[len(psk):], salt)
|
||||
outKey := buf.Make(keyLength)
|
||||
outKey := make([]byte, keyLength)
|
||||
blake3.DeriveKey(outKey, "shadowsocks 2022 session subkey", sessionKey)
|
||||
return outKey
|
||||
}
|
||||
|
@ -177,8 +175,10 @@ func aeadCipher(block func(key []byte) (cipher.Block, error), aead func(block ci
|
|||
}
|
||||
|
||||
type Method struct {
|
||||
name string
|
||||
keySaltLength int
|
||||
name string
|
||||
keySaltLength int
|
||||
timeFunc func() time.Time
|
||||
|
||||
constructor func(key []byte) (cipher.AEAD, error)
|
||||
blockConstructor func(key []byte) (cipher.Block, error)
|
||||
udpCipher cipher.AEAD
|
||||
|
@ -222,17 +222,24 @@ type clientConn struct {
|
|||
writer *shadowaead.Writer
|
||||
}
|
||||
|
||||
func (m *Method) time() time.Time {
|
||||
if m.timeFunc != nil {
|
||||
return m.timeFunc()
|
||||
} else {
|
||||
return time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Method) writeExtendedIdentityHeaders(request *buf.Buffer, salt []byte) error {
|
||||
pskLen := len(m.pskList)
|
||||
if pskLen < 2 {
|
||||
return nil
|
||||
}
|
||||
for i, psk := range m.pskList {
|
||||
keyMaterial := buf.Make(m.keySaltLength * 2)
|
||||
keyMaterial := make([]byte, m.keySaltLength*2)
|
||||
copy(keyMaterial, psk)
|
||||
copy(keyMaterial[m.keySaltLength:], salt)
|
||||
_identitySubkey := buf.StackNewSize(m.keySaltLength)
|
||||
identitySubkey := common.Dup(_identitySubkey)
|
||||
identitySubkey := buf.NewSize(m.keySaltLength)
|
||||
identitySubkey.Extend(identitySubkey.FreeLen())
|
||||
blake3.DeriveKey(identitySubkey.Bytes(), "shadowsocks 2022 identity subkey", keyMaterial)
|
||||
|
||||
|
@ -245,7 +252,6 @@ func (m *Method) writeExtendedIdentityHeaders(request *buf.Buffer, salt []byte)
|
|||
}
|
||||
b.Encrypt(header, pskHash)
|
||||
identitySubkey.Release()
|
||||
common.KeepAlive(_identitySubkey)
|
||||
if i == pskLen-2 {
|
||||
break
|
||||
}
|
||||
|
@ -258,7 +264,7 @@ func (c *clientConn) writeRequest(payload []byte) error {
|
|||
common.Must1(io.ReadFull(rand.Reader, salt))
|
||||
|
||||
key := SessionKey(c.pskList[len(c.pskList)-1], salt, c.keySaltLength)
|
||||
writeCipher, err := c.constructor(common.Dup(key))
|
||||
writeCipher, err := c.constructor(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -267,7 +273,6 @@ func (c *clientConn) writeRequest(payload []byte) error {
|
|||
writeCipher,
|
||||
MaxPacketSize,
|
||||
)
|
||||
common.KeepAlive(key)
|
||||
|
||||
header := writer.Buffer()
|
||||
header.Write(salt)
|
||||
|
@ -278,9 +283,9 @@ func (c *clientConn) writeRequest(payload []byte) error {
|
|||
}
|
||||
|
||||
var _fixedLengthBuffer [RequestHeaderFixedChunkLength]byte
|
||||
fixedLengthBuffer := buf.With(common.Dup(_fixedLengthBuffer[:]))
|
||||
fixedLengthBuffer := buf.With(_fixedLengthBuffer[:])
|
||||
common.Must(fixedLengthBuffer.WriteByte(HeaderTypeClient))
|
||||
common.Must(binary.Write(fixedLengthBuffer, binary.BigEndian, uint64(time.Now().Unix())))
|
||||
common.Must(binary.Write(fixedLengthBuffer, binary.BigEndian, uint64(c.time().Unix())))
|
||||
var paddingLen int
|
||||
if len(payload) < MaxPaddingLength {
|
||||
paddingLen = mRand.Intn(MaxPaddingLength) + 1
|
||||
|
@ -289,12 +294,13 @@ func (c *clientConn) writeRequest(payload []byte) error {
|
|||
payloadLen := len(payload)
|
||||
variableLengthHeaderLen += payloadLen
|
||||
common.Must(binary.Write(fixedLengthBuffer, binary.BigEndian, uint16(variableLengthHeaderLen)))
|
||||
writer.WriteChunk(header, fixedLengthBuffer.Slice())
|
||||
common.KeepAlive(_fixedLengthBuffer)
|
||||
writer.WriteChunk(header, fixedLengthBuffer.Bytes())
|
||||
|
||||
_variableLengthBuffer := buf.StackNewSize(variableLengthHeaderLen)
|
||||
variableLengthBuffer := common.Dup(_variableLengthBuffer)
|
||||
common.Must(M.SocksaddrSerializer.WriteAddrPort(variableLengthBuffer, c.destination))
|
||||
variableLengthBuffer := buf.NewSize(variableLengthHeaderLen)
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(variableLengthBuffer, c.destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common.Must(binary.Write(variableLengthBuffer, binary.BigEndian, uint16(paddingLen)))
|
||||
if paddingLen > 0 {
|
||||
variableLengthBuffer.Extend(paddingLen)
|
||||
|
@ -302,8 +308,7 @@ func (c *clientConn) writeRequest(payload []byte) error {
|
|||
if payloadLen > 0 {
|
||||
common.Must1(variableLengthBuffer.Write(payload[:payloadLen]))
|
||||
}
|
||||
writer.WriteChunk(header, variableLengthBuffer.Slice())
|
||||
common.KeepAlive(_variableLengthBuffer)
|
||||
writer.WriteChunk(header, variableLengthBuffer.Bytes())
|
||||
variableLengthBuffer.Release()
|
||||
|
||||
err = writer.BufferedWriter(header.Len()).Flush()
|
||||
|
@ -321,22 +326,18 @@ func (c *clientConn) readResponse() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
_salt := buf.StackNewSize(c.keySaltLength)
|
||||
salt := common.Dup(_salt)
|
||||
salt := buf.NewSize(c.keySaltLength)
|
||||
|
||||
_, err := salt.ReadFullFrom(c.Conn, salt.FreeLen())
|
||||
if err != nil {
|
||||
salt.Release()
|
||||
common.KeepAlive(_salt)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
key := SessionKey(c.pskList[len(c.pskList)-1], salt.Bytes(), c.keySaltLength)
|
||||
salt.Release()
|
||||
common.KeepAlive(_salt)
|
||||
|
||||
readCipher, err := c.constructor(common.Dup(key))
|
||||
readCipher, err := c.constructor(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -345,7 +346,6 @@ func (c *clientConn) readResponse() error {
|
|||
readCipher,
|
||||
MaxPacketSize,
|
||||
)
|
||||
common.KeepAlive(key)
|
||||
|
||||
err = reader.ReadWithLength(uint16(1 + 8 + c.keySaltLength + 2))
|
||||
if err != nil {
|
||||
|
@ -366,13 +366,12 @@ func (c *clientConn) readResponse() error {
|
|||
return err
|
||||
}
|
||||
|
||||
diff := int(math.Abs(float64(time.Now().Unix() - int64(epoch))))
|
||||
diff := int(math.Abs(float64(c.time().Unix() - int64(epoch))))
|
||||
if diff > 30 {
|
||||
return E.Extend(ErrBadTimestamp, "received ", epoch, ", diff ", diff, "s")
|
||||
}
|
||||
|
||||
_requestSalt := buf.StackNewSize(c.keySaltLength)
|
||||
requestSalt := common.Dup(_requestSalt)
|
||||
requestSalt := buf.NewSize(c.keySaltLength)
|
||||
_, err = requestSalt.ReadFullFrom(reader, requestSalt.FreeLen())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -382,7 +381,6 @@ func (c *clientConn) readResponse() error {
|
|||
return ErrBadRequestSalt
|
||||
}
|
||||
requestSalt.Release()
|
||||
common.KeepAlive(_requestSalt)
|
||||
c.requestSalt = nil
|
||||
|
||||
var length uint16
|
||||
|
@ -441,11 +439,12 @@ func (c *clientConn) WriteVectorised(buffers []*buf.Buffer) error {
|
|||
return c.writer.WriteVectorised(buffers[1:])
|
||||
}
|
||||
|
||||
func (c *clientConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if c.writer == nil {
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
return bufio.Copy(c.writer, r)
|
||||
func (c *clientConn) NeedHandshake() bool {
|
||||
return c.writer == nil
|
||||
}
|
||||
|
||||
func (c *clientConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *clientConn) Upstream() any {
|
||||
|
@ -526,7 +525,7 @@ func (c *clientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksad
|
|||
}
|
||||
common.Must(
|
||||
header.WriteByte(HeaderTypeClient),
|
||||
binary.Write(header, binary.BigEndian, uint64(time.Now().Unix())),
|
||||
binary.Write(header, binary.BigEndian, uint64(c.time().Unix())),
|
||||
binary.Write(header, binary.BigEndian, uint16(paddingLen)), // padding length
|
||||
)
|
||||
|
||||
|
@ -604,11 +603,10 @@ func (c *clientPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
|||
remoteCipher = c.session.lastRemoteCipher
|
||||
} else {
|
||||
key := SessionKey(c.pskList[len(c.pskList)-1], packetHeader[:8], c.keySaltLength)
|
||||
remoteCipher, err = c.constructor(common.Dup(key))
|
||||
remoteCipher, err = c.constructor(key)
|
||||
if err != nil {
|
||||
return M.Socksaddr{}, err
|
||||
}
|
||||
common.KeepAlive(key)
|
||||
}
|
||||
_, err = remoteCipher.Open(buffer.Index(0), packetHeader[4:16], buffer.Bytes(), nil)
|
||||
if err != nil {
|
||||
|
@ -632,7 +630,7 @@ func (c *clientPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
|||
return M.Socksaddr{}, err
|
||||
}
|
||||
|
||||
diff := int(math.Abs(float64(time.Now().Unix() - int64(epoch))))
|
||||
diff := int(math.Abs(float64(c.time().Unix() - int64(epoch))))
|
||||
if diff > 30 {
|
||||
return M.Socksaddr{}, E.Extend(ErrBadTimestamp, "received ", epoch, ", diff ", diff, "s")
|
||||
}
|
||||
|
@ -641,15 +639,15 @@ func (c *clientPacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
|
|||
c.session.window.Add(packetId)
|
||||
} else if sessionId == c.session.lastRemoteSessionId {
|
||||
c.session.lastWindow.Add(packetId)
|
||||
c.session.lastRemoteSeen = time.Now().Unix()
|
||||
c.session.lastRemoteSeen = c.time().Unix()
|
||||
} else {
|
||||
if c.session.remoteSessionId != 0 {
|
||||
if time.Now().Unix()-c.session.lastRemoteSeen < 60 {
|
||||
if c.time().Unix()-c.session.lastRemoteSeen < 60 {
|
||||
return M.Socksaddr{}, ErrTooManyServerSessions
|
||||
} else {
|
||||
c.session.lastRemoteSessionId = c.session.remoteSessionId
|
||||
c.session.lastWindow = c.session.window
|
||||
c.session.lastRemoteSeen = time.Now().Unix()
|
||||
c.session.lastRemoteSeen = c.time().Unix()
|
||||
c.session.lastRemoteCipher = c.session.remoteCipher
|
||||
c.session.window = SlidingWindow{}
|
||||
}
|
||||
|
@ -689,7 +687,11 @@ func (c *clientPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error)
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr = destination.UDPAddr()
|
||||
if destination.IsFqdn() {
|
||||
addr = destination
|
||||
} else {
|
||||
addr = destination.UDPAddr()
|
||||
}
|
||||
n = copy(p, buffer.Bytes())
|
||||
return
|
||||
}
|
||||
|
@ -717,9 +719,7 @@ func (c *clientPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|||
overHead += paddingLen
|
||||
overHead += M.SocksaddrSerializer.AddrPortLen(destination)
|
||||
|
||||
_buffer := buf.StackNewSize(overHead + len(p))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewSize(overHead + len(p))
|
||||
defer buffer.Release()
|
||||
|
||||
var dataIndex int
|
||||
|
@ -758,7 +758,7 @@ func (c *clientPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|||
}
|
||||
common.Must(
|
||||
buffer.WriteByte(HeaderTypeClient),
|
||||
binary.Write(buffer, binary.BigEndian, uint64(time.Now().Unix())),
|
||||
binary.Write(buffer, binary.BigEndian, uint64(c.time().Unix())),
|
||||
binary.Write(buffer, binary.BigEndian, uint16(paddingLen)), // padding length
|
||||
)
|
||||
|
||||
|
@ -843,11 +843,10 @@ func (m *Method) newUDPSession() *udpSession {
|
|||
binary.BigEndian.PutUint64(sessionId, session.sessionId)
|
||||
key := SessionKey(m.pskList[len(m.pskList)-1], sessionId, m.keySaltLength)
|
||||
var err error
|
||||
session.cipher, err = m.constructor(common.Dup(key))
|
||||
session.cipher, err = m.constructor(key)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
common.KeepAlive(key)
|
||||
}
|
||||
return session
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
package shadowaead_2022
|
||||
|
||||
type MethodOption func(*Method)
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/auth"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
|
@ -153,9 +152,7 @@ func (s *RelayService[U]) NewConnection(ctx context.Context, conn net.Conn, meta
|
|||
}
|
||||
|
||||
func (s *RelayService[U]) newConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
_requestHeader := buf.StackNew()
|
||||
defer common.KeepAlive(_requestHeader)
|
||||
requestHeader := common.Dup(_requestHeader)
|
||||
requestHeader := buf.New()
|
||||
defer requestHeader.Release()
|
||||
n, err := requestHeader.ReadOnceFrom(conn)
|
||||
if err != nil {
|
||||
|
@ -165,19 +162,17 @@ func (s *RelayService[U]) newConnection(ctx context.Context, conn net.Conn, meta
|
|||
}
|
||||
requestSalt := requestHeader.To(s.keySaltLength)
|
||||
var _eiHeader [aes.BlockSize]byte
|
||||
eiHeader := common.Dup(_eiHeader[:])
|
||||
eiHeader := _eiHeader[:]
|
||||
copy(eiHeader, requestHeader.Range(s.keySaltLength, s.keySaltLength+aes.BlockSize))
|
||||
|
||||
keyMaterial := buf.Make(s.keySaltLength * 2)
|
||||
keyMaterial := make([]byte, s.keySaltLength*2)
|
||||
copy(keyMaterial, s.iPSK)
|
||||
copy(keyMaterial[s.keySaltLength:], requestSalt)
|
||||
_identitySubkey := buf.StackNewSize(s.keySaltLength)
|
||||
identitySubkey := common.Dup(_identitySubkey)
|
||||
identitySubkey := buf.NewSize(s.keySaltLength)
|
||||
identitySubkey.Extend(identitySubkey.FreeLen())
|
||||
blake3.DeriveKey(identitySubkey.Bytes(), "shadowsocks 2022 identity subkey", keyMaterial)
|
||||
b, err := s.blockConstructor(identitySubkey.Bytes())
|
||||
identitySubkey.Release()
|
||||
common.KeepAlive(_identitySubkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -189,7 +184,6 @@ func (s *RelayService[U]) newConnection(ctx context.Context, conn net.Conn, meta
|
|||
} else {
|
||||
return E.New("invalid request")
|
||||
}
|
||||
common.KeepAlive(_eiHeader)
|
||||
|
||||
copy(requestHeader.Range(aes.BlockSize, aes.BlockSize+s.keySaltLength), requestHeader.To(s.keySaltLength))
|
||||
requestHeader.Advance(aes.BlockSize)
|
||||
|
@ -218,7 +212,7 @@ func (s *RelayService[U]) newPacket(ctx context.Context, conn N.PacketConn, buff
|
|||
sessionId := binary.BigEndian.Uint64(packetHeader)
|
||||
|
||||
var _eiHeader [aes.BlockSize]byte
|
||||
eiHeader := common.Dup(_eiHeader[:])
|
||||
eiHeader := _eiHeader[:]
|
||||
s.udpBlockCipher.Decrypt(eiHeader, buffer.Range(aes.BlockSize, 2*aes.BlockSize))
|
||||
xorWords(eiHeader, eiHeader, packetHeader)
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"github.com/sagernet/sing-shadowsocks/shadowaead"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/cache"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
@ -32,8 +31,9 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrNoPadding = E.New("bad request: missing payload or padding")
|
||||
ErrBadPadding = E.New("bad request: damaged padding")
|
||||
ErrInvalidRequest = E.New("invalid request")
|
||||
ErrNoPadding = E.New("bad request: missing payload or padding")
|
||||
ErrBadPadding = E.New("bad request: damaged padding")
|
||||
)
|
||||
|
||||
var _ shadowsocks.Service = (*Service)(nil)
|
||||
|
@ -42,6 +42,7 @@ type Service struct {
|
|||
name string
|
||||
keySaltLength int
|
||||
handler shadowsocks.Handler
|
||||
timeFunc func() time.Time
|
||||
|
||||
constructor func(key []byte) (cipher.AEAD, error)
|
||||
blockConstructor func(key []byte) (cipher.Block, error)
|
||||
|
@ -54,7 +55,7 @@ type Service struct {
|
|||
udpSessions *cache.LruCache[uint64, *serverUDPSession]
|
||||
}
|
||||
|
||||
func NewServiceWithPassword(method string, password string, udpTimeout int64, handler shadowsocks.Handler) (shadowsocks.Service, error) {
|
||||
func NewServiceWithPassword(method string, password string, udpTimeout int64, handler shadowsocks.Handler, timeFunc func() time.Time) (shadowsocks.Service, error) {
|
||||
if password == "" {
|
||||
return nil, ErrMissingPSK
|
||||
}
|
||||
|
@ -62,13 +63,14 @@ func NewServiceWithPassword(method string, password string, udpTimeout int64, ha
|
|||
if err != nil {
|
||||
return nil, E.Cause(err, "decode psk")
|
||||
}
|
||||
return NewService(method, psk, udpTimeout, handler)
|
||||
return NewService(method, psk, udpTimeout, handler, timeFunc)
|
||||
}
|
||||
|
||||
func NewService(method string, psk []byte, udpTimeout int64, handler shadowsocks.Handler) (shadowsocks.Service, error) {
|
||||
func NewService(method string, psk []byte, udpTimeout int64, handler shadowsocks.Handler, timeFunc func() time.Time) (shadowsocks.Service, error) {
|
||||
s := &Service{
|
||||
name: method,
|
||||
handler: handler,
|
||||
name: method,
|
||||
handler: handler,
|
||||
timeFunc: timeFunc,
|
||||
|
||||
replayFilter: replay.NewSimple(60 * time.Second),
|
||||
udpNat: udpnat.New[uint64](udpTimeout, handler),
|
||||
|
@ -135,6 +137,14 @@ func (s *Service) NewConnection(ctx context.Context, conn net.Conn, metadata M.M
|
|||
return err
|
||||
}
|
||||
|
||||
func (s *Service) time() time.Time {
|
||||
if s.timeFunc != nil {
|
||||
return s.timeFunc()
|
||||
} else {
|
||||
return time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) newConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
header := make([]byte, s.keySaltLength+shadowaead.Overhead+RequestHeaderFixedChunkLength)
|
||||
|
||||
|
@ -152,7 +162,7 @@ func (s *Service) newConnection(ctx context.Context, conn net.Conn, metadata M.M
|
|||
}
|
||||
|
||||
requestKey := SessionKey(s.psk, requestSalt, s.keySaltLength)
|
||||
readCipher, err := s.constructor(common.Dup(requestKey))
|
||||
readCipher, err := s.constructor(requestKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -161,7 +171,6 @@ func (s *Service) newConnection(ctx context.Context, conn net.Conn, metadata M.M
|
|||
readCipher,
|
||||
MaxPacketSize,
|
||||
)
|
||||
common.KeepAlive(requestKey)
|
||||
|
||||
err = reader.ReadExternalChunk(header[s.keySaltLength:])
|
||||
if err != nil {
|
||||
|
@ -183,7 +192,7 @@ func (s *Service) newConnection(ctx context.Context, conn net.Conn, metadata M.M
|
|||
return err
|
||||
}
|
||||
|
||||
diff := int(math.Abs(float64(time.Now().Unix() - int64(epoch))))
|
||||
diff := int(math.Abs(float64(s.time().Unix() - int64(epoch))))
|
||||
if diff > 30 {
|
||||
return E.Extend(ErrBadTimestamp, "received ", epoch, ", diff ", diff, "s")
|
||||
}
|
||||
|
@ -250,16 +259,13 @@ type serverConn struct {
|
|||
}
|
||||
|
||||
func (c *serverConn) writeResponse(payload []byte) (n int, err error) {
|
||||
_salt := buf.StackNewSize(c.keySaltLength)
|
||||
salt := common.Dup(_salt)
|
||||
salt := buf.NewSize(c.keySaltLength)
|
||||
salt.WriteRandom(salt.FreeLen())
|
||||
|
||||
key := SessionKey(c.uPSK, salt.Bytes(), c.keySaltLength)
|
||||
common.KeepAlive(_salt)
|
||||
writeCipher, err := c.constructor(common.Dup(key))
|
||||
writeCipher, err := c.constructor(key)
|
||||
if err != nil {
|
||||
salt.Release()
|
||||
common.KeepAlive(_salt)
|
||||
return
|
||||
}
|
||||
writer := shadowaead.NewWriter(
|
||||
|
@ -267,26 +273,22 @@ func (c *serverConn) writeResponse(payload []byte) (n int, err error) {
|
|||
writeCipher,
|
||||
MaxPacketSize,
|
||||
)
|
||||
common.KeepAlive(key)
|
||||
header := writer.Buffer()
|
||||
header.Write(salt.Bytes())
|
||||
|
||||
salt.Release()
|
||||
common.KeepAlive(_salt)
|
||||
|
||||
headerType := byte(HeaderTypeServer)
|
||||
payloadLen := len(payload)
|
||||
|
||||
_headerFixedChunk := buf.StackNewSize(1 + 8 + c.keySaltLength + 2)
|
||||
headerFixedChunk := common.Dup(_headerFixedChunk)
|
||||
headerFixedChunk := buf.NewSize(1 + 8 + c.keySaltLength + 2)
|
||||
common.Must(headerFixedChunk.WriteByte(headerType))
|
||||
common.Must(binary.Write(headerFixedChunk, binary.BigEndian, uint64(time.Now().Unix())))
|
||||
common.Must(binary.Write(headerFixedChunk, binary.BigEndian, uint64(c.time().Unix())))
|
||||
common.Must1(headerFixedChunk.Write(c.requestSalt))
|
||||
common.Must(binary.Write(headerFixedChunk, binary.BigEndian, uint16(payloadLen)))
|
||||
|
||||
writer.WriteChunk(header, headerFixedChunk.Slice())
|
||||
writer.WriteChunk(header, headerFixedChunk.Bytes())
|
||||
headerFixedChunk.Release()
|
||||
common.KeepAlive(_headerFixedChunk)
|
||||
c.requestSalt = nil
|
||||
|
||||
if payloadLen > 0 {
|
||||
|
@ -352,17 +354,6 @@ func (c *serverConn) WriteVectorised(buffers []*buf.Buffer) error {
|
|||
return c.writer.WriteVectorised(buffers[1:])
|
||||
}
|
||||
|
||||
func (c *serverConn) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
if c.writer == nil {
|
||||
return bufio.ReadFrom0(c, r)
|
||||
}
|
||||
return bufio.Copy(c.writer, r)
|
||||
}
|
||||
|
||||
func (c *serverConn) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return bufio.Copy(w, c.reader)
|
||||
}
|
||||
|
||||
func (c *serverConn) Close() error {
|
||||
return common.Close(
|
||||
c.Conn,
|
||||
|
@ -371,6 +362,10 @@ func (c *serverConn) Close() error {
|
|||
)
|
||||
}
|
||||
|
||||
func (c *serverConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *serverConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
@ -421,11 +416,10 @@ func (s *Service) newPacket(ctx context.Context, conn N.PacketConn, buffer *buf.
|
|||
session.remoteSessionId = sessionId
|
||||
if packetHeader != nil {
|
||||
key := SessionKey(s.psk, packetHeader[:8], s.keySaltLength)
|
||||
session.remoteCipher, err = s.constructor(common.Dup(key))
|
||||
session.remoteCipher, err = s.constructor(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common.KeepAlive(key)
|
||||
}
|
||||
}
|
||||
goto process
|
||||
|
@ -469,7 +463,7 @@ process:
|
|||
if err != nil {
|
||||
goto returnErr
|
||||
}
|
||||
diff := int(math.Abs(float64(time.Now().Unix() - int64(epoch))))
|
||||
diff := int(math.Abs(float64(s.time().Unix() - int64(epoch))))
|
||||
if diff > 30 {
|
||||
err = E.Extend(ErrBadTimestamp, "received ", epoch, ", diff ", diff, "s")
|
||||
goto returnErr
|
||||
|
@ -539,7 +533,7 @@ func (w *serverPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socks
|
|||
binary.Write(header, binary.BigEndian, w.session.sessionId),
|
||||
binary.Write(header, binary.BigEndian, w.session.nextPacketId()),
|
||||
header.WriteByte(HeaderTypeServer),
|
||||
binary.Write(header, binary.BigEndian, uint64(time.Now().Unix())),
|
||||
binary.Write(header, binary.BigEndian, uint64(w.time().Unix())),
|
||||
binary.Write(header, binary.BigEndian, w.session.remoteSessionId),
|
||||
binary.Write(header, binary.BigEndian, uint16(paddingLen)), // padding length
|
||||
)
|
||||
|
@ -617,9 +611,8 @@ func (s *Service) newUDPSession() *serverUDPSession {
|
|||
binary.BigEndian.PutUint64(sessionId, session.sessionId)
|
||||
key := SessionKey(s.psk, sessionId, s.keySaltLength)
|
||||
var err error
|
||||
session.cipher, err = s.constructor(common.Dup(key))
|
||||
session.cipher, err = s.constructor(key)
|
||||
common.Must(err)
|
||||
common.KeepAlive(key)
|
||||
}
|
||||
return session
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -25,6 +26,8 @@ import (
|
|||
"lukechampine.com/blake3"
|
||||
)
|
||||
|
||||
var _ shadowsocks.MultiService[int] = (*MultiService[int])(nil)
|
||||
|
||||
type MultiService[U comparable] struct {
|
||||
*Service
|
||||
|
||||
|
@ -33,7 +36,7 @@ type MultiService[U comparable] struct {
|
|||
uCipher map[U]cipher.Block
|
||||
}
|
||||
|
||||
func NewMultiServiceWithPassword[U comparable](method string, password string, udpTimeout int64, handler shadowsocks.Handler) (*MultiService[U], error) {
|
||||
func NewMultiServiceWithPassword[U comparable](method string, password string, udpTimeout int64, handler shadowsocks.Handler, timeFunc func() time.Time) (*MultiService[U], error) {
|
||||
if password == "" {
|
||||
return nil, ErrMissingPSK
|
||||
}
|
||||
|
@ -41,10 +44,10 @@ func NewMultiServiceWithPassword[U comparable](method string, password string, u
|
|||
if err != nil {
|
||||
return nil, E.Cause(err, "decode psk")
|
||||
}
|
||||
return NewMultiService[U](method, iPSK, udpTimeout, handler)
|
||||
return NewMultiService[U](method, iPSK, udpTimeout, handler, timeFunc)
|
||||
}
|
||||
|
||||
func NewMultiService[U comparable](method string, iPSK []byte, udpTimeout int64, handler shadowsocks.Handler) (*MultiService[U], error) {
|
||||
func NewMultiService[U comparable](method string, iPSK []byte, udpTimeout int64, handler shadowsocks.Handler, timeFunc func() time.Time) (*MultiService[U], error) {
|
||||
switch method {
|
||||
case "2022-blake3-aes-128-gcm":
|
||||
case "2022-blake3-aes-256-gcm":
|
||||
|
@ -52,7 +55,7 @@ func NewMultiService[U comparable](method string, iPSK []byte, udpTimeout int64,
|
|||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
ss, err := NewService(method, iPSK, udpTimeout, handler)
|
||||
ss, err := NewService(method, iPSK, udpTimeout, handler, timeFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -113,16 +116,24 @@ func (s *MultiService[U]) UpdateUsersWithPasswords(userList []U, passwordList []
|
|||
}
|
||||
|
||||
func (s *MultiService[U]) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
err := s.newConnection(ctx, conn, metadata)
|
||||
err := s.NewConnection0(ctx, conn, metadata, conn, nil)
|
||||
if err != nil {
|
||||
err = &shadowsocks.ServerConnError{Conn: conn, Source: metadata.Source, Cause: err}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *MultiService[U]) newConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
func (s *MultiService[U]) NewConnection0(ctx context.Context, conn net.Conn, metadata M.Metadata, handshakeReader io.Reader, handshakeSuccess func()) error {
|
||||
requestHeader := make([]byte, s.keySaltLength+aes.BlockSize+shadowaead.Overhead+RequestHeaderFixedChunkLength)
|
||||
n, err := conn.Read(requestHeader)
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
)
|
||||
if handshakeSuccess != nil {
|
||||
n, err = io.ReadFull(handshakeReader, requestHeader)
|
||||
} else {
|
||||
n, err = handshakeReader.Read(requestHeader)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
} else if n < len(requestHeader) {
|
||||
|
@ -134,19 +145,17 @@ func (s *MultiService[U]) newConnection(ctx context.Context, conn net.Conn, meta
|
|||
}
|
||||
|
||||
var _eiHeader [aes.BlockSize]byte
|
||||
eiHeader := common.Dup(_eiHeader[:])
|
||||
eiHeader := _eiHeader[:]
|
||||
copy(eiHeader, requestHeader[s.keySaltLength:s.keySaltLength+aes.BlockSize])
|
||||
|
||||
keyMaterial := buf.Make(s.keySaltLength * 2)
|
||||
keyMaterial := make([]byte, s.keySaltLength*2)
|
||||
copy(keyMaterial, s.psk)
|
||||
copy(keyMaterial[s.keySaltLength:], requestSalt)
|
||||
_identitySubkey := buf.StackNewSize(s.keySaltLength)
|
||||
identitySubkey := common.Dup(_identitySubkey)
|
||||
identitySubkey := buf.NewSize(s.keySaltLength)
|
||||
identitySubkey.Extend(identitySubkey.FreeLen())
|
||||
blake3.DeriveKey(identitySubkey.Bytes(), "shadowsocks 2022 identity subkey", keyMaterial)
|
||||
b, err := s.blockConstructor(identitySubkey.Bytes())
|
||||
identitySubkey.Release()
|
||||
common.KeepAlive(_identitySubkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -158,12 +167,15 @@ func (s *MultiService[U]) newConnection(ctx context.Context, conn net.Conn, meta
|
|||
user = u
|
||||
uPSK = s.uPSK[u]
|
||||
} else {
|
||||
return E.New("invalid request")
|
||||
return ErrInvalidRequest
|
||||
}
|
||||
|
||||
if handshakeSuccess != nil {
|
||||
handshakeSuccess()
|
||||
}
|
||||
common.KeepAlive(_eiHeader)
|
||||
|
||||
requestKey := SessionKey(uPSK, requestSalt, s.keySaltLength)
|
||||
readCipher, err := s.constructor(common.Dup(requestKey))
|
||||
readCipher, err := s.constructor(requestKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -192,7 +204,7 @@ func (s *MultiService[U]) newConnection(ctx context.Context, conn net.Conn, meta
|
|||
if err != nil {
|
||||
return E.Cause(err, "read timestamp")
|
||||
}
|
||||
diff := int(math.Abs(float64(time.Now().Unix() - int64(epoch))))
|
||||
diff := int(math.Abs(float64(s.time().Unix() - int64(epoch))))
|
||||
if diff > 30 {
|
||||
return E.Extend(ErrBadTimestamp, "received ", epoch, ", diff ", diff, "s")
|
||||
}
|
||||
|
@ -263,7 +275,7 @@ func (s *MultiService[U]) newPacket(ctx context.Context, conn N.PacketConn, buff
|
|||
s.udpBlockCipher.Decrypt(packetHeader, packetHeader)
|
||||
|
||||
var _eiHeader [aes.BlockSize]byte
|
||||
eiHeader := common.Dup(_eiHeader[:])
|
||||
eiHeader := _eiHeader[:]
|
||||
s.udpBlockCipher.Decrypt(eiHeader, buffer.Range(aes.BlockSize, 2*aes.BlockSize))
|
||||
xorWords(eiHeader, eiHeader, packetHeader)
|
||||
|
||||
|
@ -294,11 +306,10 @@ func (s *MultiService[U]) newPacket(ctx context.Context, conn N.PacketConn, buff
|
|||
if !loaded {
|
||||
session.remoteSessionId = sessionId
|
||||
key := SessionKey(uPSK, packetHeader[:8], s.keySaltLength)
|
||||
session.remoteCipher, err = s.constructor(common.Dup(key))
|
||||
session.remoteCipher, err = s.constructor(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
common.KeepAlive(key)
|
||||
}
|
||||
|
||||
goto process
|
||||
|
@ -342,7 +353,7 @@ process:
|
|||
if err != nil {
|
||||
goto returnErr
|
||||
}
|
||||
diff := int(math.Abs(float64(time.Now().Unix() - int64(epoch))))
|
||||
diff := int(math.Abs(float64(s.time().Unix() - int64(epoch))))
|
||||
if diff > 30 {
|
||||
err = E.Extend(ErrBadTimestamp, "received ", epoch, ", diff ", diff, "s")
|
||||
goto returnErr
|
||||
|
@ -382,8 +393,7 @@ func (s *MultiService[U]) newUDPSession(uPSK []byte) *serverUDPSession {
|
|||
binary.BigEndian.PutUint64(sessionId, session.sessionId)
|
||||
key := SessionKey(uPSK, sessionId, s.keySaltLength)
|
||||
var err error
|
||||
session.cipher, err = s.constructor(common.Dup(key))
|
||||
session.cipher, err = s.constructor(key)
|
||||
common.Must(err)
|
||||
common.KeepAlive(key)
|
||||
return session
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func TestMultiService(t *testing.T) {
|
|||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
multiService, err := shadowaead_2022.NewMultiService[string](method, iPSK[:], 500, &multiHandler{t, &wg})
|
||||
multiService, err := shadowaead_2022.NewMultiService[string](method, iPSK[:], 500, &multiHandler{t, &wg}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func TestMultiService(t *testing.T) {
|
|||
rand.Reader.Read(uPSK[:])
|
||||
multiService.UpdateUsers([]string{"my user"}, [][]byte{uPSK[:]})
|
||||
|
||||
client, err := shadowaead_2022.New(method, [][]byte{iPSK[:], uPSK[:]})
|
||||
client, err := shadowaead_2022.New(method, [][]byte{iPSK[:], uPSK[:]}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -21,12 +21,12 @@ func TestService(t *testing.T) {
|
|||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
service, err := shadowaead_2022.NewService(method, psk[:], 500, &multiHandler{t, &wg})
|
||||
service, err := shadowaead_2022.NewService(method, psk[:], 500, &multiHandler{t, &wg}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
client, err := shadowaead_2022.New(method, [][]byte{psk[:]})
|
||||
client, err := shadowaead_2022.New(method, [][]byte{psk[:]}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package shadowimpl
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-shadowsocks"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead"
|
||||
"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
|
||||
|
@ -9,7 +11,7 @@ import (
|
|||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
func FetchMethod(method string, password string) (shadowsocks.Method, error) {
|
||||
func FetchMethod(method string, password string, timeFunc func() time.Time) (shadowsocks.Method, error) {
|
||||
if method == "none" || method == "plain" || method == "dummy" {
|
||||
return shadowsocks.NewNone(), nil
|
||||
} else if common.Contains(shadowstream.List, method) {
|
||||
|
@ -17,7 +19,7 @@ func FetchMethod(method string, password string) (shadowsocks.Method, error) {
|
|||
} else if common.Contains(shadowaead.List, method) {
|
||||
return shadowaead.New(method, nil, password)
|
||||
} else if common.Contains(shadowaead_2022.List, method) {
|
||||
return shadowaead_2022.NewWithPassword(method, password)
|
||||
return shadowaead_2022.NewWithPassword(method, password, timeFunc)
|
||||
} else {
|
||||
return nil, E.New("shadowsocks: unsupported method ", method)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
var (
|
||||
ErrBadKey = E.New("bad key")
|
||||
ErrMissingPassword = E.New("missing password")
|
||||
ErrNoUsers = E.New("no users")
|
||||
)
|
||||
|
||||
type Method interface {
|
||||
|
@ -31,6 +32,15 @@ type Service interface {
|
|||
E.Handler
|
||||
}
|
||||
|
||||
type MultiService[U comparable] interface {
|
||||
Name() string
|
||||
UpdateUsers(userList []U, keyList [][]byte) error
|
||||
UpdateUsersWithPasswords(userList []U, passwordList []string) error
|
||||
N.TCPConnectionHandler
|
||||
N.UDPHandler
|
||||
E.Handler
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
N.TCPConnectionHandler
|
||||
N.UDPConnectionHandler
|
||||
|
|
|
@ -167,9 +167,7 @@ type clientConn struct {
|
|||
}
|
||||
|
||||
func (c *clientConn) writeRequest() error {
|
||||
_buffer := buf.StackNewSize(c.saltLength + M.SocksaddrSerializer.AddrPortLen(c.destination))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewSize(c.saltLength + M.SocksaddrSerializer.AddrPortLen(c.destination))
|
||||
defer buffer.Release()
|
||||
|
||||
salt := buffer.Extend(c.saltLength)
|
||||
|
@ -200,9 +198,7 @@ func (c *clientConn) readResponse() error {
|
|||
if c.readStream != nil {
|
||||
return nil
|
||||
}
|
||||
_salt := buf.Make(c.saltLength)
|
||||
defer common.KeepAlive(_salt)
|
||||
salt := common.Dup(_salt)
|
||||
salt := make([]byte, c.saltLength)
|
||||
_, err := io.ReadFull(c.Conn, salt)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -238,6 +234,10 @@ func (c *clientConn) Write(p []byte) (n int, err error) {
|
|||
return c.Conn.Write(p)
|
||||
}
|
||||
|
||||
func (c *clientConn) NeedAdditionalReadDeadline() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *clientConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
@ -293,16 +293,18 @@ func (c *clientPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error)
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
addr = destination.UDPAddr()
|
||||
if destination.IsFqdn() {
|
||||
addr = destination
|
||||
} else {
|
||||
addr = destination.UDPAddr()
|
||||
}
|
||||
n = copy(p, buffer.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
func (c *clientPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
destination := M.SocksaddrFromNet(addr)
|
||||
_buffer := buf.StackNewSize(c.saltLength + M.SocksaddrSerializer.AddrPortLen(destination) + len(p))
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
buffer := buf.NewSize(c.saltLength + M.SocksaddrSerializer.AddrPortLen(destination) + len(p))
|
||||
defer buffer.Release()
|
||||
common.Must1(buffer.ReadFullFrom(rand.Reader, c.saltLength))
|
||||
err = M.SocksaddrSerializer.WriteAddrPort(buffer, M.SocksaddrFromNet(addr))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue