mirror of
https://github.com/howmp/reality
synced 2025-04-20 03:08:05 +08:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a69fea65a6 | ||
|
d527e5cc90 | ||
|
869bcc9d45 | ||
|
e9162c84b2 | ||
|
9bb9c6d084 | ||
|
b29f81307d | ||
|
fd6cfca2db |
8
.github/workflows/go.yml
vendored
8
.github/workflows/go.yml
vendored
@ -3,7 +3,10 @@
|
||||
|
||||
name: Go
|
||||
|
||||
on: push
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
jobs:
|
||||
|
||||
@ -21,11 +24,10 @@ jobs:
|
||||
run: |
|
||||
go install github.com/go-bindata/go-bindata/...@latest
|
||||
./build.sh
|
||||
zip -r grs.zip dist/*
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
files: grs.zip
|
||||
files: ./dist/*
|
||||
|
||||
|
20
README.md
20
README.md
@ -26,6 +26,8 @@ grs是一个反向socks5代理,其中grss和grsc和grsu是通过REALITY协议通
|
||||
|
||||
若SNIAddr或ServerAddr不指定,则尝试加载已有配置文件
|
||||
|
||||
默认生成3个不同id文件名的客户端,可通过`-c`参数指定
|
||||
|
||||
```txt
|
||||
Usage:
|
||||
grss [OPTIONS] gen [gen-OPTIONS] [SNIAddr] [ServerAddr]
|
||||
@ -40,6 +42,8 @@ Help Options:
|
||||
-f=[chrome|firefox|safari|ios|android|edge|360|qq] client finger print (default: chrome)
|
||||
-e= expire second (default: 30)
|
||||
-o= server config output path (default: config.json)
|
||||
-c= client count (default: 3)
|
||||
-s skip client cert verify
|
||||
--dir= client output directory (default: .)
|
||||
|
||||
[gen command arguments]
|
||||
@ -66,14 +70,20 @@ Help Options:
|
||||
|
||||
### 启动客户端
|
||||
|
||||
`grsc`
|
||||
`grscX`
|
||||
|
||||
**X表示id**
|
||||
|
||||
### 启动用户端
|
||||
|
||||
`grsu`
|
||||
`grsu -id 0`
|
||||
|
||||
**这里id参数对应了grsc的id,不同id会连接不同的grsc**
|
||||
|
||||
```txt
|
||||
Usage of grsu:
|
||||
-i uint
|
||||
id
|
||||
-l string
|
||||
socks5 listen address (default "127.0.0.1:61080")
|
||||
```
|
||||
@ -95,3 +105,9 @@ Usage of grsu:
|
||||
1. 也可以NTP同步客户端、用户端、服务端时间
|
||||
1. 服务端配置重新生成后,也需要使用最新的`grsc`和`grsu`,否则预共享密钥不匹配
|
||||
1. 客户端的网络可能被劫持
|
||||
|
||||
### 为什么客户端/用户端提示`certificate signed by unknown authority`?
|
||||
|
||||
运行环境缺少根证书,可以生成时指定`-s`选项,跳过验证
|
||||
|
||||
`grss gen -s www.qq.com:443 127.0.0.1:443`
|
21
build.sh
21
build.sh
@ -1,18 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir -p dist
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsc_darwin ./cmd/grsc
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsc_linux ./cmd/grsc
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsc_darwin_amd64 ./cmd/grsc
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsc_darwin_arm64 ./cmd/grsc
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsc_linux_amd64 ./cmd/grsc
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsc_linux_arm64 ./cmd/grsc
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsc_linux_mips ./cmd/grsc
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsc_windows.exe ./cmd/grsc
|
||||
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsu_darwin ./cmd/grsu
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsu_linux ./cmd/grsu
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsu_darwin_amd64 ./cmd/grsu
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsu_darwin_arm64 ./cmd/grsu
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsu_linux_amd64 ./cmd/grsu
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsu_linux_arm64 ./cmd/grsu
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsu_linux_mips ./cmd/grsu
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -trimpath -ldflags "-s -w" -o ./cmd/grss/client/grsu_windows.exe ./cmd/grsu
|
||||
|
||||
go-bindata -nomemcopy -nometadata -prefix cmd/grss/client -o ./cmd/grss/files.go ./cmd/grss/client/
|
||||
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -tags forceposix -trimpath -ldflags "-s -w" -o ./dist/grss_darwin ./cmd/grss
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags forceposix -trimpath -ldflags "-s -w" -o ./dist/grss_linux ./cmd/grss
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -tags forceposix -trimpath -ldflags "-s -w" -o ./dist/grss_darwin_amd64 ./cmd/grss
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -tags forceposix -trimpath -ldflags "-s -w" -o ./dist/grss_darwin_arm64 ./cmd/grss
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags forceposix -trimpath -ldflags "-s -w" -o ./dist/grss_linux_amd64 ./cmd/grss
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -tags forceposix -trimpath -ldflags "-s -w" -o ./dist/grss_linux_arm64 ./cmd/grss
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -tags forceposix -trimpath -ldflags "-s -w" -o ./dist/grss_linux_mips ./cmd/grss
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -tags forceposix -trimpath -ldflags "-s -w" -o ./dist/grss_windows.exe ./cmd/grss
|
||||
|
||||
cp README.md ./dist
|
||||
|
17
client.go
17
client.go
@ -21,11 +21,13 @@ import (
|
||||
type ClientConfig struct {
|
||||
ServerAddr string `json:"server_addr"`
|
||||
SNI string `json:"sni_name"`
|
||||
SkipVerify bool `json:"skip_verify"`
|
||||
PublicKeyECDH string `json:"public_key_ecdh"`
|
||||
PublicKeyVerify string `json:"public_key_verify"`
|
||||
FingerPrint string `json:"finger_print,omitempty"`
|
||||
ExpireSecond uint32 `json:"expire_second,omitempty"`
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
FingerPrint string `json:"finger_print"`
|
||||
ExpireSecond uint32 `json:"expire_second"`
|
||||
Debug bool `json:"debug"`
|
||||
OverlayData byte `json:"overlay_data"`
|
||||
|
||||
fingerPrint *utls.ClientHelloID // 客户端的TLS指纹
|
||||
publicKeyECDH *ecdh.PublicKey // 用于密钥协商
|
||||
@ -133,7 +135,7 @@ func UnmarshalClientConfig(configData []byte) (*ClientConfig, error) {
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, config *ClientConfig, overlayData byte) (net.Conn, error) {
|
||||
func NewClient(ctx context.Context, config *ClientConfig) (net.Conn, error) {
|
||||
if err := config.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -184,6 +186,7 @@ func NewClient(ctx context.Context, config *ClientConfig, overlayData byte) (net
|
||||
ServerName: config.SNI,
|
||||
SessionTicketsDisabled: true,
|
||||
MaxVersion: utls.VersionTLS12,
|
||||
InsecureSkipVerify: config.SkipVerify,
|
||||
},
|
||||
*config.fingerPrint,
|
||||
)
|
||||
@ -213,10 +216,10 @@ func NewClient(ctx context.Context, config *ClientConfig, overlayData byte) (net
|
||||
is12 := state.Version == versionTLS12
|
||||
if is12 {
|
||||
// 进行我们私有握手,客户端发送附加数据,服务端回复64字节签名数据
|
||||
logger.Debugf("overlayData: %x", overlayData)
|
||||
logger.Debugf("overlayData: %x", config.OverlayData)
|
||||
// record数据前缀模仿seq
|
||||
data := generateRandomData(seqNumerOne[:])
|
||||
data[len(data)-1] = overlayData
|
||||
data[len(data)-1] = config.OverlayData
|
||||
record := newTLSRecord(recordTypeApplicationData, versionTLS12, data)
|
||||
if _, err := record.writeTo(uconn.GetUnderlyingConn()); err != nil {
|
||||
uconn.Close()
|
||||
@ -247,7 +250,7 @@ func NewClient(ctx context.Context, config *ClientConfig, overlayData byte) (net
|
||||
}
|
||||
// 服务端回复验证通过
|
||||
logger.Debugln("verify ok")
|
||||
return newWarpConn(uconn.GetUnderlyingConn(), aead, overlayData, seqNumerOne), nil
|
||||
return newWarpConn(uconn.GetUnderlyingConn(), aead, config.OverlayData, seqNumerOne), nil
|
||||
}
|
||||
uconn.Close()
|
||||
return nil, ErrVerifyFailed
|
||||
|
@ -36,7 +36,7 @@ func TestClient(t *testing.T) {
|
||||
}
|
||||
t.Log(string(d))
|
||||
|
||||
_, err = reality.NewClient(context.Background(), config, 0)
|
||||
_, err = reality.NewClient(context.Background(), config)
|
||||
if err == nil {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
@ -48,7 +48,7 @@ func TestClientConfig(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config := configServer.ToClientConfig()
|
||||
config := configServer.ToClientConfig(0)
|
||||
configData, err := config.Marshal()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1,8 +1,20 @@
|
||||
package cmd
|
||||
|
||||
const (
|
||||
OverlayGRSC = byte(0x95)
|
||||
OverlayGRSU = byte(0x27)
|
||||
)
|
||||
func NewShortID(isGRSC bool, id byte) byte {
|
||||
if id > 0x80 {
|
||||
panic("id should be less than 128")
|
||||
}
|
||||
if isGRSC {
|
||||
return id
|
||||
}
|
||||
return 0x80 | id
|
||||
}
|
||||
|
||||
var ConfigDataPlaceholder = []byte{0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
func ParseShortID(shortID byte) (isGRSC bool, id byte) {
|
||||
if shortID >= 0x80 {
|
||||
return false, shortID & 0x7f
|
||||
}
|
||||
return true, shortID
|
||||
}
|
||||
|
||||
var ConfigDataPlaceholder = []byte{0xff, 0xff, 'g', 'r', 's', 'c', 'o', 'n', 'f', 'i', 'g', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
@ -45,7 +45,7 @@ type client struct {
|
||||
|
||||
func (c *client) serve() error {
|
||||
c.logger.Infoln("try connect to server")
|
||||
client, err := reality.NewClient(context.Background(), c.config, cmd.OverlayGRSC)
|
||||
client, err := reality.NewClient(context.Background(), c.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
utls "github.com/refraction-networking/utls"
|
||||
|
||||
@ -19,6 +21,8 @@ type gen struct {
|
||||
FingerPrint string `short:"f" default:"chrome" description:"client finger print" choice:"chrome" choice:"firefox" choice:"safari" choice:"ios" choice:"android" choice:"edge" choice:"360" choice:"qq"`
|
||||
ExpireSecond uint32 `short:"e" default:"30" description:"expire second"`
|
||||
ConfigPath string `short:"o" default:"config.json" description:"server config output path"`
|
||||
ClientCount byte `short:"c" default:"3" description:"client count"`
|
||||
SkipVerify bool `short:"s" description:"skip client cert verify"`
|
||||
ClientOutputDir string `long:"dir" default:"." description:"client output directory"`
|
||||
Positional struct {
|
||||
SNIAddr string `description:"tls server address, e.g. example.com:443"`
|
||||
@ -32,6 +36,11 @@ func (c *gen) Execute(args []string) error {
|
||||
c.logger = reality.GetLogger(c.Debug)
|
||||
var config *reality.ServerConfig
|
||||
var err error
|
||||
if c.ClientCount > 128 {
|
||||
return errors.New("client count must less than 128")
|
||||
} else if c.ClientCount == 0 {
|
||||
c.ClientCount = 1
|
||||
}
|
||||
if c.Positional.SNIAddr == "" || c.Positional.ServerAddr == "" {
|
||||
c.logger.Infof("try loading config, path %s", c.ConfigPath)
|
||||
config, err = loadConfig(c.ConfigPath)
|
||||
@ -52,7 +61,7 @@ func (c *gen) Execute(args []string) error {
|
||||
if err := c.check(); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.genClient(config.ToClientConfig())
|
||||
return c.genClient(config.ToClientConfig(0))
|
||||
|
||||
}
|
||||
|
||||
@ -101,6 +110,7 @@ func (c *gen) genConfig() (*reality.ServerConfig, error) {
|
||||
config.Debug = c.Debug
|
||||
config.ClientFingerPrint = c.FingerPrint
|
||||
config.ExpireSecond = c.ExpireSecond
|
||||
config.SkipVerify = c.SkipVerify
|
||||
data, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -118,6 +128,28 @@ func (c *gen) genClient(clientConfig *reality.ClientConfig) error {
|
||||
}
|
||||
|
||||
for _, name := range AssetNames() {
|
||||
if strings.HasPrefix(name, "grsc") {
|
||||
// 根据客户端数量生成多个客户端
|
||||
for i := 0; i < int(c.ClientCount); i++ {
|
||||
path := filepath.Join(c.ClientOutputDir, fmt.Sprintf("grsc%d%s", i, name[4:]))
|
||||
clientConfig.OverlayData = cmd.NewShortID(true, byte(i))
|
||||
clientConfigData, err := clientConfig.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ClientBin, err := replaceClientTemplate(MustAsset(name), clientConfigData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(path, ClientBin, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
c.logger.Infof("generated %s", path)
|
||||
}
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(c.ClientOutputDir, name)
|
||||
ClientBin, err := replaceClientTemplate(MustAsset(name), configData)
|
||||
if err != nil {
|
||||
@ -148,8 +180,7 @@ func loadConfig(path string) (*reality.ServerConfig, error) {
|
||||
|
||||
func replaceClientTemplate(template []byte, configData []byte) ([]byte, error) {
|
||||
placeholder := make([]byte, len(cmd.ConfigDataPlaceholder))
|
||||
placeholder[0] = 0xff
|
||||
placeholder[1] = 0xff
|
||||
copy(placeholder, []byte{0xff, 0xff, 'g', 'r', 's', 'c', 'o', 'n', 'f', 'i', 'g'})
|
||||
pos := bytes.Index(template, placeholder)
|
||||
if pos == -1 {
|
||||
return nil, errors.New("config placeholder not found")
|
||||
|
155
cmd/grss/serv.go
155
cmd/grss/serv.go
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@ -27,19 +26,81 @@ func (s *serv) Execute(args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type sessionManager struct {
|
||||
logger logrus.FieldLogger
|
||||
sessions [128]*yamux.Session
|
||||
sessionsLock [128]sync.Mutex
|
||||
}
|
||||
|
||||
func (s *sessionManager) createSession(conn net.Conn, id byte) {
|
||||
if s.isSessionOpen(id) {
|
||||
s.logger.Errorf("client(id:%d) session already open, close %s", id, conn.RemoteAddr())
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
s.sessionsLock[id].Lock()
|
||||
defer s.sessionsLock[id].Unlock()
|
||||
session, err := yamux.Server(conn, nil)
|
||||
if err != nil {
|
||||
s.logger.Error(err)
|
||||
conn.Close()
|
||||
}
|
||||
go s.checkSession(id, session)
|
||||
s.sessions[id] = session
|
||||
s.logger.Infof("client(id:%d) session opened %s", id, conn.RemoteAddr())
|
||||
}
|
||||
|
||||
func (s *sessionManager) isSessionOpen(id byte) bool {
|
||||
s.sessionsLock[id].Lock()
|
||||
defer s.sessionsLock[id].Unlock()
|
||||
session := s.sessions[id]
|
||||
if session != nil {
|
||||
return !session.IsClosed()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *sessionManager) openClientSessionStream(id byte) (*yamux.Stream, error) {
|
||||
s.sessionsLock[id].Lock()
|
||||
defer s.sessionsLock[id].Unlock()
|
||||
session := s.sessions[id]
|
||||
if session != nil {
|
||||
stream, err := session.OpenStream()
|
||||
if err != nil {
|
||||
session.Close()
|
||||
s.sessions[id] = nil
|
||||
return nil, err
|
||||
}
|
||||
return stream, nil
|
||||
}
|
||||
return nil, fmt.Errorf("client(id:%d) session not open", id)
|
||||
}
|
||||
|
||||
func (s *sessionManager) checkSession(id byte, session *yamux.Session) {
|
||||
<-session.CloseChan()
|
||||
s.logger.Infof("client session closed %s", session.RemoteAddr())
|
||||
s.sessionsLock[id].Lock()
|
||||
defer s.sessionsLock[id].Unlock()
|
||||
if s.sessions[id] == session {
|
||||
s.sessions[id] = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Server 反向socks5代理服务端
|
||||
type Server struct {
|
||||
config *reality.ServerConfig
|
||||
logger logrus.FieldLogger
|
||||
session *yamux.Session
|
||||
sessionLock *sync.Mutex
|
||||
sm *sessionManager
|
||||
}
|
||||
|
||||
func NewServer(config *reality.ServerConfig) *Server {
|
||||
logger := reality.GetLogger(config.Debug)
|
||||
return &Server{
|
||||
config: config,
|
||||
logger: reality.GetLogger(config.Debug),
|
||||
sessionLock: &sync.Mutex{},
|
||||
logger: logger,
|
||||
sm: &sessionManager{
|
||||
logger: logger,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,66 +124,48 @@ func (s *Server) Serve() {
|
||||
}
|
||||
|
||||
if o, ok := conn.(reality.OverlayData); ok {
|
||||
overlayData := o.OverlayData()
|
||||
isGRSC, id := cmd.ParseShortID(o.OverlayData())
|
||||
if isGRSC {
|
||||
s.logger.Infof("accept client(id:%d) %s", id, conn.RemoteAddr())
|
||||
|
||||
if overlayData == cmd.OverlayGRSC {
|
||||
s.logger.Infof("accept client %s", conn.RemoteAddr())
|
||||
go s.handleClient(conn)
|
||||
go s.sm.createSession(conn, id)
|
||||
continue
|
||||
} else if overlayData == cmd.OverlayGRSU {
|
||||
s.logger.Infof("accept user %s", conn.RemoteAddr())
|
||||
go s.handleUser(conn)
|
||||
} else {
|
||||
s.logger.Infof("accept user(id:%d) %s", id, conn.RemoteAddr())
|
||||
go s.handleUser(conn, id)
|
||||
continue
|
||||
}
|
||||
}
|
||||
s.logger.Warnf("accept %s, but overlay wrong", conn.RemoteAddr())
|
||||
s.logger.Warnf("accept %s, but no overlay data", conn.RemoteAddr())
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleClient(conn net.Conn) {
|
||||
if s.isSessionOpen() {
|
||||
s.logger.Errorf("client session already open, close %s", conn.RemoteAddr())
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
s.sessionLock.Lock()
|
||||
defer s.sessionLock.Unlock()
|
||||
session, err := yamux.Server(conn, nil)
|
||||
if err != nil {
|
||||
s.logger.Error(err)
|
||||
conn.Close()
|
||||
}
|
||||
go s.checkSession(session)
|
||||
s.session = session
|
||||
s.logger.Infof("session opened %s", conn.RemoteAddr())
|
||||
}
|
||||
|
||||
func (s *Server) handleUser(conn net.Conn) {
|
||||
func (s *Server) handleUser(conn net.Conn, id byte) {
|
||||
defer conn.Close()
|
||||
|
||||
session, err := yamux.Client(conn, nil)
|
||||
if err != nil {
|
||||
s.logger.Errorf("yamux: %v", err)
|
||||
s.logger.Errorf("user(id:%d) yamux: %v", id, err)
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
for {
|
||||
stream, err := session.Accept()
|
||||
if err != nil {
|
||||
s.logger.Errorf("user session accept: %v", err)
|
||||
s.logger.Errorf("user(id:%d) session accept: %v", id, err)
|
||||
return
|
||||
}
|
||||
s.logger.Infof("user stream accept %s", stream.RemoteAddr())
|
||||
go s.handleUserStream(stream)
|
||||
s.logger.Infof("user(id:%d) stream accept %s", id, stream.RemoteAddr())
|
||||
go s.handleUserStream(stream, id)
|
||||
|
||||
}
|
||||
}
|
||||
func (s *Server) handleUserStream(stream net.Conn) {
|
||||
func (s *Server) handleUserStream(stream net.Conn, id byte) {
|
||||
defer stream.Close()
|
||||
conn, err := s.openClientSessionStream()
|
||||
conn, err := s.sm.openClientSessionStream(id)
|
||||
if err != nil {
|
||||
s.logger.Errorf("open client session stream: %v", err)
|
||||
s.logger.Errorf("open client(id:%d) session stream: %v", id, err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
@ -130,37 +173,3 @@ func (s *Server) handleUserStream(stream net.Conn) {
|
||||
io.Copy(stream, conn)
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) isSessionOpen() bool {
|
||||
s.sessionLock.Lock()
|
||||
defer s.sessionLock.Unlock()
|
||||
if s.session != nil {
|
||||
return !s.session.IsClosed()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Server) openClientSessionStream() (*yamux.Stream, error) {
|
||||
s.sessionLock.Lock()
|
||||
defer s.sessionLock.Unlock()
|
||||
if s.session != nil {
|
||||
stream, err := s.session.OpenStream()
|
||||
if err != nil {
|
||||
s.session.Close()
|
||||
s.session = nil
|
||||
return nil, err
|
||||
}
|
||||
return stream, nil
|
||||
}
|
||||
return nil, errors.New("client session not open")
|
||||
}
|
||||
|
||||
func (s *Server) checkSession(session *yamux.Session) {
|
||||
<-session.CloseChan()
|
||||
s.logger.Infof("client session closed %s", session.RemoteAddr())
|
||||
s.sessionLock.Lock()
|
||||
defer s.sessionLock.Unlock()
|
||||
if s.session == session {
|
||||
s.session = nil
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func (s *serverSession) connectForever() {
|
||||
}
|
||||
func (s *serverSession) connect() {
|
||||
logger := s.logger
|
||||
client, err := reality.NewClient(context.Background(), s.config, cmd.OverlayGRSU)
|
||||
client, err := reality.NewClient(context.Background(), s.config)
|
||||
if err != nil {
|
||||
logger.Errorf("connect server: %v", err)
|
||||
return
|
||||
@ -76,10 +76,12 @@ func main() {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
addr := flag.String("l", "127.0.0.1:61080", "socks5 listen address")
|
||||
flag.Parse()
|
||||
logger := reality.GetLogger(config.Debug)
|
||||
addr := flag.String("l", "127.0.0.1:61080", "socks5 listen address")
|
||||
id := flag.Uint("i", 0, "id")
|
||||
flag.Parse()
|
||||
logger.Infof("server addr: %s, sni: %s, id: %d", config.ServerAddr, config.SNI, byte(*id))
|
||||
config.OverlayData = cmd.NewShortID(false, byte(*id))
|
||||
l, err := net.Listen("tcp", *addr)
|
||||
if err != nil {
|
||||
logger.Panic(err)
|
||||
|
@ -27,7 +27,7 @@ func main() {
|
||||
logger.Panic(err)
|
||||
}
|
||||
|
||||
client, err := reality.NewClient(context.Background(), &config, 0)
|
||||
client, err := reality.NewClient(context.Background(), &config)
|
||||
if err != nil {
|
||||
logger.Panic(err)
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ func main() {
|
||||
log.Panic(err)
|
||||
}
|
||||
config.Debug = true
|
||||
jsonData, err := json.MarshalIndent(config.ToClientConfig(), "", " ")
|
||||
jsonData, err := json.MarshalIndent(config.ToClientConfig(0), "", " ")
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
type ServerConfig struct {
|
||||
SNIAddr string `json:"sni_addr"`
|
||||
ServerAddr string `json:"server_addr"`
|
||||
SkipVerify bool `json:"skip_verify"`
|
||||
PrivateKeyECDH string `json:"private_key_ecdh"`
|
||||
PrivateKeySign string `json:"private_key_sign"`
|
||||
ExpireSecond uint32 `json:"expire_second"`
|
||||
@ -106,16 +107,18 @@ func (c *ServerConfig) SNIHost() string {
|
||||
func (c *ServerConfig) SNIPort() string {
|
||||
return c.sniPort
|
||||
}
|
||||
func (s *ServerConfig) ToClientConfig() *ClientConfig {
|
||||
func (s *ServerConfig) ToClientConfig(overlayData byte) *ClientConfig {
|
||||
|
||||
return &ClientConfig{
|
||||
SNI: s.sniHost,
|
||||
ServerAddr: s.ServerAddr,
|
||||
SkipVerify: s.SkipVerify,
|
||||
PublicKeyECDH: base64.StdEncoding.EncodeToString(s.privateKeyECDH.PublicKey().Bytes()),
|
||||
PublicKeyVerify: base64.StdEncoding.EncodeToString(s.privateKeySign.Public().(ed25519.PublicKey)),
|
||||
ExpireSecond: s.ExpireSecond,
|
||||
Debug: s.Debug,
|
||||
FingerPrint: s.ClientFingerPrint,
|
||||
OverlayData: overlayData,
|
||||
}
|
||||
}
|
||||
|
||||
|
2
utils.go
2
utils.go
@ -33,7 +33,7 @@ var seqNumerOne = [8]byte{0, 0, 0, 0, 0, 0, 0, 1}
|
||||
// generateNonce 根据SessionKey和ExpireSecond生成Nonce
|
||||
func generateNonce(NonceSize int, SessionKey []byte, ExpireSecond uint32) ([]byte, error) {
|
||||
info := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(info, uint64(time.Now().Unix()%int64(ExpireSecond)))
|
||||
binary.BigEndian.PutUint64(info, uint64(time.Now().Unix()/int64(ExpireSecond)))
|
||||
nonce := make([]byte, NonceSize)
|
||||
_, err := hkdf.New(sha256.New, SessionKey[:], Prefix, info).Read(nonce[:])
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user