mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 09:36:49 +08:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d67cf3d598 | ||
![]() |
ca633fc8c5 | ||
![]() |
c345d4818e | ||
![]() |
7fb1f65354 | ||
![]() |
4b97edae74 | ||
![]() |
8aabbeefe1 | ||
![]() |
48fab4d398 | ||
![]() |
8b9c0ae593 | ||
![]() |
347d9735da | ||
![]() |
9aa49be703 | ||
![]() |
fed8610d3f | ||
![]() |
d22c2d034c | ||
![]() |
4c10a9eb4e | ||
![]() |
4ff1ff1d7d | ||
![]() |
573b7807c0 | ||
![]() |
81d993158f | ||
![]() |
df39991bb3 | ||
![]() |
1b87264c53 | ||
![]() |
96d7156eba | ||
![]() |
d170416219 | ||
![]() |
8ca8a7126b | ||
![]() |
1174ff3090 |
33
.github/build/friendly-filenames.json
vendored
Normal file
33
.github/build/friendly-filenames.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"android-arm64": { "friendlyName": "android-arm64-v8a"},
|
||||
"darwin-amd64": { "friendlyName": "macos-64" },
|
||||
"darwin-arm64": { "friendlyName": "macos-arm64-v8a" },
|
||||
"dragonfly-amd64": { "friendlyName": "dragonfly-64" },
|
||||
"freebsd-386": { "friendlyName": "freebsd-32" },
|
||||
"freebsd-amd64": { "friendlyName": "freebsd-64" },
|
||||
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
|
||||
"freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" },
|
||||
"linux-386": { "friendlyName": "linux-32" },
|
||||
"linux-amd64": { "friendlyName": "linux-64" },
|
||||
"linux-arm5": { "friendlyName": "linux-arm32-v5" },
|
||||
"linux-arm64": { "friendlyName": "linux-arm64-v8a" },
|
||||
"linux-arm6": { "friendlyName": "linux-arm32-v6" },
|
||||
"linux-arm7": { "friendlyName": "linux-arm32-v7a" },
|
||||
"linux-mips64le": { "friendlyName": "linux-mips64le" },
|
||||
"linux-mips64": { "friendlyName": "linux-mips64" },
|
||||
"linux-mipsle": { "friendlyName": "linux-mips32le" },
|
||||
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
|
||||
"linux-mips": { "friendlyName": "linux-mips32" },
|
||||
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
|
||||
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
||||
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
||||
"linux-ppc64le": { "friendlyName": "linux-rppc64le" },
|
||||
"linux-s390x": { "friendlyName": "linux-s390x" },
|
||||
"openbsd-386": { "friendlyName": "openbsd-32" },
|
||||
"openbsd-amd64": { "friendlyName": "openbsd-64" },
|
||||
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
|
||||
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
||||
"windows-amd64": { "friendlyName": "windows-64" },
|
||||
"windows-386": { "friendlyName": "windows-32" },
|
||||
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
||||
}
|
212
.github/workflows/release.yml
vendored
Normal file
212
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
# Include amd64 on all platforms.
|
||||
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
|
||||
goarch: [amd64, 386]
|
||||
exclude:
|
||||
# Exclude i386 on darwin and dragonfly.
|
||||
- goarch: 386
|
||||
goos: dragonfly
|
||||
- goarch: 386
|
||||
goos: darwin
|
||||
include:
|
||||
# BEIGIN MacOS ARM64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
# END MacOS ARM64
|
||||
# BEGIN Linux ARM 5 6 7
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 6
|
||||
- goos: linux
|
||||
goarch: arm
|
||||
goarm: 5
|
||||
# END Linux ARM 5 6 7
|
||||
# BEGIN Android ARM 8
|
||||
- goos: android
|
||||
goarch: arm64
|
||||
# END Android ARM 8
|
||||
# Windows ARM 7
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
# BEGIN Other architectures
|
||||
# BEGIN riscv64 & ARM64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: linux
|
||||
goarch: riscv64
|
||||
# END riscv64 & ARM64
|
||||
# BEGIN MIPS
|
||||
- goos: linux
|
||||
goarch: mips64
|
||||
- goos: linux
|
||||
goarch: mips64le
|
||||
- goos: linux
|
||||
goarch: mipsle
|
||||
- goos: linux
|
||||
goarch: mips
|
||||
# END MIPS
|
||||
# BEGIN PPC
|
||||
- goos: linux
|
||||
goarch: ppc64
|
||||
- goos: linux
|
||||
goarch: ppc64le
|
||||
# END PPC
|
||||
# BEGIN FreeBSD ARM
|
||||
- goos: freebsd
|
||||
goarch: arm64
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
# END FreeBSD ARM
|
||||
# BEGIN S390X
|
||||
- goos: linux
|
||||
goarch: s390x
|
||||
# END S390X
|
||||
# END Other architectures
|
||||
# BEGIN OPENBSD ARM
|
||||
- goos: openbsd
|
||||
goarch: arm64
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
goarm: 7
|
||||
# END OPENBSD ARM
|
||||
fail-fast: false
|
||||
#配置编译环境
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
CGO_ENABLED: 0
|
||||
steps:
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Show workflow information
|
||||
id: get_filename
|
||||
run: |
|
||||
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
|
||||
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||
echo "::set-output name=ASSET_NAME::$_NAME"
|
||||
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.16
|
||||
|
||||
- name: Get project dependencies
|
||||
run: go mod download
|
||||
|
||||
- name: Replace Custom to Commit ID
|
||||
if: github.event_name != 'release'
|
||||
run: |
|
||||
ID=$(git rev-parse --short ${{ github.sha }})
|
||||
if [ "${{ github.event_name }}" == 'pull_request' ]
|
||||
then
|
||||
ID=$(git rev-parse --short ${{ github.event.pull_request.head.sha }})
|
||||
fi
|
||||
sed -i '/build/ s/Custom/'$ID'/' ./core/core.go
|
||||
|
||||
- name: Build Xray
|
||||
run: |
|
||||
mkdir -p build_assets
|
||||
go build -v -o build_assets/xray -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
- name: Build Mips softfloat Xray
|
||||
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||
run: |
|
||||
GOMIPS=softfloat go build -v -o build_assets/xray_softfloat -trimpath -ldflags "-s -w -buildid=" ./main
|
||||
|
||||
- name: Rename Windows Xray
|
||||
if: matrix.goos == 'windows'
|
||||
run: |
|
||||
cd ./build_assets || exit 1
|
||||
mv xray xray.exe
|
||||
|
||||
- name: Prepare to release
|
||||
run: |
|
||||
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
|
||||
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
||||
LIST=('geoip geoip geoip' 'domain-list-community dlc geosite')
|
||||
for i in "${LIST[@]}"
|
||||
do
|
||||
INFO=($(echo $i | awk 'BEGIN{FS=" ";OFS=" "} {print $1,$2,$3}'))
|
||||
LASTEST_TAG="$(curl -sL "https://api.github.com/repos/v2fly/${INFO[0]}/releases" | jq -r ".[0].tag_name" || echo "latest")"
|
||||
FILE_NAME="${INFO[2]}.dat"
|
||||
echo -e "Downloading ${FILE_NAME}..."
|
||||
curl -L "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat" -o ./build_assets/${FILE_NAME}
|
||||
echo -e "Verifying HASH key..."
|
||||
HASH="$(curl -sL "https://github.com/v2fly/${INFO[0]}/releases/download/${LASTEST_TAG}/${INFO[1]}.dat.sha256sum" | awk -F ' ' '{print $1}')"
|
||||
[ "$(sha256sum "./build_assets/${FILE_NAME}" | awk -F ' ' '{print $1}')" == "${HASH}" ] || { echo -e "The HASH key of ${FILE_NAME} does not match cloud one."; exit 1; }
|
||||
done
|
||||
|
||||
- name: Create ZIP archive
|
||||
shell: bash
|
||||
run: |
|
||||
pushd build_assets || exit 1
|
||||
touch -mt 202101010000 *
|
||||
zip -9vr ../Xray-$ASSET_NAME.zip .
|
||||
for CORE in $(ls xray*)
|
||||
do
|
||||
COREDGST=$CORE.dgst
|
||||
for METHOD in {"md5","sha1","sha256","sha512"}
|
||||
do
|
||||
openssl dgst -$METHOD $CORE | sed 's/([^)]*)//g' >>$COREDGST
|
||||
done
|
||||
done
|
||||
popd || exit 1
|
||||
FILE=./Xray-$ASSET_NAME.zip
|
||||
DGST=$FILE.dgst
|
||||
for METHOD in {"md5","sha1","sha256","sha512"}
|
||||
do
|
||||
openssl dgst -$METHOD $FILE | sed 's/([^)]*)//g' >>$DGST
|
||||
done
|
||||
|
||||
- name: Change the name
|
||||
run: |
|
||||
mv build_assets Xray-$ASSET_NAME
|
||||
|
||||
- name: Upload files to Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Xray-${{ steps.get_filename.outputs.ASSET_NAME }}
|
||||
path: |
|
||||
./Xray-${{ steps.get_filename.outputs.ASSET_NAME }}/*
|
||||
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ./Xray-${{ steps.get_filename.outputs.ASSET_NAME }}.zip*
|
||||
tag: ${{ github.ref }}
|
||||
file_glob: true
|
48
.github/workflows/test.yml
vendored
Normal file
48
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- "**/*.go"
|
||||
- "go.mod"
|
||||
- "go.sum"
|
||||
- ".github/workflows/*.yml"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ^1.16
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare geo*dat
|
||||
if: ${{ matrix.os != 'windows-latest' }}
|
||||
run: |
|
||||
mkdir resources
|
||||
wget -O ./resources/geoip.dat https://github.com/v2fly/geoip/releases/latest/download/geoip.dat
|
||||
wget -O ./resources/geosite.dat https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat
|
||||
- name: Prepare geo*dat for Windows
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: |
|
||||
mkdir resources
|
||||
Invoke-WebRequest -Uri "https://github.com/v2fly/geoip/releases/latest/download/geoip.dat" -OutFile "./resources/geoip.dat"
|
||||
Invoke-WebRequest -Uri "https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat" -OutFile "./resources/geosite.dat"
|
||||
- name: Test
|
||||
run: go test -timeout 1h -v ./...
|
@@ -17,6 +17,8 @@
|
||||
- One Click
|
||||
- [ProxySU](https://github.com/proxysu/ProxySU)
|
||||
- [v2ray-agent](https://github.com/mack-a/v2ray-agent)
|
||||
- [Xray-yes](https://github.com/jiuqi9997/Xray-yes)
|
||||
- [Xray_onekey](https://github.com/wulabing/Xray_onekey)
|
||||
- Magisk
|
||||
- [Xray4Magisk](https://github.com/CerteKim/Xray4Magisk)
|
||||
- [Xray_For_Magisk](https://github.com/E7KMbb/Xray_For_Magisk)
|
||||
@@ -38,6 +40,7 @@
|
||||
- Windows
|
||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||
- [Qv2ray](https://github.com/Qv2ray/Qv2ray)
|
||||
- [Netch (NetFilter & TUN/TAP)](https://github.com/NetchX/Netch)
|
||||
- Android
|
||||
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
||||
- [Kitsunebi](https://github.com/rurirei/Kitsunebi/tree/release_xtls)
|
||||
|
@@ -101,8 +101,8 @@ func (*staticHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
rr, _ := dns.NewRR("localhost-b. IN A 127.0.0.4")
|
||||
ans.Answer = append(ans.Answer, rr)
|
||||
|
||||
case q.Name == "Mijia\\ Cloud." && q.Qtype == dns.TypeA:
|
||||
rr, _ := dns.NewRR("Mijia\\ Cloud. IN A 127.0.0.1")
|
||||
case q.Name == "mijia\\ cloud." && q.Qtype == dns.TypeA:
|
||||
rr, _ := dns.NewRR("mijia\\ cloud. IN A 127.0.0.1")
|
||||
ans.Answer = append(ans.Answer, rr)
|
||||
}
|
||||
}
|
||||
|
@@ -42,6 +42,9 @@ func (m *Manager) AddHandler(ctx context.Context, handler inbound.Handler) error
|
||||
|
||||
tag := handler.Tag()
|
||||
if len(tag) > 0 {
|
||||
if _, found := m.taggedHandlers[tag]; found {
|
||||
return newError("existing tag found: " + tag)
|
||||
}
|
||||
m.taggedHandlers[tag] = handler
|
||||
} else {
|
||||
m.untaggedHandler = append(m.untaggedHandler, handler)
|
||||
|
@@ -109,6 +109,9 @@ func (m *Manager) AddHandler(ctx context.Context, handler outbound.Handler) erro
|
||||
|
||||
tag := handler.Tag()
|
||||
if len(tag) > 0 {
|
||||
if _, found := m.taggedHandler[tag]; found {
|
||||
return newError("existing tag found: " + tag)
|
||||
}
|
||||
m.taggedHandler[tag] = handler
|
||||
} else {
|
||||
m.untaggedHandlers = append(m.untaggedHandlers, handler)
|
||||
|
@@ -157,6 +157,9 @@ func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) {
|
||||
if w.draining {
|
||||
continue
|
||||
}
|
||||
if w.client.Closed() {
|
||||
continue
|
||||
}
|
||||
if w.client.ActiveConnections() < minConn {
|
||||
minConn = w.client.ActiveConnections()
|
||||
minIdx = i
|
||||
|
@@ -330,7 +330,7 @@ func (m *ClientWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader)
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
err := buf.Copy(rr, s.output)
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
newError("failed to write to downstream. closing session ", s.ID).Base(err).WriteToLog()
|
||||
|
@@ -81,6 +81,9 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error {
|
||||
if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if b.UDP != nil {
|
||||
b.WriteByte(byte(TargetNetworkUDP))
|
||||
addrParser.WriteAddressPort(b, b.UDP.Address, b.UDP.Port)
|
||||
}
|
||||
|
||||
len1 := b.Len()
|
||||
@@ -119,7 +122,7 @@ func (f *FrameMetadata) UnmarshalFromBuffer(b *buf.Buffer) error {
|
||||
f.Option = bitmask.Byte(b.Byte(3))
|
||||
f.Target.Network = net.Network_Unknown
|
||||
|
||||
if f.SessionStatus == SessionStatusNew {
|
||||
if f.SessionStatus == SessionStatusNew || (f.SessionStatus == SessionStatusKeep && b.Len() != 4) {
|
||||
if b.Len() < 8 {
|
||||
return newError("insufficient buffer: ", b.Len())
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/crypto"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
)
|
||||
|
||||
@@ -12,13 +13,15 @@ import (
|
||||
type PacketReader struct {
|
||||
reader io.Reader
|
||||
eof bool
|
||||
dest *net.Destination
|
||||
}
|
||||
|
||||
// NewPacketReader creates a new PacketReader.
|
||||
func NewPacketReader(reader io.Reader) *PacketReader {
|
||||
func NewPacketReader(reader io.Reader, dest *net.Destination) *PacketReader {
|
||||
return &PacketReader{
|
||||
reader: reader,
|
||||
eof: false,
|
||||
dest: dest,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +46,9 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
return nil, err
|
||||
}
|
||||
r.eof = true
|
||||
if r.dest != nil && r.dest.Network == net.Network_UDP {
|
||||
b.UDP = r.dest
|
||||
}
|
||||
return buf.MultiBuffer{b}, nil
|
||||
}
|
||||
|
||||
|
@@ -145,7 +145,7 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
||||
return nil
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader)
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
if err := buf.Copy(rr, s.output); err != nil {
|
||||
buf.Copy(rr, buf.Discard)
|
||||
common.Interrupt(s.input)
|
||||
@@ -168,7 +168,7 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere
|
||||
return buf.Copy(NewStreamReader(reader), buf.Discard)
|
||||
}
|
||||
|
||||
rr := s.NewReader(reader)
|
||||
rr := s.NewReader(reader, &meta.Target)
|
||||
err := buf.Copy(rr, s.output)
|
||||
|
||||
if err != nil && buf.IsWriteError(err) {
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
@@ -152,9 +153,9 @@ func (s *Session) Close() error {
|
||||
}
|
||||
|
||||
// NewReader creates a buf.Reader based on the transfer type of this Session.
|
||||
func (s *Session) NewReader(reader *buf.BufferedReader) buf.Reader {
|
||||
func (s *Session) NewReader(reader *buf.BufferedReader, dest *net.Destination) buf.Reader {
|
||||
if s.transferType == protocol.TransferTypeStream {
|
||||
return NewStreamReader(reader)
|
||||
}
|
||||
return NewPacketReader(reader)
|
||||
return NewPacketReader(reader, dest)
|
||||
}
|
||||
|
@@ -63,6 +63,9 @@ func (w *Writer) writeMetaOnly() error {
|
||||
|
||||
func writeMetaWithFrame(writer buf.Writer, meta FrameMetadata, data buf.MultiBuffer) error {
|
||||
frame := buf.New()
|
||||
if len(data) == 1 {
|
||||
frame.UDP = data[0].UDP
|
||||
}
|
||||
if err := meta.WriteTo(frame); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ func GetAssetLocation(file string) string {
|
||||
defPath,
|
||||
filepath.Join("/usr/local/share/xray/", file),
|
||||
filepath.Join("/usr/share/xray/", file),
|
||||
filepath.Join("/opt/share/xray/", file),
|
||||
} {
|
||||
if _, err := os.Stat(p); os.IsNotExist(err) {
|
||||
continue
|
||||
|
137
common/xudp/xudp.go
Normal file
137
common/xudp/xudp.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package xudp
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
var addrParser = protocol.NewAddressParser(
|
||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4),
|
||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain),
|
||||
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6),
|
||||
protocol.PortThenAddress(),
|
||||
)
|
||||
|
||||
func NewPacketWriter(writer buf.Writer, dest net.Destination) *PacketWriter {
|
||||
return &PacketWriter{
|
||||
Writer: writer,
|
||||
Dest: dest,
|
||||
}
|
||||
}
|
||||
|
||||
type PacketWriter struct {
|
||||
Writer buf.Writer
|
||||
Dest net.Destination
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
defer buf.ReleaseMulti(mb)
|
||||
mb2Write := make(buf.MultiBuffer, 0, len(mb))
|
||||
for _, b := range mb {
|
||||
length := b.Len()
|
||||
if length == 0 || length+666 > buf.Size {
|
||||
continue
|
||||
}
|
||||
|
||||
eb := buf.New()
|
||||
eb.Write([]byte{0, 0, 0, 0})
|
||||
if w.Dest.Network == net.Network_UDP {
|
||||
eb.WriteByte(1) // New
|
||||
eb.WriteByte(1) // Opt
|
||||
eb.WriteByte(2) // UDP
|
||||
addrParser.WriteAddressPort(eb, w.Dest.Address, w.Dest.Port)
|
||||
w.Dest.Network = net.Network_Unknown
|
||||
} else {
|
||||
eb.WriteByte(2) // Keep
|
||||
eb.WriteByte(1)
|
||||
if b.UDP != nil {
|
||||
eb.WriteByte(2)
|
||||
addrParser.WriteAddressPort(eb, b.UDP.Address, b.UDP.Port)
|
||||
}
|
||||
}
|
||||
l := eb.Len() - 2
|
||||
eb.SetByte(0, byte(l>>8))
|
||||
eb.SetByte(1, byte(l))
|
||||
eb.WriteByte(byte(length >> 8))
|
||||
eb.WriteByte(byte(length))
|
||||
eb.Write(b.Bytes())
|
||||
|
||||
mb2Write = append(mb2Write, eb)
|
||||
}
|
||||
if mb2Write.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
return w.Writer.WriteMultiBuffer(mb2Write)
|
||||
}
|
||||
|
||||
func NewPacketReader(reader io.Reader) *PacketReader {
|
||||
return &PacketReader{
|
||||
Reader: reader,
|
||||
cache: make([]byte, 2),
|
||||
}
|
||||
}
|
||||
|
||||
type PacketReader struct {
|
||||
Reader io.Reader
|
||||
cache []byte
|
||||
}
|
||||
|
||||
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
for {
|
||||
if _, err := io.ReadFull(r.Reader, r.cache); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := int32(r.cache[0])<<8 | int32(r.cache[1])
|
||||
if l < 4 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
b := buf.New()
|
||||
if _, err := b.ReadFullFrom(r.Reader, l); err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
discard := false
|
||||
switch b.Byte(2) {
|
||||
case 2:
|
||||
if l != 4 {
|
||||
b.Advance(5)
|
||||
addr, port, err := addrParser.ReadAddressPort(nil, b)
|
||||
if err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
b.UDP = &net.Destination{
|
||||
Network: net.Network_UDP,
|
||||
Address: addr,
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
discard = true
|
||||
default:
|
||||
b.Release()
|
||||
return nil, io.EOF
|
||||
}
|
||||
if b.Byte(3) == 1 {
|
||||
if _, err := io.ReadFull(r.Reader, r.cache); err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
length := int32(r.cache[0])<<8 | int32(r.cache[1])
|
||||
if length > 0 {
|
||||
b.Clear()
|
||||
if _, err := b.ReadFullFrom(r.Reader, length); err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
if !discard {
|
||||
return buf.MultiBuffer{b}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
b.Release()
|
||||
}
|
||||
}
|
@@ -22,9 +22,13 @@ type ConfigFormat struct {
|
||||
// ConfigLoader is a utility to load Xray config from external source.
|
||||
type ConfigLoader func(input interface{}) (*Config, error)
|
||||
|
||||
// ConfigBuilder is a builder to build core.Config from filenames and formats
|
||||
type ConfigBuilder func(files []string, formats []string) (*Config, error)
|
||||
|
||||
var (
|
||||
configLoaderByName = make(map[string]*ConfigFormat)
|
||||
configLoaderByExt = make(map[string]*ConfigFormat)
|
||||
configLoaderByName = make(map[string]*ConfigFormat)
|
||||
configLoaderByExt = make(map[string]*ConfigFormat)
|
||||
ConfigBuilderForFiles ConfigBuilder
|
||||
)
|
||||
|
||||
// RegisterConfigLoader add a new ConfigLoader.
|
||||
@@ -46,6 +50,21 @@ func RegisterConfigLoader(format *ConfigFormat) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetFormatByExtension(ext string) string {
|
||||
switch strings.ToLower(ext) {
|
||||
case "pb", "protobuf":
|
||||
return "protobuf"
|
||||
case "yaml", "yml":
|
||||
return "yaml"
|
||||
case "toml":
|
||||
return "toml"
|
||||
case "json":
|
||||
return "json"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func getExtension(filename string) string {
|
||||
idx := strings.LastIndexByte(filename, '.')
|
||||
if idx == -1 {
|
||||
@@ -54,23 +73,48 @@ func getExtension(filename string) string {
|
||||
return filename[idx+1:]
|
||||
}
|
||||
|
||||
// LoadConfig loads config with given format from given source.
|
||||
// input accepts 2 different types:
|
||||
// * []string slice of multiple filename/url(s) to open to read
|
||||
// * io.Reader that reads a config content (the original way)
|
||||
func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) {
|
||||
ext := getExtension(filename)
|
||||
if len(ext) > 0 {
|
||||
if f, found := configLoaderByExt[ext]; found {
|
||||
return f.Loader(input)
|
||||
func getFormat(filename string) string {
|
||||
return GetFormatByExtension(getExtension(filename))
|
||||
}
|
||||
|
||||
func LoadConfig(formatName string, input interface{}) (*Config, error) {
|
||||
switch v := input.(type) {
|
||||
case cmdarg.Arg:
|
||||
|
||||
formats := make([]string, len(v))
|
||||
hasProtobuf := false
|
||||
for i, file := range v {
|
||||
f := getFormat(file)
|
||||
if f == "" {
|
||||
f = formatName
|
||||
}
|
||||
if f == "protobuf" {
|
||||
hasProtobuf = true
|
||||
}
|
||||
formats[i] = f
|
||||
}
|
||||
|
||||
// only one protobuf config file is allowed
|
||||
if hasProtobuf {
|
||||
if len(v) == 1 {
|
||||
return configLoaderByName["protobuf"].Loader(v)
|
||||
} else {
|
||||
return nil, newError("Only one protobuf config file is allowed").AtWarning()
|
||||
}
|
||||
}
|
||||
|
||||
// to avoid import cycle
|
||||
return ConfigBuilderForFiles(v, formats)
|
||||
|
||||
case io.Reader:
|
||||
if f, found := configLoaderByName[formatName]; found {
|
||||
return f.Loader(v)
|
||||
} else {
|
||||
return nil, newError("Unable to load config in", formatName).AtWarning()
|
||||
}
|
||||
}
|
||||
|
||||
if f, found := configLoaderByName[formatName]; found {
|
||||
return f.Loader(input)
|
||||
}
|
||||
|
||||
return nil, newError("Unable to load config in ", formatName).AtWarning()
|
||||
return nil, newError("Unable to load config").AtWarning()
|
||||
}
|
||||
|
||||
func loadProtobufConfig(data []byte) (*Config, error) {
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.2.4"
|
||||
version = "1.3.1"
|
||||
build = "Custom"
|
||||
codename = "Xray, Penetrates Everything."
|
||||
intro = "A unified platform for anti-censorship."
|
||||
|
@@ -25,7 +25,7 @@ func CreateObject(v *Instance, config interface{}) (interface{}, error) {
|
||||
//
|
||||
// xray:api:stable
|
||||
func StartInstance(configFormat string, configBytes []byte) (*Instance, error) {
|
||||
config, err := LoadConfig(configFormat, "", bytes.NewReader(configBytes))
|
||||
config, err := LoadConfig(configFormat, bytes.NewReader(configBytes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
33
core/xray.go
33
core/xray.go
@@ -2,14 +2,10 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/xtls/xray-core/app/proxyman"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/serial"
|
||||
"github.com/xtls/xray-core/features"
|
||||
@@ -184,32 +180,7 @@ func NewWithContext(ctx context.Context, config *Config) (*Instance, error) {
|
||||
}
|
||||
|
||||
func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
|
||||
cone := true
|
||||
v, t := false, false
|
||||
for _, outbound := range config.Outbound {
|
||||
s := strings.ToLower(outbound.ProxySettings.Type)
|
||||
l := len(s)
|
||||
if l >= 16 && s[11:16] == "vless" || l >= 16 && s[11:16] == "vmess" {
|
||||
v = true
|
||||
continue
|
||||
}
|
||||
if l >= 17 && s[11:17] == "trojan" || l >= 22 && s[11:22] == "shadowsocks" {
|
||||
t = true
|
||||
if outbound.SenderSettings != nil {
|
||||
var m proxyman.SenderConfig
|
||||
proto.Unmarshal(outbound.SenderSettings.Value, &m)
|
||||
if m.MultiplexSettings != nil && m.MultiplexSettings.Enabled {
|
||||
cone = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if v && !t {
|
||||
cone = false
|
||||
}
|
||||
server.ctx = context.WithValue(server.ctx, "cone", cone)
|
||||
defer debug.FreeOSMemory()
|
||||
server.ctx = context.WithValue(server.ctx, "cone", os.Getenv("XRAY_CONE_DISABLED") != "true")
|
||||
|
||||
if config.Transport != nil {
|
||||
features.PrintDeprecatedFeatureWarning("global transport settings")
|
||||
|
16
go.mod
16
go.mod
@@ -4,23 +4,23 @@ go 1.15
|
||||
|
||||
require (
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/go-cmp v0.5.4
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/lucas-clemente/quic-go v0.19.3
|
||||
github.com/miekg/dns v1.1.37
|
||||
github.com/miekg/dns v1.1.40
|
||||
github.com/pelletier/go-toml v1.8.1
|
||||
github.com/pires/go-proxyproto v0.4.2
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499
|
||||
go.starlark.net v0.0.0-20210126161401-bc864be25151
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
|
||||
google.golang.org/grpc v1.35.0
|
||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b
|
||||
google.golang.org/grpc v1.36.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
h12.io/socks v1.0.2
|
||||
)
|
||||
|
41
go.sum
41
go.sum
@@ -49,8 +49,9 @@ github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200j
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -69,6 +70,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
@@ -107,8 +109,8 @@ github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07Vxb
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.37 h1:+kky2ArpBqk0S/74RkwFjmKM9jja7AB1RN7VUuVq0iM=
|
||||
github.com/miekg/dns v1.1.37/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
@@ -169,7 +171,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@@ -180,8 +181,8 @@ github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499 h1:QHESTXtfgc1ABV+ArlbPVqU
|
||||
github.com/xtls/go v0.0.0-20201118062508-3632bf3b7499/go.mod h1:5TB2+k58gx4A4g2Nf5miSHNDF6CuAzHKpWBooLAshTs=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.starlark.net v0.0.0-20210126161401-bc864be25151 h1:BkNycm5DBve8zINASgjl15CPNQq/xuZ0ViAlir4sUcw=
|
||||
go.starlark.net v0.0.0-20210126161401-bc864be25151/go.mod h1:vxxlMsgCAPH7BR2LtxjJC4WhhZhCGd/b01+CIpj8H4k=
|
||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984 h1:xwwDQW5We85NaTk2APgoN9202w/l0DVGp+GZMfsrh7s=
|
||||
go.starlark.net v0.0.0-20210223155950-e043a3d3c984/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -190,14 +191,15 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -212,8 +214,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -225,8 +227,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -242,10 +244,10 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b h1:kHlr0tATeLRMEiZJu5CknOw/E8V6h69sXXQFGoPtjcc=
|
||||
golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -264,10 +266,13 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
@@ -292,8 +297,8 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
44
infra/conf/serial/builder.go
Normal file
44
infra/conf/serial/builder.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package serial
|
||||
|
||||
import (
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/main/confloader"
|
||||
"io"
|
||||
)
|
||||
|
||||
func BuildConfig(files []string, formats []string) (*core.Config, error) {
|
||||
|
||||
cf := &conf.Config{}
|
||||
for i, file := range files {
|
||||
newError("Reading config: ", file).AtInfo().WriteToLog()
|
||||
r, err := confloader.LoadConfig(file)
|
||||
if err != nil {
|
||||
return nil, newError("failed to read config: ", file).Base(err)
|
||||
}
|
||||
c, err := ReaderDecoderByFormat[formats[i]](r)
|
||||
if err != nil {
|
||||
return nil, newError("failed to decode config: ", file).Base(err)
|
||||
}
|
||||
if i == 0 {
|
||||
*cf = *c
|
||||
continue
|
||||
}
|
||||
cf.Override(c, file)
|
||||
}
|
||||
return cf.Build()
|
||||
}
|
||||
|
||||
type readerDecoder func(io.Reader) (*conf.Config, error)
|
||||
|
||||
var (
|
||||
ReaderDecoderByFormat = make(map[string]readerDecoder)
|
||||
)
|
||||
|
||||
func init() {
|
||||
ReaderDecoderByFormat["json"] = DecodeJSONConfig
|
||||
ReaderDecoderByFormat["yaml"] = DecodeYAMLConfig
|
||||
ReaderDecoderByFormat["toml"] = DecodeTOMLConfig
|
||||
|
||||
core.ConfigBuilderForFiles = BuildConfig
|
||||
}
|
@@ -247,12 +247,13 @@ func readFileOrString(f string, s []string) ([]byte, error) {
|
||||
}
|
||||
|
||||
type TLSCertConfig struct {
|
||||
CertFile string `json:"certificateFile"`
|
||||
CertStr []string `json:"certificate"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
KeyStr []string `json:"key"`
|
||||
Usage string `json:"usage"`
|
||||
OcspStapling int64 `json:"ocspStapling"`
|
||||
CertFile string `json:"certificateFile"`
|
||||
CertStr []string `json:"certificate"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
KeyStr []string `json:"key"`
|
||||
Usage string `json:"usage"`
|
||||
OcspStapling uint64 `json:"ocspStapling"`
|
||||
OneTimeLoading bool `json:"oneTimeLoading"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
@@ -264,6 +265,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
return nil, newError("failed to parse certificate").Base(err)
|
||||
}
|
||||
certificate.Certificate = cert
|
||||
certificate.CertificatePath = c.CertFile
|
||||
|
||||
if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 {
|
||||
key, err := readFileOrString(c.KeyFile, c.KeyStr)
|
||||
@@ -271,6 +273,7 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
return nil, newError("failed to parse key").Base(err)
|
||||
}
|
||||
certificate.Key = key
|
||||
certificate.KeyPath = c.KeyFile
|
||||
}
|
||||
|
||||
switch strings.ToLower(c.Usage) {
|
||||
@@ -283,7 +286,11 @@ func (c *TLSCertConfig) Build() (*tls.Certificate, error) {
|
||||
default:
|
||||
certificate.Usage = tls.Certificate_ENCIPHERMENT
|
||||
}
|
||||
|
||||
if certificate.KeyPath == "" && certificate.CertificatePath == "" {
|
||||
certificate.OneTimeLoading = true
|
||||
} else {
|
||||
certificate.OneTimeLoading = c.OneTimeLoading
|
||||
}
|
||||
certificate.OcspStapling = c.OcspStapling
|
||||
|
||||
return certificate, nil
|
||||
@@ -331,23 +338,24 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
||||
}
|
||||
|
||||
type XTLSCertConfig struct {
|
||||
CertFile string `json:"certificateFile"`
|
||||
CertStr []string `json:"certificate"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
KeyStr []string `json:"key"`
|
||||
Usage string `json:"usage"`
|
||||
OcspStapling int64 `json:"ocspStapling"`
|
||||
CertFile string `json:"certificateFile"`
|
||||
CertStr []string `json:"certificate"`
|
||||
KeyFile string `json:"keyFile"`
|
||||
KeyStr []string `json:"key"`
|
||||
Usage string `json:"usage"`
|
||||
OcspStapling uint64 `json:"ocspStapling"`
|
||||
OneTimeLoading bool `json:"oneTimeLoading"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
func (c *XTLSCertConfig) Build() (*xtls.Certificate, error) {
|
||||
certificate := new(xtls.Certificate)
|
||||
|
||||
cert, err := readFileOrString(c.CertFile, c.CertStr)
|
||||
if err != nil {
|
||||
return nil, newError("failed to parse certificate").Base(err)
|
||||
}
|
||||
certificate.Certificate = cert
|
||||
certificate.CertificatePath = c.CertFile
|
||||
|
||||
if len(c.KeyFile) > 0 || len(c.KeyStr) > 0 {
|
||||
key, err := readFileOrString(c.KeyFile, c.KeyStr)
|
||||
@@ -355,6 +363,7 @@ func (c *XTLSCertConfig) Build() (*xtls.Certificate, error) {
|
||||
return nil, newError("failed to parse key").Base(err)
|
||||
}
|
||||
certificate.Key = key
|
||||
certificate.KeyPath = c.KeyFile
|
||||
}
|
||||
|
||||
switch strings.ToLower(c.Usage) {
|
||||
@@ -367,7 +376,11 @@ func (c *XTLSCertConfig) Build() (*xtls.Certificate, error) {
|
||||
default:
|
||||
certificate.Usage = xtls.Certificate_ENCIPHERMENT
|
||||
}
|
||||
|
||||
if certificate.KeyPath == "" && certificate.CertificatePath == "" {
|
||||
certificate.OneTimeLoading = true
|
||||
} else {
|
||||
certificate.OneTimeLoading = c.OneTimeLoading
|
||||
}
|
||||
certificate.OcspStapling = c.OcspStapling
|
||||
|
||||
return certificate, nil
|
||||
|
29
main/run.go
29
main/run.go
@@ -11,13 +11,11 @@ import (
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/xtls/xray-core/common/cmdarg"
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
"github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/infra/conf"
|
||||
"github.com/xtls/xray-core/main/commands/base"
|
||||
)
|
||||
|
||||
@@ -83,9 +81,11 @@ func executeRun(cmd *base.Command, args []string) {
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
conf.FileCache = nil
|
||||
conf.IPCache = nil
|
||||
conf.SiteCache = nil
|
||||
/*
|
||||
conf.FileCache = nil
|
||||
conf.IPCache = nil
|
||||
conf.SiteCache = nil
|
||||
*/
|
||||
|
||||
// Explicitly triggering GC to remove garbage from config loading.
|
||||
runtime.GC()
|
||||
@@ -158,30 +158,25 @@ func getConfigFilePath() cmdarg.Arg {
|
||||
}
|
||||
|
||||
func getConfigFormat() string {
|
||||
switch strings.ToLower(*format) {
|
||||
case "pb", "protobuf":
|
||||
return "protobuf"
|
||||
case "yaml", "yml":
|
||||
return "yaml"
|
||||
case "toml":
|
||||
return "toml"
|
||||
default:
|
||||
return "json"
|
||||
f := core.GetFormatByExtension(*format)
|
||||
if f == "" {
|
||||
f = "json"
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func startXray() (core.Server, error) {
|
||||
configFiles := getConfigFilePath()
|
||||
|
||||
config, err := core.LoadConfig(getConfigFormat(), configFiles[0], configFiles)
|
||||
//config, err := core.LoadConfig(getConfigFormat(), configFiles[0], configFiles)
|
||||
|
||||
//config, err := core.LoadConfigs(getConfigFormat(), configFiles)
|
||||
c, err := core.LoadConfig(getConfigFormat(), configFiles)
|
||||
|
||||
if err != nil {
|
||||
return nil, newError("failed to load config files: [", configFiles.String(), "]").Base(err)
|
||||
}
|
||||
|
||||
server, err := core.New(config)
|
||||
server, err := core.New(c)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create server").Base(err)
|
||||
}
|
||||
|
@@ -97,13 +97,16 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
return newError("target not specified.")
|
||||
}
|
||||
destination := outbound.Target
|
||||
UDPOverride := net.UDPDestination(nil, 0)
|
||||
if h.config.DestinationOverride != nil {
|
||||
server := h.config.DestinationOverride.Server
|
||||
if isValidAddress(server.Address) {
|
||||
destination.Address = server.Address.AsAddress()
|
||||
UDPOverride.Address = destination.Address
|
||||
}
|
||||
if server.Port != 0 {
|
||||
destination.Port = net.Port(server.Port)
|
||||
UDPOverride.Port = destination.Port
|
||||
}
|
||||
}
|
||||
newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))
|
||||
@@ -149,7 +152,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
if destination.Network == net.Network_TCP {
|
||||
writer = buf.NewWriter(conn)
|
||||
} else {
|
||||
writer = NewPacketWriter(conn, h, ctx)
|
||||
writer = NewPacketWriter(conn, h, ctx, UDPOverride)
|
||||
}
|
||||
|
||||
if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil {
|
||||
@@ -226,7 +229,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
return buf.MultiBuffer{b}, nil
|
||||
}
|
||||
|
||||
func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context) buf.Writer {
|
||||
func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride net.Destination) buf.Writer {
|
||||
iConn := conn
|
||||
statConn, ok := iConn.(*internet.StatCouterConnection)
|
||||
if ok {
|
||||
@@ -242,6 +245,7 @@ func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context) buf.Writer
|
||||
Counter: counter,
|
||||
Handler: h,
|
||||
Context: ctx,
|
||||
UDPOverride: UDPOverride,
|
||||
}
|
||||
}
|
||||
return &buf.SequentialWriter{Writer: conn}
|
||||
@@ -252,6 +256,7 @@ type PacketWriter struct {
|
||||
stats.Counter
|
||||
*Handler
|
||||
context.Context
|
||||
UDPOverride net.Destination
|
||||
}
|
||||
|
||||
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
@@ -264,6 +269,12 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||
var n int
|
||||
var err error
|
||||
if b.UDP != nil {
|
||||
if w.UDPOverride.Address != nil {
|
||||
b.UDP.Address = w.UDPOverride.Address
|
||||
}
|
||||
if w.UDPOverride.Port != 0 {
|
||||
b.UDP.Port = w.UDPOverride.Port
|
||||
}
|
||||
if w.Handler.config.useIP() && b.UDP.Address.Family().IsDomain() {
|
||||
ip := w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil)
|
||||
if ip != nil {
|
||||
|
@@ -7,6 +7,8 @@ import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
@@ -31,6 +33,31 @@ func (a *MemoryAccount) Equals(another protocol.Account) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *MemoryAccount) GetCipherName() string {
|
||||
switch a.Cipher.(type) {
|
||||
case *AesCfb:
|
||||
keyBytes := a.Cipher.(*AesCfb).KeyBytes
|
||||
return "AES_" + strconv.FormatInt(int64(keyBytes*8), 10) + "_CFB"
|
||||
case *ChaCha20:
|
||||
if a.Cipher.(*ChaCha20).IVBytes == 8 {
|
||||
return "CHACHA20"
|
||||
}
|
||||
return "CHACHA20_IETF"
|
||||
case *AEADCipher:
|
||||
switch reflect.ValueOf(a.Cipher.(*AEADCipher).AEADAuthCreator).Pointer() {
|
||||
case reflect.ValueOf(createAesGcm).Pointer():
|
||||
keyBytes := a.Cipher.(*AEADCipher).KeyBytes
|
||||
return "AES_" + strconv.FormatInt(int64(keyBytes*8), 10) + "_GCM"
|
||||
case reflect.ValueOf(createChacha20Poly1305).Pointer():
|
||||
return "CHACHA20_POLY1305"
|
||||
}
|
||||
case *NoneCipher:
|
||||
return "NONE"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func createAesGcm(key []byte) cipher.AEAD {
|
||||
block, err := aes.NewCipher(key)
|
||||
common.Must(err)
|
||||
|
@@ -54,12 +54,9 @@ func (r *FullReader) Read(p []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
// ReadTCPSession reads a Shadowsocks TCP session from the given reader, returns its header and remaining parts.
|
||||
func ReadTCPSession(users []*protocol.MemoryUser, reader io.Reader) (*protocol.RequestHeader, buf.Reader, error) {
|
||||
user := users[0]
|
||||
account := user.Account.(*MemoryAccount)
|
||||
func ReadTCPSession(validator *Validator, reader io.Reader) (*protocol.RequestHeader, buf.Reader, error) {
|
||||
|
||||
hashkdf := hmac.New(sha256.New, []byte("SSBSKDF"))
|
||||
hashkdf.Write(account.Key)
|
||||
|
||||
behaviorSeed := crc32.ChecksumIEEE(hashkdf.Sum(nil))
|
||||
|
||||
@@ -71,10 +68,20 @@ func ReadTCPSession(users []*protocol.MemoryUser, reader io.Reader) (*protocol.R
|
||||
readSizeRemain := DrainSize
|
||||
|
||||
var r2 buf.Reader
|
||||
buffer := buf.New()
|
||||
defer buffer.Release()
|
||||
|
||||
if len(users) > 1 {
|
||||
buffer := buf.New()
|
||||
defer buffer.Release()
|
||||
var user *protocol.MemoryUser
|
||||
var ivLen int32
|
||||
var err error
|
||||
|
||||
count := validator.Count()
|
||||
if count == 0 {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
DrainConnN(reader, readSizeRemain)
|
||||
return nil, nil, newError("invalid user")
|
||||
} else if count > 1 {
|
||||
var aead cipher.AEAD
|
||||
|
||||
if _, err := buffer.ReadFullFrom(reader, 50); err != nil {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
@@ -83,45 +90,26 @@ func ReadTCPSession(users []*protocol.MemoryUser, reader io.Reader) (*protocol.R
|
||||
}
|
||||
|
||||
bs := buffer.Bytes()
|
||||
user, aead, _, ivLen, err = validator.Get(bs, protocol.RequestCommandTCP)
|
||||
|
||||
var aeadCipher *AEADCipher
|
||||
var ivLen int32
|
||||
subkey := make([]byte, 32)
|
||||
length := make([]byte, 16)
|
||||
var aead cipher.AEAD
|
||||
var err error
|
||||
for _, user = range users {
|
||||
account = user.Account.(*MemoryAccount)
|
||||
aeadCipher = account.Cipher.(*AEADCipher)
|
||||
ivLen = aeadCipher.IVSize()
|
||||
subkey = subkey[:aeadCipher.KeyBytes]
|
||||
hkdfSHA1(account.Key, bs[:ivLen], subkey)
|
||||
aead = aeadCipher.AEADAuthCreator(subkey)
|
||||
_, err = aead.Open(length[:0], length[4:16], bs[ivLen:ivLen+18], nil)
|
||||
if err == nil {
|
||||
reader = &FullReader{reader, bs[ivLen:]}
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
AEAD: aead,
|
||||
NonceGenerator: crypto.GenerateInitialAEADNonce(),
|
||||
}
|
||||
r2 = crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
|
||||
Auth: auth,
|
||||
}, reader, protocol.TransferTypeStream, nil)
|
||||
break
|
||||
if user != nil {
|
||||
reader = &FullReader{reader, bs[ivLen:]}
|
||||
auth := &crypto.AEADAuthenticator{
|
||||
AEAD: aead,
|
||||
NonceGenerator: crypto.GenerateInitialAEADNonce(),
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
r2 = crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
|
||||
Auth: auth,
|
||||
}, reader, protocol.TransferTypeStream, nil)
|
||||
} else {
|
||||
readSizeRemain -= int(buffer.Len())
|
||||
DrainConnN(reader, readSizeRemain)
|
||||
return nil, nil, newError("failed to match an user").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
buffer := buf.New()
|
||||
defer buffer.Release()
|
||||
|
||||
if r2 == nil {
|
||||
ivLen := account.Cipher.IVSize()
|
||||
} else {
|
||||
user, ivLen = validator.GetOnlyUser()
|
||||
account := user.Account.(*MemoryAccount)
|
||||
hashkdf.Write(account.Key)
|
||||
var iv []byte
|
||||
if ivLen > 0 {
|
||||
if _, err := buffer.ReadFullFrom(reader, ivLen); err != nil {
|
||||
@@ -261,40 +249,31 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
func DecodeUDPPacket(users []*protocol.MemoryUser, payload *buf.Buffer) (*protocol.RequestHeader, *buf.Buffer, error) {
|
||||
func DecodeUDPPacket(validator *Validator, payload *buf.Buffer) (*protocol.RequestHeader, *buf.Buffer, error) {
|
||||
bs := payload.Bytes()
|
||||
if len(bs) <= 32 {
|
||||
return nil, nil, newError("len(bs) <= 32")
|
||||
}
|
||||
|
||||
var user *protocol.MemoryUser
|
||||
var account *MemoryAccount
|
||||
var err error
|
||||
|
||||
if len(users) > 1 {
|
||||
bs := payload.Bytes()
|
||||
if len(bs) <= 32 {
|
||||
return nil, nil, newError("len(bs) <= 32")
|
||||
}
|
||||
|
||||
var aeadCipher *AEADCipher
|
||||
var ivLen int32
|
||||
subkey := make([]byte, 32)
|
||||
data := make([]byte, 8192)
|
||||
var aead cipher.AEAD
|
||||
count := validator.Count()
|
||||
if count == 0 {
|
||||
return nil, nil, newError("invalid user")
|
||||
} else if count > 1 {
|
||||
var d []byte
|
||||
for _, user = range users {
|
||||
account = user.Account.(*MemoryAccount)
|
||||
aeadCipher = account.Cipher.(*AEADCipher)
|
||||
ivLen = aeadCipher.IVSize()
|
||||
subkey = subkey[:aeadCipher.KeyBytes]
|
||||
hkdfSHA1(account.Key, bs[:ivLen], subkey)
|
||||
aead = aeadCipher.AEADAuthCreator(subkey)
|
||||
d, err = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil)
|
||||
if err == nil {
|
||||
payload.Clear()
|
||||
payload.Write(d)
|
||||
break
|
||||
}
|
||||
user, _, d, _, err = validator.Get(bs, protocol.RequestCommandUDP)
|
||||
|
||||
if user != nil {
|
||||
payload.Clear()
|
||||
payload.Write(d)
|
||||
} else {
|
||||
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
|
||||
}
|
||||
} else {
|
||||
user = users[0]
|
||||
account = user.Account.(*MemoryAccount)
|
||||
user, _ = validator.GetOnlyUser()
|
||||
account := user.Account.(*MemoryAccount)
|
||||
|
||||
var iv []byte
|
||||
if !account.Cipher.IsAEAD() && account.Cipher.IVSize() > 0 {
|
||||
@@ -302,12 +281,9 @@ func DecodeUDPPacket(users []*protocol.MemoryUser, payload *buf.Buffer) (*protoc
|
||||
iv = make([]byte, account.Cipher.IVSize())
|
||||
copy(iv, payload.BytesTo(account.Cipher.IVSize()))
|
||||
}
|
||||
|
||||
err = account.Cipher.DecodePacket(account.Key, payload)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
|
||||
if err = account.Cipher.DecodePacket(account.Key, payload); err != nil {
|
||||
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
|
||||
}
|
||||
}
|
||||
|
||||
request := &protocol.RequestHeader{
|
||||
@@ -341,7 +317,10 @@ func (v *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
}
|
||||
u, payload, err := DecodeUDPPacket([]*protocol.MemoryUser{v.User}, buffer)
|
||||
validator := new(Validator)
|
||||
validator.Add(v.User)
|
||||
|
||||
u, payload, err := DecodeUDPPacket(validator, buffer)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return nil, err
|
||||
|
@@ -38,7 +38,9 @@ func TestUDPEncoding(t *testing.T) {
|
||||
encodedData, err := EncodeUDPPacket(request, data.Bytes())
|
||||
common.Must(err)
|
||||
|
||||
decodedRequest, decodedData, err := DecodeUDPPacket([]*protocol.MemoryUser{request.User}, encodedData)
|
||||
validator := new(Validator)
|
||||
validator.Add(request.User)
|
||||
decodedRequest, decodedData, err := DecodeUDPPacket(validator, encodedData)
|
||||
common.Must(err)
|
||||
|
||||
if r := cmp.Diff(decodedData.Bytes(), data.Bytes()); r != "" {
|
||||
@@ -117,7 +119,9 @@ func TestTCPRequest(t *testing.T) {
|
||||
|
||||
common.Must(writer.WriteMultiBuffer(buf.MultiBuffer{data}))
|
||||
|
||||
decodedRequest, reader, err := ReadTCPSession([]*protocol.MemoryUser{request.User}, cache)
|
||||
validator := new(Validator)
|
||||
validator.Add(request.User)
|
||||
decodedRequest, reader, err := ReadTCPSession(validator, cache)
|
||||
common.Must(err)
|
||||
if r := cmp.Diff(decodedRequest, request, cmp.Comparer(func(a1, a2 protocol.Account) bool { return a1.Equals(a2) })); r != "" {
|
||||
t.Error("request: ", r)
|
||||
|
@@ -22,35 +22,46 @@ import (
|
||||
|
||||
type Server struct {
|
||||
config *ServerConfig
|
||||
users []*protocol.MemoryUser
|
||||
validator *Validator
|
||||
policyManager policy.Manager
|
||||
cone bool
|
||||
}
|
||||
|
||||
// NewServer create a new Shadowsocks server.
|
||||
func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) {
|
||||
if config.Users == nil {
|
||||
return nil, newError("empty users")
|
||||
validator := new(Validator)
|
||||
for _, user := range config.Users {
|
||||
u, err := user.ToMemoryUser()
|
||||
if err != nil {
|
||||
return nil, newError("failed to get shadowsocks user").Base(err).AtError()
|
||||
}
|
||||
|
||||
if err := validator.Add(u); err != nil {
|
||||
return nil, newError("failed to add user").Base(err).AtError()
|
||||
}
|
||||
}
|
||||
|
||||
v := core.MustFromContext(ctx)
|
||||
s := &Server{
|
||||
config: config,
|
||||
validator: validator,
|
||||
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||
cone: ctx.Value("cone").(bool),
|
||||
}
|
||||
|
||||
for _, user := range config.Users {
|
||||
u, err := user.ToMemoryUser()
|
||||
if err != nil {
|
||||
return nil, newError("failed to parse user account").Base(err)
|
||||
}
|
||||
s.users = append(s.users, u)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// AddUser implements proxy.UserManager.AddUser().
|
||||
func (s *Server) AddUser(ctx context.Context, u *protocol.MemoryUser) error {
|
||||
return s.validator.Add(u)
|
||||
}
|
||||
|
||||
// RemoveUser implements proxy.UserManager.RemoveUser().
|
||||
func (s *Server) RemoveUser(ctx context.Context, e string) error {
|
||||
return s.validator.Del(e)
|
||||
}
|
||||
|
||||
func (s *Server) Network() []net.Network {
|
||||
list := s.config.Network
|
||||
if len(list) == 0 {
|
||||
@@ -64,13 +75,13 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet
|
||||
case net.Network_TCP:
|
||||
return s.handleConnection(ctx, conn, dispatcher)
|
||||
case net.Network_UDP:
|
||||
return s.handlerUDPPayload(ctx, conn, dispatcher)
|
||||
return s.handleUDPPayload(ctx, conn, dispatcher)
|
||||
default:
|
||||
return newError("unknown network: ", network)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
||||
func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error {
|
||||
udpServer := udp.NewDispatcher(dispatcher, func(ctx context.Context, packet *udp_proto.Packet) {
|
||||
request := protocol.RequestHeaderFromContext(ctx)
|
||||
if request == nil {
|
||||
@@ -102,8 +113,9 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
if inbound == nil {
|
||||
panic("no inbound metadata")
|
||||
}
|
||||
if len(s.users) == 1 {
|
||||
inbound.User = s.users[0]
|
||||
|
||||
if s.validator.Count() == 1 {
|
||||
inbound.User, _ = s.validator.GetOnlyUser()
|
||||
}
|
||||
|
||||
var dest *net.Destination
|
||||
@@ -121,9 +133,11 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection
|
||||
var err error
|
||||
|
||||
if inbound.User != nil {
|
||||
request, data, err = DecodeUDPPacket([]*protocol.MemoryUser{inbound.User}, payload)
|
||||
validator := new(Validator)
|
||||
validator.Add(inbound.User)
|
||||
request, data, err = DecodeUDPPacket(validator, payload)
|
||||
} else {
|
||||
request, data, err = DecodeUDPPacket(s.users, payload)
|
||||
request, data, err = DecodeUDPPacket(s.validator, payload)
|
||||
if err == nil {
|
||||
inbound.User = request.User
|
||||
}
|
||||
@@ -178,7 +192,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection,
|
||||
}
|
||||
|
||||
bufferedReader := buf.BufferedReader{Reader: buf.NewReader(conn)}
|
||||
request, bodyReader, err := ReadTCPSession(s.users, &bufferedReader)
|
||||
request, bodyReader, err := ReadTCPSession(s.validator, &bufferedReader)
|
||||
if err != nil {
|
||||
log.Record(&log.AccessMessage{
|
||||
From: conn.RemoteAddr(),
|
||||
|
113
proxy/shadowsocks/validator.go
Normal file
113
proxy/shadowsocks/validator.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package shadowsocks
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
)
|
||||
|
||||
// Validator stores valid Shadowsocks users.
|
||||
type Validator struct {
|
||||
// Considering email's usage here, map + sync.Mutex/RWMutex may have better performance.
|
||||
email sync.Map
|
||||
users sync.Map
|
||||
}
|
||||
|
||||
// Add a Shadowsocks user, Email must be empty or unique.
|
||||
func (v *Validator) Add(u *protocol.MemoryUser) error {
|
||||
account := u.Account.(*MemoryAccount)
|
||||
|
||||
if !account.Cipher.IsAEAD() && v.Count() > 0 {
|
||||
return newError("The cipher do not support Single-port Multi-user")
|
||||
}
|
||||
|
||||
if u.Email != "" {
|
||||
_, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u)
|
||||
if loaded {
|
||||
return newError("User ", u.Email, " already exists.")
|
||||
}
|
||||
}
|
||||
|
||||
v.users.Store(string(account.Key)+"&"+account.GetCipherName(), u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Del a Shadowsocks user with a non-empty Email.
|
||||
func (v *Validator) Del(e string) error {
|
||||
if e == "" {
|
||||
return newError("Email must not be empty.")
|
||||
}
|
||||
le := strings.ToLower(e)
|
||||
u, _ := v.email.Load(le)
|
||||
if u == nil {
|
||||
return newError("User ", e, " not found.")
|
||||
}
|
||||
account := u.(*protocol.MemoryUser).Account.(*MemoryAccount)
|
||||
v.email.Delete(le)
|
||||
v.users.Delete(string(account.Key) + "&" + account.GetCipherName())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Count the number of Shadowsocks users
|
||||
func (v *Validator) Count() int {
|
||||
length := 0
|
||||
v.users.Range(func(_, _ interface{}) bool {
|
||||
length++
|
||||
|
||||
return true
|
||||
})
|
||||
return length
|
||||
}
|
||||
|
||||
// Get a Shadowsocks user and the user's cipher.
|
||||
func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol.MemoryUser, aead cipher.AEAD, ret []byte, ivLen int32, err error) {
|
||||
var dataSize int
|
||||
|
||||
switch command {
|
||||
case protocol.RequestCommandTCP:
|
||||
dataSize = 16
|
||||
case protocol.RequestCommandUDP:
|
||||
dataSize = 8192
|
||||
}
|
||||
|
||||
var aeadCipher *AEADCipher
|
||||
subkey := make([]byte, 32)
|
||||
data := make([]byte, dataSize)
|
||||
|
||||
v.users.Range(func(key, user interface{}) bool {
|
||||
account := user.(*protocol.MemoryUser).Account.(*MemoryAccount)
|
||||
aeadCipher = account.Cipher.(*AEADCipher)
|
||||
ivLen = aeadCipher.IVSize()
|
||||
subkey = subkey[:aeadCipher.KeyBytes]
|
||||
hkdfSHA1(account.Key, bs[:ivLen], subkey)
|
||||
aead = aeadCipher.AEADAuthCreator(subkey)
|
||||
|
||||
switch command {
|
||||
case protocol.RequestCommandTCP:
|
||||
ret, err = aead.Open(data[:0], data[4:16], bs[ivLen:ivLen+18], nil)
|
||||
case protocol.RequestCommandUDP:
|
||||
ret, err = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
u = user.(*protocol.MemoryUser)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Get the only user without authentication
|
||||
func (v *Validator) GetOnlyUser() (u *protocol.MemoryUser, ivLen int32) {
|
||||
v.users.Range(func(_, user interface{}) bool {
|
||||
u = user.(*protocol.MemoryUser)
|
||||
return false
|
||||
})
|
||||
ivLen = u.Account.(*MemoryAccount).Cipher.IVSize()
|
||||
|
||||
return
|
||||
}
|
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/common/xudp"
|
||||
core "github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
"github.com/xtls/xray-core/features/stats"
|
||||
@@ -48,6 +49,7 @@ type Handler struct {
|
||||
serverList *protocol.ServerList
|
||||
serverPicker protocol.ServerPicker
|
||||
policyManager policy.Manager
|
||||
cone bool
|
||||
}
|
||||
|
||||
// New creates a new VLess outbound handler.
|
||||
@@ -66,6 +68,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
||||
serverList: serverList,
|
||||
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
|
||||
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||
cone: ctx.Value("cone").(bool),
|
||||
}
|
||||
|
||||
return handler, nil
|
||||
@@ -175,6 +178,12 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
clientReader := link.Reader // .(*pipe.Reader)
|
||||
clientWriter := link.Writer // .(*pipe.Writer)
|
||||
|
||||
if request.Command == protocol.RequestCommandUDP && h.cone {
|
||||
request.Command = protocol.RequestCommandMux
|
||||
request.Address = net.DomainAddress("v1.mux.cool")
|
||||
request.Port = net.Port(666)
|
||||
}
|
||||
|
||||
postRequest := func() error {
|
||||
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||
|
||||
@@ -185,6 +194,9 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
|
||||
// default: serverWriter := bufferWriter
|
||||
serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons)
|
||||
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
||||
serverWriter = xudp.NewPacketWriter(serverWriter, target)
|
||||
}
|
||||
if err := buf.CopyOnceTimeout(clientReader, serverWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
|
||||
return err // ...
|
||||
}
|
||||
@@ -216,6 +228,9 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
|
||||
// default: serverReader := buf.NewReader(conn)
|
||||
serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons)
|
||||
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
||||
serverReader = xudp.NewPacketReader(conn)
|
||||
}
|
||||
|
||||
if rawConn != nil {
|
||||
var counter stats.Counter
|
||||
|
@@ -6,11 +6,20 @@ import (
|
||||
"hash"
|
||||
)
|
||||
|
||||
type hash2 struct {
|
||||
hash.Hash
|
||||
}
|
||||
|
||||
func KDF(key []byte, path ...string) []byte {
|
||||
hmacf := hmac.New(sha256.New, []byte(KDFSaltConstVMessAEADKDF))
|
||||
|
||||
for _, v := range path {
|
||||
first := true
|
||||
hmacf = hmac.New(func() hash.Hash {
|
||||
if first {
|
||||
first = false
|
||||
return hash2{hmacf}
|
||||
}
|
||||
return hmacf
|
||||
}, []byte(v))
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/common/signal"
|
||||
"github.com/xtls/xray-core/common/task"
|
||||
"github.com/xtls/xray-core/common/xudp"
|
||||
core "github.com/xtls/xray-core/core"
|
||||
"github.com/xtls/xray-core/features/policy"
|
||||
"github.com/xtls/xray-core/proxy/vmess"
|
||||
@@ -28,6 +29,7 @@ type Handler struct {
|
||||
serverList *protocol.ServerList
|
||||
serverPicker protocol.ServerPicker
|
||||
policyManager policy.Manager
|
||||
cone bool
|
||||
}
|
||||
|
||||
// New creates a new VMess outbound handler.
|
||||
@@ -46,6 +48,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
||||
serverList: serverList,
|
||||
serverPicker: protocol.NewRoundRobinServerPicker(serverList),
|
||||
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||
cone: ctx.Value("cone").(bool),
|
||||
}
|
||||
|
||||
return handler, nil
|
||||
@@ -122,6 +125,12 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
|
||||
|
||||
if request.Command == protocol.RequestCommandUDP && h.cone {
|
||||
request.Command = protocol.RequestCommandMux
|
||||
request.Address = net.DomainAddress("v1.mux.cool")
|
||||
request.Port = net.Port(666)
|
||||
}
|
||||
|
||||
requestDone := func() error {
|
||||
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
|
||||
|
||||
@@ -131,6 +140,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
}
|
||||
|
||||
bodyWriter := session.EncodeRequestBody(request, writer)
|
||||
bodyWriter2 := bodyWriter
|
||||
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
||||
bodyWriter = xudp.NewPacketWriter(bodyWriter, target)
|
||||
}
|
||||
if err := buf.CopyOnceTimeout(input, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
|
||||
return newError("failed to write first payload").Base(err)
|
||||
}
|
||||
@@ -144,7 +157,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
}
|
||||
|
||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||
if err := bodyWriter.WriteMultiBuffer(buf.MultiBuffer{}); err != nil {
|
||||
if err := bodyWriter2.WriteMultiBuffer(buf.MultiBuffer{}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -163,6 +176,9 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||
h.handleCommand(rec.Destination(), header.Command)
|
||||
|
||||
bodyReader := session.DecodeResponseBody(request, reader)
|
||||
if request.Command == protocol.RequestCommandMux && request.Port == 666 {
|
||||
bodyReader = xudp.NewPacketReader(&buf.BufferedReader{Reader: bodyReader})
|
||||
}
|
||||
|
||||
return buf.Copy(bodyReader, output, buf.UpdateActivity(timer))
|
||||
}
|
||||
|
@@ -583,7 +583,7 @@ func TestHTTP2(t *testing.T) {
|
||||
|
||||
var errg errgroup.Group
|
||||
for i := 0; i < 10; i++ {
|
||||
errg.Go(testTCPConn(clientPort, 10240*1024, time.Second*40))
|
||||
errg.Go(testTCPConn(clientPort, 1024*1024, time.Second*40))
|
||||
}
|
||||
if err := errg.Wait(); err != nil {
|
||||
t.Error(err)
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/net/cnc"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
"github.com/xtls/xray-core/transport/internet/tls"
|
||||
"github.com/xtls/xray-core/transport/pipe"
|
||||
@@ -22,7 +23,7 @@ var (
|
||||
globalDialerAccess sync.Mutex
|
||||
)
|
||||
|
||||
func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.Config) (*http.Client, error) {
|
||||
func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.Config, sockopt *internet.SocketConfig) (*http.Client, error) {
|
||||
globalDialerAccess.Lock()
|
||||
defer globalDialerAccess.Unlock()
|
||||
|
||||
@@ -49,17 +50,24 @@ func getHTTPClient(ctx context.Context, dest net.Destination, tlsSettings *tls.C
|
||||
}
|
||||
address := net.ParseAddress(rawHost)
|
||||
|
||||
pconn, err := internet.DialSystem(ctx, net.TCPDestination(address, port), nil)
|
||||
dctx := context.Background()
|
||||
dctx = session.ContextWithID(dctx, session.IDFromContext(ctx))
|
||||
dctx = session.ContextWithOutbound(dctx, session.OutboundFromContext(ctx))
|
||||
|
||||
pconn, err := internet.DialSystem(dctx, net.TCPDestination(address, port), sockopt)
|
||||
if err != nil {
|
||||
newError("failed to dial to " + addr).Base(err).AtError().WriteToLog()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cn := gotls.Client(pconn, tlsConfig)
|
||||
if err := cn.Handshake(); err != nil {
|
||||
newError("failed to dial to " + addr).Base(err).AtError().WriteToLog()
|
||||
return nil, err
|
||||
}
|
||||
if !tlsConfig.InsecureSkipVerify {
|
||||
if err := cn.VerifyHostname(tlsConfig.ServerName); err != nil {
|
||||
newError("failed to dial to " + addr).Base(err).AtError().WriteToLog()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -90,7 +98,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
||||
if tlsConfig == nil {
|
||||
return nil, newError("TLS must be enabled for http transport.").AtWarning()
|
||||
}
|
||||
client, err := getHTTPClient(ctx, dest, tlsConfig)
|
||||
client, err := getHTTPClient(ctx, dest, tlsConfig, streamSettings.SocketSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -73,10 +73,10 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe
|
||||
l.listener = listener
|
||||
|
||||
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||
l.tlsConfig = config.GetTLSConfig(tls.WithNextProto("h2"))
|
||||
l.tlsConfig = config.GetTLSConfig()
|
||||
}
|
||||
if config := xtls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||
l.xtlsConfig = config.GetXTLSConfig(xtls.WithNextProto("h2"))
|
||||
l.xtlsConfig = config.GetXTLSConfig()
|
||||
}
|
||||
|
||||
if tcpSettings.HeaderSettings != nil {
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/ocsp"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
@@ -59,18 +60,56 @@ func (c *Config) BuildCertificates() []*tls.Certificate {
|
||||
continue
|
||||
}
|
||||
certs = append(certs, &keyPair)
|
||||
if entry.OcspStapling != 0 {
|
||||
go func(cert *tls.Certificate) {
|
||||
t := time.NewTicker(time.Duration(entry.OcspStapling) * time.Second)
|
||||
if !entry.OneTimeLoading {
|
||||
var isOcspstapling bool
|
||||
hotReloadCertInterval := uint64(3600)
|
||||
if entry.OcspStapling != 0 {
|
||||
hotReloadCertInterval = entry.OcspStapling
|
||||
isOcspstapling = true
|
||||
}
|
||||
index := len(certs) - 1
|
||||
go func(cert *tls.Certificate, index int) {
|
||||
t := time.NewTicker(time.Duration(hotReloadCertInterval) * time.Second)
|
||||
for {
|
||||
if newData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil {
|
||||
newError("ignoring invalid OCSP").Base(err).AtWarning().WriteToLog()
|
||||
} else if string(newData) != string(cert.OCSPStaple) {
|
||||
cert.OCSPStaple = newData
|
||||
if entry.CertificatePath != "" && entry.KeyPath != "" {
|
||||
newCert, err := filesystem.ReadFile(entry.CertificatePath)
|
||||
if err != nil {
|
||||
newError("failed to parse certificate").Base(err).AtError().WriteToLog()
|
||||
<-t.C
|
||||
continue
|
||||
}
|
||||
newKey, err := filesystem.ReadFile(entry.KeyPath)
|
||||
if err != nil {
|
||||
newError("failed to parse key").Base(err).AtError().WriteToLog()
|
||||
<-t.C
|
||||
continue
|
||||
}
|
||||
if string(newCert) != string(entry.Certificate) && string(newKey) != string(entry.Key) {
|
||||
newKeyPair, err := tls.X509KeyPair(newCert, newKey)
|
||||
if err != nil {
|
||||
newError("ignoring invalid X509 key pair").Base(err).AtError().WriteToLog()
|
||||
<-t.C
|
||||
continue
|
||||
}
|
||||
if newKeyPair.Leaf, err = x509.ParseCertificate(newKeyPair.Certificate[0]); err != nil {
|
||||
newError("ignoring invalid certificate").Base(err).AtError().WriteToLog()
|
||||
<-t.C
|
||||
continue
|
||||
}
|
||||
cert = &newKeyPair
|
||||
}
|
||||
}
|
||||
if isOcspstapling {
|
||||
if newOCSPData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil {
|
||||
newError("ignoring invalid OCSP").Base(err).AtWarning().WriteToLog()
|
||||
} else if string(newOCSPData) != string(cert.OCSPStaple) {
|
||||
cert.OCSPStaple = newOCSPData
|
||||
}
|
||||
}
|
||||
certs[index] = cert
|
||||
<-t.C
|
||||
}
|
||||
}(certs[len(certs)-1])
|
||||
}(certs[len(certs)-1], index)
|
||||
}
|
||||
}
|
||||
return certs
|
||||
|
@@ -84,7 +84,13 @@ type Certificate struct {
|
||||
// TLS key in x509 format.
|
||||
Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Usage Certificate_Usage `protobuf:"varint,3,opt,name=usage,proto3,enum=xray.transport.internet.tls.Certificate_Usage" json:"usage,omitempty"`
|
||||
OcspStapling int64 `protobuf:"varint,4,opt,name=ocsp_stapling,json=ocspStapling,proto3" json:"ocsp_stapling,omitempty"`
|
||||
OcspStapling uint64 `protobuf:"varint,4,opt,name=ocsp_stapling,json=ocspStapling,proto3" json:"ocsp_stapling,omitempty"`
|
||||
// TLS certificate path
|
||||
CertificatePath string `protobuf:"bytes,5,opt,name=certificate_path,json=certificatePath,proto3" json:"certificate_path,omitempty"`
|
||||
// TLS Key path
|
||||
KeyPath string `protobuf:"bytes,6,opt,name=key_path,json=keyPath,proto3" json:"key_path,omitempty"`
|
||||
// If true, one-Time Loading
|
||||
OneTimeLoading bool `protobuf:"varint,7,opt,name=One_time_loading,json=OneTimeLoading,proto3" json:"One_time_loading,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Certificate) Reset() {
|
||||
@@ -140,13 +146,34 @@ func (x *Certificate) GetUsage() Certificate_Usage {
|
||||
return Certificate_ENCIPHERMENT
|
||||
}
|
||||
|
||||
func (x *Certificate) GetOcspStapling() int64 {
|
||||
func (x *Certificate) GetOcspStapling() uint64 {
|
||||
if x != nil {
|
||||
return x.OcspStapling
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Certificate) GetCertificatePath() string {
|
||||
if x != nil {
|
||||
return x.CertificatePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Certificate) GetKeyPath() string {
|
||||
if x != nil {
|
||||
return x.KeyPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Certificate) GetOneTimeLoading() bool {
|
||||
if x != nil {
|
||||
return x.OneTimeLoading
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -284,7 +311,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74,
|
||||
0x6c, 0x73, 0x22, 0xf2, 0x01, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||
0x6c, 0x73, 0x22, 0xe2, 0x02, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||
0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
||||
0x63, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
@@ -294,7 +321,14 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||
0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x2e,
|
||||
0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d,
|
||||
0x6f, 0x63, 0x73, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20,
|
||||
0x01, 0x28, 0x03, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x53, 0x74, 0x61, 0x70, 0x6c, 0x69, 0x6e,
|
||||
0x01, 0x28, 0x04, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x53, 0x74, 0x61, 0x70, 0x6c, 0x69, 0x6e,
|
||||
0x67, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
|
||||
0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x65, 0x72,
|
||||
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08,
|
||||
0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x4f, 0x6e, 0x65, 0x5f, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x0e, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e,
|
||||
0x67, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x4e,
|
||||
0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10,
|
||||
0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59,
|
||||
|
@@ -21,7 +21,16 @@ message Certificate {
|
||||
|
||||
Usage usage = 3;
|
||||
|
||||
int64 ocsp_stapling = 4;
|
||||
uint64 ocsp_stapling = 4;
|
||||
|
||||
// TLS certificate path
|
||||
string certificate_path = 5;
|
||||
|
||||
// TLS Key path
|
||||
string key_path = 6;
|
||||
|
||||
// If true, one-Time Loading
|
||||
bool One_time_loading = 7;
|
||||
}
|
||||
|
||||
message Config {
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/ocsp"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/common/protocol/tls/cert"
|
||||
"github.com/xtls/xray-core/transport/internet"
|
||||
)
|
||||
@@ -58,18 +59,56 @@ func (c *Config) BuildCertificates() []*xtls.Certificate {
|
||||
continue
|
||||
}
|
||||
certs = append(certs, &keyPair)
|
||||
if entry.OcspStapling != 0 {
|
||||
go func(cert *xtls.Certificate) {
|
||||
t := time.NewTicker(time.Duration(entry.OcspStapling) * time.Second)
|
||||
if !entry.OneTimeLoading {
|
||||
var isOcspstapling bool
|
||||
hotRelodaInterval := uint64(3600)
|
||||
if entry.OcspStapling != 0 {
|
||||
hotRelodaInterval = entry.OcspStapling
|
||||
isOcspstapling = true
|
||||
}
|
||||
index := len(certs) - 1
|
||||
go func(cert *xtls.Certificate, index int) {
|
||||
t := time.NewTicker(time.Duration(hotRelodaInterval) * time.Second)
|
||||
for {
|
||||
if newData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil {
|
||||
newError("ignoring invalid OCSP").Base(err).AtWarning().WriteToLog()
|
||||
} else if string(newData) != string(cert.OCSPStaple) {
|
||||
cert.OCSPStaple = newData
|
||||
if entry.CertificatePath != "" && entry.KeyPath != "" {
|
||||
newCert, err := filesystem.ReadFile(entry.CertificatePath)
|
||||
if err != nil {
|
||||
newError("failed to parse certificate").Base(err).AtError().WriteToLog()
|
||||
<-t.C
|
||||
continue
|
||||
}
|
||||
newKey, err := filesystem.ReadFile(entry.KeyPath)
|
||||
if err != nil {
|
||||
newError("failed to parse key").Base(err).AtError().WriteToLog()
|
||||
<-t.C
|
||||
continue
|
||||
}
|
||||
if string(newCert) != string(entry.Certificate) && string(newKey) != string(entry.Key) {
|
||||
newKeyPair, err := xtls.X509KeyPair(newCert, newKey)
|
||||
if err != nil {
|
||||
newError("ignoring invalid X509 key pair").Base(err).AtError().WriteToLog()
|
||||
<-t.C
|
||||
continue
|
||||
}
|
||||
if newKeyPair.Leaf, err = x509.ParseCertificate(newKeyPair.Certificate[0]); err != nil {
|
||||
newError("ignoring invalid certificate").Base(err).AtError().WriteToLog()
|
||||
<-t.C
|
||||
continue
|
||||
}
|
||||
cert = &newKeyPair
|
||||
}
|
||||
}
|
||||
if isOcspstapling {
|
||||
if newOCSPData, err := ocsp.GetOCSPForCert(cert.Certificate); err != nil {
|
||||
newError("ignoring invalid OCSP").Base(err).AtWarning().WriteToLog()
|
||||
} else if string(newOCSPData) != string(cert.OCSPStaple) {
|
||||
cert.OCSPStaple = newOCSPData
|
||||
}
|
||||
}
|
||||
certs[index] = cert
|
||||
<-t.C
|
||||
}
|
||||
}(certs[len(certs)-1])
|
||||
}(certs[len(certs)-1], index)
|
||||
}
|
||||
}
|
||||
return certs
|
||||
|
@@ -84,7 +84,13 @@ type Certificate struct {
|
||||
// TLS key in x509 format.
|
||||
Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Usage Certificate_Usage `protobuf:"varint,3,opt,name=usage,proto3,enum=xray.transport.internet.xtls.Certificate_Usage" json:"usage,omitempty"`
|
||||
OcspStapling int64 `protobuf:"varint,4,opt,name=ocsp_stapling,json=ocspStapling,proto3" json:"ocsp_stapling,omitempty"`
|
||||
OcspStapling uint64 `protobuf:"varint,4,opt,name=ocsp_stapling,json=ocspStapling,proto3" json:"ocsp_stapling,omitempty"`
|
||||
// TLS certificate path
|
||||
CertificatePath string `protobuf:"bytes,5,opt,name=certificate_path,json=certificatePath,proto3" json:"certificate_path,omitempty"`
|
||||
// TLS Key path
|
||||
KeyPath string `protobuf:"bytes,6,opt,name=key_path,json=keyPath,proto3" json:"key_path,omitempty"`
|
||||
// If true, one-Time Loading
|
||||
OneTimeLoading bool `protobuf:"varint,7,opt,name=One_time_loading,json=OneTimeLoading,proto3" json:"One_time_loading,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Certificate) Reset() {
|
||||
@@ -140,13 +146,34 @@ func (x *Certificate) GetUsage() Certificate_Usage {
|
||||
return Certificate_ENCIPHERMENT
|
||||
}
|
||||
|
||||
func (x *Certificate) GetOcspStapling() int64 {
|
||||
func (x *Certificate) GetOcspStapling() uint64 {
|
||||
if x != nil {
|
||||
return x.OcspStapling
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Certificate) GetCertificatePath() string {
|
||||
if x != nil {
|
||||
return x.CertificatePath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Certificate) GetKeyPath() string {
|
||||
if x != nil {
|
||||
return x.KeyPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Certificate) GetOneTimeLoading() bool {
|
||||
if x != nil {
|
||||
return x.OneTimeLoading
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -284,7 +311,7 @@ var file_transport_internet_xtls_config_proto_rawDesc = []byte{
|
||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
||||
0x78, 0x74, 0x6c, 0x73, 0x22, 0xf3, 0x01, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
||||
0x78, 0x74, 0x6c, 0x73, 0x22, 0xe3, 0x02, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
||||
0x63, 0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69,
|
||||
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20,
|
||||
@@ -294,8 +321,15 @@ var file_transport_internet_xtls_config_proto_rawDesc = []byte{
|
||||
0x74, 0x2e, 0x78, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||
0x74, 0x65, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12,
|
||||
0x23, 0x0a, 0x0d, 0x6f, 0x63, 0x73, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x53, 0x74, 0x61, 0x70,
|
||||
0x6c, 0x69, 0x6e, 0x67, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x53, 0x74, 0x61, 0x70,
|
||||
0x6c, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||
0x61, 0x74, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f,
|
||||
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12,
|
||||
0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x4f, 0x6e,
|
||||
0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07,
|
||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x61,
|
||||
0x64, 0x69, 0x6e, 0x67, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a,
|
||||
0x0c, 0x45, 0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12,
|
||||
0x14, 0x0a, 0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52,
|
||||
0x49, 0x46, 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49,
|
||||
|
@@ -21,7 +21,16 @@ message Certificate {
|
||||
|
||||
Usage usage = 3;
|
||||
|
||||
int64 ocsp_stapling = 4;
|
||||
uint64 ocsp_stapling = 4;
|
||||
|
||||
// TLS certificate path
|
||||
string certificate_path = 5;
|
||||
|
||||
// TLS Key path
|
||||
string key_path = 6;
|
||||
|
||||
// If true, one-Time Loading
|
||||
bool One_time_loading = 7;
|
||||
}
|
||||
|
||||
message Config {
|
||||
|
Reference in New Issue
Block a user